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