1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate 27*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*0Sstevel@tonic-gate 29*0Sstevel@tonic-gate /* Copyright (c) 1988 AT&T */ 30*0Sstevel@tonic-gate /* All Rights Reserved */ 31*0Sstevel@tonic-gate 32*0Sstevel@tonic-gate 33*0Sstevel@tonic-gate /* 34*0Sstevel@tonic-gate * nftw - new file tree walk 35*0Sstevel@tonic-gate * 36*0Sstevel@tonic-gate * int nftw(char *path, int (*fn)(), int depth, int flags); 37*0Sstevel@tonic-gate * 38*0Sstevel@tonic-gate * Derived from System V ftw() by David Korn 39*0Sstevel@tonic-gate * 40*0Sstevel@tonic-gate * nftw visits each file and directory in the tree starting at 41*0Sstevel@tonic-gate * path. It uses the generic directory reading library so it works 42*0Sstevel@tonic-gate * for any file system type. The flags field is used to specify: 43*0Sstevel@tonic-gate * FTW_PHYS Physical walk, does not follow symblolic links 44*0Sstevel@tonic-gate * Otherwise, nftw will follow links but will not 45*0Sstevel@tonic-gate * walk down any path the crosses itself. 46*0Sstevel@tonic-gate * FTW_MOUNT The walk will not cross a mount point. 47*0Sstevel@tonic-gate * FTW_DEPTH All subdirectories will be visited before the 48*0Sstevel@tonic-gate * directory itself. 49*0Sstevel@tonic-gate * FTW_CHDIR The walk will change to each directory before 50*0Sstevel@tonic-gate * reading it. This is faster but core dumps 51*0Sstevel@tonic-gate * may not get generated. 52*0Sstevel@tonic-gate * 53*0Sstevel@tonic-gate * The following flags are private, and are used by the find 54*0Sstevel@tonic-gate * utility: 55*0Sstevel@tonic-gate * FTW_ANYERR Call the callback function and return 56*0Sstevel@tonic-gate * FTW_NS on any stat failure, not just 57*0Sstevel@tonic-gate * lack of permission. 58*0Sstevel@tonic-gate * FTW_HOPTION Use stat the first time the walk 59*0Sstevel@tonic-gate * function is called, regardless of 60*0Sstevel@tonic-gate * whether or not FTW_PHYS is specified. 61*0Sstevel@tonic-gate * 62*0Sstevel@tonic-gate * fn is called with four arguments at each file and directory. 63*0Sstevel@tonic-gate * The first argument is the pathname of the object, the second 64*0Sstevel@tonic-gate * is a pointer to the stat buffer and the third is an integer 65*0Sstevel@tonic-gate * giving additional information as follows: 66*0Sstevel@tonic-gate * 67*0Sstevel@tonic-gate * FTW_F The object is a file. 68*0Sstevel@tonic-gate * FTW_D The object is a directory. 69*0Sstevel@tonic-gate * FTW_DP The object is a directory and subdirectories 70*0Sstevel@tonic-gate * have been visited. 71*0Sstevel@tonic-gate * FTW_SL The object is a symbolic link. 72*0Sstevel@tonic-gate * FTW_SLN The object is a symbolic link pointing at a 73*0Sstevel@tonic-gate * non-existing file. 74*0Sstevel@tonic-gate * FTW_DNR The object is a directory that cannot be read. 75*0Sstevel@tonic-gate * fn will not be called for any of its descendants. 76*0Sstevel@tonic-gate * FTW_NS Stat failed on the object because of lack of 77*0Sstevel@tonic-gate * appropriate permission. The stat buffer passed to fn 78*0Sstevel@tonic-gate * is undefined. Stat failure for any reason is 79*0Sstevel@tonic-gate * considered an error and nftw will return -1. 80*0Sstevel@tonic-gate * The fourth argument is a struct FTW* which contains the depth 81*0Sstevel@tonic-gate * and the offset into pathname to the base name. 82*0Sstevel@tonic-gate * If fn returns nonzero, nftw returns this value to its caller. 83*0Sstevel@tonic-gate * 84*0Sstevel@tonic-gate * depth limits the number of open directories that ftw uses 85*0Sstevel@tonic-gate * before it starts recycling file descriptors. In general, 86*0Sstevel@tonic-gate * a file descriptor is used for each level. 87*0Sstevel@tonic-gate * 88*0Sstevel@tonic-gate */ 89*0Sstevel@tonic-gate 90*0Sstevel@tonic-gate #include <sys/feature_tests.h> 91*0Sstevel@tonic-gate 92*0Sstevel@tonic-gate #if !defined(_LP64) && _FILE_OFFSET_BITS == 64 93*0Sstevel@tonic-gate #pragma weak nftw64 = _nftw64 94*0Sstevel@tonic-gate #define _nftw _nftw64 95*0Sstevel@tonic-gate #define fstat64 _fstat64 96*0Sstevel@tonic-gate #define lstat64 _lstat64 97*0Sstevel@tonic-gate #define readdir64 _readdir64 98*0Sstevel@tonic-gate #define stat64 _stat64 99*0Sstevel@tonic-gate #else 100*0Sstevel@tonic-gate #pragma weak nftw = _nftw 101*0Sstevel@tonic-gate #define fstat _fstat 102*0Sstevel@tonic-gate #define lstat _lstat 103*0Sstevel@tonic-gate #define readdir _readdir 104*0Sstevel@tonic-gate #define stat _stat 105*0Sstevel@tonic-gate #endif /* !_LP64 && _FILE_OFFSET_BITS == 64 */ 106*0Sstevel@tonic-gate 107*0Sstevel@tonic-gate #define chdir _chdir 108*0Sstevel@tonic-gate #define closedir _closedir 109*0Sstevel@tonic-gate #define fchdir _fchdir 110*0Sstevel@tonic-gate #define fprintf _fprintf 111*0Sstevel@tonic-gate #define getcwd _getcwd 112*0Sstevel@tonic-gate #define opendir _opendir 113*0Sstevel@tonic-gate #define seekdir _seekdir 114*0Sstevel@tonic-gate #define telldir _telldir 115*0Sstevel@tonic-gate 116*0Sstevel@tonic-gate #include "lint.h" 117*0Sstevel@tonic-gate #include <mtlib.h> 118*0Sstevel@tonic-gate #include <sys/types.h> 119*0Sstevel@tonic-gate #include <sys/stat.h> 120*0Sstevel@tonic-gate #include <dirent.h> 121*0Sstevel@tonic-gate #include <errno.h> 122*0Sstevel@tonic-gate #include <limits.h> 123*0Sstevel@tonic-gate #include <ftw.h> 124*0Sstevel@tonic-gate #include <stdlib.h> 125*0Sstevel@tonic-gate #include <string.h> 126*0Sstevel@tonic-gate #include <unistd.h> 127*0Sstevel@tonic-gate #include <thread.h> 128*0Sstevel@tonic-gate #include <synch.h> 129*0Sstevel@tonic-gate #include <stdio.h> 130*0Sstevel@tonic-gate 131*0Sstevel@tonic-gate #ifndef PATH_MAX 132*0Sstevel@tonic-gate #define PATH_MAX 1023 133*0Sstevel@tonic-gate #endif 134*0Sstevel@tonic-gate 135*0Sstevel@tonic-gate /* 136*0Sstevel@tonic-gate * Local variables (used to be static local). 137*0Sstevel@tonic-gate * Putting them into a structure that is passed 138*0Sstevel@tonic-gate * around makes nftw() MT-safe with no locking required. 139*0Sstevel@tonic-gate */ 140*0Sstevel@tonic-gate struct Var { 141*0Sstevel@tonic-gate char *fullpath; 142*0Sstevel@tonic-gate char *tmppath; 143*0Sstevel@tonic-gate int curflags; 144*0Sstevel@tonic-gate dev_t cur_mount; 145*0Sstevel@tonic-gate struct FTW state; 146*0Sstevel@tonic-gate int walklevel; 147*0Sstevel@tonic-gate int (*statf)(const char *, struct stat *); 148*0Sstevel@tonic-gate int (*savedstatf)(const char *, struct stat *); 149*0Sstevel@tonic-gate }; 150*0Sstevel@tonic-gate 151*0Sstevel@tonic-gate struct Save { 152*0Sstevel@tonic-gate struct Save *last; 153*0Sstevel@tonic-gate DIR *fd; 154*0Sstevel@tonic-gate char *comp; 155*0Sstevel@tonic-gate long here; 156*0Sstevel@tonic-gate dev_t dev; 157*0Sstevel@tonic-gate ino_t inode; 158*0Sstevel@tonic-gate }; 159*0Sstevel@tonic-gate 160*0Sstevel@tonic-gate static int oldclose(struct Save *); 161*0Sstevel@tonic-gate 162*0Sstevel@tonic-gate /* 163*0Sstevel@tonic-gate * This is the recursive walker. 164*0Sstevel@tonic-gate */ 165*0Sstevel@tonic-gate static int 166*0Sstevel@tonic-gate walk(char *component, 167*0Sstevel@tonic-gate int (*fn)(const char *, const struct stat *, int, struct FTW *), 168*0Sstevel@tonic-gate int depth, struct Save *last, struct Var *vp) 169*0Sstevel@tonic-gate { 170*0Sstevel@tonic-gate struct stat statb; 171*0Sstevel@tonic-gate char *p; 172*0Sstevel@tonic-gate int type; 173*0Sstevel@tonic-gate char *comp; 174*0Sstevel@tonic-gate struct dirent *dir; 175*0Sstevel@tonic-gate char *q; 176*0Sstevel@tonic-gate int rc = 0; 177*0Sstevel@tonic-gate int val = -1; 178*0Sstevel@tonic-gate int cdval = -1; 179*0Sstevel@tonic-gate int oldbase; 180*0Sstevel@tonic-gate int skip; 181*0Sstevel@tonic-gate struct Save this; 182*0Sstevel@tonic-gate 183*0Sstevel@tonic-gate this.last = last; 184*0Sstevel@tonic-gate this.fd = 0; 185*0Sstevel@tonic-gate if ((vp->curflags & FTW_CHDIR) && last) 186*0Sstevel@tonic-gate comp = last->comp; 187*0Sstevel@tonic-gate else 188*0Sstevel@tonic-gate comp = vp->tmppath; 189*0Sstevel@tonic-gate 190*0Sstevel@tonic-gate if (vp->savedstatf == NULL) 191*0Sstevel@tonic-gate vp->savedstatf = vp->statf; 192*0Sstevel@tonic-gate 193*0Sstevel@tonic-gate if ((vp->walklevel++ == 0) && (vp->curflags & FTW_HOPTION)) 194*0Sstevel@tonic-gate vp->statf = stat; 195*0Sstevel@tonic-gate else 196*0Sstevel@tonic-gate vp->statf = vp->savedstatf; 197*0Sstevel@tonic-gate 198*0Sstevel@tonic-gate /* 199*0Sstevel@tonic-gate * Determine the type of the component. 200*0Sstevel@tonic-gate */ 201*0Sstevel@tonic-gate if ((*vp->statf)(comp, &statb) >= 0) { 202*0Sstevel@tonic-gate if ((statb.st_mode & S_IFMT) == S_IFDIR) { 203*0Sstevel@tonic-gate type = FTW_D; 204*0Sstevel@tonic-gate if (depth <= 1) 205*0Sstevel@tonic-gate (void) oldclose(last); 206*0Sstevel@tonic-gate if ((this.fd = opendir(comp)) == 0) { 207*0Sstevel@tonic-gate if (errno == EMFILE && oldclose(last) && 208*0Sstevel@tonic-gate (this.fd = opendir(comp)) != 0) { 209*0Sstevel@tonic-gate depth = 1; 210*0Sstevel@tonic-gate } else { 211*0Sstevel@tonic-gate type = FTW_DNR; 212*0Sstevel@tonic-gate goto fail; 213*0Sstevel@tonic-gate } 214*0Sstevel@tonic-gate } 215*0Sstevel@tonic-gate if (statb.st_fstype[0] == 'a' && 216*0Sstevel@tonic-gate strcmp(statb.st_fstype, "autofs") == 0) { 217*0Sstevel@tonic-gate /* 218*0Sstevel@tonic-gate * this dir is on autofs 219*0Sstevel@tonic-gate */ 220*0Sstevel@tonic-gate if (fstat(this.fd->dd_fd, &statb) < 0) { 221*0Sstevel@tonic-gate (void) closedir(this.fd); 222*0Sstevel@tonic-gate type = FTW_NS; 223*0Sstevel@tonic-gate goto fail; 224*0Sstevel@tonic-gate } 225*0Sstevel@tonic-gate } 226*0Sstevel@tonic-gate } else if ((statb.st_mode & S_IFMT) == S_IFLNK) { 227*0Sstevel@tonic-gate type = FTW_SL; 228*0Sstevel@tonic-gate } else { 229*0Sstevel@tonic-gate type = FTW_F; 230*0Sstevel@tonic-gate } 231*0Sstevel@tonic-gate } else if ((vp->curflags & FTW_ANYERR) && errno != ENOENT) { 232*0Sstevel@tonic-gate /* 233*0Sstevel@tonic-gate * If FTW_ANYERR is specified, then a stat error 234*0Sstevel@tonic-gate * other than ENOENT automatically results in 235*0Sstevel@tonic-gate * failure. This allows the callback function 236*0Sstevel@tonic-gate * to properly handle ENAMETOOLONG and ELOOP and 237*0Sstevel@tonic-gate * things of that nature, that would be masked 238*0Sstevel@tonic-gate * by calling lstat before failing. 239*0Sstevel@tonic-gate */ 240*0Sstevel@tonic-gate type = FTW_NS; 241*0Sstevel@tonic-gate goto fail; 242*0Sstevel@tonic-gate } else { 243*0Sstevel@tonic-gate /* 244*0Sstevel@tonic-gate * Statf has failed. If stat was used instead of lstat, 245*0Sstevel@tonic-gate * try using lstat. If lstat doesn't fail, "comp" 246*0Sstevel@tonic-gate * must be a symbolic link pointing to a non-existent 247*0Sstevel@tonic-gate * file. Such a symbolic link should be ignored. 248*0Sstevel@tonic-gate * Also check the file type, if possible, for symbolic 249*0Sstevel@tonic-gate * link. 250*0Sstevel@tonic-gate */ 251*0Sstevel@tonic-gate if ((vp->statf == stat) && (lstat(comp, &statb) >= 0) && 252*0Sstevel@tonic-gate ((statb.st_mode & S_IFMT) == S_IFLNK)) { 253*0Sstevel@tonic-gate 254*0Sstevel@tonic-gate /* 255*0Sstevel@tonic-gate * Ignore bad symbolic link, let "fn" 256*0Sstevel@tonic-gate * report it. 257*0Sstevel@tonic-gate */ 258*0Sstevel@tonic-gate 259*0Sstevel@tonic-gate errno = ENOENT; 260*0Sstevel@tonic-gate type = FTW_SLN; 261*0Sstevel@tonic-gate } else { 262*0Sstevel@tonic-gate type = FTW_NS; 263*0Sstevel@tonic-gate fail: 264*0Sstevel@tonic-gate /* 265*0Sstevel@tonic-gate * if FTW_ANYERR is set in flags, we call 266*0Sstevel@tonic-gate * the user function with FTW_NS set, regardless 267*0Sstevel@tonic-gate * of the reason stat failed. 268*0Sstevel@tonic-gate */ 269*0Sstevel@tonic-gate if (!(vp->curflags & FTW_ANYERR)) 270*0Sstevel@tonic-gate if (errno != EACCES) 271*0Sstevel@tonic-gate return (-1); 272*0Sstevel@tonic-gate } 273*0Sstevel@tonic-gate } 274*0Sstevel@tonic-gate 275*0Sstevel@tonic-gate /* 276*0Sstevel@tonic-gate * If the walk is not supposed to cross a mount point, 277*0Sstevel@tonic-gate * and it did, get ready to return. 278*0Sstevel@tonic-gate */ 279*0Sstevel@tonic-gate if ((vp->curflags & FTW_MOUNT) && type != FTW_NS && 280*0Sstevel@tonic-gate statb.st_dev != vp->cur_mount) 281*0Sstevel@tonic-gate goto quit; 282*0Sstevel@tonic-gate vp->state.quit = 0; 283*0Sstevel@tonic-gate 284*0Sstevel@tonic-gate /* 285*0Sstevel@tonic-gate * If current component is not a directory, call user 286*0Sstevel@tonic-gate * specified function and get ready to return. 287*0Sstevel@tonic-gate */ 288*0Sstevel@tonic-gate if (type != FTW_D || (vp->curflags & FTW_DEPTH) == 0) 289*0Sstevel@tonic-gate rc = (*fn)(vp->tmppath, &statb, type, &vp->state); 290*0Sstevel@tonic-gate if (rc > 0) 291*0Sstevel@tonic-gate val = rc; 292*0Sstevel@tonic-gate skip = (vp->state.quit & FTW_SKD); 293*0Sstevel@tonic-gate if (rc != 0 || type != FTW_D || (vp->state.quit & FTW_PRUNE)) 294*0Sstevel@tonic-gate goto quit; 295*0Sstevel@tonic-gate 296*0Sstevel@tonic-gate if (vp->tmppath[0] != '\0' && component[-1] != '/') 297*0Sstevel@tonic-gate *component++ = '/'; 298*0Sstevel@tonic-gate if (vp->curflags & FTW_CHDIR) { 299*0Sstevel@tonic-gate struct stat statb2; 300*0Sstevel@tonic-gate 301*0Sstevel@tonic-gate *component = 0; 302*0Sstevel@tonic-gate /* 303*0Sstevel@tonic-gate * Security check (there is a window between 304*0Sstevel@tonic-gate * (*vp->statf)() and opendir() above). 305*0Sstevel@tonic-gate */ 306*0Sstevel@tonic-gate if ((vp->curflags & FTW_PHYS) && 307*0Sstevel@tonic-gate (fstat(this.fd->dd_fd, &statb2) < 0 || 308*0Sstevel@tonic-gate statb2.st_ino != statb.st_ino || 309*0Sstevel@tonic-gate statb2.st_dev != statb.st_dev)) { 310*0Sstevel@tonic-gate errno = EAGAIN; 311*0Sstevel@tonic-gate rc = -1; 312*0Sstevel@tonic-gate goto quit; 313*0Sstevel@tonic-gate } 314*0Sstevel@tonic-gate 315*0Sstevel@tonic-gate if ((cdval = fchdir(this.fd->dd_fd)) >= 0) { 316*0Sstevel@tonic-gate this.comp = component; 317*0Sstevel@tonic-gate } else { 318*0Sstevel@tonic-gate type = FTW_DNR; 319*0Sstevel@tonic-gate rc = (*fn)(vp->tmppath, &statb, type, &vp->state); 320*0Sstevel@tonic-gate goto quit; 321*0Sstevel@tonic-gate } 322*0Sstevel@tonic-gate } 323*0Sstevel@tonic-gate 324*0Sstevel@tonic-gate /* 325*0Sstevel@tonic-gate * If the walk has followed a symbolic link, traverse 326*0Sstevel@tonic-gate * the walk back to make sure there is not a loop. 327*0Sstevel@tonic-gate * 328*0Sstevel@tonic-gate * XXX - may need to look at this 329*0Sstevel@tonic-gate * There's code to check for cycles, but only for FTW_PHYS 330*0Sstevel@tonic-gate * (find -L flag). However, all directories should be 331*0Sstevel@tonic-gate * checked, even if not following links because of hardlinks 332*0Sstevel@tonic-gate * to directories (not recommended, but can exist). 333*0Sstevel@tonic-gate * 334*0Sstevel@tonic-gate * We might have added AVL tree routines here to store and search 335*0Sstevel@tonic-gate * the inodes and devices, as is done for du/ls/chgrp/chown, 336*0Sstevel@tonic-gate * but libcmdutils is for for internal use only, so we can't 337*0Sstevel@tonic-gate * add it to a public libc function (nftw()). 338*0Sstevel@tonic-gate */ 339*0Sstevel@tonic-gate if ((vp->curflags & FTW_PHYS) == 0) { 340*0Sstevel@tonic-gate struct Save *sp = last; 341*0Sstevel@tonic-gate while (sp) { 342*0Sstevel@tonic-gate /* 343*0Sstevel@tonic-gate * If the same node has already been visited, there 344*0Sstevel@tonic-gate * is a loop. Get ready to return. 345*0Sstevel@tonic-gate */ 346*0Sstevel@tonic-gate if (sp->dev == statb.st_dev && 347*0Sstevel@tonic-gate sp->inode == statb.st_ino) 348*0Sstevel@tonic-gate goto quit; 349*0Sstevel@tonic-gate sp = sp->last; 350*0Sstevel@tonic-gate } 351*0Sstevel@tonic-gate } 352*0Sstevel@tonic-gate this.dev = statb.st_dev; 353*0Sstevel@tonic-gate this.inode = statb.st_ino; 354*0Sstevel@tonic-gate oldbase = vp->state.base; 355*0Sstevel@tonic-gate vp->state.base = (int)(component - vp->tmppath); 356*0Sstevel@tonic-gate while (dir = readdir(this.fd)) { 357*0Sstevel@tonic-gate if (dir->d_ino == 0) 358*0Sstevel@tonic-gate continue; 359*0Sstevel@tonic-gate q = dir->d_name; 360*0Sstevel@tonic-gate if (*q == '.') { 361*0Sstevel@tonic-gate if (q[1] == 0) 362*0Sstevel@tonic-gate continue; 363*0Sstevel@tonic-gate else if (q[1] == '.' && q[2] == 0) 364*0Sstevel@tonic-gate continue; 365*0Sstevel@tonic-gate } 366*0Sstevel@tonic-gate p = component; 367*0Sstevel@tonic-gate while (p < &vp->tmppath[PATH_MAX] && *q != '\0') 368*0Sstevel@tonic-gate *p++ = *q++; 369*0Sstevel@tonic-gate *p = '\0'; 370*0Sstevel@tonic-gate vp->state.level++; 371*0Sstevel@tonic-gate 372*0Sstevel@tonic-gate /* Call walk() recursively. */ 373*0Sstevel@tonic-gate rc = walk(p, fn, depth-1, &this, vp); 374*0Sstevel@tonic-gate vp->state.level--; 375*0Sstevel@tonic-gate if (this.fd == 0) { 376*0Sstevel@tonic-gate *component = 0; 377*0Sstevel@tonic-gate if (vp->curflags & FTW_CHDIR) { 378*0Sstevel@tonic-gate this.fd = opendir("."); 379*0Sstevel@tonic-gate } else { 380*0Sstevel@tonic-gate this.fd = opendir(comp); 381*0Sstevel@tonic-gate } 382*0Sstevel@tonic-gate if (this.fd == 0) { 383*0Sstevel@tonic-gate rc = -1; 384*0Sstevel@tonic-gate goto quit; 385*0Sstevel@tonic-gate } 386*0Sstevel@tonic-gate seekdir(this.fd, this.here); 387*0Sstevel@tonic-gate } 388*0Sstevel@tonic-gate if (rc != 0) { 389*0Sstevel@tonic-gate if (errno == ENOENT) { 390*0Sstevel@tonic-gate (void) fprintf(stderr, "cannot open %s: %s\n", 391*0Sstevel@tonic-gate vp->tmppath, strerror(errno)); 392*0Sstevel@tonic-gate val = rc; 393*0Sstevel@tonic-gate continue; 394*0Sstevel@tonic-gate } 395*0Sstevel@tonic-gate goto quit; /* this seems extreme */ 396*0Sstevel@tonic-gate } 397*0Sstevel@tonic-gate } 398*0Sstevel@tonic-gate vp->state.base = oldbase; 399*0Sstevel@tonic-gate *--component = 0; 400*0Sstevel@tonic-gate type = FTW_DP; 401*0Sstevel@tonic-gate if ((vp->tmppath[0] != '\0') && (vp->curflags & FTW_DEPTH) && !skip) 402*0Sstevel@tonic-gate rc = (*fn)(vp->tmppath, &statb, type, &vp->state); 403*0Sstevel@tonic-gate quit: 404*0Sstevel@tonic-gate if (cdval >= 0 && last) { 405*0Sstevel@tonic-gate /* try to change back to previous directory */ 406*0Sstevel@tonic-gate if (last->fd != NULL) { 407*0Sstevel@tonic-gate if (fchdir(last->fd->dd_fd) < 0) { 408*0Sstevel@tonic-gate rc = -1; 409*0Sstevel@tonic-gate } 410*0Sstevel@tonic-gate } else { 411*0Sstevel@tonic-gate if ((cdval = chdir("..")) >= 0) { 412*0Sstevel@tonic-gate if ((*vp->statf)(".", &statb) < 0 || 413*0Sstevel@tonic-gate statb.st_ino != last->inode || 414*0Sstevel@tonic-gate statb.st_dev != last->dev) 415*0Sstevel@tonic-gate cdval = -1; 416*0Sstevel@tonic-gate } 417*0Sstevel@tonic-gate *comp = 0; 418*0Sstevel@tonic-gate if (cdval < 0) { 419*0Sstevel@tonic-gate if (chdir(vp->fullpath) < 0) { 420*0Sstevel@tonic-gate rc = -1; 421*0Sstevel@tonic-gate } else { 422*0Sstevel@tonic-gate /* Security check */ 423*0Sstevel@tonic-gate if ((vp->curflags & FTW_PHYS) && 424*0Sstevel@tonic-gate ((*vp->statf)(".", &statb) < 0 || 425*0Sstevel@tonic-gate statb.st_ino != last->inode || 426*0Sstevel@tonic-gate statb.st_dev != last->dev)) { 427*0Sstevel@tonic-gate errno = EAGAIN; 428*0Sstevel@tonic-gate rc = -1; 429*0Sstevel@tonic-gate } 430*0Sstevel@tonic-gate } 431*0Sstevel@tonic-gate } 432*0Sstevel@tonic-gate } 433*0Sstevel@tonic-gate } 434*0Sstevel@tonic-gate if (this.fd) 435*0Sstevel@tonic-gate (void) closedir(this.fd); 436*0Sstevel@tonic-gate if (val > rc) 437*0Sstevel@tonic-gate return (val); 438*0Sstevel@tonic-gate else 439*0Sstevel@tonic-gate return (rc); 440*0Sstevel@tonic-gate } 441*0Sstevel@tonic-gate 442*0Sstevel@tonic-gate int 443*0Sstevel@tonic-gate _nftw(const char *path, 444*0Sstevel@tonic-gate int (*fn)(const char *, const struct stat *, int, struct FTW *), 445*0Sstevel@tonic-gate int depth, int flags) 446*0Sstevel@tonic-gate { 447*0Sstevel@tonic-gate struct Var var; 448*0Sstevel@tonic-gate struct stat statb; 449*0Sstevel@tonic-gate char home[2*(PATH_MAX+1)]; 450*0Sstevel@tonic-gate int rc = -1; 451*0Sstevel@tonic-gate char *dp; 452*0Sstevel@tonic-gate char *base; 453*0Sstevel@tonic-gate char *endhome; 454*0Sstevel@tonic-gate const char *savepath = path; 455*0Sstevel@tonic-gate int save_errno; 456*0Sstevel@tonic-gate 457*0Sstevel@tonic-gate home[0] = 0; 458*0Sstevel@tonic-gate 459*0Sstevel@tonic-gate /* 460*0Sstevel@tonic-gate * If the walk is going to change directory before 461*0Sstevel@tonic-gate * reading it, save current woring directory. 462*0Sstevel@tonic-gate */ 463*0Sstevel@tonic-gate if (flags & FTW_CHDIR) { 464*0Sstevel@tonic-gate if (getcwd(home, PATH_MAX+1) == 0) 465*0Sstevel@tonic-gate return (-1); 466*0Sstevel@tonic-gate } 467*0Sstevel@tonic-gate endhome = dp = home + strlen(home); 468*0Sstevel@tonic-gate if (*path == '/') 469*0Sstevel@tonic-gate var.fullpath = dp; 470*0Sstevel@tonic-gate else { 471*0Sstevel@tonic-gate *dp++ = '/'; 472*0Sstevel@tonic-gate var.fullpath = home; 473*0Sstevel@tonic-gate } 474*0Sstevel@tonic-gate var.tmppath = dp; 475*0Sstevel@tonic-gate base = dp-1; 476*0Sstevel@tonic-gate while (*path && dp < &var.tmppath[PATH_MAX]) { 477*0Sstevel@tonic-gate *dp = *path; 478*0Sstevel@tonic-gate if (*dp == '/') 479*0Sstevel@tonic-gate base = dp; 480*0Sstevel@tonic-gate dp++, path++; 481*0Sstevel@tonic-gate } 482*0Sstevel@tonic-gate *dp = 0; 483*0Sstevel@tonic-gate var.state.base = (int)(base + 1 - var.tmppath); 484*0Sstevel@tonic-gate if (*path) { 485*0Sstevel@tonic-gate errno = ENAMETOOLONG; 486*0Sstevel@tonic-gate return (-1); 487*0Sstevel@tonic-gate } 488*0Sstevel@tonic-gate var.curflags = flags; 489*0Sstevel@tonic-gate 490*0Sstevel@tonic-gate /* 491*0Sstevel@tonic-gate * If doing a physical walk (not following symbolic link), set 492*0Sstevel@tonic-gate * var.statf to lstat(). Otherwise, set var.statf to stat(). 493*0Sstevel@tonic-gate */ 494*0Sstevel@tonic-gate if ((flags & FTW_PHYS) == 0) 495*0Sstevel@tonic-gate var.statf = stat; 496*0Sstevel@tonic-gate else 497*0Sstevel@tonic-gate var.statf = lstat; 498*0Sstevel@tonic-gate 499*0Sstevel@tonic-gate /* 500*0Sstevel@tonic-gate * If walk is not going to cross a mount point, 501*0Sstevel@tonic-gate * save the current mount point. 502*0Sstevel@tonic-gate */ 503*0Sstevel@tonic-gate if (flags & FTW_MOUNT) { 504*0Sstevel@tonic-gate if ((*var.statf)(savepath, &statb) >= 0) 505*0Sstevel@tonic-gate var.cur_mount = statb.st_dev; 506*0Sstevel@tonic-gate else 507*0Sstevel@tonic-gate goto done; 508*0Sstevel@tonic-gate } 509*0Sstevel@tonic-gate var.state.level = 0; 510*0Sstevel@tonic-gate 511*0Sstevel@tonic-gate /* 512*0Sstevel@tonic-gate * Call walk() which does most of the work. 513*0Sstevel@tonic-gate * walk() uses errno in a rather obtuse way 514*0Sstevel@tonic-gate * so we shield any incoming errno. 515*0Sstevel@tonic-gate */ 516*0Sstevel@tonic-gate save_errno = errno; 517*0Sstevel@tonic-gate errno = 0; 518*0Sstevel@tonic-gate var.savedstatf = NULL; 519*0Sstevel@tonic-gate var.walklevel = 0; 520*0Sstevel@tonic-gate rc = walk(dp, fn, depth, (struct Save *)0, &var); 521*0Sstevel@tonic-gate if (errno == 0) 522*0Sstevel@tonic-gate errno = save_errno; 523*0Sstevel@tonic-gate done: 524*0Sstevel@tonic-gate *endhome = 0; 525*0Sstevel@tonic-gate if (flags & FTW_CHDIR) 526*0Sstevel@tonic-gate (void) chdir(home); 527*0Sstevel@tonic-gate return (rc); 528*0Sstevel@tonic-gate } 529*0Sstevel@tonic-gate 530*0Sstevel@tonic-gate /* 531*0Sstevel@tonic-gate * close the oldest directory. It saves the seek offset. 532*0Sstevel@tonic-gate * return value is 0 unless it was unable to close any descriptor 533*0Sstevel@tonic-gate */ 534*0Sstevel@tonic-gate 535*0Sstevel@tonic-gate static int 536*0Sstevel@tonic-gate oldclose(struct Save *sp) 537*0Sstevel@tonic-gate { 538*0Sstevel@tonic-gate struct Save *spnext; 539*0Sstevel@tonic-gate while (sp) { 540*0Sstevel@tonic-gate spnext = sp->last; 541*0Sstevel@tonic-gate if (spnext == 0 || spnext->fd == 0) 542*0Sstevel@tonic-gate break; 543*0Sstevel@tonic-gate sp = spnext; 544*0Sstevel@tonic-gate } 545*0Sstevel@tonic-gate if (sp == 0 || sp->fd == 0) 546*0Sstevel@tonic-gate return (0); 547*0Sstevel@tonic-gate sp->here = telldir(sp->fd); 548*0Sstevel@tonic-gate (void) closedir(sp->fd); 549*0Sstevel@tonic-gate sp->fd = 0; 550*0Sstevel@tonic-gate return (1); 551*0Sstevel@tonic-gate } 552