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 */
pvfs_list_no_wildcard(struct pvfs_state * pvfs,struct pvfs_filename * name,const char * pattern,struct pvfs_dir * dir)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 */
pvfs_dirlist_destructor(struct pvfs_dir * dir)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 */
pvfs_list_start(struct pvfs_state * pvfs,struct pvfs_filename * name,TALLOC_CTX * mem_ctx,struct pvfs_dir ** dirp)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 */
dcache_add(struct pvfs_dir * dir,const char * name)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 */
pvfs_list_next(struct pvfs_dir * dir,off_t * ofs)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,
203 false) == 0) {
204 dcache_add(dir, ".");
205 return ".";
206 }
207 }
208
209 if (*ofs == DIR_OFFSET_DOTDOT) {
210 (*ofs) = DIR_OFFSET_BASE;
211 dir->offset = *ofs;
212 if (ms_fnmatch_protocol(dir->pattern, "..", protocol,
213 false) == 0) {
214 dcache_add(dir, "..");
215 return "..";
216 }
217 }
218
219 if (*ofs == DIR_OFFSET_BASE) {
220 rewinddir(dir->dir);
221 } else if (*ofs != dir->offset) {
222 seekdir(dir->dir, (*ofs) - DIR_OFFSET_BASE);
223 }
224 dir->offset = *ofs;
225
226 while ((de = readdir(dir->dir))) {
227 const char *dname = de->d_name;
228
229 if (ISDOT(dname) || ISDOTDOT(dname)) {
230 continue;
231 }
232
233 if (ms_fnmatch_protocol(dir->pattern, dname, protocol,
234 false) != 0) {
235 char *short_name = pvfs_short_name_component(dir->pvfs, dname);
236 if (short_name == NULL ||
237 ms_fnmatch_protocol(dir->pattern, short_name,
238 protocol, false) != 0) {
239 talloc_free(short_name);
240 continue;
241 }
242 talloc_free(short_name);
243 }
244
245 dir->offset = telldir(dir->dir) + DIR_OFFSET_BASE;
246 (*ofs) = dir->offset;
247
248 dcache_add(dir, dname);
249
250 return dname;
251 }
252
253 dir->end_of_search = true;
254 return NULL;
255 }
256
257 /*
258 return unix directory of an open search
259 */
pvfs_list_unix_path(struct pvfs_dir * dir)260 const char *pvfs_list_unix_path(struct pvfs_dir *dir)
261 {
262 return dir->unix_path;
263 }
264
265 /*
266 return true if end of search has been reached
267 */
pvfs_list_eos(struct pvfs_dir * dir,off_t ofs)268 bool pvfs_list_eos(struct pvfs_dir *dir, off_t ofs)
269 {
270 return dir->end_of_search;
271 }
272
273 /*
274 seek to the given name
275 */
pvfs_list_seek(struct pvfs_dir * dir,const char * name,off_t * ofs)276 NTSTATUS pvfs_list_seek(struct pvfs_dir *dir, const char *name, off_t *ofs)
277 {
278 struct dirent *de;
279 int i;
280
281 dir->end_of_search = false;
282
283 if (ISDOT(name)) {
284 dir->offset = DIR_OFFSET_DOTDOT;
285 *ofs = dir->offset;
286 return NT_STATUS_OK;
287 }
288
289 if (ISDOTDOT(name)) {
290 dir->offset = DIR_OFFSET_BASE;
291 *ofs = dir->offset;
292 return NT_STATUS_OK;
293 }
294
295 for (i=dir->name_cache_index;i>=0;i--) {
296 struct name_cache_entry *e = &dir->name_cache[i];
297 if (e->name && strcasecmp_m(name, e->name) == 0) {
298 *ofs = e->offset;
299 return NT_STATUS_OK;
300 }
301 }
302 for (i=NAME_CACHE_SIZE-1;i>dir->name_cache_index;i--) {
303 struct name_cache_entry *e = &dir->name_cache[i];
304 if (e->name && strcasecmp_m(name, e->name) == 0) {
305 *ofs = e->offset;
306 return NT_STATUS_OK;
307 }
308 }
309
310 rewinddir(dir->dir);
311
312 while ((de = readdir(dir->dir))) {
313 if (strcasecmp_m(name, de->d_name) == 0) {
314 dir->offset = telldir(dir->dir) + DIR_OFFSET_BASE;
315 *ofs = dir->offset;
316 return NT_STATUS_OK;
317 }
318 }
319
320 dir->end_of_search = true;
321
322 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
323 }
324
325 /*
326 seek to the given offset
327 */
pvfs_list_seek_ofs(struct pvfs_dir * dir,uint32_t resume_key,off_t * ofs)328 NTSTATUS pvfs_list_seek_ofs(struct pvfs_dir *dir, uint32_t resume_key, off_t *ofs)
329 {
330 struct dirent *de;
331 int i;
332
333 dir->end_of_search = false;
334
335 if (resume_key == DIR_OFFSET_DOT) {
336 *ofs = DIR_OFFSET_DOTDOT;
337 return NT_STATUS_OK;
338 }
339
340 if (resume_key == DIR_OFFSET_DOTDOT) {
341 *ofs = DIR_OFFSET_BASE;
342 return NT_STATUS_OK;
343 }
344
345 if (resume_key == DIR_OFFSET_BASE) {
346 rewinddir(dir->dir);
347 if ((de=readdir(dir->dir)) == NULL) {
348 dir->end_of_search = true;
349 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
350 }
351 *ofs = telldir(dir->dir) + DIR_OFFSET_BASE;
352 dir->offset = *ofs;
353 return NT_STATUS_OK;
354 }
355
356 for (i=dir->name_cache_index;i>=0;i--) {
357 struct name_cache_entry *e = &dir->name_cache[i];
358 if (resume_key == (uint32_t)e->offset) {
359 *ofs = e->offset;
360 return NT_STATUS_OK;
361 }
362 }
363 for (i=NAME_CACHE_SIZE-1;i>dir->name_cache_index;i--) {
364 struct name_cache_entry *e = &dir->name_cache[i];
365 if (resume_key == (uint32_t)e->offset) {
366 *ofs = e->offset;
367 return NT_STATUS_OK;
368 }
369 }
370
371 rewinddir(dir->dir);
372
373 while ((de = readdir(dir->dir))) {
374 dir->offset = telldir(dir->dir) + DIR_OFFSET_BASE;
375 if (resume_key == (uint32_t)dir->offset) {
376 *ofs = dir->offset;
377 return NT_STATUS_OK;
378 }
379 }
380
381 dir->end_of_search = true;
382
383 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
384 }
385
386
387 /*
388 see if a directory is empty
389 */
pvfs_directory_empty(struct pvfs_state * pvfs,struct pvfs_filename * name)390 bool pvfs_directory_empty(struct pvfs_state *pvfs, struct pvfs_filename *name)
391 {
392 struct dirent *de;
393 DIR *dir = opendir(name->full_name);
394 if (dir == NULL) {
395 return true;
396 }
397
398 while ((de = readdir(dir))) {
399 if (!ISDOT(de->d_name) && !ISDOTDOT(de->d_name)) {
400 closedir(dir);
401 return false;
402 }
403 }
404
405 closedir(dir);
406 return true;
407 }
408