summaryrefslogtreecommitdiff
path: root/source4/ntvfs/posix
diff options
context:
space:
mode:
Diffstat (limited to 'source4/ntvfs/posix')
-rw-r--r--source4/ntvfs/posix/pvfs_dirlist.c210
-rw-r--r--source4/ntvfs/posix/pvfs_search.c40
-rw-r--r--source4/ntvfs/posix/pvfs_unlink.c18
-rw-r--r--source4/ntvfs/posix/vfs_posix.h9
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;