summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source3/smbd/dir.c350
-rw-r--r--source3/smbd/filename.c4
-rw-r--r--source3/smbd/notify_hash.c4
-rw-r--r--source3/smbd/reply.c20
-rw-r--r--source3/smbd/trans2.c94
5 files changed, 251 insertions, 221 deletions
diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c
index f721bf3ba8..ff240b7e5a 100644
--- a/source3/smbd/dir.c
+++ b/source3/smbd/dir.c
@@ -612,9 +612,10 @@ BOOL get_dir_entry(connection_struct *conn,char *mask,int dirtype, pstring fname
return(False);
while (!found) {
- dname = ReadDirName(conn->dirptr);
+ long curoff = TellDir(conn->dirptr);
+ dname = ReadDirName(conn->dirptr, &curoff);
- DEBUG(6,("readdir on dirptr 0x%lx now at offset %d\n",
+ DEBUG(6,("readdir on dirptr 0x%lx now at offset %ld\n",
(long)conn->dirptr,TellDir(conn->dirptr)));
if (dname == NULL)
@@ -667,14 +668,6 @@ BOOL get_dir_entry(connection_struct *conn,char *mask,int dirtype, pstring fname
return(found);
}
-typedef struct {
- int pos;
- int numentries;
- int mallocsize;
- char *data;
- char *current;
-} Dir;
-
/*******************************************************************
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
@@ -806,117 +799,74 @@ static BOOL file_is_special(connection_struct *conn, char *name, SMB_STRUCT_STAT
return True;
}
+#define NAME_CACHE_SIZE 100
+
+struct name_cache_entry {
+ char *name;
+ long offset;
+};
+
+typedef struct {
+ connection_struct *conn;
+ DIR *dir;
+ long offset;
+ 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;
+ BOOL finished;
+} Dir;
+
/*******************************************************************
Open a directory.
********************************************************************/
void *OpenDir(connection_struct *conn, const char *name, BOOL use_veto)
{
- Dir *dirp;
- const char *n;
- DIR *p = SMB_VFS_OPENDIR(conn,name);
- int used=0;
-
- if (!p)
- return(NULL);
- dirp = SMB_MALLOC_P(Dir);
+ Dir *dirp = SMB_MALLOC_P(Dir);
if (!dirp) {
- DEBUG(0,("Out of memory in OpenDir\n"));
- SMB_VFS_CLOSEDIR(conn,p);
- return(NULL);
+ return NULL;
}
- dirp->pos = dirp->numentries = dirp->mallocsize = 0;
- dirp->data = dirp->current = NULL;
-
- while (True) {
- int l;
- BOOL normal_entry = True;
- SMB_STRUCT_STAT st;
- char *entry = NULL;
-
- if (used == 0) {
- n = ".";
- normal_entry = False;
- } else if (used == 2) {
- n = "..";
- normal_entry = False;
- } else {
- n = vfs_readdirname(conn, p);
- if (n == NULL)
- break;
- if ((strcmp(".",n) == 0) ||(strcmp("..",n) == 0))
- continue;
- normal_entry = True;
- }
+ ZERO_STRUCTP(dirp);
- ZERO_STRUCT(st);
- l = strlen(n)+1;
+ dirp->conn = conn;
+ dirp->use_veto = use_veto;
- /* If it's a vetoed file, pretend it doesn't even exist */
- if (normal_entry && use_veto && conn && IS_VETO_PATH(conn, n))
- continue;
+ dirp->dir_path = SMB_STRDUP(name);
+ if (!dirp->dir_path) {
+ goto fail;
+ }
+ dirp->dir = SMB_VFS_OPENDIR(conn, dirp->dir_path);
+ if (!dirp->dir) {
+ DEBUG(5,("OpenDir: Can't open %s. %s\n", dirp->dir_path, strerror(errno) ));
+ goto fail;
+ }
- /* Honour _hide unreadable_ option */
- if (normal_entry && conn && lp_hideunreadable(SNUM(conn))) {
- int ret=0;
-
- if (entry || asprintf(&entry, "%s/%s/%s", conn->origpath, name, n) > 0) {
- ret = user_can_read_file(conn, entry, &st);
- }
- if (!ret) {
- SAFE_FREE(entry);
- continue;
- }
- }
+ dirp->name_cache = SMB_CALLOC_ARRAY(struct name_cache_entry, NAME_CACHE_SIZE);
+ if (!dirp->name_cache) {
+ goto fail;
+ }
- /* Honour _hide unwriteable_ option */
- if (normal_entry && conn && lp_hideunwriteable_files(SNUM(conn))) {
- int ret=0;
-
- if (entry || asprintf(&entry, "%s/%s/%s", conn->origpath, name, n) > 0) {
- ret = user_can_write_file(conn, entry, &st);
- }
- if (!ret) {
- SAFE_FREE(entry);
- continue;
- }
- }
+ dirp->hide_unreadable = lp_hideunreadable(SNUM(conn));
+ dirp->hide_unwriteable = lp_hideunwriteable_files(SNUM(conn));
+ dirp->hide_special = lp_hide_special_files(SNUM(conn));
- /* Honour _hide_special_ option */
- if (normal_entry && conn && lp_hide_special_files(SNUM(conn))) {
- int ret=0;
-
- if (entry || asprintf(&entry, "%s/%s/%s", conn->origpath, name, n) > 0) {
- ret = file_is_special(conn, entry, &st);
- }
- if (ret) {
- SAFE_FREE(entry);
- continue;
- }
- }
+ return((void *)dirp);
- SAFE_FREE(entry);
+ fail:
- if (used + l > dirp->mallocsize) {
- int s = MAX(used+l,used+2000);
- char *r;
- r = (char *)SMB_REALLOC(dirp->data,s);
- if (!r) {
- DEBUG(0,("Out of memory in OpenDir\n"));
- break;
- }
- dirp->data = r;
- dirp->mallocsize = s;
- dirp->current = dirp->data;
+ if (dirp) {
+ if (dirp->dir) {
+ SMB_VFS_CLOSEDIR(conn,dirp->dir);
}
-
- safe_strcpy_base(dirp->data+used,n, dirp->data, dirp->mallocsize);
- used += l;
- dirp->numentries++;
+ SAFE_FREE(dirp->dir_path);
+ SAFE_FREE(dirp->name_cache);
+ SAFE_FREE(dirp);
}
-
- SMB_VFS_CLOSEDIR(conn,p);
- return((void *)dirp);
+ return NULL;
}
@@ -924,65 +874,195 @@ void *OpenDir(connection_struct *conn, const char *name, BOOL use_veto)
Close a directory.
********************************************************************/
-void CloseDir(void *p)
+int CloseDir(void *p)
{
- if (!p)
- return;
- SAFE_FREE(((Dir *)p)->data);
- SAFE_FREE(p);
+ int i, ret = 0;
+ Dir *dirp = (Dir *)p;
+
+ if (dirp->dir) {
+ ret = SMB_VFS_CLOSEDIR(dirp->conn,dirp->dir);
+ }
+ SAFE_FREE(dirp->dir_path);
+ if (dirp->name_cache) {
+ for (i = 0; i < NAME_CACHE_SIZE; i++) {
+ SAFE_FREE(dirp->name_cache[i].name);
+ }
+ }
+ SAFE_FREE(dirp->name_cache);
+ SAFE_FREE(dirp);
+ return ret;
}
/*******************************************************************
- Read from a directory.
+ Set a directory into an inactive state.
********************************************************************/
-const char *ReadDirName(void *p)
+static void SleepDir(Dir *dirp)
{
- char *ret;
- Dir *dirp = (Dir *)p;
-
- if (!dirp || !dirp->current || dirp->pos >= dirp->numentries)
- return(NULL);
+ if (dirp->dir) {
+ SMB_VFS_CLOSEDIR(dirp->conn,dirp->dir);
+ dirp->dir = 0;
+ }
+ dirp->offset = 0;
+}
- ret = dirp->current;
- dirp->current = skip_string(dirp->current,1);
- dirp->pos++;
+/*******************************************************************
+ Wake a directory into a known state.
+********************************************************************/
- return(ret);
+static int WakeDir(Dir *dirp, long offset)
+{
+ if (!dirp->dir) {
+ dirp->dir = SMB_VFS_OPENDIR(dirp->conn, dirp->dir_path);
+ if (!dirp->dir) {
+ DEBUG(0,("WakeDir: Can't open %s. %s\n", dirp->dir_path, strerror(errno) ));
+ dirp->finished = True;
+ return -1;
+ }
+ }
+ if (offset != dirp->offset) {
+ SMB_VFS_SEEKDIR(dirp->conn, dirp->dir, offset);
+ dirp->offset = SMB_VFS_TELLDIR(dirp->conn, dirp->dir);
+ if (dirp->offset != offset) {
+ DEBUG(0,("WakeDir: in path %s. offset changed %ld -> %ld\n",
+ dirp->dir_path, offset, dirp->offset ));
+ return -1;
+ }
+ }
+ return 0;
}
/*******************************************************************
- Seek a dir.
+ Read from a directory. Also return current offset.
********************************************************************/
-BOOL SeekDir(void *p,int pos)
+const char *ReadDirName(void *p, long *poffset)
{
+ const char *n;
Dir *dirp = (Dir *)p;
+ connection_struct *conn = dirp->conn;
- if (!dirp)
- return(False);
+ if (WakeDir(dirp, *poffset) == -1) {
+ return NULL;
+ }
+
+ 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;
+ ZERO_STRUCT(st);
+ char *entry = NULL;
+
+ 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;
+ }
+ dirp->name_cache_index = (dirp->name_cache_index+1) % NAME_CACHE_SIZE;
- if (pos < dirp->pos) {
- dirp->current = dirp->data;
- dirp->pos = 0;
+ e = &dirp->name_cache[dirp->name_cache_index];
+ SAFE_FREE(e->name);
+ e->name = SMB_STRDUP(n);
+ *poffset = e->offset= dirp->offset;
+ return e->name;
}
- while (dirp->pos < pos && ReadDirName(p))
- ;
+ dirp->finished = True;
+ SleepDir(dirp);
+ return NULL;
+}
+
+/*******************************************************************
+ Seek a dir.
+********************************************************************/
- return (dirp->pos == pos);
+BOOL SeekDir(void *p,long offset)
+{
+ Dir *dirp = (Dir *)p;
+ return (WakeDir(dirp, offset) != -1);
}
/*******************************************************************
Tell a dir position.
********************************************************************/
-int TellDir(void *p)
+long TellDir(void *p)
{
Dir *dirp = (Dir *)p;
+ return(dirp->offset);
+}
- if (!dirp)
- return(-1);
-
- return(dirp->pos);
+/*******************************************************************
+ Find an entry by name. Leave us at the offset after it.
+********************************************************************/
+
+BOOL SearchDir(void *p, const char *name, long *poffset, BOOL case_sensitive)
+{
+ int i;
+ Dir *dirp = (Dir *)p;
+ const char *entry;
+ connection_struct *conn = dirp->conn;
+
+ /* Re-create dir but don't seek. */
+ if (WakeDir(dirp, dirp->offset) == -1) {
+ return False;
+ }
+
+ /* 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))) {
+ *poffset = e->offset;
+ WakeDir(dirp, e->offset);
+ return True;
+ }
+ }
+ 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))) {
+ *poffset = e->offset;
+ WakeDir(dirp, e->offset);
+ return True;
+ }
+ }
+
+ /* Not found in the name cache. Rewind directory and start from scratch. */
+ SMB_VFS_REWINDDIR(conn, dirp->dir);
+ *poffset = 0;
+ while ((entry = ReadDirName(dirp, poffset))) {
+ if (case_sensitive ? (strcmp(entry, name) == 0) : strequal(entry, name)) {
+ return True;
+ }
+ }
+
+ SleepDir(dirp);
+ return False;
}
diff --git a/source3/smbd/filename.c b/source3/smbd/filename.c
index 279c9dd3c4..fa8ddfd6ca 100644
--- a/source3/smbd/filename.c
+++ b/source3/smbd/filename.c
@@ -435,6 +435,7 @@ static BOOL scan_directory(connection_struct *conn, const char *path, char *name
void *cur_dir;
const char *dname;
BOOL mangled;
+ long curpos;
mangled = mangle_is_mangled(name);
@@ -459,7 +460,8 @@ static BOOL scan_directory(connection_struct *conn, const char *path, char *name
}
/* now scan for matching names */
- while ((dname = ReadDirName(cur_dir))) {
+ curpos = 0;
+ while ((dname = ReadDirName(cur_dir, &curpos))) {
/* Is it dot or dot dot. */
if ((dname[0] == '.') && (!dname[1] || (dname[1] == '.' && !dname[2]))) {
diff --git a/source3/smbd/notify_hash.c b/source3/smbd/notify_hash.c
index 843580f6ed..80e8fee54a 100644
--- a/source3/smbd/notify_hash.c
+++ b/source3/smbd/notify_hash.c
@@ -45,6 +45,7 @@ static BOOL notify_hash(connection_struct *conn, char *path, uint32 flags,
size_t remaining_len;
size_t fullname_len;
void *dp;
+ long offset;
ZERO_STRUCTP(data);
@@ -88,7 +89,8 @@ static BOOL notify_hash(connection_struct *conn, char *path, uint32 flags,
remaining_len = sizeof(full_name) - fullname_len - 1;
p = &full_name[fullname_len];
- while ((fname = ReadDirName(dp))) {
+ offset = 0;
+ while ((fname = ReadDirName(dp, &offset))) {
if(strequal(fname, ".") || strequal(fname, ".."))
continue;
diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c
index 26a0c9e7a9..86ee331e6b 100644
--- a/source3/smbd/reply.c
+++ b/source3/smbd/reply.c
@@ -1614,11 +1614,12 @@ NTSTATUS unlink_internals(connection_struct *conn, int dirtype, char *name)
if (dirptr) {
error = NT_STATUS_NO_SUCH_FILE;
-
+ long offset = 0;
+
if (strequal(mask,"????????.???"))
pstrcpy(mask,"*");
- while ((dname = ReadDirName(dirptr))) {
+ while ((dname = ReadDirName(dirptr, &offset))) {
pstring fname;
BOOL sys_direntry = False;
pstrcpy(fname,dname);
@@ -3361,12 +3362,13 @@ static BOOL recursive_rmdir(connection_struct *conn, char *directory)
{
const char *dname = NULL;
BOOL ret = False;
+ long offset = 0;
void *dirptr = OpenDir(conn, directory, False);
if(dirptr == NULL)
return True;
- while((dname = ReadDirName(dirptr))) {
+ while((dname = ReadDirName(dirptr, &offset))) {
pstring fullname;
SMB_STRUCT_STAT st;
@@ -3428,8 +3430,8 @@ BOOL rmdir_internals(connection_struct *conn, char *directory)
void *dirptr = OpenDir(conn, directory, False);
if(dirptr != NULL) {
- int dirpos = TellDir(dirptr);
- while ((dname = ReadDirName(dirptr))) {
+ long dirpos = TellDir(dirptr);
+ while ((dname = ReadDirName(dirptr,&dirpos))) {
if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0))
continue;
if(!IS_VETO_PATH(conn, dname)) {
@@ -3440,7 +3442,7 @@ BOOL rmdir_internals(connection_struct *conn, char *directory)
if(all_veto_files) {
SeekDir(dirptr,dirpos);
- while ((dname = ReadDirName(dirptr))) {
+ while ((dname = ReadDirName(dirptr,&dirpos))) {
pstring fullname;
SMB_STRUCT_STAT st;
@@ -3984,13 +3986,14 @@ directory = %s, newname = %s, last_component_dest = %s, is_8_3 = %d\n",
dirptr = OpenDir(conn, directory, True);
if (dirptr) {
+ long offset = 0;
error = NT_STATUS_NO_SUCH_FILE;
/* Was error = NT_STATUS_OBJECT_NAME_NOT_FOUND; - gentest fix. JRA */
if (strequal(mask,"????????.???"))
pstrcpy(mask,"*");
- while ((dname = ReadDirName(dirptr))) {
+ while ((dname = ReadDirName(dirptr, &offset))) {
pstring fname;
BOOL sysdir_entry = False;
@@ -4337,12 +4340,13 @@ int reply_copy(connection_struct *conn, char *inbuf,char *outbuf, int dum_size,
dirptr = OpenDir(conn, directory, True);
if (dirptr) {
+ long offset = 0;
error = ERRbadfile;
if (strequal(mask,"????????.???"))
pstrcpy(mask,"*");
- while ((dname = ReadDirName(dirptr))) {
+ while ((dname = ReadDirName(dirptr, &offset))) {
pstring fname;
pstrcpy(fname,dname);
diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c
index 7269ab9157..8d3055f6cf 100644
--- a/source3/smbd/trans2.c
+++ b/source3/smbd/trans2.c
@@ -837,7 +837,7 @@ static BOOL get_lanman2_dir_entry(connection_struct *conn,
pstring fname;
char *p, *q, *pdata = *ppdata;
uint32 reskey=0;
- int prev_dirpos=0;
+ long prev_dirpos=0;
int mode=0;
SMB_OFF_T file_size = 0;
SMB_BIG_UINT allocation_size = 0;
@@ -866,10 +866,9 @@ static BOOL get_lanman2_dir_entry(connection_struct *conn,
while (!found) {
BOOL got_match;
-
/* Needed if we run out of space */
- prev_dirpos = TellDir(conn->dirptr);
- dname = ReadDirName(conn->dirptr);
+ long curr_dirpos = prev_dirpos = TellDir(conn->dirptr);
+ dname = ReadDirName(conn->dirptr,&curr_dirpos);
/*
* Due to bugs in NT client redirectors we are not using
@@ -880,8 +879,8 @@ static BOOL get_lanman2_dir_entry(connection_struct *conn,
reskey = 0;
- DEBUG(8,("get_lanman2_dir_entry:readdir on dirptr 0x%lx now at offset %d\n",
- (long)conn->dirptr,TellDir(conn->dirptr)));
+ DEBUG(8,("get_lanman2_dir_entry:readdir on dirptr 0x%lx now at offset %ld\n",
+ (long)conn->dirptr,curr_dirpos));
if (!dname)
return(False);
@@ -1649,7 +1648,7 @@ resume_key = %d resume name = %s continue=%d level = %d\n",
/* Get the attr mask from the dptr */
dirtype = dptr_attr(dptr_num);
- DEBUG(3,("dptr_num is %d, mask = %s, attr = %x, dirptr=(0x%lX,%d)\n",
+ DEBUG(3,("dptr_num is %d, mask = %s, attr = %x, dirptr=(0x%lX,%ld)\n",
dptr_num, mask, dirtype,
(long)conn->dirptr,
TellDir(conn->dirptr)));
@@ -1671,6 +1670,16 @@ resume_key = %d resume name = %s continue=%d level = %d\n",
*/
if(*resume_name && !continue_bit) {
+ long current_pos = 0;
+ /*
+ * Remember, mangle_map is called by
+ * get_lanman2_dir_entry(), so the resume name
+ * could be mangled. Ensure we check the unmangled name.
+ */
+
+ if (mangle_is_mangled(resume_name)) {
+ mangle_check_cache(resume_name, sizeof(resume_name)-1);
+ }
/*
* Fix for NT redirector problem triggered by resume key indexes
@@ -1678,77 +1687,10 @@ resume_key = %d resume name = %s continue=%d level = %d\n",
* and instead look for the filename to continue from (also given
* to us by NT/95/smbfs/smbclient). If no other scans have been done between the
* findfirst/findnext (as is usual) then the directory pointer
- * should already be at the correct place. Check this by scanning
- * backwards looking for an exact (ie. case sensitive) filename match.
- * If we get to the beginning of the directory and haven't found it then scan
- * forwards again looking for a match. JRA.
+ * should already be at the correct place.
*/
- int current_pos, start_pos;
- const char *dname = NULL;
- pstring dname_pstring;
- void *dirptr = conn->dirptr;
- start_pos = TellDir(dirptr);
- for(current_pos = start_pos; current_pos >= 0; current_pos--) {
- DEBUG(7,("call_trans2findnext: seeking to pos %d\n", current_pos));
-
- SeekDir(dirptr, current_pos);
- dname = ReadDirName(dirptr);
- if (dname) {
- /*
- * Remember, mangle_map is called by
- * get_lanman2_dir_entry(), so the resume name
- * could be mangled. Ensure we do the same
- * here.
- */
-
- /* make sure we get a copy that mangle_map can modify */
-
- pstrcpy(dname_pstring, dname);
- mangle_map( dname_pstring, False, True, SNUM(conn));
-
- if(strcsequal( resume_name, dname_pstring)) {
- SeekDir(dirptr, current_pos+1);
- DEBUG(7,("call_trans2findnext: got match at pos %d\n", current_pos+1 ));
- break;
- }
- }
- }
-
- /*
- * Scan forward from start if not found going backwards.
- */
-
- if(current_pos < 0) {
- DEBUG(7,("call_trans2findnext: notfound: seeking to pos %d\n", start_pos));
- SeekDir(dirptr, start_pos);
- for(current_pos = start_pos; (dname = ReadDirName(dirptr)) != NULL; ++current_pos) {
-
- /*
- * Remember, mangle_map is called by
- * get_lanman2_dir_entry(), so the resume name
- * could be mangled. Ensure we do the same
- * here.
- */
-
- if(dname) {
- /* make sure we get a copy that mangle_map can modify */
-
- pstrcpy(dname_pstring, dname);
- mangle_map(dname_pstring, False, True, SNUM(conn));
-
- if(strcsequal( resume_name, dname_pstring)) {
- SeekDir(dirptr, current_pos+1);
- DEBUG(7,("call_trans2findnext: got match at pos %d\n", current_pos+1 ));
- break;
- }
- }
- } /* end for */
- } /* end if current_pos */
- /* Can't find the name. Just resume from where we were... */
- if (dname == 0) {
- SeekDir(dirptr, start_pos);
- }
+ finished = !SearchDir(conn->dirptr, resume_name, &current_pos, True);
} /* end if resume_name && !continue_bit */
for (i=0;(i<(int)maxentries) && !finished && !out_of_space ;i++) {