r8753: fixed directory handling on systems that do not return . and .. as the
authorAndrew Tridgell <tridge@samba.org>
Mon, 25 Jul 2005 10:40:17 +0000 (10:40 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 18:30:02 +0000 (13:30 -0500)
first two entries in a directory. This is what caused the FC3 system
shelob in the build farm to fail the RAW-UNLINK and RAW-SEARCH tests.
(This used to be commit f48abaaaca301c025ebd381f62345b3869809917)

source4/ntvfs/posix/pvfs_dirlist.c

index 328d07f71b7871fb7698d91fb98baa243aaa691e..a5d394d2aa659cc4ee544d4ecb0032643aa13169 100644 (file)
@@ -45,6 +45,11 @@ struct pvfs_dir {
        uint32_t name_cache_index;
 };
 
        uint32_t name_cache_index;
 };
 
+#define DIR_OFFSET_DOT    0
+#define DIR_OFFSET_DOTDOT 1
+#define DIR_OFFSET_BASE   2
+
+
 /*
   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
 /*
   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
@@ -150,12 +155,29 @@ NTSTATUS pvfs_list_start(struct pvfs_state *pvfs, struct pvfs_filename *name,
        return NT_STATUS_OK;
 }
 
        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)
 {
        struct dirent *de;
 /* 
    return the next entry
 */
 const char *pvfs_list_next(struct pvfs_dir *dir, uint_t *ofs)
 {
        struct dirent *de;
+       enum protocol_types protocol = dir->pvfs->tcon->smb_conn->negotiate.protocol;
 
        /* non-wildcard searches are easy */
        if (dir->no_wildcard) {
 
        /* non-wildcard searches are easy */
        if (dir->no_wildcard) {
@@ -165,39 +187,58 @@ const char *pvfs_list_next(struct pvfs_dir *dir, uint_t *ofs)
                return dir->single_name;
        }
 
                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 = *ofs;
                dir->offset = *ofs;
+               if (ms_fnmatch(dir->pattern, ".", protocol) == 0) {
+                       dcache_add(dir, ".");
+                       return ".";
+               }
        }
        }
+
+       if (*ofs == DIR_OFFSET_DOTDOT) {
+               (*ofs)++;
+               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;
        
        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 (strcmp(dname, ".") == 0 ||
+                   strcmp(dname, "..") == 0) {
+                       continue;
+               }
+
+               if (ms_fnmatch(dir->pattern, dname, protocol) != 0) {
                        char *short_name = pvfs_short_name_component(dir->pvfs, dname);
                        if (short_name == NULL ||
                        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);
                }
 
                                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;
 
                (*ofs) = dir->offset;
 
-               dir->name_cache_index = (dir->name_cache_index+1) % NAME_CACHE_SIZE;
-               e = &dir->name_cache[dir->name_cache_index];
+               dcache_add(dir, dname);
 
 
-               if (e->name) talloc_free(e->name);
-
-               e->name = talloc_strdup(dir->name_cache, de->d_name);
-               e->offset = dir->offset;
-
-               return e->name;
+               return dname;
        }
 
        dir->end_of_search = True;
        }
 
        dir->end_of_search = True;
@@ -228,6 +269,18 @@ NTSTATUS pvfs_list_seek(struct pvfs_dir *dir, const char *name, uint_t *ofs)
        struct dirent *de;
        int i;
 
        struct dirent *de;
        int i;
 
+       if (strcmp(name, ".") == 0) {
+               dir->offset = DIR_OFFSET_DOTDOT;
+               *ofs = dir->offset;
+               return NT_STATUS_OK;
+       }
+
+       if (strcmp(name, "..") == 0) {
+               dir->offset = DIR_OFFSET_BASE;
+               *ofs = dir->offset;
+               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) {
        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) {
@@ -247,7 +300,7 @@ NTSTATUS pvfs_list_seek(struct pvfs_dir *dir, const char *name, uint_t *ofs)
 
        while ((de = readdir(dir->dir))) {
                if (StrCaseCmp(name, de->d_name) == 0) {
 
        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;
                        *ofs = dir->offset;
                        return NT_STATUS_OK;
                }
                        *ofs = dir->offset;
                        return NT_STATUS_OK;
                }