This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
+ the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
directory listing functions for posix backend
struct pvfs_dir {
struct pvfs_state *pvfs;
- BOOL no_wildcard;
+ bool no_wildcard;
char *single_name;
const char *pattern;
off_t offset;
DIR *dir;
const char *unix_path;
- BOOL end_of_search;
+ bool end_of_search;
struct name_cache_entry *name_cache;
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
+
+ On XFS on linux telldir returns 0x7FFFFFFF at the end of a
+ directory. Thus the change from 0x80000002, as otherwise
+ 0x7FFFFFFF+0x80000002==1==DIR_OFFSET_DOTDOT
+*/
+#define DIR_OFFSET_DOT 0
+#define DIR_OFFSET_DOTDOT 1
+#define DIR_OFFSET_BASE 0x80000022
+
/*
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
}
dir->pvfs = pvfs;
- dir->no_wildcard = True;
- dir->end_of_search = False;
+ 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;
/*
destroy an open search
*/
-static int pvfs_dirlist_destructor(void *ptr)
+static int pvfs_dirlist_destructor(struct pvfs_dir *dir)
{
- struct pvfs_dir *dir = ptr;
if (dir->dir) closedir(dir->dir);
return 0;
}
char *pattern;
struct pvfs_dir *dir;
- (*dirp) = talloc_zero_p(mem_ctx, struct pvfs_dir);
+ (*dirp) = talloc_zero(mem_ctx, struct pvfs_dir);
if (*dirp == NULL) {
return NT_STATUS_NO_MEMORY;
}
}
dir->pvfs = pvfs;
- dir->no_wildcard = False;
- dir->end_of_search = False;
- dir->offset = 0;
- dir->name_cache = talloc_zero_array_p(dir,
- struct name_cache_entry,
- NAME_CACHE_SIZE);
+ dir->no_wildcard = false;
+ dir->end_of_search = false;
+ dir->offset = DIR_OFFSET_DOT;
+ dir->name_cache = talloc_zero_array(dir,
+ struct name_cache_entry,
+ NAME_CACHE_SIZE);
if (dir->name_cache == NULL) {
talloc_free(dir);
return NT_STATUS_NO_MEMORY;
return NT_STATUS_OK;
}
+/*
+ add an entry to the local cache
+*/
+static void dcache_add(struct pvfs_dir *dir, const char *name)
+{
+ struct name_cache_entry *e;
+
+ dir->name_cache_index = (dir->name_cache_index+1) % NAME_CACHE_SIZE;
+ e = &dir->name_cache[dir->name_cache_index];
+
+ if (e->name) talloc_free(e->name);
+
+ e->name = talloc_strdup(dir->name_cache, name);
+ e->offset = dir->offset;
+}
+
/*
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;
/* non-wildcard searches are easy */
if (dir->no_wildcard) {
- dir->end_of_search = True;
+ dir->end_of_search = true;
if (*ofs != 0) return NULL;
(*ofs)++;
return dir->single_name;
}
- if (*ofs != dir->offset) {
- seekdir(dir->dir, *ofs);
+ /* . and .. are handled separately as some unix systems will
+ not return them first in a directory, but windows client
+ may assume that these entries always appear first */
+ if (*ofs == DIR_OFFSET_DOT) {
+ (*ofs) = DIR_OFFSET_DOTDOT;
dir->offset = *ofs;
+ if (ms_fnmatch(dir->pattern, ".", protocol) == 0) {
+ dcache_add(dir, ".");
+ return ".";
+ }
}
+
+ if (*ofs == DIR_OFFSET_DOTDOT) {
+ (*ofs) = DIR_OFFSET_BASE;
+ dir->offset = *ofs;
+ if (ms_fnmatch(dir->pattern, "..", protocol) == 0) {
+ dcache_add(dir, "..");
+ return "..";
+ }
+ }
+
+ if (*ofs == DIR_OFFSET_BASE) {
+ rewinddir(dir->dir);
+ } else if (*ofs != dir->offset) {
+ seekdir(dir->dir, (*ofs) - DIR_OFFSET_BASE);
+ }
+ dir->offset = *ofs;
while ((de = readdir(dir->dir))) {
const char *dname = de->d_name;
- struct name_cache_entry *e;
- if (ms_fnmatch(dir->pattern, dname,
- dir->pvfs->tcon->smb_conn->negotiate.protocol) != 0) {
+ if (ISDOT(dname) || ISDOTDOT(dname)) {
+ continue;
+ }
+
+ if (ms_fnmatch(dir->pattern, dname, protocol) != 0) {
char *short_name = pvfs_short_name_component(dir->pvfs, dname);
if (short_name == NULL ||
- ms_fnmatch(dir->pattern, short_name,
- dir->pvfs->tcon->smb_conn->negotiate.protocol) != 0) {
+ ms_fnmatch(dir->pattern, short_name, protocol) != 0) {
talloc_free(short_name);
continue;
}
talloc_free(short_name);
}
- dir->offset = telldir(dir->dir);
+ dir->offset = telldir(dir->dir) + DIR_OFFSET_BASE;
(*ofs) = dir->offset;
- dir->name_cache_index = (dir->name_cache_index+1) % NAME_CACHE_SIZE;
- e = &dir->name_cache[dir->name_cache_index];
-
- if (e->name) talloc_free(e->name);
+ dcache_add(dir, dname);
- e->name = talloc_strdup(dir, de->d_name);
- e->offset = dir->offset;
-
- return e->name;
+ return dname;
}
- dir->end_of_search = True;
- pvfs_list_hibernate(dir);
+ dir->end_of_search = true;
return NULL;
}
-/*
- put the directory to sleep. Used between search calls to give the
- right directory change semantics
+/*
+ return unix directory of an open search
*/
-void pvfs_list_hibernate(struct pvfs_dir *dir)
+const char *pvfs_list_unix_path(struct pvfs_dir *dir)
{
- if (dir->dir) {
- closedir(dir->dir);
- dir->dir = NULL;
- }
+ return dir->unix_path;
}
+/*
+ return true if end of search has been reached
+*/
+bool pvfs_list_eos(struct pvfs_dir *dir, off_t ofs)
+{
+ return dir->end_of_search;
+}
-/*
- wake up the directory search
+/*
+ seek to the given name
*/
-NTSTATUS pvfs_list_wakeup(struct pvfs_dir *dir, uint_t *ofs)
+NTSTATUS pvfs_list_seek(struct pvfs_dir *dir, const char *name, off_t *ofs)
{
- if (dir->no_wildcard ||
- dir->dir != NULL) {
+ struct dirent *de;
+ int i;
+
+ dir->end_of_search = false;
+
+ if (ISDOT(name)) {
+ dir->offset = DIR_OFFSET_DOTDOT;
+ *ofs = dir->offset;
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);
+ if (ISDOTDOT(name)) {
+ dir->offset = DIR_OFFSET_BASE;
+ *ofs = dir->offset;
+ return NT_STATUS_OK;
}
- 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));
+ for (i=dir->name_cache_index;i>=0;i--) {
+ struct name_cache_entry *e = &dir->name_cache[i];
+ if (e->name && strcasecmp_m(name, e->name) == 0) {
+ *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 (e->name && strcasecmp_m(name, e->name) == 0) {
+ *ofs = e->offset;
+ return NT_STATUS_OK;
+ }
}
- return NT_STATUS_OK;
-}
-
+ rewinddir(dir->dir);
+ while ((de = readdir(dir->dir))) {
+ if (strcasecmp_m(name, de->d_name) == 0) {
+ dir->offset = telldir(dir->dir) + DIR_OFFSET_BASE;
+ *ofs = dir->offset;
+ return NT_STATUS_OK;
+ }
+ }
-/*
- return unix directory of an open search
-*/
-const char *pvfs_list_unix_path(struct pvfs_dir *dir)
-{
- return dir->unix_path;
-}
+ dir->end_of_search = true;
-/*
- return True if end of search has been reached
-*/
-BOOL pvfs_list_eos(struct pvfs_dir *dir, uint_t ofs)
-{
- return dir->end_of_search;
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
/*
- seek to the given name
+ seek to the given offset
*/
-NTSTATUS pvfs_list_seek(struct pvfs_dir *dir, const char *name, uint_t *ofs)
+NTSTATUS pvfs_list_seek_ofs(struct pvfs_dir *dir, uint32_t resume_key, off_t *ofs)
{
struct dirent *de;
- NTSTATUS status;
int i;
- status = pvfs_list_wakeup(dir, ofs);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
+ 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 (e->name && StrCaseCmp(name, e->name) == 0) {
+ 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 (e->name && StrCaseCmp(name, e->name) == 0) {
+ if (resume_key == (uint32_t)e->offset) {
*ofs = e->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);
+ 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;
-
- pvfs_list_hibernate(dir);
+ dir->end_of_search = true;
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
+
+
+/*
+ see if a directory is empty
+*/
+bool pvfs_directory_empty(struct pvfs_state *pvfs, struct pvfs_filename *name)
+{
+ struct dirent *de;
+ DIR *dir = opendir(name->full_name);
+ if (dir == NULL) {
+ return true;
+ }
+
+ while ((de = readdir(dir))) {
+ if (!ISDOT(de->d_name) && !ISDOTDOT(de->d_name)) {
+ closedir(dir);
+ return false;
+ }
+ }
+
+ closedir(dir);
+ return true;
+}