Main Page Modules Data Structures File List Data Fields Globals Related Pages

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