Line data Source code
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 4186 : static NTSTATUS pvfs_list_no_wildcard(struct pvfs_state *pvfs, struct pvfs_filename *name,
65 : const char *pattern, struct pvfs_dir *dir)
66 : {
67 4186 : if (!name->exists) {
68 0 : return NT_STATUS_OBJECT_NAME_NOT_FOUND;
69 : }
70 :
71 4186 : dir->pvfs = pvfs;
72 4186 : dir->no_wildcard = true;
73 4186 : dir->end_of_search = false;
74 4186 : dir->unix_path = talloc_strdup(dir, name->full_name);
75 4186 : if (!dir->unix_path) {
76 0 : return NT_STATUS_NO_MEMORY;
77 : }
78 :
79 4186 : dir->single_name = talloc_strdup(dir, pattern);
80 4186 : if (!dir->single_name) {
81 0 : return NT_STATUS_NO_MEMORY;
82 : }
83 :
84 4186 : dir->dir = NULL;
85 4186 : dir->offset = 0;
86 4186 : dir->pattern = NULL;
87 :
88 4186 : return NT_STATUS_OK;
89 : }
90 :
91 : /*
92 : destroy an open search
93 : */
94 5425 : static int pvfs_dirlist_destructor(struct pvfs_dir *dir)
95 : {
96 5425 : if (dir->dir) closedir(dir->dir);
97 5425 : 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 9613 : 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 9613 : (*dirp) = talloc_zero(mem_ctx, struct pvfs_dir);
112 9613 : if (*dirp == NULL) {
113 0 : return NT_STATUS_NO_MEMORY;
114 : }
115 :
116 9613 : dir = *dirp;
117 :
118 : /* split the unix path into a directory + pattern */
119 9613 : pattern = strrchr(name->full_name, '/');
120 9613 : if (!pattern) {
121 : /* this should not happen, as pvfs_unix_path is supposed to
122 : return an absolute path */
123 0 : return NT_STATUS_UNSUCCESSFUL;
124 : }
125 :
126 9613 : *pattern++ = 0;
127 :
128 9613 : if (!name->has_wildcard) {
129 4186 : return pvfs_list_no_wildcard(pvfs, name, pattern, dir);
130 : }
131 :
132 5427 : dir->unix_path = talloc_strdup(dir, name->full_name);
133 5427 : if (!dir->unix_path) {
134 0 : return NT_STATUS_NO_MEMORY;
135 : }
136 :
137 5427 : dir->pattern = talloc_strdup(dir, pattern);
138 5427 : if (dir->pattern == NULL) {
139 0 : return NT_STATUS_NO_MEMORY;
140 : }
141 :
142 5427 : dir->dir = opendir(name->full_name);
143 5427 : if (!dir->dir) {
144 2 : return pvfs_map_errno(pvfs, errno);
145 : }
146 :
147 5425 : dir->pvfs = pvfs;
148 5425 : dir->no_wildcard = false;
149 5425 : dir->end_of_search = false;
150 5425 : dir->offset = DIR_OFFSET_DOT;
151 5425 : dir->name_cache = talloc_zero_array(dir,
152 : struct name_cache_entry,
153 : NAME_CACHE_SIZE);
154 5425 : if (dir->name_cache == NULL) {
155 0 : talloc_free(dir);
156 0 : return NT_STATUS_NO_MEMORY;
157 : }
158 :
159 5425 : talloc_set_destructor(dir, pvfs_dirlist_destructor);
160 :
161 5425 : return NT_STATUS_OK;
162 : }
163 :
164 : /*
165 : add an entry to the local cache
166 : */
167 128397 : static void dcache_add(struct pvfs_dir *dir, const char *name)
168 : {
169 : struct name_cache_entry *e;
170 :
171 128397 : dir->name_cache_index = (dir->name_cache_index+1) % NAME_CACHE_SIZE;
172 128397 : e = &dir->name_cache[dir->name_cache_index];
173 :
174 128397 : if (e->name) talloc_free(e->name);
175 :
176 128397 : e->name = talloc_strdup(dir->name_cache, name);
177 128397 : e->offset = dir->offset;
178 128397 : }
179 :
180 : /*
181 : return the next entry
182 : */
183 140405 : const char *pvfs_list_next(struct pvfs_dir *dir, off_t *ofs)
184 : {
185 : struct dirent *de;
186 140405 : enum protocol_types protocol = dir->pvfs->ntvfs->ctx->protocol;
187 :
188 : /* non-wildcard searches are easy */
189 140405 : if (dir->no_wildcard) {
190 8171 : dir->end_of_search = true;
191 8171 : if (*ofs != 0) return NULL;
192 4186 : (*ofs)++;
193 4186 : 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 132234 : if (*ofs == DIR_OFFSET_DOT) {
200 5425 : (*ofs) = DIR_OFFSET_DOTDOT;
201 5425 : dir->offset = *ofs;
202 5425 : if (ms_fnmatch_protocol(dir->pattern, ".", protocol,
203 : false) == 0) {
204 5218 : dcache_add(dir, ".");
205 5218 : return ".";
206 : }
207 : }
208 :
209 127016 : if (*ofs == DIR_OFFSET_DOTDOT) {
210 3387 : (*ofs) = DIR_OFFSET_BASE;
211 3387 : dir->offset = *ofs;
212 3387 : if (ms_fnmatch_protocol(dir->pattern, "..", protocol,
213 : false) == 0) {
214 3180 : dcache_add(dir, "..");
215 3180 : return "..";
216 : }
217 : }
218 :
219 123836 : if (*ofs == DIR_OFFSET_BASE) {
220 3451 : rewinddir(dir->dir);
221 120385 : } else if (*ofs != dir->offset) {
222 197 : seekdir(dir->dir, (*ofs) - DIR_OFFSET_BASE);
223 : }
224 123836 : dir->offset = *ofs;
225 :
226 255087 : while ((de = readdir(dir->dir))) {
227 127440 : const char *dname = de->d_name;
228 :
229 127440 : if (ISDOT(dname) || ISDOTDOT(dname)) {
230 6875 : continue;
231 : }
232 :
233 120565 : if (ms_fnmatch_protocol(dir->pattern, dname, protocol,
234 : false) != 0) {
235 567 : char *short_name = pvfs_short_name_component(dir->pvfs, dname);
236 726 : if (short_name == NULL ||
237 159 : ms_fnmatch_protocol(dir->pattern, short_name,
238 : protocol, false) != 0) {
239 566 : talloc_free(short_name);
240 566 : continue;
241 : }
242 1 : talloc_free(short_name);
243 : }
244 :
245 119999 : dir->offset = telldir(dir->dir) + DIR_OFFSET_BASE;
246 119999 : (*ofs) = dir->offset;
247 :
248 119999 : dcache_add(dir, dname);
249 :
250 119999 : return dname;
251 : }
252 :
253 3837 : dir->end_of_search = true;
254 3837 : return NULL;
255 : }
256 :
257 : /*
258 : return unix directory of an open search
259 : */
260 130545 : const char *pvfs_list_unix_path(struct pvfs_dir *dir)
261 : {
262 130545 : return dir->unix_path;
263 : }
264 :
265 : /*
266 : return true if end of search has been reached
267 : */
268 3176 : bool pvfs_list_eos(struct pvfs_dir *dir, off_t ofs)
269 : {
270 3176 : return dir->end_of_search;
271 : }
272 :
273 : /*
274 : seek to the given name
275 : */
276 241 : NTSTATUS pvfs_list_seek(struct pvfs_dir *dir, const char *name, off_t *ofs)
277 : {
278 : struct dirent *de;
279 : int i;
280 :
281 241 : dir->end_of_search = false;
282 :
283 241 : if (ISDOT(name)) {
284 1 : dir->offset = DIR_OFFSET_DOTDOT;
285 1 : *ofs = dir->offset;
286 1 : return NT_STATUS_OK;
287 : }
288 :
289 240 : if (ISDOTDOT(name)) {
290 0 : dir->offset = DIR_OFFSET_BASE;
291 0 : *ofs = dir->offset;
292 0 : return NT_STATUS_OK;
293 : }
294 :
295 1612 : for (i=dir->name_cache_index;i>=0;i--) {
296 1451 : struct name_cache_entry *e = &dir->name_cache[i];
297 1451 : if (e->name && strcasecmp_m(name, e->name) == 0) {
298 79 : *ofs = e->offset;
299 79 : return NT_STATUS_OK;
300 : }
301 : }
302 14390 : for (i=NAME_CACHE_SIZE-1;i>dir->name_cache_index;i--) {
303 14390 : struct name_cache_entry *e = &dir->name_cache[i];
304 14390 : if (e->name && strcasecmp_m(name, e->name) == 0) {
305 161 : *ofs = e->offset;
306 161 : return NT_STATUS_OK;
307 : }
308 : }
309 :
310 0 : rewinddir(dir->dir);
311 :
312 0 : while ((de = readdir(dir->dir))) {
313 0 : if (strcasecmp_m(name, de->d_name) == 0) {
314 0 : dir->offset = telldir(dir->dir) + DIR_OFFSET_BASE;
315 0 : *ofs = dir->offset;
316 0 : return NT_STATUS_OK;
317 : }
318 : }
319 :
320 0 : dir->end_of_search = true;
321 :
322 0 : return NT_STATUS_OBJECT_NAME_NOT_FOUND;
323 : }
324 :
325 : /*
326 : seek to the given offset
327 : */
328 116 : 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 116 : dir->end_of_search = false;
334 :
335 116 : if (resume_key == DIR_OFFSET_DOT) {
336 0 : *ofs = DIR_OFFSET_DOTDOT;
337 0 : return NT_STATUS_OK;
338 : }
339 :
340 116 : if (resume_key == DIR_OFFSET_DOTDOT) {
341 0 : *ofs = DIR_OFFSET_BASE;
342 0 : return NT_STATUS_OK;
343 : }
344 :
345 116 : if (resume_key == DIR_OFFSET_BASE) {
346 0 : rewinddir(dir->dir);
347 0 : if ((de=readdir(dir->dir)) == NULL) {
348 0 : dir->end_of_search = true;
349 0 : return NT_STATUS_OBJECT_NAME_NOT_FOUND;
350 : }
351 0 : *ofs = telldir(dir->dir) + DIR_OFFSET_BASE;
352 0 : dir->offset = *ofs;
353 0 : return NT_STATUS_OK;
354 : }
355 :
356 138 : for (i=dir->name_cache_index;i>=0;i--) {
357 138 : struct name_cache_entry *e = &dir->name_cache[i];
358 138 : if (resume_key == (uint32_t)e->offset) {
359 116 : *ofs = e->offset;
360 116 : return NT_STATUS_OK;
361 : }
362 : }
363 0 : for (i=NAME_CACHE_SIZE-1;i>dir->name_cache_index;i--) {
364 0 : struct name_cache_entry *e = &dir->name_cache[i];
365 0 : if (resume_key == (uint32_t)e->offset) {
366 0 : *ofs = e->offset;
367 0 : return NT_STATUS_OK;
368 : }
369 : }
370 :
371 0 : rewinddir(dir->dir);
372 :
373 0 : while ((de = readdir(dir->dir))) {
374 0 : dir->offset = telldir(dir->dir) + DIR_OFFSET_BASE;
375 0 : if (resume_key == (uint32_t)dir->offset) {
376 0 : *ofs = dir->offset;
377 0 : return NT_STATUS_OK;
378 : }
379 : }
380 :
381 0 : dir->end_of_search = true;
382 :
383 0 : return NT_STATUS_OBJECT_NAME_NOT_FOUND;
384 : }
385 :
386 :
387 : /*
388 : see if a directory is empty
389 : */
390 419 : bool pvfs_directory_empty(struct pvfs_state *pvfs, struct pvfs_filename *name)
391 : {
392 : struct dirent *de;
393 419 : DIR *dir = opendir(name->full_name);
394 419 : if (dir == NULL) {
395 2 : return true;
396 : }
397 :
398 1659 : while ((de = readdir(dir))) {
399 834 : if (!ISDOT(de->d_name) && !ISDOTDOT(de->d_name)) {
400 9 : closedir(dir);
401 9 : return false;
402 : }
403 : }
404 :
405 408 : closedir(dir);
406 408 : return true;
407 : }
|