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