rpm 5.2.1

- Main Page
- Related Pages
- Modules
- Data Structures
- Files

- File List
- Globals

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 @*/