r3262: - new pvfs_dirlist code that reopens the directory between search
authorAndrew Tridgell <tridge@samba.org>
Tue, 26 Oct 2004 13:18:34 +0000 (13:18 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 18:04:48 +0000 (13:04 -0500)
  calls. This is needed to allow for "new files appear during a search"
  behaviour

- pvfs now passes RAW-SEARCH
(This used to be commit 0d98f7653a1d58510a6cd4c2ac6c5e05c541109c)

source4/ntvfs/posix/pvfs_dirlist.c
source4/ntvfs/posix/pvfs_search.c
source4/ntvfs/posix/pvfs_unlink.c
source4/ntvfs/posix/vfs_posix.h

index 59e755d6ffa096acb239fb7c6efb85a6ed9e62b2..3f5b606bf9363a16485b4c49f364c96cb4444b13 100644 (file)
 #include "includes.h"
 #include "vfs_posix.h"
 
+struct pvfs_dir {
+       struct pvfs_state *pvfs;
+       BOOL no_wildcard;
+       char *last_name;
+       off_t offset;
+       DIR *dir;
+       const char *unix_path;
+       BOOL end_of_search;
+};
+
 /*
   a special directory listing case where the pattern has no wildcard. We can just do a single stat()
   thus avoiding the more expensive directory scan
@@ -35,38 +45,52 @@ static NTSTATUS pvfs_list_no_wildcard(struct pvfs_state *pvfs, struct pvfs_filen
                return NT_STATUS_OBJECT_NAME_NOT_FOUND;
        }
 
-       dir->count = 0;
+       dir->pvfs = pvfs;
+       dir->no_wildcard = True;
+       dir->end_of_search = False;
        dir->unix_path = talloc_strdup(dir, name->full_name);
        if (!dir->unix_path) {
                return NT_STATUS_NO_MEMORY;
        }
 
-       dir->names = talloc_array_p(dir, const char *, 1);
-       if (!dir->names) {
+       dir->last_name = talloc_strdup(dir, pattern);
+       if (!dir->last_name) {
                return NT_STATUS_NO_MEMORY;
        }
 
-       dir->names[0] = talloc_strdup(dir, pattern);
-       if (!dir->names[0]) {
-               return NT_STATUS_NO_MEMORY;
-       }
-
-       dir->count = 1;
+       dir->dir = NULL;
+       dir->offset = 0;
 
        return NT_STATUS_OK;
 }
 
+/*
+  destroy an open search
+*/
+static int pvfs_dirlist_destructor(void *ptr)
+{
+       struct pvfs_dir *dir = ptr;
+       if (dir->dir) closedir(dir->dir);
+       return 0;
+}
+
 /*
   start to read a directory 
 
   if the pattern matches no files then we return NT_STATUS_OK, with dir->count = 0
 */
-NTSTATUS pvfs_list_start(struct pvfs_state *pvfs, struct pvfs_filename *name, struct pvfs_dir *dir)
+NTSTATUS pvfs_list_start(struct pvfs_state *pvfs, struct pvfs_filename *name, 
+                        TALLOC_CTX *mem_ctx, struct pvfs_dir **dirp)
 {
-       DIR *odir;
-       struct dirent *dent;
-       uint_t allocated = 0;
        char *pattern;
+       struct pvfs_dir *dir;
+
+       (*dirp) = talloc_p(mem_ctx, struct pvfs_dir);
+       if (*dirp == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       
+       dir = *dirp;
 
        /* split the unix path into a directory + pattern */
        pattern = strrchr(name->full_name, '/');
@@ -82,58 +106,104 @@ NTSTATUS pvfs_list_start(struct pvfs_state *pvfs, struct pvfs_filename *name, st
                return pvfs_list_no_wildcard(pvfs, name, pattern, dir);
        }
 
-       dir->names = NULL;
-       dir->count = 0;
        dir->unix_path = talloc_strdup(dir, name->full_name);
        if (!dir->unix_path) {
                return NT_STATUS_NO_MEMORY;
        }
        
-       odir = opendir(name->full_name);
-       if (!odir) { 
+       dir->dir = opendir(name->full_name);
+       if (!dir->dir) { 
                return pvfs_map_errno(pvfs, errno); 
        }
 
-       while ((dent = readdir(odir))) {
-               uint_t i = dir->count;
-               const char *dname = dent->d_name;
-
-               if (ms_fnmatch(pattern, dname, 
-                              pvfs->tcon->smb_conn->negotiate.protocol) != 0) {
-                       char *short_name = pvfs_short_name_component(pvfs, dname);
-                       if (short_name == NULL ||
-                           ms_fnmatch(pattern, short_name, 
-                                      pvfs->tcon->smb_conn->negotiate.protocol) != 0) {
-                               talloc_free(short_name);
-                               continue;
-                       }
-                       talloc_free(short_name);
-               }
+       dir->pvfs = pvfs;
+       dir->no_wildcard = False;
+       dir->last_name = NULL;
+       dir->end_of_search = False;
+       dir->offset = 0;
 
-               if (dir->count >= allocated) {
-                       allocated = (allocated + 100) * 1.2;
-                       dir->names = talloc_realloc_p(dir, dir->names, const char *, allocated);
-                       if (!dir->names) { 
-                               closedir(odir);
-                               return NT_STATUS_NO_MEMORY;
-                       }
-               }
+       talloc_set_destructor(dir, pvfs_dirlist_destructor);
 
-               dir->names[i] = talloc_strdup(dir, dname);
-               if (!dir->names[i]) { 
-                       closedir(odir);
-                       return NT_STATUS_NO_MEMORY;
-               }
-               
-               dir->count++;
+       return NT_STATUS_OK;
+}
+
+/* 
+   return the next entry
+*/
+const char *pvfs_list_next(struct pvfs_dir *dir, uint_t *ofs)
+{
+       struct dirent *de;
+
+       /* non-wildcard searches are easy */
+       if (dir->no_wildcard) {
+               dir->end_of_search = True;
+               if (*ofs != 0) return NULL;
+               (*ofs)++;
+               return dir->last_name;
+       }
+
+       if (*ofs != dir->offset) {
+               seekdir(dir->dir, *ofs);
+               dir->offset = *ofs;
+       }
+       
+       de = readdir(dir->dir);
+       if (de == NULL) {
+               dir->last_name = NULL;
+               dir->end_of_search = True;
+               pvfs_list_hibernate(dir);
+               return NULL;
        }
 
-       closedir(odir);
+       dir->offset = telldir(dir->dir);
+       (*ofs) = dir->offset;
+
+       dir->last_name = de->d_name;
+
+       return dir->last_name;
+}
+
+/* 
+   put the directory to sleep. Used between search calls to give the
+   right directory change semantics
+*/
+void pvfs_list_hibernate(struct pvfs_dir *dir)
+{
+       if (dir->dir) {
+               closedir(dir->dir);
+               dir->dir = NULL;
+       }
+}
+
+
+/* 
+   wake up the directory search
+*/
+NTSTATUS pvfs_list_wakeup(struct pvfs_dir *dir, uint_t *ofs)
+{
+       if (dir->no_wildcard ||
+           dir->dir != NULL) {
+               return NT_STATUS_OK;
+       }
+
+       dir->dir = opendir(dir->unix_path);
+       if (dir->dir == NULL) {
+               dir->end_of_search = True;
+               return pvfs_map_errno(dir->pvfs, errno);
+       }
+
+       seekdir(dir->dir, *ofs);
+       dir->offset = telldir(dir->dir);
+       if (dir->offset != *ofs) {
+               DEBUG(0,("pvfs_list_wakeup: search offset changed %u -> %u\n", 
+                        *ofs, (unsigned)dir->offset));
+       }
 
        return NT_STATUS_OK;
 }
 
 
+
 /*
   return unix directory of an open search
 */
@@ -147,24 +217,40 @@ const char *pvfs_list_unix_path(struct pvfs_dir *dir)
 */
 BOOL pvfs_list_eos(struct pvfs_dir *dir, uint_t ofs)
 {
-       return ofs >= dir->count;
-}
-
-/* 
-   return the next entry
-*/
-const char *pvfs_list_next(struct pvfs_dir *dir, uint_t ofs)
-{
-       if (ofs >= dir->count) return NULL;
-       return dir->names[ofs];
+       return dir->end_of_search;
 }
 
 /*
   seek to the given name
 */
-uint_t pvfs_list_seek(struct pvfs_dir *dir, const char *name, uint_t ofs)
+NTSTATUS pvfs_list_seek(struct pvfs_dir *dir, const char *name, uint_t *ofs)
 {
-       /* not correct, needs to be replaced with real search when 
-          DIR* implementation is done */
-       return ofs;
+       struct dirent *de;
+       NTSTATUS status;
+
+       status = pvfs_list_wakeup(dir, ofs);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       if (StrCaseCmp(name, dir->last_name) == 0) {
+               *ofs = dir->offset;
+               return NT_STATUS_OK;
+       }
+
+       rewinddir(dir->dir);
+
+       while ((de = readdir(dir->dir))) {
+               if (StrCaseCmp(name, de->d_name) == 0) {
+                       dir->offset = telldir(dir->dir);
+                       *ofs = dir->offset;
+                       return NT_STATUS_OK;
+               }
+       }
+
+       dir->end_of_search = True;
+
+       pvfs_list_hibernate(dir);
+
+       return NT_STATUS_OBJECT_NAME_NOT_FOUND;
 }
index 5efc2f023f96fed69bb95906bdbddd316ffbb71b..0df3ebee0f72ebd4ce8cf49f8fe5ce4e5b0f3172 100644 (file)
@@ -204,12 +204,11 @@ static NTSTATUS pvfs_search_fill(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
        while ((*reply_count) < max_count) {
                union smb_search_data *file;
                const char *name;
+               uint_t ofs = search->current_index;
 
-               name = pvfs_list_next(dir, search->current_index);
+               name = pvfs_list_next(dir, &search->current_index);
                if (name == NULL) break;
 
-               search->current_index++;
-
                file = talloc_p(mem_ctx, union smb_search_data);
                if (!file) {
                        return NT_STATUS_NO_MEMORY;
@@ -225,7 +224,7 @@ static NTSTATUS pvfs_search_fill(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
 
                if (!callback(search_private, file)) {
                        talloc_free(file);
-                       search->current_index--;
+                       search->current_index = ofs;
                        break;
                }
 
@@ -233,6 +232,8 @@ static NTSTATUS pvfs_search_fill(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
                talloc_free(file);
        }
 
+       pvfs_list_hibernate(dir);
+
        return NT_STATUS_OK;
 }
 
@@ -275,13 +276,8 @@ static NTSTATUS pvfs_search_first_old(struct ntvfs_module_context *ntvfs,
                return NT_STATUS_NO_MEMORY;
        }
 
-       dir = talloc_p(search, struct pvfs_dir);
-       if (!dir) {
-               return NT_STATUS_NO_MEMORY;
-       }
-
        /* do the actual directory listing */
-       status = pvfs_list_start(pvfs, name, dir);
+       status = pvfs_list_start(pvfs, name, search, &dir);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
@@ -343,9 +339,13 @@ static NTSTATUS pvfs_search_next_old(struct ntvfs_module_context *ntvfs,
        }
 
        search->current_index = io->search_next.in.id.server_cookie;
-
        dir = search->dir;
 
+       status = pvfs_list_wakeup(dir, &search->current_index);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
        status = pvfs_search_fill(pvfs, req, max_count, search, io->generic.level,
                                  &reply_count, search_private, callback);
        if (!NT_STATUS_IS_OK(status)) {
@@ -406,13 +406,8 @@ NTSTATUS pvfs_search_first(struct ntvfs_module_context *ntvfs,
                return NT_STATUS_NO_MEMORY;
        }
 
-       dir = talloc_p(search, struct pvfs_dir);
-       if (!dir) {
-               return NT_STATUS_NO_MEMORY;
-       }
-
        /* do the actual directory listing */
-       status = pvfs_list_start(pvfs, name, dir);
+       status = pvfs_list_start(pvfs, name, search, &dir);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
@@ -488,14 +483,21 @@ NTSTATUS pvfs_search_next(struct ntvfs_module_context *ntvfs,
 
        /* work out what type of continuation is being used */
        if (io->t2fnext.in.last_name && *io->t2fnext.in.last_name) {
-               search->current_index = pvfs_list_seek(dir, io->t2fnext.in.last_name, 
-                                                      search->current_index);
+               status = pvfs_list_seek(dir, io->t2fnext.in.last_name, &search->current_index);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
        } else if (io->t2fnext.in.flags & FLAG_TRANS2_FIND_CONTINUE) {
                /* plain continue - nothing to do */
        } else {
                search->current_index = io->t2fnext.in.resume_key;
        }
 
+       status = pvfs_list_wakeup(dir, &search->current_index);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
        status = pvfs_search_fill(pvfs, req, io->t2fnext.in.max_count, search, io->generic.level,
                                  &reply_count, search_private, callback);
        if (!NT_STATUS_IS_OK(status)) {
index 74614d194a6d099706bec51d20ac901b04263c99..ff02d77613059e3390c7489c237b22665f9fbdbd 100644 (file)
@@ -79,9 +79,10 @@ NTSTATUS pvfs_unlink(struct ntvfs_module_context *ntvfs,
        struct pvfs_state *pvfs = ntvfs->private_data;
        struct pvfs_dir *dir;
        NTSTATUS status;
-       uint32_t i, total_deleted=0;
+       uint32_t total_deleted=0;
        struct pvfs_filename *name;
        const char *fname;
+       uint_t ofs;
 
        /* resolve the cifs name to a posix name */
        status = pvfs_resolve_name(pvfs, req, unl->in.pattern, 0, &name);
@@ -98,22 +99,17 @@ NTSTATUS pvfs_unlink(struct ntvfs_module_context *ntvfs,
                return NT_STATUS_FILE_IS_A_DIRECTORY;
        }
 
-       dir = talloc_p(req, struct pvfs_dir);
-       if (dir == NULL) {
-               return NT_STATUS_NO_MEMORY;
-       }
-
        /* get list of matching files */
-       status = pvfs_list_start(pvfs, name, dir);
+       status = pvfs_list_start(pvfs, name, req, &dir);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
 
        status = NT_STATUS_NO_SUCH_FILE;
 
-       for (i=0; 
-            (fname = pvfs_list_next(dir, i));
-             i++) {
+       ofs = 0;
+
+       while ((fname = pvfs_list_next(dir, &ofs))) {
                /* this seems to be a special case */
                if ((unl->in.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
                    (strcmp(fname, ".") == 0 ||
@@ -121,7 +117,7 @@ NTSTATUS pvfs_unlink(struct ntvfs_module_context *ntvfs,
                        return NT_STATUS_OBJECT_NAME_INVALID;
                }
 
-               status = pvfs_unlink_one(pvfs, req, dir->unix_path, fname, unl->in.attrib);
+               status = pvfs_unlink_one(pvfs, req, pvfs_list_unix_path(dir), fname, unl->in.attrib);
                if (NT_STATUS_IS_OK(status)) {
                        total_deleted++;
                }
index 48a2ba9288e7b8ab3ba12cdfb45d6f4a7b28b4ae..19408848fdcc693ce7192b4beffe0d6ec15c7372 100644 (file)
@@ -75,15 +75,6 @@ struct pvfs_filename {
 };
 
 
-/* this holds a list of file names for a search. We deliberately do
-   not hold the file stat information here to minimise the memory
-   overhead of idle searches */
-struct pvfs_dir {
-       uint_t count;
-       const char *unix_path;
-       const char **names;
-};
-
 /* the state of a search started with pvfs_search_first() */
 struct pvfs_search_state {
        struct pvfs_state *pvfs;