X-Git-Url: http://git.samba.org/samba.git/?p=kai%2Fsamba-autobuild%2F.git;a=blobdiff_plain;f=source3%2Fsmbd%2Fdir.c;h=ad572947edd386a45daacc715e56d9a3b1404b48;hp=0c40a509c42ad81bb56bea32e35d3da3b0c52434;hb=16f202871ca850bec87e0ec243644b2c20266c44;hpb=940ad94906129cf5f65125961470a3a5f41ea78f diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c index 0c40a509c42..ad572947edd 100644 --- a/source3/smbd/dir.c +++ b/source3/smbd/dir.c @@ -24,6 +24,8 @@ #include "smbd/globals.h" #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. @@ -34,6 +36,11 @@ #define START_OF_DIRECTORY_OFFSET ((long)0) #define DOT_DOT_DIRECTORY_OFFSET ((long)0x80000000) +/* "Special" directory offsets in 32-bit wire format. */ +#define WIRE_END_OF_DIRECTORY_OFFSET ((uint32_t)0xFFFFFFFF) +#define WIRE_START_OF_DIRECTORY_OFFSET ((uint32_t)0) +#define WIRE_DOT_DOT_DIRECTORY_OFFSET ((uint32_t)0x80000000) + /* Make directory handle internals available. */ struct name_cache_entry { @@ -57,73 +64,29 @@ struct smb_Dir { struct dptr_struct { struct dptr_struct *next, *prev; int dnum; - uint16 spid; + uint16_t spid; struct connection_struct *conn; struct smb_Dir *dir_hnd; bool expect_close; char *wcard; - uint32 attr; + uint32_t attr; char *path; bool has_wild; /* Set to true if the wcard entry has MS wildcard characters in it. */ bool did_stat; /* Optimisation for non-wcard searches. */ bool priv; /* Directory handle opened with privilege. */ + uint32_t counter; + struct memcache *dptr_cache; }; static struct smb_Dir *OpenDir_fsp(TALLOC_CTX *mem_ctx, connection_struct *conn, files_struct *fsp, const char *mask, - uint32 attr); + uint32_t attr); static void DirCacheAdd(struct smb_Dir *dirp, const char *name, long offset); #define INVALID_DPTR_KEY (-3) -/**************************************************************************** - Make a dir struct. -****************************************************************************/ - -bool make_dir_struct(TALLOC_CTX *ctx, - char *buf, - const char *mask, - const char *fname, - off_t size, - uint32 mode, - time_t date, - bool uc) -{ - char *p; - char *mask2 = talloc_strdup(ctx, mask); - - if (!mask2) { - return False; - } - - if ((mode & FILE_ATTRIBUTE_DIRECTORY) != 0) { - size = 0; - } - - memset(buf+1,' ',11); - if ((p = strchr_m(mask2,'.')) != NULL) { - *p = 0; - push_ascii(buf+1,mask2,8, 0); - push_ascii(buf+9,p+1,3, 0); - *p = '.'; - } else { - push_ascii(buf+1,mask2,11, 0); - } - - memset(buf+21,'\0',DIR_STRUCT_SIZE-21); - SCVAL(buf,21,mode); - srv_put_dos_date(buf,22,date); - SSVAL(buf,26,size & 0xFFFF); - SSVAL(buf,28,(size >> 16)&0xFFFF); - /* We only uppercase if FLAGS2_LONG_PATH_COMPONENTS is zero in the input buf. - Strange, but verified on W2K3. Needed for OS/2. JRA. */ - push_ascii(buf+30,fname,12, uc ? STR_UPPER : 0); - DEBUG(8,("put name [%s] from [%s] into dir struct\n",buf+30, fname)); - return True; -} - /**************************************************************************** Initialise the dir bitmap. ****************************************************************************/ @@ -153,6 +116,8 @@ static void dptr_idle(struct dptr_struct *dptr) if (dptr->dir_hnd) { DEBUG(4,("Idling dptr dnum %d\n",dptr->dnum)); TALLOC_FREE(dptr->dir_hnd); + TALLOC_FREE(dptr->dptr_cache); + dptr->counter = 0; } } @@ -244,7 +209,7 @@ const char *dptr_wcard(struct smbd_server_connection *sconn, int key) Get the dir attrib for a dir index. ****************************************************************************/ -uint16 dptr_attr(struct smbd_server_connection *sconn, int key) +uint16_t dptr_attr(struct smbd_server_connection *sconn, int key) { struct dptr_struct *dptr = dptr_get(sconn, key, false); if (dptr) @@ -369,7 +334,7 @@ void dptr_idlecnum(connection_struct *conn) ****************************************************************************/ void dptr_closepath(struct smbd_server_connection *sconn, - char *path,uint16 spid) + char *path,uint16_t spid) { struct dptr_struct *dptr, *next; for(dptr = sconn->searches.dirptrs; dptr; dptr = next) { @@ -426,7 +391,6 @@ static struct smb_Dir *open_dir_with_privilege(connection_struct *conn, const char *wcard, uint32_t attr) { - NTSTATUS status; struct smb_Dir *dir_hnd = NULL; struct smb_filename *smb_fname_cwd; char *saved_dir = vfs_GetWd(talloc_tos(), conn); @@ -445,7 +409,6 @@ static struct smb_Dir *open_dir_with_privilege(connection_struct *conn, smb_fname_cwd = synthetic_smb_fname(talloc_tos(), ".", NULL, NULL); if (smb_fname_cwd == NULL) { - status = NT_STATUS_NO_MEMORY; goto out; } ret = SMB_VFS_STAT(conn, smb_fname_cwd); @@ -481,8 +444,8 @@ static struct smb_Dir *open_dir_with_privilege(connection_struct *conn, NTSTATUS dptr_create(connection_struct *conn, struct smb_request *req, files_struct *fsp, - const char *path, bool old_handle, bool expect_close,uint16 spid, - const char *wcard, bool wcard_has_wild, uint32 attr, struct dptr_struct **dptr_ret) + const char *path, bool old_handle, bool expect_close,uint16_t spid, + const char *wcard, bool wcard_has_wild, uint32_t attr, struct dptr_struct **dptr_ret) { struct smbd_server_connection *sconn = conn->sconn; struct dptr_struct *dptr = NULL; @@ -559,15 +522,13 @@ NTSTATUS dptr_create(connection_struct *conn, dptr_idleoldest(sconn); } - dptr = talloc(NULL, struct dptr_struct); + dptr = talloc_zero(NULL, struct dptr_struct); if(!dptr) { DEBUG(0,("talloc fail in dptr_create.\n")); TALLOC_FREE(dir_hnd); return NT_STATUS_NO_MEMORY; } - ZERO_STRUCTP(dptr); - dptr->path = talloc_strdup(dptr, path); if (!dptr->path) { TALLOC_FREE(dptr); @@ -685,6 +646,7 @@ void dptr_CloseDir(files_struct *fsp) * all resource deallocation. */ dptr_close_internal(fsp->dptr); + fsp->dptr = NULL; } } @@ -745,10 +707,10 @@ static const char *dptr_normal_ReadDirName(struct dptr_struct *dptr, Return the next visible file name, skipping veto'd and invisible files. ****************************************************************************/ -char *dptr_ReadDirName(TALLOC_CTX *ctx, - struct dptr_struct *dptr, - long *poffset, - SMB_STRUCT_STAT *pst) +static char *dptr_ReadDirName(TALLOC_CTX *ctx, + struct dptr_struct *dptr, + long *poffset, + SMB_STRUCT_STAT *pst) { struct smb_filename smb_fname_base; char *name = NULL; @@ -809,8 +771,7 @@ char *dptr_ReadDirName(TALLOC_CTX *ctx, return NULL; /* Create an smb_filename with stream_name == NULL. */ - ZERO_STRUCT(smb_fname_base); - smb_fname_base.base_name = pathreal; + smb_fname_base = (struct smb_filename) { .base_name = pathreal }; if (SMB_VFS_STAT(dptr->conn, &smb_fname_base) == 0) { *pst = smb_fname_base.st; @@ -895,6 +856,76 @@ void dptr_init_search_op(struct dptr_struct *dptr) SMB_VFS_INIT_SEARCH_OP(dptr->conn, dptr->dir_hnd->dir); } +/**************************************************************************** + Map a native directory offset to a 32-bit cookie. +****************************************************************************/ + +static uint32_t map_dir_offset_to_wire(struct dptr_struct *dptr, long offset) +{ + DATA_BLOB key; + DATA_BLOB val; + + if (offset == END_OF_DIRECTORY_OFFSET) { + return WIRE_END_OF_DIRECTORY_OFFSET; + } else if(offset == START_OF_DIRECTORY_OFFSET) { + return WIRE_START_OF_DIRECTORY_OFFSET; + } else if (offset == DOT_DOT_DIRECTORY_OFFSET) { + return WIRE_DOT_DOT_DIRECTORY_OFFSET; + } + if (sizeof(long) == 4) { + /* 32-bit machine. We can cheat... */ + return (uint32_t)offset; + } + if (dptr->dptr_cache == NULL) { + /* Lazy initialize cache. */ + dptr->dptr_cache = memcache_init(dptr, 0); + if (dptr->dptr_cache == NULL) { + return WIRE_END_OF_DIRECTORY_OFFSET; + } + } else { + /* Have we seen this offset before ? */ + key.data = (void *)&offset; + key.length = sizeof(offset); + if (memcache_lookup(dptr->dptr_cache, + SMB1_SEARCH_OFFSET_MAP, + key, + &val)) { + uint32_t wire_offset; + SMB_ASSERT(val.length == sizeof(wire_offset)); + memcpy(&wire_offset, val.data, sizeof(wire_offset)); + DEBUG(10,("found wire %u <-> offset %ld\n", + (unsigned int)wire_offset, + (long)offset)); + return wire_offset; + } + } + /* Allocate a new wire cookie. */ + do { + dptr->counter++; + } while (dptr->counter == WIRE_START_OF_DIRECTORY_OFFSET || + dptr->counter == WIRE_END_OF_DIRECTORY_OFFSET || + dptr->counter == WIRE_DOT_DOT_DIRECTORY_OFFSET); + /* Store it in the cache. */ + key.data = (void *)&offset; + key.length = sizeof(offset); + val.data = (void *)&dptr->counter; + val.length = sizeof(dptr->counter); /* MUST BE uint32_t ! */ + memcache_add(dptr->dptr_cache, + SMB1_SEARCH_OFFSET_MAP, + key, + val); + /* And the reverse mapping for lookup from + map_wire_to_dir_offset(). */ + memcache_add(dptr->dptr_cache, + SMB1_SEARCH_OFFSET_MAP, + val, + key); + DEBUG(10,("stored wire %u <-> offset %ld\n", + (unsigned int)dptr->counter, + (long)offset)); + return dptr->counter; +} + /**************************************************************************** Fill the 5 byte server reserved dptr field. ****************************************************************************/ @@ -904,19 +935,61 @@ bool dptr_fill(struct smbd_server_connection *sconn, { unsigned char *buf = (unsigned char *)buf1; struct dptr_struct *dptr = dptr_get(sconn, key, false); - uint32 offset; + uint32_t wire_offset; if (!dptr) { DEBUG(1,("filling null dirptr %d\n",key)); return(False); } - offset = (uint32)TellDir(dptr->dir_hnd); + wire_offset = map_dir_offset_to_wire(dptr,TellDir(dptr->dir_hnd)); DEBUG(6,("fill on key %u dirptr 0x%lx now at %d\n",key, - (long)dptr->dir_hnd,(int)offset)); + (long)dptr->dir_hnd,(int)wire_offset)); buf[0] = key; - SIVAL(buf,1,offset); + SIVAL(buf,1,wire_offset); return(True); } +/**************************************************************************** + Map a 32-bit wire cookie to a native directory offset. +****************************************************************************/ + +static long map_wire_to_dir_offset(struct dptr_struct *dptr, uint32_t wire_offset) +{ + DATA_BLOB key; + DATA_BLOB val; + + if (wire_offset == WIRE_END_OF_DIRECTORY_OFFSET) { + return END_OF_DIRECTORY_OFFSET; + } else if(wire_offset == WIRE_START_OF_DIRECTORY_OFFSET) { + return START_OF_DIRECTORY_OFFSET; + } else if (wire_offset == WIRE_DOT_DOT_DIRECTORY_OFFSET) { + return DOT_DOT_DIRECTORY_OFFSET; + } + if (sizeof(long) == 4) { + /* 32-bit machine. We can cheat... */ + return (long)wire_offset; + } + if (dptr->dptr_cache == NULL) { + /* Logic error, cache should be initialized. */ + return END_OF_DIRECTORY_OFFSET; + } + key.data = (void *)&wire_offset; + key.length = sizeof(wire_offset); + if (memcache_lookup(dptr->dptr_cache, + SMB1_SEARCH_OFFSET_MAP, + key, + &val)) { + /* Found mapping. */ + long offset; + SMB_ASSERT(val.length == sizeof(offset)); + memcpy(&offset, val.data, sizeof(offset)); + DEBUG(10,("lookup wire %u <-> offset %ld\n", + (unsigned int)wire_offset, + (long)offset)); + return offset; + } + return END_OF_DIRECTORY_OFFSET; +} + /**************************************************************************** Fetch the dir ptr and seek it given the 5 byte server field. ****************************************************************************/ @@ -926,7 +999,7 @@ struct dptr_struct *dptr_fetch(struct smbd_server_connection *sconn, { unsigned int key = *(unsigned char *)buf; struct dptr_struct *dptr = dptr_get(sconn, key, false); - uint32 offset; + uint32_t wire_offset; long seekoff; if (!dptr) { @@ -934,12 +1007,8 @@ struct dptr_struct *dptr_fetch(struct smbd_server_connection *sconn, return(NULL); } *num = key; - offset = IVAL(buf,1); - if (offset == (uint32)-1) { - seekoff = END_OF_DIRECTORY_OFFSET; - } else { - seekoff = (long)offset; - } + wire_offset = IVAL(buf,1); + seekoff = map_wire_to_dir_offset(dptr, wire_offset); SeekDir(dptr->dir_hnd,seekoff); DEBUG(3,("fetching dirptr %d for path %s at offset %d\n", key, dptr->path, (int)seekoff)); @@ -963,32 +1032,6 @@ struct dptr_struct *dptr_fetch_lanman2(struct smbd_server_connection *sconn, return(dptr); } -/**************************************************************************** - Check that a file matches a particular file type. -****************************************************************************/ - -bool dir_check_ftype(connection_struct *conn, uint32 mode, uint32 dirtype) -{ - uint32 mask; - - /* Check the "may have" search bits. */ - if (((mode & ~dirtype) & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY)) != 0) - return False; - - /* Check the "must have" bits, which are the may have bits shifted eight */ - /* If must have bit is set, the file/dir can not be returned in search unless the matching - file attribute is set */ - mask = ((dirtype >> 8) & (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM)); /* & 0x37 */ - if(mask) { - if((mask & (mode & (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM))) == mask) /* check if matching attribute present */ - return True; - else - return False; - } - - return True; -} - static bool mangle_mask_match(connection_struct *conn, const char *filename, const char *mask) @@ -1025,6 +1068,7 @@ bool smbd_dirptr_get_entry(TALLOC_CTX *ctx, connection_struct *conn = dirptr->conn; size_t slashlen; size_t pathlen; + bool dirptr_path_is_dot = ISDOT(dirptr->path); *_smb_fname = NULL; *_mode = 0; @@ -1035,7 +1079,7 @@ bool smbd_dirptr_get_entry(TALLOC_CTX *ctx, while (true) { long cur_offset; long prev_offset; - SMB_STRUCT_STAT sbuf; + SMB_STRUCT_STAT sbuf = { 0 }; char *dname = NULL; bool isdots; char *fname = NULL; @@ -1089,15 +1133,23 @@ bool smbd_dirptr_get_entry(TALLOC_CTX *ctx, return false; } - memcpy(pathreal, dirptr->path, pathlen); - pathreal[pathlen] = '/'; - memcpy(pathreal + slashlen + pathlen, dname, - talloc_get_size(dname)); + /* + * We don't want to pass ./xxx to modules below us so don't + * add the path if it is just . by itself. + */ + if (dirptr_path_is_dot) { + memcpy(pathreal, dname, talloc_get_size(dname)); + } else { + memcpy(pathreal, dirptr->path, pathlen); + pathreal[pathlen] = '/'; + memcpy(pathreal + slashlen + pathlen, dname, + talloc_get_size(dname)); + } /* Create smb_fname with NULL stream_name. */ - ZERO_STRUCT(smb_fname); - smb_fname.base_name = pathreal; - smb_fname.st = sbuf; + smb_fname = (struct smb_filename) { + .base_name = pathreal, .st = sbuf + }; ok = mode_fn(ctx, private_data, &smb_fname, &mode); if (!ok) { @@ -1107,7 +1159,7 @@ bool smbd_dirptr_get_entry(TALLOC_CTX *ctx, continue; } - if (!dir_check_ftype(conn, mode, dirtype)) { + if (!dir_check_ftype(mode, dirtype)) { DEBUG(5,("[%s] attribs 0x%x didn't match 0x%x\n", fname, (unsigned int)mode, (unsigned int)dirtype)); TALLOC_FREE(dname); @@ -1170,6 +1222,30 @@ static bool smbd_dirptr_8_3_match_fn(TALLOC_CTX *ctx, mangle_mask_match(conn, dname, mask)) { char mname[13]; const char *fname; + /* + * Ensure we can push the original name as UCS2. If + * not, then just don't return this name. + */ + NTSTATUS status; + size_t ret_len = 0; + size_t len = (strlen(dname) + 2) * 4; /* Allow enough space. */ + uint8_t *tmp = talloc_array(talloc_tos(), + uint8_t, + len); + + status = srvstr_push(NULL, + FLAGS2_UNICODE_STRINGS, + tmp, + dname, + len, + STR_TERMINATE, + &ret_len); + + TALLOC_FREE(tmp); + + if (!NT_STATUS_IS_OK(status)) { + return false; + } if (!mangle_is_8_3(dname, false, conn->params)) { bool ok = name_to_8_3(dname, mname, false, @@ -1267,6 +1343,15 @@ bool get_dir_entry(TALLOC_CTX *ctx, static bool user_can_read_file(connection_struct *conn, struct smb_filename *smb_fname) { + NTSTATUS status; + uint32_t rejected_share_access = 0; + uint32_t rejected_mask = 0; + struct security_descriptor *sd = NULL; + uint32_t access_mask = FILE_READ_DATA| + FILE_READ_EA| + FILE_READ_ATTRIBUTES| + SEC_STD_READ_CONTROL; + /* * Never hide files from the root user. * We use (uid_t)0 here not sec_initial_uid() @@ -1277,10 +1362,59 @@ static bool user_can_read_file(connection_struct *conn, return True; } - return NT_STATUS_IS_OK(smbd_check_access_rights(conn, - smb_fname, + /* + * We can't directly use smbd_check_access_rights() + * here, as this implicitly grants FILE_READ_ATTRIBUTES + * which the Windows access-based-enumeration code + * explicitly checks for on the file security descriptor. + * See bug: + * + * https://bugzilla.samba.org/show_bug.cgi?id=10252 + * + * and the smb2.acl2.ACCESSBASED test for details. + */ + + rejected_share_access = access_mask & ~(conn->share_access); + if (rejected_share_access) { + DEBUG(10, ("rejected share access 0x%x " + "on %s (0x%x)\n", + (unsigned int)access_mask, + smb_fname_str_dbg(smb_fname), + (unsigned int)rejected_share_access )); + return false; + } + + status = SMB_VFS_GET_NT_ACL(conn, + smb_fname->base_name, + (SECINFO_OWNER | + SECINFO_GROUP | + SECINFO_DACL), + talloc_tos(), + &sd); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("Could not get acl " + "on %s: %s\n", + smb_fname_str_dbg(smb_fname), + nt_errstr(status))); + return false; + } + + status = se_file_access_check(sd, + get_current_nttok(conn), false, - FILE_READ_DATA)); + access_mask, + &rejected_mask); + + TALLOC_FREE(sd); + + if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) { + DEBUG(10,("rejected bits 0x%x read access for %s\n", + (unsigned int)rejected_mask, + smb_fname_str_dbg(smb_fname) )); + return false; + } + return true; } /******************************************************************* @@ -1349,8 +1483,8 @@ static bool file_is_special(connection_struct *conn, bool is_visible_file(connection_struct *conn, const char *dir_path, const char *name, SMB_STRUCT_STAT *pst, bool use_veto) { - bool hide_unreadable = lp_hideunreadable(SNUM(conn)); - bool hide_unwriteable = lp_hideunwriteable_files(SNUM(conn)); + bool hide_unreadable = lp_hide_unreadable(SNUM(conn)); + bool hide_unwriteable = lp_hide_unwriteable_files(SNUM(conn)); bool hide_special = lp_hide_special_files(SNUM(conn)); char *entry = NULL; struct smb_filename *smb_fname_base = NULL; @@ -1389,9 +1523,8 @@ bool is_visible_file(connection_struct *conn, const char *dir_path, if (SMB_VFS_STAT(conn, smb_fname_base) != 0) { ret = true; goto out; - } else { - *pst = smb_fname_base->st; } + *pst = smb_fname_base->st; } /* Honour _hide unreadable_ option */ @@ -1457,7 +1590,7 @@ static int smb_Dir_destructor(struct smb_Dir *dirp) struct smb_Dir *OpenDir(TALLOC_CTX *mem_ctx, connection_struct *conn, const char *name, const char *mask, - uint32 attr) + uint32_t attr) { struct smb_Dir *dirp = talloc_zero(mem_ctx, struct smb_Dir); struct smbd_server_connection *sconn = conn->sconn; @@ -1501,7 +1634,7 @@ struct smb_Dir *OpenDir(TALLOC_CTX *mem_ctx, connection_struct *conn, static struct smb_Dir *OpenDir_fsp(TALLOC_CTX *mem_ctx, connection_struct *conn, files_struct *fsp, const char *mask, - uint32 attr) + uint32_t attr) { struct smb_Dir *dirp = talloc_zero(mem_ctx, struct smb_Dir); struct smbd_server_connection *sconn = conn->sconn; @@ -1585,14 +1718,16 @@ const char *ReadDirName(struct smb_Dir *dirp, long *poffset, dirp->file_number++; *ptalloced = NULL; return n; - } else if (*poffset == END_OF_DIRECTORY_OFFSET) { + } + + if (*poffset == END_OF_DIRECTORY_OFFSET) { *poffset = dirp->offset = END_OF_DIRECTORY_OFFSET; return NULL; - } else { - /* A real offset, seek to it. */ - SeekDir(dirp, *poffset); } + /* A real offset, seek to it. */ + SeekDir(dirp, *poffset); + while ((n = vfs_readdirname(conn, dirp->dir, sbuf, &talloced))) { /* Ignore . and .. - we've already returned them. */ if (*n == '.') { @@ -1740,6 +1875,117 @@ 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, state->dirpath_len) != 0) { + /* + * Not a parent + */ + return 0; + } + + return state->fn(fid, data, state->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 = { + .fn = fn, + .private_data = private_data, + }; + 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; +} + +bool have_file_open_below(connection_struct *conn, + const struct smb_filename *name) +{ + struct have_file_open_below_state state = { + .found_one = false, + }; + 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 ? *****************************************************************/ @@ -1785,5 +2031,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; }