r25554: Convert last instances of BOOL, True and False to the standard types.
[amitay/samba.git] / source4 / ntvfs / posix / pvfs_dirlist.c
index 5c67b2d189d1ac4535f86558475f6ddd1ee1ae7d..77f19c358503809f56c085bca9020aefcb2defd1 100644 (file)
@@ -5,7 +5,7 @@
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
@@ -14,8 +14,7 @@
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 /*
   directory listing functions for posix backend
@@ -34,17 +33,30 @@ struct name_cache_entry {
 
 struct pvfs_dir {
        struct pvfs_state *pvfs;
-       BOOL no_wildcard;
+       bool no_wildcard;
        char *single_name;
        const char *pattern;
        off_t offset;
        DIR *dir;
        const char *unix_path;
-       BOOL end_of_search;
+       bool end_of_search;
        struct name_cache_entry *name_cache;
        uint32_t name_cache_index;
 };
 
+/* these three numbers are chosen to minimise the chances of a bad
+   interaction with the OS value for 'end of directory'. On IRIX
+   telldir() returns 0xFFFFFFFF at the end of a directory, and that
+   caused an infinite loop with the original values of 0,1,2
+
+   On XFS on linux telldir returns 0x7FFFFFFF at the end of a
+   directory. Thus the change from 0x80000002, as otherwise
+   0x7FFFFFFF+0x80000002==1==DIR_OFFSET_DOTDOT
+*/
+#define DIR_OFFSET_DOT    0
+#define DIR_OFFSET_DOTDOT 1
+#define DIR_OFFSET_BASE   0x80000022
+
 /*
   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
@@ -57,8 +69,8 @@ static NTSTATUS pvfs_list_no_wildcard(struct pvfs_state *pvfs, struct pvfs_filen
        }
 
        dir->pvfs = pvfs;
-       dir->no_wildcard = True;
-       dir->end_of_search = False;
+       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;
@@ -79,9 +91,8 @@ static NTSTATUS pvfs_list_no_wildcard(struct pvfs_state *pvfs, struct pvfs_filen
 /*
   destroy an open search
 */
-static int pvfs_dirlist_destructor(void *ptr)
+static int pvfs_dirlist_destructor(struct pvfs_dir *dir)
 {
-       struct pvfs_dir *dir = ptr;
        if (dir->dir) closedir(dir->dir);
        return 0;
 }
