From c2fdd9bfdf52c23234f9f47251b6fc4ac16c25c2 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 8 Oct 2014 09:06:06 -0700 Subject: [PATCH 01/10] s3: smbd: Preparation for leases code merge. Ensure VFS is ready for 4.2.0. Signed-off-by: Jeremy Allison Reviewed-by: Volker Lendecke Autobuild-User(master): Jeremy Allison Autobuild-Date(master): Fri Oct 10 02:55:53 CEST 2014 on sn-devel-104 (cherry picked from commit aa2a6c7b18e2e2bd3979ffb53208564764b8b9cf) --- source3/include/vfs.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/source3/include/vfs.h b/source3/include/vfs.h index 3702b75..b0f00e8 100644 --- a/source3/include/vfs.h +++ b/source3/include/vfs.h @@ -158,6 +158,7 @@ /* Bump to version 32 - Samba 4.2 will ship with that. */ /* Version 32 - Add "lease" to CREATE_FILE operation */ +/* Version 32 - Add "lease" to struct files_struct */ #define SMB_VFS_INTERFACE_VERSION 32 @@ -202,6 +203,11 @@ struct fd_handle { unsigned long gen_id; }; +struct fsp_lease { + size_t ref_count; + struct smb2_lease lease; +}; + typedef struct files_struct { struct files_struct *next, *prev; uint64_t fnum; @@ -225,6 +231,7 @@ typedef struct files_struct { bool write_time_forced; int oplock_type; + struct fsp_lease *lease; /* Not yet used. Placeholder for leases. */ int sent_oplock_break; struct tevent_timer *oplock_timeout; struct lock_struct last_lock_failure; -- 2.1.0.rc2.206.gedb03e5 From 1390ab9f9f0add5b1a8589dd2a94b757673ed5c0 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Tue, 23 Sep 2014 05:18:54 +0200 Subject: [PATCH 02/10] s3:locking: Rename share_mode_forall->share_entry_forall Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison Reviewed-by: Stefan Metzmacher (cherry picked from commit 0d4f7bfdb995a239508457cd433bc8001c0e8279) --- source3/locking/proto.h | 4 ++-- source3/locking/share_mode_lock.c | 4 ++-- source3/rpc_server/srvsvc/srv_srvsvc_nt.c | 8 ++++---- source3/utils/status.c | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/source3/locking/proto.h b/source3/locking/proto.h index 46eec2a..ce8c25c 100644 --- a/source3/locking/proto.h +++ b/source3/locking/proto.h @@ -190,8 +190,8 @@ bool is_delete_on_close_set(struct share_mode_lock *lck, uint32_t name_hash); bool set_sticky_write_time(struct file_id fileid, struct timespec write_time); bool set_write_time(struct file_id fileid, struct timespec write_time); struct timespec get_share_mode_write_time(struct share_mode_lock *lck); -int share_mode_forall(void (*fn)(const struct share_mode_entry *, const char *, - const char *, void *), +int share_entry_forall(void (*fn)(const struct share_mode_entry *, const char *, + const char *, void *), void *private_data); bool share_mode_cleanup_disconnected(struct file_id id, uint64_t open_persistent_id); diff --git a/source3/locking/share_mode_lock.c b/source3/locking/share_mode_lock.c index 12f499b..53039eb 100644 --- a/source3/locking/share_mode_lock.c +++ b/source3/locking/share_mode_lock.c @@ -499,8 +499,8 @@ static int traverse_fn(struct db_record *rec, void *_state) share mode system. ********************************************************************/ -int share_mode_forall(void (*fn)(const struct share_mode_entry *, const char *, - const char *, void *), +int share_entry_forall(void (*fn)(const struct share_mode_entry *, + const char *, const char *, void *), void *private_data) { struct forall_state state; diff --git a/source3/rpc_server/srvsvc/srv_srvsvc_nt.c b/source3/rpc_server/srvsvc/srv_srvsvc_nt.c index 855b8c7..d2f05f3 100644 --- a/source3/rpc_server/srvsvc/srv_srvsvc_nt.c +++ b/source3/rpc_server/srvsvc/srv_srvsvc_nt.c @@ -166,7 +166,7 @@ static WERROR net_enum_files(TALLOC_CTX *ctx, f_enum_cnt.username = username; f_enum_cnt.ctr3 = *ctr3; - share_mode_forall( enum_file_fn, (void *)&f_enum_cnt ); + share_entry_forall( enum_file_fn, (void *)&f_enum_cnt ); *ctr3 = f_enum_cnt.ctr3; @@ -867,7 +867,7 @@ static void net_count_files_for_all_sess(struct srvsvc_NetSessCtr1 *ctr1, s_file_info.resume_handle = resume_handle; s_file_info.num_entries = num_entries; - share_mode_forall(count_sess_files_fn, &s_file_info); + share_entry_forall(count_sess_files_fn, &s_file_info); } /******************************************************************* @@ -984,7 +984,7 @@ static void count_share_opens(struct srvsvc_NetConnInfo1 *arr, sfs.resp_entries = resp_entries; sfs.total_entries = total_entries; - share_mode_forall(share_file_fn, &sfs); + share_entry_forall(share_file_fn, &sfs); } /**************************************************************************** @@ -2744,7 +2744,7 @@ WERROR _srvsvc_NetFileClose(struct pipes_struct *p, r->out.result = WERR_BADFILE; state.r = r; state.msg_ctx = p->msg_ctx; - share_mode_forall(enum_file_close_fn, &state); + share_entry_forall(enum_file_close_fn, &state); return r->out.result; } diff --git a/source3/utils/status.c b/source3/utils/status.c index 7bbcea5..2c850bb 100644 --- a/source3/utils/status.c +++ b/source3/utils/status.c @@ -526,7 +526,7 @@ int main(int argc, const char *argv[]) goto done; } - result = share_mode_forall(print_share_mode, NULL); + result = share_entry_forall(print_share_mode, NULL); if (result == 0) { d_printf("No locked files\n"); -- 2.1.0.rc2.206.gedb03e5 From f6cecf7c2240fdc42e1c1e1404d357510361b353 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Tue, 23 Sep 2014 05:45:49 +0200 Subject: [PATCH 03/10] s3:locking: Introduce share_mode_forall Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison Reviewed-by: Stefan Metzmacher (cherry picked from commit 48926b761975a7d9cb6daf30d64d6a4f0a34f38a) --- source3/locking/proto.h | 4 ++ source3/locking/share_mode_lock.c | 92 +++++++++++++++++++++++++++------------ 2 files changed, 68 insertions(+), 28 deletions(-) diff --git a/source3/locking/proto.h b/source3/locking/proto.h index ce8c25c..a5c46c8 100644 --- a/source3/locking/proto.h +++ b/source3/locking/proto.h @@ -190,6 +190,10 @@ bool is_delete_on_close_set(struct share_mode_lock *lck, uint32_t name_hash); bool set_sticky_write_time(struct file_id fileid, struct timespec write_time); bool set_write_time(struct file_id fileid, struct timespec write_time); struct timespec get_share_mode_write_time(struct share_mode_lock *lck); +int share_mode_forall(int (*fn)(struct file_id fid, + const struct share_mode_data *data, + void *private_data), + void *private_data); int share_entry_forall(void (*fn)(const struct share_mode_entry *, const char *, const char *, void *), void *private_data); diff --git a/source3/locking/share_mode_lock.c b/source3/locking/share_mode_lock.c index 53039eb..c2f3402 100644 --- a/source3/locking/share_mode_lock.c +++ b/source3/locking/share_mode_lock.c @@ -440,30 +440,33 @@ struct share_mode_lock *fetch_share_mode_unlocked(TALLOC_CTX *mem_ctx, return lck; } -struct forall_state { - void (*fn)(const struct share_mode_entry *entry, - const char *sharepath, - const char *fname, - void *private_data); +struct share_mode_forall_state { + int (*fn)(struct file_id fid, const struct share_mode_data *data, + void *private_data); void *private_data; }; -static int traverse_fn(struct db_record *rec, void *_state) +static int share_mode_traverse_fn(struct db_record *rec, void *_state) { - struct forall_state *state = (struct forall_state *)_state; + struct share_mode_forall_state *state = + (struct share_mode_forall_state *)_state; uint32_t i; TDB_DATA key; TDB_DATA value; DATA_BLOB blob; enum ndr_err_code ndr_err; struct share_mode_data *d; + struct file_id fid; + int ret; key = dbwrap_record_get_key(rec); value = dbwrap_record_get_value(rec); /* Ensure this is a locking_key record. */ - if (key.dsize != sizeof(struct file_id)) + if (key.dsize != sizeof(fid)) { return 0; + } + memcpy(&fid, key.dptr, sizeof(fid)); d = talloc(talloc_tos(), struct share_mode_data); if (d == NULL) { @@ -485,11 +488,58 @@ static int traverse_fn(struct db_record *rec, void *_state) } for (i=0; inum_share_modes; i++) { d->share_modes[i].stale = false; /* [skip] in idl */ - state->fn(&d->share_modes[i], - d->servicepath, d->base_name, - state->private_data); } + + ret = state->fn(fid, d, state->private_data); + TALLOC_FREE(d); + return ret; +} + +int share_mode_forall(int (*fn)(struct file_id fid, + const struct share_mode_data *data, + void *private_data), + void *private_data) +{ + struct share_mode_forall_state state = { + .fn = fn, + .private_data = private_data + }; + NTSTATUS status; + int count; + + if (lock_db == NULL) { + return 0; + } + + status = dbwrap_traverse_read(lock_db, share_mode_traverse_fn, + &state, &count); + if (!NT_STATUS_IS_OK(status)) { + return -1; + } + + return count; +} + +struct share_entry_forall_state { + void (*fn)(const struct share_mode_entry *e, + const char *service_path, const char *base_name, + void *private_data); + void *private_data; +}; + +static int share_entry_traverse_fn(struct file_id fid, + const struct share_mode_data *data, + void *private_data) +{ + struct share_entry_forall_state *state = private_data; + uint32_t i; + + for (i=0; inum_share_modes; i++) { + state->fn(&data->share_modes[i], + data->servicepath, data->base_name, + state->private_data); + } return 0; } @@ -503,24 +553,10 @@ int share_entry_forall(void (*fn)(const struct share_mode_entry *, const char *, const char *, void *), void *private_data) { - struct forall_state state; - NTSTATUS status; - int count; - - if (lock_db == NULL) - return 0; - - state.fn = fn; - state.private_data = private_data; + struct share_entry_forall_state state = { + .fn = fn, .private_data = private_data }; - status = dbwrap_traverse_read(lock_db, traverse_fn, (void *)&state, - &count); - - if (!NT_STATUS_IS_OK(status)) { - return -1; - } else { - return count; - } + return share_mode_forall(share_entry_traverse_fn, &state); } bool share_mode_cleanup_disconnected(struct file_id fid, -- 2.1.0.rc2.206.gedb03e5 From fcb5a60652474c69c1b3dbd6d387b4828c0fa766 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Wed, 24 Sep 2014 20:46:15 +0200 Subject: [PATCH 04/10] s3:locking: allow early return for share_entry_forall() Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison Reviewed-by: Stefan Metzmacher (cherry picked from commit 9010bbeb00264f4476c3be7d2e8c8420c695cfbb) --- source3/locking/proto.h | 4 +-- source3/locking/share_mode_lock.c | 23 ++++++++++------ source3/rpc_server/srvsvc/srv_srvsvc_nt.c | 46 +++++++++++++++++-------------- source3/utils/status.c | 12 ++++---- 4 files changed, 49 insertions(+), 36 deletions(-) diff --git a/source3/locking/proto.h b/source3/locking/proto.h index a5c46c8..44f3ba1 100644 --- a/source3/locking/proto.h +++ b/source3/locking/proto.h @@ -194,8 +194,8 @@ int share_mode_forall(int (*fn)(struct file_id fid, const struct share_mode_data *data, void *private_data), void *private_data); -int share_entry_forall(void (*fn)(const struct share_mode_entry *, const char *, - const char *, void *), +int share_entry_forall(int (*fn)(const struct share_mode_entry *, const char *, + const char *, void *), void *private_data); bool share_mode_cleanup_disconnected(struct file_id id, uint64_t open_persistent_id); diff --git a/source3/locking/share_mode_lock.c b/source3/locking/share_mode_lock.c index c2f3402..da16d1a 100644 --- a/source3/locking/share_mode_lock.c +++ b/source3/locking/share_mode_lock.c @@ -522,9 +522,9 @@ int share_mode_forall(int (*fn)(struct file_id fid, } struct share_entry_forall_state { - void (*fn)(const struct share_mode_entry *e, - const char *service_path, const char *base_name, - void *private_data); + int (*fn)(const struct share_mode_entry *e, + const char *service_path, const char *base_name, + void *private_data); void *private_data; }; @@ -536,9 +536,14 @@ static int share_entry_traverse_fn(struct file_id fid, uint32_t i; for (i=0; inum_share_modes; i++) { - state->fn(&data->share_modes[i], - data->servicepath, data->base_name, - state->private_data); + int ret; + + ret = state->fn(&data->share_modes[i], + data->servicepath, data->base_name, + state->private_data); + if (ret != 0) { + return ret; + } } return 0; @@ -549,9 +554,9 @@ static int share_entry_traverse_fn(struct file_id fid, share mode system. ********************************************************************/ -int share_entry_forall(void (*fn)(const struct share_mode_entry *, - const char *, const char *, void *), - void *private_data) +int share_entry_forall(int (*fn)(const struct share_mode_entry *, + const char *, const char *, void *), + void *private_data) { struct share_entry_forall_state state = { .fn = fn, .private_data = private_data }; diff --git a/source3/rpc_server/srvsvc/srv_srvsvc_nt.c b/source3/rpc_server/srvsvc/srv_srvsvc_nt.c index d2f05f3..eaa70e7 100644 --- a/source3/rpc_server/srvsvc/srv_srvsvc_nt.c +++ b/source3/rpc_server/srvsvc/srv_srvsvc_nt.c @@ -79,9 +79,9 @@ struct share_conn_stat { /******************************************************************* ********************************************************************/ -static void enum_file_fn( const struct share_mode_entry *e, - const char *sharepath, const char *fname, - void *private_data ) +static int enum_file_fn(const struct share_mode_entry *e, + const char *sharepath, const char *fname, + void *private_data) { struct file_enum_count *fenum = (struct file_enum_count *)private_data; @@ -98,21 +98,21 @@ static void enum_file_fn( const struct share_mode_entry *e, /* If the pid was not found delete the entry from connections.tdb */ if ( !process_exists(e->pid) ) { - return; + return 0; } username = uidtoname(e->uid); if ((fenum->username != NULL) && !strequal(username, fenum->username)) { - return; + return 0; } f = talloc_realloc(fenum->ctx, fenum->ctr3->array, struct srvsvc_NetFileInfo3, i+1); if ( !f ) { DEBUG(0,("conn_enum_fn: realloc failed for %d items\n", i+1)); - return; + return 0; } fenum->ctr3->array = f; @@ -133,7 +133,7 @@ static void enum_file_fn( const struct share_mode_entry *e, sharepath, fname ); } if (!fullpath) { - return; + return 0; } string_replace( fullpath, '/', '\\' ); @@ -150,6 +150,8 @@ static void enum_file_fn( const struct share_mode_entry *e, fenum->ctr3->array[i].user = username; fenum->ctr3->count++; + + return 0; } /******************************************************************* @@ -826,9 +828,9 @@ static WERROR init_srv_sess_info_0(struct pipes_struct *p, * find out the session on which this file is open and bump up its count **********************************************************************/ -static void count_sess_files_fn(const struct share_mode_entry *e, - const char *sharepath, const char *fname, - void *data) +static int count_sess_files_fn(const struct share_mode_entry *e, + const char *sharepath, const char *fname, + void *data) { struct sess_file_info *info = data; uint32_t rh = info->resume_handle; @@ -846,9 +848,10 @@ static void count_sess_files_fn(const struct share_mode_entry *e, serverid_equal(&e->pid, &sess->pid)) { info->ctr->array[i].num_open++; - return; + return 0; } } + return 0; } /******************************************************************* @@ -950,9 +953,9 @@ static WERROR init_srv_sess_info_1(struct pipes_struct *p, find the share connection on which this open exists. ********************************************************************/ -static void share_file_fn(const struct share_mode_entry *e, - const char *sharepath, const char *fname, - void *data) +static int share_file_fn(const struct share_mode_entry *e, + const char *sharepath, const char *fname, + void *data) { struct share_file_stat *sfs = data; uint32_t i; @@ -962,10 +965,11 @@ static void share_file_fn(const struct share_mode_entry *e, for (i=0; i < sfs->resp_entries; i++) { if (serverid_equal(&e->pid, &sfs->svrid_arr[offset + i])) { sfs->netconn_arr[i].num_open ++; - return; + return 0; } } } + return 0; } /******************************************************************* @@ -2690,9 +2694,9 @@ struct enum_file_close_state { struct messaging_context *msg_ctx; }; -static void enum_file_close_fn( const struct share_mode_entry *e, - const char *sharepath, const char *fname, - void *private_data ) +static int enum_file_close_fn(const struct share_mode_entry *e, + const char *sharepath, const char *fname, + void *private_data) { char msg[MSG_SMB_SHARE_MODE_ENTRY_SIZE]; struct enum_file_close_state *state = @@ -2700,11 +2704,11 @@ static void enum_file_close_fn( const struct share_mode_entry *e, uint32_t fid = (((uint32_t)(procid_to_pid(&e->pid))<<16) | e->share_file_id); if (fid != state->r->in.fid) { - return; /* Not this file. */ + return 0; /* Not this file. */ } if (!process_exists(e->pid) ) { - return; + return 0; } /* Ok - send the close message. */ @@ -2718,6 +2722,8 @@ static void enum_file_close_fn( const struct share_mode_entry *e, messaging_send_buf(state->msg_ctx, e->pid, MSG_SMB_CLOSE_FILE, (uint8 *)msg, sizeof(msg))); + + return 0; } /******************************************************************** diff --git a/source3/utils/status.c b/source3/utils/status.c index 2c850bb..b589813 100644 --- a/source3/utils/status.c +++ b/source3/utils/status.c @@ -115,10 +115,10 @@ static bool Ucrit_addPid( struct server_id pid ) return True; } -static void print_share_mode(const struct share_mode_entry *e, - const char *sharepath, - const char *fname, - void *dummy) +static int print_share_mode(const struct share_mode_entry *e, + const char *sharepath, + const char *fname, + void *dummy) { static int count; @@ -135,7 +135,7 @@ static void print_share_mode(const struct share_mode_entry *e, if (do_checks && !serverid_exists(&e->pid)) { /* the process for this entry does not exist any more */ - return; + return 0; } if (Ucrit_checkPid(e->pid)) { @@ -183,6 +183,8 @@ static void print_share_mode(const struct share_mode_entry *e, d_printf(" %s %s %s",sharepath, fname, time_to_asc((time_t)e->time.tv_sec)); } + + return 0; } static void print_brl(struct file_id id, -- 2.1.0.rc2.206.gedb03e5 From 742df0942ab0ea9f9ce8be8d2b674a9ca45bbedd Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 24 Oct 2014 13:57:04 -0700 Subject: [PATCH 05/10] s3:param: Add new option "strict rename". Control whether smbd can rename directories containing open files. Defaults to "no" (meaning we *can* do such renames). Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher (cherry picked from commit b0a434386dc2f77df89811bc3f56c4cc7fb7b16c) --- docs-xml/smbdotconf/tuning/strictrename.xml | 25 +++++++++++++++++++++++++ lib/param/param_table.c | 9 +++++++++ source3/param/loadparm.c | 1 + 3 files changed, 35 insertions(+) create mode 100644 docs-xml/smbdotconf/tuning/strictrename.xml diff --git a/docs-xml/smbdotconf/tuning/strictrename.xml b/docs-xml/smbdotconf/tuning/strictrename.xml new file mode 100644 index 0000000..5478863 --- /dev/null +++ b/docs-xml/smbdotconf/tuning/strictrename.xml @@ -0,0 +1,25 @@ + + + By default a Windows SMB server prevents directory + renames when there are open file or directory handles below + it in the filesystem hierarchy. Historically Samba has always + allowed this as POSIX filesystem semantics require it. + + This boolean parameter allows Samba to match the Windows + behavior. Setting this to "yes" is a very expensive change, + as it forces Samba to travers the entire open file handle + database on every directory rename request. In a clustered + Samba system the cost is even greater than the non-clustered + case. + + For this reason the default is "no", and it is recommended + to be left that way unless a specific Windows application requires + it to be changed. + + + +no + diff --git a/lib/param/param_table.c b/lib/param/param_table.c index d3f60c3..4d0e6a9 100644 --- a/lib/param/param_table.c +++ b/lib/param/param_table.c @@ -1882,6 +1882,15 @@ struct parm_struct parm_table[] = { .flags = FLAG_ADVANCED | FLAG_SHARE, }, { + .label = "strict rename", + .type = P_BOOL, + .p_class = P_LOCAL, + .offset = LOCAL_VAR(strict_rename), + .special = NULL, + .enum_list = NULL, + .flags = FLAG_ADVANCED | FLAG_SHARE, + }, + { .label = "strict sync", .type = P_BOOL, .p_class = P_LOCAL, diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c index 5ab0de7..884cc45 100644 --- a/source3/param/loadparm.c +++ b/source3/param/loadparm.c @@ -206,6 +206,7 @@ static struct loadparm_service sDefault = .follow_symlinks = true, .sync_always = false, .strict_allocate = false, + .strict_rename = false, .strict_sync = false, .mangling_char = '~', .copymap = NULL, -- 2.1.0.rc2.206.gedb03e5 From 96cd73c632761bbad3e63a8e6f87c8d53dc50072 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 24 Oct 2014 13:57:04 -0700 Subject: [PATCH 06/10] selftest:Samba3: use "strict rename = yes" Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher (cherry picked from commit 5f60dcc38ca275aedeb1d67611b5acf9b26361d5) --- selftest/target/Samba3.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm index de40ced..ebe2c09 100755 --- a/selftest/target/Samba3.pm +++ b/selftest/target/Samba3.pm @@ -1073,6 +1073,7 @@ sub provision($$$$$$) store dos attributes = yes create mask = 755 dos filemode = yes + strict rename = yes vfs objects = acl_xattr fake_acls xattr_tdb streams_depot printing = vlp -- 2.1.0.rc2.206.gedb03e5 From 43524fe3e32874c3c8b12188fc187f2c71c8a306 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Thu, 25 Sep 2014 01:30:33 +0200 Subject: [PATCH 07/10] s3:smbd: Don't rename a dir with files open underneath This is an EXPENSIVE check. We'll have to guard this with an option Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison Reviewed-by: Stefan Metzmacher (cherry picked from commit 035fd7200d8a025cdb8bfae30c264757aa3cb193) --- source3/smbd/dir.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 120 insertions(+), 1 deletion(-) diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c index e60bc2c..36d95d5 100644 --- a/source3/smbd/dir.c +++ b/source3/smbd/dir.c @@ -25,6 +25,7 @@ #include "libcli/security/security.h" #include "lib/util/bitmap.h" #include "../lib/util/memcache.h" +#include "../librpc/gen_ndr/open_files.h" /* This module implements directory related functions for Samba. @@ -1809,6 +1810,113 @@ bool SearchDir(struct smb_Dir *dirp, const char *name, long *poffset) return False; } +struct files_below_forall_state { + char *dirpath; + size_t dirpath_len; + int (*fn)(struct file_id fid, const struct share_mode_data *data, + void *private_data); + void *private_data; +}; + +static int files_below_forall_fn(struct file_id fid, + const struct share_mode_data *data, + void *private_data) +{ + struct files_below_forall_state *state = private_data; + char tmpbuf[PATH_MAX]; + char *fullpath, *to_free; + size_t len; + + len = full_path_tos(data->servicepath, data->base_name, + tmpbuf, sizeof(tmpbuf), + &fullpath, &to_free); + if (len == -1) { + return 0; + } + if (state->dirpath_len >= len) { + /* + * Filter files above dirpath + */ + return 0; + } + if (fullpath[state->dirpath_len] != '/') { + /* + * Filter file that don't have a path separator at the end of + * dirpath's length + */ + return 0; + } + + if (memcmp(state->dirpath, fullpath, len) != 0) { + /* + * Not a parent + */ + return 0; + } + + return state->fn(fid, data, private_data); +} + +static int files_below_forall(connection_struct *conn, + const struct smb_filename *dir_name, + int (*fn)(struct file_id fid, + const struct share_mode_data *data, + void *private_data), + void *private_data) +{ + struct files_below_forall_state state = {}; + int ret; + char tmpbuf[PATH_MAX]; + char *to_free; + + state.dirpath_len = full_path_tos(conn->connectpath, + dir_name->base_name, + tmpbuf, sizeof(tmpbuf), + &state.dirpath, &to_free); + if (state.dirpath_len == -1) { + return -1; + + } + + ret = share_mode_forall(files_below_forall_fn, &state); + TALLOC_FREE(to_free); + return ret; +} + +struct have_file_open_below_state { + bool found_one; +}; + +static int have_file_open_below_fn(struct file_id fid, + const struct share_mode_data *data, + void *private_data) +{ + struct have_file_open_below_state *state = private_data; + state->found_one = true; + return 1; +} + +static bool have_file_open_below(connection_struct *conn, + const struct smb_filename *name) +{ + struct have_file_open_below_state state = {}; + int ret; + + if (!VALID_STAT(name->st)) { + return false; + } + if (!S_ISDIR(name->st.st_ex_mode)) { + return false; + } + + ret = files_below_forall(conn, name, have_file_open_below_fn, &state); + if (ret == -1) { + return false; + } + + return state.found_one; +} + /***************************************************************** Is this directory empty ? *****************************************************************/ @@ -1854,5 +1962,16 @@ NTSTATUS can_delete_directory_fsp(files_struct *fsp) TALLOC_FREE(talloced); TALLOC_FREE(dir_hnd); - return status; + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (!lp_posix_pathnames() && + lp_strict_rename(SNUM(conn)) && + have_file_open_below(fsp->conn, fsp->fsp_name)) + { + return NT_STATUS_ACCESS_DENIED; + } + + return NT_STATUS_OK; } -- 2.1.0.rc2.206.gedb03e5 From 25bee982a551a2c37be807ad51baea73bcf4a463 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Thu, 25 Sep 2014 01:32:00 +0200 Subject: [PATCH 08/10] s4:torture/smb2: test rename dir deny with open files Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison Reviewed-by: Stefan Metzmacher (cherry picked from commit 8334428666b8282d2cfbcfd411acab0c338ae390) --- selftest/knownfail | 1 + source4/torture/smb2/rename.c | 97 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) diff --git a/selftest/knownfail b/selftest/knownfail index 3d73495..6cca3dd 100644 --- a/selftest/knownfail +++ b/selftest/knownfail @@ -112,6 +112,7 @@ ^samba4.smb2.rename.no_share_delete_no_delete_access\(.*\)$ ^samba4.smb2.rename.msword ^samba4.smb2.rename.rename_dir_bench\(dc\) +^samba4.smb2.rename.rename_dir_openfile\(.*\)$ ^samba4.smb2.oplock.doc ^samba4.smb2.compound.related3 ^samba4.smb2.compound.compound-break diff --git a/source4/torture/smb2/rename.c b/source4/torture/smb2/rename.c index 9d0f4e1..07bdabd 100644 --- a/source4/torture/smb2/rename.c +++ b/source4/torture/smb2/rename.c @@ -928,6 +928,99 @@ done: return ret; } +static bool torture_smb2_rename_dir_openfile(struct torture_context *torture, + struct smb2_tree *tree1) +{ + bool ret = true; + NTSTATUS status; + union smb_open io; + union smb_close cl; + union smb_setfileinfo sinfo; + struct smb2_handle d1, h1; + + smb2_deltree(tree1, BASEDIR); + smb2_util_rmdir(tree1, BASEDIR); + + torture_comment(torture, "Creating base directory\n"); + + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = 0x0017019f; + io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + io.smb2.in.share_access = 0; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = BASEDIR; + + status = smb2_create(tree1, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + d1 = io.smb2.out.file.handle; + + torture_comment(torture, "Creating test file\n"); + + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = 0x0017019f; + io.smb2.in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = 0; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = BASEDIR "\\file.txt"; + + status = smb2_create(tree1, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.smb2.out.file.handle; + + torture_comment(torture, "Renaming directory\n"); + + ZERO_STRUCT(sinfo); + sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + sinfo.rename_information.in.file.handle = d1; + sinfo.rename_information.in.overwrite = 0; + sinfo.rename_information.in.root_fid = 0; + sinfo.rename_information.in.new_name = + BASEDIR "-new"; + status = smb2_setinfo_file(tree1, &sinfo); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + torture_comment(torture, "Closing directory\n"); + + ZERO_STRUCT(cl.smb2); + cl.smb2.level = RAW_CLOSE_SMB2; + cl.smb2.in.file.handle = d1; + status = smb2_close(tree1, &(cl.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + ZERO_STRUCT(d1); + + torture_comment(torture, "Closing test file\n"); + + cl.smb2.in.file.handle = h1; + status = smb2_close(tree1, &(cl.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + ZERO_STRUCT(h1); + +done: + + torture_comment(torture, "Cleaning up\n"); + + if (h1.data) { + ZERO_STRUCT(cl.smb2); + cl.smb2.level = RAW_CLOSE_SMB2; + cl.smb2.in.file.handle = h1; + status = smb2_close(tree1, &(cl.smb2)); + } + smb2_deltree(tree1, BASEDIR); + return ret; +} + struct rename_one_dir_cycle_state { struct tevent_context *ev; struct smb2_tree *tree; @@ -1336,6 +1429,10 @@ struct torture_suite *torture_smb2_rename_init(void) "msword", torture_smb2_rename_msword); + torture_suite_add_1smb2_test( + suite, "rename_dir_openfile", + torture_smb2_rename_dir_openfile); + torture_suite_add_1smb2_test(suite, "rename_dir_bench", torture_smb2_rename_dir_bench); -- 2.1.0.rc2.206.gedb03e5 From 4881014943f992ca8d0402bc6bf90ddb327a4710 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Tue, 28 Oct 2014 15:20:26 -0700 Subject: [PATCH 09/10] s3:locking: Change from ndr_pull_struct_blob() to ndr_pull_struct_blob_all() so we fail if not all bytes are consumed. Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison Reviewed-by: Stefan Metzmacher (cherry picked from commit 151b9caeef7dc4fa4816035a406acb9f1c5812c3) --- source3/locking/share_mode_lock.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source3/locking/share_mode_lock.c b/source3/locking/share_mode_lock.c index da16d1a..65409ac 100644 --- a/source3/locking/share_mode_lock.c +++ b/source3/locking/share_mode_lock.c @@ -133,7 +133,7 @@ static struct share_mode_data *parse_share_modes(TALLOC_CTX *mem_ctx, blob.data = dbuf.dptr; blob.length = dbuf.dsize; - ndr_err = ndr_pull_struct_blob( + ndr_err = ndr_pull_struct_blob_all( &blob, d, d, (ndr_pull_flags_fn_t)ndr_pull_share_mode_data); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { DEBUG(1, ("ndr_pull_share_mode_lock failed: %s\n", @@ -476,7 +476,7 @@ static int share_mode_traverse_fn(struct db_record *rec, void *_state) blob.data = value.dptr; blob.length = value.dsize; - ndr_err = ndr_pull_struct_blob( + ndr_err = ndr_pull_struct_blob_all( &blob, d, d, (ndr_pull_flags_fn_t)ndr_pull_share_mode_data); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { DEBUG(1, ("ndr_pull_share_mode_lock failed\n")); -- 2.1.0.rc2.206.gedb03e5 From dadda2d92a2be92f66d9d15a14807a66e1aa0163 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 29 Oct 2014 17:29:06 +0100 Subject: [PATCH 10/10] s3:locking: remove dead code from brl_get_locks_readonly() struct byte_range_lock *rw = NULL; will never change... commit 105724073300af03eb0835b3c93d9b2e2bfacb07 removed the possible assigment of 'rw'. So we can remove all code under if (rw != NULL) { ... Signed-off-by: Stefan Metzmacher Reviewed-by: Michael Adam Autobuild-User(master): Jeremy Allison Autobuild-Date(master): Fri Oct 31 06:07:43 CET 2014 on sn-devel-104 (cherry picked from commit a3b333a1a2ab23ba0c59be1fc65c730e2b53e8c5) --- source3/locking/brlock.c | 78 +++++++++++++++++------------------------------- 1 file changed, 28 insertions(+), 50 deletions(-) diff --git a/source3/locking/brlock.c b/source3/locking/brlock.c index 295e147..1c4c4d0 100644 --- a/source3/locking/brlock.c +++ b/source3/locking/brlock.c @@ -2026,7 +2026,8 @@ static void brl_get_locks_readonly_parser(TDB_DATA key, TDB_DATA data, struct byte_range_lock *brl_get_locks_readonly(files_struct *fsp) { struct byte_range_lock *br_lock = NULL; - struct byte_range_lock *rw = NULL; + struct brl_get_locks_readonly_state state; + NTSTATUS status; DEBUG(10, ("seqnum=%d, fsp->brlock_seqnum=%d\n", dbwrap_get_seqnum(brlock_db), fsp->brlock_seqnum)); @@ -2040,60 +2041,39 @@ struct byte_range_lock *brl_get_locks_readonly(files_struct *fsp) return fsp->brlock_rec; } - if (rw != NULL) { - size_t lock_data_size; + /* + * Parse the record fresh from the database + */ + + state.mem_ctx = fsp; + state.br_lock = &br_lock; + status = dbwrap_parse_record( + brlock_db, + make_tdb_data((uint8_t *)&fsp->file_id, + sizeof(fsp->file_id)), + brl_get_locks_readonly_parser, &state); + + if (NT_STATUS_EQUAL(status,NT_STATUS_NOT_FOUND)) { /* - * Make a copy of the already retrieved and sanitized rw record + * No locks on this file. Return an empty br_lock. */ - lock_data_size = rw->num_locks * sizeof(struct lock_struct); - br_lock = talloc_pooled_object( - fsp, struct byte_range_lock, 1, lock_data_size); + br_lock = talloc(fsp, struct byte_range_lock); if (br_lock == NULL) { - goto fail; + return NULL; } - br_lock->have_read_oplocks = rw->have_read_oplocks; - br_lock->num_locks = rw->num_locks; - br_lock->lock_data = (struct lock_struct *)talloc_memdup( - br_lock, rw->lock_data, lock_data_size); - } else { - struct brl_get_locks_readonly_state state; - NTSTATUS status; - - /* - * Parse the record fresh from the database - */ - state.mem_ctx = fsp; - state.br_lock = &br_lock; - - status = dbwrap_parse_record( - brlock_db, - make_tdb_data((uint8_t *)&fsp->file_id, - sizeof(fsp->file_id)), - brl_get_locks_readonly_parser, &state); - - if (NT_STATUS_EQUAL(status,NT_STATUS_NOT_FOUND)) { - /* - * No locks on this file. Return an empty br_lock. - */ - br_lock = talloc(fsp, struct byte_range_lock); - if (br_lock == NULL) { - goto fail; - } - - br_lock->have_read_oplocks = false; - br_lock->num_locks = 0; - br_lock->lock_data = NULL; + br_lock->have_read_oplocks = false; + br_lock->num_locks = 0; + br_lock->lock_data = NULL; - } else if (!NT_STATUS_IS_OK(status)) { - DEBUG(3, ("Could not parse byte range lock record: " - "%s\n", nt_errstr(status))); - goto fail; - } - if (br_lock == NULL) { - goto fail; - } + } else if (!NT_STATUS_IS_OK(status)) { + DEBUG(3, ("Could not parse byte range lock record: " + "%s\n", nt_errstr(status))); + return NULL; + } + if (br_lock == NULL) { + return NULL; } br_lock->fsp = fsp; @@ -2117,8 +2097,6 @@ struct byte_range_lock *brl_get_locks_readonly(files_struct *fsp) fsp->brlock_seqnum = dbwrap_get_seqnum(brlock_db); } -fail: - TALLOC_FREE(rw); return br_lock; } -- 2.1.0.rc2.206.gedb03e5