10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 53523Scf46844 * Common Development and Distribution License (the "License"). 63523Scf46844 * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 211219Sraf 220Sstevel@tonic-gate /* 233523Scf46844 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 240Sstevel@tonic-gate * Use is subject to license terms. 250Sstevel@tonic-gate */ 260Sstevel@tonic-gate 270Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 280Sstevel@tonic-gate 290Sstevel@tonic-gate /* Copyright (c) 1988 AT&T */ 300Sstevel@tonic-gate /* All Rights Reserved */ 310Sstevel@tonic-gate 320Sstevel@tonic-gate 330Sstevel@tonic-gate /* 340Sstevel@tonic-gate * nftw - new file tree walk 350Sstevel@tonic-gate * 360Sstevel@tonic-gate * int nftw(char *path, int (*fn)(), int depth, int flags); 370Sstevel@tonic-gate * 380Sstevel@tonic-gate * Derived from System V ftw() by David Korn 390Sstevel@tonic-gate * 400Sstevel@tonic-gate * nftw visits each file and directory in the tree starting at 410Sstevel@tonic-gate * path. It uses the generic directory reading library so it works 420Sstevel@tonic-gate * for any file system type. The flags field is used to specify: 433523Scf46844 * FTW_PHYS Physical walk, does not follow symbolic links 440Sstevel@tonic-gate * Otherwise, nftw will follow links but will not 450Sstevel@tonic-gate * walk down any path the crosses itself. 460Sstevel@tonic-gate * FTW_MOUNT The walk will not cross a mount point. 470Sstevel@tonic-gate * FTW_DEPTH All subdirectories will be visited before the 480Sstevel@tonic-gate * directory itself. 490Sstevel@tonic-gate * FTW_CHDIR The walk will change to each directory before 500Sstevel@tonic-gate * reading it. This is faster but core dumps 510Sstevel@tonic-gate * may not get generated. 520Sstevel@tonic-gate * 530Sstevel@tonic-gate * The following flags are private, and are used by the find 540Sstevel@tonic-gate * utility: 550Sstevel@tonic-gate * FTW_ANYERR Call the callback function and return 560Sstevel@tonic-gate * FTW_NS on any stat failure, not just 570Sstevel@tonic-gate * lack of permission. 580Sstevel@tonic-gate * FTW_HOPTION Use stat the first time the walk 590Sstevel@tonic-gate * function is called, regardless of 600Sstevel@tonic-gate * whether or not FTW_PHYS is specified. 613523Scf46844 * FTW_NOLOOP Allow find utility to detect infinite loops created 623523Scf46844 * by both symbolic and hard linked directories. 630Sstevel@tonic-gate * 640Sstevel@tonic-gate * fn is called with four arguments at each file and directory. 650Sstevel@tonic-gate * The first argument is the pathname of the object, the second 660Sstevel@tonic-gate * is a pointer to the stat buffer and the third is an integer 670Sstevel@tonic-gate * giving additional information as follows: 680Sstevel@tonic-gate * 690Sstevel@tonic-gate * FTW_F The object is a file. 700Sstevel@tonic-gate * FTW_D The object is a directory. 710Sstevel@tonic-gate * FTW_DP The object is a directory and subdirectories 720Sstevel@tonic-gate * have been visited. 730Sstevel@tonic-gate * FTW_SL The object is a symbolic link. 740Sstevel@tonic-gate * FTW_SLN The object is a symbolic link pointing at a 750Sstevel@tonic-gate * non-existing file. 760Sstevel@tonic-gate * FTW_DNR The object is a directory that cannot be read. 770Sstevel@tonic-gate * fn will not be called for any of its descendants. 780Sstevel@tonic-gate * FTW_NS Stat failed on the object because of lack of 790Sstevel@tonic-gate * appropriate permission. The stat buffer passed to fn 800Sstevel@tonic-gate * is undefined. Stat failure for any reason is 810Sstevel@tonic-gate * considered an error and nftw will return -1. 823523Scf46844 * The following value is private, and is used by the find utility: 833523Scf46844 * FTW_DL An infinite loop has been detected. 840Sstevel@tonic-gate * The fourth argument is a struct FTW* which contains the depth 850Sstevel@tonic-gate * and the offset into pathname to the base name. 860Sstevel@tonic-gate * If fn returns nonzero, nftw returns this value to its caller. 870Sstevel@tonic-gate * 880Sstevel@tonic-gate * depth limits the number of open directories that ftw uses 890Sstevel@tonic-gate * before it starts recycling file descriptors. In general, 903523Scf46844 * a file descriptor is used for each level. When FTW_CHDIR isn't set, 913523Scf46844 * in order to descend to arbitrary depths, nftw requires 2 file 923523Scf46844 * descriptors to be open during the call to openat(), therefore if 933523Scf46844 * the depth argument is less than 2 nftw will not use openat(), and 943523Scf46844 * it will fail with ENAMETOOLONG if it descends to a directory that 953523Scf46844 * exceeds PATH_MAX. 960Sstevel@tonic-gate * 970Sstevel@tonic-gate */ 980Sstevel@tonic-gate 990Sstevel@tonic-gate #include <sys/feature_tests.h> 1000Sstevel@tonic-gate 1010Sstevel@tonic-gate #if !defined(_LP64) && _FILE_OFFSET_BITS == 64 1020Sstevel@tonic-gate #pragma weak nftw64 = _nftw64 1030Sstevel@tonic-gate #define _nftw _nftw64 1040Sstevel@tonic-gate #define fstat64 _fstat64 1053523Scf46844 #define fstatat64 _fstatat64 1060Sstevel@tonic-gate #define lstat64 _lstat64 1073523Scf46844 #define openat64 _openat64 1080Sstevel@tonic-gate #define readdir64 _readdir64 1090Sstevel@tonic-gate #define stat64 _stat64 1100Sstevel@tonic-gate #else 1110Sstevel@tonic-gate #pragma weak nftw = _nftw 1120Sstevel@tonic-gate #define fstat _fstat 1133523Scf46844 #define fstatat _fstatat 1140Sstevel@tonic-gate #define lstat _lstat 1153523Scf46844 #define openat _openat 1160Sstevel@tonic-gate #define readdir _readdir 1170Sstevel@tonic-gate #define stat _stat 1180Sstevel@tonic-gate #endif /* !_LP64 && _FILE_OFFSET_BITS == 64 */ 1190Sstevel@tonic-gate 1200Sstevel@tonic-gate #define chdir _chdir 1213523Scf46844 #define close _close 1220Sstevel@tonic-gate #define closedir _closedir 1230Sstevel@tonic-gate #define fchdir _fchdir 1243523Scf46844 #define fdopendir _fdopendir 1250Sstevel@tonic-gate #define fprintf _fprintf 1260Sstevel@tonic-gate #define getcwd _getcwd 1270Sstevel@tonic-gate #define opendir _opendir 1280Sstevel@tonic-gate #define seekdir _seekdir 1293523Scf46844 #define strdup _strdup 1301219Sraf #define strerror _strerror 1313523Scf46844 #define strtok_r _strtok_r 1320Sstevel@tonic-gate #define telldir _telldir 1330Sstevel@tonic-gate 1340Sstevel@tonic-gate #include "lint.h" 1350Sstevel@tonic-gate #include <mtlib.h> 1360Sstevel@tonic-gate #include <sys/types.h> 1370Sstevel@tonic-gate #include <sys/stat.h> 1380Sstevel@tonic-gate #include <dirent.h> 1390Sstevel@tonic-gate #include <errno.h> 1400Sstevel@tonic-gate #include <limits.h> 1410Sstevel@tonic-gate #include <ftw.h> 1420Sstevel@tonic-gate #include <stdlib.h> 1430Sstevel@tonic-gate #include <string.h> 1440Sstevel@tonic-gate #include <unistd.h> 1450Sstevel@tonic-gate #include <thread.h> 1460Sstevel@tonic-gate #include <synch.h> 1470Sstevel@tonic-gate #include <stdio.h> 1483523Scf46844 #include <strings.h> 1493523Scf46844 #include <fcntl.h> 1500Sstevel@tonic-gate 1510Sstevel@tonic-gate #ifndef PATH_MAX 1520Sstevel@tonic-gate #define PATH_MAX 1023 1530Sstevel@tonic-gate #endif 1540Sstevel@tonic-gate 1550Sstevel@tonic-gate /* 1560Sstevel@tonic-gate * Local variables (used to be static local). 1570Sstevel@tonic-gate * Putting them into a structure that is passed 1580Sstevel@tonic-gate * around makes nftw() MT-safe with no locking required. 1590Sstevel@tonic-gate */ 1600Sstevel@tonic-gate struct Save { 1610Sstevel@tonic-gate struct Save *last; 1620Sstevel@tonic-gate DIR *fd; 1630Sstevel@tonic-gate char *comp; 1640Sstevel@tonic-gate long here; 1650Sstevel@tonic-gate dev_t dev; 1660Sstevel@tonic-gate ino_t inode; 1670Sstevel@tonic-gate }; 1680Sstevel@tonic-gate 1693523Scf46844 struct Var { 1703523Scf46844 char *home; 1713523Scf46844 size_t len; 1723523Scf46844 char *fullpath; 1733523Scf46844 char *tmppath; 1743523Scf46844 int curflags; 1753523Scf46844 dev_t cur_mount; 1763523Scf46844 struct FTW state; 1773523Scf46844 int walklevel; 178*5302Sth199096 int (*statf)(const char *, struct stat *, struct Save *, int flags); 179*5302Sth199096 int (*savedstatf)(const char *, struct stat *, struct Save *, 180*5302Sth199096 int flags); 1813523Scf46844 DIR *(*opendirf)(const char *); 1823523Scf46844 }; 1833523Scf46844 1840Sstevel@tonic-gate static int oldclose(struct Save *); 185*5302Sth199096 static int cdlstat(const char *, struct stat *, struct Save *, int flags); 186*5302Sth199096 static int cdstat(const char *, struct stat *, struct Save *, int flags); 187*5302Sth199096 static int nocdlstat(const char *, struct stat *, struct Save *, int flags); 188*5302Sth199096 static int nocdstat(const char *, struct stat *, struct Save *, int flags); 1893523Scf46844 static DIR *cdopendir(const char *); 1903523Scf46844 static DIR *nocdopendir(const char *); 1913523Scf46844 static const char *get_unrooted(const char *); 1920Sstevel@tonic-gate 1930Sstevel@tonic-gate /* 1940Sstevel@tonic-gate * This is the recursive walker. 1950Sstevel@tonic-gate */ 1960Sstevel@tonic-gate static int 1970Sstevel@tonic-gate walk(char *component, 1980Sstevel@tonic-gate int (*fn)(const char *, const struct stat *, int, struct FTW *), 1990Sstevel@tonic-gate int depth, struct Save *last, struct Var *vp) 2000Sstevel@tonic-gate { 2010Sstevel@tonic-gate struct stat statb; 2023523Scf46844 char *p, *tmp; 2030Sstevel@tonic-gate int type; 2040Sstevel@tonic-gate char *comp; 2050Sstevel@tonic-gate struct dirent *dir; 2060Sstevel@tonic-gate char *q; 2070Sstevel@tonic-gate int rc = 0; 2080Sstevel@tonic-gate int val = -1; 2090Sstevel@tonic-gate int cdval = -1; 2100Sstevel@tonic-gate int oldbase; 2110Sstevel@tonic-gate int skip; 2120Sstevel@tonic-gate struct Save this; 2133523Scf46844 size_t base_comp, base_component, base_this_comp, base_last_comp; 2143523Scf46844 size_t base_fullpath, base_tmppath; 2150Sstevel@tonic-gate 2160Sstevel@tonic-gate this.last = last; 2170Sstevel@tonic-gate this.fd = 0; 2180Sstevel@tonic-gate if ((vp->curflags & FTW_CHDIR) && last) 2190Sstevel@tonic-gate comp = last->comp; 2200Sstevel@tonic-gate else 2210Sstevel@tonic-gate comp = vp->tmppath; 2220Sstevel@tonic-gate 2230Sstevel@tonic-gate if (vp->savedstatf == NULL) 2240Sstevel@tonic-gate vp->savedstatf = vp->statf; 2250Sstevel@tonic-gate 2263523Scf46844 if ((vp->walklevel++ == 0) && (vp->curflags & FTW_HOPTION)) { 2273523Scf46844 if (((vp->curflags & FTW_CHDIR) == 0) && (depth >= 2)) { 2283523Scf46844 vp->statf = nocdstat; 2293523Scf46844 } else { 2303523Scf46844 vp->statf = cdstat; 2313523Scf46844 } 2323523Scf46844 } else { 2330Sstevel@tonic-gate vp->statf = vp->savedstatf; 2343523Scf46844 } 2350Sstevel@tonic-gate 2360Sstevel@tonic-gate /* 2370Sstevel@tonic-gate * Determine the type of the component. 238*5302Sth199096 * 239*5302Sth199096 * Note that if the component is a trigger mount, this 240*5302Sth199096 * will cause it to load. 2410Sstevel@tonic-gate */ 242*5302Sth199096 if ((*vp->statf)(comp, &statb, last, _AT_TRIGGER) >= 0) { 2430Sstevel@tonic-gate if ((statb.st_mode & S_IFMT) == S_IFDIR) { 2440Sstevel@tonic-gate type = FTW_D; 2450Sstevel@tonic-gate if (depth <= 1) 2460Sstevel@tonic-gate (void) oldclose(last); 2473523Scf46844 if ((this.fd = (*vp->opendirf)(comp)) == 0) { 2480Sstevel@tonic-gate if (errno == EMFILE && oldclose(last) && 2493523Scf46844 (this.fd = (*vp->opendirf)(comp)) != 0) { 2503523Scf46844 /* 2513523Scf46844 * If opendirf fails because there 2523523Scf46844 * are OPEN_MAX fd in the calling 2533523Scf46844 * process, and we close the oldest 2543523Scf46844 * fd, and another opendirf doesn't 2553523Scf46844 * fail, depth is set to 1. 2563523Scf46844 */ 2570Sstevel@tonic-gate depth = 1; 2580Sstevel@tonic-gate } else { 2590Sstevel@tonic-gate type = FTW_DNR; 2600Sstevel@tonic-gate goto fail; 2610Sstevel@tonic-gate } 2620Sstevel@tonic-gate } 2630Sstevel@tonic-gate } else if ((statb.st_mode & S_IFMT) == S_IFLNK) { 2640Sstevel@tonic-gate type = FTW_SL; 2650Sstevel@tonic-gate } else { 2660Sstevel@tonic-gate type = FTW_F; 2670Sstevel@tonic-gate } 2680Sstevel@tonic-gate } else if ((vp->curflags & FTW_ANYERR) && errno != ENOENT) { 2690Sstevel@tonic-gate /* 2700Sstevel@tonic-gate * If FTW_ANYERR is specified, then a stat error 2710Sstevel@tonic-gate * other than ENOENT automatically results in 2720Sstevel@tonic-gate * failure. This allows the callback function 2730Sstevel@tonic-gate * to properly handle ENAMETOOLONG and ELOOP and 2740Sstevel@tonic-gate * things of that nature, that would be masked 2750Sstevel@tonic-gate * by calling lstat before failing. 2760Sstevel@tonic-gate */ 2770Sstevel@tonic-gate type = FTW_NS; 2780Sstevel@tonic-gate goto fail; 2790Sstevel@tonic-gate } else { 2800Sstevel@tonic-gate /* 2810Sstevel@tonic-gate * Statf has failed. If stat was used instead of lstat, 2820Sstevel@tonic-gate * try using lstat. If lstat doesn't fail, "comp" 2830Sstevel@tonic-gate * must be a symbolic link pointing to a non-existent 2840Sstevel@tonic-gate * file. Such a symbolic link should be ignored. 2850Sstevel@tonic-gate * Also check the file type, if possible, for symbolic 2860Sstevel@tonic-gate * link. 2870Sstevel@tonic-gate */ 2883523Scf46844 if (((vp->statf == cdstat) && 289*5302Sth199096 (cdlstat(comp, &statb, last, 0) >= 0) && 2903523Scf46844 ((statb.st_mode & S_IFMT) == S_IFLNK)) || 2913523Scf46844 ((vp->statf == nocdstat) && 292*5302Sth199096 (nocdlstat(comp, &statb, last, 0) >= 0) && 2933523Scf46844 ((statb.st_mode & S_IFMT) == S_IFLNK))) { 2940Sstevel@tonic-gate 2950Sstevel@tonic-gate /* 2960Sstevel@tonic-gate * Ignore bad symbolic link, let "fn" 2970Sstevel@tonic-gate * report it. 2980Sstevel@tonic-gate */ 2990Sstevel@tonic-gate 3000Sstevel@tonic-gate errno = ENOENT; 3010Sstevel@tonic-gate type = FTW_SLN; 3020Sstevel@tonic-gate } else { 3030Sstevel@tonic-gate type = FTW_NS; 3040Sstevel@tonic-gate fail: 3050Sstevel@tonic-gate /* 3060Sstevel@tonic-gate * if FTW_ANYERR is set in flags, we call 3070Sstevel@tonic-gate * the user function with FTW_NS set, regardless 3080Sstevel@tonic-gate * of the reason stat failed. 3090Sstevel@tonic-gate */ 3100Sstevel@tonic-gate if (!(vp->curflags & FTW_ANYERR)) 3110Sstevel@tonic-gate if (errno != EACCES) 3120Sstevel@tonic-gate return (-1); 3130Sstevel@tonic-gate } 3140Sstevel@tonic-gate } 3150Sstevel@tonic-gate 3160Sstevel@tonic-gate /* 3170Sstevel@tonic-gate * If the walk is not supposed to cross a mount point, 3180Sstevel@tonic-gate * and it did, get ready to return. 3190Sstevel@tonic-gate */ 3200Sstevel@tonic-gate if ((vp->curflags & FTW_MOUNT) && type != FTW_NS && 3210Sstevel@tonic-gate statb.st_dev != vp->cur_mount) 3220Sstevel@tonic-gate goto quit; 3230Sstevel@tonic-gate vp->state.quit = 0; 3240Sstevel@tonic-gate 3250Sstevel@tonic-gate /* 3260Sstevel@tonic-gate * If current component is not a directory, call user 3270Sstevel@tonic-gate * specified function and get ready to return. 3280Sstevel@tonic-gate */ 3290Sstevel@tonic-gate if (type != FTW_D || (vp->curflags & FTW_DEPTH) == 0) 3300Sstevel@tonic-gate rc = (*fn)(vp->tmppath, &statb, type, &vp->state); 3310Sstevel@tonic-gate if (rc > 0) 3320Sstevel@tonic-gate val = rc; 3330Sstevel@tonic-gate skip = (vp->state.quit & FTW_SKD); 3340Sstevel@tonic-gate if (rc != 0 || type != FTW_D || (vp->state.quit & FTW_PRUNE)) 3350Sstevel@tonic-gate goto quit; 3360Sstevel@tonic-gate 3370Sstevel@tonic-gate if (vp->tmppath[0] != '\0' && component[-1] != '/') 3380Sstevel@tonic-gate *component++ = '/'; 3393523Scf46844 *component = 0; 3400Sstevel@tonic-gate if (vp->curflags & FTW_CHDIR) { 3410Sstevel@tonic-gate struct stat statb2; 3420Sstevel@tonic-gate 3430Sstevel@tonic-gate /* 3440Sstevel@tonic-gate * Security check (there is a window between 3450Sstevel@tonic-gate * (*vp->statf)() and opendir() above). 3460Sstevel@tonic-gate */ 3470Sstevel@tonic-gate if ((vp->curflags & FTW_PHYS) && 3480Sstevel@tonic-gate (fstat(this.fd->dd_fd, &statb2) < 0 || 3490Sstevel@tonic-gate statb2.st_ino != statb.st_ino || 3500Sstevel@tonic-gate statb2.st_dev != statb.st_dev)) { 3510Sstevel@tonic-gate errno = EAGAIN; 3520Sstevel@tonic-gate rc = -1; 3530Sstevel@tonic-gate goto quit; 3540Sstevel@tonic-gate } 3550Sstevel@tonic-gate 3560Sstevel@tonic-gate if ((cdval = fchdir(this.fd->dd_fd)) >= 0) { 3570Sstevel@tonic-gate this.comp = component; 3580Sstevel@tonic-gate } else { 3590Sstevel@tonic-gate type = FTW_DNR; 3600Sstevel@tonic-gate rc = (*fn)(vp->tmppath, &statb, type, &vp->state); 3610Sstevel@tonic-gate goto quit; 3620Sstevel@tonic-gate } 3630Sstevel@tonic-gate } 3640Sstevel@tonic-gate 3650Sstevel@tonic-gate /* 3663523Scf46844 * If the walk has followed a symbolic link (FTW_PHYS is not set), 3673523Scf46844 * traverse the walk back to make sure there is not a loop. 3683523Scf46844 * The find utility (FTW_NOLOOP is set) detects infinite loops 3693523Scf46844 * in both symbolic and hard linked directories. 3700Sstevel@tonic-gate */ 3713523Scf46844 if ((vp->curflags & FTW_NOLOOP) || 3723523Scf46844 ((vp->curflags & FTW_PHYS) == 0)) { 3730Sstevel@tonic-gate struct Save *sp = last; 3740Sstevel@tonic-gate while (sp) { 3750Sstevel@tonic-gate /* 3760Sstevel@tonic-gate * If the same node has already been visited, there 3770Sstevel@tonic-gate * is a loop. Get ready to return. 3780Sstevel@tonic-gate */ 3790Sstevel@tonic-gate if (sp->dev == statb.st_dev && 3803523Scf46844 sp->inode == statb.st_ino) { 3813523Scf46844 if (vp->curflags & FTW_NOLOOP) { 3823523Scf46844 /* private interface for find util */ 3833523Scf46844 type = FTW_DL; 3843523Scf46844 goto fail; 3853523Scf46844 } 3860Sstevel@tonic-gate goto quit; 3873523Scf46844 } 3880Sstevel@tonic-gate sp = sp->last; 3890Sstevel@tonic-gate } 3900Sstevel@tonic-gate } 3910Sstevel@tonic-gate this.dev = statb.st_dev; 3920Sstevel@tonic-gate this.inode = statb.st_ino; 3930Sstevel@tonic-gate oldbase = vp->state.base; 3940Sstevel@tonic-gate vp->state.base = (int)(component - vp->tmppath); 3950Sstevel@tonic-gate while (dir = readdir(this.fd)) { 3960Sstevel@tonic-gate if (dir->d_ino == 0) 3970Sstevel@tonic-gate continue; 3980Sstevel@tonic-gate q = dir->d_name; 3990Sstevel@tonic-gate if (*q == '.') { 4000Sstevel@tonic-gate if (q[1] == 0) 4010Sstevel@tonic-gate continue; 4020Sstevel@tonic-gate else if (q[1] == '.' && q[2] == 0) 4030Sstevel@tonic-gate continue; 4040Sstevel@tonic-gate } 4053523Scf46844 if (last != NULL && last->comp != NULL) { 4063523Scf46844 base_last_comp = last->comp - vp->home; 4073523Scf46844 } 4083523Scf46844 base_comp = comp - vp->home; 4093523Scf46844 base_component = component - vp->home; 4103523Scf46844 if ((strlen(q) + strlen(vp->home) + 1) > vp->len) { 4113523Scf46844 /* 4123523Scf46844 * When the space needed for vp->home has 4133523Scf46844 * exceeded the amount of space that has 4143523Scf46844 * been allocated, realloc() more space 4153523Scf46844 * and adjust pointers to point to the 4163523Scf46844 * (possibly moved) new block for vp->home 4173523Scf46844 */ 4183523Scf46844 base_this_comp = this.comp - vp->home; 4193523Scf46844 base_fullpath = vp->fullpath - vp->home; 4203523Scf46844 base_tmppath = vp->tmppath - vp->home; 4213523Scf46844 vp->len *= 2; 4223523Scf46844 tmp = (char *)realloc(vp->home, vp->len); 4233523Scf46844 if (tmp == NULL) { 4243523Scf46844 rc = -1; 4253523Scf46844 goto quit; 4263523Scf46844 } 4273523Scf46844 vp->home = tmp; 4283523Scf46844 comp = vp->home + base_comp; 4293523Scf46844 component = vp->home + base_component; 4303523Scf46844 this.comp = vp->home + base_this_comp; 4313523Scf46844 vp->fullpath = vp->home + base_fullpath; 4323523Scf46844 vp->tmppath = vp->home + base_tmppath; 4333523Scf46844 if (last != NULL && last->comp != NULL) { 4343523Scf46844 last->comp = vp->home + base_last_comp; 4353523Scf46844 } 4363523Scf46844 } 4370Sstevel@tonic-gate p = component; 4383523Scf46844 while (*q != '\0') 4390Sstevel@tonic-gate *p++ = *q++; 4400Sstevel@tonic-gate *p = '\0'; 4410Sstevel@tonic-gate vp->state.level++; 4420Sstevel@tonic-gate 4430Sstevel@tonic-gate /* Call walk() recursively. */ 4440Sstevel@tonic-gate rc = walk(p, fn, depth-1, &this, vp); 4453523Scf46844 if (last != NULL && last->comp != NULL) { 4463523Scf46844 last->comp = vp->home + base_last_comp; 4473523Scf46844 } 4483523Scf46844 comp = vp->home + base_comp; 4493523Scf46844 component = vp->home + base_component; 4500Sstevel@tonic-gate vp->state.level--; 4510Sstevel@tonic-gate if (this.fd == 0) { 4520Sstevel@tonic-gate *component = 0; 4530Sstevel@tonic-gate if (vp->curflags & FTW_CHDIR) { 4540Sstevel@tonic-gate this.fd = opendir("."); 4550Sstevel@tonic-gate } else { 4563523Scf46844 this.fd = (*vp->opendirf)(comp); 4570Sstevel@tonic-gate } 4580Sstevel@tonic-gate if (this.fd == 0) { 4590Sstevel@tonic-gate rc = -1; 4600Sstevel@tonic-gate goto quit; 4610Sstevel@tonic-gate } 4620Sstevel@tonic-gate seekdir(this.fd, this.here); 4630Sstevel@tonic-gate } 4640Sstevel@tonic-gate if (rc != 0) { 4650Sstevel@tonic-gate if (errno == ENOENT) { 4660Sstevel@tonic-gate (void) fprintf(stderr, "cannot open %s: %s\n", 4670Sstevel@tonic-gate vp->tmppath, strerror(errno)); 4680Sstevel@tonic-gate val = rc; 4690Sstevel@tonic-gate continue; 4700Sstevel@tonic-gate } 4710Sstevel@tonic-gate goto quit; /* this seems extreme */ 4720Sstevel@tonic-gate } 4730Sstevel@tonic-gate } 4740Sstevel@tonic-gate vp->state.base = oldbase; 4750Sstevel@tonic-gate *--component = 0; 4760Sstevel@tonic-gate type = FTW_DP; 4770Sstevel@tonic-gate if ((vp->tmppath[0] != '\0') && (vp->curflags & FTW_DEPTH) && !skip) 4780Sstevel@tonic-gate rc = (*fn)(vp->tmppath, &statb, type, &vp->state); 4790Sstevel@tonic-gate quit: 4800Sstevel@tonic-gate if (cdval >= 0 && last) { 4810Sstevel@tonic-gate /* try to change back to previous directory */ 4820Sstevel@tonic-gate if (last->fd != NULL) { 4830Sstevel@tonic-gate if (fchdir(last->fd->dd_fd) < 0) { 4840Sstevel@tonic-gate rc = -1; 4850Sstevel@tonic-gate } 4860Sstevel@tonic-gate } else { 4870Sstevel@tonic-gate if ((cdval = chdir("..")) >= 0) { 488*5302Sth199096 if ((*vp->statf)(".", &statb, last, 0) < 0 || 4890Sstevel@tonic-gate statb.st_ino != last->inode || 4900Sstevel@tonic-gate statb.st_dev != last->dev) 4910Sstevel@tonic-gate cdval = -1; 4920Sstevel@tonic-gate } 4930Sstevel@tonic-gate *comp = 0; 4940Sstevel@tonic-gate if (cdval < 0) { 4950Sstevel@tonic-gate if (chdir(vp->fullpath) < 0) { 4960Sstevel@tonic-gate rc = -1; 4970Sstevel@tonic-gate } else { 498*5302Sth199096 /* Security check */ 499*5302Sth199096 if ((vp->curflags & FTW_PHYS) && 500*5302Sth199096 ((*vp->statf)(".", &statb, 501*5302Sth199096 last, 0) < 0 || 502*5302Sth199096 statb.st_ino != last->inode || 503*5302Sth199096 statb.st_dev != last->dev)) { 504*5302Sth199096 errno = EAGAIN; 505*5302Sth199096 rc = -1; 506*5302Sth199096 } 5070Sstevel@tonic-gate } 5080Sstevel@tonic-gate } 5090Sstevel@tonic-gate } 5100Sstevel@tonic-gate } 511*5302Sth199096 5120Sstevel@tonic-gate if (this.fd) 5130Sstevel@tonic-gate (void) closedir(this.fd); 5140Sstevel@tonic-gate if (val > rc) 5150Sstevel@tonic-gate return (val); 5160Sstevel@tonic-gate else 5170Sstevel@tonic-gate return (rc); 5180Sstevel@tonic-gate } 5190Sstevel@tonic-gate 5200Sstevel@tonic-gate int 5210Sstevel@tonic-gate _nftw(const char *path, 5220Sstevel@tonic-gate int (*fn)(const char *, const struct stat *, int, struct FTW *), 5230Sstevel@tonic-gate int depth, int flags) 5240Sstevel@tonic-gate { 5250Sstevel@tonic-gate struct Var var; 5260Sstevel@tonic-gate struct stat statb; 5270Sstevel@tonic-gate int rc = -1; 5280Sstevel@tonic-gate char *dp; 5290Sstevel@tonic-gate char *base; 5300Sstevel@tonic-gate char *endhome; 5310Sstevel@tonic-gate const char *savepath = path; 5320Sstevel@tonic-gate int save_errno; 5330Sstevel@tonic-gate 5343523Scf46844 var.walklevel = 0; 5353523Scf46844 var.len = 2*(PATH_MAX+1); 5363523Scf46844 var.home = (char *)malloc(var.len); 5373523Scf46844 if (var.home == NULL) 5383523Scf46844 return (-1); 5393523Scf46844 5403523Scf46844 var.home[0] = 0; 5410Sstevel@tonic-gate 5420Sstevel@tonic-gate /* 5430Sstevel@tonic-gate * If the walk is going to change directory before 5443523Scf46844 * reading it, save current working directory. 5450Sstevel@tonic-gate */ 5460Sstevel@tonic-gate if (flags & FTW_CHDIR) { 5473523Scf46844 if (getcwd(var.home, PATH_MAX+1) == 0) { 5483523Scf46844 free(var.home); 5490Sstevel@tonic-gate return (-1); 5503523Scf46844 } 5510Sstevel@tonic-gate } 5523523Scf46844 endhome = dp = var.home + strlen(var.home); 5530Sstevel@tonic-gate if (*path == '/') 5540Sstevel@tonic-gate var.fullpath = dp; 5550Sstevel@tonic-gate else { 5560Sstevel@tonic-gate *dp++ = '/'; 5573523Scf46844 var.fullpath = var.home; 5580Sstevel@tonic-gate } 5590Sstevel@tonic-gate var.tmppath = dp; 5600Sstevel@tonic-gate base = dp-1; 5613523Scf46844 while (*path) { 5620Sstevel@tonic-gate *dp = *path; 5630Sstevel@tonic-gate if (*dp == '/') 5640Sstevel@tonic-gate base = dp; 5650Sstevel@tonic-gate dp++, path++; 5660Sstevel@tonic-gate } 5670Sstevel@tonic-gate *dp = 0; 5680Sstevel@tonic-gate var.state.base = (int)(base + 1 - var.tmppath); 5690Sstevel@tonic-gate if (*path) { 5703523Scf46844 free(var.home); 5710Sstevel@tonic-gate errno = ENAMETOOLONG; 5720Sstevel@tonic-gate return (-1); 5730Sstevel@tonic-gate } 5740Sstevel@tonic-gate var.curflags = flags; 5750Sstevel@tonic-gate 5760Sstevel@tonic-gate /* 5773523Scf46844 * If doing chdir()'s, set var.opendirf to cdopendir. 5783523Scf46844 * If not doing chdir()'s and if nftw()'s depth arg >= 2, 5793523Scf46844 * set var.opendirf to nocdopendir. In order to 5803523Scf46844 * descend to arbitrary depths without doing chdir()'s, nftw() 5813523Scf46844 * requires a depth arg >= 2 so that nocdopendir() can use openat() 5823523Scf46844 * to traverse the directories. So when not doing 5833523Scf46844 * chdir()'s if nftw()'s depth arg <= 1, set var.opendirf to 5843523Scf46844 * cdopendir. 5850Sstevel@tonic-gate * If doing a physical walk (not following symbolic link), set 5863523Scf46844 * var.statf to cdlstat() or nocdlstat(). Otherwise, set var.statf 5873523Scf46844 * to cdstat() or nocdstat(). 5880Sstevel@tonic-gate */ 5893523Scf46844 if (((flags & FTW_CHDIR) == 0) && (depth >= 2)) { 5903523Scf46844 var.opendirf = nocdopendir; 5913523Scf46844 if (flags & FTW_PHYS) 5923523Scf46844 var.statf = nocdlstat; 5933523Scf46844 else 5943523Scf46844 var.statf = nocdstat; 5953523Scf46844 } else { 5963523Scf46844 var.opendirf = cdopendir; 5973523Scf46844 if (flags & FTW_PHYS) 5983523Scf46844 var.statf = cdlstat; 5993523Scf46844 else 6003523Scf46844 var.statf = cdstat; 6013523Scf46844 } 6020Sstevel@tonic-gate 6030Sstevel@tonic-gate /* 6040Sstevel@tonic-gate * If walk is not going to cross a mount point, 6050Sstevel@tonic-gate * save the current mount point. 6060Sstevel@tonic-gate */ 6070Sstevel@tonic-gate if (flags & FTW_MOUNT) { 608*5302Sth199096 if ((*var.statf)(savepath, &statb, NULL, 0) >= 0) 6090Sstevel@tonic-gate var.cur_mount = statb.st_dev; 6100Sstevel@tonic-gate else 6110Sstevel@tonic-gate goto done; 6120Sstevel@tonic-gate } 6130Sstevel@tonic-gate var.state.level = 0; 6140Sstevel@tonic-gate 6150Sstevel@tonic-gate /* 6160Sstevel@tonic-gate * Call walk() which does most of the work. 6170Sstevel@tonic-gate * walk() uses errno in a rather obtuse way 6180Sstevel@tonic-gate * so we shield any incoming errno. 6190Sstevel@tonic-gate */ 6200Sstevel@tonic-gate save_errno = errno; 6210Sstevel@tonic-gate errno = 0; 6220Sstevel@tonic-gate var.savedstatf = NULL; 6230Sstevel@tonic-gate rc = walk(dp, fn, depth, (struct Save *)0, &var); 6240Sstevel@tonic-gate if (errno == 0) 6250Sstevel@tonic-gate errno = save_errno; 6260Sstevel@tonic-gate done: 6270Sstevel@tonic-gate *endhome = 0; 6280Sstevel@tonic-gate if (flags & FTW_CHDIR) 6293523Scf46844 (void) chdir(var.home); 6303523Scf46844 free(var.home); 6310Sstevel@tonic-gate return (rc); 6320Sstevel@tonic-gate } 6330Sstevel@tonic-gate 6340Sstevel@tonic-gate /* 6353523Scf46844 * Get stat info on path when FTW_CHDIR is set. 6363523Scf46844 */ 6373523Scf46844 /*ARGSUSED1*/ 6383523Scf46844 static int 639*5302Sth199096 cdstat(const char *path, struct stat *statp, struct Save *lp, int flags) 640*5302Sth199096 { 641*5302Sth199096 return (fstatat(AT_FDCWD, path, statp, flags)); 6423523Scf46844 } 6433523Scf46844 6443523Scf46844 /* 6453523Scf46844 * Get lstat info on path when FTW_CHDIR is set. 6463523Scf46844 */ 6473523Scf46844 /*ARGSUSED1*/ 6483523Scf46844 static int 649*5302Sth199096 cdlstat(const char *path, struct stat *statp, struct Save *lp, int flags) 6503523Scf46844 { 651*5302Sth199096 return (fstatat(AT_FDCWD, path, statp, 652*5302Sth199096 flags | AT_SYMLINK_NOFOLLOW)); 6533523Scf46844 } 6543523Scf46844 6553523Scf46844 /* 6563523Scf46844 * Get stat info on path when FTW_CHDIR is not set. 6573523Scf46844 */ 6583523Scf46844 static int 659*5302Sth199096 nocdstat(const char *path, struct stat *statp, struct Save *lp, int flags) 6603523Scf46844 { 661*5302Sth199096 int fd; 662*5302Sth199096 const char *basepath; 6633523Scf46844 6643523Scf46844 if (lp && lp->fd) { 6653523Scf46844 /* get basename of path */ 666*5302Sth199096 basepath = get_unrooted(path); 667*5302Sth199096 668*5302Sth199096 fd = lp->fd->dd_fd; 6693523Scf46844 } else { 670*5302Sth199096 basepath = path; 671*5302Sth199096 672*5302Sth199096 fd = AT_FDCWD; 6733523Scf46844 } 674*5302Sth199096 675*5302Sth199096 return (fstatat(fd, basepath, statp, flags)); 6763523Scf46844 } 6773523Scf46844 6783523Scf46844 /* 6793523Scf46844 * Get lstat info on path when FTW_CHDIR is not set. 6803523Scf46844 */ 6813523Scf46844 static int 682*5302Sth199096 nocdlstat(const char *path, struct stat *statp, struct Save *lp, int flags) 6833523Scf46844 { 684*5302Sth199096 int fd; 685*5302Sth199096 const char *basepath; 6863523Scf46844 6873523Scf46844 if (lp && lp->fd) { 6883523Scf46844 /* get basename of path */ 689*5302Sth199096 basepath = get_unrooted(path); 690*5302Sth199096 691*5302Sth199096 fd = lp->fd->dd_fd; 6923523Scf46844 } else { 693*5302Sth199096 basepath = path; 694*5302Sth199096 695*5302Sth199096 fd = AT_FDCWD; 6963523Scf46844 } 697*5302Sth199096 698*5302Sth199096 return (fstatat(fd, basepath, statp, flags | AT_SYMLINK_NOFOLLOW)); 6993523Scf46844 } 7003523Scf46844 7013523Scf46844 /* 7023523Scf46844 * Open path directory when FTW_CHDIR is set. 7033523Scf46844 * 7043523Scf46844 */ 7053523Scf46844 static DIR * 7063523Scf46844 cdopendir(const char *path) 7073523Scf46844 { 7083523Scf46844 return (opendir(path)); 7093523Scf46844 } 7103523Scf46844 7113523Scf46844 /* 7123523Scf46844 * Open path directory when FTW_CHDIR is not set. 7133523Scf46844 */ 7143523Scf46844 static DIR * 7153523Scf46844 nocdopendir(const char *path) 7163523Scf46844 { 7173523Scf46844 int fd, cfd; 7183523Scf46844 DIR *fdd; 7193523Scf46844 char *dirp, *token, *ptr; 7203523Scf46844 7213523Scf46844 if (((fdd = opendir(path)) == NULL) && (errno == ENAMETOOLONG)) { 7223523Scf46844 if ((dirp = strdup(path)) == NULL) { 7233523Scf46844 errno = ENAMETOOLONG; 7243523Scf46844 return (NULL); 7253523Scf46844 } 7263523Scf46844 if ((token = strtok_r(dirp, "/", &ptr)) != NULL) { 727*5302Sth199096 if ((fd = openat(AT_FDCWD, dirp, O_RDONLY)) < 0) { 728*5302Sth199096 (void) free(dirp); 729*5302Sth199096 errno = ENAMETOOLONG; 730*5302Sth199096 return (NULL); 731*5302Sth199096 } 732*5302Sth199096 while ((token = strtok_r(NULL, "/", &ptr)) != NULL) { 733*5302Sth199096 if ((cfd = openat(fd, token, O_RDONLY)) < 0) { 734*5302Sth199096 (void) close(fd); 735*5302Sth199096 (void) free(dirp); 736*5302Sth199096 errno = ENAMETOOLONG; 737*5302Sth199096 return (NULL); 738*5302Sth199096 } 739*5302Sth199096 (void) close(fd); 740*5302Sth199096 fd = cfd; 741*5302Sth199096 } 7423523Scf46844 (void) free(dirp); 743*5302Sth199096 return (fdopendir(fd)); 7443523Scf46844 } 7453523Scf46844 (void) free(dirp); 7463523Scf46844 errno = ENAMETOOLONG; 7473523Scf46844 } 7483523Scf46844 return (fdd); 7493523Scf46844 } 7503523Scf46844 751*5302Sth199096 /* 752*5302Sth199096 * return pointer basename of path, which may contain trailing slashes 753*5302Sth199096 * 754*5302Sth199096 * We do this when we do not chdir() on the input. 755*5302Sth199096 */ 7563523Scf46844 static const char * 7573523Scf46844 get_unrooted(const char *path) 7583523Scf46844 { 7593523Scf46844 const char *ptr; 7603523Scf46844 7613523Scf46844 if (!path || !*path) 7623523Scf46844 return (NULL); 7633523Scf46844 7643523Scf46844 ptr = path + strlen(path); 7653523Scf46844 /* find last char in path before any trailing slashes */ 7663523Scf46844 while (ptr != path && *--ptr == '/') 7673523Scf46844 ; 7683523Scf46844 7693523Scf46844 if (ptr == path) /* all slashes */ 7703523Scf46844 return (ptr); 7713523Scf46844 7723523Scf46844 while (ptr != path) 7733523Scf46844 if (*--ptr == '/') 7743523Scf46844 return (++ptr); 7753523Scf46844 7763523Scf46844 return (ptr); 7773523Scf46844 } 7783523Scf46844 7793523Scf46844 /* 7800Sstevel@tonic-gate * close the oldest directory. It saves the seek offset. 7810Sstevel@tonic-gate * return value is 0 unless it was unable to close any descriptor 7820Sstevel@tonic-gate */ 7830Sstevel@tonic-gate 7840Sstevel@tonic-gate static int 7850Sstevel@tonic-gate oldclose(struct Save *sp) 7860Sstevel@tonic-gate { 7870Sstevel@tonic-gate struct Save *spnext; 7880Sstevel@tonic-gate while (sp) { 7890Sstevel@tonic-gate spnext = sp->last; 7900Sstevel@tonic-gate if (spnext == 0 || spnext->fd == 0) 7910Sstevel@tonic-gate break; 7920Sstevel@tonic-gate sp = spnext; 7930Sstevel@tonic-gate } 7940Sstevel@tonic-gate if (sp == 0 || sp->fd == 0) 7950Sstevel@tonic-gate return (0); 7960Sstevel@tonic-gate sp->here = telldir(sp->fd); 7970Sstevel@tonic-gate (void) closedir(sp->fd); 7980Sstevel@tonic-gate sp->fd = 0; 7990Sstevel@tonic-gate return (1); 8000Sstevel@tonic-gate } 801