]>
Commit | Line | Data |
---|---|---|
76b87c03 | 1 | /* File tree walker functions. |
0ecb606c | 2 | Copyright (C) 1996-2003, 2004, 2006, 2007 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 | ||
ae9ecd08 UD |
25 | #if __GNUC__ |
26 | # define alloca __builtin_alloca | |
27 | #else | |
28 | # if HAVE_ALLOCA_H | |
29 | # include <alloca.h> | |
30 | # else | |
31 | # ifdef _AIX | |
32 | # pragma alloca | |
33 | # else | |
34 | char *alloca (); | |
35 | # endif | |
36 | # endif | |
37 | #endif | |
38 | ||
0ecb606c | 39 | #ifdef _LIBC |
ae9ecd08 UD |
40 | # include <dirent.h> |
41 | # define NAMLEN(dirent) _D_EXACT_NAMLEN (dirent) | |
42 | #else | |
43 | # if HAVE_DIRENT_H | |
44 | # include <dirent.h> | |
45 | # define NAMLEN(dirent) strlen ((dirent)->d_name) | |
46 | # else | |
47 | # define dirent direct | |
48 | # define NAMLEN(dirent) (dirent)->d_namlen | |
49 | # if HAVE_SYS_NDIR_H | |
50 | # include <sys/ndir.h> | |
51 | # endif | |
52 | # if HAVE_SYS_DIR_H | |
53 | # include <sys/dir.h> | |
54 | # endif | |
55 | # if HAVE_NDIR_H | |
56 | # include <ndir.h> | |
57 | # endif | |
58 | # endif | |
59 | #endif | |
60 | ||
28f540f4 | 61 | #include <errno.h> |
0ecb606c | 62 | #include <fcntl.h> |
76b87c03 | 63 | #include <ftw.h> |
5049f197 | 64 | #include <limits.h> |
76b87c03 | 65 | #include <search.h> |
28f540f4 RM |
66 | #include <stdlib.h> |
67 | #include <string.h> | |
76b87c03 | 68 | #include <unistd.h> |
0ecb606c | 69 | #include <not-cancel.h> |
aff4519d UD |
70 | #if HAVE_SYS_PARAM_H || defined _LIBC |
71 | # include <sys/param.h> | |
72 | #endif | |
73 | #ifdef _LIBC | |
74 | # include <include/sys/stat.h> | |
75 | #else | |
76 | # include <sys/stat.h> | |
77 | #endif | |
28f540f4 | 78 | |
ae9ecd08 UD |
79 | #if ! _LIBC && !HAVE_DECL_STPCPY && !defined stpcpy |
80 | char *stpcpy (); | |
81 | #endif | |
82 | ||
83 | #if ! _LIBC && ! defined HAVE_MEMPCPY && ! defined mempcpy | |
84 | /* Be CAREFUL that there are no side effects in N. */ | |
85 | # define mempcpy(D, S, N) ((void *) ((char *) memcpy (D, S, N) + (N))) | |
86 | #endif | |
87 | ||
76b87c03 UD |
88 | /* #define NDEBUG 1 */ |
89 | #include <assert.h> | |
28f540f4 | 90 | |
aff4519d UD |
91 | #ifndef _LIBC |
92 | # undef __chdir | |
93 | # define __chdir chdir | |
94 | # undef __closedir | |
95 | # define __closedir closedir | |
96 | # undef __fchdir | |
97 | # define __fchdir fchdir | |
98 | # undef __getcwd | |
ae9ecd08 UD |
99 | # define __getcwd(P, N) xgetcwd () |
100 | extern char *xgetcwd (void); | |
101 | # undef __mempcpy | |
102 | # define __mempcpy mempcpy | |
aff4519d UD |
103 | # undef __opendir |
104 | # define __opendir opendir | |
105 | # undef __readdir64 | |
106 | # define __readdir64 readdir | |
ae9ecd08 UD |
107 | # undef __stpcpy |
108 | # define __stpcpy stpcpy | |
aff4519d UD |
109 | # undef __tdestroy |
110 | # define __tdestroy tdestroy | |
111 | # undef __tfind | |
112 | # define __tfind tfind | |
113 | # undef __tsearch | |
114 | # define __tsearch tsearch | |
115 | # undef internal_function | |
116 | # define internal_function /* empty */ | |
117 | # undef dirent64 | |
118 | # define dirent64 dirent | |
119 | # undef MAX | |
120 | # define MAX(a, b) ((a) > (b) ? (a) : (b)) | |
121 | #endif | |
122 | ||
ae9ecd08 UD |
123 | /* Arrange to make lstat calls go through the wrapper function |
124 | on systems with an lstat function that does not dereference symlinks | |
125 | that are specified with a trailing slash. */ | |
126 | #if ! _LIBC && ! LSTAT_FOLLOWS_SLASHED_SYMLINK | |
127 | int rpl_lstat (const char *, struct stat *); | |
128 | # undef lstat | |
129 | # define lstat(Name, Stat_buf) rpl_lstat(Name, Stat_buf) | |
130 | #endif | |
131 | ||
aff4519d UD |
132 | #ifndef __set_errno |
133 | # define __set_errno(Val) errno = (Val) | |
134 | #endif | |
135 | ||
dfd2257a UD |
136 | /* Support for the LFS API version. */ |
137 | #ifndef FTW_NAME | |
138 | # define FTW_NAME ftw | |
139 | # define NFTW_NAME nftw | |
ca10f338 UD |
140 | # define NFTW_OLD_NAME __old_nftw |
141 | # define NFTW_NEW_NAME __new_nftw | |
dfd2257a UD |
142 | # define INO_T ino_t |
143 | # define STAT stat | |
ae9ecd08 UD |
144 | # ifdef _LIBC |
145 | # define LXSTAT __lxstat | |
146 | # define XSTAT __xstat | |
0ecb606c | 147 | # define FXSTATAT __fxstatat |
ae9ecd08 UD |
148 | # else |
149 | # define LXSTAT(V,f,sb) lstat (f,sb) | |
150 | # define XSTAT(V,f,sb) stat (f,sb) | |
0ecb606c | 151 | # define FXSTATAT(V,d,f,sb,m) fstatat (d, f, sb, m) |
ae9ecd08 | 152 | # endif |
dfd2257a UD |
153 | # define FTW_FUNC_T __ftw_func_t |
154 | # define NFTW_FUNC_T __nftw_func_t | |
155 | #endif | |
28f540f4 | 156 | |
5049f197 UD |
157 | /* We define PATH_MAX if the system does not provide a definition. |
158 | This does not artificially limit any operation. PATH_MAX is simply | |
159 | used as a guesstimate for the expected maximal path length. | |
160 | Buffers will be enlarged if necessary. */ | |
161 | #ifndef PATH_MAX | |
162 | # define PATH_MAX 1024 | |
163 | #endif | |
164 | ||
76b87c03 UD |
165 | struct dir_data |
166 | { | |
167 | DIR *stream; | |
0ecb606c | 168 | int streamfd; |
76b87c03 UD |
169 | char *content; |
170 | }; | |
28f540f4 | 171 | |
d951286f UD |
172 | struct known_object |
173 | { | |
174 | dev_t dev; | |
dfd2257a | 175 | INO_T ino; |
d951286f UD |
176 | }; |
177 | ||
76b87c03 | 178 | struct ftw_data |
28f540f4 | 179 | { |
d951286f | 180 | /* Array with pointers to open directory streams. */ |
76b87c03 UD |
181 | struct dir_data **dirstreams; |
182 | size_t actdir; | |
183 | size_t maxdir; | |
28f540f4 | 184 | |
d951286f | 185 | /* Buffer containing name of currently processed object. */ |
76b87c03 UD |
186 | char *dirbuf; |
187 | size_t dirbufsize; | |
d951286f UD |
188 | |
189 | /* Passed as fourth argument to `nftw' callback. The `base' member | |
190 | tracks the content of the `dirbuf'. */ | |
76b87c03 | 191 | struct FTW ftw; |
28f540f4 | 192 | |
d951286f | 193 | /* Flags passed to `nftw' function. 0 for `ftw'. */ |
76b87c03 | 194 | int flags; |
28f540f4 | 195 | |
d951286f | 196 | /* Conversion array for flag values. It is the identity mapping for |
ae9ecd08 | 197 | `nftw' calls, otherwise it maps the values to those known by |
d951286f | 198 | `ftw'. */ |
390500b1 | 199 | const int *cvt_arr; |
28f540f4 | 200 | |
d951286f | 201 | /* Callback function. We always use the `nftw' form. */ |
dfd2257a | 202 | NFTW_FUNC_T func; |
28f540f4 | 203 | |
d951286f | 204 | /* Device of starting point. Needed for FTW_MOUNT. */ |
76b87c03 | 205 | dev_t dev; |
d951286f UD |
206 | |
207 | /* Data structure for keeping fingerprints of already processed | |
208 | object. This is needed when not using FTW_PHYS. */ | |
209 | void *known_objects; | |
76b87c03 | 210 | }; |
28f540f4 | 211 | |
92777700 | 212 | |
ae9ecd08 UD |
213 | /* Internally we use the FTW_* constants used for `nftw'. When invoked |
214 | as `ftw', map each flag to the subset of values used by `ftw'. */ | |
390500b1 | 215 | static const int nftw_arr[] = |
76b87c03 UD |
216 | { |
217 | FTW_F, FTW_D, FTW_DNR, FTW_NS, FTW_SL, FTW_DP, FTW_SLN | |
218 | }; | |
28f540f4 | 219 | |
390500b1 | 220 | static const int ftw_arr[] = |
76b87c03 UD |
221 | { |
222 | FTW_F, FTW_D, FTW_DNR, FTW_NS, FTW_F, FTW_D, FTW_NS | |
223 | }; | |
28f540f4 | 224 | |
76b87c03 UD |
225 | |
226 | /* Forward declarations of local functions. */ | |
ca10f338 UD |
227 | static int ftw_dir (struct ftw_data *data, struct STAT *st, |
228 | struct dir_data *old_dir) internal_function; | |
d951286f UD |
229 | |
230 | ||
231 | static int | |
232 | object_compare (const void *p1, const void *p2) | |
233 | { | |
234 | /* We don't need a sophisticated and useful comparison. We are only | |
0413b54c UD |
235 | interested in equality. However, we must be careful not to |
236 | accidentally compare `holes' in the structure. */ | |
237 | const struct known_object *kp1 = p1, *kp2 = p2; | |
238 | int cmp1; | |
78e88510 | 239 | cmp1 = (kp1->ino > kp2->ino) - (kp1->ino < kp2->ino); |
0413b54c UD |
240 | if (cmp1 != 0) |
241 | return cmp1; | |
78e88510 | 242 | return (kp1->dev > kp2->dev) - (kp1->dev < kp2->dev); |
d951286f UD |
243 | } |
244 | ||
245 | ||
246 | static inline int | |
dfd2257a | 247 | add_object (struct ftw_data *data, struct STAT *st) |
d951286f UD |
248 | { |
249 | struct known_object *newp = malloc (sizeof (struct known_object)); | |
250 | if (newp == NULL) | |
251 | return -1; | |
252 | newp->dev = st->st_dev; | |
253 | newp->ino = st->st_ino; | |
254 | return __tsearch (newp, &data->known_objects, object_compare) ? 0 : -1; | |
255 | } | |
256 | ||
257 | ||
258 | static inline int | |
dfd2257a | 259 | find_object (struct ftw_data *data, struct STAT *st) |
d951286f | 260 | { |
ae9ecd08 UD |
261 | struct known_object obj; |
262 | obj.dev = st->st_dev; | |
263 | obj.ino = st->st_ino; | |
d951286f UD |
264 | return __tfind (&obj, &data->known_objects, object_compare) != NULL; |
265 | } | |
76b87c03 UD |
266 | |
267 | ||
268 | static inline int | |
dd9423a6 | 269 | __attribute ((always_inline)) |
0ecb606c | 270 | open_dir_stream (int *dfdp, struct ftw_data *data, struct dir_data *dirp) |
76b87c03 UD |
271 | { |
272 | int result = 0; | |
273 | ||
274 | if (data->dirstreams[data->actdir] != NULL) | |
275 | { | |
276 | /* Oh, oh. We must close this stream. Get all remaining | |
277 | entries and store them as a list in the `content' member of | |
278 | the `struct dir_data' variable. */ | |
279 | size_t bufsize = 1024; | |
280 | char *buf = malloc (bufsize); | |
281 | ||
282 | if (buf == NULL) | |
283 | result = -1; | |
284 | else | |
28f540f4 | 285 | { |
76b87c03 | 286 | DIR *st = data->dirstreams[data->actdir]->stream; |
2958e6cc | 287 | struct dirent64 *d; |
76b87c03 UD |
288 | size_t actsize = 0; |
289 | ||
2958e6cc | 290 | while ((d = __readdir64 (st)) != NULL) |
76b87c03 | 291 | { |
ae9ecd08 | 292 | size_t this_len = NAMLEN (d); |
76b87c03 UD |
293 | if (actsize + this_len + 2 >= bufsize) |
294 | { | |
295 | char *newp; | |
296 | bufsize += MAX (1024, 2 * this_len); | |
a5ce5fcf | 297 | newp = (char *) realloc (buf, bufsize); |
76b87c03 UD |
298 | if (newp == NULL) |
299 | { | |
300 | /* No more memory. */ | |
301 | int save_err = errno; | |
302 | free (buf); | |
303 | __set_errno (save_err); | |
0ecb606c | 304 | return -1; |
76b87c03 UD |
305 | } |
306 | buf = newp; | |
307 | } | |
308 | ||
86187531 UD |
309 | *((char *) __mempcpy (buf + actsize, d->d_name, this_len)) |
310 | = '\0'; | |
311 | actsize += this_len + 1; | |
76b87c03 | 312 | } |
28f540f4 | 313 | |
76b87c03 UD |
314 | /* Terminate the list with an additional NUL byte. */ |
315 | buf[actsize++] = '\0'; | |
28f540f4 | 316 | |
76b87c03 UD |
317 | /* Shrink the buffer to what we actually need. */ |
318 | data->dirstreams[data->actdir]->content = realloc (buf, actsize); | |
319 | if (data->dirstreams[data->actdir]->content == NULL) | |
320 | { | |
321 | int save_err = errno; | |
322 | free (buf); | |
323 | __set_errno (save_err); | |
324 | result = -1; | |
325 | } | |
28f540f4 RM |
326 | else |
327 | { | |
50304ef0 | 328 | __closedir (st); |
76b87c03 | 329 | data->dirstreams[data->actdir]->stream = NULL; |
0ecb606c | 330 | data->dirstreams[data->actdir]->streamfd = -1; |
76b87c03 | 331 | data->dirstreams[data->actdir] = NULL; |
28f540f4 RM |
332 | } |
333 | } | |
76b87c03 UD |
334 | } |
335 | ||
336 | /* Open the new stream. */ | |
337 | if (result == 0) | |
338 | { | |
339 | assert (data->dirstreams[data->actdir] == NULL); | |
340 | ||
0ecb606c JJ |
341 | if (dfdp != NULL && *dfdp != -1) |
342 | { | |
343 | int fd = openat64_not_cancel_3 (*dfdp, data->dirbuf + data->ftw.base, | |
344 | O_RDONLY | O_DIRECTORY | O_NDELAY); | |
345 | dirp->stream = NULL; | |
346 | if (fd != -1 && (dirp->stream = __fdopendir (fd)) == NULL) | |
347 | close_not_cancel_no_status (fd); | |
348 | } | |
349 | else | |
350 | { | |
351 | const char *name; | |
352 | ||
353 | if (data->flags & FTW_CHDIR) | |
354 | { | |
355 | name = data->dirbuf + data->ftw.base; | |
356 | if (name[0] == '\0') | |
357 | name = "."; | |
358 | } | |
359 | else | |
360 | name = data->dirbuf; | |
361 | ||
362 | dirp->stream = __opendir (name); | |
363 | } | |
364 | ||
76b87c03 UD |
365 | if (dirp->stream == NULL) |
366 | result = -1; | |
28f540f4 | 367 | else |
76b87c03 | 368 | { |
0ecb606c | 369 | dirp->streamfd = dirfd (dirp->stream); |
76b87c03 UD |
370 | dirp->content = NULL; |
371 | data->dirstreams[data->actdir] = dirp; | |
372 | ||
373 | if (++data->actdir == data->maxdir) | |
374 | data->actdir = 0; | |
375 | } | |
376 | } | |
377 | ||
378 | return result; | |
379 | } | |
380 | ||
381 | ||
dd9423a6 UD |
382 | static int |
383 | internal_function | |
76b87c03 | 384 | process_entry (struct ftw_data *data, struct dir_data *dir, const char *name, |
0ecb606c | 385 | size_t namlen, int d_type) |
76b87c03 | 386 | { |
dfd2257a | 387 | struct STAT st; |
76b87c03 | 388 | int result = 0; |
256846bb | 389 | int flag = 0; |
5049f197 | 390 | size_t new_buflen; |
76b87c03 UD |
391 | |
392 | if (name[0] == '.' && (name[1] == '\0' | |
393 | || (name[1] == '.' && name[2] == '\0'))) | |
394 | /* Don't process the "." and ".." entries. */ | |
395 | return 0; | |
396 | ||
5049f197 UD |
397 | new_buflen = data->ftw.base + namlen + 2; |
398 | if (data->dirbufsize < new_buflen) | |
76b87c03 UD |
399 | { |
400 | /* Enlarge the buffer. */ | |
401 | char *newp; | |
402 | ||
5049f197 | 403 | data->dirbufsize = 2 * new_buflen; |
aff4519d | 404 | newp = (char *) realloc (data->dirbuf, data->dirbufsize); |
76b87c03 UD |
405 | if (newp == NULL) |
406 | return -1; | |
407 | data->dirbuf = newp; | |
408 | } | |
28f540f4 | 409 | |
86187531 | 410 | *((char *) __mempcpy (data->dirbuf + data->ftw.base, name, namlen)) = '\0'; |
28f540f4 | 411 | |
0ecb606c JJ |
412 | int statres; |
413 | if (dir->streamfd != -1) | |
414 | statres = FXSTATAT (_STAT_VER, dir->streamfd, name, &st, | |
415 | (data->flags & FTW_PHYS) ? AT_SYMLINK_NOFOLLOW : 0); | |
416 | else | |
417 | { | |
418 | if ((data->flags & FTW_CHDIR) == 0) | |
419 | name = data->dirbuf; | |
aff4519d | 420 | |
0ecb606c JJ |
421 | statres = ((data->flags & FTW_PHYS) |
422 | ? LXSTAT (_STAT_VER, name, &st) | |
423 | : XSTAT (_STAT_VER, name, &st)); | |
424 | } | |
425 | ||
426 | if (statres < 0) | |
76b87c03 UD |
427 | { |
428 | if (errno != EACCES && errno != ENOENT) | |
429 | result = -1; | |
0ecb606c JJ |
430 | else if (data->flags & FTW_PHYS) |
431 | flag = FTW_NS; | |
432 | else if (d_type == DT_LNK) | |
76b87c03 UD |
433 | flag = FTW_SLN; |
434 | else | |
0ecb606c JJ |
435 | { |
436 | if (dir->streamfd != -1) | |
437 | statres = FXSTATAT (_STAT_VER, dir->streamfd, name, &st, | |
438 | AT_SYMLINK_NOFOLLOW); | |
439 | else | |
440 | statres = LXSTAT (_STAT_VER, name, &st); | |
441 | if (statres == 0 && S_ISLNK (st.st_mode)) | |
442 | flag = FTW_SLN; | |
443 | else | |
444 | flag = FTW_NS; | |
445 | } | |
76b87c03 UD |
446 | } |
447 | else | |
448 | { | |
d951286f | 449 | if (S_ISDIR (st.st_mode)) |
76b87c03 | 450 | flag = FTW_D; |
d951286f | 451 | else if (S_ISLNK (st.st_mode)) |
76b87c03 UD |
452 | flag = FTW_SL; |
453 | else | |
454 | flag = FTW_F; | |
455 | } | |
456 | ||
457 | if (result == 0 | |
b13927da UD |
458 | && (flag == FTW_NS |
459 | || !(data->flags & FTW_MOUNT) || st.st_dev == data->dev)) | |
76b87c03 | 460 | { |
eb7c2001 | 461 | if (flag == FTW_D) |
28f540f4 | 462 | { |
eb7c2001 UD |
463 | if ((data->flags & FTW_PHYS) |
464 | || (!find_object (data, &st) | |
465 | /* Remember the object. */ | |
466 | && (result = add_object (data, &st)) == 0)) | |
ca10f338 | 467 | result = ftw_dir (data, &st, dir); |
28f540f4 | 468 | } |
eb7c2001 UD |
469 | else |
470 | result = (*data->func) (data->dirbuf, &st, data->cvt_arr[flag], | |
471 | &data->ftw); | |
76b87c03 UD |
472 | } |
473 | ||
ca10f338 UD |
474 | if ((data->flags & FTW_ACTIONRETVAL) && result == FTW_SKIP_SUBTREE) |
475 | result = 0; | |
476 | ||
76b87c03 UD |
477 | return result; |
478 | } | |
479 | ||
480 | ||
481 | static int | |
ca10f338 | 482 | __attribute ((noinline)) |
dfd2257a | 483 | internal_function |
ca10f338 | 484 | ftw_dir (struct ftw_data *data, struct STAT *st, struct dir_data *old_dir) |
76b87c03 UD |
485 | { |
486 | struct dir_data dir; | |
2958e6cc | 487 | struct dirent64 *d; |
76b87c03 | 488 | int previous_base = data->ftw.base; |
d951286f | 489 | int result; |
76b87c03 UD |
490 | char *startp; |
491 | ||
d951286f UD |
492 | /* Open the stream for this directory. This might require that |
493 | another stream has to be closed. */ | |
0ecb606c JJ |
494 | result = open_dir_stream (old_dir == NULL ? NULL : &old_dir->streamfd, |
495 | data, &dir); | |
d951286f UD |
496 | if (result != 0) |
497 | { | |
498 | if (errno == EACCES) | |
499 | /* We cannot read the directory. Signal this with a special flag. */ | |
500 | result = (*data->func) (data->dirbuf, st, FTW_DNR, &data->ftw); | |
501 | ||
502 | return result; | |
503 | } | |
504 | ||
76b87c03 UD |
505 | /* First, report the directory (if not depth-first). */ |
506 | if (!(data->flags & FTW_DEPTH)) | |
507 | { | |
d951286f | 508 | result = (*data->func) (data->dirbuf, st, FTW_D, &data->ftw); |
76b87c03 | 509 | if (result != 0) |
76b87c03 | 510 | { |
93787845 UD |
511 | int save_err; |
512 | fail: | |
513 | save_err = errno; | |
50304ef0 | 514 | __closedir (dir.stream); |
0ecb606c | 515 | dir.streamfd = -1; |
76b87c03 UD |
516 | __set_errno (save_err); |
517 | ||
518 | if (data->actdir-- == 0) | |
519 | data->actdir = data->maxdir - 1; | |
520 | data->dirstreams[data->actdir] = NULL; | |
93787845 UD |
521 | return result; |
522 | } | |
523 | } | |
76b87c03 | 524 | |
93787845 UD |
525 | /* If necessary, change to this directory. */ |
526 | if (data->flags & FTW_CHDIR) | |
527 | { | |
528 | if (__fchdir (dirfd (dir.stream)) < 0) | |
529 | { | |
530 | result = -1; | |
531 | goto fail; | |
76b87c03 | 532 | } |
28f540f4 RM |
533 | } |
534 | ||
76b87c03 UD |
535 | /* Next, update the `struct FTW' information. */ |
536 | ++data->ftw.level; | |
0ecb606c | 537 | startp = __rawmemchr (data->dirbuf, '\0'); |
25f227b9 UD |
538 | /* There always must be a directory name. */ |
539 | assert (startp != data->dirbuf); | |
21a568e2 | 540 | if (startp[-1] != '/') |
25f227b9 | 541 | *startp++ = '/'; |
76b87c03 | 542 | data->ftw.base = startp - data->dirbuf; |
28f540f4 | 543 | |
2958e6cc | 544 | while (dir.stream != NULL && (d = __readdir64 (dir.stream)) != NULL) |
76b87c03 | 545 | { |
0ecb606c | 546 | result = process_entry (data, &dir, d->d_name, NAMLEN (d), d->d_type); |
76b87c03 UD |
547 | if (result != 0) |
548 | break; | |
549 | } | |
28f540f4 | 550 | |
76b87c03 | 551 | if (dir.stream != NULL) |
28f540f4 | 552 | { |
76b87c03 UD |
553 | /* The stream is still open. I.e., we did not need more |
554 | descriptors. Simply close the stream now. */ | |
555 | int save_err = errno; | |
556 | ||
557 | assert (dir.content == NULL); | |
558 | ||
50304ef0 | 559 | __closedir (dir.stream); |
0ecb606c | 560 | dir.streamfd = -1; |
76b87c03 UD |
561 | __set_errno (save_err); |
562 | ||
563 | if (data->actdir-- == 0) | |
564 | data->actdir = data->maxdir - 1; | |
565 | data->dirstreams[data->actdir] = NULL; | |
28f540f4 | 566 | } |
76b87c03 | 567 | else |
28f540f4 | 568 | { |
76b87c03 UD |
569 | int save_err; |
570 | char *runp = dir.content; | |
571 | ||
3d73829c | 572 | while (result == 0 && *runp != '\0') |
28f540f4 | 573 | { |
76b87c03 UD |
574 | char *endp = strchr (runp, '\0'); |
575 | ||
0ecb606c JJ |
576 | // XXX Should store the d_type values as well?! |
577 | result = process_entry (data, &dir, runp, endp - runp, DT_UNKNOWN); | |
76b87c03 UD |
578 | |
579 | runp = endp + 1; | |
28f540f4 | 580 | } |
76b87c03 UD |
581 | |
582 | save_err = errno; | |
583 | free (dir.content); | |
584 | __set_errno (save_err); | |
28f540f4 | 585 | } |
28f540f4 | 586 | |
ca10f338 UD |
587 | if ((data->flags & FTW_ACTIONRETVAL) && result == FTW_SKIP_SIBLINGS) |
588 | result = 0; | |
589 | ||
76b87c03 | 590 | /* Prepare the return, revert the `struct FTW' information. */ |
d951286f | 591 | data->dirbuf[data->ftw.base - 1] = '\0'; |
76b87c03 UD |
592 | --data->ftw.level; |
593 | data->ftw.base = previous_base; | |
594 | ||
595 | /* Finally, if we process depth-first report the directory. */ | |
596 | if (result == 0 && (data->flags & FTW_DEPTH)) | |
d951286f | 597 | result = (*data->func) (data->dirbuf, st, FTW_DP, &data->ftw); |
28f540f4 | 598 | |
ca10f338 UD |
599 | if (old_dir |
600 | && (data->flags & FTW_CHDIR) | |
601 | && (result == 0 | |
602 | || ((data->flags & FTW_ACTIONRETVAL) | |
603 | && (result != -1 && result != FTW_STOP)))) | |
604 | { | |
605 | /* Change back to the parent directory. */ | |
606 | int done = 0; | |
607 | if (old_dir->stream != NULL) | |
608 | if (__fchdir (dirfd (old_dir->stream)) == 0) | |
609 | done = 1; | |
610 | ||
611 | if (!done) | |
612 | { | |
613 | if (data->ftw.base == 1) | |
614 | { | |
615 | if (__chdir ("/") < 0) | |
616 | result = -1; | |
617 | } | |
618 | else | |
619 | if (__chdir ("..") < 0) | |
620 | result = -1; | |
621 | } | |
622 | } | |
623 | ||
76b87c03 UD |
624 | return result; |
625 | } | |
626 | ||
627 | ||
628 | static int | |
ca10f338 | 629 | __attribute ((noinline)) |
dfd2257a | 630 | internal_function |
76b87c03 UD |
631 | ftw_startup (const char *dir, int is_nftw, void *func, int descriptors, |
632 | int flags) | |
633 | { | |
634 | struct ftw_data data; | |
dfd2257a | 635 | struct STAT st; |
76b87c03 UD |
636 | int result = 0; |
637 | int save_err; | |
0ecb606c | 638 | int cwdfd = -1; |
d951286f | 639 | char *cwd = NULL; |
76b87c03 UD |
640 | char *cp; |
641 | ||
642 | /* First make sure the parameters are reasonable. */ | |
643 | if (dir[0] == '\0') | |
644 | { | |
a7f775a5 | 645 | __set_errno (ENOENT); |
76b87c03 UD |
646 | return -1; |
647 | } | |
28f540f4 | 648 | |
76b87c03 UD |
649 | data.maxdir = descriptors < 1 ? 1 : descriptors; |
650 | data.actdir = 0; | |
651 | data.dirstreams = (struct dir_data **) alloca (data.maxdir | |
652 | * sizeof (struct dir_data *)); | |
653 | memset (data.dirstreams, '\0', data.maxdir * sizeof (struct dir_data *)); | |
654 | ||
5049f197 | 655 | /* PATH_MAX is always defined when we get here. */ |
76b87c03 UD |
656 | data.dirbufsize = MAX (2 * strlen (dir), PATH_MAX); |
657 | data.dirbuf = (char *) malloc (data.dirbufsize); | |
658 | if (data.dirbuf == NULL) | |
659 | return -1; | |
d951286f | 660 | cp = __stpcpy (data.dirbuf, dir); |
76b87c03 UD |
661 | /* Strip trailing slashes. */ |
662 | while (cp > data.dirbuf + 1 && cp[-1] == '/') | |
663 | --cp; | |
664 | *cp = '\0'; | |
665 | ||
666 | data.ftw.level = 0; | |
667 | ||
668 | /* Find basename. */ | |
669 | while (cp > data.dirbuf && cp[-1] != '/') | |
670 | --cp; | |
671 | data.ftw.base = cp - data.dirbuf; | |
672 | ||
673 | data.flags = flags; | |
674 | ||
675 | /* This assignment might seem to be strange but it is what we want. | |
676 | The trick is that the first three arguments to the `ftw' and | |
677 | `nftw' callback functions are equal. Therefore we can call in | |
678 | every case the callback using the format of the `nftw' version | |
679 | and get the correct result since the stack layout for a function | |
680 | call in C allows this. */ | |
dfd2257a | 681 | data.func = (NFTW_FUNC_T) func; |
76b87c03 UD |
682 | |
683 | /* Since we internally use the complete set of FTW_* values we need | |
684 | to reduce the value range before calling a `ftw' callback. */ | |
685 | data.cvt_arr = is_nftw ? nftw_arr : ftw_arr; | |
686 | ||
d951286f UD |
687 | /* No object known so far. */ |
688 | data.known_objects = NULL; | |
689 | ||
76b87c03 | 690 | /* Now go to the directory containing the initial file/directory. */ |
34c86f42 | 691 | if (flags & FTW_CHDIR) |
28f540f4 | 692 | { |
0ecb606c JJ |
693 | /* We have to be able to go back to the current working |
694 | directory. The best way to do this is to use a file | |
695 | descriptor. */ | |
696 | cwdfd = __open (".", O_RDONLY | O_DIRECTORY); | |
697 | if (cwdfd == -1) | |
698 | { | |
699 | /* Try getting the directory name. This can be needed if | |
700 | the current directory is executable but not readable. */ | |
701 | if (errno == EACCES) | |
702 | /* GNU extension ahead. */ | |
703 | cwd = __getcwd (NULL, 0); | |
704 | ||
705 | if (cwd == NULL) | |
706 | goto out_fail; | |
707 | } | |
708 | else if (data.maxdir > 1) | |
709 | /* Account for the file descriptor we use here. */ | |
710 | --data.maxdir; | |
711 | ||
712 | if (data.ftw.base > 0) | |
28f540f4 | 713 | { |
76b87c03 UD |
714 | /* Change to the directory the file is in. In data.dirbuf |
715 | we have a writable copy of the file name. Just NUL | |
716 | terminate it for now and change the directory. */ | |
717 | if (data.ftw.base == 1) | |
718 | /* I.e., the file is in the root directory. */ | |
50304ef0 | 719 | result = __chdir ("/"); |
76b87c03 UD |
720 | else |
721 | { | |
722 | char ch = data.dirbuf[data.ftw.base - 1]; | |
723 | data.dirbuf[data.ftw.base - 1] = '\0'; | |
50304ef0 | 724 | result = __chdir (data.dirbuf); |
76b87c03 UD |
725 | data.dirbuf[data.ftw.base - 1] = ch; |
726 | } | |
28f540f4 RM |
727 | } |
728 | } | |
729 | ||
76b87c03 UD |
730 | /* Get stat info for start directory. */ |
731 | if (result == 0) | |
6e4c40ba | 732 | { |
0ecb606c JJ |
733 | const char *name; |
734 | ||
735 | if (data.flags & FTW_CHDIR) | |
736 | { | |
737 | name = data.dirbuf + data.ftw.base; | |
738 | if (name[0] == '\0') | |
739 | name = "."; | |
740 | } | |
741 | else | |
742 | name = data.dirbuf; | |
b2608c22 | 743 | |
6e4c40ba | 744 | if (((flags & FTW_PHYS) |
b2608c22 UD |
745 | ? LXSTAT (_STAT_VER, name, &st) |
746 | : XSTAT (_STAT_VER, name, &st)) < 0) | |
6e4c40ba | 747 | { |
0e24e73d UD |
748 | if (!(flags & FTW_PHYS) |
749 | && errno == ENOENT | |
40212ce0 | 750 | && LXSTAT (_STAT_VER, name, &st) == 0 |
0e24e73d | 751 | && S_ISLNK (st.st_mode)) |
6e4c40ba | 752 | result = (*data.func) (data.dirbuf, &st, data.cvt_arr[FTW_SLN], |
76b87c03 | 753 | &data.ftw); |
6e4c40ba UD |
754 | else |
755 | /* No need to call the callback since we cannot say anything | |
756 | about the object. */ | |
757 | result = -1; | |
758 | } | |
759 | else | |
760 | { | |
761 | if (S_ISDIR (st.st_mode)) | |
762 | { | |
763 | /* Remember the device of the initial directory in case | |
764 | FTW_MOUNT is given. */ | |
765 | data.dev = st.st_dev; | |
766 | ||
767 | /* We know this directory now. */ | |
768 | if (!(flags & FTW_PHYS)) | |
769 | result = add_object (&data, &st); | |
770 | ||
771 | if (result == 0) | |
ca10f338 | 772 | result = ftw_dir (&data, &st, NULL); |
6e4c40ba UD |
773 | } |
774 | else | |
775 | { | |
776 | int flag = S_ISLNK (st.st_mode) ? FTW_SL : FTW_F; | |
777 | ||
778 | result = (*data.func) (data.dirbuf, &st, data.cvt_arr[flag], | |
779 | &data.ftw); | |
780 | } | |
781 | } | |
ca10f338 UD |
782 | |
783 | if ((flags & FTW_ACTIONRETVAL) | |
784 | && (result == FTW_SKIP_SUBTREE || result == FTW_SKIP_SIBLINGS)) | |
785 | result = 0; | |
6e4c40ba | 786 | } |
76b87c03 UD |
787 | |
788 | /* Return to the start directory (if necessary). */ | |
0ecb606c JJ |
789 | if (cwdfd != -1) |
790 | { | |
791 | int save_err = errno; | |
792 | __fchdir (cwdfd); | |
793 | __set_errno (save_err); | |
794 | } | |
795 | else if (cwd != NULL) | |
76b87c03 UD |
796 | { |
797 | int save_err = errno; | |
50304ef0 | 798 | __chdir (cwd); |
76b87c03 UD |
799 | free (cwd); |
800 | __set_errno (save_err); | |
801 | } | |
802 | ||
803 | /* Free all memory. */ | |
0ecb606c | 804 | out_fail: |
76b87c03 | 805 | save_err = errno; |
d951286f | 806 | __tdestroy (data.known_objects, free); |
76b87c03 UD |
807 | free (data.dirbuf); |
808 | __set_errno (save_err); | |
809 | ||
810 | return result; | |
811 | } | |
812 | ||
813 | ||
814 | ||
815 | /* Entry points. */ | |
816 | ||
817 | int | |
dfd2257a | 818 | FTW_NAME (path, func, descriptors) |
76b87c03 | 819 | const char *path; |
dfd2257a | 820 | FTW_FUNC_T func; |
76b87c03 UD |
821 | int descriptors; |
822 | { | |
823 | return ftw_startup (path, 0, func, descriptors, 0); | |
824 | } | |
825 | ||
ca10f338 | 826 | #ifndef _LIBC |
76b87c03 | 827 | int |
dfd2257a | 828 | NFTW_NAME (path, func, descriptors, flags) |
76b87c03 | 829 | const char *path; |
dfd2257a | 830 | NFTW_FUNC_T func; |
76b87c03 UD |
831 | int descriptors; |
832 | int flags; | |
833 | { | |
834 | return ftw_startup (path, 1, func, descriptors, flags); | |
28f540f4 | 835 | } |
ca10f338 UD |
836 | #else |
837 | ||
0ecb606c | 838 | # include <shlib-compat.h> |
ca10f338 | 839 | |
3c0fb574 UD |
840 | int NFTW_NEW_NAME (const char *, NFTW_FUNC_T, int, int); |
841 | ||
ca10f338 UD |
842 | int |
843 | NFTW_NEW_NAME (path, func, descriptors, flags) | |
844 | const char *path; | |
845 | NFTW_FUNC_T func; | |
846 | int descriptors; | |
847 | int flags; | |
848 | { | |
849 | if (flags | |
850 | & ~(FTW_PHYS | FTW_MOUNT | FTW_CHDIR | FTW_DEPTH | FTW_ACTIONRETVAL)) | |
851 | { | |
852 | __set_errno (EINVAL); | |
853 | return -1; | |
854 | } | |
855 | return ftw_startup (path, 1, func, descriptors, flags); | |
856 | } | |
857 | ||
858 | versioned_symbol (libc, NFTW_NEW_NAME, NFTW_NAME, GLIBC_2_3_3); | |
859 | ||
0ecb606c | 860 | # if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_3_3) |
ca10f338 UD |
861 | |
862 | /* Older nftw* version just ignored all unknown flags. */ | |
863 | ||
3c0fb574 UD |
864 | int NFTW_OLD_NAME (const char *, NFTW_FUNC_T, int, int); |
865 | ||
ca10f338 | 866 | int |
4a381a81 | 867 | attribute_compat_text_section |
ca10f338 UD |
868 | NFTW_OLD_NAME (path, func, descriptors, flags) |
869 | const char *path; | |
870 | NFTW_FUNC_T func; | |
871 | int descriptors; | |
872 | int flags; | |
873 | { | |
874 | flags &= (FTW_PHYS | FTW_MOUNT | FTW_CHDIR | FTW_DEPTH); | |
875 | return ftw_startup (path, 1, func, descriptors, flags); | |
876 | } | |
877 | ||
878 | compat_symbol (libc, NFTW_OLD_NAME, NFTW_NAME, GLIBC_2_1); | |
0ecb606c | 879 | # endif |
ca10f338 | 880 | #endif |