rpm  5.4.4
rpmio/fts.c
Go to the documentation of this file.
00001 /*@-dependenttrans -nullpass -retalias -usereleased @*/
00002 /*-
00003  * Copyright (c) 1990, 1993, 1994
00004  *      The Regents of the University of California.  All rights reserved.
00005  *
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions
00008  * are met:
00009  * 1. Redistributions of source code must retain the above copyright
00010  *    notice, this list of conditions and the following disclaimer.
00011  * 2. Redistributions in binary form must reproduce the above copyright
00012  *    notice, this list of conditions and the following disclaimer in the
00013  *    documentation and/or other materials provided with the distribution.
00014  * 4. Neither the name of the University nor the names of its contributors
00015  *    may be used to endorse or promote products derived from this software
00016  *    without specific prior written permission.
00017  *
00018  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
00019  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00020  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00021  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
00022  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00023  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00024  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00025  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00026  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00027  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00028  * SUCH DAMAGE.
00029  */
00030 
00031 #include "system.h"
00032 
00033 #if defined(LIBC_SCCS) && !defined(lint)
00034 static char sccsid[] = "@(#)fts.c       8.6 (Berkeley) 8/14/94";
00035 #endif /* LIBC_SCCS and not lint */
00036 
00037 #if defined(_LIBC)
00038 #include <sys/param.h>
00039 #include <include/sys/stat.h>
00040 #include <fcntl.h>
00041 #include <dirent.h>
00042 #include <errno.h>
00043 #include <fts.h>
00044 #include <stdlib.h>
00045 #include <string.h>
00046 #include <unistd.h>
00047 #else
00048 #if defined(__UCLIBC__)
00049 #   define __fxstat64(_stat_ver, _fd, _sbp)    fstat((_fd), (_sbp))
00050 #endif
00051 #if defined(hpux) || defined(__hpux)
00052 # define        _INCLUDE_POSIX_SOURCE
00053 #   define __errno_location()   (&errno)
00054 #   define dirfd(dirp)          -1
00055 #   define stat64               stat
00056 #   define _STAT_VER            0
00057 #   define __fxstat64(_stat_ver, _fd, _sbp)     fstat((_fd), (_sbp))
00058 #   define _D_EXACT_NAMLEN(d) ((d)->d_namlen)
00059 #endif
00060 #if defined(sun) || defined(RPM_OS_UNIXWARE)
00061 #   define __errno_location()   (&errno)
00062 #   define dirfd(dirp)          -1
00063 #   define _STAT_VER            0
00064 #   define __fxstat64(_stat_ver, _fd, _sbp)     fstat((_fd), (_sbp))
00065 #endif
00066 #if defined(__APPLE__)
00067 #   include <sys/stat.h>
00068 #   define __errno_location()   (__error())
00069 #ifndef __DARWIN_STRUCT_STAT64
00070 #   define stat64               stat
00071 #endif
00072 #   define _STAT_VER            0
00073 #ifndef __DARWIN_STRUCT_STAT64
00074 #   define __fxstat64(_stat_ver, _fd, _sbp)     fstat((_fd), (_sbp))
00075 #else
00076 #   define __fxstat64(_stat_ver, _fd, _sbp)     fstat64((_fd), (_sbp))
00077 #endif
00078 #endif
00079 #if defined(__CYGWIN__) || defined(__MINGW32__)
00080 #   include <sys/stat.h>
00081 #if defined(__CYGWIN__)
00082 #   define __errno_location()   (__errno())
00083 #elif !defined(_UWIN)
00084 #   define __errno_location()   (_errno())
00085 #else
00086 #   define __errno_location()   (&errno)
00087 #endif
00088 #   define stat64               stat
00089 #   define _STAT_VER            0
00090 #   define __fxstat64(_stat_ver, _fd, _sbp)     fstat((_fd), (_sbp))
00091 #endif
00092 #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
00093 #   define __errno_location()  (&errno)
00094 #   define stat64              stat
00095 #   define __fxstat64(_stat_ver, _fd, _sbp)    fstat((_fd), (_sbp))
00096 #   define _D_EXACT_NAMLEN(d) ((d)->d_namlen)
00097 #endif
00098 #if defined(__osf__)
00099 #   define __errno_location()   (&errno)
00100 #   define dirfd(dirp)          -1
00101 #   define stat64               stat
00102 #   define _STAT_VER            0
00103 #   define __fxstat64(_stat_ver, _fd, _sbp)     fstat((_fd), (_sbp))
00104 #   define _D_EXACT_NAMLEN(d) ((d)->d_namlen)
00105 #endif
00106 #if defined(RPM_OS_IRIX)
00107 #   define __errno_location()   (&errno)
00108 #   define dirfd(dirp)          -1
00109 #   define __fxstat64(_stat_ver, _fd, _sbp)     fstat((_fd), (_sbp))
00110 #   define _D_EXACT_NAMLEN(d) ((d)->d_reclen)
00111 #endif
00112 #if defined(RPM_OS_AIX)
00113 #   define __errno_location()   (&errno)
00114 #   define dirfd(dirp)          ((dirp)->dd_fd)
00115 #   define _STAT_VER            0
00116 #   define __fxstat64(_stat_ver, _fd, _sbp)     fstat((_fd), (_sbp))
00117 #   define _D_EXACT_NAMLEN(d) ((d)->d_namlen)
00118 #endif
00119 #if defined(RPM_OS_NTOQNX)
00120 #   define __errno_location()  (&errno)
00121 #   define stat64              stat
00122 #   define _STAT_VER           0
00123 #   define dirfd(dirp)         -1
00124 #   define __fxstat64(_stat_ver, _fd, _sbp)    fstat((_fd), (_sbp))
00125 #endif
00126 
00127 #if !defined(_D_EXACT_NAMLEN)
00128 #   define _D_EXACT_NAMLEN(d) (strlen((d)->d_name))
00129 #endif
00130 
00131 #include "fts.h"
00132 #include <rpmio.h>
00133 #include <rpmurl.h>
00134 #include <rpmdir.h>
00135 
00136 #include "debug.h"
00137 
00138 #   define __set_errno(val) (*__errno_location ()) = (val)
00139 #   define __open       open
00140 #   define __close      close
00141 #   define __fchdir     fchdir
00142 #endif  /* defined(_LIBC) */
00143 
00144 #if !defined(USHRT_MAX)
00145 #define USHRT_MAX       65535
00146 #endif
00147 
00148 /* Largest alignment size needed, minus one.
00149    Usually long double is the worst case.  */
00150 #ifndef ALIGNBYTES
00151 #if defined __GNUC__ && __GNUC__ >= 2
00152 # define alignof(TYPE) __alignof__ (TYPE)
00153 #else
00154 # define alignof(TYPE) \
00155     ((int) &((struct { char dummy1; TYPE dummy2; } *) 0)->dummy2)
00156 #endif
00157 #define ALIGNBYTES      (alignof(long double) - 1)
00158 #endif
00159 /* Align P to that size.  */
00160 #ifndef ALIGN
00161 #define ALIGN(p)        (((unsigned long int) (p) + ALIGNBYTES) & ~ALIGNBYTES)
00162 #endif
00163 
00164 /*@unchecked@*/
00165 int _fts_debug = 0;
00166 
00167 /*@only@*/ /*@null@*/
00168 static FTSENT * fts_alloc(FTS * sp, const char * name, int namelen)
00169         /*@*/;
00170 /*@null@*/
00171 static FTSENT * fts_build(FTS * sp, int type)
00172         /*@globals fileSystem, internalState @*/
00173         /*@modifies *sp, fileSystem, internalState @*/;
00174 static void     fts_lfree(/*@only@*/ FTSENT * head)
00175         /*@modifies head @*/;
00176 static void     fts_load(FTS * sp, FTSENT * p)
00177         /*@modifies *sp, *p @*/;
00178 static size_t   fts_maxarglen(char * const * argv)
00179         /*@*/;
00180 static void     fts_padjust(FTS * sp, FTSENT * head)
00181         /*@modifies *sp, *head @*/;
00182 static int      fts_palloc(FTS * sp, size_t more)
00183         /*@modifies *sp @*/;
00184 static FTSENT * fts_sort(FTS * sp, /*@returned@*/ FTSENT * head, int nitems)
00185         /*@modifies *sp @*/;
00186 static u_short  fts_stat(FTS * sp, FTSENT * p, int follow)
00187         /*@modifies *p @*/;
00188 static int      fts_safe_changedir(FTS * sp, FTSENT * p, int fd,
00189                         const char * path)
00190         /*@globals fileSystem, internalState @*/
00191         /*@modifies fileSystem, internalState @*/;
00192 
00193 #define ISDOT(a)        (a[0] == '.' && (!a[1] || (a[1] == '/' && !a[2]) || (a[1] == '.' && (!a[2] || (a[2] == '/' && !a[3])))))
00194 
00195 #define CLR(opt)        (sp->fts_options &= ~(opt))
00196 #define ISSET(opt)      (sp->fts_options & (opt))
00197 #define SET(opt)        (sp->fts_options |= (opt))
00198 
00199 #define FCHDIR(sp, fd)  (!ISSET(FTS_NOCHDIR) && __fchdir(fd))
00200 
00201 /* fts_build flags */
00202 #define BCHILD          1               /* fts_children */
00203 #define BNAMES          2               /* fts_children, names only */
00204 #define BREAD           3               /* fts_read */
00205 
00206 FTS *
00207 Fts_open(char * const * argv, int options,
00208                 int (*compar) (const FTSENT **, const FTSENT **))
00209 {
00210         register FTS *sp;
00211         register FTSENT *p, *root;
00212         register int nitems;
00213         FTSENT *parent = NULL;
00214         FTSENT *tmp = NULL;
00215         size_t len;
00216 
00217 /*@-formattype -modfilesys@*/
00218 if (_fts_debug)
00219 fprintf(stderr, "--> Fts_open(%p, 0x%x, %p) av[0] %s\n", argv, options, compar, argv[0]);
00220 /*@=formattype =modfilesys@*/
00221 
00222         /* Options check. */
00223         if (options & ~FTS_OPTIONMASK) {
00224 /*@-sysunrecog@*/
00225                 __set_errno (EINVAL);
00226 /*@=sysunrecog@*/
00227                 return (NULL);
00228         }
00229 
00230         /* Allocate/initialize the stream */
00231         if ((sp = malloc((u_int)sizeof(*sp))) == NULL)
00232                 return (NULL);
00233         memset(sp, 0, sizeof(*sp));
00234         sp->fts_compar = (int (*) (const void *, const void *)) compar;
00235         sp->fts_opendir = Opendir;
00236         sp->fts_readdir = Readdir;
00237         sp->fts_closedir = Closedir;
00238         sp->fts_stat = Stat;
00239         sp->fts_lstat = Lstat;
00240         sp->fts_options = options;
00241 
00242         /* Logical walks turn on NOCHDIR; symbolic links are too hard. */
00243         if (ISSET(FTS_LOGICAL))
00244                 SET(FTS_NOCHDIR);
00245 
00246         /*
00247          * Start out with 1K of path space, and enough, in any case,
00248          * to hold the user's paths.
00249          */
00250 #ifndef MAXPATHLEN
00251 #define MAXPATHLEN 1024
00252 #endif
00253         len = fts_maxarglen(argv);
00254         if (len < MAXPATHLEN)
00255             len = MAXPATHLEN;
00256         if (fts_palloc(sp, len))
00257                 goto mem1;
00258 
00259         /* Allocate/initialize root's parent. */
00260         if (*argv != NULL) {
00261                 if ((parent = fts_alloc(sp, "", 0)) == NULL)
00262                         goto mem2;
00263                 parent->fts_level = FTS_ROOTPARENTLEVEL;
00264         }
00265 
00266         /* Allocate/initialize root(s). */
00267         for (root = NULL, nitems = 0; *argv != NULL; ++argv, ++nitems) {
00268                 /* Don't allow zero-length paths. */
00269                 if ((len = strlen(*argv)) == 0) {
00270                         __set_errno (ENOENT);
00271                         goto mem3;
00272                 }
00273 
00274                 /* Use fchdir(2) speedup only if local DASDI. */
00275                 switch (urlIsURL(*argv)) {
00276                 case URL_IS_DASH:
00277                 case URL_IS_HKP:
00278                 case URL_IS_MONGO:      /* XXX FIXME */
00279                         __set_errno (ENOENT);
00280                         goto mem3;
00281                         /*@notreached@*/ /*@switchbreak@*/ break;
00282                 case URL_IS_HTTPS:
00283                 case URL_IS_HTTP:
00284                 case URL_IS_FTP:
00285                         SET(FTS_NOCHDIR);
00286                         /*@switchbreak@*/ break;
00287                 case URL_IS_UNKNOWN:
00288                 case URL_IS_PATH:
00289                         /*@switchbreak@*/ break;
00290                 }
00291 
00292                 p = fts_alloc(sp, *argv, (int)len);
00293                 if (p == NULL)
00294                         goto mem3;
00295                 p->fts_level = FTS_ROOTLEVEL;
00296                 p->fts_parent = parent;
00297                 p->fts_accpath = p->fts_name;
00298                 p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW));
00299 
00300                 /* Command-line "." and ".." are real directories. */
00301                 if (p->fts_info == FTS_DOT)
00302                         p->fts_info = FTS_D;
00303 
00304                 /*
00305                  * If comparison routine supplied, traverse in sorted
00306                  * order; otherwise traverse in the order specified.
00307                  */
00308                 if (compar) {
00309                         p->fts_link = root;
00310                         root = p;
00311                 } else {
00312                         p->fts_link = NULL;
00313                         if (root == NULL)
00314                                 tmp = root = p;
00315                         else {
00316                                 if (tmp != NULL)        /* XXX can't happen */
00317                                         tmp->fts_link = p;
00318                                 tmp = p;
00319                         }
00320                 }
00321         }
00322         if (compar && nitems > 1)
00323                 root = fts_sort(sp, root, nitems);
00324 
00325         /*
00326          * Allocate a dummy pointer and make fts_read think that we've just
00327          * finished the node before the root(s); set p->fts_info to FTS_INIT
00328          * so that everything about the "current" node is ignored.
00329          */
00330         if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL)
00331                 goto mem3;
00332         sp->fts_cur->fts_link = root;
00333         sp->fts_cur->fts_info = FTS_INIT;
00334 
00335         /*
00336          * If using chdir(2), grab a file descriptor pointing to dot to ensure
00337          * that we can get back here; this could be avoided for some paths,
00338          * but almost certainly not worth the effort.  Slashes, symbolic links,
00339          * and ".." are all fairly nasty problems.  Note, if we can't get the
00340          * descriptor we run anyway, just more slowly.
00341          */
00342         if (!ISSET(FTS_NOCHDIR)
00343             && (sp->fts_rfd = __open(".", O_RDONLY, 0)) < 0)
00344                 SET(FTS_NOCHDIR);
00345 
00346         return (sp);
00347 
00348 mem3:   fts_lfree(root);
00349         free(parent);
00350 mem2:   free(sp->fts_path);
00351 mem1:   free(sp);
00352         return (NULL);
00353 }
00354 
00355 static void
00356 fts_load(FTS * sp, FTSENT * p)
00357 {
00358         register size_t len;
00359         register char *cp;
00360 
00361         /*
00362          * Load the stream structure for the next traversal.  Since we don't
00363          * actually enter the directory until after the preorder visit, set
00364          * the fts_accpath field specially so the chdir gets done to the right
00365          * place and the user can access the first node.  From fts_open it's
00366          * known that the path will fit.
00367          */
00368         len = p->fts_pathlen = p->fts_namelen;
00369         memmove(sp->fts_path, p->fts_name, len + 1);
00370         if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) {
00371                 len = strlen(++cp);
00372                 memmove(p->fts_name, cp, len + 1);
00373                 p->fts_namelen = (u_short)len;
00374         }
00375         p->fts_accpath = p->fts_path = sp->fts_path;
00376         sp->fts_dev = p->fts_dev;
00377 }
00378 
00379 int
00380 Fts_close(FTS * sp)
00381 {
00382         register FTSENT *freep, *p;
00383         int saved_errno;
00384 
00385 if (_fts_debug)
00386 fprintf(stderr, "--> Fts_close(%p)\n", sp);
00387 
00388         if (sp == NULL)
00389                 return 0;
00390 
00391         /*
00392          * This still works if we haven't read anything -- the dummy structure
00393          * points to the root list, so we step through to the end of the root
00394          * list which has a valid parent pointer.
00395          */
00396         if (sp->fts_cur) {
00397                 for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {
00398                         freep = p;
00399                         p = p->fts_link != NULL ? p->fts_link : p->fts_parent;
00400                         free(freep);
00401                 }
00402                 free(p);
00403         }
00404 
00405         /* Free up child linked list, sort array, path buffer. */
00406         if (sp->fts_child)
00407                 fts_lfree(sp->fts_child);
00408         if (sp->fts_array)
00409                 free(sp->fts_array);
00410         free(sp->fts_path);
00411 
00412         /* Return to original directory, save errno if necessary. */
00413         if (!ISSET(FTS_NOCHDIR)) {
00414                 saved_errno = __fchdir(sp->fts_rfd) ? errno : 0;
00415                 (void)__close(sp->fts_rfd);
00416 
00417                 /* Set errno and return. */
00418                 if (saved_errno != 0) {
00419                         /* Free up the stream pointer. */
00420                         free(sp);
00421                         __set_errno (saved_errno);
00422                         return (-1);
00423                 }
00424         }
00425 
00426         /* Free up the stream pointer. */
00427         free(sp);
00428         return (0);
00429 }
00430 
00431 static int indent = 2;
00432 
00433 static const char * ftsInfoStrings[] = {
00434     "UNKNOWN",  
00435     "D",
00436     "DC",
00437     "DEFAULT",
00438     "DNR",
00439     "DOT",
00440     "DP",
00441     "ERR",
00442     "F",
00443     "INIT",
00444     "NS",
00445     "NSOK",
00446     "SL",
00447     "SLNONE",
00448     "W",
00449 };
00450 
00451 static const char * ftsInfoStr(int fts_info)
00452 {
00453     if (!(fts_info >= 1 && fts_info <= 14))
00454         fts_info = 0;
00455     return ftsInfoStrings[ fts_info ];
00456 }
00457 
00458 /*
00459  * Special case of "/" at the end of the path so that slashes aren't
00460  * appended which would cause paths to be written as "....//foo".
00461  */
00462 #define NAPPEND(p)                                                      \
00463         (p->fts_path[p->fts_pathlen - 1] == '/'                         \
00464             ? p->fts_pathlen - 1 : p->fts_pathlen)
00465 
00466 FTSENT *
00467 Fts_read(FTS * sp)
00468 {
00469         register FTSENT *p;
00470         register FTSENT *tmp;
00471         register int instr;
00472         register char *t;
00473         int saved_errno;
00474 
00475 if (_fts_debug)
00476 fprintf(stderr, "--> Fts_read(%p)\n", sp);
00477         /* If finished or unrecoverable error, return NULL. */
00478         if (sp == NULL || sp->fts_cur == NULL || ISSET(FTS_STOP)) {
00479                 p = NULL;
00480                 goto exit;
00481         }
00482 
00483         /* Set current node pointer. */
00484         p = sp->fts_cur;
00485 
00486         /* Save and zero out user instructions. */
00487         instr = p->fts_instr;
00488         p->fts_instr = FTS_NOINSTR;
00489 
00490         /* Any type of file may be re-visited; re-stat and re-turn. */
00491         if (instr == FTS_AGAIN) {
00492                 p->fts_info = fts_stat(sp, p, 0);
00493                 goto exit;
00494         }
00495 
00496         /*
00497          * Following a symlink -- SLNONE test allows application to see
00498          * SLNONE and recover.  If indirecting through a symlink, have
00499          * keep a pointer to current location.  If unable to get that
00500          * pointer, follow fails.
00501          */
00502         if (instr == FTS_FOLLOW &&
00503             (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {
00504                 p->fts_info = fts_stat(sp, p, 1);
00505                 if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
00506                         if ((p->fts_symfd = __open(".", O_RDONLY, 0)) < 0) {
00507                                 p->fts_errno = errno;
00508                                 p->fts_info = FTS_ERR;
00509                         } else
00510                                 p->fts_flags |= FTS_SYMFOLLOW;
00511                 }
00512                 goto exit;
00513         }
00514 
00515         /* Directory in pre-order. */
00516         if (p->fts_info == FTS_D) {
00517                 /* If skipped or crossed mount point, do post-order visit. */
00518                 if (instr == FTS_SKIP ||
00519                     (ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev)) {
00520                         if (p->fts_flags & FTS_SYMFOLLOW) {
00521                                 (void)__close(p->fts_symfd);
00522                                 p->fts_symfd = -1;
00523                         }
00524                         if (sp->fts_child) {
00525                                 fts_lfree(sp->fts_child);
00526                                 sp->fts_child = NULL;
00527                         }
00528                         p->fts_info = FTS_DP;
00529                         goto exit;
00530                 }
00531 
00532                 /* Rebuild if only read the names and now traversing. */
00533                 if (sp->fts_child != NULL && ISSET(FTS_NAMEONLY)) {
00534                         CLR(FTS_NAMEONLY);
00535                         fts_lfree(sp->fts_child);
00536                         sp->fts_child = NULL;
00537                 }
00538 
00539                 /*
00540                  * Cd to the subdirectory.
00541                  *
00542                  * If have already read and now fail to chdir, whack the list
00543                  * to make the names come out right, and set the parent errno
00544                  * so the application will eventually get an error condition.
00545                  * Set the FTS_DONTCHDIR flag so that when we logically change
00546                  * directories back to the parent we don't do a chdir.
00547                  *
00548                  * If haven't read do so.  If the read fails, fts_build sets
00549                  * FTS_STOP or the fts_info field of the node.
00550                  */
00551                 if (sp->fts_child != NULL) {
00552                         if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) {
00553                                 p->fts_errno = errno;
00554                                 p->fts_flags |= FTS_DONTCHDIR;
00555                                 for (p = sp->fts_child; p != NULL;
00556                                      p = p->fts_link)
00557                                         p->fts_accpath =
00558                                             p->fts_parent->fts_accpath;
00559                         }
00560                 } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) {
00561                         if (ISSET(FTS_STOP)) {
00562                                 p = NULL;
00563                                 goto exit;
00564                         }
00565                         goto exit;
00566                 }
00567                 p = sp->fts_child;
00568                 sp->fts_child = NULL;
00569                 sp->fts_cur = p;
00570                 goto name;
00571         }
00572 
00573         /* Move to the next node on this level. */
00574 next:   tmp = p;
00575         if ((p = p->fts_link) != NULL) {
00576                 sp->fts_cur = p;
00577                 free(tmp);
00578 
00579                 /*
00580                  * If reached the top, return to the original directory (or
00581                  * the root of the tree), and load the paths for the next root.
00582                  */
00583                 if (p->fts_level == FTS_ROOTLEVEL) {
00584                         if (FCHDIR(sp, sp->fts_rfd)) {
00585                                 SET(FTS_STOP);
00586                                 p = NULL;
00587                                 goto exit;
00588                         }
00589                         fts_load(sp, p);
00590                         goto exit;
00591                 }
00592 
00593                 /*
00594                  * User may have called fts_set on the node.  If skipped,
00595                  * ignore.  If followed, get a file descriptor so we can
00596                  * get back if necessary.
00597                  */
00598                 if (p->fts_instr == FTS_SKIP)
00599                         goto next;
00600                 if (p->fts_instr == FTS_FOLLOW) {
00601                         p->fts_info = fts_stat(sp, p, 1);
00602                         if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
00603                                 if ((p->fts_symfd =
00604                                     __open(".", O_RDONLY, 0)) < 0) {
00605                                         p->fts_errno = errno;
00606                                         p->fts_info = FTS_ERR;
00607                                 } else
00608                                         p->fts_flags |= FTS_SYMFOLLOW;
00609                         }
00610                         p->fts_instr = FTS_NOINSTR;
00611                 }
00612 
00613 name:           t = sp->fts_path + NAPPEND(p->fts_parent);
00614                 *t++ = '/';
00615                 memmove(t, p->fts_name, p->fts_namelen + 1);
00616                 goto exit;
00617         }
00618 
00619         /* Move up to the parent node. */
00620         p = tmp->fts_parent;
00621         sp->fts_cur = p;
00622         free(tmp);
00623 
00624         if (p->fts_level == FTS_ROOTPARENTLEVEL) {
00625                 /*
00626                  * Done; free everything up and set errno to 0 so the user
00627                  * can distinguish between error and EOF.
00628                  */
00629                 free(p);
00630                 __set_errno (0);
00631                 sp->fts_cur = p = NULL;
00632                 goto exit;
00633         }
00634 
00635         /* NUL terminate the pathname. */
00636         sp->fts_path[p->fts_pathlen] = '\0';
00637 
00638         /*
00639          * Return to the parent directory.  If at a root node or came through
00640          * a symlink, go back through the file descriptor.  Otherwise, cd up
00641          * one directory.
00642          */
00643         if (p->fts_level == FTS_ROOTLEVEL) {
00644                 if (FCHDIR(sp, sp->fts_rfd)) {
00645                         SET(FTS_STOP);
00646                         p = NULL;
00647                         goto exit;
00648                 }
00649         } else if (p->fts_flags & FTS_SYMFOLLOW) {
00650                 if (FCHDIR(sp, p->fts_symfd)) {
00651                         saved_errno = errno;
00652                         (void)__close(p->fts_symfd);
00653                         p->fts_symfd = -1;
00654                         __set_errno (saved_errno);
00655                         SET(FTS_STOP);
00656                         p = NULL;
00657                         goto exit;
00658                 }
00659                 (void)__close(p->fts_symfd);
00660                 p->fts_symfd = -1;
00661         } else if (!(p->fts_flags & FTS_DONTCHDIR) &&
00662                    fts_safe_changedir(sp, p->fts_parent, -1, "..")) {
00663                 SET(FTS_STOP);
00664                 p = NULL;
00665                 goto exit;
00666         }
00667         p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;
00668 
00669 exit:
00670   if (_fts_debug) {
00671     fprintf(stderr, "<-- Fts_read(%p) ret %p", sp, p);
00672     if (p)
00673         fprintf(stderr, " FTS_%s\t%*s %s", ftsInfoStr(p->fts_info),
00674                 indent * (p->fts_level < 0 ? 0 : p->fts_level), "",
00675                 (p->fts_name[0] != '\0' ? p->fts_name : p->fts_path));
00676     fprintf(stderr, "\n");
00677   }
00678         return (p);
00679 }
00680 
00681 /*
00682  * Fts_set takes the stream as an argument although it's not used in this
00683  * implementation; it would be necessary if anyone wanted to add global
00684  * semantics to fts using fts_set.  An error return is allowed for similar
00685  * reasons.
00686  */
00687 int
00688 Fts_set(/*@unused@*/ FTS * sp, FTSENT * p, int instr)
00689 {
00690 /*@-modfilesys@*/
00691 if (_fts_debug)
00692 fprintf(stderr, "--> Fts_set(%p, %p, 0x%x)\n", sp, p, instr);
00693 /*@=modfilesys@*/
00694 
00695         if (instr != 0 && instr != FTS_AGAIN && instr != FTS_FOLLOW &&
00696             instr != FTS_NOINSTR && instr != FTS_SKIP) {
00697                 __set_errno (EINVAL);
00698                 return (1);
00699         }
00700         p->fts_instr = instr;
00701         return (0);
00702 }
00703 
00704 FTSENT *
00705 Fts_children(FTS * sp, int instr)
00706 {
00707         register FTSENT *p;
00708         int fd;
00709 
00710 /*@-modfilesys@*/
00711 if (_fts_debug)
00712 fprintf(stderr, "--> Fts_children(%p, 0x%x)\n", sp, instr);
00713 /*@=modfilesys@*/
00714 
00715         if (instr != 0 && instr != FTS_NAMEONLY) {
00716                 __set_errno (EINVAL);
00717                 return (NULL);
00718         }
00719 
00720         /* Set current node pointer. */
00721         p = sp->fts_cur;
00722 
00723         /*
00724          * Errno set to 0 so user can distinguish empty directory from
00725          * an error.
00726          */
00727         __set_errno (0);
00728 
00729         /* Fatal errors stop here. */
00730         if (ISSET(FTS_STOP))
00731                 return (NULL);
00732 
00733         /* Return logical hierarchy of user's arguments. */
00734         if (p->fts_info == FTS_INIT)
00735                 return (p->fts_link);
00736 
00737         /*
00738          * If not a directory being visited in pre-order, stop here.  Could
00739          * allow FTS_DNR, assuming the user has fixed the problem, but the
00740          * same effect is available with FTS_AGAIN.
00741          */
00742         if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */)
00743                 return (NULL);
00744 
00745         /* Free up any previous child list. */
00746         if (sp->fts_child != NULL)
00747                 fts_lfree(sp->fts_child);
00748 
00749         if (instr == FTS_NAMEONLY) {
00750                 SET(FTS_NAMEONLY);
00751                 instr = BNAMES;
00752         } else
00753                 instr = BCHILD;
00754 
00755         /*
00756          * If using chdir on a relative path and called BEFORE fts_read does
00757          * its chdir to the root of a traversal, we can lose -- we need to
00758          * chdir into the subdirectory, and we don't know where the current
00759          * directory is, so we can't get back so that the upcoming chdir by
00760          * fts_read will work.
00761          */
00762         if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' ||
00763             ISSET(FTS_NOCHDIR))
00764                 return (sp->fts_child = fts_build(sp, instr));
00765 
00766         if ((fd = __open(".", O_RDONLY, 0)) < 0)
00767                 return (NULL);
00768         sp->fts_child = fts_build(sp, instr);
00769         if (__fchdir(fd))
00770                 return (NULL);
00771         (void)__close(fd);
00772         return (sp->fts_child);
00773 }
00774 
00775 /*
00776  * This is the tricky part -- do not casually change *anything* in here.  The
00777  * idea is to build the linked list of entries that are used by fts_children
00778  * and fts_read.  There are lots of special cases.
00779  *
00780  * The real slowdown in walking the tree is the stat calls.  If FTS_NOSTAT is
00781  * set and it's a physical walk (so that symbolic links can't be directories),
00782  * we can do things quickly.  First, if it's a 4.4BSD file system, the type
00783  * of the file is in the directory entry.  Otherwise, we assume that the number
00784  * of subdirectories in a node is equal to the number of links to the parent.
00785  * The former skips all stat calls.  The latter skips stat calls in any leaf
00786  * directories and for any files after the subdirectories in the directory have
00787  * been found, cutting the stat calls by about 2/3.
00788  */
00789 static FTSENT *
00790 fts_build(FTS * sp, int type)
00791 {
00792         register struct dirent *dp;
00793         register FTSENT *p, *head;
00794         register int nitems;
00795         FTSENT *cur, *tail;
00796         DIR *dirp;
00797         void *oldaddr;
00798         int cderrno, descend, len, level, nlinks, saved_errno,
00799             nostat, doadjust;
00800         size_t maxlen;
00801         char *cp;
00802 
00803         /* Set current node pointer. */
00804         cur = sp->fts_cur;
00805 
00806         /*
00807          * Open the directory for reading.  If this fails, we're done.
00808          * If being called from fts_read, set the fts_info field.
00809          */
00810 #if defined FTS_WHITEOUT && 0
00811         if (ISSET(FTS_WHITEOUT))
00812                 oflag = DTF_NODUP|DTF_REWIND;
00813         else
00814                 oflag = DTF_HIDEW|DTF_NODUP|DTF_REWIND;
00815 #else
00816 # define __opendir2(path, flag) (*sp->fts_opendir) (path)
00817 #endif
00818        if ((dirp = __opendir2(cur->fts_accpath, oflag)) == NULL) {
00819                 if (type == BREAD) {
00820                         cur->fts_info = FTS_DNR;
00821                         cur->fts_errno = errno;
00822                 }
00823                 return (NULL);
00824         }
00825 
00826         /*
00827          * Nlinks is the number of possible entries of type directory in the
00828          * directory if we're cheating on stat calls, 0 if we're not doing
00829          * any stat calls at all, -1 if we're doing stats on everything.
00830          */
00831         if (type == BNAMES) {
00832                 nlinks = 0;
00833                 /* Be quiet about nostat, GCC. */
00834                 nostat = 0;
00835         } else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) {
00836                 nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2);
00837                 nostat = 1;
00838         } else {
00839                 nlinks = -1;
00840                 nostat = 0;
00841         }
00842 
00843 #ifdef notdef
00844         (void)printf("nlinks == %d (cur: %d)\n", nlinks, cur->fts_nlink);
00845         (void)printf("NOSTAT %d PHYSICAL %d SEEDOT %d\n",
00846             ISSET(FTS_NOSTAT), ISSET(FTS_PHYSICAL), ISSET(FTS_SEEDOT));
00847 #endif
00848         /*
00849          * If we're going to need to stat anything or we want to descend
00850          * and stay in the directory, chdir.  If this fails we keep going,
00851          * but set a flag so we don't chdir after the post-order visit.
00852          * We won't be able to stat anything, but we can still return the
00853          * names themselves.  Note, that since fts_read won't be able to
00854          * chdir into the directory, it will have to return different path
00855          * names than before, i.e. "a/b" instead of "b".  Since the node
00856          * has already been visited in pre-order, have to wait until the
00857          * post-order visit to return the error.  There is a special case
00858          * here, if there was nothing to stat then it's not an error to
00859          * not be able to stat.  This is all fairly nasty.  If a program
00860          * needed sorted entries or stat information, they had better be
00861          * checking FTS_NS on the returned nodes.
00862          */
00863         cderrno = 0;
00864         if (nlinks || type == BREAD) {
00865 /*@-unrecog@*/
00866                 if (fts_safe_changedir(sp, cur, dirfd(dirp), NULL)) {
00867 /*@=unrecog@*/
00868                         if (nlinks && type == BREAD)
00869                                 cur->fts_errno = errno;
00870                         cur->fts_flags |= FTS_DONTCHDIR;
00871                         descend = 0;
00872                         cderrno = errno;
00873                         (void) (*sp->fts_closedir) (dirp);
00874                         dirp = NULL;
00875                 } else
00876                         descend = 1;
00877         } else
00878                 descend = 0;
00879 
00880         /*
00881          * Figure out the max file name length that can be stored in the
00882          * current path -- the inner loop allocates more path as necessary.
00883          * We really wouldn't have to do the maxlen calculations here, we
00884          * could do them in fts_read before returning the path, but it's a
00885          * lot easier here since the length is part of the dirent structure.
00886          *
00887          * If not changing directories set a pointer so that can just append
00888          * each new name into the path.
00889          */
00890         len = NAPPEND(cur);
00891         if (ISSET(FTS_NOCHDIR)) {
00892                 cp = sp->fts_path + len;
00893                 *cp++ = '/';
00894         } else {
00895                 /* GCC, you're too verbose. */
00896                 cp = NULL;
00897         }
00898         len++;
00899         maxlen = sp->fts_pathlen - len;
00900 
00901         level = cur->fts_level + 1;
00902 
00903         /* Read the directory, attaching each entry to the `link' pointer. */
00904         doadjust = 0;
00905         for (head = tail = NULL, nitems = 0;
00906              dirp && (dp = (*sp->fts_readdir) (dirp));)
00907         {
00908                 if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))
00909                         continue;
00910 
00911                 if ((p = fts_alloc(sp, dp->d_name, (int)_D_EXACT_NAMLEN (dp))) == NULL)
00912                         goto mem1;
00913                 if (_D_EXACT_NAMLEN (dp) >= maxlen) {/* include space for NUL */
00914                         oldaddr = sp->fts_path;
00915                         if (fts_palloc(sp, _D_EXACT_NAMLEN (dp) + len + 1)) {
00916                                 /*
00917                                  * No more memory for path or structures.  Save
00918                                  * errno, free up the current structure and the
00919                                  * structures already allocated.
00920                                  */
00921 mem1:                           saved_errno = errno;
00922                                 if (p)
00923                                         free(p);
00924                                 fts_lfree(head);
00925                                 (void) (*sp->fts_closedir) (dirp);
00926                                 cur->fts_info = FTS_ERR;
00927                                 SET(FTS_STOP);
00928                                 __set_errno (saved_errno);
00929                                 return (NULL);
00930                         }
00931                         /* Did realloc() change the pointer? */
00932                         if (oldaddr != sp->fts_path) {
00933                                 doadjust = 1;
00934                                 if (ISSET(FTS_NOCHDIR))
00935                                         cp = sp->fts_path + len;
00936                         }
00937                         maxlen = sp->fts_pathlen - len;
00938                 }
00939 
00940                 if (len + _D_EXACT_NAMLEN (dp) >= USHRT_MAX) {
00941                         /*
00942                          * In an FTSENT, fts_pathlen is a u_short so it is
00943                          * possible to wraparound here.  If we do, free up
00944                          * the current structure and the structures already
00945                          * allocated, then error out with ENAMETOOLONG.
00946                          */
00947                         free(p);
00948                         fts_lfree(head);
00949                         (void) (*sp->fts_closedir) (dirp);
00950                         cur->fts_info = FTS_ERR;
00951                         SET(FTS_STOP);
00952                         __set_errno (ENAMETOOLONG);
00953                         return (NULL);
00954                 }
00955                 p->fts_level = level;
00956                 p->fts_parent = sp->fts_cur;
00957                 p->fts_pathlen = (u_short)(len + _D_EXACT_NAMLEN (dp));
00958 
00959 #if defined FTS_WHITEOUT && 0
00960                 if (dp->d_type == DT_WHT)
00961                         p->fts_flags |= FTS_ISW;
00962 #endif
00963 
00964 #if 0
00965                 /*
00966                  * Unreachable code.  cderrno is only ever set to a nonnull
00967                  * value if dirp is closed at the same time.  But then we
00968                  * cannot enter this loop.
00969                  */
00970                 if (cderrno) {
00971                         if (nlinks) {
00972                                 p->fts_info = FTS_NS;
00973                                 p->fts_errno = cderrno;
00974                         } else
00975                                 p->fts_info = FTS_NSOK;
00976                         p->fts_accpath = cur->fts_accpath;
00977                 } else
00978 #endif
00979                 if (nlinks == 0
00980 #if defined DT_DIR && defined _DIRENT_HAVE_D_TYPE
00981                            || (nostat &&
00982                                dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN)
00983 #endif
00984                     ) {
00985                         p->fts_accpath =
00986                             ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name;
00987                         p->fts_info = FTS_NSOK;
00988                 } else {
00989                         /* Build a file name for fts_stat to stat. */
00990                         if (ISSET(FTS_NOCHDIR)) {
00991                                 p->fts_accpath = p->fts_path;
00992                                 memmove(cp, p->fts_name, p->fts_namelen + 1);
00993                         } else
00994                                 p->fts_accpath = p->fts_name;
00995                         /* Stat it. */
00996                         p->fts_info = fts_stat(sp, p, 0);
00997 
00998                         /* Decrement link count if applicable. */
00999                         if (nlinks > 0 && (p->fts_info == FTS_D ||
01000                             p->fts_info == FTS_DC || p->fts_info == FTS_DOT))
01001                                 --nlinks;
01002                 }
01003 
01004                 /* We walk in directory order so "ls -f" doesn't get upset. */
01005                 p->fts_link = NULL;
01006                 if (head == NULL)
01007                         head = tail = p;
01008                 else {
01009                         tail->fts_link = p;
01010                         tail = p;
01011                 }
01012                 ++nitems;
01013         }
01014         if (dirp)
01015                 (void) (*sp->fts_closedir) (dirp);
01016 
01017         /*
01018          * If realloc() changed the address of the path, adjust the
01019          * addresses for the rest of the tree and the dir list.
01020          */
01021         if (doadjust)
01022                 fts_padjust(sp, head);
01023 
01024         /*
01025          * If not changing directories, reset the path back to original
01026          * state.
01027          */
01028         if (ISSET(FTS_NOCHDIR)) {
01029                 if (len == sp->fts_pathlen || nitems == 0)
01030                         --cp;
01031                 if (cp != NULL) /* XXX can't happen */
01032                         *cp = '\0';
01033         }
01034 
01035         /*
01036          * If descended after called from fts_children or after called from
01037          * fts_read and nothing found, get back.  At the root level we use
01038          * the saved fd; if one of fts_open()'s arguments is a relative path
01039          * to an empty directory, we wind up here with no other way back.  If
01040          * can't get back, we're done.
01041          */
01042         if (descend && (type == BCHILD || !nitems) &&
01043             (cur->fts_level == FTS_ROOTLEVEL ?
01044              FCHDIR(sp, sp->fts_rfd) :
01045              fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) {
01046                 cur->fts_info = FTS_ERR;
01047                 SET(FTS_STOP);
01048                 fts_lfree(head);
01049                 return (NULL);
01050         }
01051 
01052         /* If didn't find anything, return NULL. */
01053         if (!nitems) {
01054                 if (type == BREAD)
01055                         cur->fts_info = FTS_DP;
01056                 fts_lfree(head);
01057                 return (NULL);
01058         }
01059 
01060         /* Sort the entries. */
01061         if (sp->fts_compar && nitems > 1)
01062                 head = fts_sort(sp, head, nitems);
01063         return (head);
01064 }
01065 
01066 static u_short
01067 fts_stat(FTS * sp, FTSENT * p, int follow)
01068 {
01069         register FTSENT *t;
01070         register dev_t dev;
01071         register ino_t ino;
01072         struct stat *sbp, sb;
01073         int saved_errno;
01074 
01075         /* If user needs stat info, stat buffer already allocated. */
01076         sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp;
01077 
01078 #if defined FTS_WHITEOUT && 0
01079         /* check for whiteout */
01080         if (p->fts_flags & FTS_ISW) {
01081                 if (sbp != &sb) {
01082                         memset(sbp, '\0', sizeof (*sbp));
01083                         sbp->st_mode = S_IFWHT;
01084                 }
01085                 return (FTS_W);
01086        }
01087 #endif
01088 
01089         /*
01090          * If doing a logical walk, or application requested FTS_FOLLOW, do
01091          * a stat(2).  If that fails, check for a non-existent symlink.  If
01092          * fail, set the errno from the stat call.
01093          */
01094         if (ISSET(FTS_LOGICAL) || follow) {
01095                 if ((*sp->fts_stat) (p->fts_accpath, sbp)) {
01096                         saved_errno = errno;
01097                         if (!(*sp->fts_lstat) (p->fts_accpath, sbp)) {
01098                                 __set_errno (0);
01099                                 return (FTS_SLNONE);
01100                         }
01101                         p->fts_errno = saved_errno;
01102                         goto err;
01103                 }
01104         } else if ((*sp->fts_lstat) (p->fts_accpath, sbp)) {
01105                 p->fts_errno = errno;
01106 err:            memset(sbp, 0, sizeof(*sbp));
01107                 return (FTS_NS);
01108         }
01109 
01110         if (S_ISDIR(sbp->st_mode)) {
01111                 /*
01112                  * Set the device/inode.  Used to find cycles and check for
01113                  * crossing mount points.  Also remember the link count, used
01114                  * in fts_build to limit the number of stat calls.  It is
01115                  * understood that these fields are only referenced if fts_info
01116                  * is set to FTS_D.
01117                  */
01118                 dev = p->fts_dev = sbp->st_dev;
01119                 ino = p->fts_ino = sbp->st_ino;
01120                 p->fts_nlink = sbp->st_nlink;
01121 
01122                 if (ISDOT(p->fts_name))
01123                         return (FTS_DOT);
01124 
01125                 /*
01126                  * Cycle detection is done by brute force when the directory
01127                  * is first encountered.  If the tree gets deep enough or the
01128                  * number of symbolic links to directories is high enough,
01129                  * something faster might be worthwhile.
01130                  */
01131                 for (t = p->fts_parent;
01132                     t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent)
01133                         if (ino == t->fts_ino && dev == t->fts_dev) {
01134                                 p->fts_cycle = t;
01135                                 return (FTS_DC);
01136                         }
01137                 return (FTS_D);
01138         }
01139         if (S_ISLNK(sbp->st_mode))
01140                 return (FTS_SL);
01141         if (S_ISREG(sbp->st_mode))
01142                 return (FTS_F);
01143         return (FTS_DEFAULT);
01144 }
01145 
01146 static FTSENT *
01147 fts_sort(FTS * sp, FTSENT * head, int nitems)
01148 {
01149         register FTSENT **ap, *p;
01150 
01151         /*
01152          * Construct an array of pointers to the structures and call qsort(3).
01153          * Reassemble the array in the order returned by qsort.  If unable to
01154          * sort for memory reasons, return the directory entries in their
01155          * current order.  Allocate enough space for the current needs plus
01156          * 40 so don't realloc one entry at a time.
01157          */
01158         if (nitems > sp->fts_nitems) {
01159                 struct _ftsent **a;
01160 
01161                 sp->fts_nitems = nitems + 40;
01162                 if ((a = realloc(sp->fts_array,
01163                     (size_t)(sp->fts_nitems * sizeof(*sp->fts_array)))) == NULL)
01164                 {
01165                         free(sp->fts_array);
01166                         sp->fts_array = NULL;
01167                         sp->fts_nitems = 0;
01168                         return (head);
01169                 }
01170                 sp->fts_array = a;
01171         }
01172         for (ap = sp->fts_array, p = head; p != NULL; p = p->fts_link)
01173                 *ap++ = p;
01174         qsort((void *)sp->fts_array, nitems, sizeof(*sp->fts_array),
01175                 sp->fts_compar);
01176         for (head = *(ap = sp->fts_array); --nitems; ++ap)
01177                 ap[0]->fts_link = ap[1];
01178         ap[0]->fts_link = NULL;
01179         return (head);
01180 }
01181 
01182 static FTSENT *
01183 fts_alloc(FTS * sp, const char * name, int namelen)
01184 {
01185         register FTSENT *p;
01186         size_t len;
01187 
01188         /*
01189          * The file name is a variable length array and no stat structure is
01190          * necessary if the user has set the nostat bit.  Allocate the FTSENT
01191          * structure, the file name and the stat structure in one chunk, but
01192          * be careful that the stat structure is reasonably aligned.  Since the
01193          * fts_name field is declared to be of size 1, the fts_name pointer is
01194          * namelen + 2 before the first possible address of the stat structure.
01195          */
01196         len = sizeof(*p) + namelen;
01197         if (!ISSET(FTS_NOSTAT))
01198                 len += sizeof(*p->fts_statp) + ALIGNBYTES;
01199         if ((p = malloc(len)) == NULL)
01200                 return (NULL);
01201         memset(p, 0, sizeof(*p));
01202         p->fts_symfd = -1;
01203 
01204         /* Copy the name and guarantee NUL termination. */
01205         memmove(p->fts_name, name, namelen);
01206         p->fts_name[namelen] = '\0';
01207 
01208         if (!ISSET(FTS_NOSTAT))
01209                 p->fts_statp = (struct stat *)ALIGN(p->fts_name + namelen + 2);
01210         p->fts_namelen = namelen;
01211         p->fts_path = sp->fts_path;
01212         p->fts_errno = 0;
01213         p->fts_flags = 0;
01214         p->fts_instr = FTS_NOINSTR;
01215         p->fts_number = 0;
01216         p->fts_pointer = NULL;
01217         return (p);
01218 }
01219 
01220 static void
01221 fts_lfree(FTSENT * head)
01222 {
01223         register FTSENT *p;
01224 
01225         /* Free a linked list of structures. */
01226         while ((p = head)) {
01227                 head = head->fts_link;
01228                 free(p);
01229         }
01230 }
01231 
01232 /*
01233  * Allow essentially unlimited paths; find, rm, ls should all work on any tree.
01234  * Most systems will allow creation of paths much longer than MAXPATHLEN, even
01235  * though the kernel won't resolve them.  Add the size (not just what's needed)
01236  * plus 256 bytes so don't realloc the path 2 bytes at a time.
01237  */
01238 static int
01239 fts_palloc(FTS * sp, size_t more)
01240 {
01241         char *p;
01242 
01243         sp->fts_pathlen += more + 256;
01244         /*
01245          * Check for possible wraparound.  In an FTS, fts_pathlen is
01246          * a signed int but in an FTSENT it is an unsigned short.
01247          * We limit fts_pathlen to USHRT_MAX to be safe in both cases.
01248          */
01249         if (sp->fts_pathlen < 0 || sp->fts_pathlen >= USHRT_MAX) {
01250                 if (sp->fts_path)
01251                         free(sp->fts_path);
01252                 sp->fts_path = NULL;
01253                 __set_errno (ENAMETOOLONG);
01254                 return (1);
01255         }
01256         p = realloc(sp->fts_path, sp->fts_pathlen);
01257         if (p == NULL) {
01258                 free(sp->fts_path);
01259                 sp->fts_path = NULL;
01260                 return 1;
01261         }
01262         sp->fts_path = p;
01263         return 0;
01264 }
01265 
01266 /*
01267  * When the path is realloc'd, have to fix all of the pointers in structures
01268  * already returned.
01269  */
01270 static void
01271 fts_padjust(FTS * sp, FTSENT * head)
01272 {
01273         FTSENT *p;
01274         char *addr = sp->fts_path;
01275 
01276 #define ADJUST(p) do {                                                  \
01277         if ((p)->fts_accpath != (p)->fts_name) {                        \
01278                 (p)->fts_accpath =                                      \
01279                     (char *)addr + ((p)->fts_accpath - (p)->fts_path);  \
01280         }                                                               \
01281         (p)->fts_path = addr;                                           \
01282 } while (0)
01283         /* Adjust the current set of children. */
01284         for (p = sp->fts_child; p != NULL; p = p->fts_link)
01285                 ADJUST(p);
01286 
01287         /* Adjust the rest of the tree, including the current level. */
01288         for (p = head; p->fts_level >= FTS_ROOTLEVEL;) {
01289                 ADJUST(p);
01290                 p = p->fts_link ? p->fts_link : p->fts_parent;
01291         }
01292 }
01293 
01294 static size_t
01295 fts_maxarglen(char * const * argv)
01296 {
01297         size_t len, max;
01298 
01299         for (max = 0; *argv; ++argv)
01300                 if ((len = strlen(*argv)) > max)
01301                         max = len;
01302         return (max + 1);
01303 }
01304 
01305 /*
01306  * Change to dir specified by fd or p->fts_accpath without getting
01307  * tricked by someone changing the world out from underneath us.
01308  * Assumes p->fts_dev and p->fts_ino are filled in.
01309  */
01310 static int
01311 fts_safe_changedir(FTS * sp, FTSENT * p, int fd, const char * path)
01312 {
01313         int ret, oerrno, newfd;
01314         struct stat64 sb;
01315 
01316         newfd = fd;
01317         if (ISSET(FTS_NOCHDIR))
01318                 return (0);
01319 
01320         /* Permit open(2) on file:// prefixed URI paths. */
01321         /* XXX todo: use Open(2), which is Chroot(2) path invariant. */
01322         /* XXX todo: add Fts(3) options to disable the hackery? */
01323         {       const char * lpath = NULL;
01324                 int ut = urlPath(path, &lpath);
01325                 if (ut == URL_IS_PATH) path = lpath;
01326         }
01327 
01328         if (fd < 0 && (newfd = __open(path, O_RDONLY, 0)) < 0)
01329                 return (-1);
01330 /*@-sysunrecog -unrecog @*/
01331         if (__fxstat64(_STAT_VER, newfd, &sb)) {
01332                 ret = -1;
01333                 goto bail;
01334         }
01335 /*@=sysunrecog =unrecog @*/
01336         if (p->fts_dev != sb.st_dev || p->fts_ino != sb.st_ino) {
01337                 __set_errno (ENOENT);           /* disinformation */
01338                 ret = -1;
01339                 goto bail;
01340         }
01341         ret = __fchdir(newfd);
01342 bail:
01343         oerrno = errno;
01344         if (fd < 0)
01345                 (void)__close(newfd);
01346         __set_errno (oerrno);
01347         return (ret);
01348 }
01349 /*@=dependenttrans =nullpass =retalias =usereleased @*/