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

Generated on Mon Aug 3 15:19:00 2009 for rpm by  doxygen 1.5.1