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