diff options
Diffstat (limited to 'source4/ntvfs/posix/pvfs_dirlist.c')
-rw-r--r-- | source4/ntvfs/posix/pvfs_dirlist.c | 210 |
1 files changed, 148 insertions, 62 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; } |