r9792: Rename StrCaseCmp -> strcasecmp_m. All these years I was thinking
[jra/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 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->tcon->smb_conn->negotiate.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 (strcmp(dname, ".") == 0 ||
222                     strcmp(dname, "..") == 0) {
223                         continue;
224                 }
225
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);
231                                 continue;
232                         }
233                         talloc_free(short_name);
234                 }
235
236                 dir->offset = telldir(dir->dir) + DIR_OFFSET_BASE;
237                 (*ofs) = dir->offset;
238
239                 dcache_add(dir, dname);
240
241                 return dname;
242         }
243
244         dir->end_of_search = True;
245         return NULL;
246 }
247
248 /*
249   return unix directory of an open search
250 */
251 const char *pvfs_list_unix_path(struct pvfs_dir *dir)
252 {
253         return dir->unix_path;
254 }
255
256 /*
257   return True if end of search has been reached
258 */
259 BOOL pvfs_list_eos(struct pvfs_dir *dir, uint_t ofs)
260 {
261         return dir->end_of_search;
262 }
263
264 /*
265   seek to the given name
266 */
267 NTSTATUS pvfs_list_seek(struct pvfs_dir *dir, const char *name, uint_t *ofs)
268 {
269         struct dirent *de;
270         int i;
271
272         if (strcmp(name, ".") == 0) {
273                 dir->offset = DIR_OFFSET_DOTDOT;
274                 *ofs = dir->offset;
275                 return NT_STATUS_OK;
276         }
277
278         if (strcmp(name, "..") == 0) {
279                 dir->offset = DIR_OFFSET_BASE;
280                 *ofs = dir->offset;
281                 return NT_STATUS_OK;
282         }
283
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) {
287                         *ofs = e->offset;
288                         return NT_STATUS_OK;
289                 }
290         }
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) {
294                         *ofs = e->offset;
295                         return NT_STATUS_OK;
296                 }
297         }
298
299         rewinddir(dir->dir);
300
301         while ((de = readdir(dir->dir))) {
302                 if (strcasecmp_m(name, de->d_name) == 0) {
303                         dir->offset = telldir(dir->dir) + DIR_OFFSET_BASE;
304                         *ofs = dir->offset;
305                         return NT_STATUS_OK;
306                 }
307         }
308
309         dir->end_of_search = True;
310
311         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
312 }
313
314
315 /*
316   see if a directory is empty
317 */
318 BOOL pvfs_directory_empty(struct pvfs_state *pvfs, struct pvfs_filename *name)
319 {
320         struct dirent *de;
321         DIR *dir = opendir(name->full_name);
322         if (dir == NULL) {
323                 return True;
324         }
325
326         while ((de = readdir(dir))) {
327                 if (strcmp(de->d_name, ".") != 0 &&
328                     strcmp(de->d_name, "..") != 0) {
329                         closedir(dir);
330                         return False;
331                 }
332         }
333
334         closedir(dir);
335         return True;
336 }