source: rtems/cpukit/libmisc/shell/fts.c @ 1f868bd

4.104.115
Last change on this file since 1f868bd was 1f868bd, checked in by Ralf Corsepius <ralf.corsepius@…>, on 05/29/10 at 05:17:11

2010-05-29 Ralf Corsépius <ralf.corsepius@…>

PR 1531/newlib:

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