summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Tridgell <tridge@samba.org>2006-09-10 07:24:41 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 14:18:08 -0500
commit89efea81d9d67ebed159321e8ea496eee54c2deb (patch)
tree9ffc195a05eab698b1697015410f15546e48e266
parentc74d5b9204c004f30494645f04d34306667990c8 (diff)
downloadsamba-89efea81d9d67ebed159321e8ea496eee54c2deb.tar.gz
samba-89efea81d9d67ebed159321e8ea496eee54c2deb.tar.bz2
samba-89efea81d9d67ebed159321e8ea496eee54c2deb.zip
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)
-rw-r--r--source4/ntvfs/posix/pvfs_dirlist.c83
-rw-r--r--source4/ntvfs/posix/pvfs_rename.c2
-rw-r--r--source4/ntvfs/posix/pvfs_search.c39
-rw-r--r--source4/ntvfs/posix/pvfs_unlink.c2
-rw-r--r--source4/ntvfs/posix/vfs_posix.h2
-rw-r--r--source4/torture/raw/search.c10
6 files changed, 107 insertions, 31 deletions
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;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;