@@ -97,7 +108,7 @@ NTSTATUS pvfs_list_start(struct pvfs_state *pvfs, struct pvfs_filename *name,
        char *pattern;
        struct pvfs_dir *dir;
 
-       (*dirp) = talloc_zero_p(mem_ctx, struct pvfs_dir);
+       (*dirp) = talloc_zero(mem_ctx, struct pvfs_dir);
        if (*dirp == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
@@ -134,12 +145,12 @@ NTSTATUS pvfs_list_start(struct pvfs_state *pvfs, struct pvfs_filename *name,
        }
 
        dir->pvfs = pvfs;
-       dir->no_wildcard = False;
-       dir->end_of_search = False;
-       dir->offset = 0;
-       dir->name_cache = talloc_zero_array_p(dir, 
-                                             struct name_cache_entry, 
-                                             NAME_CACHE_SIZE);
+       dir->no_wildcard = false;
+       dir->end_of_search = false;
+       dir->offset = DIR_OFFSET_DOT;
+       dir->name_cache = talloc_zero_array(dir, 
+                                           struct name_cache_entry, 
+                                           NAME_CACHE_SIZE);
        if (dir->name_cache == NULL) {
                talloc_free(dir);
                return NT_STATUS_NO_MEMORY;
@@ -150,142 +161,204 @@ NTSTATUS pvfs_list_start(struct pvfs_state *pvfs, struct pvfs_filename *name,
        return NT_STATUS_OK;
 }
 
+/*
+  add an entry to the local cache
+*/
+static void dcache_add(struct pvfs_dir *dir, const char *name)
+{
+       struct name_cache_entry *e;
+
+       dir->name_cache_index = (dir->name_cache_index+1) % NAME_CACHE_SIZE;
+       e = &dir->name_cache[dir->name_cache_index];
+
+       if (e->name) talloc_free(e->name);
+
+       e->name = talloc_strdup(dir->name_cache, name);
+       e->offset = dir->offset;
+}
+
 /* 
    return the next entry
 */
-const char *pvfs_list_next(struct pvfs_dir *dir, uint_t *ofs)
+const char *pvfs_list_next(struct pvfs_dir *dir, off_t *ofs)
 {
        struct dirent *de;
+       enum protocol_types protocol = dir->pvfs->ntvfs->ctx->protocol;
 
        /* non-wildcard searches are easy */
        if (dir->no_wildcard) {
-               dir->end_of_search = True;
+               dir->end_of_search = true;
                if (*ofs != 0) return NULL;
                (*ofs)++;
                return dir->single_name;
        }
 
-       if (*ofs != dir->offset) {
-               seekdir(dir->dir, *ofs);
+       /* . and .. are handled separately as some unix systems will
+          not return them first in a directory, but windows client
+          may assume that these entries always appear first */
+       if (*ofs == DIR_OFFSET_DOT) {
+               (*ofs) = DIR_OFFSET_DOTDOT;
                dir->offset = *ofs;
+               if (ms_fnmatch(dir->pattern, ".", protocol) == 0) {
+                       dcache_add(dir, ".");
+                       return ".";
+               }
        }
+
+       if (*ofs == DIR_OFFSET_DOTDOT) {
+               (*ofs) = DIR_OFFSET_BASE;
+               dir->offset = *ofs;
+               if (ms_fnmatch(dir->pattern, "..", protocol) == 0) {
+                       dcache_add(dir, "..");
+                       return "..";
+               }
+       }
+
+       if (*ofs == DIR_OFFSET_BASE) {
+               rewinddir(dir->dir);
+       } else if (*ofs != dir->offset) {
+               seekdir(dir->dir, (*ofs) - DIR_OFFSET_BASE);
+       }
+       dir->offset = *ofs;
        
        while ((de = readdir(dir->dir))) {
                const char *dname = de->d_name;
-               struct name_cache_entry *e;
 
-               if (ms_fnmatch(dir->pattern, dname, 
-                              dir->pvfs->tcon->smb_conn->negotiate.protocol) != 0) {
+               if (ISDOT(dname) || ISDOTDOT(dname)) {
+                       continue;
+               }
+
+               if (ms_fnmatch(dir->pattern, dname, protocol) != 0) {
                        char *short_name = pvfs_short_name_component(dir->pvfs, dname);
                        if (short_name == NULL ||
-                           ms_fnmatch(dir->pattern, short_name, 
-                                      dir->pvfs->tcon->smb_conn->negotiate.protocol) != 0) {
+                           ms_fnmatch(dir->pattern, short_name, protocol) != 0) {
                                talloc_free(short_name);
                                continue;
                        }
                        talloc_free(short_name);
                }
 
-               dir->offset = telldir(dir->dir);
+               dir->offset = telldir(dir->dir) + DIR_OFFSET_BASE;
                (*ofs) = dir->offset;
 
-               dir->name_cache_index = (dir->name_cache_index+1) % NAME_CACHE_SIZE;
-               e = &dir->name_cache[dir->name_cache_index];
-
-               if (e->name) talloc_free(e->name);
+               dcache_add(dir, dname);
 
-               e->name = talloc_strdup(dir, de->d_name);
-               e->offset = dir->offset;
-
-               return e->name;
+               return dname;
        }
 
-       dir->end_of_search = True;
-       pvfs_list_hibernate(dir);
+       dir->end_of_search = true;
        return NULL;
 }
 
-/* 
-   put the directory to sleep. Used between search calls to give the
-   right directory change semantics
+/*
+  return unix directory of an open search
 */
-void pvfs_list_hibernate(struct pvfs_dir *dir)
+const char *pvfs_list_unix_path(struct pvfs_dir *dir)
 {
-       if (dir->dir) {
-               closedir(dir->dir);
-               dir->dir = NULL;
-       }
+       return dir->unix_path;
 }
 
+/*
+  return true if end of search has been reached
+*/
+bool pvfs_list_eos(struct pvfs_dir *dir, off_t ofs)
+{
+       return dir->end_of_search;
+}
 
-/* 
-   wake up the directory search
+/*
+  seek to the given name
 */
-NTSTATUS pvfs_list_wakeup(struct pvfs_dir *dir, uint_t *ofs)
+NTSTATUS pvfs_list_seek(struct pvfs_dir *dir, const char *name, off_t *ofs)
 {
-       if (dir->no_wildcard ||
-           dir->dir != NULL) {
+       struct dirent *de;
+       int i;
+
+       dir->end_of_search = false;
+
+       if (ISDOT(name)) {
+               dir->offset = DIR_OFFSET_DOTDOT;
+               *ofs = dir->offset;
                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);
+       if (ISDOTDOT(name)) {
+               dir->offset = DIR_OFFSET_BASE;
+               *ofs = dir->offset;
+               return NT_STATUS_OK;
        }
 
-       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));
+       for (i=dir->name_cache_index;i>=0;i--) {
+               struct name_cache_entry *e = &dir->name_cache[i];
+               if (e->name && strcasecmp_m(name, e->name) == 0) {
+                       *ofs = e->offset;
+                       return NT_STATUS_OK;
+               }
+       }
+       for (i=NAME_CACHE_SIZE-1;i>dir->name_cache_index;i--) {
+               struct name_cache_entry *e = &dir->name_cache[i];
+               if (e->name && strcasecmp_m(name, e->name) == 0) {
+                       *ofs = e->offset;
+                       return NT_STATUS_OK;
+               }
        }
 
-       return NT_STATUS_OK;
-}
-
+       rewinddir(dir->dir);
 
+       while ((de = readdir(dir->dir))) {
+               if (strcasecmp_m(name, de->d_name) == 0) {
+                       dir->offset = telldir(dir->dir) + DIR_OFFSET_BASE;
+                       *ofs = dir->offset;
+                       return NT_STATUS_OK;
+               }
+       }
 
-/*
-  return unix directory of an open search
-*/
-const char *pvfs_list_unix_path(struct pvfs_dir *dir)
-{
-       return dir->unix_path;
-}
+       dir->end_of_search = true;
 
-/*
-  return True if end of search has been reached
-*/
-BOOL pvfs_list_eos(struct pvfs_dir *dir, uint_t ofs)
-{
-       return dir->end_of_search;
+       return NT_STATUS_OBJECT_NAME_NOT_FOUND;
 }
 
 /*
-  seek to the given name
+  seek to the given offset
 */
-NTSTATUS pvfs_list_seek(struct pvfs_dir *dir, const char *name, uint_t *ofs)
+NTSTATUS pvfs_list_seek_ofs(struct pvfs_dir *dir, uint32_t resume_key, off_t *ofs)
 {
        struct dirent *de;
-       NTSTATUS status;
        int i;
 
-       status = pvfs_list_wakeup(dir, ofs);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
+       dir->end_of_search = false;
+
+       if (resume_key == DIR_OFFSET_DOT) {
+               *ofs = DIR_OFFSET_DOTDOT;
+               return NT_STATUS_OK;
+       }
+
+       if (resume_key == DIR_OFFSET_DOTDOT) {
+               *ofs = DIR_OFFSET_BASE;
+               return NT_STATUS_OK;
+       }
+
+       if (resume_key == DIR_OFFSET_BASE) {
+               rewinddir(dir->dir);
+               if ((de=readdir(dir->dir)) == NULL) {
+                       dir->end_of_search = true;
+                       return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+               }
+               *ofs = telldir(dir->dir) + DIR_OFFSET_BASE;
+               dir->offset = *ofs;
+               return NT_STATUS_OK;
        }
 
        for (i=dir->name_cache_index;i>=0;i--) {
                struct name_cache_entry *e = &dir->name_cache[i];
-               if (e->name && StrCaseCmp(name, e->name) == 0) {
+               if (resume_key == (uint32_t)e->offset) {
                        *ofs = e->offset;
                        return NT_STATUS_OK;
                }
        }
        for (i=NAME_CACHE_SIZE-1;i>dir->name_cache_index;i--) {
                struct name_cache_entry *e = &dir->name_cache[i];
-               if (e->name && StrCaseCmp(name, e->name) == 0) {
+               if (resume_key == (uint32_t)e->offset) {
                        *ofs = e->offset;
                        return NT_STATUS_OK;
                }
@@ -294,16 +367,37 @@ NTSTATUS pvfs_list_seek(struct pvfs_dir *dir, const char *name, uint_t *ofs)
        rewinddir(dir->dir);
 
        while ((de = readdir(dir->dir))) {
-               if (StrCaseCmp(name, de->d_name) == 0) {
-                       dir->offset = telldir(dir->dir);
+               dir->offset = telldir(dir->dir) + DIR_OFFSET_BASE;
+               if (resume_key == (uint32_t)dir->offset) {
                        *ofs = dir->offset;
                        return NT_STATUS_OK;
                }
        }
 
-       dir->end_of_search = True;
-
-       pvfs_list_hibernate(dir);
+       dir->end_of_search = true;
 
        return NT_STATUS_OBJECT_NAME_NOT_FOUND;
 }
+
+
+/*
+  see if a directory is empty
+*/
+bool pvfs_directory_empty(struct pvfs_state *pvfs, struct pvfs_filename *name)
+{
+       struct dirent *de;
+       DIR *dir = opendir(name->full_name);
+       if (dir == NULL) {
+               return true;
+       }
+
+       while ((de = readdir(dir))) {
+               if (!ISDOT(de->d_name) && !ISDOTDOT(de->d_name)) {
+                       closedir(dir);
+                       return false;
+               }
+       }
+
+       closedir(dir);
+       return true;
+}