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