2 Unix SMB/CIFS implementation.
4 Copyright (C) Andrew Tridgell 2004
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 directory listing functions for posix backend
25 #include "vfs_posix.h"
26 #include "system/dir.h"
28 #define NAME_CACHE_SIZE 100
30 struct name_cache_entry {
36 struct pvfs_state *pvfs;
42 const char *unix_path;
44 struct name_cache_entry *name_cache;
45 uint32_t name_cache_index;
48 #define DIR_OFFSET_DOT 0
49 #define DIR_OFFSET_DOTDOT 1
50 #define DIR_OFFSET_BASE 2
54 a special directory listing case where the pattern has no wildcard. We can just do a single stat()
55 thus avoiding the more expensive directory scan
57 static NTSTATUS pvfs_list_no_wildcard(struct pvfs_state *pvfs, struct pvfs_filename *name,
58 const char *pattern, struct pvfs_dir *dir)
61 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
65 dir->no_wildcard = True;
66 dir->end_of_search = False;
67 dir->unix_path = talloc_strdup(dir, name->full_name);
68 if (!dir->unix_path) {
69 return NT_STATUS_NO_MEMORY;
72 dir->single_name = talloc_strdup(dir, pattern);
73 if (!dir->single_name) {
74 return NT_STATUS_NO_MEMORY;
85 destroy an open search
87 static int pvfs_dirlist_destructor(void *ptr)
89 struct pvfs_dir *dir = ptr;
90 if (dir->dir) closedir(dir->dir);
95 start to read a directory
97 if the pattern matches no files then we return NT_STATUS_OK, with dir->count = 0
99 NTSTATUS pvfs_list_start(struct pvfs_state *pvfs, struct pvfs_filename *name,
100 TALLOC_CTX *mem_ctx, struct pvfs_dir **dirp)
103 struct pvfs_dir *dir;
105 (*dirp) = talloc_zero(mem_ctx, struct pvfs_dir);
107 return NT_STATUS_NO_MEMORY;
112 /* split the unix path into a directory + pattern */
113 pattern = strrchr(name->full_name, '/');
115 /* this should not happen, as pvfs_unix_path is supposed to
116 return an absolute path */
117 return NT_STATUS_UNSUCCESSFUL;
122 if (!name->has_wildcard) {
123 return pvfs_list_no_wildcard(pvfs, name, pattern, dir);
126 dir->unix_path = talloc_strdup(dir, name->full_name);
127 if (!dir->unix_path) {
128 return NT_STATUS_NO_MEMORY;
131 dir->pattern = talloc_strdup(dir, pattern);
132 if (dir->pattern == NULL) {
133 return NT_STATUS_NO_MEMORY;
136 dir->dir = opendir(name->full_name);
138 return pvfs_map_errno(pvfs, errno);
142 dir->no_wildcard = False;
143 dir->end_of_search = False;
145 dir->name_cache = talloc_zero_array(dir,
146 struct name_cache_entry,
148 if (dir->name_cache == NULL) {
150 return NT_STATUS_NO_MEMORY;
153 talloc_set_destructor(dir, pvfs_dirlist_destructor);
159 add an entry to the local cache
161 static void dcache_add(struct pvfs_dir *dir, const char *name)
163 struct name_cache_entry *e;
165 dir->name_cache_index = (dir->name_cache_index+1) % NAME_CACHE_SIZE;
166 e = &dir->name_cache[dir->name_cache_index];
168 if (e->name) talloc_free(e->name);
170 e->name = talloc_strdup(dir->name_cache, name);
171 e->offset = dir->offset;
175 return the next entry
177 const char *pvfs_list_next(struct pvfs_dir *dir, uint_t *ofs)
180 enum protocol_types protocol = dir->pvfs->tcon->smb_conn->negotiate.protocol;
182 /* non-wildcard searches are easy */
183 if (dir->no_wildcard) {
184 dir->end_of_search = True;
185 if (*ofs != 0) return NULL;
187 return dir->single_name;
190 /* . and .. are handled separately as some unix systems will
191 not return them first in a directory, but windows client
192 may assume that these entries always appear first */
193 if (*ofs == DIR_OFFSET_DOT) {
196 if (ms_fnmatch(dir->pattern, ".", protocol) == 0) {
197 dcache_add(dir, ".");
202 if (*ofs == DIR_OFFSET_DOTDOT) {
205 if (ms_fnmatch(dir->pattern, "..", protocol) == 0) {
206 dcache_add(dir, "..");
211 if (*ofs == DIR_OFFSET_BASE) {
213 } else if (*ofs != dir->offset) {
214 seekdir(dir->dir, (*ofs) - DIR_OFFSET_BASE);
218 while ((de = readdir(dir->dir))) {
219 const char *dname = de->d_name;
221 if (strcmp(dname, ".") == 0 ||
222 strcmp(dname, "..") == 0) {
226 if (ms_fnmatch(dir->pattern, dname, protocol) != 0) {
227 char *short_name = pvfs_short_name_component(dir->pvfs, dname);
228 if (short_name == NULL ||
229 ms_fnmatch(dir->pattern, short_name, protocol) != 0) {
230 talloc_free(short_name);
233 talloc_free(short_name);
236 dir->offset = telldir(dir->dir) + DIR_OFFSET_BASE;
237 (*ofs) = dir->offset;
239 dcache_add(dir, dname);
244 dir->end_of_search = True;
249 return unix directory of an open search
251 const char *pvfs_list_unix_path(struct pvfs_dir *dir)
253 return dir->unix_path;
257 return True if end of search has been reached
259 BOOL pvfs_list_eos(struct pvfs_dir *dir, uint_t ofs)
261 return dir->end_of_search;
265 seek to the given name
267 NTSTATUS pvfs_list_seek(struct pvfs_dir *dir, const char *name, uint_t *ofs)
272 if (strcmp(name, ".") == 0) {
273 dir->offset = DIR_OFFSET_DOTDOT;
278 if (strcmp(name, "..") == 0) {
279 dir->offset = DIR_OFFSET_BASE;
284 for (i=dir->name_cache_index;i>=0;i--) {
285 struct name_cache_entry *e = &dir->name_cache[i];
286 if (e->name && strcasecmp_m(name, e->name) == 0) {
291 for (i=NAME_CACHE_SIZE-1;i>dir->name_cache_index;i--) {
292 struct name_cache_entry *e = &dir->name_cache[i];
293 if (e->name && strcasecmp_m(name, e->name) == 0) {
301 while ((de = readdir(dir->dir))) {
302 if (strcasecmp_m(name, de->d_name) == 0) {
303 dir->offset = telldir(dir->dir) + DIR_OFFSET_BASE;
309 dir->end_of_search = True;
311 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
316 see if a directory is empty
318 BOOL pvfs_directory_empty(struct pvfs_state *pvfs, struct pvfs_filename *name)
321 DIR *dir = opendir(name->full_name);
326 while ((de = readdir(dir))) {
327 if (strcmp(de->d_name, ".") != 0 &&
328 strcmp(de->d_name, "..") != 0) {