r18319: fixed the directory search resume code on IRIX
authorAndrew Tridgell <tridge@samba.org>
Sun, 10 Sep 2006 07:24:41 +0000 (07:24 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 19:18:08 +0000 (14:18 -0500)
The problem was twofold:

  1) irix returns 64 bit numbers in telldir(). The protocol uses a 32
     bit resume key. We now cope with this properly using the code in
     pvfs_list_seek_ofs().

  2) irix returns 0xFFFFFFFF from telldir() for the last entry in the
     directory. When added to DIR_OFFSET_BASE this became
     DIR_OFFSET_DOTDOT which meant an infinite loop!
(This used to be commit 8cce9740ed0da9f08d6821beb4acaa9d28d149c2)

source4/ntvfs/posix/pvfs_dirlist.c
source4/ntvfs/posix/pvfs_rename.c
source4/ntvfs/posix/pvfs_search.c
source4/ntvfs/posix/pvfs_unlink.c
source4/ntvfs/posix/vfs_posix.h
source4/torture/raw/search.c

index c351f6ba41a60ea525bc1219e69ba7686f391601..e8dd149836c40b3eb7ee19d6621aedccdd11942b 100644 (file)
@@ -45,10 +45,14 @@ struct pvfs_dir {
        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
+*/
 #define DIR_OFFSET_DOT    0
 #define DIR_OFFSET_DOTDOT 1
-#define DIR_OFFSET_BASE   2
-
+#define DIR_OFFSET_BASE   0x80000002
 
 /*
   a special directory listing case where the pattern has no wildcard. We can just do a single stat()
@@ -140,7 +144,7 @@ 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->offset = DIR_OFFSET_DOT;
        dir->name_cache = talloc_zero_array(dir, 
                                            struct name_cache_entry, 
                                            NAME_CACHE_SIZE);
@@ -173,7 +177,7 @@ static void dcache_add(struct pvfs_dir *dir, const char *name)
 /* 
    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;
@@ -190,7 +194,7 @@ const char *pvfs_list_next(struct pvfs_dir *dir, uint_t *ofs)
           not return them first in a directory, but windows client
           may assume that these entries always appear first */
        if (*ofs == DIR_OFFSET_DOT) {
-               (*ofs)++;
+               (*ofs) = DIR_OFFSET_DOTDOT;
                dir->offset = *ofs;
                if (ms_fnmatch(dir->pattern, ".", protocol) == 0) {
                        dcache_add(dir, ".");
@@ -199,7 +203,7 @@ const char *pvfs_list_next(struct pvfs_dir *dir, uint_t *ofs)
        }
 
        if (*ofs == DIR_OFFSET_DOTDOT) {
-               (*ofs)++;
+               (*ofs) = DIR_OFFSET_BASE;
                dir->offset = *ofs;
                if (ms_fnmatch(dir->pattern, "..", protocol) == 0) {
                        dcache_add(dir, "..");
@@ -254,7 +258,7 @@ const char *pvfs_list_unix_path(struct pvfs_dir *dir)
 /*
   return True if end of search has been reached
 */
-BOOL pvfs_list_eos(struct pvfs_dir *dir, uint_t ofs)
+BOOL pvfs_list_eos(struct pvfs_dir *dir, off_t ofs)
 {
        return dir->end_of_search;
 }
@@ -262,11 +266,13 @@ BOOL pvfs_list_eos(struct pvfs_dir *dir, uint_t ofs)
 /*
   seek to the given name
 */
-NTSTATUS pvfs_list_seek(struct pvfs_dir *dir, const char *name, uint_t *ofs)
+NTSTATUS pvfs_list_seek(struct pvfs_dir *dir, const char *name, off_t *ofs)
 {
        struct dirent *de;
        int i;
 
+       dir->end_of_search = False;
+
        if (ISDOT(name)) {
                dir->offset = DIR_OFFSET_DOTDOT;
                *ofs = dir->offset;
@@ -309,6 +315,67 @@ NTSTATUS pvfs_list_seek(struct pvfs_dir *dir, const char *name, uint_t *ofs)
        return NT_STATUS_OBJECT_NAME_NOT_FOUND;
 }
 
+/*
+  seek to the given offset
+*/
+NTSTATUS pvfs_list_seek_ofs(struct pvfs_dir *dir, uint32_t resume_key, off_t *ofs)
+{
+       struct dirent *de;
+       int i;
+
+       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 (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 (resume_key == (uint32_t)e->offset) {
+                       *ofs = e->offset;
+                       return NT_STATUS_OK;
+               }
+       }
+
+       rewinddir(dir->dir);
+
+       while ((de = readdir(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;
+
+       return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+}
+
 
 /*
   see if a directory is empty
index 6a397ef981a283cd6828f30ba415fe64a1e646f7..8956a0174ee20018ac1ceac045a51fb40e9a6182 100644 (file)
@@ -257,7 +257,7 @@ static NTSTATUS pvfs_rename_wildcard(struct pvfs_state *pvfs,
 {
        struct pvfs_dir *dir;
        NTSTATUS status;
-       uint_t ofs = 0;
+       off_t ofs = 0;
        const char *fname, *fname2, *dir_path;
        uint16_t attrib = ren->rename.in.attrib;
        int total_renamed = 0;
index bfe0780958a334a3894efb389b572f8aa812ffd6..6224e15304d0d0cdf3225029a98fe4eec5fb76d2 100644 (file)
@@ -75,12 +75,15 @@ static NTSTATUS fill_search_info(struct pvfs_state *pvfs,
                                 const char *unix_path,
                                 const char *fname, 
                                 struct pvfs_search_state *search,
-                                uint32_t dir_index,
+                                off_t dir_offset,
                                 union smb_search_data *file)
 {
        struct pvfs_filename *name;
        NTSTATUS status;
        const char *shortname;
+       uint32_t dir_index = (uint32_t)dir_offset; /* truncated - see the code 
+                                                     in pvfs_list_seek_ofs() for 
+                                                     how we cope with this */
 
        status = pvfs_resolve_partial(pvfs, file, unix_path, fname, &name);
        if (!NT_STATUS_IS_OK(status)) {
@@ -249,7 +252,7 @@ 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;
+               off_t ofs = search->current_index;
 
                name = pvfs_list_next(dir, &search->current_index);
                if (name == NULL) break;
@@ -419,10 +422,15 @@ static NTSTATUS pvfs_search_next_old(struct ntvfs_module_context *ntvfs,
                return NT_STATUS_INVALID_HANDLE;
        }
 
-       search->current_index = io->search_next.in.id.server_cookie;
-       search->last_used = time(NULL);
        dir = search->dir;
 
+       status = pvfs_list_seek_ofs(dir, io->search_next.in.id.server_cookie, 
+                                   &search->current_index);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+       search->last_used = time(NULL);
+
        status = pvfs_search_fill(pvfs, req, max_count, search, io->generic.data_level,
                                  &reply_count, search_private, callback);
        if (!NT_STATUS_IS_OK(status)) {
@@ -557,23 +565,24 @@ static NTSTATUS pvfs_search_next_trans2(struct ntvfs_module_context *ntvfs,
                /* we didn't find the search handle */
                return NT_STATUS_INVALID_HANDLE;
        }
-
+       
        dir = search->dir;
+       
+       status = NT_STATUS_OK;
 
        /* work out what type of continuation is being used */
        if (io->t2fnext.in.last_name && *io->t2fnext.in.last_name) {
                status = pvfs_list_seek(dir, io->t2fnext.in.last_name, &search->current_index);
-               if (!NT_STATUS_IS_OK(status)) {
-                       if (io->t2fnext.in.resume_key) {
-                               search->current_index = io->t2fnext.in.resume_key;
-                       } else {
-                               return status;
-                       }
+               if (!NT_STATUS_IS_OK(status) && io->t2fnext.in.resume_key) {
+                       status = pvfs_list_seek_ofs(dir, io->t2fnext.in.resume_key, 
+                                                   &search->current_index);
                }
-       } else if (io->t2fnext.in.flags & FLAG_TRANS2_FIND_CONTINUE) {
-               /* plain continue - nothing to do */
-       } else {
-               search->current_index = io->t2fnext.in.resume_key;
+       } else if (!(io->t2fnext.in.flags & FLAG_TRANS2_FIND_CONTINUE)) {
+               status = pvfs_list_seek_ofs(dir, io->t2fnext.in.resume_key, 
+                                           &search->current_index);
+       }
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
        }
 
        search->num_ea_names = io->t2fnext.in.num_names;
index c40634035d8e28d833e866eb9d4e401551430ce7..c02f704bd503efb975777234c823d29d412d36c7 100644 (file)
@@ -130,7 +130,7 @@ NTSTATUS pvfs_unlink(struct ntvfs_module_context *ntvfs,
        uint32_t total_deleted=0;
        struct pvfs_filename *name;
        const char *fname;
-       uint_t ofs;
+       off_t ofs;
 
        /* resolve the cifs name to a posix name */
        status = pvfs_resolve_name(pvfs, req, unl->unlink.in.pattern, 
index a788ddd19c10feb5d1ffda076643026e4c06cf45..82a3d341725779c0d640db1c657621fb134ad956 100644 (file)
@@ -183,7 +183,7 @@ struct pvfs_search_state {
        struct pvfs_search_state *prev, *next;
        struct pvfs_state *pvfs;
        uint16_t handle;
-       uint_t current_index;
+       off_t current_index;
        uint16_t search_attrib;
        uint16_t must_attrib;
        struct pvfs_dir *dir;
index 6ee21d630a455b74ef224fefc991ecb5f4f4924a..d10bdf662be4ca9302f0c5dc1d77b8b199944443 100644 (file)
@@ -203,17 +203,17 @@ static const char *extract_name(void *data, enum smb_search_level level,
 /*
   extract the name from a smb_data structure and level
 */
-static int extract_resume_key(void *data, enum smb_search_level level,
-                             enum smb_search_data_level data_level)
+static uint32_t extract_resume_key(void *data, enum smb_search_level level,
+                                  enum smb_search_data_level data_level)
 {
        int i;
        for (i=0;i<ARRAY_SIZE(levels);i++) {
                if (level == levels[i].level &&
                    data_level == levels[i].data_level) {
-                       return (int)*(uint32_t *)(levels[i].resume_key_offset + (char *)data);
+                       return *(uint32_t *)(levels[i].resume_key_offset + (char *)data);
                }
        }
-       return -1;
+       return 0;
 }
 
 /* find a level in the table by name */
@@ -604,7 +604,7 @@ static NTSTATUS multiple_search(struct smbcli_state *cli,
                        case CONT_RESUME_KEY:
                                io2.t2fnext.in.resume_key = extract_resume_key(&result->list[result->count-1],
                                                                               io2.t2fnext.level, io2.t2fnext.data_level);
-                               if (io2.t2fnext.in.resume_key <= 0) {
+                               if (io2.t2fnext.in.resume_key == 0) {
                                        printf("Server does not support resume by key for level %s\n",
                                               level_name(io2.t2fnext.level, io2.t2fnext.data_level));
                                        return NT_STATUS_NOT_SUPPORTED;