]>
Commit | Line | Data |
---|---|---|
76b87c03 | 1 | /* File tree walker functions. |
aff4519d | 2 | Copyright (C) 1996-2001, 2002, 2003 Free Software Foundation, Inc. |
47707456 | 3 | This file is part of the GNU C Library. |
76b87c03 | 4 | Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996. |
28f540f4 | 5 | |
47707456 | 6 | The GNU C Library is free software; you can redistribute it and/or |
41bdb6e2 AJ |
7 | modify it under the terms of the GNU Lesser General Public |
8 | License as published by the Free Software Foundation; either | |
9 | version 2.1 of the License, or (at your option) any later version. | |
28f540f4 | 10 | |
47707456 UD |
11 | The GNU C Library 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 GNU | |
41bdb6e2 | 14 | Lesser General Public License for more details. |
28f540f4 | 15 | |
41bdb6e2 AJ |
16 | You should have received a copy of the GNU Lesser General Public |
17 | License along with the GNU C Library; if not, write to the Free | |
18 | Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | |
19 | 02111-1307 USA. */ | |
28f540f4 | 20 | |
aff4519d UD |
21 | #ifdef HAVE_CONFIG_H |
22 | # include <config.h> | |
23 | #endif | |
24 | ||
76b87c03 | 25 | #include <dirent.h> |
28f540f4 | 26 | #include <errno.h> |
76b87c03 UD |
27 | #include <ftw.h> |
28 | #include <search.h> | |
28f540f4 RM |
29 | #include <stdlib.h> |
30 | #include <string.h> | |
76b87c03 | 31 | #include <unistd.h> |
aff4519d UD |
32 | #if HAVE_SYS_PARAM_H || defined _LIBC |
33 | # include <sys/param.h> | |
34 | #endif | |
35 | #ifdef _LIBC | |
36 | # include <include/sys/stat.h> | |
37 | #else | |
38 | # include <sys/stat.h> | |
39 | #endif | |
28f540f4 | 40 | |
76b87c03 UD |
41 | /* #define NDEBUG 1 */ |
42 | #include <assert.h> | |
28f540f4 | 43 | |
aff4519d UD |
44 | #ifndef _LIBC |
45 | # undef __chdir | |
46 | # define __chdir chdir | |
47 | # undef __closedir | |
48 | # define __closedir closedir | |
49 | # undef __fchdir | |
50 | # define __fchdir fchdir | |
51 | # undef __getcwd | |
52 | # define __getcwd getcwd | |
53 | # undef __opendir | |
54 | # define __opendir opendir | |
55 | # undef __readdir64 | |
56 | # define __readdir64 readdir | |
57 | # undef __tdestroy | |
58 | # define __tdestroy tdestroy | |
59 | # undef __tfind | |
60 | # define __tfind tfind | |
61 | # undef __tsearch | |
62 | # define __tsearch tsearch | |
63 | # undef internal_function | |
64 | # define internal_function /* empty */ | |
65 | # undef dirent64 | |
66 | # define dirent64 dirent | |
67 | # undef MAX | |
68 | # define MAX(a, b) ((a) > (b) ? (a) : (b)) | |
69 | #endif | |
70 | ||
71 | #ifndef __set_errno | |
72 | # define __set_errno(Val) errno = (Val) | |
73 | #endif | |
74 | ||
dfd2257a UD |
75 | /* Support for the LFS API version. */ |
76 | #ifndef FTW_NAME | |
77 | # define FTW_NAME ftw | |
78 | # define NFTW_NAME nftw | |
79 | # define INO_T ino_t | |
80 | # define STAT stat | |
dfd2257a UD |
81 | # define LXSTAT __lxstat |
82 | # define XSTAT __xstat | |
83 | # define FTW_FUNC_T __ftw_func_t | |
84 | # define NFTW_FUNC_T __nftw_func_t | |
85 | #endif | |
28f540f4 | 86 | |
76b87c03 UD |
87 | struct dir_data |
88 | { | |
89 | DIR *stream; | |
90 | char *content; | |
91 | }; | |
28f540f4 | 92 | |
d951286f UD |
93 | struct known_object |
94 | { | |
95 | dev_t dev; | |
dfd2257a | 96 | INO_T ino; |
d951286f UD |
97 | }; |
98 | ||
76b87c03 | 99 | struct ftw_data |
28f540f4 | 100 | { |
d951286f | 101 | /* Array with pointers to open directory streams. */ |
76b87c03 UD |
102 | struct dir_data **dirstreams; |
103 | size_t actdir; | |
104 | size_t maxdir; | |
28f540f4 | 105 | |
d951286f | 106 | /* Buffer containing name of currently processed object. */ |
76b87c03 UD |
107 | char *dirbuf; |
108 | size_t dirbufsize; | |
d951286f UD |
109 | |
110 | /* Passed as fourth argument to `nftw' callback. The `base' member | |
111 | tracks the content of the `dirbuf'. */ | |
76b87c03 | 112 | struct FTW ftw; |
28f540f4 | 113 | |
d951286f | 114 | /* Flags passed to `nftw' function. 0 for `ftw'. */ |
76b87c03 | 115 | int flags; |
28f540f4 | 116 | |
d951286f UD |
117 | /* Conversion array for flag values. It is the identity mapping for |
118 | `nftw' calls, otherwise it maps the values to those know by | |
119 | `ftw'. */ | |
390500b1 | 120 | const int *cvt_arr; |
28f540f4 | 121 | |
d951286f | 122 | /* Callback function. We always use the `nftw' form. */ |
dfd2257a | 123 | NFTW_FUNC_T func; |
28f540f4 | 124 | |
d951286f | 125 | /* Device of starting point. Needed for FTW_MOUNT. */ |
76b87c03 | 126 | dev_t dev; |
d951286f UD |
127 | |
128 | /* Data structure for keeping fingerprints of already processed | |
129 | object. This is needed when not using FTW_PHYS. */ | |
130 | void *known_objects; | |
76b87c03 | 131 | }; |
28f540f4 | 132 | |
92777700 | 133 | |
76b87c03 UD |
134 | /* Internally we use the FTW_* constants used for `nftw'. When the |
135 | process called `ftw' we must reduce the flag to the known flags | |
136 | for `ftw'. */ | |
390500b1 | 137 | static const int nftw_arr[] = |
76b87c03 UD |
138 | { |
139 | FTW_F, FTW_D, FTW_DNR, FTW_NS, FTW_SL, FTW_DP, FTW_SLN | |
140 | }; | |
28f540f4 | 141 | |
390500b1 | 142 | static const int ftw_arr[] = |
76b87c03 UD |
143 | { |
144 | FTW_F, FTW_D, FTW_DNR, FTW_NS, FTW_F, FTW_D, FTW_NS | |
145 | }; | |
28f540f4 | 146 | |
76b87c03 UD |
147 | |
148 | /* Forward declarations of local functions. */ | |
dfd2257a | 149 | static int ftw_dir (struct ftw_data *data, struct STAT *st) internal_function; |
d951286f UD |
150 | |
151 | ||
152 | static int | |
153 | object_compare (const void *p1, const void *p2) | |
154 | { | |
155 | /* We don't need a sophisticated and useful comparison. We are only | |
0413b54c UD |
156 | interested in equality. However, we must be careful not to |
157 | accidentally compare `holes' in the structure. */ | |
158 | const struct known_object *kp1 = p1, *kp2 = p2; | |
159 | int cmp1; | |
ca34d7a7 | 160 | cmp1 = (kp1->dev > kp2->dev) - (kp1->dev < kp2->dev); |
0413b54c UD |
161 | if (cmp1 != 0) |
162 | return cmp1; | |
ca34d7a7 | 163 | return (kp1->ino > kp2->ino) - (kp1->ino < kp2->ino); |
d951286f UD |
164 | } |
165 | ||
166 | ||
167 | static inline int | |
dfd2257a | 168 | add_object (struct ftw_data *data, struct STAT *st) |
d951286f UD |
169 | { |
170 | struct known_object *newp = malloc (sizeof (struct known_object)); | |
171 | if (newp == NULL) | |
172 | return -1; | |
173 | newp->dev = st->st_dev; | |
174 | newp->ino = st->st_ino; | |
175 | return __tsearch (newp, &data->known_objects, object_compare) ? 0 : -1; | |
176 | } | |
177 | ||
178 | ||
179 | static inline int | |
dfd2257a | 180 | find_object (struct ftw_data *data, struct STAT *st) |
d951286f | 181 | { |
f83c7164 | 182 | struct known_object obj = { .dev = st->st_dev, .ino = st->st_ino }; |
d951286f UD |
183 | return __tfind (&obj, &data->known_objects, object_compare) != NULL; |
184 | } | |
76b87c03 UD |
185 | |
186 | ||
187 | static inline int | |
188 | open_dir_stream (struct ftw_data *data, struct dir_data *dirp) | |
189 | { | |
190 | int result = 0; | |
191 | ||
192 | if (data->dirstreams[data->actdir] != NULL) | |
193 | { | |
194 | /* Oh, oh. We must close this stream. Get all remaining | |
195 | entries and store them as a list in the `content' member of | |
196 | the `struct dir_data' variable. */ | |
197 | size_t bufsize = 1024; | |
198 | char *buf = malloc (bufsize); | |
199 | ||
200 | if (buf == NULL) | |
201 | result = -1; | |
202 | else | |
28f540f4 | 203 | { |
76b87c03 | 204 | DIR *st = data->dirstreams[data->actdir]->stream; |
2958e6cc | 205 | struct dirent64 *d; |
76b87c03 UD |
206 | size_t actsize = 0; |
207 | ||
2958e6cc | 208 | while ((d = __readdir64 (st)) != NULL) |
76b87c03 UD |
209 | { |
210 | size_t this_len = _D_EXACT_NAMLEN (d); | |
211 | if (actsize + this_len + 2 >= bufsize) | |
212 | { | |
213 | char *newp; | |
214 | bufsize += MAX (1024, 2 * this_len); | |
a5ce5fcf | 215 | newp = (char *) realloc (buf, bufsize); |
76b87c03 UD |
216 | if (newp == NULL) |
217 | { | |
218 | /* No more memory. */ | |
219 | int save_err = errno; | |
220 | free (buf); | |
221 | __set_errno (save_err); | |
222 | result = -1; | |
223 | break; | |
224 | } | |
225 | buf = newp; | |
226 | } | |
227 | ||
86187531 UD |
228 | *((char *) __mempcpy (buf + actsize, d->d_name, this_len)) |
229 | = '\0'; | |
230 | actsize += this_len + 1; | |
76b87c03 | 231 | } |
28f540f4 | 232 | |
76b87c03 UD |
233 | /* Terminate the list with an additional NUL byte. */ |
234 | buf[actsize++] = '\0'; | |
28f540f4 | 235 | |
76b87c03 UD |
236 | /* Shrink the buffer to what we actually need. */ |
237 | data->dirstreams[data->actdir]->content = realloc (buf, actsize); | |
238 | if (data->dirstreams[data->actdir]->content == NULL) | |
239 | { | |
240 | int save_err = errno; | |
241 | free (buf); | |
242 | __set_errno (save_err); | |
243 | result = -1; | |
244 | } | |
28f540f4 RM |
245 | else |
246 | { | |
50304ef0 | 247 | __closedir (st); |
76b87c03 UD |
248 | data->dirstreams[data->actdir]->stream = NULL; |
249 | data->dirstreams[data->actdir] = NULL; | |
28f540f4 RM |
250 | } |
251 | } | |
76b87c03 UD |
252 | } |
253 | ||
254 | /* Open the new stream. */ | |
255 | if (result == 0) | |
256 | { | |
aff4519d UD |
257 | const char *name = ((data->flags & FTW_CHDIR) |
258 | ? data->dirbuf + data->ftw.base: data->dirbuf); | |
76b87c03 UD |
259 | assert (data->dirstreams[data->actdir] == NULL); |
260 | ||
aff4519d | 261 | dirp->stream = __opendir (name); |
76b87c03 UD |
262 | if (dirp->stream == NULL) |
263 | result = -1; | |
28f540f4 | 264 | else |
76b87c03 UD |
265 | { |
266 | dirp->content = NULL; | |
267 | data->dirstreams[data->actdir] = dirp; | |
268 | ||
269 | if (++data->actdir == data->maxdir) | |
270 | data->actdir = 0; | |
271 | } | |
272 | } | |
273 | ||
274 | return result; | |
275 | } | |
276 | ||
277 | ||
278 | static inline int | |
279 | process_entry (struct ftw_data *data, struct dir_data *dir, const char *name, | |
280 | size_t namlen) | |
281 | { | |
dfd2257a | 282 | struct STAT st; |
76b87c03 | 283 | int result = 0; |
256846bb | 284 | int flag = 0; |
76b87c03 UD |
285 | |
286 | if (name[0] == '.' && (name[1] == '\0' | |
287 | || (name[1] == '.' && name[2] == '\0'))) | |
288 | /* Don't process the "." and ".." entries. */ | |
289 | return 0; | |
290 | ||
291 | if (data->dirbufsize < data->ftw.base + namlen + 2) | |
292 | { | |
293 | /* Enlarge the buffer. */ | |
294 | char *newp; | |
295 | ||
296 | data->dirbufsize *= 2; | |
aff4519d | 297 | newp = (char *) realloc (data->dirbuf, data->dirbufsize); |
76b87c03 UD |
298 | if (newp == NULL) |
299 | return -1; | |
300 | data->dirbuf = newp; | |
301 | } | |
28f540f4 | 302 | |
86187531 | 303 | *((char *) __mempcpy (data->dirbuf + data->ftw.base, name, namlen)) = '\0'; |
28f540f4 | 304 | |
aff4519d UD |
305 | if ((data->flags & FTW_CHDIR) == 0) |
306 | name = data->dirbuf; | |
307 | ||
b13927da | 308 | if (((data->flags & FTW_PHYS) |
aff4519d UD |
309 | ? LXSTAT (_STAT_VER, name, &st) |
310 | : XSTAT (_STAT_VER, name, &st)) < 0) | |
76b87c03 UD |
311 | { |
312 | if (errno != EACCES && errno != ENOENT) | |
313 | result = -1; | |
314 | else if (!(data->flags & FTW_PHYS) | |
aff4519d | 315 | && LXSTAT (_STAT_VER, name, &st) == 0 |
d951286f | 316 | && S_ISLNK (st.st_mode)) |
76b87c03 UD |
317 | flag = FTW_SLN; |
318 | else | |
319 | flag = FTW_NS; | |
320 | } | |
321 | else | |
322 | { | |
d951286f | 323 | if (S_ISDIR (st.st_mode)) |
76b87c03 | 324 | flag = FTW_D; |
d951286f | 325 | else if (S_ISLNK (st.st_mode)) |
76b87c03 UD |
326 | flag = FTW_SL; |
327 | else | |
328 | flag = FTW_F; | |
329 | } | |
330 | ||
331 | if (result == 0 | |
b13927da UD |
332 | && (flag == FTW_NS |
333 | || !(data->flags & FTW_MOUNT) || st.st_dev == data->dev)) | |
76b87c03 | 334 | { |
eb7c2001 | 335 | if (flag == FTW_D) |
28f540f4 | 336 | { |
eb7c2001 UD |
337 | if ((data->flags & FTW_PHYS) |
338 | || (!find_object (data, &st) | |
339 | /* Remember the object. */ | |
340 | && (result = add_object (data, &st)) == 0)) | |
76b87c03 | 341 | { |
d951286f | 342 | result = ftw_dir (data, &st); |
76b87c03 | 343 | |
d951286f | 344 | if (result == 0 && (data->flags & FTW_CHDIR)) |
76b87c03 | 345 | { |
d951286f UD |
346 | /* Change back to current directory. */ |
347 | int done = 0; | |
348 | if (dir->stream != NULL) | |
b13927da | 349 | if (__fchdir (dirfd (dir->stream)) == 0) |
d951286f | 350 | done = 1; |
76b87c03 | 351 | |
d951286f UD |
352 | if (!done) |
353 | { | |
354 | if (data->ftw.base == 1) | |
355 | { | |
50304ef0 | 356 | if (__chdir ("/") < 0) |
d951286f UD |
357 | result = -1; |
358 | } | |
76b87c03 | 359 | else |
a5ce5fcf | 360 | if (__chdir ("..") < 0) |
b2608c22 | 361 | result = -1; |
76b87c03 UD |
362 | } |
363 | } | |
28f540f4 RM |
364 | } |
365 | } | |
eb7c2001 UD |
366 | else |
367 | result = (*data->func) (data->dirbuf, &st, data->cvt_arr[flag], | |
368 | &data->ftw); | |
76b87c03 UD |
369 | } |
370 | ||
371 | return result; | |
372 | } | |
373 | ||
374 | ||
375 | static int | |
dfd2257a UD |
376 | internal_function |
377 | ftw_dir (struct ftw_data *data, struct STAT *st) | |
76b87c03 UD |
378 | { |
379 | struct dir_data dir; | |
2958e6cc | 380 | struct dirent64 *d; |
76b87c03 | 381 | int previous_base = data->ftw.base; |
d951286f | 382 | int result; |
76b87c03 UD |
383 | char *startp; |
384 | ||
d951286f UD |
385 | /* Open the stream for this directory. This might require that |
386 | another stream has to be closed. */ | |
387 | result = open_dir_stream (data, &dir); | |
388 | if (result != 0) | |
389 | { | |
390 | if (errno == EACCES) | |
391 | /* We cannot read the directory. Signal this with a special flag. */ | |
392 | result = (*data->func) (data->dirbuf, st, FTW_DNR, &data->ftw); | |
393 | ||
394 | return result; | |
395 | } | |
396 | ||
76b87c03 UD |
397 | /* First, report the directory (if not depth-first). */ |
398 | if (!(data->flags & FTW_DEPTH)) | |
399 | { | |
d951286f | 400 | result = (*data->func) (data->dirbuf, st, FTW_D, &data->ftw); |
76b87c03 UD |
401 | if (result != 0) |
402 | return result; | |
403 | } | |
28f540f4 | 404 | |
76b87c03 UD |
405 | /* If necessary, change to this directory. */ |
406 | if (data->flags & FTW_CHDIR) | |
407 | { | |
b13927da | 408 | if (__fchdir (dirfd (dir.stream)) < 0) |
28f540f4 | 409 | { |
76b87c03 | 410 | if (errno == ENOSYS) |
28f540f4 | 411 | { |
50304ef0 | 412 | if (__chdir (data->dirbuf) < 0) |
76b87c03 | 413 | result = -1; |
28f540f4 | 414 | } |
76b87c03 UD |
415 | else |
416 | result = -1; | |
28f540f4 RM |
417 | } |
418 | ||
76b87c03 UD |
419 | if (result != 0) |
420 | { | |
421 | int save_err = errno; | |
50304ef0 | 422 | __closedir (dir.stream); |
76b87c03 UD |
423 | __set_errno (save_err); |
424 | ||
425 | if (data->actdir-- == 0) | |
426 | data->actdir = data->maxdir - 1; | |
427 | data->dirstreams[data->actdir] = NULL; | |
428 | ||
429 | return result; | |
430 | } | |
28f540f4 RM |
431 | } |
432 | ||
76b87c03 UD |
433 | /* Next, update the `struct FTW' information. */ |
434 | ++data->ftw.level; | |
435 | startp = strchr (data->dirbuf, '\0'); | |
25f227b9 UD |
436 | /* There always must be a directory name. */ |
437 | assert (startp != data->dirbuf); | |
21a568e2 | 438 | if (startp[-1] != '/') |
25f227b9 | 439 | *startp++ = '/'; |
76b87c03 | 440 | data->ftw.base = startp - data->dirbuf; |
28f540f4 | 441 | |
2958e6cc | 442 | while (dir.stream != NULL && (d = __readdir64 (dir.stream)) != NULL) |
76b87c03 UD |
443 | { |
444 | result = process_entry (data, &dir, d->d_name, _D_EXACT_NAMLEN (d)); | |
445 | if (result != 0) | |
446 | break; | |
447 | } | |
28f540f4 | 448 | |
76b87c03 | 449 | if (dir.stream != NULL) |
28f540f4 | 450 | { |
76b87c03 UD |
451 | /* The stream is still open. I.e., we did not need more |
452 | descriptors. Simply close the stream now. */ | |
453 | int save_err = errno; | |
454 | ||
455 | assert (dir.content == NULL); | |
456 | ||
50304ef0 | 457 | __closedir (dir.stream); |
76b87c03 UD |
458 | __set_errno (save_err); |
459 | ||
460 | if (data->actdir-- == 0) | |
461 | data->actdir = data->maxdir - 1; | |
462 | data->dirstreams[data->actdir] = NULL; | |
28f540f4 | 463 | } |
76b87c03 | 464 | else |
28f540f4 | 465 | { |
76b87c03 UD |
466 | int save_err; |
467 | char *runp = dir.content; | |
468 | ||
3d73829c | 469 | while (result == 0 && *runp != '\0') |
28f540f4 | 470 | { |
76b87c03 UD |
471 | char *endp = strchr (runp, '\0'); |
472 | ||
473 | result = process_entry (data, &dir, runp, endp - runp); | |
76b87c03 UD |
474 | |
475 | runp = endp + 1; | |
28f540f4 | 476 | } |
76b87c03 UD |
477 | |
478 | save_err = errno; | |
479 | free (dir.content); | |
480 | __set_errno (save_err); | |
28f540f4 | 481 | } |
28f540f4 | 482 | |
76b87c03 | 483 | /* Prepare the return, revert the `struct FTW' information. */ |
d951286f | 484 | data->dirbuf[data->ftw.base - 1] = '\0'; |
76b87c03 UD |
485 | --data->ftw.level; |
486 | data->ftw.base = previous_base; | |
487 | ||
488 | /* Finally, if we process depth-first report the directory. */ | |
489 | if (result == 0 && (data->flags & FTW_DEPTH)) | |
d951286f | 490 | result = (*data->func) (data->dirbuf, st, FTW_DP, &data->ftw); |
28f540f4 | 491 | |
76b87c03 UD |
492 | return result; |
493 | } | |
494 | ||
495 | ||
496 | static int | |
dfd2257a | 497 | internal_function |
76b87c03 UD |
498 | ftw_startup (const char *dir, int is_nftw, void *func, int descriptors, |
499 | int flags) | |
500 | { | |
501 | struct ftw_data data; | |
dfd2257a | 502 | struct STAT st; |
76b87c03 UD |
503 | int result = 0; |
504 | int save_err; | |
d951286f | 505 | char *cwd = NULL; |
76b87c03 UD |
506 | char *cp; |
507 | ||
508 | /* First make sure the parameters are reasonable. */ | |
509 | if (dir[0] == '\0') | |
510 | { | |
a7f775a5 | 511 | __set_errno (ENOENT); |
76b87c03 UD |
512 | return -1; |
513 | } | |
28f540f4 | 514 | |
76b87c03 UD |
515 | data.maxdir = descriptors < 1 ? 1 : descriptors; |
516 | data.actdir = 0; | |
517 | data.dirstreams = (struct dir_data **) alloca (data.maxdir | |
518 | * sizeof (struct dir_data *)); | |
519 | memset (data.dirstreams, '\0', data.maxdir * sizeof (struct dir_data *)); | |
520 | ||
ae1025be | 521 | #ifdef PATH_MAX |
76b87c03 | 522 | data.dirbufsize = MAX (2 * strlen (dir), PATH_MAX); |
ae1025be UD |
523 | #else |
524 | data.dirbufsize = 2 * strlen (dir); | |
525 | #endif | |
76b87c03 UD |
526 | data.dirbuf = (char *) malloc (data.dirbufsize); |
527 | if (data.dirbuf == NULL) | |
528 | return -1; | |
d951286f | 529 | cp = __stpcpy (data.dirbuf, dir); |
76b87c03 UD |
530 | /* Strip trailing slashes. */ |
531 | while (cp > data.dirbuf + 1 && cp[-1] == '/') | |
532 | --cp; | |
533 | *cp = '\0'; | |
534 | ||
535 | data.ftw.level = 0; | |
536 | ||
537 | /* Find basename. */ | |
538 | while (cp > data.dirbuf && cp[-1] != '/') | |
539 | --cp; | |
540 | data.ftw.base = cp - data.dirbuf; | |
541 | ||
542 | data.flags = flags; | |
543 | ||
544 | /* This assignment might seem to be strange but it is what we want. | |
545 | The trick is that the first three arguments to the `ftw' and | |
546 | `nftw' callback functions are equal. Therefore we can call in | |
547 | every case the callback using the format of the `nftw' version | |
548 | and get the correct result since the stack layout for a function | |
549 | call in C allows this. */ | |
dfd2257a | 550 | data.func = (NFTW_FUNC_T) func; |
76b87c03 UD |
551 | |
552 | /* Since we internally use the complete set of FTW_* values we need | |
553 | to reduce the value range before calling a `ftw' callback. */ | |
554 | data.cvt_arr = is_nftw ? nftw_arr : ftw_arr; | |
555 | ||
d951286f UD |
556 | /* No object known so far. */ |
557 | data.known_objects = NULL; | |
558 | ||
76b87c03 UD |
559 | /* Now go to the directory containing the initial file/directory. */ |
560 | if ((flags & FTW_CHDIR) && data.ftw.base > 0) | |
28f540f4 | 561 | { |
76b87c03 | 562 | /* GNU extension ahead. */ |
50304ef0 | 563 | cwd = __getcwd (NULL, 0); |
76b87c03 UD |
564 | if (cwd == NULL) |
565 | result = -1; | |
566 | else | |
28f540f4 | 567 | { |
76b87c03 UD |
568 | /* Change to the directory the file is in. In data.dirbuf |
569 | we have a writable copy of the file name. Just NUL | |
570 | terminate it for now and change the directory. */ | |
571 | if (data.ftw.base == 1) | |
572 | /* I.e., the file is in the root directory. */ | |
50304ef0 | 573 | result = __chdir ("/"); |
76b87c03 UD |
574 | else |
575 | { | |
576 | char ch = data.dirbuf[data.ftw.base - 1]; | |
577 | data.dirbuf[data.ftw.base - 1] = '\0'; | |
50304ef0 | 578 | result = __chdir (data.dirbuf); |
76b87c03 UD |
579 | data.dirbuf[data.ftw.base - 1] = ch; |
580 | } | |
28f540f4 RM |
581 | } |
582 | } | |
583 | ||
76b87c03 UD |
584 | /* Get stat info for start directory. */ |
585 | if (result == 0) | |
6e4c40ba | 586 | { |
b2608c22 UD |
587 | const char *name = ((data.flags & FTW_CHDIR) |
588 | ? data.dirbuf + data.ftw.base | |
589 | : data.dirbuf); | |
590 | ||
6e4c40ba | 591 | if (((flags & FTW_PHYS) |
b2608c22 UD |
592 | ? LXSTAT (_STAT_VER, name, &st) |
593 | : XSTAT (_STAT_VER, name, &st)) < 0) | |
6e4c40ba | 594 | { |
0e24e73d UD |
595 | if (!(flags & FTW_PHYS) |
596 | && errno == ENOENT | |
597 | && LXSTAT (_STAT_VER, dir, &st) == 0 | |
598 | && S_ISLNK (st.st_mode)) | |
6e4c40ba | 599 | result = (*data.func) (data.dirbuf, &st, data.cvt_arr[FTW_SLN], |
76b87c03 | 600 | &data.ftw); |
6e4c40ba UD |
601 | else |
602 | /* No need to call the callback since we cannot say anything | |
603 | about the object. */ | |
604 | result = -1; | |
605 | } | |
606 | else | |
607 | { | |
608 | if (S_ISDIR (st.st_mode)) | |
609 | { | |
610 | /* Remember the device of the initial directory in case | |
611 | FTW_MOUNT is given. */ | |
612 | data.dev = st.st_dev; | |
613 | ||
614 | /* We know this directory now. */ | |
615 | if (!(flags & FTW_PHYS)) | |
616 | result = add_object (&data, &st); | |
617 | ||
618 | if (result == 0) | |
619 | result = ftw_dir (&data, &st); | |
620 | } | |
621 | else | |
622 | { | |
623 | int flag = S_ISLNK (st.st_mode) ? FTW_SL : FTW_F; | |
624 | ||
625 | result = (*data.func) (data.dirbuf, &st, data.cvt_arr[flag], | |
626 | &data.ftw); | |
627 | } | |
628 | } | |
629 | } | |
76b87c03 UD |
630 | |
631 | /* Return to the start directory (if necessary). */ | |
632 | if (cwd != NULL) | |
633 | { | |
634 | int save_err = errno; | |
50304ef0 | 635 | __chdir (cwd); |
76b87c03 UD |
636 | free (cwd); |
637 | __set_errno (save_err); | |
638 | } | |
639 | ||
640 | /* Free all memory. */ | |
641 | save_err = errno; | |
d951286f | 642 | __tdestroy (data.known_objects, free); |
76b87c03 UD |
643 | free (data.dirbuf); |
644 | __set_errno (save_err); | |
645 | ||
646 | return result; | |
647 | } | |
648 | ||
649 | ||
650 | ||
651 | /* Entry points. */ | |
652 | ||
653 | int | |
dfd2257a | 654 | FTW_NAME (path, func, descriptors) |
76b87c03 | 655 | const char *path; |
dfd2257a | 656 | FTW_FUNC_T func; |
76b87c03 UD |
657 | int descriptors; |
658 | { | |
659 | return ftw_startup (path, 0, func, descriptors, 0); | |
660 | } | |
661 | ||
662 | int | |
dfd2257a | 663 | NFTW_NAME (path, func, descriptors, flags) |
76b87c03 | 664 | const char *path; |
dfd2257a | 665 | NFTW_FUNC_T func; |
76b87c03 UD |
666 | int descriptors; |
667 | int flags; | |
668 | { | |
669 | return ftw_startup (path, 1, func, descriptors, flags); | |
28f540f4 | 670 | } |