source: trunk/src/gmake/kmkbuiltin/fts.c@ 619

Last change on this file since 619 was 619, checked in by bird, 19 years ago

NetBSD: /fts13.c/1.44/Wed Jan 19 00:59:48 2005

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.7 KB
Line 
1/* $NetBSD: __fts13.c,v 1.44 2005/01/19 00:59:48 mycroft Exp $ */
2
3/*-
4 * Copyright (c) 1990, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#if HAVE_NBTOOL_CONFIG_H
33#include "nbtool_config.h"
34#endif
35
36#include <sys/cdefs.h>
37#if defined(LIBC_SCCS) && !defined(lint)
38#if 0
39static char sccsid[] = "@(#)fts.c 8.6 (Berkeley) 8/14/94";
40#else
41__RCSID("$NetBSD: __fts13.c,v 1.44 2005/01/19 00:59:48 mycroft Exp $");
42#endif
43#endif /* LIBC_SCCS and not lint */
44
45#include "namespace.h"
46#include <sys/param.h>
47#include <sys/stat.h>
48
49#include <assert.h>
50#include <dirent.h>
51#include <errno.h>
52#include <fcntl.h>
53#include <fts.h>
54#include <stdlib.h>
55#include <string.h>
56#include <unistd.h>
57
58#if ! HAVE_NBTOOL_CONFIG_H
59#define HAVE_STRUCT_DIRENT_D_NAMLEN 1
60#endif
61
62#ifdef __weak_alias
63#ifdef __LIBC12_SOURCE__
64__weak_alias(fts_children,_fts_children)
65__weak_alias(fts_close,_fts_close)
66__weak_alias(fts_open,_fts_open)
67__weak_alias(fts_read,_fts_read)
68__weak_alias(fts_set,_fts_set)
69#endif /* __LIBC12_SOURCE__ */
70#endif /* __weak_alias */
71
72#ifdef __LIBC12_SOURCE__
73#define STAT stat12
74#else
75#define STAT stat
76#endif
77
78#ifdef __LIBC12_SOURCE__
79__warn_references(fts_children,
80 "warning: reference to compatibility fts_children();"
81 " include <fts.h> for correct reference")
82__warn_references(fts_close,
83 "warning: reference to compatibility fts_close();"
84 " include <fts.h> for correct reference")
85__warn_references(fts_open,
86 "warning: reference to compatibility fts_open();"
87 " include <fts.h> for correct reference")
88__warn_references(fts_read,
89 "warning: reference to compatibility fts_read();"
90 " include <fts.h> for correct reference")
91__warn_references(fts_set,
92 "warning: reference to compatibility fts_set();"
93 " include <fts.h> for correct reference")
94#endif
95
96static FTSENT *fts_alloc __P((FTS *, const char *, size_t));
97static FTSENT *fts_build __P((FTS *, int));
98static void fts_lfree __P((FTSENT *));
99static void fts_load __P((FTS *, FTSENT *));
100static size_t fts_maxarglen __P((char * const *));
101static size_t fts_pow2 __P((size_t));
102static int fts_palloc __P((FTS *, size_t));
103static void fts_padjust __P((FTS *, FTSENT *));
104static FTSENT *fts_sort __P((FTS *, FTSENT *, size_t));
105static u_short fts_stat __P((FTS *, FTSENT *, int));
106static int fts_safe_changedir __P((const FTS *, const FTSENT *, int,
107 const char *));
108
109#define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))
110
111#define CLR(opt) (sp->fts_options &= ~(opt))
112#define ISSET(opt) (sp->fts_options & (opt))
113#define SET(opt) (sp->fts_options |= (opt))
114
115#define CHDIR(sp, path) (!ISSET(FTS_NOCHDIR) && chdir(path))
116#define FCHDIR(sp, fd) (!ISSET(FTS_NOCHDIR) && fchdir(fd))
117
118/* fts_build flags */
119#define BCHILD 1 /* fts_children */
120#define BNAMES 2 /* fts_children, names only */
121#define BREAD 3 /* fts_read */
122
123#ifndef DTF_HIDEW
124#undef FTS_WHITEOUT
125#endif
126
127FTS *
128fts_open(argv, options, compar)
129 char * const *argv;
130 int options;
131 int (*compar) __P((const FTSENT **, const FTSENT **));
132{
133 FTS *sp;
134 FTSENT *p, *root;
135 size_t nitems;
136 FTSENT *parent, *tmp = NULL; /* pacify gcc */
137 size_t len;
138
139 _DIAGASSERT(argv != NULL);
140
141 /* Options check. */
142 if (options & ~FTS_OPTIONMASK) {
143 errno = EINVAL;
144 return (NULL);
145 }
146
147 /* Allocate/initialize the stream */
148 if ((sp = malloc((u_int)sizeof(FTS))) == NULL)
149 return (NULL);
150 memset(sp, 0, sizeof(FTS));
151 sp->fts_compar = compar;
152 sp->fts_options = options;
153
154 /* Logical walks turn on NOCHDIR; symbolic links are too hard. */
155 if (ISSET(FTS_LOGICAL))
156 SET(FTS_NOCHDIR);
157
158 /*
159 * Start out with 1K of path space, and enough, in any case,
160 * to hold the user's paths.
161 */
162 if (fts_palloc(sp, MAX(fts_maxarglen(argv), MAXPATHLEN)))
163 goto mem1;
164
165 /* Allocate/initialize root's parent. */
166 if ((parent = fts_alloc(sp, "", 0)) == NULL)
167 goto mem2;
168 parent->fts_level = FTS_ROOTPARENTLEVEL;
169
170 /* Allocate/initialize root(s). */
171 for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) {
172 /* Don't allow zero-length paths. */
173 if ((len = strlen(*argv)) == 0) {
174 errno = ENOENT;
175 goto mem3;
176 }
177
178 if ((p = fts_alloc(sp, *argv, len)) == NULL)
179 goto mem3;
180 p->fts_level = FTS_ROOTLEVEL;
181 p->fts_parent = parent;
182 p->fts_accpath = p->fts_name;
183 p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW));
184
185 /* Command-line "." and ".." are real directories. */
186 if (p->fts_info == FTS_DOT)
187 p->fts_info = FTS_D;
188
189 /*
190 * If comparison routine supplied, traverse in sorted
191 * order; otherwise traverse in the order specified.
192 */
193 if (compar) {
194 p->fts_link = root;
195 root = p;
196 } else {
197 p->fts_link = NULL;
198 if (root == NULL)
199 tmp = root = p;
200 else {
201 tmp->fts_link = p;
202 tmp = p;
203 }
204 }
205 }
206 if (compar && nitems > 1)
207 root = fts_sort(sp, root, nitems);
208
209 /*
210 * Allocate a dummy pointer and make fts_read think that we've just
211 * finished the node before the root(s); set p->fts_info to FTS_INIT
212 * so that everything about the "current" node is ignored.
213 */
214 if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL)
215 goto mem3;
216 sp->fts_cur->fts_link = root;
217 sp->fts_cur->fts_info = FTS_INIT;
218
219 /*
220 * If using chdir(2), grab a file descriptor pointing to dot to insure
221 * that we can get back here; this could be avoided for some paths,
222 * but almost certainly not worth the effort. Slashes, symbolic links,
223 * and ".." are all fairly nasty problems. Note, if we can't get the
224 * descriptor we run anyway, just more slowly.
225 */
226 if (!ISSET(FTS_NOCHDIR)) {
227 if ((sp->fts_rfd = open(".", O_RDONLY, 0)) == -1)
228 SET(FTS_NOCHDIR);
229 else if (fcntl(sp->fts_rfd, F_SETFD, FD_CLOEXEC) == -1) {
230 close(sp->fts_rfd);
231 SET(FTS_NOCHDIR);
232 }
233 }
234
235 return (sp);
236
237mem3: fts_lfree(root);
238 free(parent);
239mem2: free(sp->fts_path);
240mem1: free(sp);
241 return (NULL);
242}
243
244static void
245fts_load(sp, p)
246 FTS *sp;
247 FTSENT *p;
248{
249 size_t len;
250 char *cp;
251
252 _DIAGASSERT(sp != NULL);
253 _DIAGASSERT(p != NULL);
254
255 /*
256 * Load the stream structure for the next traversal. Since we don't
257 * actually enter the directory until after the preorder visit, set
258 * the fts_accpath field specially so the chdir gets done to the right
259 * place and the user can access the first node. From fts_open it's
260 * known that the path will fit.
261 */
262 len = p->fts_pathlen = p->fts_namelen;
263 memmove(sp->fts_path, p->fts_name, len + 1);
264 if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) {
265 len = strlen(++cp);
266 memmove(p->fts_name, cp, len + 1);
267 p->fts_namelen = len;
268 }
269 p->fts_accpath = p->fts_path = sp->fts_path;
270 sp->fts_dev = p->fts_dev;
271}
272
273int
274fts_close(sp)
275 FTS *sp;
276{
277 FTSENT *freep, *p;
278 int saved_errno = 0;
279
280 _DIAGASSERT(sp != NULL);
281
282 /*
283 * This still works if we haven't read anything -- the dummy structure
284 * points to the root list, so we step through to the end of the root
285 * list which has a valid parent pointer.
286 */
287 if (sp->fts_cur) {
288 if (ISSET(FTS_SYMFOLLOW))
289 (void)close(sp->fts_cur->fts_symfd);
290 for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {
291 freep = p;
292 p = p->fts_link ? p->fts_link : p->fts_parent;
293 free(freep);
294 }
295 free(p);
296 }
297
298 /* Free up child linked list, sort array, path buffer. */
299 if (sp->fts_child)
300 fts_lfree(sp->fts_child);
301 if (sp->fts_array)
302 free(sp->fts_array);
303 free(sp->fts_path);
304
305 /* Return to original directory, save errno if necessary. */
306 if (!ISSET(FTS_NOCHDIR)) {
307 if (fchdir(sp->fts_rfd))
308 saved_errno = errno;
309 (void)close(sp->fts_rfd);
310 }
311
312 /* Free up the stream pointer. */
313 free(sp);
314 /* ISSET() is illegal after this, since the macro touches sp */
315
316 /* Set errno and return. */
317 if (saved_errno) {
318 errno = saved_errno;
319 return (-1);
320 }
321 return (0);
322}
323
324/*
325 * Special case a root of "/" so that slashes aren't appended which would
326 * cause paths to be written as "//foo".
327 */
328#define NAPPEND(p) \
329 (p->fts_level == FTS_ROOTLEVEL && p->fts_pathlen == 1 && \
330 p->fts_path[0] == '/' ? 0 : p->fts_pathlen)
331
332FTSENT *
333fts_read(sp)
334 FTS *sp;
335{
336 FTSENT *p, *tmp;
337 int instr;
338 char *t;
339 int saved_errno;
340
341 _DIAGASSERT(sp != NULL);
342
343 /* If finished or unrecoverable error, return NULL. */
344 if (sp->fts_cur == NULL || ISSET(FTS_STOP))
345 return (NULL);
346
347 /* Set current node pointer. */
348 p = sp->fts_cur;
349
350 /* Save and zero out user instructions. */
351 instr = p->fts_instr;
352 p->fts_instr = FTS_NOINSTR;
353
354 /* Any type of file may be re-visited; re-stat and re-turn. */
355 if (instr == FTS_AGAIN) {
356 p->fts_info = fts_stat(sp, p, 0);
357 return (p);
358 }
359
360 /*
361 * Following a symlink -- SLNONE test allows application to see
362 * SLNONE and recover. If indirecting through a symlink, have
363 * keep a pointer to current location. If unable to get that
364 * pointer, follow fails.
365 */
366 if (instr == FTS_FOLLOW &&
367 (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {
368 p->fts_info = fts_stat(sp, p, 1);
369 if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
370 if ((p->fts_symfd = open(".", O_RDONLY, 0)) == -1) {
371 p->fts_errno = errno;
372 p->fts_info = FTS_ERR;
373 } else if (fcntl(p->fts_symfd, F_SETFD, FD_CLOEXEC) == -1) {
374 p->fts_errno = errno;
375 p->fts_info = FTS_ERR;
376 close(p->fts_symfd);
377 } else
378 p->fts_flags |= FTS_SYMFOLLOW;
379 }
380 return (p);
381 }
382
383 /* Directory in pre-order. */
384 if (p->fts_info == FTS_D) {
385 /* If skipped or crossed mount point, do post-order visit. */
386 if (instr == FTS_SKIP ||
387 (ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev)) {
388 if (p->fts_flags & FTS_SYMFOLLOW)
389 (void)close(p->fts_symfd);
390 if (sp->fts_child) {
391 fts_lfree(sp->fts_child);
392 sp->fts_child = NULL;
393 }
394 p->fts_info = FTS_DP;
395 return (p);
396 }
397
398 /* Rebuild if only read the names and now traversing. */
399 if (sp->fts_child && ISSET(FTS_NAMEONLY)) {
400 CLR(FTS_NAMEONLY);
401 fts_lfree(sp->fts_child);
402 sp->fts_child = NULL;
403 }
404
405 /*
406 * Cd to the subdirectory.
407 *
408 * If have already read and now fail to chdir, whack the list
409 * to make the names come out right, and set the parent errno
410 * so the application will eventually get an error condition.
411 * Set the FTS_DONTCHDIR flag so that when we logically change
412 * directories back to the parent we don't do a chdir.
413 *
414 * If haven't read do so. If the read fails, fts_build sets
415 * FTS_STOP or the fts_info field of the node.
416 */
417 if (sp->fts_child) {
418 if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) {
419 p->fts_errno = errno;
420 p->fts_flags |= FTS_DONTCHDIR;
421 for (p = sp->fts_child; p; p = p->fts_link)
422 p->fts_accpath =
423 p->fts_parent->fts_accpath;
424 }
425 } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) {
426 if (ISSET(FTS_STOP))
427 return (NULL);
428 return (p);
429 }
430 p = sp->fts_child;
431 sp->fts_child = NULL;
432 goto name;
433 }
434
435 /* Move to the next node on this level. */
436next: tmp = p;
437 if ((p = p->fts_link) != NULL) {
438 free(tmp);
439
440 /*
441 * If reached the top, return to the original directory, and
442 * load the paths for the next root.
443 */
444 if (p->fts_level == FTS_ROOTLEVEL) {
445 if (FCHDIR(sp, sp->fts_rfd)) {
446 SET(FTS_STOP);
447 return (NULL);
448 }
449 fts_load(sp, p);
450 return (sp->fts_cur = p);
451 }
452
453 /*
454 * User may have called fts_set on the node. If skipped,
455 * ignore. If followed, get a file descriptor so we can
456 * get back if necessary.
457 */
458 if (p->fts_instr == FTS_SKIP)
459 goto next;
460 if (p->fts_instr == FTS_FOLLOW) {
461 p->fts_info = fts_stat(sp, p, 1);
462 if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
463 if ((p->fts_symfd =
464 open(".", O_RDONLY, 0)) == -1) {
465 p->fts_errno = errno;
466 p->fts_info = FTS_ERR;
467 } else if (fcntl(p->fts_symfd, F_SETFD, FD_CLOEXEC) == -1) {
468 p->fts_errno = errno;
469 p->fts_info = FTS_ERR;
470 close(p->fts_symfd);
471 } else
472 p->fts_flags |= FTS_SYMFOLLOW;
473 }
474 p->fts_instr = FTS_NOINSTR;
475 }
476
477name: t = sp->fts_path + NAPPEND(p->fts_parent);
478 *t++ = '/';
479 memmove(t, p->fts_name, (size_t)(p->fts_namelen + 1));
480 return (sp->fts_cur = p);
481 }
482
483 /* Move up to the parent node. */
484 p = tmp->fts_parent;
485 free(tmp);
486
487 if (p->fts_level == FTS_ROOTPARENTLEVEL) {
488 /*
489 * Done; free everything up and set errno to 0 so the user
490 * can distinguish between error and EOF.
491 */
492 free(p);
493 errno = 0;
494 return (sp->fts_cur = NULL);
495 }
496
497 /* Nul terminate the pathname. */
498 sp->fts_path[p->fts_pathlen] = '\0';
499
500 /*
501 * Return to the parent directory. If at a root node or came through
502 * a symlink, go back through the file descriptor. Otherwise, cd up
503 * one directory.
504 */
505 if (p->fts_level == FTS_ROOTLEVEL) {
506 if (FCHDIR(sp, sp->fts_rfd)) {
507 SET(FTS_STOP);
508 return (NULL);
509 }
510 } else if (p->fts_flags & FTS_SYMFOLLOW) {
511 if (FCHDIR(sp, p->fts_symfd)) {
512 saved_errno = errno;
513 (void)close(p->fts_symfd);
514 errno = saved_errno;
515 SET(FTS_STOP);
516 return (NULL);
517 }
518 (void)close(p->fts_symfd);
519 } else if (!(p->fts_flags & FTS_DONTCHDIR) &&
520 fts_safe_changedir(sp, p->fts_parent, -1, "..")) {
521 SET(FTS_STOP);
522 return (NULL);
523 }
524 p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;
525 return (sp->fts_cur = p);
526}
527
528/*
529 * Fts_set takes the stream as an argument although it's not used in this
530 * implementation; it would be necessary if anyone wanted to add global
531 * semantics to fts using fts_set. An error return is allowed for similar
532 * reasons.
533 */
534/* ARGSUSED */
535int
536fts_set(sp, p, instr)
537 FTS *sp;
538 FTSENT *p;
539 int instr;
540{
541
542 _DIAGASSERT(sp != NULL);
543 _DIAGASSERT(p != NULL);
544
545 if (instr && instr != FTS_AGAIN && instr != FTS_FOLLOW &&
546 instr != FTS_NOINSTR && instr != FTS_SKIP) {
547 errno = EINVAL;
548 return (1);
549 }
550 p->fts_instr = instr;
551 return (0);
552}
553
554FTSENT *
555fts_children(sp, instr)
556 FTS *sp;
557 int instr;
558{
559 FTSENT *p;
560 int fd;
561
562 _DIAGASSERT(sp != NULL);
563
564 if (instr && instr != FTS_NAMEONLY) {
565 errno = EINVAL;
566 return (NULL);
567 }
568
569 /* Set current node pointer. */
570 p = sp->fts_cur;
571
572 /*
573 * Errno set to 0 so user can distinguish empty directory from
574 * an error.
575 */
576 errno = 0;
577
578 /* Fatal errors stop here. */
579 if (ISSET(FTS_STOP))
580 return (NULL);
581
582 /* Return logical hierarchy of user's arguments. */
583 if (p->fts_info == FTS_INIT)
584 return (p->fts_link);
585
586 /*
587 * If not a directory being visited in pre-order, stop here. Could
588 * allow FTS_DNR, assuming the user has fixed the problem, but the
589 * same effect is available with FTS_AGAIN.
590 */
591 if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */)
592 return (NULL);
593
594 /* Free up any previous child list. */
595 if (sp->fts_child)
596 fts_lfree(sp->fts_child);
597
598 if (instr == FTS_NAMEONLY) {
599 SET(FTS_NAMEONLY);
600 instr = BNAMES;
601 } else
602 instr = BCHILD;
603
604 /*
605 * If using chdir on a relative path and called BEFORE fts_read does
606 * its chdir to the root of a traversal, we can lose -- we need to
607 * chdir into the subdirectory, and we don't know where the current
608 * directory is, so we can't get back so that the upcoming chdir by
609 * fts_read will work.
610 */
611 if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' ||
612 ISSET(FTS_NOCHDIR))
613 return (sp->fts_child = fts_build(sp, instr));
614
615 if ((fd = open(".", O_RDONLY, 0)) == -1)
616 return (sp->fts_child = NULL);
617 sp->fts_child = fts_build(sp, instr);
618 if (fchdir(fd)) {
619 (void)close(fd);
620 return (NULL);
621 }
622 (void)close(fd);
623 return (sp->fts_child);
624}
625
626/*
627 * This is the tricky part -- do not casually change *anything* in here. The
628 * idea is to build the linked list of entries that are used by fts_children
629 * and fts_read. There are lots of special cases.
630 *
631 * The real slowdown in walking the tree is the stat calls. If FTS_NOSTAT is
632 * set and it's a physical walk (so that symbolic links can't be directories),
633 * we can do things quickly. First, if it's a 4.4BSD file system, the type
634 * of the file is in the directory entry. Otherwise, we assume that the number
635 * of subdirectories in a node is equal to the number of links to the parent.
636 * The former skips all stat calls. The latter skips stat calls in any leaf
637 * directories and for any files after the subdirectories in the directory have
638 * been found, cutting the stat calls by about 2/3.
639 */
640static FTSENT *
641fts_build(sp, type)
642 FTS *sp;
643 int type;
644{
645 struct dirent *dp;
646 FTSENT *p, *head;
647 size_t nitems;
648 FTSENT *cur, *tail;
649 DIR *dirp;
650 int adjust, cderrno, descend, len, level, nlinks, saved_errno, nostat;
651 size_t maxlen;
652#ifdef FTS_WHITEOUT
653 int oflag;
654#endif
655 char *cp = NULL; /* pacify gcc */
656
657 _DIAGASSERT(sp != NULL);
658
659 /* Set current node pointer. */
660 cur = sp->fts_cur;
661
662 /*
663 * Open the directory for reading. If this fails, we're done.
664 * If being called from fts_read, set the fts_info field.
665 */
666#ifdef FTS_WHITEOUT
667 if (ISSET(FTS_WHITEOUT))
668 oflag = DTF_NODUP|DTF_REWIND;
669 else
670 oflag = DTF_HIDEW|DTF_NODUP|DTF_REWIND;
671#else
672#define __opendir2(path, flag) opendir(path)
673#endif
674 if ((dirp = __opendir2(cur->fts_accpath, oflag)) == NULL) {
675 if (type == BREAD) {
676 cur->fts_info = FTS_DNR;
677 cur->fts_errno = errno;
678 }
679 return (NULL);
680 }
681
682 /*
683 * Nlinks is the number of possible entries of type directory in the
684 * directory if we're cheating on stat calls, 0 if we're not doing
685 * any stat calls at all, -1 if we're doing stats on everything.
686 */
687 if (type == BNAMES) {
688 nlinks = 0;
689 nostat = 1;
690 } else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) {
691 nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2);
692 nostat = 1;
693 } else {
694 nlinks = -1;
695 nostat = 0;
696 }
697
698#ifdef notdef
699 (void)printf("nlinks == %d (cur: %d)\n", nlinks, cur->fts_nlink);
700 (void)printf("NOSTAT %d PHYSICAL %d SEEDOT %d\n",
701 ISSET(FTS_NOSTAT), ISSET(FTS_PHYSICAL), ISSET(FTS_SEEDOT));
702#endif
703 /*
704 * If we're going to need to stat anything or we want to descend
705 * and stay in the directory, chdir. If this fails we keep going,
706 * but set a flag so we don't chdir after the post-order visit.
707 * We won't be able to stat anything, but we can still return the
708 * names themselves. Note, that since fts_read won't be able to
709 * chdir into the directory, it will have to return different path
710 * names than before, i.e. "a/b" instead of "b". Since the node
711 * has already been visited in pre-order, have to wait until the
712 * post-order visit to return the error. There is a special case
713 * here, if there was nothing to stat then it's not an error to
714 * not be able to stat. This is all fairly nasty. If a program
715 * needed sorted entries or stat information, they had better be
716 * checking FTS_NS on the returned nodes.
717 */
718 cderrno = 0;
719 if (nlinks || type == BREAD) {
720 if (fts_safe_changedir(sp, cur, dirfd(dirp), NULL)) {
721 if (nlinks && type == BREAD)
722 cur->fts_errno = errno;
723 cur->fts_flags |= FTS_DONTCHDIR;
724 descend = 0;
725 cderrno = errno;
726 } else
727 descend = 1;
728 } else
729 descend = 0;
730
731 /*
732 * Figure out the max file name length that can be stored in the
733 * current path -- the inner loop allocates more path as necessary.
734 * We really wouldn't have to do the maxlen calculations here, we
735 * could do them in fts_read before returning the path, but it's a
736 * lot easier here since the length is part of the dirent structure.
737 *
738 * If not changing directories set a pointer so that can just append
739 * each new name into the path.
740 */
741 len = NAPPEND(cur);
742 if (ISSET(FTS_NOCHDIR)) {
743 cp = sp->fts_path + len;
744 *cp++ = '/';
745 }
746 len++;
747 maxlen = sp->fts_pathlen - len;
748
749 level = cur->fts_level + 1;
750
751 /* Read the directory, attaching each entry to the `link' pointer. */
752 adjust = 0;
753 for (head = tail = NULL, nitems = 0; (dp = readdir(dirp)) != NULL;) {
754 size_t dlen;
755
756 if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))
757 continue;
758
759#if HAVE_STRUCT_DIRENT_D_NAMLEN
760 dlen = dp->d_namlen;
761#else
762 dlen = strlen(dp->d_name);
763#endif
764 if ((p = fts_alloc(sp, dp->d_name, dlen)) == NULL)
765 goto mem1;
766 if (dlen >= maxlen) { /* include space for NUL */
767 if (fts_palloc(sp, len + dlen + 1)) {
768 /*
769 * No more memory for path or structures. Save
770 * errno, free up the current structure and the
771 * structures already allocated.
772 */
773mem1: saved_errno = errno;
774 if (p)
775 free(p);
776 fts_lfree(head);
777 (void)closedir(dirp);
778 errno = saved_errno;
779 cur->fts_info = FTS_ERR;
780 SET(FTS_STOP);
781 return (NULL);
782 }
783 adjust = 1;
784 if (ISSET(FTS_NOCHDIR))
785 cp = sp->fts_path + len;
786 maxlen = sp->fts_pathlen - len;
787 }
788
789 p->fts_pathlen = len + dlen;
790 p->fts_parent = sp->fts_cur;
791 p->fts_level = level;
792
793#ifdef FTS_WHITEOUT
794 if (dp->d_type == DT_WHT)
795 p->fts_flags |= FTS_ISW;
796#endif
797
798 if (cderrno) {
799 if (nlinks) {
800 p->fts_info = FTS_NS;
801 p->fts_errno = cderrno;
802 } else
803 p->fts_info = FTS_NSOK;
804 p->fts_accpath = cur->fts_accpath;
805 } else if (nlinks == 0
806#ifdef DT_DIR
807 || (nostat &&
808 dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN)
809#endif
810 ) {
811 p->fts_accpath =
812 ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name;
813 p->fts_info = FTS_NSOK;
814 } else {
815 /* Build a file name for fts_stat to stat. */
816 if (ISSET(FTS_NOCHDIR)) {
817 p->fts_accpath = p->fts_path;
818 memmove(cp, p->fts_name,
819 (size_t)(p->fts_namelen + 1));
820 } else
821 p->fts_accpath = p->fts_name;
822 /* Stat it. */
823 p->fts_info = fts_stat(sp, p, 0);
824
825 /* Decrement link count if applicable. */
826 if (nlinks > 0 && (p->fts_info == FTS_D ||
827 p->fts_info == FTS_DC || p->fts_info == FTS_DOT))
828 --nlinks;
829 }
830
831 /* We walk in directory order so "ls -f" doesn't get upset. */
832 p->fts_link = NULL;
833 if (head == NULL)
834 head = tail = p;
835 else {
836 tail->fts_link = p;
837 tail = p;
838 }
839 ++nitems;
840 }
841 (void)closedir(dirp);
842
843 /*
844 * If had to realloc the path, adjust the addresses for the rest
845 * of the tree.
846 */
847 if (adjust)
848 fts_padjust(sp, head);
849
850 /*
851 * If not changing directories, reset the path back to original
852 * state.
853 */
854 if (ISSET(FTS_NOCHDIR)) {
855 if (cp - 1 > sp->fts_path)
856 --cp;
857 *cp = '\0';
858 }
859
860 /*
861 * If descended after called from fts_children or after called from
862 * fts_read and nothing found, get back. At the root level we use
863 * the saved fd; if one of fts_open()'s arguments is a relative path
864 * to an empty directory, we wind up here with no other way back. If
865 * can't get back, we're done.
866 */
867 if (descend && (type == BCHILD || !nitems) &&
868 (cur->fts_level == FTS_ROOTLEVEL ?
869 FCHDIR(sp, sp->fts_rfd) :
870 fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) {
871 cur->fts_info = FTS_ERR;
872 SET(FTS_STOP);
873 return (NULL);
874 }
875
876 /* If didn't find anything, return NULL. */
877 if (!nitems) {
878 if (type == BREAD)
879 cur->fts_info = FTS_DP;
880 return (NULL);
881 }
882
883 /* Sort the entries. */
884 if (sp->fts_compar && nitems > 1)
885 head = fts_sort(sp, head, nitems);
886 return (head);
887}
888
889static u_short
890fts_stat(sp, p, follow)
891 FTS *sp;
892 FTSENT *p;
893 int follow;
894{
895 FTSENT *t;
896 dev_t dev;
897 ino_t ino;
898 struct STAT *sbp, sb;
899 int saved_errno;
900
901 _DIAGASSERT(sp != NULL);
902 _DIAGASSERT(p != NULL);
903
904 /* If user needs stat info, stat buffer already allocated. */
905 sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp;
906
907#ifdef FTS_WHITEOUT
908 /* check for whiteout */
909 if (p->fts_flags & FTS_ISW) {
910 if (sbp != &sb) {
911 memset(sbp, '\0', sizeof (*sbp));
912 sbp->st_mode = S_IFWHT;
913 }
914 return (FTS_W);
915 }
916#endif
917
918 /*
919 * If doing a logical walk, or application requested FTS_FOLLOW, do
920 * a stat(2). If that fails, check for a non-existent symlink. If
921 * fail, set the errno from the stat call.
922 */
923 if (ISSET(FTS_LOGICAL) || follow) {
924 if (stat(p->fts_accpath, sbp)) {
925 saved_errno = errno;
926 if (!lstat(p->fts_accpath, sbp)) {
927 errno = 0;
928 return (FTS_SLNONE);
929 }
930 p->fts_errno = saved_errno;
931 goto err;
932 }
933 } else if (lstat(p->fts_accpath, sbp)) {
934 p->fts_errno = errno;
935err: memset(sbp, 0, sizeof(struct STAT));
936 return (FTS_NS);
937 }
938
939 if (S_ISDIR(sbp->st_mode)) {
940 /*
941 * Set the device/inode. Used to find cycles and check for
942 * crossing mount points. Also remember the link count, used
943 * in fts_build to limit the number of stat calls. It is
944 * understood that these fields are only referenced if fts_info
945 * is set to FTS_D.
946 */
947 dev = p->fts_dev = sbp->st_dev;
948 ino = p->fts_ino = sbp->st_ino;
949 p->fts_nlink = sbp->st_nlink;
950
951 if (ISDOT(p->fts_name))
952 return (FTS_DOT);
953
954 /*
955 * Cycle detection is done by brute force when the directory
956 * is first encountered. If the tree gets deep enough or the
957 * number of symbolic links to directories is high enough,
958 * something faster might be worthwhile.
959 */
960 for (t = p->fts_parent;
961 t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent)
962 if (ino == t->fts_ino && dev == t->fts_dev) {
963 p->fts_cycle = t;
964 return (FTS_DC);
965 }
966 return (FTS_D);
967 }
968 if (S_ISLNK(sbp->st_mode))
969 return (FTS_SL);
970 if (S_ISREG(sbp->st_mode))
971 return (FTS_F);
972 return (FTS_DEFAULT);
973}
974
975static FTSENT *
976fts_sort(sp, head, nitems)
977 FTS *sp;
978 FTSENT *head;
979 size_t nitems;
980{
981 FTSENT **ap, *p;
982
983 _DIAGASSERT(sp != NULL);
984 _DIAGASSERT(head != NULL);
985
986 /*
987 * Construct an array of pointers to the structures and call qsort(3).
988 * Reassemble the array in the order returned by qsort. If unable to
989 * sort for memory reasons, return the directory entries in their
990 * current order. Allocate enough space for the current needs plus
991 * 40 so don't realloc one entry at a time.
992 */
993 if (nitems > sp->fts_nitems) {
994 FTSENT **new;
995
996 new = realloc(sp->fts_array, sizeof(FTSENT *) * (nitems + 40));
997 if (new == 0)
998 return (head);
999 sp->fts_array = new;
1000 sp->fts_nitems = nitems + 40;
1001 }
1002 for (ap = sp->fts_array, p = head; p; p = p->fts_link)
1003 *ap++ = p;
1004 qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *),
1005 (int (*) __P((const void *, const void *)))sp->fts_compar);
1006 for (head = *(ap = sp->fts_array); --nitems; ++ap)
1007 ap[0]->fts_link = ap[1];
1008 ap[0]->fts_link = NULL;
1009 return (head);
1010}
1011
1012static FTSENT *
1013fts_alloc(sp, name, namelen)
1014 FTS *sp;
1015 const char *name;
1016 size_t namelen;
1017{
1018 FTSENT *p;
1019 size_t len;
1020
1021 _DIAGASSERT(sp != NULL);
1022 _DIAGASSERT(name != NULL);
1023
1024#if defined(ALIGNBYTES) && defined(ALIGN)
1025 /*
1026 * The file name is a variable length array and no stat structure is
1027 * necessary if the user has set the nostat bit. Allocate the FTSENT
1028 * structure, the file name and the stat structure in one chunk, but
1029 * be careful that the stat structure is reasonably aligned. Since the
1030 * fts_name field is declared to be of size 1, the fts_name pointer is
1031 * namelen + 2 before the first possible address of the stat structure.
1032 */
1033 len = sizeof(FTSENT) + namelen;
1034 if (!ISSET(FTS_NOSTAT))
1035 len += sizeof(struct STAT) + ALIGNBYTES;
1036 if ((p = malloc(len)) == NULL)
1037 return (NULL);
1038
1039 if (!ISSET(FTS_NOSTAT))
1040 p->fts_statp =
1041 (struct STAT *)ALIGN((u_long)(p->fts_name + namelen + 2));
1042#else
1043 if ((p = malloc(sizeof(FTSENT) + namelen)) == NULL)
1044 return (NULL);
1045
1046 if (!ISSET(FTS_NOSTAT))
1047 if ((p->fts_statp = malloc(sizeof(struct STAT))) == NULL) {
1048 free(p);
1049 return (NULL);
1050 }
1051#endif
1052
1053 /* Copy the name plus the trailing NULL. */
1054 memmove(p->fts_name, name, namelen + 1);
1055
1056 p->fts_namelen = namelen;
1057 p->fts_path = sp->fts_path;
1058 p->fts_errno = 0;
1059 p->fts_flags = 0;
1060 p->fts_instr = FTS_NOINSTR;
1061 p->fts_number = 0;
1062 p->fts_pointer = NULL;
1063 return (p);
1064}
1065
1066static void
1067fts_lfree(head)
1068 FTSENT *head;
1069{
1070 FTSENT *p;
1071
1072 /* XXX: head may be NULL ? */
1073
1074 /* Free a linked list of structures. */
1075 while ((p = head) != NULL) {
1076 head = head->fts_link;
1077
1078#if !defined(ALIGNBYTES) || !defined(ALIGN)
1079 if (p->fts_statp)
1080 free(p->fts_statp);
1081#endif
1082 free(p);
1083 }
1084}
1085
1086static size_t
1087fts_pow2(x)
1088 size_t x;
1089{
1090
1091 x--;
1092 x |= x>>1;
1093 x |= x>>2;
1094 x |= x>>4;
1095 x |= x>>8;
1096 x |= x>>16;
1097#if LONG_BIT > 32
1098 x |= x>>32;
1099#endif
1100#if LONG_BIT > 64
1101 x |= x>>64;
1102#endif
1103 x++;
1104 return (x);
1105}
1106
1107/*
1108 * Allow essentially unlimited paths; find, rm, ls should all work on any tree.
1109 * Most systems will allow creation of paths much longer than MAXPATHLEN, even
1110 * though the kernel won't resolve them. Round up the new size to a power of 2,
1111 * so we don't realloc the path 2 bytes at a time.
1112 */
1113static int
1114fts_palloc(sp, size)
1115 FTS *sp;
1116 size_t size;
1117{
1118 char *new;
1119
1120 _DIAGASSERT(sp != NULL);
1121
1122#if 1
1123 /* Protect against fts_pathlen overflow. */
1124 if (size > USHRT_MAX + 1) {
1125 errno = ENOMEM;
1126 return (1);
1127 }
1128#endif
1129 size = fts_pow2(size);
1130 new = realloc(sp->fts_path, size);
1131 if (new == 0)
1132 return (1);
1133 sp->fts_path = new;
1134 sp->fts_pathlen = size;
1135 return (0);
1136}
1137
1138/*
1139 * When the path is realloc'd, have to fix all of the pointers in structures
1140 * already returned.
1141 */
1142static void
1143fts_padjust(sp, head)
1144 FTS *sp;
1145 FTSENT *head;
1146{
1147 FTSENT *p;
1148 char *addr;
1149
1150 _DIAGASSERT(sp != NULL);
1151
1152#define ADJUST(p) do { \
1153 if ((p)->fts_accpath != (p)->fts_name) \
1154 (p)->fts_accpath = \
1155 addr + ((p)->fts_accpath - (p)->fts_path); \
1156 (p)->fts_path = addr; \
1157} while (/*CONSTCOND*/0)
1158
1159 addr = sp->fts_path;
1160
1161 /* Adjust the current set of children. */
1162 for (p = sp->fts_child; p; p = p->fts_link)
1163 ADJUST(p);
1164
1165 /* Adjust the rest of the tree, including the current level. */
1166 for (p = head; p->fts_level >= FTS_ROOTLEVEL;) {
1167 ADJUST(p);
1168 p = p->fts_link ? p->fts_link : p->fts_parent;
1169 }
1170}
1171
1172static size_t
1173fts_maxarglen(argv)
1174 char * const *argv;
1175{
1176 size_t len, max;
1177
1178 _DIAGASSERT(argv != NULL);
1179
1180 for (max = 0; *argv; ++argv)
1181 if ((len = strlen(*argv)) > max)
1182 max = len;
1183 return (max + 1);
1184}
1185
1186/*
1187 * Change to dir specified by fd or p->fts_accpath without getting
1188 * tricked by someone changing the world out from underneath us.
1189 * Assumes p->fts_dev and p->fts_ino are filled in.
1190 */
1191static int
1192fts_safe_changedir(sp, p, fd, path)
1193 const FTS *sp;
1194 const FTSENT *p;
1195 int fd;
1196 const char *path;
1197{
1198 int oldfd = fd, ret = -1;
1199 struct STAT sb;
1200
1201 if (ISSET(FTS_NOCHDIR))
1202 return 0;
1203
1204 if (oldfd < 0 && (fd = open(path, O_RDONLY)) == -1)
1205 return -1;
1206
1207 if (fstat(fd, &sb) == -1)
1208 goto bail;
1209
1210 if (sb.st_ino != p->fts_ino || sb.st_dev != p->fts_dev) {
1211 errno = ENOENT;
1212 goto bail;
1213 }
1214
1215 ret = fchdir(fd);
1216
1217bail:
1218 if (oldfd < 0) {
1219 int save_errno = errno;
1220 (void)close(fd);
1221 errno = save_errno;
1222 }
1223 return ret;
1224}
Note: See TracBrowser for help on using the repository browser.