diff options
author | Jeremy Allison <jra@samba.org> | 2005-02-01 18:33:50 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 10:55:30 -0500 |
commit | 021011f90030f6f4c39c9ef27db17eec5d4536ad (patch) | |
tree | 98292958a3a2ce9f10c3afcfd622a73b2ccf0bc2 | |
parent | b75ebde786540d24de7a911ae3e55a6d796b19aa (diff) | |
download | samba-021011f90030f6f4c39c9ef27db17eec5d4536ad.tar.gz samba-021011f90030f6f4c39c9ef27db17eec5d4536ad.tar.bz2 samba-021011f90030f6f4c39c9ef27db17eec5d4536ad.zip |
r5160: First cut at refactoring of directory code to handle non-wildcard
directory match more efficiently. Passes RAW-SEARCH under valgrind but needs more
testing (which I'll do later today :-).
Jeremy.
(This used to be commit 0b04dd9d0c6d1fe02d1b5e43f203577bf5466f33)
-rw-r--r-- | source3/smbd/dir.c | 212 | ||||
-rw-r--r-- | source3/smbd/filename.c | 2 | ||||
-rw-r--r-- | source3/smbd/notify_hash.c | 12 | ||||
-rw-r--r-- | source3/smbd/reply.c | 30 | ||||
-rw-r--r-- | source3/smbd/trans2.c | 15 |
5 files changed, 191 insertions, 80 deletions
diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c index 29ef4cbe61..e9934ff49c 100644 --- a/source3/smbd/dir.c +++ b/source3/smbd/dir.c @@ -40,10 +40,6 @@ struct smb_Dir { char *dir_path; struct name_cache_entry *name_cache; unsigned int name_cache_index; - BOOL hide_unreadable; - BOOL hide_unwriteable; - BOOL hide_special; - BOOL use_veto; }; struct dptr_struct { @@ -142,7 +138,7 @@ static struct dptr_struct *dptr_get(int key, BOOL forclose) if (dptrs_open >= MAX_OPEN_DIRECTORIES) dptr_idleoldest(); DEBUG(4,("dptr_get: Reopening dptr key %d\n",key)); - if (!(dptr->dir_hnd = OpenDir(dptr->conn, dptr->path, True))) { + if (!(dptr->dir_hnd = OpenDir(dptr->conn, dptr->path))) { DEBUG(4,("dptr_get: Failed to open %s (%s)\n",dptr->path, strerror(errno))); return False; @@ -205,7 +201,11 @@ BOOL dptr_set_wcard_and_attributes(int key, const char *wcard, uint16 attr) dptr->wcard = SMB_STRDUP(wcard); if (!dptr->wcard) return False; - dptr->has_wild = ms_has_wild(wcard); + if (wcard[0] == '.' && wcard[1] == 0) { + dptr->has_wild = True; + } else { + dptr->has_wild = ms_has_wild(wcard); + } return True; } return False; @@ -378,7 +378,7 @@ int dptr_create(connection_struct *conn, pstring path, BOOL old_handle, BOOL exp if (!*dir2) dir2 = "."; - dir_hnd = OpenDir(conn, dir2, True); + dir_hnd = OpenDir(conn, dir2); if (!dir_hnd) { return (-2); } @@ -490,11 +490,6 @@ int dptr_CloseDir(struct dptr_struct *dptr) return CloseDir(dptr->dir_hnd); } -const char *dptr_ReadDirName(struct dptr_struct *dptr, long *poffset) -{ - return ReadDirName(dptr->dir_hnd, poffset); -} - void dptr_SeekDir(struct dptr_struct *dptr, long offset) { SeekDir(dptr->dir_hnd, offset); @@ -505,9 +500,90 @@ long dptr_TellDir(struct dptr_struct *dptr) return TellDir(dptr->dir_hnd); } -BOOL dptr_SearchDir(struct dptr_struct *dptr, const char *name, long *poffset, BOOL case_sensitive) +/**************************************************************************** + 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) { - return SearchDir(dptr->dir_hnd, name, poffset, case_sensitive); + /* Normal search for the next file. */ + const char *name; + while ((name = ReadDirName(dptr->dir_hnd, poffset)) != NULL) { + if (is_visible_file(dptr->conn, dptr->path, name, pst, True)) { + return name; + } + } + return NULL; +} + +/**************************************************************************** + Return the next visible file name, skipping veto'd and invisible files. +****************************************************************************/ + +const char *dptr_ReadDirName(struct dptr_struct *dptr, long *poffset, SMB_STRUCT_STAT *pst) +{ + pstring pathreal; + + ZERO_STRUCTP(pst); + if (dptr->has_wild) { + return dptr_normal_ReadDirName(dptr, poffset, pst); + } + + /* We know the stored wcard contains no wildcard characters. See if we can match + with a stat call. If we can't, then set has_wild to true to + prevent us from doing this on every call. */ + + /* First check if it should be visible. */ + if (!is_visible_file(dptr->conn, dptr->path, dptr->wcard, pst, True)) { + dptr->has_wild = True; + return dptr_normal_ReadDirName(dptr, poffset, pst); + } + + if (VALID_STAT(*pst)) { + return dptr->wcard; + } + + pstrcpy(pathreal,dptr->path); + pstrcat(pathreal,"/"); + pstrcat(pathreal,dptr->wcard); + + if (SMB_VFS_STAT(dptr->conn,pathreal,pst) == 0) { + 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) { + return dptr->wcard; + } + } + + dptr->has_wild = True; + + /* In case sensitive mode we don't search - we know if it doesn't exist + with a stat we will fail. */ + + if (dptr->conn->case_sensitive) { + return NULL; + } else { + return dptr_normal_ReadDirName(dptr, poffset, pst); + } +} + +/**************************************************************************** + Search for a file by name, skipping veto'ed and not visible files. +****************************************************************************/ + +BOOL dptr_SearchDir(struct dptr_struct *dptr, const char *name, long *poffset, SMB_STRUCT_STAT *pst) +{ + BOOL ret; + + ZERO_STRUCTP(pst); + while ((ret = SearchDir(dptr->dir_hnd, name, poffset)) == True) { + if (is_visible_file(dptr->conn, dptr->path, name, pst, True)) { + return True; + } + } + return False; } /**************************************************************************** @@ -573,7 +649,7 @@ struct dptr_struct *dptr_fetch_lanman2(int dptr_num) Check a filetype for being valid. ****************************************************************************/ -BOOL dir_check_ftype(connection_struct *conn,int mode,SMB_STRUCT_STAT *st,int dirtype) +BOOL dir_check_ftype(connection_struct *conn,int mode,int dirtype) { int mask; @@ -629,8 +705,8 @@ BOOL get_dir_entry(connection_struct *conn,char *mask,int dirtype, pstring fname return(False); while (!found) { - long curoff = TellDir(conn->dirptr->dir_hnd); - dname = ReadDirName(conn->dirptr->dir_hnd, &curoff); + long curoff = dptr_TellDir(conn->dirptr); + dname = dptr_ReadDirName(conn->dirptr, &curoff, &sbuf); DEBUG(6,("readdir on dirptr 0x%lx now at offset %ld\n", (long)conn->dirptr,TellDir(conn->dirptr->dir_hnd))); @@ -661,14 +737,14 @@ BOOL get_dir_entry(connection_struct *conn,char *mask,int dirtype, pstring fname pstrcpy(pathreal,path); pstrcat(path,fname); pstrcat(pathreal,dname); - if (SMB_VFS_STAT(conn, pathreal, &sbuf) != 0) { + if (!VALID_STAT(sbuf) && (SMB_VFS_STAT(conn, pathreal, &sbuf)) != 0) { DEBUG(5,("Couldn't stat 1 [%s]. Error = %s\n",path, strerror(errno) )); continue; } *mode = dos_mode(conn,pathreal,&sbuf); - if (!dir_check_ftype(conn,*mode,&sbuf,dirtype)) { + if (!dir_check_ftype(conn,*mode,dirtype)) { DEBUG(5,("[%s] attribs didn't match %x\n",filename,dirtype)); continue; } @@ -817,10 +893,57 @@ static BOOL file_is_special(connection_struct *conn, char *name, SMB_STRUCT_STAT } /******************************************************************* + Should the file be seen by the client ? +********************************************************************/ + +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)); + + ZERO_STRUCT(pst); + + if ((strcmp(".",name) == 0) || (strcmp("..",name) == 0)) { + return True; /* . and .. are always visible. */ + } + + /* If it's a vetoed file, pretend it doesn't even exist */ + if (use_veto && IS_VETO_PATH(conn, name)) { + return False; + } + + if (hide_unreadable || hide_unwriteable || hide_special) { + char *entry = NULL; + + if (asprintf(&entry, "%s/%s", dir_path, name) == -1) { + return False; + } + /* Honour _hide unreadable_ option */ + if (hide_unreadable && !user_can_read_file(conn, entry, pst)) { + SAFE_FREE(entry); + return False; + } + /* Honour _hide unwriteable_ option */ + if (hide_unwriteable && !user_can_write_file(conn, entry, pst)) { + SAFE_FREE(entry); + return False; + } + /* Honour _hide_special_ option */ + if (hide_special && !file_is_special(conn, entry, pst)) { + SAFE_FREE(entry); + return False; + } + SAFE_FREE(entry); + } + return True; +} + +/******************************************************************* Open a directory. ********************************************************************/ -struct smb_Dir *OpenDir(connection_struct *conn, const char *name, BOOL use_veto) +struct smb_Dir *OpenDir(connection_struct *conn, const char *name) { struct smb_Dir *dirp = SMB_MALLOC_P(struct smb_Dir); if (!dirp) { @@ -829,7 +952,6 @@ struct smb_Dir *OpenDir(connection_struct *conn, const char *name, BOOL use_veto ZERO_STRUCTP(dirp); dirp->conn = conn; - dirp->use_veto = use_veto; dirp->dir_path = SMB_STRDUP(name); if (!dirp->dir_path) { @@ -846,10 +968,6 @@ struct smb_Dir *OpenDir(connection_struct *conn, const char *name, BOOL use_veto goto fail; } - dirp->hide_unreadable = lp_hideunreadable(SNUM(conn)); - dirp->hide_unwriteable = lp_hideunwriteable_files(SNUM(conn)); - dirp->hide_special = lp_hide_special_files(SNUM(conn)); - dptrs_open++; return dirp; @@ -892,6 +1010,7 @@ int CloseDir(struct smb_Dir *dirp) /******************************************************************* Read from a directory. Also return current offset. + Don't check for veto or invisible files. ********************************************************************/ const char *ReadDirName(struct smb_Dir *dirp, long *poffset) @@ -902,40 +1021,6 @@ const char *ReadDirName(struct smb_Dir *dirp, long *poffset) SeekDir(dirp, *poffset); while ((n = vfs_readdirname(conn, dirp->dir))) { struct name_cache_entry *e; - - if (!((strcmp(".",n) == 0) || (strcmp("..",n) == 0))) { - /* If it's a vetoed file, pretend it doesn't even exist */ - if (dirp->use_veto && IS_VETO_PATH(conn, n)) { - continue; - } - - if (dirp->hide_unreadable || dirp->hide_unwriteable || dirp->hide_special) { - SMB_STRUCT_STAT st; - char *entry = NULL; - ZERO_STRUCT(st); - - if (asprintf(&entry, "%s/%s/%s", conn->origpath, dirp->dir_path, n) == -1) { - return NULL; - } - /* Honour _hide unreadable_ option */ - if (dirp->hide_unreadable && !user_can_read_file(conn, entry, &st)) { - SAFE_FREE(entry); - continue; - } - /* Honour _hide unwriteable_ option */ - if (dirp->hide_unwriteable && !user_can_write_file(conn, entry, &st)) { - SAFE_FREE(entry); - continue; - } - /* Honour _hide_special_ option */ - if (dirp->hide_special && !file_is_special(conn, entry, &st)) { - SAFE_FREE(entry); - continue; - } - SAFE_FREE(entry); - } - } - dirp->offset = SMB_VFS_TELLDIR(conn, dirp->dir); if (dirp->offset == -1) { return NULL; @@ -974,9 +1059,10 @@ long TellDir(struct smb_Dir *dirp) /******************************************************************* Find an entry by name. Leave us at the offset after it. + Don't check for veto or invisible files. ********************************************************************/ -BOOL SearchDir(struct smb_Dir *dirp, const char *name, long *poffset, BOOL case_sensitive) +BOOL SearchDir(struct smb_Dir *dirp, const char *name, long *poffset) { int i; const char *entry; @@ -985,7 +1071,7 @@ BOOL SearchDir(struct smb_Dir *dirp, const char *name, long *poffset, BOOL case_ /* Search back in the name cache. */ for (i = dirp->name_cache_index; i >= 0; i--) { struct name_cache_entry *e = &dirp->name_cache[i]; - if (e->name && (case_sensitive ? (strcmp(e->name, name) == 0) : strequal(e->name, name))) { + if (e->name && (conn->case_sensitive ? (strcmp(e->name, name) == 0) : strequal(e->name, name))) { *poffset = e->offset; SeekDir(dirp, e->offset); return True; @@ -993,7 +1079,7 @@ BOOL SearchDir(struct smb_Dir *dirp, const char *name, long *poffset, BOOL case_ } for (i = NAME_CACHE_SIZE-1; i > dirp->name_cache_index; i--) { struct name_cache_entry *e = &dirp->name_cache[i]; - if (e->name && (case_sensitive ? (strcmp(e->name, name) == 0) : strequal(e->name, name))) { + if (e->name && (conn->case_sensitive ? (strcmp(e->name, name) == 0) : strequal(e->name, name))) { *poffset = e->offset; SeekDir(dirp, e->offset); return True; @@ -1004,7 +1090,7 @@ BOOL SearchDir(struct smb_Dir *dirp, const char *name, long *poffset, BOOL case_ SMB_VFS_REWINDDIR(conn, dirp->dir); *poffset = 0; while ((entry = ReadDirName(dirp, poffset))) { - if (case_sensitive ? (strcmp(entry, name) == 0) : strequal(entry, name)) { + if (conn->case_sensitive ? (strcmp(entry, name) == 0) : strequal(entry, name)) { return True; } } diff --git a/source3/smbd/filename.c b/source3/smbd/filename.c index 9af73776eb..c04b43b873 100644 --- a/source3/smbd/filename.c +++ b/source3/smbd/filename.c @@ -454,7 +454,7 @@ static BOOL scan_directory(connection_struct *conn, const char *path, char *name mangled = !mangle_check_cache( name, maxlength ); /* open the directory */ - if (!(cur_dir = OpenDir(conn, path, True))) { + if (!(cur_dir = OpenDir(conn, path))) { DEBUG(3,("scan dir didn't open dir [%s]\n",path)); return(False); } diff --git a/source3/smbd/notify_hash.c b/source3/smbd/notify_hash.c index d7cb1139dc..b16b767783 100644 --- a/source3/smbd/notify_hash.c +++ b/source3/smbd/notify_hash.c @@ -76,7 +76,7 @@ static BOOL notify_hash(connection_struct *conn, char *path, uint32 flags, * larger than the max time_t value). */ - dp = OpenDir(conn, path, True); + dp = OpenDir(conn, path); if (dp == NULL) return False; @@ -91,18 +91,22 @@ static BOOL notify_hash(connection_struct *conn, char *path, uint32 flags, offset = 0; while ((fname = ReadDirName(dp, &offset))) { + ZERO_STRUCT(st); if(strequal(fname, ".") || strequal(fname, "..")) continue; + if (!is_visible_file(conn, path, fname, &st, True)) + continue; + data->num_entries++; safe_strcpy(p, fname, remaining_len); - ZERO_STRUCT(st); - /* * Do the stat - but ignore errors. */ - SMB_VFS_STAT(conn,full_name, &st); + if (!VALID_STAT(st)) { + SMB_VFS_STAT(conn,full_name, &st); + } /* * Always sum the times. diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c index 899dba56d8..748d9958bc 100644 --- a/source3/smbd/reply.c +++ b/source3/smbd/reply.c @@ -1610,7 +1610,7 @@ NTSTATUS unlink_internals(connection_struct *conn, int dirtype, char *name) const char *dname; if (check_name(directory,conn)) - dir_hnd = OpenDir(conn, directory, True); + dir_hnd = OpenDir(conn, directory); /* XXXX the CIFS spec says that if bit0 of the flags2 field is set then the pattern matches against the long name, otherwise the short name @@ -1625,10 +1625,15 @@ NTSTATUS unlink_internals(connection_struct *conn, int dirtype, char *name) pstrcpy(mask,"*"); while ((dname = ReadDirName(dir_hnd, &offset))) { + SMB_STRUCT_STAT st; pstring fname; BOOL sys_direntry = False; pstrcpy(fname,dname); + if (!is_visible_file(conn, directory, dname, &st, True)) { + continue; + } + /* Quick check for "." and ".." */ if (fname[0] == '.') { if (!fname[1] || (fname[1] == '.' && !fname[2])) { @@ -3368,7 +3373,7 @@ static BOOL recursive_rmdir(connection_struct *conn, char *directory) const char *dname = NULL; BOOL ret = False; long offset = 0; - struct smb_Dir *dir_hnd = OpenDir(conn, directory, False); + struct smb_Dir *dir_hnd = OpenDir(conn, directory); if(dir_hnd == NULL) return True; @@ -3380,6 +3385,9 @@ static BOOL recursive_rmdir(connection_struct *conn, char *directory) if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0)) continue; + if (!is_visible_file(conn, directory, dname, &st, False)) + continue; + /* Construct the full name. */ if(strlen(directory) + strlen(dname) + 1 >= sizeof(fullname)) { errno = ENOMEM; @@ -3421,6 +3429,7 @@ static BOOL recursive_rmdir(connection_struct *conn, char *directory) BOOL rmdir_internals(connection_struct *conn, char *directory) { BOOL ok; + SMB_STRUCT_STAT st; ok = (SMB_VFS_RMDIR(conn,directory) == 0); if(!ok && ((errno == ENOTEMPTY)||(errno == EEXIST)) && lp_veto_files(SNUM(conn))) { @@ -3432,13 +3441,15 @@ BOOL rmdir_internals(connection_struct *conn, char *directory) */ BOOL all_veto_files = True; const char *dname; - struct smb_Dir *dir_hnd = OpenDir(conn, directory, False); + struct smb_Dir *dir_hnd = OpenDir(conn, directory); if(dir_hnd != NULL) { long dirpos = TellDir(dir_hnd); while ((dname = ReadDirName(dir_hnd,&dirpos))) { if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0)) continue; + if (!is_visible_file(conn, directory, dname, &st, False)) + continue; if(!IS_VETO_PATH(conn, dname)) { all_veto_files = False; break; @@ -3449,10 +3460,11 @@ BOOL rmdir_internals(connection_struct *conn, char *directory) SeekDir(dir_hnd,dirpos); while ((dname = ReadDirName(dir_hnd,&dirpos))) { pstring fullname; - SMB_STRUCT_STAT st; if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0)) continue; + if (!is_visible_file(conn, directory, dname, &st, False)) + continue; /* Construct the full name. */ if(strlen(directory) + strlen(dname) + 1 >= sizeof(fullname)) { @@ -3988,7 +4000,7 @@ directory = %s, newname = %s, last_component_dest = %s, is_8_3 = %d\n", pstring destname; if (check_name(directory,conn)) - dir_hnd = OpenDir(conn, directory, True); + dir_hnd = OpenDir(conn, directory); if (dir_hnd) { long offset = 0; @@ -4015,6 +4027,9 @@ directory = %s, newname = %s, last_component_dest = %s, is_8_3 = %d\n", } } + if (!is_visible_file(conn, directory, dname, &sbuf1, False)) + continue; + if(!mask_match(fname, mask, conn->case_sensitive)) continue; @@ -4342,7 +4357,7 @@ int reply_copy(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, pstring destname; if (check_name(directory,conn)) - dir_hnd = OpenDir(conn, directory, True); + dir_hnd = OpenDir(conn, directory); if (dir_hnd) { long offset = 0; @@ -4355,6 +4370,9 @@ int reply_copy(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, pstring fname; pstrcpy(fname,dname); + if (!is_visible_file(conn, directory, dname, &sbuf1, False)) + continue; + if(!mask_match(fname, mask, conn->case_sensitive)) continue; diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c index d2cef6049d..b5029ccd77 100644 --- a/source3/smbd/trans2.c +++ b/source3/smbd/trans2.c @@ -864,11 +864,12 @@ static BOOL get_lanman2_dir_entry(connection_struct *conn, } else pstrcpy(mask, path_mask); + while (!found) { BOOL got_match; /* Needed if we run out of space */ long curr_dirpos = prev_dirpos = dptr_TellDir(conn->dirptr); - dname = dptr_ReadDirName(conn->dirptr,&curr_dirpos); + dname = dptr_ReadDirName(conn->dirptr,&curr_dirpos,&sbuf); /* * Due to bugs in NT client redirectors we are not using @@ -922,7 +923,7 @@ static BOOL get_lanman2_dir_entry(connection_struct *conn, pathreal,strerror(errno))); continue; } - } else if (SMB_VFS_STAT(conn,pathreal,&sbuf) != 0) { + } else if (!VALID_STAT(sbuf) && SMB_VFS_STAT(conn,pathreal,&sbuf) != 0) { /* Needed to show the msdfs symlinks as * directories */ @@ -945,7 +946,7 @@ static BOOL get_lanman2_dir_entry(connection_struct *conn, mode = dos_mode(conn,pathreal,&sbuf); - if (!dir_check_ftype(conn,mode,&sbuf,dirtype)) { + if (!dir_check_ftype(conn,mode,dirtype)) { DEBUG(5,("[%s] attribs didn't match %x\n",fname,dirtype)); continue; } @@ -1341,7 +1342,7 @@ static int call_trans2findfirst(connection_struct *conn, char *inbuf, char *outb int info_level = SVAL(params,6); pstring directory; pstring mask; - char *p, *wcard; + char *p; int last_name_off=0; int dptr_num = -1; int numentries = 0; @@ -1442,7 +1443,7 @@ close_if_end = %d requires_resume_key = %d level = 0x%x, max_data_bytes = %d\n", return ERROR_DOS(ERRDOS,ERRnomem); } - DEBUG(4,("dptr_num is %d, wcard = %s, attr = %d\n",dptr_num, wcard, dirtype)); + DEBUG(4,("dptr_num is %d, wcard = %s, attr = %d\n",dptr_num, mask, dirtype)); /* We don't need to check for VOL here as this is returned by a different TRANS2 call. */ @@ -1667,6 +1668,8 @@ resume_key = %d resume name = %s continue=%d level = %d\n", */ if(*resume_name && !continue_bit) { + SMB_STRUCT_STAT st; + long current_pos = 0; /* * Remember, mangle_map is called by @@ -1687,7 +1690,7 @@ resume_key = %d resume name = %s continue=%d level = %d\n", * should already be at the correct place. */ - finished = !dptr_SearchDir(conn->dirptr, resume_name, ¤t_pos, True); + finished = !dptr_SearchDir(conn->dirptr, resume_name, ¤t_pos, &st); } /* end if resume_name && !continue_bit */ for (i=0;(i<(int)maxentries) && !finished && !out_of_space ;i++) { |