diff options
Diffstat (limited to 'source4/ntvfs/posix')
-rw-r--r-- | source4/ntvfs/posix/pvfs_dirlist.c | 210 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_search.c | 40 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_unlink.c | 18 | ||||
-rw-r--r-- | source4/ntvfs/posix/vfs_posix.h | 9 |
4 files changed, 176 insertions, 101 deletions
diff --git a/source4/ntvfs/posix/pvfs_dirlist.c b/source4/ntvfs/posix/pvfs_dirlist.c index 59e755d6ff..3f5b606bf9 100644 --- a/source4/ntvfs/posix/pvfs_dirlist.c +++ b/source4/ntvfs/posix/pvfs_dirlist.c @@ -24,6 +24,16 @@ #include "includes.h" #include "vfs_posix.h" +struct pvfs_dir { + struct pvfs_state *pvfs; + BOOL no_wildcard; + char *last_name; + off_t offset; + DIR *dir; + const char *unix_path; + BOOL end_of_search; +}; + /* 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 @@ -35,38 +45,52 @@ static NTSTATUS pvfs_list_no_wildcard(struct pvfs_state *pvfs, struct pvfs_filen return NT_STATUS_OBJECT_NAME_NOT_FOUND; } - dir->count = 0; + dir->pvfs = pvfs; + 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; } - dir->names = talloc_array_p(dir, const char *, 1); - if (!dir->names) { + dir->last_name = talloc_strdup(dir, pattern); + if (!dir->last_name) { return NT_STATUS_NO_MEMORY; } - dir->names[0] = talloc_strdup(dir, pattern); - if (!dir->names[0]) { - return NT_STATUS_NO_MEMORY; - } - - dir->count = 1; + dir->dir = NULL; + dir->offset = 0; return NT_STATUS_OK; } /* + destroy an open search +*/ +static int pvfs_dirlist_destructor(void *ptr) +{ + struct pvfs_dir *dir = ptr; + if (dir->dir) closedir(dir->dir); + return 0; +} + +/* start to read a directory if the pattern matches no files then we return NT_STATUS_OK, with dir->count = 0 */ -NTSTATUS pvfs_list_start(struct pvfs_state *pvfs, struct pvfs_filename *name, struct pvfs_dir *dir) +NTSTATUS pvfs_list_start(struct pvfs_state *pvfs, struct pvfs_filename *name, + TALLOC_CTX *mem_ctx, struct pvfs_dir **dirp) { - DIR *odir; - struct dirent *dent; - uint_t allocated = 0; char *pattern; + struct pvfs_dir *dir; + + (*dirp) = talloc_p(mem_ctx, struct pvfs_dir); + if (*dirp == NULL) { + return NT_STATUS_NO_MEMORY; + } + + dir = *dirp; /* split the unix path into a directory + pattern */ pattern = strrchr(name->full_name, '/'); @@ -82,58 +106,104 @@ NTSTATUS pvfs_list_start(struct pvfs_state *pvfs, struct pvfs_filename *name, st return pvfs_list_no_wildcard(pvfs, name, pattern, dir); } - dir->names = NULL; - dir->count = 0; dir->unix_path = talloc_strdup(dir, name->full_name); if (!dir->unix_path) { return NT_STATUS_NO_MEMORY; } - odir = opendir(name->full_name); - if (!odir) { + dir->dir = opendir(name->full_name); + if (!dir->dir) { return pvfs_map_errno(pvfs, errno); } - while ((dent = readdir(odir))) { - uint_t i = dir->count; - const char *dname = dent->d_name; - - if (ms_fnmatch(pattern, dname, - pvfs->tcon->smb_conn->negotiate.protocol) != 0) { - char *short_name = pvfs_short_name_component(pvfs, dname); - if (short_name == NULL || - ms_fnmatch(pattern, short_name, - pvfs->tcon->smb_conn->negotiate.protocol) != 0) { - talloc_free(short_name); - continue; - } - talloc_free(short_name); - } + dir->pvfs = pvfs; + dir->no_wildcard = False; + dir->last_name = NULL; + dir->end_of_search = False; + dir->offset = 0; - if (dir->count >= allocated) { - allocated = (allocated + 100) * 1.2; - dir->names = talloc_realloc_p(dir, dir->names, const char *, allocated); - if (!dir->names) { - closedir(odir); - return NT_STATUS_NO_MEMORY; - } - } + talloc_set_destructor(dir, pvfs_dirlist_destructor); - dir->names[i] = talloc_strdup(dir, dname); - if (!dir->names[i]) { - closedir(odir); - return NT_STATUS_NO_MEMORY; - } - - dir->count++; + return NT_STATUS_OK; +} + +/* + return the next entry +*/ +const char *pvfs_list_next(struct pvfs_dir *dir, uint_t *ofs) +{ + struct dirent *de; + + /* non-wildcard searches are easy */ + if (dir->no_wildcard) { + dir->end_of_search = True; + if (*ofs != 0) return NULL; + (*ofs)++; + return dir->last_name; + } + + if (*ofs != dir->offset) { + seekdir(dir->dir, *ofs); + dir->offset = *ofs; + } + + de = readdir(dir->dir); + if (de == NULL) { + dir->last_name = NULL; + dir->end_of_search = True; + pvfs_list_hibernate(dir); + return NULL; } - closedir(odir); + dir->offset = telldir(dir->dir); + (*ofs) = dir->offset; + + dir->last_name = de->d_name; + + return dir->last_name; +} + +/* + put the directory to sleep. Used between search calls to give the + right directory change semantics +*/ +void pvfs_list_hibernate(struct pvfs_dir *dir) +{ + if (dir->dir) { + closedir(dir->dir); + dir->dir = NULL; + } +} + + +/* + wake up the directory search +*/ +NTSTATUS pvfs_list_wakeup(struct pvfs_dir *dir, uint_t *ofs) +{ + if (dir->no_wildcard || + dir->dir != NULL) { + 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); + } + + 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)); + } return NT_STATUS_OK; } + /* return unix directory of an open search */ @@ -147,24 +217,40 @@ const char *pvfs_list_unix_path(struct pvfs_dir *dir) */ BOOL pvfs_list_eos(struct pvfs_dir *dir, uint_t ofs) { - return ofs >= dir->count; -} - -/* - return the next entry -*/ -const char *pvfs_list_next(struct pvfs_dir *dir, uint_t ofs) -{ - if (ofs >= dir->count) return NULL; - return dir->names[ofs]; + return dir->end_of_search; } /* seek to the given name */ -uint_t pvfs_list_seek(struct pvfs_dir *dir, const char *name, uint_t ofs) +NTSTATUS pvfs_list_seek(struct pvfs_dir *dir, const char *name, uint_t *ofs) { - /* not correct, needs to be replaced with real search when - DIR* implementation is done */ - return ofs; + struct dirent *de; + NTSTATUS status; + + status = pvfs_list_wakeup(dir, ofs); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (StrCaseCmp(name, dir->last_name) == 0) { + *ofs = dir->offset; + return NT_STATUS_OK; + } + + rewinddir(dir->dir); + + while ((de = readdir(dir->dir))) { + if (StrCaseCmp(name, de->d_name) == 0) { + dir->offset = telldir(dir->dir); + *ofs = dir->offset; + return NT_STATUS_OK; + } + } + + dir->end_of_search = True; + + pvfs_list_hibernate(dir); + + return NT_STATUS_OBJECT_NAME_NOT_FOUND; } diff --git a/source4/ntvfs/posix/pvfs_search.c b/source4/ntvfs/posix/pvfs_search.c index 5efc2f023f..0df3ebee0f 100644 --- a/source4/ntvfs/posix/pvfs_search.c +++ b/source4/ntvfs/posix/pvfs_search.c @@ -204,12 +204,11 @@ 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; - name = pvfs_list_next(dir, search->current_index); + name = pvfs_list_next(dir, &search->current_index); if (name == NULL) break; - search->current_index++; - file = talloc_p(mem_ctx, union smb_search_data); if (!file) { return NT_STATUS_NO_MEMORY; @@ -225,7 +224,7 @@ static NTSTATUS pvfs_search_fill(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx, if (!callback(search_private, file)) { talloc_free(file); - search->current_index--; + search->current_index = ofs; break; } @@ -233,6 +232,8 @@ static NTSTATUS pvfs_search_fill(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx, talloc_free(file); } + pvfs_list_hibernate(dir); + return NT_STATUS_OK; } @@ -275,13 +276,8 @@ static NTSTATUS pvfs_search_first_old(struct ntvfs_module_context *ntvfs, return NT_STATUS_NO_MEMORY; } - dir = talloc_p(search, struct pvfs_dir); - if (!dir) { - return NT_STATUS_NO_MEMORY; - } - /* do the actual directory listing */ - status = pvfs_list_start(pvfs, name, dir); + status = pvfs_list_start(pvfs, name, search, &dir); if (!NT_STATUS_IS_OK(status)) { return status; } @@ -343,9 +339,13 @@ static NTSTATUS pvfs_search_next_old(struct ntvfs_module_context *ntvfs, } search->current_index = io->search_next.in.id.server_cookie; - dir = search->dir; + status = pvfs_list_wakeup(dir, &search->current_index); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + status = pvfs_search_fill(pvfs, req, max_count, search, io->generic.level, &reply_count, search_private, callback); if (!NT_STATUS_IS_OK(status)) { @@ -406,13 +406,8 @@ NTSTATUS pvfs_search_first(struct ntvfs_module_context *ntvfs, return NT_STATUS_NO_MEMORY; } - dir = talloc_p(search, struct pvfs_dir); - if (!dir) { - return NT_STATUS_NO_MEMORY; - } - /* do the actual directory listing */ - status = pvfs_list_start(pvfs, name, dir); + status = pvfs_list_start(pvfs, name, search, &dir); if (!NT_STATUS_IS_OK(status)) { return status; } @@ -488,14 +483,21 @@ NTSTATUS pvfs_search_next(struct ntvfs_module_context *ntvfs, /* work out what type of continuation is being used */ if (io->t2fnext.in.last_name && *io->t2fnext.in.last_name) { - search->current_index = pvfs_list_seek(dir, io->t2fnext.in.last_name, - search->current_index); + status = pvfs_list_seek(dir, io->t2fnext.in.last_name, &search->current_index); + if (!NT_STATUS_IS_OK(status)) { + return status; + } } else if (io->t2fnext.in.flags & FLAG_TRANS2_FIND_CONTINUE) { /* plain continue - nothing to do */ } else { search->current_index = io->t2fnext.in.resume_key; } + status = pvfs_list_wakeup(dir, &search->current_index); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + status = pvfs_search_fill(pvfs, req, io->t2fnext.in.max_count, search, io->generic.level, &reply_count, search_private, callback); if (!NT_STATUS_IS_OK(status)) { diff --git a/source4/ntvfs/posix/pvfs_unlink.c b/source4/ntvfs/posix/pvfs_unlink.c index 74614d194a..ff02d77613 100644 --- a/source4/ntvfs/posix/pvfs_unlink.c +++ b/source4/ntvfs/posix/pvfs_unlink.c @@ -79,9 +79,10 @@ NTSTATUS pvfs_unlink(struct ntvfs_module_context *ntvfs, struct pvfs_state *pvfs = ntvfs->private_data; struct pvfs_dir *dir; NTSTATUS status; - uint32_t i, total_deleted=0; + uint32_t total_deleted=0; struct pvfs_filename *name; const char *fname; + uint_t ofs; /* resolve the cifs name to a posix name */ status = pvfs_resolve_name(pvfs, req, unl->in.pattern, 0, &name); @@ -98,22 +99,17 @@ NTSTATUS pvfs_unlink(struct ntvfs_module_context *ntvfs, return NT_STATUS_FILE_IS_A_DIRECTORY; } - dir = talloc_p(req, struct pvfs_dir); - if (dir == NULL) { - return NT_STATUS_NO_MEMORY; - } - /* get list of matching files */ - status = pvfs_list_start(pvfs, name, dir); + status = pvfs_list_start(pvfs, name, req, &dir); if (!NT_STATUS_IS_OK(status)) { return status; } status = NT_STATUS_NO_SUCH_FILE; - for (i=0; - (fname = pvfs_list_next(dir, i)); - i++) { + ofs = 0; + + while ((fname = pvfs_list_next(dir, &ofs))) { /* this seems to be a special case */ if ((unl->in.attrib & FILE_ATTRIBUTE_DIRECTORY) && (strcmp(fname, ".") == 0 || @@ -121,7 +117,7 @@ NTSTATUS pvfs_unlink(struct ntvfs_module_context *ntvfs, return NT_STATUS_OBJECT_NAME_INVALID; } - status = pvfs_unlink_one(pvfs, req, dir->unix_path, fname, unl->in.attrib); + status = pvfs_unlink_one(pvfs, req, pvfs_list_unix_path(dir), fname, unl->in.attrib); if (NT_STATUS_IS_OK(status)) { total_deleted++; } diff --git a/source4/ntvfs/posix/vfs_posix.h b/source4/ntvfs/posix/vfs_posix.h index 48a2ba9288..19408848fd 100644 --- a/source4/ntvfs/posix/vfs_posix.h +++ b/source4/ntvfs/posix/vfs_posix.h @@ -75,15 +75,6 @@ struct pvfs_filename { }; -/* this holds a list of file names for a search. We deliberately do - not hold the file stat information here to minimise the memory - overhead of idle searches */ -struct pvfs_dir { - uint_t count; - const char *unix_path; - const char **names; -}; - /* the state of a search started with pvfs_search_first() */ struct pvfs_search_state { struct pvfs_state *pvfs; |