dns: Use new DNS debugclass in DNS server
[kai/samba.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 3 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, see <http://www.gnu.org/licenses/>.
18 */
19 /*
20   directory listing functions for posix backend
21 */
22
23 #include "includes.h"
24 #include "vfs_posix.h"
25 #include "system/dir.h"
26
27 #define NAME_CACHE_SIZE 100
28
29 struct name_cache_entry {
30         char *name;
31         off_t offset;
32 };
33
34 struct pvfs_dir {
35         struct pvfs_state *pvfs;
36         bool no_wildcard;
37         char *single_name;
38         const char *pattern;
39         off_t offset;
40         DIR *dir;
41         const char *unix_path;
42         bool end_of_search;
43         struct name_cache_entry *name_cache;
44         uint32_t name_cache_index;
45 };
46
47 /* these three numbers are chosen to minimise the chances of a bad
48    interaction with the OS value for 'end of directory'. On IRIX
49    telldir() returns 0xFFFFFFFF at the end of a directory, and that
50    caused an infinite loop with the original values of 0,1,2
51
52    On XFS on linux telldir returns 0x7FFFFFFF at the end of a
53    directory. Thus the change from 0x80000002, as otherwise
54    0x7FFFFFFF+0x80000002==1==DIR_OFFSET_DOTDOT
55 */
56 #define DIR_OFFSET_DOT    0
57 #define DIR_OFFSET_DOTDOT 1
58 #define DIR_OFFSET_BASE   0x80000022
59
60 /*
61   a special directory listing case where the pattern has no wildcard. We can just do a single stat()
62   thus avoiding the more expensive directory scan
63 */
64 static NTSTATUS pvfs_list_no_wildcard(struct pvfs_state *pvfs, struct pvfs_filename *name, 
65                                       const char *pattern, struct pvfs_dir *dir)
66 {
67         if (!name->exists) {
68                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
69         }
70
71         dir->pvfs = pvfs;
72         dir->no_wildcard = true;
73         dir->end_of_search = false;
74         dir->unix_path = talloc_strdup(dir, name->full_name);
75         if (!dir->unix_path) {
76                 return NT_STATUS_NO_MEMORY;
77         }
78
79         dir->single_name = talloc_strdup(dir, pattern);
80         if (!dir->single_name) {
81                 return NT_STATUS_NO_MEMORY;
82         }
83
84         dir->dir = NULL;
85         dir->offset = 0;
86         dir->pattern = NULL;
87
88         return NT_STATUS_OK;
89 }
90
91 /*
92   destroy an open search
93 */
94 static int pvfs_dirlist_destructor(struct pvfs_dir *dir)
95 {
96         if (dir->dir) closedir(dir->dir);
97         return 0;
98 }
99
100 /*
101   start to read a directory 
102
103   if the pattern matches no files then we return NT_STATUS_OK, with dir->count = 0
104 */
105 NTSTATUS pvfs_list_start(struct pvfs_state *pvfs, struct pvfs_filename *name, 
106                          TALLOC_CTX *mem_ctx, struct pvfs_dir **dirp)
107 {
108         char *pattern;
109         struct pvfs_dir *dir;
110
111         (*dirp) = talloc_zero(mem_ctx, struct pvfs_dir);
112         if (*dirp == NULL) {
113                 return NT_STATUS_NO_MEMORY;
114         }
115         
116         dir = *dirp;
117
118         /* split the unix path into a directory + pattern */
119         pattern = strrchr(name->full_name, '/');
120         if (!pattern) {
121                 /* this should not happen, as pvfs_unix_path is supposed to 
122                    return an absolute path */
123                 return NT_STATUS_UNSUCCESSFUL;
124         }
125
126         *pattern++ = 0;
127
128         if (!name->has_wildcard) {
129                 return pvfs_list_no_wildcard(pvfs, name, pattern, dir);
130         }
131
132         dir->unix_path = talloc_strdup(dir, name->full_name);
133         if (!dir->unix_path) {
134                 return NT_STATUS_NO_MEMORY;
135         }
136
137         dir->pattern = talloc_strdup(dir, pattern);
138         if (dir->pattern == NULL) {
139                 return NT_STATUS_NO_MEMORY;
140         }
141         
142         dir->dir = opendir(name->full_name);
143         if (!dir->dir) { 
144                 return pvfs_map_errno(pvfs, errno); 
145         }
146
147         dir->pvfs = pvfs;
148         dir->no_wildcard = false;
149         dir->end_of_search = false;
150         dir->offset = DIR_OFFSET_DOT;
151         dir->name_cache = talloc_zero_array(dir, 
152                                             struct name_cache_entry, 
153                                             NAME_CACHE_SIZE);
154         if (dir->name_cache == NULL) {
155                 talloc_free(dir);
156                 return NT_STATUS_NO_MEMORY;
157         }
158
159         talloc_set_destructor(dir, pvfs_dirlist_destructor);
160
161         return NT_STATUS_OK;
162 }
163
164 /*
165   add an entry to the local cache
166 */
167 static void dcache_add(struct pvfs_dir *dir, const char *name)
168 {
169         struct name_cache_entry *e;
170
171         dir->name_cache_index = (dir->name_cache_index+1) % NAME_CACHE_SIZE;
172         e = &dir->name_cache[dir->name_cache_index];
173
174         if (e->name) talloc_free(e->name);
175
176         e->name = talloc_strdup(dir->name_cache, name);
177         e->offset = dir->offset;
178 }
179
180 /* 
181    return the next entry
182 */
183 const char *pvfs_list_next(struct pvfs_dir *dir, off_t *ofs)
184 {
185         struct dirent *de;
186         enum protocol_types protocol = dir->pvfs->ntvfs->ctx->protocol;
187
188         /* non-wildcard searches are easy */
189         if (dir->no_wildcard) {
190                 dir->end_of_search = true;
191                 if (*ofs != 0) return NULL;
192                 (*ofs)++;
193                 return dir->single_name;
194         }
195
196         /* . and .. are handled separately as some unix systems will
197            not return them first in a directory, but windows client
198            may assume that these entries always appear first */
199         if (*ofs == DIR_OFFSET_DOT) {
200                 (*ofs) = DIR_OFFSET_DOTDOT;
201                 dir->offset = *ofs;
202                 if (ms_fnmatch_protocol(dir->pattern, ".", protocol) == 0) {
203                         dcache_add(dir, ".");
204                         return ".";
205                 }
206         }
207
208         if (*ofs == DIR_OFFSET_DOTDOT) {
209                 (*ofs) = DIR_OFFSET_BASE;
210                 dir->offset = *ofs;
211                 if (ms_fnmatch_protocol(dir->pattern, "..", protocol) == 0) {
212                         dcache_add(dir, "..");
213                         return "..";
214                 }
215         }
216
217         if (*ofs == DIR_OFFSET_BASE) {
218                 rewinddir(dir->dir);
219         } else if (*ofs != dir->offset) {
220                 seekdir(dir->dir, (*ofs) - DIR_OFFSET_BASE);
221         }
222         dir->offset = *ofs;
223         
224         while ((de = readdir(dir->dir))) {
225                 const char *dname = de->d_name;
226
227                 if (ISDOT(dname) || ISDOTDOT(dname)) {
228                         continue;
229                 }
230
231                 if (ms_fnmatch_protocol(dir->pattern, dname, protocol) != 0) {
232                         char *short_name = pvfs_short_name_component(dir->pvfs, dname);
233                         if (short_name == NULL ||
234                             ms_fnmatch_protocol(dir->pattern, short_name, protocol) != 0) {
235                                 talloc_free(short_name);
236                                 continue;
237                         }
238                         talloc_free(short_name);
239                 }
240
241                 dir->offset = telldir(dir->dir) + DIR_OFFSET_BASE;
242                 (*ofs) = dir->offset;
243
244                 dcache_add(dir, dname);
245
246                 return dname;
247         }
248
249         dir->end_of_search = true;
250         return NULL;
251 }
252
253 /*
254   return unix directory of an open search
255 */
256 const char *pvfs_list_unix_path(struct pvfs_dir *dir)
257 {
258         return dir->unix_path;
259 }
260
261 /*
262   return true if end of search has been reached
263 */
264 bool pvfs_list_eos(struct pvfs_dir *dir, off_t ofs)
265 {
266         return dir->end_of_search;
267 }
268
269 /*
270   seek to the given name
271 */
272 NTSTATUS pvfs_list_seek(struct pvfs_dir *dir, const char *name, off_t *ofs)
273 {
274         struct dirent *de;
275         int i;
276
277         dir->end_of_search = false;
278
279         if (ISDOT(name)) {
280                 dir->offset = DIR_OFFSET_DOTDOT;
281                 *ofs = dir->offset;
282                 return NT_STATUS_OK;
283         }
284
285         if (ISDOTDOT(name)) {
286                 dir->offset = DIR_OFFSET_BASE;
287                 *ofs = dir->offset;
288                 return NT_STATUS_OK;
289         }
290
291         for (i=dir->name_cache_index;i>=0;i--) {
292                 struct name_cache_entry *e = &dir->name_cache[i];
293                 if (e->name && strcasecmp_m(name, e->name) == 0) {
294                         *ofs = e->offset;
295                         return NT_STATUS_OK;
296                 }
297         }
298         for (i=NAME_CACHE_SIZE-1;i>dir->name_cache_index;i--) {
299                 struct name_cache_entry *e = &dir->name_cache[i];
300                 if (e->name && strcasecmp_m(name, e->name) == 0) {
301                         *ofs = e->offset;
302                         return NT_STATUS_OK;
303                 }
304         }
305
306         rewinddir(dir->dir);
307
308         while ((de = readdir(dir->dir))) {
309                 if (strcasecmp_m(name, de->d_name) == 0) {
310                         dir->offset = telldir(dir->dir) + DIR_OFFSET_BASE;
311                         *ofs = dir->offset;
312                         return NT_STATUS_OK;
313                 }
314         }
315
316         dir->end_of_search = true;
317
318         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
319 }
320
321 /*
322   seek to the given offset
323 */
324 NTSTATUS pvfs_list_seek_ofs(struct pvfs_dir *dir, uint32_t resume_key, off_t *ofs)
325 {
326         struct dirent *de;
327         int i;
328
329         dir->end_of_search = false;
330
331         if (resume_key == DIR_OFFSET_DOT) {
332                 *ofs = DIR_OFFSET_DOTDOT;
333                 return NT_STATUS_OK;
334         }
335
336         if (resume_key == DIR_OFFSET_DOTDOT) {
337                 *ofs = DIR_OFFSET_BASE;
338                 return NT_STATUS_OK;
339         }
340
341         if (resume_key == DIR_OFFSET_BASE) {
342                 rewinddir(dir->dir);
343                 if ((de=readdir(dir->dir)) == NULL) {
344                         dir->end_of_search = true;
345                         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
346                 }
347                 *ofs = telldir(dir->dir) + DIR_OFFSET_BASE;
348                 dir->offset = *ofs;
349                 return NT_STATUS_OK;
350         }
351
352         for (i=dir->name_cache_index;i>=0;i--) {
353                 struct name_cache_entry *e = &dir->name_cache[i];
354                 if (resume_key == (uint32_t)e->offset) {
355                         *ofs = e->offset;
356                         return NT_STATUS_OK;
357                 }
358         }
359         for (i=NAME_CACHE_SIZE-1;i>dir->name_cache_index;i--) {
360                 struct name_cache_entry *e = &dir->name_cache[i];
361                 if (resume_key == (uint32_t)e->offset) {
362                         *ofs = e->offset;
363                         return NT_STATUS_OK;
364                 }
365         }
366
367         rewinddir(dir->dir);
368
369         while ((de = readdir(dir->dir))) {
370                 dir->offset = telldir(dir->dir) + DIR_OFFSET_BASE;
371                 if (resume_key == (uint32_t)dir->offset) {
372                         *ofs = dir->offset;
373                         return NT_STATUS_OK;
374                 }
375         }
376
377         dir->end_of_search = true;
378
379         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
380 }
381
382
383 /*
384   see if a directory is empty
385 */
386 bool pvfs_directory_empty(struct pvfs_state *pvfs, struct pvfs_filename *name)
387 {
388         struct dirent *de;
389         DIR *dir = opendir(name->full_name);
390         if (dir == NULL) {
391                 return true;
392         }
393
394         while ((de = readdir(dir))) {
395                 if (!ISDOT(de->d_name) && !ISDOTDOT(de->d_name)) {
396                         closedir(dir);
397                         return false;
398                 }
399         }
400
401         closedir(dir);
402         return true;
403 }