Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:19

0001 /*  $NetBSD: fts.c,v 1.40 2009/11/02 17:17:34 stacktic Exp $    */
0002 
0003 /*-
0004  * Copyright (c) 1990, 1993, 1994
0005  *  The Regents of the University of California.  All rights reserved.
0006  *
0007  * Redistribution and use in source and binary forms, with or without
0008  * modification, are permitted provided that the following conditions
0009  * are met:
0010  * 1. Redistributions of source code must retain the above copyright
0011  *    notice, this list of conditions and the following disclaimer.
0012  * 2. Redistributions in binary form must reproduce the above copyright
0013  *    notice, this list of conditions and the following disclaimer in the
0014  *    documentation and/or other materials provided with the distribution.
0015  * 3. Neither the name of the University nor the names of its contributors
0016  *    may be used to endorse or promote products derived from this software
0017  *    without specific prior written permission.
0018  *
0019  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
0020  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0021  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0022  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
0023  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
0024  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
0025  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
0026  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
0027  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
0028  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0029  * SUCH DAMAGE.
0030  */
0031 
0032 #ifdef HAVE_CONFIG_H
0033 #include "config.h"
0034 #endif
0035 
0036 #if HAVE_NBTOOL_CONFIG_H
0037 #include "nbtool_config.h"
0038 #endif
0039 
0040 #include <sys/cdefs.h>
0041 #if defined(LIBC_SCCS) && !defined(lint)
0042 #if 0
0043 static char sccsid[] = "@(#)fts.c   8.6 (Berkeley) 8/14/94";
0044 #else
0045 __RCSID("$NetBSD: fts.c,v 1.40 2009/11/02 17:17:34 stacktic Exp $");
0046 #endif
0047 #endif /* LIBC_SCCS and not lint */
0048 
0049 #ifndef __rtems__
0050 #include "namespace.h"
0051 #endif
0052 #include <limits.h>
0053 #include <sys/param.h>
0054 #include <sys/stat.h>
0055 
0056 #include <assert.h>
0057 #include <dirent.h>
0058 #include <errno.h>
0059 #include <fcntl.h>
0060 #include "fts.h"
0061 #include <stdlib.h>
0062 #include <stdint.h>
0063 #include <string.h>
0064 #include <unistd.h>
0065 
0066 #define _DIAGASSERT(a)
0067 #undef FTS_WHITEOUT
0068 #define dirfd(dp)  __dirfd(dp)
0069 
0070 #if ! HAVE_NBTOOL_CONFIG_H
0071 #define HAVE_STRUCT_DIRENT_D_NAMLEN
0072 #endif
0073 
0074 static FTSENT   *fts_alloc(FTS *, const char *, size_t);
0075 static FTSENT   *fts_build(FTS *, int);
0076 static void  fts_free(FTSENT *);
0077 static void  fts_lfree(FTSENT *);
0078 static void  fts_load(FTS *, FTSENT *);
0079 static size_t    fts_maxarglen(char * const *);
0080 static size_t    fts_pow2(size_t);
0081 static int   fts_palloc(FTS *, size_t);
0082 static void  fts_padjust(FTS *, FTSENT *);
0083 static FTSENT   *fts_sort(FTS *, FTSENT *, size_t);
0084 static unsigned short fts_stat(FTS *, FTSENT *, int);
0085 static int   fts_safe_changedir(const FTS *, const FTSENT *, int,
0086     const char *);
0087 
0088 #if defined(ALIGNBYTES) && defined(ALIGN)
0089 #define FTS_ALLOC_ALIGNED   1
0090 /* FIXME: Redefine because some versions of 
0091  * RTEMS newlib and the BSDs ship a broken ALIGN */
0092 #undef ALIGN
0093 #define ALIGN(p)    (((uintptr_t)(p) + ALIGNBYTES) & ~ALIGNBYTES)
0094 #else
0095 #undef  FTS_ALLOC_ALIGNED
0096 #endif
0097 
0098 #define ISDOT(a)    (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))
0099 
0100 #define CLR(opt)    (sp->fts_options &= ~(opt))
0101 #define ISSET(opt)  (sp->fts_options & (opt))
0102 #define SET(opt)    (sp->fts_options |= (opt))
0103 
0104 #define CHDIR(sp, path) (!ISSET(FTS_NOCHDIR) && chdir(path))
0105 #define FCHDIR(sp, fd)  (!ISSET(FTS_NOCHDIR) && fchdir(fd))
0106 
0107 /* fts_build flags */
0108 #define BCHILD      1       /* fts_children */
0109 #define BNAMES      2       /* fts_children, names only */
0110 #define BREAD       3       /* fts_read */
0111 
0112 #ifndef DTF_HIDEW
0113 #undef FTS_WHITEOUT
0114 #endif
0115 
0116 FTS *
0117 fts_open(char * const *argv, int options,
0118     int (*compar)(const FTSENT **, const FTSENT **))
0119 {
0120     FTS *sp;
0121     FTSENT *p, *root;
0122     size_t nitems;
0123     FTSENT *parent, *tmp = NULL;    /* pacify gcc */
0124     size_t len;
0125 
0126     _DIAGASSERT(argv != NULL);
0127 
0128     /* Options check. */
0129     if (options & ~FTS_OPTIONMASK) {
0130         errno = EINVAL;
0131         return (NULL);
0132     }
0133 
0134     /* Allocate/initialize the stream */
0135     if ((sp = malloc((unsigned int)sizeof(FTS))) == NULL)
0136         return (NULL);
0137     memset(sp, 0, sizeof(FTS));
0138     sp->fts_compar = compar;
0139     sp->fts_options = options;
0140 
0141     /* Logical walks turn on NOCHDIR; symbolic links are too hard. */
0142     if (ISSET(FTS_LOGICAL))
0143         SET(FTS_NOCHDIR);
0144 
0145     /*
0146      * Start out with 1K of path space, and enough, in any case,
0147      * to hold the user's paths.
0148      */
0149     if (fts_palloc(sp, MAX(fts_maxarglen(argv), MAXPATHLEN)))
0150         goto mem1;
0151 
0152     /* Allocate/initialize root's parent. */
0153     if ((parent = fts_alloc(sp, "", 0)) == NULL)
0154         goto mem2;
0155     parent->fts_level = FTS_ROOTPARENTLEVEL;
0156 
0157     /* Allocate/initialize root(s). */
0158     for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) {
0159         /* Don't allow zero-length paths. */
0160         if ((len = strlen(*argv)) == 0) {
0161             errno = ENOENT;
0162             goto mem3;
0163         }
0164 
0165         if ((p = fts_alloc(sp, *argv, len)) == NULL)
0166             goto mem3;
0167         p->fts_level = FTS_ROOTLEVEL;
0168         p->fts_parent = parent;
0169         p->fts_accpath = p->fts_name;
0170         p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW));
0171 
0172         /* Command-line "." and ".." are real directories. */
0173         if (p->fts_info == FTS_DOT)
0174             p->fts_info = FTS_D;
0175 
0176         /*
0177          * If comparison routine supplied, traverse in sorted
0178          * order; otherwise traverse in the order specified.
0179          */
0180         if (compar) {
0181             p->fts_link = root;
0182             root = p;
0183         } else {
0184             p->fts_link = NULL;
0185             if (root == NULL)
0186                 tmp = root = p;
0187             else {
0188                 tmp->fts_link = p;
0189                 tmp = p;
0190             }
0191         }
0192     }
0193     if (compar && nitems > 1)
0194         root = fts_sort(sp, root, nitems);
0195 
0196     /*
0197      * Allocate a dummy pointer and make fts_read think that we've just
0198      * finished the node before the root(s); set p->fts_info to FTS_INIT
0199      * so that everything about the "current" node is ignored.
0200      */
0201     if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL)
0202         goto mem3;
0203     sp->fts_cur->fts_link = root;
0204     sp->fts_cur->fts_info = FTS_INIT;
0205 
0206     /*
0207      * If using chdir(2), grab a file descriptor pointing to dot to insure
0208      * that we can get back here; this could be avoided for some paths,
0209      * but almost certainly not worth the effort.  Slashes, symbolic links,
0210      * and ".." are all fairly nasty problems.  Note, if we can't get the
0211      * descriptor we run anyway, just more slowly.
0212      */
0213     if (!ISSET(FTS_NOCHDIR)) {
0214         if ((sp->fts_rfd = open(".", O_RDONLY, 0)) == -1)
0215             SET(FTS_NOCHDIR);
0216         else if (fcntl(sp->fts_rfd, F_SETFD, FD_CLOEXEC) == -1) {
0217             close(sp->fts_rfd);
0218             SET(FTS_NOCHDIR);
0219         }
0220     }
0221 
0222     if (nitems == 0)
0223         fts_free(parent);
0224 
0225     return (sp);
0226 
0227 mem3:   fts_lfree(root);
0228     fts_free(parent);
0229 mem2:   free(sp->fts_path);
0230 mem1:   free(sp);
0231     return (NULL);
0232 }
0233 
0234 static void
0235 fts_load(FTS *sp, FTSENT *p)
0236 {
0237     size_t len;
0238     char *cp;
0239 
0240     _DIAGASSERT(sp != NULL);
0241     _DIAGASSERT(p != NULL);
0242 
0243     /*
0244      * Load the stream structure for the next traversal.  Since we don't
0245      * actually enter the directory until after the preorder visit, set
0246      * the fts_accpath field specially so the chdir gets done to the right
0247      * place and the user can access the first node.  From fts_open it's
0248      * known that the path will fit.
0249      */
0250     len = p->fts_pathlen = p->fts_namelen;
0251     memmove(sp->fts_path, p->fts_name, len + 1);
0252     if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) {
0253         len = strlen(++cp);
0254         memmove(p->fts_name, cp, len + 1);
0255         p->fts_namelen = len;
0256     }
0257     p->fts_accpath = p->fts_path = sp->fts_path;
0258     sp->fts_dev = p->fts_dev;
0259 }
0260 
0261 int
0262 fts_close(FTS *sp)
0263 {
0264     FTSENT *freep, *p;
0265     int saved_errno = 0;
0266 
0267     _DIAGASSERT(sp != NULL);
0268 
0269     /*
0270      * This still works if we haven't read anything -- the dummy structure
0271      * points to the root list, so we step through to the end of the root
0272      * list which has a valid parent pointer.
0273      */
0274     if (sp->fts_cur) {
0275         if (sp->fts_cur->fts_flags & FTS_SYMFOLLOW)
0276             (void)close(sp->fts_cur->fts_symfd);
0277         for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {
0278             freep = p;
0279             p = p->fts_link ? p->fts_link : p->fts_parent;
0280             fts_free(freep);
0281         }
0282         fts_free(p);
0283     }
0284 
0285     /* Free up child linked list, sort array, path buffer. */
0286     if (sp->fts_child)
0287         fts_lfree(sp->fts_child);
0288     if (sp->fts_array)
0289         free(sp->fts_array);
0290     free(sp->fts_path);
0291 
0292     /* Return to original directory, save errno if necessary. */
0293     if (!ISSET(FTS_NOCHDIR)) {
0294         if (fchdir(sp->fts_rfd) == -1)
0295             saved_errno = errno;
0296         (void)close(sp->fts_rfd);
0297     }
0298 
0299     /* Free up the stream pointer. */
0300     free(sp);
0301     if (saved_errno) {
0302         errno = saved_errno;
0303         return -1;
0304     }
0305 
0306     return 0;
0307 }
0308 
0309 #if !defined(__FTS_COMPAT_TAILINGSLASH)
0310 
0311 /*
0312  * Special case of "/" at the end of the path so that slashes aren't
0313  * appended which would cause paths to be written as "....//foo".
0314  */
0315 #define NAPPEND(p)                          \
0316     (p->fts_path[p->fts_pathlen - 1] == '/'             \
0317         ? p->fts_pathlen - 1 : p->fts_pathlen)
0318 
0319 #else /* !defined(__FTS_COMPAT_TAILINGSLASH) */
0320 
0321 /*
0322  * compatibility with the old behaviour.
0323  *
0324  * Special case a root of "/" so that slashes aren't appended which would
0325  * cause paths to be written as "//foo".
0326  */
0327 
0328 #define NAPPEND(p)                          \
0329     (p->fts_level == FTS_ROOTLEVEL && p->fts_pathlen == 1 &&    \
0330         p->fts_path[0] == '/' ? 0 : p->fts_pathlen)
0331 
0332 #endif /* !defined(__FTS_COMPAT_TAILINGSLASH) */
0333 
0334 FTSENT *
0335 fts_read(FTS *sp)
0336 {
0337     FTSENT *p, *tmp;
0338     int instr;
0339     char *t;
0340     int saved_errno;
0341 
0342     _DIAGASSERT(sp != NULL);
0343 
0344     /* If finished or unrecoverable error, return NULL. */
0345     if (sp->fts_cur == NULL || ISSET(FTS_STOP))
0346         return (NULL);
0347 
0348     /* Set current node pointer. */
0349     p = sp->fts_cur;
0350 
0351     /* Save and zero out user instructions. */
0352     instr = p->fts_instr;
0353     p->fts_instr = FTS_NOINSTR;
0354 
0355     /* Any type of file may be re-visited; re-stat and re-turn. */
0356     if (instr == FTS_AGAIN) {
0357         p->fts_info = fts_stat(sp, p, 0);
0358         return (p);
0359     }
0360 
0361     /*
0362      * Following a symlink -- SLNONE test allows application to see
0363      * SLNONE and recover.  If indirecting through a symlink, have
0364      * keep a pointer to current location.  If unable to get that
0365      * pointer, follow fails.
0366      */
0367     if (instr == FTS_FOLLOW &&
0368         (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {
0369         p->fts_info = fts_stat(sp, p, 1);
0370         if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
0371             if ((p->fts_symfd = open(".", O_RDONLY, 0)) == -1) {
0372                 p->fts_errno = errno;
0373                 p->fts_info = FTS_ERR;
0374             } else if (fcntl(p->fts_symfd, F_SETFD, FD_CLOEXEC) == -1) {
0375                 p->fts_errno = errno;
0376                 p->fts_info = FTS_ERR;
0377                 close(p->fts_symfd);
0378             } else
0379                 p->fts_flags |= FTS_SYMFOLLOW;
0380         }
0381         return (p);
0382     }
0383 
0384     /* Directory in pre-order. */
0385     if (p->fts_info == FTS_D) {
0386         /* If skipped or crossed mount point, do post-order visit. */
0387         if (instr == FTS_SKIP ||
0388             (ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev)) {
0389             if (p->fts_flags & FTS_SYMFOLLOW)
0390                 (void)close(p->fts_symfd);
0391             if (sp->fts_child) {
0392                 fts_lfree(sp->fts_child);
0393                 sp->fts_child = NULL;
0394             }
0395             p->fts_info = FTS_DP;
0396             return (p);
0397         }
0398 
0399         /* Rebuild if only read the names and now traversing. */
0400         if (sp->fts_child && ISSET(FTS_NAMEONLY)) {
0401             CLR(FTS_NAMEONLY);
0402             fts_lfree(sp->fts_child);
0403             sp->fts_child = NULL;
0404         }
0405 
0406         /*
0407          * Cd to the subdirectory.
0408          *
0409          * If have already read and now fail to chdir, whack the list
0410          * to make the names come out right, and set the parent errno
0411          * so the application will eventually get an error condition.
0412          * Set the FTS_DONTCHDIR flag so that when we logically change
0413          * directories back to the parent we don't do a chdir.
0414          *
0415          * If haven't read do so.  If the read fails, fts_build sets
0416          * FTS_STOP or the fts_info field of the node.
0417          */
0418         if (sp->fts_child) {
0419             if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) {
0420                 p->fts_errno = errno;
0421                 p->fts_flags |= FTS_DONTCHDIR;
0422                 for (p = sp->fts_child; p; p = p->fts_link)
0423                     p->fts_accpath =
0424                         p->fts_parent->fts_accpath;
0425             }
0426         } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) {
0427             if (ISSET(FTS_STOP))
0428                 return (NULL);
0429             return (p);
0430         }
0431         p = sp->fts_child;
0432         sp->fts_child = NULL;
0433         goto name;
0434     }
0435 
0436     /* Move to the next node on this level. */
0437 next:   tmp = p;
0438     if ((p = p->fts_link) != NULL) {
0439         fts_free(tmp);
0440 
0441         /*
0442          * If reached the top, return to the original directory, and
0443          * load the paths for the next root.
0444          */
0445         if (p->fts_level == FTS_ROOTLEVEL) {
0446             if (FCHDIR(sp, sp->fts_rfd)) {
0447                 SET(FTS_STOP);
0448                 return (NULL);
0449             }
0450             fts_load(sp, p);
0451             return (sp->fts_cur = p);
0452         }
0453 
0454         /*
0455          * User may have called fts_set on the node.  If skipped,
0456          * ignore.  If followed, get a file descriptor so we can
0457          * get back if necessary.
0458          */
0459         if (p->fts_instr == FTS_SKIP)
0460             goto next;
0461         if (p->fts_instr == FTS_FOLLOW) {
0462             p->fts_info = fts_stat(sp, p, 1);
0463             if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
0464                 if ((p->fts_symfd =
0465                     open(".", O_RDONLY, 0)) == -1) {
0466                     p->fts_errno = errno;
0467                     p->fts_info = FTS_ERR;
0468                 } else if (fcntl(p->fts_symfd, F_SETFD, FD_CLOEXEC) == -1) {
0469                     p->fts_errno = errno;
0470                     p->fts_info = FTS_ERR;
0471                     close(p->fts_symfd);
0472                 } else
0473                     p->fts_flags |= FTS_SYMFOLLOW;
0474             }
0475             p->fts_instr = FTS_NOINSTR;
0476         }
0477 
0478 name:       t = sp->fts_path + NAPPEND(p->fts_parent);
0479         *t++ = '/';
0480         memmove(t, p->fts_name, (size_t)(p->fts_namelen + 1));
0481         return (sp->fts_cur = p);
0482     }
0483 
0484     /* Move up to the parent node. */
0485     p = tmp->fts_parent;
0486     fts_free(tmp);
0487 
0488     if (p->fts_level == FTS_ROOTPARENTLEVEL) {
0489         /*
0490          * Done; free everything up and set errno to 0 so the user
0491          * can distinguish between error and EOF.
0492          */
0493         fts_free(p);
0494         errno = 0;
0495         return (sp->fts_cur = NULL);
0496     }
0497 
0498     /* Nul terminate the pathname. */
0499     sp->fts_path[p->fts_pathlen] = '\0';
0500 
0501     /*
0502      * Return to the parent directory.  If at a root node or came through
0503      * a symlink, go back through the file descriptor.  Otherwise, cd up
0504      * one directory.
0505      */
0506     if (p->fts_level == FTS_ROOTLEVEL) {
0507         if (FCHDIR(sp, sp->fts_rfd)) {
0508             SET(FTS_STOP);
0509             return (NULL);
0510         }
0511     } else if (p->fts_flags & FTS_SYMFOLLOW) {
0512         if (FCHDIR(sp, p->fts_symfd)) {
0513             saved_errno = errno;
0514             (void)close(p->fts_symfd);
0515             errno = saved_errno;
0516             SET(FTS_STOP);
0517             return (NULL);
0518         }
0519         (void)close(p->fts_symfd);
0520     } else if (!(p->fts_flags & FTS_DONTCHDIR) &&
0521         fts_safe_changedir(sp, p->fts_parent, -1, "..")) {
0522         SET(FTS_STOP);
0523         return (NULL);
0524     }
0525     p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;
0526     return (sp->fts_cur = p);
0527 }
0528 
0529 /*
0530  * Fts_set takes the stream as an argument although it's not used in this
0531  * implementation; it would be necessary if anyone wanted to add global
0532  * semantics to fts using fts_set.  An error return is allowed for similar
0533  * reasons.
0534  */
0535 /* ARGSUSED */
0536 int
0537 fts_set(FTS *sp, FTSENT *p, int instr)
0538 {
0539 
0540     _DIAGASSERT(sp != NULL);
0541     _DIAGASSERT(p != NULL);
0542 
0543     if (instr && instr != FTS_AGAIN && instr != FTS_FOLLOW &&
0544         instr != FTS_NOINSTR && instr != FTS_SKIP) {
0545         errno = EINVAL;
0546         return (1);
0547     }
0548     p->fts_instr = instr;
0549     return (0);
0550 }
0551 
0552 FTSENT *
0553 fts_children(FTS *sp, int instr)
0554 {
0555     FTSENT *p;
0556     int fd;
0557 
0558     _DIAGASSERT(sp != NULL);
0559 
0560     if (instr && instr != FTS_NAMEONLY) {
0561         errno = EINVAL;
0562         return (NULL);
0563     }
0564 
0565     /* Set current node pointer. */
0566     p = sp->fts_cur;
0567 
0568     /*
0569      * Errno set to 0 so user can distinguish empty directory from
0570      * an error.
0571      */
0572     errno = 0;
0573 
0574     /* Fatal errors stop here. */
0575     if (ISSET(FTS_STOP))
0576         return (NULL);
0577 
0578     /* Return logical hierarchy of user's arguments. */
0579     if (p->fts_info == FTS_INIT)
0580         return (p->fts_link);
0581 
0582     /*
0583      * If not a directory being visited in pre-order, stop here.  Could
0584      * allow FTS_DNR, assuming the user has fixed the problem, but the
0585      * same effect is available with FTS_AGAIN.
0586      */
0587     if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */)
0588         return (NULL);
0589 
0590     /* Free up any previous child list. */
0591     if (sp->fts_child)
0592         fts_lfree(sp->fts_child);
0593 
0594     if (instr == FTS_NAMEONLY) {
0595         SET(FTS_NAMEONLY);
0596         instr = BNAMES;
0597     } else
0598         instr = BCHILD;
0599 
0600     /*
0601      * If using chdir on a relative path and called BEFORE fts_read does
0602      * its chdir to the root of a traversal, we can lose -- we need to
0603      * chdir into the subdirectory, and we don't know where the current
0604      * directory is, so we can't get back so that the upcoming chdir by
0605      * fts_read will work.
0606      */
0607     if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' ||
0608         ISSET(FTS_NOCHDIR))
0609         return (sp->fts_child = fts_build(sp, instr));
0610 
0611     if ((fd = open(".", O_RDONLY, 0)) == -1)
0612         return (sp->fts_child = NULL);
0613     sp->fts_child = fts_build(sp, instr);
0614     if (fchdir(fd)) {
0615         (void)close(fd);
0616         return (NULL);
0617     }
0618     (void)close(fd);
0619     return (sp->fts_child);
0620 }
0621 
0622 /*
0623  * This is the tricky part -- do not casually change *anything* in here.  The
0624  * idea is to build the linked list of entries that are used by fts_children
0625  * and fts_read.  There are lots of special cases.
0626  *
0627  * The real slowdown in walking the tree is the stat calls.  If FTS_NOSTAT is
0628  * set and it's a physical walk (so that symbolic links can't be directories),
0629  * we can do things quickly.  First, if it's a 4.4BSD file system, the type
0630  * of the file is in the directory entry.  Otherwise, we assume that the number
0631  * of subdirectories in a node is equal to the number of links to the parent.
0632  * The former skips all stat calls.  The latter skips stat calls in any leaf
0633  * directories and for any files after the subdirectories in the directory have
0634  * been found, cutting the stat calls by about 2/3.
0635  */
0636 static FTSENT *
0637 fts_build(FTS *sp, int type)
0638 {
0639     struct dirent *dp;
0640     FTSENT *p, *head;
0641     size_t nitems;
0642     FTSENT *cur, *tail;
0643     DIR *dirp;
0644     void *oldaddr;
0645     size_t dnamlen;
0646     int cderrno, descend, level, nlinks, saved_errno;
0647 #ifdef DT_DIR
0648     int nostat;
0649 #endif
0650     int doadjust;
0651     size_t len, maxlen;
0652 #ifdef FTS_WHITEOUT
0653     int oflag;
0654 #endif
0655     char *cp = NULL;    /* pacify gcc */
0656 
0657     _DIAGASSERT(sp != NULL);
0658 
0659     /* Set current node pointer. */
0660     cur = sp->fts_cur;
0661 
0662     /*
0663      * Open the directory for reading.  If this fails, we're done.
0664      * If being called from fts_read, set the fts_info field.
0665      */
0666 #ifdef FTS_WHITEOUT
0667     if (ISSET(FTS_WHITEOUT))
0668         oflag = DTF_NODUP|DTF_REWIND;
0669     else
0670         oflag = DTF_HIDEW|DTF_NODUP|DTF_REWIND;
0671 #else
0672 #define __opendir2(path, flag) opendir(path)
0673 #endif
0674     if ((dirp = __opendir2(cur->fts_accpath, oflag)) == NULL) {
0675         if (type == BREAD) {
0676             cur->fts_info = FTS_DNR;
0677             cur->fts_errno = errno;
0678         }
0679         return (NULL);
0680     }
0681 
0682     /*
0683      * Nlinks is the number of possible entries of type directory in the
0684      * directory if we're cheating on stat calls, 0 if we're not doing
0685      * any stat calls at all, -1 if we're doing stats on everything.
0686      */
0687     if (type == BNAMES) {
0688         nlinks = 0;
0689 #ifdef DT_DIR
0690         nostat = 1;
0691 #endif
0692     } else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) {
0693         nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2);
0694 #ifdef DT_DIR
0695         nostat = 1;
0696 #endif
0697     } else {
0698         nlinks = -1;
0699 #ifdef DT_DIR
0700         nostat = 0;
0701 #endif
0702     }
0703 
0704 #ifdef notdef
0705     (void)printf("nlinks == %d (cur: %d)\n", nlinks, cur->fts_nlink);
0706     (void)printf("NOSTAT %d PHYSICAL %d SEEDOT %d\n",
0707         ISSET(FTS_NOSTAT), ISSET(FTS_PHYSICAL), ISSET(FTS_SEEDOT));
0708 #endif
0709     /*
0710      * If we're going to need to stat anything or we want to descend
0711      * and stay in the directory, chdir.  If this fails we keep going,
0712      * but set a flag so we don't chdir after the post-order visit.
0713      * We won't be able to stat anything, but we can still return the
0714      * names themselves.  Note, that since fts_read won't be able to
0715      * chdir into the directory, it will have to return different path
0716      * names than before, i.e. "a/b" instead of "b".  Since the node
0717      * has already been visited in pre-order, have to wait until the
0718      * post-order visit to return the error.  There is a special case
0719      * here, if there was nothing to stat then it's not an error to
0720      * not be able to stat.  This is all fairly nasty.  If a program
0721      * needed sorted entries or stat information, they had better be
0722      * checking FTS_NS on the returned nodes.
0723      */
0724     cderrno = 0;
0725     if (nlinks || type == BREAD) {
0726         if (fts_safe_changedir(sp, cur, dirfd(dirp), NULL)) {
0727             if (nlinks && type == BREAD)
0728                 cur->fts_errno = errno;
0729             cur->fts_flags |= FTS_DONTCHDIR;
0730             descend = 0;
0731             cderrno = errno;
0732         } else
0733             descend = 1;
0734     } else
0735         descend = 0;
0736 
0737     /*
0738      * Figure out the max file name length that can be stored in the
0739      * current path -- the inner loop allocates more path as necessary.
0740      * We really wouldn't have to do the maxlen calculations here, we
0741      * could do them in fts_read before returning the path, but it's a
0742      * lot easier here since the length is part of the dirent structure.
0743      *
0744      * If not changing directories set a pointer so that can just append
0745      * each new name into the path.
0746      */
0747     len = NAPPEND(cur);
0748     if (ISSET(FTS_NOCHDIR)) {
0749         cp = sp->fts_path + len;
0750         *cp++ = '/';
0751     }
0752     len++;
0753     maxlen = sp->fts_pathlen - len;
0754 
0755 #if defined(__FTS_COMPAT_LEVEL)
0756     if (cur->fts_level == SHRT_MAX) {
0757         (void)closedir(dirp);
0758         cur->fts_info = FTS_ERR;
0759         SET(FTS_STOP);
0760         errno = ENAMETOOLONG;
0761         return (NULL);
0762     }
0763 #endif
0764 
0765     level = cur->fts_level + 1;
0766 
0767     /* Read the directory, attaching each entry to the `link' pointer. */
0768     doadjust = 0;
0769     for (head = tail = NULL, nitems = 0; (dp = readdir(dirp)) != NULL;) {
0770 
0771         if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))
0772             continue;
0773 
0774 #if defined(HAVE_STRUCT_DIRENT_D_NAMLEN)
0775         dnamlen = dp->d_namlen;
0776 #else
0777         dnamlen = strlen(dp->d_name);
0778 #endif
0779         if ((p = fts_alloc(sp, dp->d_name, dnamlen)) == NULL)
0780             goto mem1;
0781         if (dnamlen >= maxlen) {    /* include space for NUL */
0782             oldaddr = sp->fts_path;
0783             if (fts_palloc(sp, dnamlen + len + 1)) {
0784                 /*
0785                  * No more memory for path or structures.  Save
0786                  * errno, free up the current structure and the
0787                  * structures already allocated.
0788                  */
0789 mem1:               saved_errno = errno;
0790                 if (p)
0791                     fts_free(p);
0792                 fts_lfree(head);
0793                 (void)closedir(dirp);
0794                 errno = saved_errno;
0795                 cur->fts_info = FTS_ERR;
0796                 SET(FTS_STOP);
0797                 return (NULL);
0798             }
0799             /* Did realloc() change the pointer? */
0800             if (oldaddr != sp->fts_path) {
0801                 doadjust = 1;
0802                 if (ISSET(FTS_NOCHDIR))
0803                     cp = sp->fts_path + len;
0804             }
0805             maxlen = sp->fts_pathlen - len;
0806         }
0807 
0808 #if defined(__FTS_COMPAT_LENGTH)
0809         if (len + dnamlen >= USHRT_MAX) {
0810             /*
0811              * In an FTSENT, fts_pathlen is an unsigned short
0812              * so it is possible to wraparound here.
0813              * If we do, free up the current structure and the
0814              * structures already allocated, then error out
0815              * with ENAMETOOLONG.
0816              */
0817             fts_free(p);
0818             fts_lfree(head);
0819             (void)closedir(dirp);
0820             cur->fts_info = FTS_ERR;
0821             SET(FTS_STOP);
0822             errno = ENAMETOOLONG;
0823             return (NULL);
0824         }
0825 #endif
0826         p->fts_level = level;
0827         p->fts_pathlen = len + dnamlen;
0828         p->fts_parent = sp->fts_cur;
0829 
0830 #ifdef FTS_WHITEOUT
0831         if (dp->d_type == DT_WHT)
0832             p->fts_flags |= FTS_ISW;
0833 #endif
0834 
0835         if (cderrno) {
0836             if (nlinks) {
0837                 p->fts_info = FTS_NS;
0838                 p->fts_errno = cderrno;
0839             } /* else
0840                 p->fts_info = FTS_NSOK;
0841                 */
0842                 /* Coverity Scan Id 1 says above is dead code */
0843             p->fts_accpath = cur->fts_accpath;
0844         } else if (nlinks == 0
0845 #ifdef DT_DIR
0846             || (nostat &&
0847             dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN)
0848 #endif
0849             ) {
0850             p->fts_accpath =
0851                 ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name;
0852             p->fts_info = FTS_NSOK;
0853         } else {
0854             /* Build a file name for fts_stat to stat. */
0855             if (ISSET(FTS_NOCHDIR)) {
0856                 p->fts_accpath = p->fts_path;
0857                 memmove(cp, p->fts_name,
0858                         (size_t)(p->fts_namelen + 1));
0859             } else
0860                 p->fts_accpath = p->fts_name;
0861             /* Stat it. */
0862             p->fts_info = fts_stat(sp, p, 0);
0863 
0864             /* Decrement link count if applicable. */
0865             if (nlinks > 0 && (p->fts_info == FTS_D ||
0866                 p->fts_info == FTS_DC || p->fts_info == FTS_DOT))
0867                 --nlinks;
0868         }
0869 
0870         /* We walk in directory order so "ls -f" doesn't get upset. */
0871         p->fts_link = NULL;
0872         if (head == NULL)
0873             head = tail = p;
0874         else {
0875             tail->fts_link = p;
0876             tail = p;
0877         }
0878         ++nitems;
0879     }
0880     (void)closedir(dirp);
0881 
0882     /*
0883      * If had to realloc the path, adjust the addresses for the rest
0884      * of the tree.
0885      */
0886     if (doadjust)
0887         fts_padjust(sp, head);
0888 
0889     /*
0890      * If not changing directories, reset the path back to original
0891      * state.
0892      */
0893     if (ISSET(FTS_NOCHDIR)) {
0894         if (len == sp->fts_pathlen || nitems == 0)
0895             --cp;
0896         *cp = '\0';
0897     }
0898 
0899     /*
0900      * If descended after called from fts_children or after called from
0901      * fts_read and nothing found, get back.  At the root level we use
0902      * the saved fd; if one of fts_open()'s arguments is a relative path
0903      * to an empty directory, we wind up here with no other way back.  If
0904      * can't get back, we're done.
0905      */
0906     if (descend && (type == BCHILD || !nitems) &&
0907         (cur->fts_level == FTS_ROOTLEVEL ?
0908         FCHDIR(sp, sp->fts_rfd) :
0909         fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) {
0910         cur->fts_info = FTS_ERR;
0911         SET(FTS_STOP);
0912         return (NULL);
0913     }
0914 
0915     /* If didn't find anything, return NULL. */
0916     if (!nitems) {
0917         if (type == BREAD)
0918             cur->fts_info = FTS_DP;
0919         return (NULL);
0920     }
0921 
0922     /* Sort the entries. */
0923     if (sp->fts_compar && nitems > 1)
0924         head = fts_sort(sp, head, nitems);
0925     return (head);
0926 }
0927 
0928 static unsigned short
0929 fts_stat(FTS *sp, FTSENT *p, int follow)
0930 {
0931     FTSENT *t;
0932     dev_t dev;
0933     __fts_ino_t ino;
0934     __fts_stat_t *sbp, sb;
0935     int saved_errno;
0936 
0937     _DIAGASSERT(sp != NULL);
0938     _DIAGASSERT(p != NULL);
0939 
0940     /* If user needs stat info, stat buffer already allocated. */
0941     sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp;
0942 
0943 #ifdef FTS_WHITEOUT
0944     /* check for whiteout */
0945     if (p->fts_flags & FTS_ISW) {
0946         if (sbp != &sb) {
0947             memset(sbp, '\0', sizeof (*sbp));
0948             sbp->st_mode = S_IFWHT;
0949         }
0950         return (FTS_W);
0951     }
0952 #endif
0953 
0954     /*
0955      * If doing a logical walk, or application requested FTS_FOLLOW, do
0956      * a stat(2).  If that fails, check for a non-existent symlink.  If
0957      * fail, set the errno from the stat call.
0958      */
0959     if (ISSET(FTS_LOGICAL) || follow) {
0960         if (stat(p->fts_accpath, sbp)) {
0961             saved_errno = errno;
0962             if (!lstat(p->fts_accpath, sbp)) {
0963                 errno = 0;
0964                 return (FTS_SLNONE);
0965             }
0966             p->fts_errno = saved_errno;
0967             goto err;
0968         }
0969     } else if (lstat(p->fts_accpath, sbp)) {
0970         p->fts_errno = errno;
0971 err:        memset(sbp, 0, sizeof(*sbp));
0972         return (FTS_NS);
0973     }
0974 
0975     if (S_ISDIR(sbp->st_mode)) {
0976         /*
0977          * Set the device/inode.  Used to find cycles and check for
0978          * crossing mount points.  Also remember the link count, used
0979          * in fts_build to limit the number of stat calls.  It is
0980          * understood that these fields are only referenced if fts_info
0981          * is set to FTS_D.
0982          */
0983         dev = p->fts_dev = sbp->st_dev;
0984         ino = p->fts_ino = sbp->st_ino;
0985         p->fts_nlink = sbp->st_nlink;
0986 
0987         if (ISDOT(p->fts_name))
0988             return (FTS_DOT);
0989 
0990         /*
0991          * Cycle detection is done by brute force when the directory
0992          * is first encountered.  If the tree gets deep enough or the
0993          * number of symbolic links to directories is high enough,
0994          * something faster might be worthwhile.
0995          */
0996         for (t = p->fts_parent;
0997             t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent)
0998             if (ino == t->fts_ino && dev == t->fts_dev) {
0999                 p->fts_cycle = t;
1000                 return (FTS_DC);
1001             }
1002         return (FTS_D);
1003     }
1004     if (S_ISLNK(sbp->st_mode))
1005         return (FTS_SL);
1006     if (S_ISREG(sbp->st_mode))
1007         return (FTS_F);
1008     return (FTS_DEFAULT);
1009 }
1010 
1011 static FTSENT *
1012 fts_sort(FTS *sp, FTSENT *head, size_t nitems)
1013 {
1014     FTSENT **ap, *p;
1015 
1016     _DIAGASSERT(sp != NULL);
1017     _DIAGASSERT(head != NULL);
1018 
1019     /*
1020      * Construct an array of pointers to the structures and call qsort(3).
1021      * Reassemble the array in the order returned by qsort.  If unable to
1022      * sort for memory reasons, return the directory entries in their
1023      * current order.  Allocate enough space for the current needs plus
1024      * 40 so don't realloc one entry at a time.
1025      */
1026     if (nitems > sp->fts_nitems) {
1027         FTSENT **new;
1028 
1029         new = realloc(sp->fts_array, sizeof(FTSENT *) * (nitems + 40));
1030         if (new == 0)
1031             return (head);
1032         sp->fts_array = new;
1033         sp->fts_nitems = nitems + 40;
1034     }
1035     for (ap = sp->fts_array, p = head; p; p = p->fts_link)
1036         *ap++ = p;
1037     qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *),
1038         (int (*)(const void *, const void *))sp->fts_compar);
1039     for (head = *(ap = sp->fts_array); --nitems; ++ap)
1040         ap[0]->fts_link = ap[1];
1041     ap[0]->fts_link = NULL;
1042     return (head);
1043 }
1044 
1045 static FTSENT *
1046 fts_alloc(FTS *sp, const char *name, size_t namelen)
1047 {
1048     FTSENT *p;
1049 #if defined(FTS_ALLOC_ALIGNED)
1050     size_t len;
1051 #endif
1052 
1053     _DIAGASSERT(sp != NULL);
1054     _DIAGASSERT(name != NULL);
1055 
1056 #if defined(FTS_ALLOC_ALIGNED)
1057     /*
1058      * The file name is a variable length array and no stat structure is
1059      * necessary if the user has set the nostat bit.  Allocate the FTSENT
1060      * structure, the file name and the stat structure in one chunk, but
1061      * be careful that the stat structure is reasonably aligned.  Since the
1062      * fts_name field is declared to be of size 1, the fts_name pointer is
1063      * namelen + 2 before the first possible address of the stat structure.
1064      */
1065     len = sizeof(FTSENT) + namelen;
1066     if (!ISSET(FTS_NOSTAT))
1067         len += sizeof(*(p->fts_statp)) + ALIGNBYTES;
1068     if ((p = malloc(len)) == NULL)
1069         return (NULL);
1070 
1071     if (!ISSET(FTS_NOSTAT))
1072         p->fts_statp = (__fts_stat_t *)ALIGN(p->fts_name + namelen + 2);
1073 #else
1074     if ((p = malloc(sizeof(FTSENT) + namelen)) == NULL)
1075         return (NULL);
1076 
1077     if (!ISSET(FTS_NOSTAT))
1078         if ((p->fts_statp = malloc(sizeof(*(p->fts_statp)))) == NULL) {
1079             free(p);
1080             return (NULL);
1081         }
1082 #endif
1083 
1084         if (ISSET(FTS_NOSTAT))
1085                 p->fts_statp = NULL;
1086 
1087     /* Copy the name plus the trailing NULL. */
1088     memmove(p->fts_name, name, namelen + 1);
1089 
1090     p->fts_namelen = namelen;
1091     p->fts_path = sp->fts_path;
1092     p->fts_errno = 0;
1093     p->fts_flags = 0;
1094     p->fts_instr = FTS_NOINSTR;
1095     p->fts_number = 0;
1096     p->fts_pointer = NULL;
1097     return (p);
1098 }
1099 
1100 static void
1101 fts_free(FTSENT *p)
1102 {
1103 #if !defined(FTS_ALLOC_ALIGNED)
1104     if (p->fts_statp)
1105         free(p->fts_statp);
1106 #endif
1107     free(p);
1108 }
1109 
1110 static void
1111 fts_lfree(FTSENT *head)
1112 {
1113     FTSENT *p;
1114 
1115     /* XXX: head may be NULL ? */
1116 
1117     /* Free a linked list of structures. */
1118     while ((p = head) != NULL) {
1119         head = head->fts_link;
1120         fts_free(p);
1121     }
1122 }
1123 
1124 static size_t
1125 fts_pow2(size_t x)
1126 {
1127 
1128     x--;
1129     x |= x>>1;
1130     x |= x>>2;
1131     x |= x>>4;
1132     x |= x>>8;
1133 #if (SIZEOF_SIZE_T * CHAR_BIT) > 16
1134     x |= x>>16;
1135 #endif
1136 #if (SIZEOF_SIZE_T * CHAR_BIT) > 32
1137     x |= x>>32;
1138 #endif
1139 #if (SIZEOF_SIZE_T * CHAR_BIT) > 64
1140     x |= x>>64;
1141 #endif
1142     x++;
1143     return (x);
1144 }
1145 
1146 /*
1147  * Allow essentially unlimited paths; find, rm, ls should all work on any tree.
1148  * Most systems will allow creation of paths much longer than MAXPATHLEN, even
1149  * though the kernel won't resolve them.  Round up the new size to a power of 2,
1150  * so we don't realloc the path 2 bytes at a time.
1151  */
1152 static int
1153 fts_palloc(FTS *sp, size_t size)
1154 {
1155     char *new;
1156 
1157     _DIAGASSERT(sp != NULL);
1158 
1159 #ifdef __FTS_COMPAT_LENGTH
1160     /* Protect against fts_pathlen overflow. */
1161     if (size > USHRT_MAX + 1) {
1162         errno = ENAMETOOLONG;
1163         return (1);
1164     }
1165 #endif
1166     size = fts_pow2(size);
1167     new = realloc(sp->fts_path, size);
1168     if (new == 0)
1169         return (1);
1170     sp->fts_path = new;
1171     sp->fts_pathlen = size;
1172     return (0);
1173 }
1174 
1175 /*
1176  * When the path is realloc'd, have to fix all of the pointers in structures
1177  * already returned.
1178  */
1179 static void
1180 fts_padjust(FTS *sp, FTSENT *head)
1181 {
1182     FTSENT *p;
1183     char *addr;
1184 
1185     _DIAGASSERT(sp != NULL);
1186 
1187 #define ADJUST(p) do {                          \
1188     if ((p)->fts_accpath != (p)->fts_name)              \
1189         (p)->fts_accpath =                  \
1190             addr + ((p)->fts_accpath - (p)->fts_path);      \
1191     (p)->fts_path = addr;                       \
1192 } while (/*CONSTCOND*/0)
1193 
1194     addr = sp->fts_path;
1195 
1196     /* Adjust the current set of children. */
1197     for (p = sp->fts_child; p; p = p->fts_link)
1198         ADJUST(p);
1199 
1200     /* Adjust the rest of the tree, including the current level. */
1201     for (p = head; p->fts_level >= FTS_ROOTLEVEL;) {
1202         ADJUST(p);
1203         p = p->fts_link ? p->fts_link : p->fts_parent;
1204     }
1205 }
1206 
1207 static size_t
1208 fts_maxarglen(char * const *argv)
1209 {
1210     size_t len, max;
1211 
1212     _DIAGASSERT(argv != NULL);
1213 
1214     for (max = 0; *argv; ++argv)
1215         if ((len = strlen(*argv)) > max)
1216             max = len;
1217     return (max + 1);
1218 }
1219 
1220 /*
1221  * Change to dir specified by fd or p->fts_accpath without getting
1222  * tricked by someone changing the world out from underneath us.
1223  * Assumes p->fts_dev and p->fts_ino are filled in.
1224  */
1225 static int
1226 fts_safe_changedir(const FTS *sp, const FTSENT *p, int fd, const char *path)
1227 {
1228     int oldfd = fd, ret = -1;
1229     __fts_stat_t sb;
1230 
1231     if (ISSET(FTS_NOCHDIR))
1232         return 0;
1233 
1234     if (oldfd < 0 && (fd = open(path, O_RDONLY)) == -1)
1235         return -1;
1236 
1237     if (fstat(fd, &sb) == -1)
1238         goto bail;
1239 
1240     if (sb.st_ino != p->fts_ino || sb.st_dev != p->fts_dev) {
1241         errno = ENOENT;
1242         goto bail;
1243     }
1244 
1245     ret = fchdir(fd);
1246 
1247 bail:
1248     if (oldfd < 0) {
1249         int save_errno = errno;
1250         (void)close(fd);
1251         errno = save_errno;
1252     }
1253     return ret;
1254 }