From 89efea81d9d67ebed159321e8ea496eee54c2deb Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Sun, 10 Sep 2006 07:24:41 +0000 Subject: r18319: fixed the directory search resume code on IRIX 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 | 83 ++++++++++++++++++++++++++++++++++---- source4/ntvfs/posix/pvfs_rename.c | 2 +- source4/ntvfs/posix/pvfs_search.c | 39 +++++++++++------- source4/ntvfs/posix/pvfs_unlink.c | 2 +- source4/ntvfs/posix/vfs_posix.h | 2 +- source4/torture/raw/search.c | 10 ++--- 6 files changed, 107 insertions(+), 31 deletions(-) (limited to 'source4') diff --git a/source4/ntvfs/posix/pvfs_dirlist.c b/source4/ntvfs/posix/pvfs_dirlist.c index c351f6ba41..e8dd149836 100644 --- a/source4/ntvfs/posix/pvfs_dirlist.c +++ b/source4/ntvfs/posix/pvfs_dirlist.c @@ -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 diff --git a/source4/ntvfs/posix/pvfs_rename.c b/source4/ntvfs/posix/pvfs_rename.c index 6a397ef981..8956a0174e 100644 --- a/source4/ntvfs/posix/pvfs_rename.c +++ b/source4/ntvfs/posix/pvfs_rename.c @@ -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; diff --git a/source4/ntvfs/posix/pvfs_search.c b/source4/ntvfs/posix/pvfs_search.c index bfe0780958..6224e15304 100644 --- a/source4/ntvfs/posix/pvfs_search.c +++ b/source4/ntvfs/posix/pvfs_search.c @@ -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; diff --git a/source4/ntvfs/posix/pvfs_unlink.c b/source4/ntvfs/posix/pvfs_unlink.c index c40634035d..c02f704bd5 100644 --- a/source4/ntvfs/posix/pvfs_unlink.c +++ b/source4/ntvfs/posix/pvfs_unlink.c @@ -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, diff --git a/source4/ntvfs/posix/vfs_posix.h b/source4/ntvfs/posix/vfs_posix.h index a788ddd19c..82a3d34172 100644 --- a/source4/ntvfs/posix/vfs_posix.h +++ b/source4/ntvfs/posix/vfs_posix.h @@ -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; diff --git a/source4/torture/raw/search.c b/source4/torture/raw/search.c index 6ee21d630a..d10bdf662b 100644 --- a/source4/torture/raw/search.c +++ b/source4/torture/raw/search.c @@ -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;ilist[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; -- cgit