rpmio/fts.c

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

Generated on Fri Oct 12 08:44:54 2007 for rpm by  doxygen 1.5.2