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