517f5b68d0e8ee0f1466e5f4fa120dc91b936bfc
[bbaumbach/samba-autobuild/.git] / source4 / ntvfs / posix / pvfs_dirlist.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Copyright (C) Andrew Tridgell 2004
5
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.
10    
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.
15    
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.
19 */
20 /*
21   directory listing functions for posix backend
22 */
23
24 #include "includes.h"
25 #include "vfs_posix.h"
26 #include "system/dir.h"
27
28 #define NAME_CACHE_SIZE 100
29
30 struct name_cache_entry {
31         char *name;
32         off_t offset;
33 };
34
35 struct pvfs_dir {
36         struct pvfs_state *pvfs;
37         BOOL no_wildcard;
38         char *single_name;
39         const char *pattern;
40         off_t offset;
41         DIR *dir;
42         const char *unix_path;
43         BOOL end_of_search;
44         struct name_cache_entry *name_cache;
45         uint32_t name_cache_index;
46 };
47
48 #define DIR_OFFSET_DOT    0
49 #define DIR_OFFSET_DOTDOT 1
50 #define DIR_OFFSET_BASE   2
51
52
53 /*
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
56 */
57 static NTSTATUS pvfs_list_no_wildcard(struct pvfs_state *pvfs, struct pvfs_filename *name, 
58                                       const char *pattern, struct pvfs_dir *dir)
59 {
60         if (!name->exists) {
61                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
62         }
63
64         dir->pvfs = pvfs;
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;
70         }
71
72         dir->single_name = talloc_strdup(dir, pattern);
73         if (!dir->single_name) {
74                 return NT_STATUS_NO_MEMORY;
75         }
76
77         dir->dir = NULL;
78         dir->offset = 0;
79         dir->pattern = NULL;
80
81         return NT_STATUS_OK;
82 }
83
84 /*
85   destroy an open search
86 */
87 static int pvfs_dirlist_destructor(void *ptr)
88 {
89         struct pvfs_dir *dir = ptr;
90         if (dir->dir) closedir(dir->dir);
91         return 0;
92 }
93
94 /*
95   start to read a directory 
96
97   if the pattern matches no files then we return NT_STATUS_OK, with dir->count = 0
98 */
99 NTSTATUS pvfs_list_start(struct pvfs_state *pvfs, struct pvfs_filename *name, 
100                          TALLOC_CTX *mem_ctx, struct pvfs_dir **dirp)
101 {
102         char *pattern;
103         struct pvfs_dir *dir;
104
105         (*dirp) = talloc_zero(mem_ctx, struct pvfs_dir);
106         if (*dirp == NULL) {
107                 return NT_STATUS_NO_MEMORY;
108         }
109         
110         dir = *dirp;
111
112         /* split the unix path into a directory + pattern */
113         pattern = strrchr(name->full_name, '/');
114         if (!pattern) {
115                 /* this should not happen, as pvfs_unix_path is supposed to 
116                    return an absolute path */
117                 return NT_STATUS_UNSUCCESSFUL;
118         }
119
120         *pattern++ = 0;
121
122         if (!name->has_wildcard) {
123                 return pvfs_list_no_wildcard(pvfs, name, pattern, dir);
124         }
125
126         dir->unix_path = talloc_strdup(dir, name->full_name);
127         if (!dir->unix_path) {
128                 return NT_STATUS_NO_MEMORY;
129         }
130
131         dir->pattern = talloc_strdup(dir, pattern);
132         if (dir->pattern == NULL) {
133                 return NT_STATUS_NO_MEMORY;
134         }
135         
136         dir->dir = opendir(name->full_name);
137         if (!dir->dir) { 
138                 return pvfs_map_errno(pvfs, errno); 
139         }
140
141         dir->pvfs = pvfs;
142         dir->no_wildcard = False;
143         dir->end_of_search = False;
144         dir->offset = 0;
145         dir->name_cache = talloc_zero_array(dir, 
146                                             struct name_cache_entry, 
147                                             NAME_CACHE_SIZE);
148         if (dir->name_cache == NULL) {
149                 talloc_free(dir);
150                 return NT_STATUS_NO_MEMORY;
151         }
152
153         talloc_set_destructor(dir, pvfs_dirlist_destructor);
154
155         return NT_STATUS_OK;
156 }
157
158 /*
159   add an entry to the local cache
160 */
161 static void dcache_add(struct pvfs_dir *dir, const char *name)
162 {
163         struct name_cache_entry *e;
164
165         dir->name_cache_index = (dir->name_cache_index+1) % NAME_CACHE_SIZE;
166         e = &dir->name_cache[dir->name_cache_index];
167
168         if (e->name) talloc_free(e->name);
169
170         e->name = talloc_strdup(dir->name_cache, name);
171         e->offset = dir->offset;
172 }
173
174 /* 
175    return the next entry
176 */
177 const char *pvfs_list_next(struct pvfs_dir *dir, uint_t *ofs)
178 {
179         struct dirent *de;
180         enum protocol_types protocol = dir->pvfs->ntvfs->ctx->protocol;
181
182         /* non-wildcard searches are easy */
183         if (dir->no_wildcard) {
184                 dir->end_of_search = True;
185                 if (*ofs != 0) return NULL;
186                 (*ofs)++;
187                 return dir->single_name;
188         }
189
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) {
194                 (*ofs)++;
195                 dir->offset = *ofs;
196                 if (ms_fnmatch(dir->pattern, ".", protocol) == 0) {
197                         dcache_add(dir, ".");
198                         return ".";
199                 }
200         }
201
202         if (*ofs == DIR_OFFSET_DOTDOT) {
203                 (*ofs)++;
204                 dir->offset = *ofs;
205                 if (ms_fnmatch(dir->pattern, "..", protocol) == 0) {
206                         dcache_add(dir, "..");
207                         return "..";
208                 }
209         }
210
211         if (*ofs == DIR_OFFSET_BASE) {
212                 rewinddir(dir->dir);
213         } else if (*ofs != dir->offset) {
214                 seekdir(dir->dir, (*ofs) - DIR_OFFSET_BASE);
215         }
216         dir->offset = *ofs;
217         
218         while ((de = readdir(dir->dir))) {
219                 const char *dname = de->d_name;
220
221                 if (ISDOT(dname) || ISDOTDOT(dname)) {
222                         continue;
223                 }
224
225                 if (ms_fnmatch(dir->pattern, dname, protocol) != 0) {
226                         char *short_name = pvfs_short_name_component(dir->pvfs, dname);
227                         if (short_name == NULL ||
228                             ms_fnmatch(dir->pattern, short_name, protocol) != 0) {
229                                 talloc_free(short_name);
230                                 continue;
231                         }
232                         talloc_free(short_name);
233                 }
234
235                 dir->offset = telldir(dir->dir) + DIR_OFFSET_BASE;
236                 (*ofs) = dir->offset;
237
238                 dcache_add(dir, dname);
239
240                 return dname;
241         }
242
243         dir->end_of_search = True;
244         return NULL;
245 }
246
247 /*
248   return unix directory of an open search
249 */
250 const char *pvfs_list_unix_path(struct pvfs_dir *dir)
251 {
252         return dir->unix_path;
253 }
254
255 /*
256   return True if end of search has been reached
257 */
258 BOOL pvfs_list_eos(struct pvfs_dir *dir, uint_t ofs)
259 {
260         return dir->end_of_search;
261 }
262
263 /*
264   seek to the given name
265 */
266 NTSTATUS pvfs_list_seek(struct pvfs_dir *dir, const char *name, uint_t *ofs)
267 {
268         struct dirent *de;
269         int i;
270
271         if (ISDOT(name)) {
272                 dir->offset = DIR_OFFSET_DOTDOT;
273                 *ofs = dir->offset;
274                 return NT_STATUS_OK;
275         }
276
277         if (ISDOTDOT(name)) {
278                 dir->offset = DIR_OFFSET_BASE;
279                 *ofs = dir->offset;
280                 return NT_STATUS_OK;
281         }
282
283         for (i=dir->name_cache_index;i>=0;i--) {
284                 struct name_cache_entry *e = &dir->name_cache[i];
285                 if (e->name && strcasecmp_m(name, e->name) == 0) {
286                         *ofs = e->offset;
287                         return NT_STATUS_OK;
288                 }
289         }
290         for (i=NAME_CACHE_SIZE-1;i>dir->name_cache_index;i--) {
291                 struct name_cache_entry *e = &dir->name_cache[i];
292                 if (e->name && strcasecmp_m(name, e->name) == 0) {
293                         *ofs = e->offset;
294                         return NT_STATUS_OK;
295                 }
296         }
297
298         rewinddir(dir->dir);
299
300         while ((de = readdir(dir->dir))) {
301                 if (strcasecmp_m(name, de->d_name) == 0) {
302                         dir->offset = telldir(dir->dir) + DIR_OFFSET_BASE;
303                         *ofs = dir->offset;
304                         return NT_STATUS_OK;
305                 }
306         }
307
308         dir->end_of_search = True;
309
310         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
311 }
312
313
314 /*
315   see if a directory is empty
316 */
317 BOOL pvfs_directory_empty(struct pvfs_state *pvfs, struct pvfs_filename *name)
318 {
319         struct dirent *de;
320         DIR *dir = opendir(name->full_name);
321         if (dir == NULL) {
322                 return True;
323         }
324
325         while ((de = readdir(dir))) {
326                 if (!ISDOT(de->d_name) && !ISDOTDOT(de->d_name)) {
327                         closedir(dir);
328                         return False;
329                 }
330         }
331
332         closedir(dir);
333         return True;
334 }