]> git.ipfire.org Git - thirdparty/glibc.git/blame - io/ftw.c
Update.
[thirdparty/glibc.git] / io / ftw.c
CommitLineData
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
87struct dir_data
88{
89 DIR *stream;
90 char *content;
91};
28f540f4 92
d951286f
UD
93struct known_object
94{
95 dev_t dev;
dfd2257a 96 INO_T ino;
d951286f
UD
97};
98
76b87c03 99struct 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 137static 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 142static 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 149static int ftw_dir (struct ftw_data *data, struct STAT *st) internal_function;
d951286f
UD
150
151
152static int
153object_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
167static inline int
dfd2257a 168add_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
179static inline int
dfd2257a 180find_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
187static inline int
188open_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
278static inline int
279process_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
375static int
dfd2257a
UD
376internal_function
377ftw_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
496static int
dfd2257a 497internal_function
76b87c03
UD
498ftw_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
653int
dfd2257a 654FTW_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
662int
dfd2257a 663NFTW_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}