X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source3%2Fsmbd%2Fdir.c;h=f81206e0ecfeebde899a2d1c0a3c1779570f403d;hb=224fc03cb56b0d76f6ad7f18dd0528d6b0e57fb1;hp=ca6f8bfd8dc22a2690afaf177f6a6c559fda2aaa;hpb=582bd8071f168190e0cf1f6d1637f79ebee287c3;p=samba.git diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c index ca6f8bfd8dc..f81206e0ecf 100644 --- a/source3/smbd/dir.c +++ b/source3/smbd/dir.c @@ -19,13 +19,13 @@ */ #include "includes.h" +#include "smbd/globals.h" +#include "libcli/security/security.h" /* This module implements directory related functions for Samba. */ -extern struct current_user current_user; - /* "Special" directory offsets. */ #define END_OF_DIRECTORY_OFFSET ((long)-1) #define START_OF_DIRECTORY_OFFSET ((long)0) @@ -63,9 +63,6 @@ struct dptr_struct { bool did_stat; /* Optimisation for non-wcard searches. */ }; -static struct bitmap *dptr_bmap; -static struct dptr_struct *dirptrs; -static int dirhandles_open = 0; #define INVALID_DPTR_KEY (-3) @@ -119,19 +116,20 @@ bool make_dir_struct(TALLOC_CTX *ctx, Initialise the dir bitmap. ****************************************************************************/ -void init_dptrs(void) +bool init_dptrs(struct smbd_server_connection *sconn) { - static bool dptrs_init=False; - - if (dptrs_init) - return; + if (sconn->smb1.searches.dptr_bmap) { + return true; + } - dptr_bmap = bitmap_allocate(MAX_DIRECTORY_HANDLES); + sconn->smb1.searches.dptr_bmap = bitmap_talloc( + sconn, MAX_DIRECTORY_HANDLES); - if (!dptr_bmap) - exit_server("out of memory in init_dptrs"); + if (sconn->smb1.searches.dptr_bmap == NULL) { + return false; + } - dptrs_init = True; + return true; } /**************************************************************************** @@ -150,15 +148,14 @@ static void dptr_idle(struct dptr_struct *dptr) Idle the oldest dptr. ****************************************************************************/ -static void dptr_idleoldest(void) +static void dptr_idleoldest(struct smbd_server_connection *sconn) { struct dptr_struct *dptr; /* * Go to the end of the list. */ - for(dptr = dirptrs; dptr && dptr->next; dptr = dptr->next) - ; + dptr = DLIST_TAIL(sconn->smb1.searches.dirptrs); if(!dptr) { DEBUG(0,("No dptrs available to idle ?\n")); @@ -169,7 +166,7 @@ static void dptr_idleoldest(void) * Idle the oldest pointer. */ - for(; dptr; dptr = dptr->prev) { + for(; dptr; dptr = DLIST_PREV(dptr)) { if (dptr->dir_hnd) { dptr_idle(dptr); return; @@ -181,15 +178,16 @@ static void dptr_idleoldest(void) Get the struct dptr_struct for a dir index. ****************************************************************************/ -static struct dptr_struct *dptr_get(int key, bool forclose) +static struct dptr_struct *dptr_get(struct smbd_server_connection *sconn, + int key, bool forclose) { struct dptr_struct *dptr; - for(dptr = dirptrs; dptr; dptr = dptr->next) { + for(dptr = sconn->smb1.searches.dirptrs; dptr; dptr = dptr->next) { if(dptr->dnum == key) { if (!forclose && !dptr->dir_hnd) { - if (dirhandles_open >= MAX_OPEN_DIRECTORIES) - dptr_idleoldest(); + if (sconn->smb1.searches.dirhandles_open >= MAX_OPEN_DIRECTORIES) + dptr_idleoldest(sconn); DEBUG(4,("dptr_get: Reopening dptr key %d\n",key)); if (!(dptr->dir_hnd = OpenDir( NULL, dptr->conn, dptr->path, @@ -199,7 +197,7 @@ static struct dptr_struct *dptr_get(int key, bool forclose) return False; } } - DLIST_PROMOTE(dirptrs,dptr); + DLIST_PROMOTE(sconn->smb1.searches.dirptrs,dptr); return dptr; } } @@ -210,9 +208,9 @@ static struct dptr_struct *dptr_get(int key, bool forclose) Get the dir path for a dir index. ****************************************************************************/ -char *dptr_path(int key) +char *dptr_path(struct smbd_server_connection *sconn, int key) { - struct dptr_struct *dptr = dptr_get(key, False); + struct dptr_struct *dptr = dptr_get(sconn, key, false); if (dptr) return(dptr->path); return(NULL); @@ -222,9 +220,9 @@ char *dptr_path(int key) Get the dir wcard for a dir index. ****************************************************************************/ -char *dptr_wcard(int key) +char *dptr_wcard(struct smbd_server_connection *sconn, int key) { - struct dptr_struct *dptr = dptr_get(key, False); + struct dptr_struct *dptr = dptr_get(sconn, key, false); if (dptr) return(dptr->wcard); return(NULL); @@ -234,9 +232,9 @@ char *dptr_wcard(int key) Get the dir attrib for a dir index. ****************************************************************************/ -uint16 dptr_attr(int key) +uint16 dptr_attr(struct smbd_server_connection *sconn, int key) { - struct dptr_struct *dptr = dptr_get(key, False); + struct dptr_struct *dptr = dptr_get(sconn, key, false); if (dptr) return(dptr->attr); return(0); @@ -248,22 +246,29 @@ uint16 dptr_attr(int key) static void dptr_close_internal(struct dptr_struct *dptr) { + struct smbd_server_connection *sconn = dptr->conn->sconn; + DEBUG(4,("closing dptr key %d\n",dptr->dnum)); - DLIST_REMOVE(dirptrs, dptr); + if (sconn == NULL) { + goto done; + } + + DLIST_REMOVE(sconn->smb1.searches.dirptrs, dptr); /* * Free the dnum in the bitmap. Remember the dnum value is always * biased by one with respect to the bitmap. */ - if(bitmap_query( dptr_bmap, dptr->dnum - 1) != True) { + if (!bitmap_query(sconn->smb1.searches.dptr_bmap, dptr->dnum - 1)) { DEBUG(0,("dptr_close_internal : Error - closing dnum = %d and bitmap not set !\n", dptr->dnum )); } - bitmap_clear(dptr_bmap, dptr->dnum - 1); + bitmap_clear(sconn->smb1.searches.dptr_bmap, dptr->dnum - 1); +done: TALLOC_FREE(dptr->dir_hnd); /* Lanman 2 specific code */ @@ -276,7 +281,7 @@ static void dptr_close_internal(struct dptr_struct *dptr) Close a dptr given a key. ****************************************************************************/ -void dptr_close(int *key) +void dptr_close(struct smbd_server_connection *sconn, int *key) { struct dptr_struct *dptr; @@ -286,7 +291,7 @@ void dptr_close(int *key) /* OS/2 seems to use -1 to indicate "close all directories" */ if (*key == -1) { struct dptr_struct *next; - for(dptr = dirptrs; dptr; dptr = next) { + for(dptr = sconn->smb1.searches.dirptrs; dptr; dptr = next) { next = dptr->next; dptr_close_internal(dptr); } @@ -294,7 +299,7 @@ void dptr_close(int *key) return; } - dptr = dptr_get(*key, True); + dptr = dptr_get(sconn, *key, true); if (!dptr) { DEBUG(0,("Invalid key %d given to dptr_close\n", *key)); @@ -313,10 +318,17 @@ void dptr_close(int *key) void dptr_closecnum(connection_struct *conn) { struct dptr_struct *dptr, *next; - for(dptr = dirptrs; dptr; dptr = next) { + struct smbd_server_connection *sconn = conn->sconn; + + if (sconn == NULL) { + return; + } + + for(dptr = sconn->smb1.searches.dirptrs; dptr; dptr = next) { next = dptr->next; - if (dptr->conn == conn) + if (dptr->conn == conn) { dptr_close_internal(dptr); + } } } @@ -327,9 +339,16 @@ void dptr_closecnum(connection_struct *conn) void dptr_idlecnum(connection_struct *conn) { struct dptr_struct *dptr; - for(dptr = dirptrs; dptr; dptr = dptr->next) { - if (dptr->conn == conn && dptr->dir_hnd) + struct smbd_server_connection *sconn = conn->sconn; + + if (sconn == NULL) { + return; + } + + for(dptr = sconn->smb1.searches.dirptrs; dptr; dptr = dptr->next) { + if (dptr->conn == conn && dptr->dir_hnd) { dptr_idle(dptr); + } } } @@ -337,10 +356,11 @@ void dptr_idlecnum(connection_struct *conn) Close a dptr that matches a given path, only if it matches the spid also. ****************************************************************************/ -void dptr_closepath(char *path,uint16 spid) +void dptr_closepath(struct smbd_server_connection *sconn, + char *path,uint16 spid) { struct dptr_struct *dptr, *next; - for(dptr = dirptrs; dptr; dptr = next) { + for(dptr = sconn->smb1.searches.dirptrs; dptr; dptr = next) { next = dptr->next; if (spid == dptr->spid && strequal(dptr->path,path)) dptr_close_internal(dptr); @@ -353,14 +373,15 @@ void dptr_closepath(char *path,uint16 spid) finished with that one. ****************************************************************************/ -static void dptr_close_oldest(bool old) +static void dptr_close_oldest(struct smbd_server_connection *sconn, + bool old) { struct dptr_struct *dptr; /* * Go to the end of the list. */ - for(dptr = dirptrs; dptr && dptr->next; dptr = dptr->next) + for(dptr = sconn->smb1.searches.dirptrs; dptr && dptr->next; dptr = dptr->next) ; if(!dptr) { @@ -374,7 +395,7 @@ static void dptr_close_oldest(bool old) * one of the new dnum handles. */ - for(; dptr; dptr = dptr->prev) { + for(; dptr; dptr = DLIST_PREV(dptr)) { if ((old && (dptr->dnum < 256) && !dptr->expect_close) || (!old && (dptr->dnum > 255))) { dptr_close_internal(dptr); @@ -395,12 +416,18 @@ static void dptr_close_oldest(bool old) NTSTATUS dptr_create(connection_struct *conn, 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) { + struct smbd_server_connection *sconn = conn->sconn; struct dptr_struct *dptr = NULL; struct smb_Dir *dir_hnd; NTSTATUS status; DEBUG(5,("dptr_create dir=%s\n", path)); + if (sconn == NULL) { + DEBUG(0,("dptr_create: called with fake connection_struct\n")); + return NT_STATUS_INTERNAL_ERROR; + } + if (!wcard) { return NT_STATUS_INVALID_PARAMETER; } @@ -415,10 +442,8 @@ NTSTATUS dptr_create(connection_struct *conn, const char *path, bool old_handle, return map_nt_error_from_unix(errno); } - string_set(&conn->dirpath,path); - - if (dirhandles_open >= MAX_OPEN_DIRECTORIES) { - dptr_idleoldest(); + if (sconn->smb1.searches.dirhandles_open >= MAX_OPEN_DIRECTORIES) { + dptr_idleoldest(sconn); } dptr = SMB_MALLOC_P(struct dptr_struct); @@ -437,7 +462,7 @@ NTSTATUS dptr_create(connection_struct *conn, const char *path, bool old_handle, * value we return will fit in the range 1-255. */ - dptr->dnum = bitmap_find(dptr_bmap, 0); + dptr->dnum = bitmap_find(sconn->smb1.searches.dptr_bmap, 0); if(dptr->dnum == -1 || dptr->dnum > 254) { @@ -447,10 +472,10 @@ NTSTATUS dptr_create(connection_struct *conn, const char *path, bool old_handle, * finished with that one. */ - dptr_close_oldest(True); + dptr_close_oldest(sconn, true); /* Now try again... */ - dptr->dnum = bitmap_find(dptr_bmap, 0); + dptr->dnum = bitmap_find(sconn->smb1.searches.dptr_bmap, 0); if(dptr->dnum == -1 || dptr->dnum > 254) { DEBUG(0,("dptr_create: returned %d: Error - all old dirptrs in use ?\n", dptr->dnum)); SAFE_FREE(dptr); @@ -465,7 +490,7 @@ NTSTATUS dptr_create(connection_struct *conn, const char *path, bool old_handle, * a range that will return 256 - MAX_DIRECTORY_HANDLES. */ - dptr->dnum = bitmap_find(dptr_bmap, 255); + dptr->dnum = bitmap_find(sconn->smb1.searches.dptr_bmap, 255); if(dptr->dnum == -1 || dptr->dnum < 255) { @@ -476,10 +501,10 @@ NTSTATUS dptr_create(connection_struct *conn, const char *path, bool old_handle, * directory handles. */ - dptr_close_oldest(False); + dptr_close_oldest(sconn, false); /* Now try again... */ - dptr->dnum = bitmap_find(dptr_bmap, 255); + dptr->dnum = bitmap_find(sconn->smb1.searches.dptr_bmap, 255); if(dptr->dnum == -1 || dptr->dnum < 255) { DEBUG(0,("dptr_create: returned %d: Error - all new dirptrs in use ?\n", dptr->dnum)); @@ -490,7 +515,7 @@ NTSTATUS dptr_create(connection_struct *conn, const char *path, bool old_handle, } } - bitmap_set(dptr_bmap, dptr->dnum); + bitmap_set(sconn->smb1.searches.dptr_bmap, dptr->dnum); dptr->dnum += 1; /* Always bias the dnum by one - no zero dnums allowed. */ @@ -501,7 +526,7 @@ NTSTATUS dptr_create(connection_struct *conn, const char *path, bool old_handle, dptr->expect_close = expect_close; dptr->wcard = SMB_STRDUP(wcard); if (!dptr->wcard) { - bitmap_clear(dptr_bmap, dptr->dnum - 1); + bitmap_clear(sconn->smb1.searches.dptr_bmap, dptr->dnum - 1); SAFE_FREE(dptr); TALLOC_FREE(dir_hnd); return NT_STATUS_NO_MEMORY; @@ -514,7 +539,7 @@ NTSTATUS dptr_create(connection_struct *conn, const char *path, bool old_handle, dptr->attr = attr; - DLIST_ADD(dirptrs, dptr); + DLIST_ADD(sconn->smb1.searches.dirptrs, dptr); DEBUG(3,("creating new dirptr %d for path %s, expect_close = %d\n", dptr->dnum,path,expect_close)); @@ -529,11 +554,16 @@ NTSTATUS dptr_create(connection_struct *conn, const char *path, bool old_handle, Wrapper functions to access the lower level directory handles. ****************************************************************************/ -int dptr_CloseDir(struct dptr_struct *dptr) +void dptr_CloseDir(files_struct *fsp) { - DLIST_REMOVE(dirptrs, dptr); - TALLOC_FREE(dptr->dir_hnd); - return 0; + if (fsp->dptr) { + if (fsp->fh->fd == dirfd(fsp->dptr->dir_hnd->dir)) { + /* The call below closes the underlying fd. */ + fsp->fh->fd = -1; + } + dptr_close_internal(fsp->dptr); + fsp->dptr = NULL; + } } void dptr_SeekDir(struct dptr_struct *dptr, long offset) @@ -560,14 +590,21 @@ int dptr_dnum(struct dptr_struct *dptr) Return the next visible file name, skipping veto'd and invisible files. ****************************************************************************/ -static const char *dptr_normal_ReadDirName(struct dptr_struct *dptr, long *poffset, SMB_STRUCT_STAT *pst) +static const char *dptr_normal_ReadDirName(struct dptr_struct *dptr, + long *poffset, SMB_STRUCT_STAT *pst, + char **ptalloced) { /* Normal search for the next file. */ const char *name; - while ((name = ReadDirName(dptr->dir_hnd, poffset)) != NULL) { + char *talloced = NULL; + + while ((name = ReadDirName(dptr->dir_hnd, poffset, pst, &talloced)) + != NULL) { if (is_visible_file(dptr->conn, dptr->path, name, pst, True)) { + *ptalloced = talloced; return name; } + TALLOC_FREE(talloced); } return NULL; } @@ -576,90 +613,129 @@ static const char *dptr_normal_ReadDirName(struct dptr_struct *dptr, long *poffs Return the next visible file name, skipping veto'd and invisible files. ****************************************************************************/ -const char *dptr_ReadDirName(TALLOC_CTX *ctx, +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; + const char *name_temp = NULL; + char *talloced = NULL; + char *pathreal = NULL; + char *found_name = NULL; + int ret; + SET_STAT_INVALID(*pst); - if (dptr->has_wild) { - return dptr_normal_ReadDirName(dptr, poffset, pst); + if (dptr->has_wild || dptr->did_stat) { + name_temp = dptr_normal_ReadDirName(dptr, poffset, pst, + &talloced); + if (name_temp == NULL) { + return NULL; + } + if (talloced != NULL) { + return talloc_move(ctx, &talloced); + } + return talloc_strdup(ctx, name_temp); } - /* If poffset is -1 then we know we returned this name before and we have - no wildcards. We're at the end of the directory. */ + /* If poffset is -1 then we know we returned this name before and we + * have no wildcards. We're at the end of the directory. */ if (*poffset == END_OF_DIRECTORY_OFFSET) { return NULL; } - if (!dptr->did_stat) { - char *pathreal = NULL; - - /* We know the stored wcard contains no wildcard characters. See if we can match - with a stat call. If we can't, then set did_stat to true to - ensure we only do this once and keep searching. */ + /* We know the stored wcard contains no wildcard characters. + * See if we can match with a stat call. If we can't, then set + * did_stat to true to ensure we only do this once and keep + * searching. */ + + dptr->did_stat = true; + + /* First check if it should be visible. */ + if (!is_visible_file(dptr->conn, dptr->path, dptr->wcard, + pst, true)) + { + /* This only returns false if the file was found, but + is explicitly not visible. Set us to end of + directory, but return NULL as we know we can't ever + find it. */ + goto ret; + } - dptr->did_stat = True; + if (VALID_STAT(*pst)) { + name = talloc_strdup(ctx, dptr->wcard); + goto ret; + } - /* First check if it should be visible. */ - if (!is_visible_file(dptr->conn, dptr->path, dptr->wcard, pst, True)) { - /* This only returns False if the file was found, but - is explicitly not visible. Set us to end of directory, - but return NULL as we know we can't ever find it. */ - dptr->dir_hnd->offset = *poffset = END_OF_DIRECTORY_OFFSET; - return NULL; - } + pathreal = talloc_asprintf(ctx, + "%s/%s", + dptr->path, + dptr->wcard); + if (!pathreal) + return NULL; - if (VALID_STAT(*pst)) { - /* We need to set the underlying dir_hnd offset to -1 also as - this function is usually called with the output from TellDir. */ - dptr->dir_hnd->offset = *poffset = END_OF_DIRECTORY_OFFSET; - return dptr->wcard; - } + /* Create an smb_filename with stream_name == NULL. */ + ZERO_STRUCT(smb_fname_base); + smb_fname_base.base_name = pathreal; - pathreal = talloc_asprintf(ctx, - "%s/%s", - dptr->path, - dptr->wcard); - if (!pathreal) { - return NULL; + if (SMB_VFS_STAT(dptr->conn, &smb_fname_base) == 0) { + *pst = smb_fname_base.st; + name = talloc_strdup(ctx, dptr->wcard); + goto clean; + } else { + /* If we get any other error than ENOENT or ENOTDIR + then the file exists we just can't stat it. */ + if (errno != ENOENT && errno != ENOTDIR) { + name = talloc_strdup(ctx, dptr->wcard); + goto clean; } + } - if (SMB_VFS_STAT(dptr->conn,pathreal,pst) == 0) { - /* We need to set the underlying dir_hnd offset to -1 also as - this function is usually called with the output from TellDir. */ - dptr->dir_hnd->offset = *poffset = END_OF_DIRECTORY_OFFSET; - TALLOC_FREE(pathreal); - return dptr->wcard; - } else { - /* If we get any other error than ENOENT or ENOTDIR - then the file exists we just can't stat it. */ - if (errno != ENOENT && errno != ENOTDIR) { - /* We need to set the underlying dir_hdn offset to -1 also as - this function is usually called with the output from TellDir. */ - dptr->dir_hnd->offset = *poffset = END_OF_DIRECTORY_OFFSET; - TALLOC_FREE(pathreal); - return dptr->wcard; - } - } + /* Stat failed. We know this is authoratiative if we are + * providing case sensitive semantics or the underlying + * filesystem is case sensitive. + */ + if (dptr->conn->case_sensitive || + !(dptr->conn->fs_capabilities & FILE_CASE_SENSITIVE_SEARCH)) + { + goto clean; + } - TALLOC_FREE(pathreal); + /* + * Try case-insensitive stat if the fs has the ability. This avoids + * scanning the whole directory. + */ + ret = SMB_VFS_GET_REAL_FILENAME(dptr->conn, dptr->path, dptr->wcard, + ctx, &found_name); + if (ret == 0) { + name = found_name; + goto clean; + } else if (errno == ENOENT) { + /* The case-insensitive lookup was authoritative. */ + goto clean; + } - /* Stat failed. We know this is authoratiative if we are - * providing case sensitive semantics or the underlying - * filesystem is case sensitive. - */ + TALLOC_FREE(pathreal); - if (dptr->conn->case_sensitive || - !(dptr->conn->fs_capabilities & FILE_CASE_SENSITIVE_SEARCH)) { - /* We need to set the underlying dir_hnd offset to -1 also as - this function is usually called with the output from TellDir. */ - dptr->dir_hnd->offset = *poffset = END_OF_DIRECTORY_OFFSET; - return NULL; - } + name_temp = dptr_normal_ReadDirName(dptr, poffset, pst, &talloced); + if (name_temp == NULL) { + return NULL; + } + if (talloced != NULL) { + return talloc_move(ctx, &talloced); } - return dptr_normal_ReadDirName(dptr, poffset, pst); + return talloc_strdup(ctx, name_temp); + +clean: + TALLOC_FREE(pathreal); +ret: + /* We need to set the underlying dir_hnd offset to -1 + * also as this function is usually called with the + * output from TellDir. */ + dptr->dir_hnd->offset = *poffset = END_OF_DIRECTORY_OFFSET; + return name; } /**************************************************************************** @@ -688,14 +764,23 @@ void dptr_DirCacheAdd(struct dptr_struct *dptr, const char *name, long offset) DirCacheAdd(dptr->dir_hnd, name, offset); } +/**************************************************************************** + Initialize variables & state data at the beginning of all search SMB requests. +****************************************************************************/ +void dptr_init_search_op(struct dptr_struct *dptr) +{ + SMB_VFS_INIT_SEARCH_OP(dptr->conn, dptr->dir_hnd->dir); +} + /**************************************************************************** Fill the 5 byte server reserved dptr field. ****************************************************************************/ -bool dptr_fill(char *buf1,unsigned int key) +bool dptr_fill(struct smbd_server_connection *sconn, + char *buf1,unsigned int key) { unsigned char *buf = (unsigned char *)buf1; - struct dptr_struct *dptr = dptr_get(key, False); + struct dptr_struct *dptr = dptr_get(sconn, key, false); uint32 offset; if (!dptr) { DEBUG(1,("filling null dirptr %d\n",key)); @@ -713,10 +798,11 @@ bool dptr_fill(char *buf1,unsigned int key) Fetch the dir ptr and seek it given the 5 byte server field. ****************************************************************************/ -struct dptr_struct *dptr_fetch(char *buf,int *num) +struct dptr_struct *dptr_fetch(struct smbd_server_connection *sconn, + char *buf, int *num) { unsigned int key = *(unsigned char *)buf; - struct dptr_struct *dptr = dptr_get(key, False); + struct dptr_struct *dptr = dptr_get(sconn, key, false); uint32 offset; long seekoff; @@ -733,7 +819,7 @@ struct dptr_struct *dptr_fetch(char *buf,int *num) } SeekDir(dptr->dir_hnd,seekoff); DEBUG(3,("fetching dirptr %d for path %s at offset %d\n", - key,dptr_path(key),(int)seekoff)); + key, dptr->path, (int)seekoff)); return(dptr); } @@ -741,15 +827,16 @@ struct dptr_struct *dptr_fetch(char *buf,int *num) Fetch the dir ptr. ****************************************************************************/ -struct dptr_struct *dptr_fetch_lanman2(int dptr_num) +struct dptr_struct *dptr_fetch_lanman2(struct smbd_server_connection *sconn, + int dptr_num) { - struct dptr_struct *dptr = dptr_get(dptr_num, False); + struct dptr_struct *dptr = dptr_get(sconn, dptr_num, false); if (!dptr) { DEBUG(3,("fetched null dirptr %d\n",dptr_num)); return(NULL); } - DEBUG(3,("fetching dirptr %d for path %s\n",dptr_num,dptr_path(dptr_num))); + DEBUG(3,("fetching dirptr %d for path %s\n",dptr_num,dptr->path)); return(dptr); } @@ -791,174 +878,271 @@ static bool mangle_mask_match(connection_struct *conn, return mask_match_search(mname,mask,False); } -/**************************************************************************** - Get an 8.3 directory entry. -****************************************************************************/ - -bool get_dir_entry(TALLOC_CTX *ctx, - connection_struct *conn, - const char *mask, - uint32 dirtype, - char **pp_fname_out, - SMB_OFF_T *size, - uint32 *mode, - time_t *date, - bool check_descend) +bool smbd_dirptr_get_entry(TALLOC_CTX *ctx, + struct dptr_struct *dirptr, + const char *mask, + uint32_t dirtype, + bool dont_descend, + bool ask_sharemode, + bool (*match_fn)(TALLOC_CTX *ctx, + void *private_data, + const char *dname, + const char *mask, + char **_fname), + bool (*mode_fn)(TALLOC_CTX *ctx, + void *private_data, + struct smb_filename *smb_fname, + uint32_t *_mode), + void *private_data, + char **_fname, + struct smb_filename **_smb_fname, + uint32_t *_mode, + long *_prev_offset) { - const char *dname = NULL; - bool found = False; - SMB_STRUCT_STAT sbuf; - char *pathreal = NULL; - const char *filename = NULL; + connection_struct *conn = dirptr->conn; bool needslash; - *pp_fname_out = NULL; + *_smb_fname = NULL; + *_mode = 0; - needslash = ( conn->dirpath[strlen(conn->dirpath) -1] != '/'); + needslash = ( dirptr->path[strlen(dirptr->path) -1] != '/'); - if (!conn->dirptr) { - return(False); - } + while (true) { + long cur_offset; + long prev_offset; + SMB_STRUCT_STAT sbuf; + char *dname = NULL; + bool isdots; + char *fname = NULL; + char *pathreal = NULL; + struct smb_filename smb_fname; + uint32_t mode = 0; + bool ok; + NTSTATUS status; - while (!found) { - long curoff = dptr_TellDir(conn->dirptr); - dname = dptr_ReadDirName(ctx, conn->dirptr, &curoff, &sbuf); + cur_offset = dptr_TellDir(dirptr); + prev_offset = cur_offset; + dname = dptr_ReadDirName(ctx, dirptr, &cur_offset, &sbuf); - DEBUG(6,("readdir on dirptr 0x%lx now at offset %ld\n", - (long)conn->dirptr,TellDir(conn->dirptr->dir_hnd))); + DEBUG(6,("smbd_dirptr_get_entry: dirptr 0x%lx now at offset %ld\n", + (long)dirptr, cur_offset)); if (dname == NULL) { - return(False); + return false; } - filename = dname; - - /* notice the special *.* handling. This appears to be the only difference - between the wildcard handling in this routine and in the trans2 routines. - see masktest for a demo - */ - if ((strcmp(mask,"*.*") == 0) || - mask_match_search(filename,mask,False) || - mangle_mask_match(conn,filename,mask)) { - char mname[13]; - - if (!mangle_is_8_3(filename, False, conn->params)) { - if (!name_to_8_3(filename,mname,False, - conn->params)) { - continue; - } - filename = mname; - } + isdots = (ISDOT(dname) || ISDOTDOT(dname)); + if (dont_descend && !isdots) { + TALLOC_FREE(dname); + continue; + } - if (needslash) { - pathreal = talloc_asprintf(ctx, - "%s/%s", - conn->dirpath, - dname); - } else { - pathreal = talloc_asprintf(ctx, - "%s%s", - conn->dirpath, - dname); - } - if (!pathreal) { - return False; - } + /* + * fname may get mangled, dname is never mangled. + * Whenever we're accessing the filesystem we use + * pathreal which is composed from dname. + */ - if (!VALID_STAT(sbuf) && (SMB_VFS_STAT(conn, pathreal, &sbuf)) != 0) { - DEBUG(5,("Couldn't stat 1 [%s]. Error = %s\n", - pathreal, strerror(errno) )); - TALLOC_FREE(pathreal); - continue; - } + ok = match_fn(ctx, private_data, dname, mask, &fname); + if (!ok) { + TALLOC_FREE(dname); + continue; + } - *mode = dos_mode(conn,pathreal,&sbuf); + pathreal = talloc_asprintf(ctx, "%s%s%s", + dirptr->path, + needslash?"/":"", + dname); + if (!pathreal) { + TALLOC_FREE(dname); + TALLOC_FREE(fname); + return false; + } - if (!dir_check_ftype(conn,*mode,dirtype)) { - DEBUG(5,("[%s] attribs 0x%x didn't match 0x%x\n",filename,(unsigned int)*mode,(unsigned int)dirtype)); - TALLOC_FREE(pathreal); - continue; - } + /* Create smb_fname with NULL stream_name. */ + ZERO_STRUCT(smb_fname); + smb_fname.base_name = pathreal; + smb_fname.st = sbuf; - *size = sbuf.st_size; - *date = sbuf.st_mtime; + ok = mode_fn(ctx, private_data, &smb_fname, &mode); + if (!ok) { + TALLOC_FREE(dname); + TALLOC_FREE(fname); + TALLOC_FREE(pathreal); + continue; + } - DEBUG(3,("get_dir_entry mask=[%s] found %s " - "fname=%s (%s)\n", - mask, - pathreal, - dname, - filename)); + if (!dir_check_ftype(conn, mode, dirtype)) { + DEBUG(5,("[%s] attribs 0x%x didn't match 0x%x\n", + fname, (unsigned int)mode, (unsigned int)dirtype)); + TALLOC_FREE(dname); + TALLOC_FREE(fname); + TALLOC_FREE(pathreal); + continue; + } - found = True; + if (ask_sharemode) { + struct timespec write_time_ts; + struct file_id fileid; - *pp_fname_out = talloc_strdup(ctx, filename); - if (!*pp_fname_out) { - return False; + fileid = vfs_file_id_from_sbuf(conn, + &smb_fname.st); + get_file_infos(fileid, 0, NULL, &write_time_ts); + if (!null_timespec(write_time_ts)) { + update_stat_ex_mtime(&smb_fname.st, + write_time_ts); } + } - DirCacheAdd(conn->dirptr->dir_hnd, dname, curoff); - TALLOC_FREE(pathreal); + DEBUG(3,("smbd_dirptr_get_entry mask=[%s] found %s " + "fname=%s (%s)\n", + mask, smb_fname_str_dbg(&smb_fname), + dname, fname)); + + DirCacheAdd(dirptr->dir_hnd, dname, cur_offset); + + TALLOC_FREE(dname); + + status = copy_smb_filename(ctx, &smb_fname, _smb_fname); + TALLOC_FREE(pathreal); + if (!NT_STATUS_IS_OK(status)) { + return false; } + *_fname = fname; + *_mode = mode; + *_prev_offset = prev_offset; + + return true; } - return(found); + return false; } -/******************************************************************* - Check to see if a user can read a file. This is only approximate, - it is used as part of the "hide unreadable" option. Don't - use it for anything security sensitive. -********************************************************************/ +/**************************************************************************** + Get an 8.3 directory entry. +****************************************************************************/ -static bool user_can_read_file(connection_struct *conn, char *name, SMB_STRUCT_STAT *pst) +static bool smbd_dirptr_8_3_match_fn(TALLOC_CTX *ctx, + void *private_data, + const char *dname, + const char *mask, + char **_fname) { - SEC_DESC *psd = NULL; - files_struct *fsp; - NTSTATUS status; - uint32 access_granted; + connection_struct *conn = (connection_struct *)private_data; + + if ((strcmp(mask,"*.*") == 0) || + mask_match_search(dname, mask, false) || + mangle_mask_match(conn, dname, mask)) { + char mname[13]; + const char *fname; + + if (!mangle_is_8_3(dname, false, conn->params)) { + bool ok = name_to_8_3(dname, mname, false, + conn->params); + if (!ok) { + return false; + } + fname = mname; + } else { + fname = dname; + } - /* - * If user is a member of the Admin group - * we never hide files from them. - */ + *_fname = talloc_strdup(ctx, fname); + if (*_fname == NULL) { + return false; + } - if (conn->admin_user) { - return True; + return true; } - SMB_ASSERT(VALID_STAT(*pst)); - - /* Pseudo-open the file (note - no fd's created). */ + return false; +} - if(S_ISDIR(pst->st_mode)) { - status = open_directory(conn, NULL, name, pst, - READ_CONTROL_ACCESS, - FILE_SHARE_READ|FILE_SHARE_WRITE, - FILE_OPEN, - 0, /* no create options. */ - FILE_ATTRIBUTE_DIRECTORY, - NULL, &fsp); - } else { - status = open_file_stat(conn, NULL, name, pst, &fsp); +static bool smbd_dirptr_8_3_mode_fn(TALLOC_CTX *ctx, + void *private_data, + struct smb_filename *smb_fname, + uint32_t *_mode) +{ + connection_struct *conn = (connection_struct *)private_data; + + if (!VALID_STAT(smb_fname->st)) { + if ((SMB_VFS_STAT(conn, smb_fname)) != 0) { + DEBUG(5,("smbd_dirptr_8_3_mode_fn: " + "Couldn't stat [%s]. Error " + "= %s\n", + smb_fname_str_dbg(smb_fname), + strerror(errno))); + return false; + } } - if (!NT_STATUS_IS_OK(status)) { - return False; + *_mode = dos_mode(conn, smb_fname); + return true; +} + +bool get_dir_entry(TALLOC_CTX *ctx, + struct dptr_struct *dirptr, + const char *mask, + uint32_t dirtype, + char **_fname, + SMB_OFF_T *_size, + uint32_t *_mode, + struct timespec *_date, + bool check_descend, + bool ask_sharemode) +{ + connection_struct *conn = dirptr->conn; + char *fname = NULL; + struct smb_filename *smb_fname = NULL; + uint32_t mode = 0; + long prev_offset; + bool ok; + + ok = smbd_dirptr_get_entry(ctx, + dirptr, + mask, + dirtype, + check_descend, + ask_sharemode, + smbd_dirptr_8_3_match_fn, + smbd_dirptr_8_3_mode_fn, + conn, + &fname, + &smb_fname, + &mode, + &prev_offset); + if (!ok) { + return false; } - /* Get NT ACL -allocated in main loop talloc context. No free needed here. */ - status = SMB_VFS_FGET_NT_ACL(fsp, - (OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION), &psd); - close_file(fsp, NORMAL_CLOSE); + *_fname = talloc_move(ctx, &fname); + *_size = smb_fname->st.st_ex_size; + *_mode = mode; + *_date = smb_fname->st.st_ex_mtime; + TALLOC_FREE(smb_fname); + return true; +} - /* No access if SD get failed. */ - if (!NT_STATUS_IS_OK(status)) { - return False; +/******************************************************************* + Check to see if a user can read a file. This is only approximate, + it is used as part of the "hide unreadable" option. Don't + use it for anything security sensitive. +********************************************************************/ + +static bool user_can_read_file(connection_struct *conn, + struct smb_filename *smb_fname) +{ + /* + * Never hide files from the root user. + * We use (uid_t)0 here not sec_initial_uid() + * as make test uses a single user context. + */ + + if (get_current_uid(conn) == (uid_t)0) { + return True; } - return se_access_check(psd, current_user.nt_user_token, FILE_READ_DATA, - &access_granted, &status); + return can_access_file_acl(conn, smb_fname, FILE_READ_DATA); } /******************************************************************* @@ -968,93 +1152,72 @@ static bool user_can_read_file(connection_struct *conn, char *name, SMB_STRUCT_S use it for anything security sensitive. ********************************************************************/ -static bool user_can_write_file(connection_struct *conn, char *name, SMB_STRUCT_STAT *pst) +static bool user_can_write_file(connection_struct *conn, + const struct smb_filename *smb_fname) { - SEC_DESC *psd = NULL; - files_struct *fsp; - int info; - NTSTATUS status; - uint32 access_granted; - /* - * If user is a member of the Admin group - * we never hide files from them. + * Never hide files from the root user. + * We use (uid_t)0 here not sec_initial_uid() + * as make test uses a single user context. */ - if (conn->admin_user) { + if (get_current_uid(conn) == (uid_t)0) { return True; } - SMB_ASSERT(VALID_STAT(*pst)); + SMB_ASSERT(VALID_STAT(smb_fname->st)); /* Pseudo-open the file */ - if(S_ISDIR(pst->st_mode)) { + if(S_ISDIR(smb_fname->st.st_ex_mode)) { return True; - } else { - status = open_file_ntcreate(conn, NULL, name, pst, - FILE_WRITE_ATTRIBUTES, - FILE_SHARE_READ|FILE_SHARE_WRITE, - FILE_OPEN, - 0, - FILE_ATTRIBUTE_NORMAL, - INTERNAL_OPEN_ONLY, - &info, &fsp); - } - - if (!NT_STATUS_IS_OK(status)) { - return False; - } - - /* Get NT ACL -allocated in main loop talloc context. No free needed here. */ - status = SMB_VFS_FGET_NT_ACL(fsp, - (OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION), &psd); - close_file(fsp, NORMAL_CLOSE); - - /* No access if SD get failed. */ - if (!NT_STATUS_IS_OK(status)) { - return False; } - return se_access_check(psd, current_user.nt_user_token, FILE_WRITE_DATA, - &access_granted, &status); + return can_write_to_file(conn, smb_fname); } /******************************************************************* Is a file a "special" type ? ********************************************************************/ -static bool file_is_special(connection_struct *conn, char *name, SMB_STRUCT_STAT *pst) +static bool file_is_special(connection_struct *conn, + const struct smb_filename *smb_fname) { /* - * If user is a member of the Admin group - * we never hide files from them. + * Never hide files from the root user. + * We use (uid_t)0 here not sec_initial_uid() + * as make test uses a single user context. */ - if (conn->admin_user) + if (get_current_uid(conn) == (uid_t)0) { return False; + } - SMB_ASSERT(VALID_STAT(*pst)); + SMB_ASSERT(VALID_STAT(smb_fname->st)); - if (S_ISREG(pst->st_mode) || S_ISDIR(pst->st_mode) || S_ISLNK(pst->st_mode)) + if (S_ISREG(smb_fname->st.st_ex_mode) || + S_ISDIR(smb_fname->st.st_ex_mode) || + S_ISLNK(smb_fname->st.st_ex_mode)) return False; return True; } /******************************************************************* - Should the file be seen by the client ? NOTE: A successful return - is no guarantee of the file's existence ... you also have to check - whether pst is valid. + Should the file be seen by the client? + NOTE: A successful return is no guarantee of the file's existence. ********************************************************************/ -bool is_visible_file(connection_struct *conn, const char *dir_path, const char *name, SMB_STRUCT_STAT *pst, bool use_veto) +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_special = lp_hide_special_files(SNUM(conn)); - - SET_STAT_INVALID(*pst); + char *entry = NULL; + struct smb_filename *smb_fname_base = NULL; + NTSTATUS status; + bool ret = false; if ((strcmp(".",name) == 0) || (strcmp("..",name) == 0)) { return True; /* . and .. are always visible. */ @@ -1067,50 +1230,63 @@ bool is_visible_file(connection_struct *conn, const char *dir_path, const char * } if (hide_unreadable || hide_unwriteable || hide_special) { - char *entry = NULL; - - if (asprintf(&entry, "%s/%s", dir_path, name) == -1) { - return False; + entry = talloc_asprintf(talloc_tos(), "%s/%s", dir_path, name); + if (!entry) { + ret = false; + goto out; } - /* If it's a dfs symlink, ignore _hide xxxx_ options */ - if (lp_host_msdfs() && - lp_msdfs_root(SNUM(conn)) && - is_msdfs_link(conn, entry, NULL)) { - SAFE_FREE(entry); - return True; + /* Create an smb_filename with stream_name == NULL. */ + status = create_synthetic_smb_fname(talloc_tos(), entry, NULL, + pst, &smb_fname_base); + if (!NT_STATUS_IS_OK(status)) { + ret = false; + goto out; } /* If the file name does not exist, there's no point checking * the configuration options. We succeed, on the basis that the * checks *might* have passed if the file was present. */ - if (SMB_VFS_STAT(conn, entry, pst) != 0) { - SAFE_FREE(entry); - return True; + if (!VALID_STAT(*pst)) { + if (SMB_VFS_STAT(conn, smb_fname_base) != 0) { + ret = true; + goto out; + } else { + *pst = smb_fname_base->st; + } } /* Honour _hide unreadable_ option */ - if (hide_unreadable && !user_can_read_file(conn, entry, pst)) { - DEBUG(10,("is_visible_file: file %s is unreadable.\n", entry )); - SAFE_FREE(entry); - return False; + if (hide_unreadable && + !user_can_read_file(conn, smb_fname_base)) { + DEBUG(10,("is_visible_file: file %s is unreadable.\n", + entry )); + ret = false; + goto out; } /* Honour _hide unwriteable_ option */ - if (hide_unwriteable && !user_can_write_file(conn, entry, pst)) { - DEBUG(10,("is_visible_file: file %s is unwritable.\n", entry )); - SAFE_FREE(entry); - return False; + if (hide_unwriteable && !user_can_write_file(conn, + smb_fname_base)) { + DEBUG(10,("is_visible_file: file %s is unwritable.\n", + entry )); + ret = false; + goto out; } /* Honour _hide_special_ option */ - if (hide_special && file_is_special(conn, entry, pst)) { - DEBUG(10,("is_visible_file: file %s is special.\n", entry )); - SAFE_FREE(entry); - return False; + if (hide_special && file_is_special(conn, smb_fname_base)) { + DEBUG(10,("is_visible_file: file %s is special.\n", + entry )); + ret = false; + goto out; } - SAFE_FREE(entry); } - return True; + + ret = true; + out: + TALLOC_FREE(smb_fname_base); + TALLOC_FREE(entry); + return ret; } static int smb_Dir_destructor(struct smb_Dir *dirp) @@ -1118,7 +1294,9 @@ static int smb_Dir_destructor(struct smb_Dir *dirp) if (dirp->dir) { SMB_VFS_CLOSEDIR(dirp->conn,dirp->dir); } - dirhandles_open--; + if (dirp->conn->sconn) { + dirp->conn->sconn->smb1.searches.dirhandles_open--; + } return 0; } @@ -1130,6 +1308,7 @@ struct smb_Dir *OpenDir(TALLOC_CTX *mem_ctx, connection_struct *conn, const char *name, const char *mask, uint32 attr) { struct smb_Dir *dirp = TALLOC_ZERO_P(mem_ctx, struct smb_Dir); + struct smbd_server_connection *sconn = conn->sconn; if (!dirp) { return NULL; @@ -1140,10 +1319,13 @@ struct smb_Dir *OpenDir(TALLOC_CTX *mem_ctx, connection_struct *conn, dirp->dir_path = talloc_strdup(dirp, name); if (!dirp->dir_path) { + errno = ENOMEM; goto fail; } - dirhandles_open++; + if (sconn) { + sconn->smb1.searches.dirhandles_open++; + } talloc_set_destructor(dirp, smb_Dir_destructor); dirp->dir = SMB_VFS_OPENDIR(conn, dirp->dir_path, mask, attr); @@ -1161,25 +1343,31 @@ struct smb_Dir *OpenDir(TALLOC_CTX *mem_ctx, connection_struct *conn, } /******************************************************************* - Read from a directory. Also return current offset. + Read from a directory. + Return directory entry, current offset, and optional stat information. Don't check for veto or invisible files. ********************************************************************/ -const char *ReadDirName(struct smb_Dir *dirp, long *poffset) +const char *ReadDirName(struct smb_Dir *dirp, long *poffset, + SMB_STRUCT_STAT *sbuf, char **ptalloced) { const char *n; + char *talloced = NULL; connection_struct *conn = dirp->conn; /* Cheat to allow . and .. to be the first entries returned. */ - if (((*poffset == START_OF_DIRECTORY_OFFSET) || (*poffset == DOT_DOT_DIRECTORY_OFFSET)) && (dirp->file_number < 2)) { + if (((*poffset == START_OF_DIRECTORY_OFFSET) || + (*poffset == DOT_DOT_DIRECTORY_OFFSET)) && (dirp->file_number < 2)) + { if (dirp->file_number == 0) { n = "."; *poffset = dirp->offset = START_OF_DIRECTORY_OFFSET; } else { - *poffset = dirp->offset = DOT_DOT_DIRECTORY_OFFSET; n = ".."; + *poffset = dirp->offset = DOT_DOT_DIRECTORY_OFFSET; } dirp->file_number++; + *ptalloced = NULL; return n; } else if (*poffset == END_OF_DIRECTORY_OFFSET) { *poffset = dirp->offset = END_OF_DIRECTORY_OFFSET; @@ -1189,18 +1377,21 @@ const char *ReadDirName(struct smb_Dir *dirp, long *poffset) SeekDir(dirp, *poffset); } - while ((n = vfs_readdirname(conn, dirp->dir))) { + while ((n = vfs_readdirname(conn, dirp->dir, sbuf, &talloced))) { /* Ignore . and .. - we've already returned them. */ if (*n == '.') { if ((n[1] == '\0') || (n[1] == '.' && n[2] == '\0')) { + TALLOC_FREE(talloced); continue; } } *poffset = dirp->offset = SMB_VFS_TELLDIR(conn, dirp->dir); + *ptalloced = talloced; dirp->file_number++; return n; } *poffset = dirp->offset = END_OF_DIRECTORY_OFFSET; + *ptalloced = NULL; return NULL; } @@ -1295,7 +1486,8 @@ void DirCacheAdd(struct smb_Dir *dirp, const char *name, long offset) bool SearchDir(struct smb_Dir *dirp, const char *name, long *poffset) { int i; - const char *entry; + const char *entry = NULL; + char *talloced = NULL; connection_struct *conn = dirp->conn; /* Search back in the name cache. */ @@ -1322,10 +1514,12 @@ bool SearchDir(struct smb_Dir *dirp, const char *name, long *poffset) SMB_VFS_REWINDDIR(conn, dirp->dir); dirp->file_number = 0; *poffset = START_OF_DIRECTORY_OFFSET; - while ((entry = ReadDirName(dirp, poffset))) { + while ((entry = ReadDirName(dirp, poffset, NULL, &talloced))) { if (conn->case_sensitive ? (strcmp(entry, name) == 0) : strequal(entry, name)) { + TALLOC_FREE(talloced); return True; } + TALLOC_FREE(talloced); } return False; } @@ -1339,7 +1533,9 @@ NTSTATUS can_delete_directory(struct connection_struct *conn, { NTSTATUS status = NT_STATUS_OK; long dirpos = 0; - const char *dname; + const char *dname = NULL; + char *talloced = NULL; + SMB_STRUCT_STAT st; struct smb_Dir *dir_hnd = OpenDir(talloc_tos(), conn, dirname, NULL, 0); @@ -1347,24 +1543,26 @@ NTSTATUS can_delete_directory(struct connection_struct *conn, return map_nt_error_from_unix(errno); } - while ((dname = ReadDirName(dir_hnd,&dirpos))) { - SMB_STRUCT_STAT st; - + while ((dname = ReadDirName(dir_hnd, &dirpos, &st, &talloced))) { /* Quick check for "." and ".." */ if (dname[0] == '.') { if (!dname[1] || (dname[1] == '.' && !dname[2])) { + TALLOC_FREE(talloced); continue; } } if (!is_visible_file(conn, dirname, dname, &st, True)) { + TALLOC_FREE(talloced); continue; } - DEBUG(10,("can_delete_directory: got name %s - can't delete\n", dname )); + DEBUG(10,("can_delete_directory: got name %s - can't delete\n", + dname )); status = NT_STATUS_DIRECTORY_NOT_EMPTY; break; } + TALLOC_FREE(talloced); TALLOC_FREE(dir_hnd); return status;