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 /*
23*6812Sraf * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
240Sstevel@tonic-gate * Use is subject to license terms.
250Sstevel@tonic-gate */
260Sstevel@tonic-gate
270Sstevel@tonic-gate /* Copyright (c) 1988 AT&T */
280Sstevel@tonic-gate /* All Rights Reserved */
290Sstevel@tonic-gate
30*6812Sraf #pragma ident "%Z%%M% %I% %E% SMI"
310Sstevel@tonic-gate
320Sstevel@tonic-gate /*
330Sstevel@tonic-gate * nftw - new file tree walk
340Sstevel@tonic-gate *
350Sstevel@tonic-gate * int nftw(char *path, int (*fn)(), int depth, int flags);
360Sstevel@tonic-gate *
370Sstevel@tonic-gate * Derived from System V ftw() by David Korn
380Sstevel@tonic-gate *
390Sstevel@tonic-gate * nftw visits each file and directory in the tree starting at
400Sstevel@tonic-gate * path. It uses the generic directory reading library so it works
410Sstevel@tonic-gate * for any file system type. The flags field is used to specify:
423523Scf46844 * FTW_PHYS Physical walk, does not follow symbolic links
430Sstevel@tonic-gate * Otherwise, nftw will follow links but will not
440Sstevel@tonic-gate * walk down any path the crosses itself.
450Sstevel@tonic-gate * FTW_MOUNT The walk will not cross a mount point.
460Sstevel@tonic-gate * FTW_DEPTH All subdirectories will be visited before the
470Sstevel@tonic-gate * directory itself.
480Sstevel@tonic-gate * FTW_CHDIR The walk will change to each directory before
490Sstevel@tonic-gate * reading it. This is faster but core dumps
500Sstevel@tonic-gate * may not get generated.
510Sstevel@tonic-gate *
520Sstevel@tonic-gate * The following flags are private, and are used by the find
530Sstevel@tonic-gate * utility:
540Sstevel@tonic-gate * FTW_ANYERR Call the callback function and return
550Sstevel@tonic-gate * FTW_NS on any stat failure, not just
560Sstevel@tonic-gate * lack of permission.
570Sstevel@tonic-gate * FTW_HOPTION Use stat the first time the walk
580Sstevel@tonic-gate * function is called, regardless of
590Sstevel@tonic-gate * whether or not FTW_PHYS is specified.
603523Scf46844 * FTW_NOLOOP Allow find utility to detect infinite loops created
613523Scf46844 * by both symbolic and hard linked directories.
620Sstevel@tonic-gate *
630Sstevel@tonic-gate * fn is called with four arguments at each file and directory.
640Sstevel@tonic-gate * The first argument is the pathname of the object, the second
650Sstevel@tonic-gate * is a pointer to the stat buffer and the third is an integer
660Sstevel@tonic-gate * giving additional information as follows:
670Sstevel@tonic-gate *
680Sstevel@tonic-gate * FTW_F The object is a file.
690Sstevel@tonic-gate * FTW_D The object is a directory.
700Sstevel@tonic-gate * FTW_DP The object is a directory and subdirectories
710Sstevel@tonic-gate * have been visited.
720Sstevel@tonic-gate * FTW_SL The object is a symbolic link.
730Sstevel@tonic-gate * FTW_SLN The object is a symbolic link pointing at a
740Sstevel@tonic-gate * non-existing file.
750Sstevel@tonic-gate * FTW_DNR The object is a directory that cannot be read.
760Sstevel@tonic-gate * fn will not be called for any of its descendants.
770Sstevel@tonic-gate * FTW_NS Stat failed on the object because of lack of
780Sstevel@tonic-gate * appropriate permission. The stat buffer passed to fn
790Sstevel@tonic-gate * is undefined. Stat failure for any reason is
800Sstevel@tonic-gate * considered an error and nftw will return -1.
813523Scf46844 * The following value is private, and is used by the find utility:
823523Scf46844 * FTW_DL An infinite loop has been detected.
830Sstevel@tonic-gate * The fourth argument is a struct FTW* which contains the depth
840Sstevel@tonic-gate * and the offset into pathname to the base name.
850Sstevel@tonic-gate * If fn returns nonzero, nftw returns this value to its caller.
860Sstevel@tonic-gate *
870Sstevel@tonic-gate * depth limits the number of open directories that ftw uses
880Sstevel@tonic-gate * before it starts recycling file descriptors. In general,
893523Scf46844 * a file descriptor is used for each level. When FTW_CHDIR isn't set,
903523Scf46844 * in order to descend to arbitrary depths, nftw requires 2 file
913523Scf46844 * descriptors to be open during the call to openat(), therefore if
923523Scf46844 * the depth argument is less than 2 nftw will not use openat(), and
933523Scf46844 * it will fail with ENAMETOOLONG if it descends to a directory that
943523Scf46844 * exceeds PATH_MAX.
950Sstevel@tonic-gate *
960Sstevel@tonic-gate */
970Sstevel@tonic-gate
980Sstevel@tonic-gate #include "lint.h"
990Sstevel@tonic-gate #include <mtlib.h>
1000Sstevel@tonic-gate #include <sys/types.h>
1010Sstevel@tonic-gate #include <sys/stat.h>
1020Sstevel@tonic-gate #include <dirent.h>
1030Sstevel@tonic-gate #include <errno.h>
1040Sstevel@tonic-gate #include <limits.h>
1050Sstevel@tonic-gate #include <ftw.h>
1060Sstevel@tonic-gate #include <stdlib.h>
1070Sstevel@tonic-gate #include <string.h>
1080Sstevel@tonic-gate #include <unistd.h>
1090Sstevel@tonic-gate #include <thread.h>
1100Sstevel@tonic-gate #include <synch.h>
1110Sstevel@tonic-gate #include <stdio.h>
1123523Scf46844 #include <strings.h>
1133523Scf46844 #include <fcntl.h>
1140Sstevel@tonic-gate
115*6812Sraf #if !defined(_LP64) && _FILE_OFFSET_BITS == 64
116*6812Sraf #define nftw nftw64
117*6812Sraf #define stat stat64
118*6812Sraf #define fstat fstat64
119*6812Sraf #define fstatat fstatat64
120*6812Sraf #pragma weak _nftw64 = nftw64
121*6812Sraf #else
122*6812Sraf #pragma weak _nftw = nftw
123*6812Sraf #endif /* !_LP64 && _FILE_OFFSET_BITS == 64 */
124*6812Sraf
1250Sstevel@tonic-gate #ifndef PATH_MAX
1260Sstevel@tonic-gate #define PATH_MAX 1023
1270Sstevel@tonic-gate #endif
1280Sstevel@tonic-gate
1290Sstevel@tonic-gate /*
1300Sstevel@tonic-gate * Local variables (used to be static local).
1310Sstevel@tonic-gate * Putting them into a structure that is passed
1320Sstevel@tonic-gate * around makes nftw() MT-safe with no locking required.
1330Sstevel@tonic-gate */
1340Sstevel@tonic-gate struct Save {
1350Sstevel@tonic-gate struct Save *last;
1360Sstevel@tonic-gate DIR *fd;
1370Sstevel@tonic-gate char *comp;
1380Sstevel@tonic-gate long here;
1390Sstevel@tonic-gate dev_t dev;
1400Sstevel@tonic-gate ino_t inode;
1410Sstevel@tonic-gate };
1420Sstevel@tonic-gate
1433523Scf46844 struct Var {
1443523Scf46844 char *home;
1453523Scf46844 size_t len;
1463523Scf46844 char *fullpath;
1473523Scf46844 char *tmppath;
1483523Scf46844 int curflags;
1493523Scf46844 dev_t cur_mount;
1503523Scf46844 struct FTW state;
1513523Scf46844 int walklevel;
1525302Sth199096 int (*statf)(const char *, struct stat *, struct Save *, int flags);
1535302Sth199096 int (*savedstatf)(const char *, struct stat *, struct Save *,
1545302Sth199096 int flags);
1553523Scf46844 DIR *(*opendirf)(const char *);
1563523Scf46844 };
1573523Scf46844
1580Sstevel@tonic-gate static int oldclose(struct Save *);
1595302Sth199096 static int cdlstat(const char *, struct stat *, struct Save *, int flags);
1605302Sth199096 static int cdstat(const char *, struct stat *, struct Save *, int flags);
1615302Sth199096 static int nocdlstat(const char *, struct stat *, struct Save *, int flags);
1625302Sth199096 static int nocdstat(const char *, struct stat *, struct Save *, int flags);
1633523Scf46844 static DIR *cdopendir(const char *);
1643523Scf46844 static DIR *nocdopendir(const char *);
1653523Scf46844 static const char *get_unrooted(const char *);
1660Sstevel@tonic-gate
1670Sstevel@tonic-gate /*
1680Sstevel@tonic-gate * This is the recursive walker.
1690Sstevel@tonic-gate */
1700Sstevel@tonic-gate static int
walk(char * component,int (* fn)(const char *,const struct stat *,int,struct FTW *),int depth,struct Save * last,struct Var * vp)1710Sstevel@tonic-gate walk(char *component,
1720Sstevel@tonic-gate int (*fn)(const char *, const struct stat *, int, struct FTW *),
1730Sstevel@tonic-gate int depth, struct Save *last, struct Var *vp)
1740Sstevel@tonic-gate {
1750Sstevel@tonic-gate struct stat statb;
1763523Scf46844 char *p, *tmp;
1770Sstevel@tonic-gate int type;
1780Sstevel@tonic-gate char *comp;
1790Sstevel@tonic-gate struct dirent *dir;
1800Sstevel@tonic-gate char *q;
1810Sstevel@tonic-gate int rc = 0;
1820Sstevel@tonic-gate int val = -1;
1830Sstevel@tonic-gate int cdval = -1;
1840Sstevel@tonic-gate int oldbase;
1850Sstevel@tonic-gate int skip;
1860Sstevel@tonic-gate struct Save this;
1873523Scf46844 size_t base_comp, base_component, base_this_comp, base_last_comp;
1883523Scf46844 size_t base_fullpath, base_tmppath;
1890Sstevel@tonic-gate
1900Sstevel@tonic-gate this.last = last;
1910Sstevel@tonic-gate this.fd = 0;
1920Sstevel@tonic-gate if ((vp->curflags & FTW_CHDIR) && last)
1930Sstevel@tonic-gate comp = last->comp;
1940Sstevel@tonic-gate else
1950Sstevel@tonic-gate comp = vp->tmppath;
1960Sstevel@tonic-gate
1970Sstevel@tonic-gate if (vp->savedstatf == NULL)
1980Sstevel@tonic-gate vp->savedstatf = vp->statf;
1990Sstevel@tonic-gate
2003523Scf46844 if ((vp->walklevel++ == 0) && (vp->curflags & FTW_HOPTION)) {
2013523Scf46844 if (((vp->curflags & FTW_CHDIR) == 0) && (depth >= 2)) {
2023523Scf46844 vp->statf = nocdstat;
2033523Scf46844 } else {
2043523Scf46844 vp->statf = cdstat;
2053523Scf46844 }
2063523Scf46844 } else {
2070Sstevel@tonic-gate vp->statf = vp->savedstatf;
2083523Scf46844 }
2090Sstevel@tonic-gate
2100Sstevel@tonic-gate /*
2110Sstevel@tonic-gate * Determine the type of the component.
2125302Sth199096 *
2135302Sth199096 * Note that if the component is a trigger mount, this
2145302Sth199096 * will cause it to load.
2150Sstevel@tonic-gate */
2165302Sth199096 if ((*vp->statf)(comp, &statb, last, _AT_TRIGGER) >= 0) {
2170Sstevel@tonic-gate if ((statb.st_mode & S_IFMT) == S_IFDIR) {
2180Sstevel@tonic-gate type = FTW_D;
2190Sstevel@tonic-gate if (depth <= 1)
2200Sstevel@tonic-gate (void) oldclose(last);
2213523Scf46844 if ((this.fd = (*vp->opendirf)(comp)) == 0) {
2220Sstevel@tonic-gate if (errno == EMFILE && oldclose(last) &&
2233523Scf46844 (this.fd = (*vp->opendirf)(comp)) != 0) {
2243523Scf46844 /*
2253523Scf46844 * If opendirf fails because there
2263523Scf46844 * are OPEN_MAX fd in the calling
2273523Scf46844 * process, and we close the oldest
2283523Scf46844 * fd, and another opendirf doesn't
2293523Scf46844 * fail, depth is set to 1.
2303523Scf46844 */
2310Sstevel@tonic-gate depth = 1;
2320Sstevel@tonic-gate } else {
2330Sstevel@tonic-gate type = FTW_DNR;
2340Sstevel@tonic-gate goto fail;
2350Sstevel@tonic-gate }
2360Sstevel@tonic-gate }
2370Sstevel@tonic-gate } else if ((statb.st_mode & S_IFMT) == S_IFLNK) {
2380Sstevel@tonic-gate type = FTW_SL;
2390Sstevel@tonic-gate } else {
2400Sstevel@tonic-gate type = FTW_F;
2410Sstevel@tonic-gate }
2420Sstevel@tonic-gate } else if ((vp->curflags & FTW_ANYERR) && errno != ENOENT) {
2430Sstevel@tonic-gate /*
2440Sstevel@tonic-gate * If FTW_ANYERR is specified, then a stat error
2450Sstevel@tonic-gate * other than ENOENT automatically results in
2460Sstevel@tonic-gate * failure. This allows the callback function
2470Sstevel@tonic-gate * to properly handle ENAMETOOLONG and ELOOP and
2480Sstevel@tonic-gate * things of that nature, that would be masked
2490Sstevel@tonic-gate * by calling lstat before failing.
2500Sstevel@tonic-gate */
2510Sstevel@tonic-gate type = FTW_NS;
2520Sstevel@tonic-gate goto fail;
2530Sstevel@tonic-gate } else {
2540Sstevel@tonic-gate /*
2550Sstevel@tonic-gate * Statf has failed. If stat was used instead of lstat,
2560Sstevel@tonic-gate * try using lstat. If lstat doesn't fail, "comp"
2570Sstevel@tonic-gate * must be a symbolic link pointing to a non-existent
2580Sstevel@tonic-gate * file. Such a symbolic link should be ignored.
2590Sstevel@tonic-gate * Also check the file type, if possible, for symbolic
2600Sstevel@tonic-gate * link.
2610Sstevel@tonic-gate */
2623523Scf46844 if (((vp->statf == cdstat) &&
2635302Sth199096 (cdlstat(comp, &statb, last, 0) >= 0) &&
2643523Scf46844 ((statb.st_mode & S_IFMT) == S_IFLNK)) ||
2653523Scf46844 ((vp->statf == nocdstat) &&
2665302Sth199096 (nocdlstat(comp, &statb, last, 0) >= 0) &&
2673523Scf46844 ((statb.st_mode & S_IFMT) == S_IFLNK))) {
2680Sstevel@tonic-gate
2690Sstevel@tonic-gate /*
2700Sstevel@tonic-gate * Ignore bad symbolic link, let "fn"
2710Sstevel@tonic-gate * report it.
2720Sstevel@tonic-gate */
2730Sstevel@tonic-gate
2740Sstevel@tonic-gate errno = ENOENT;
2750Sstevel@tonic-gate type = FTW_SLN;
2760Sstevel@tonic-gate } else {
2770Sstevel@tonic-gate type = FTW_NS;
2780Sstevel@tonic-gate fail:
2790Sstevel@tonic-gate /*
2800Sstevel@tonic-gate * if FTW_ANYERR is set in flags, we call
2810Sstevel@tonic-gate * the user function with FTW_NS set, regardless
2820Sstevel@tonic-gate * of the reason stat failed.
2830Sstevel@tonic-gate */
2840Sstevel@tonic-gate if (!(vp->curflags & FTW_ANYERR))
2850Sstevel@tonic-gate if (errno != EACCES)
2860Sstevel@tonic-gate return (-1);
2870Sstevel@tonic-gate }
2880Sstevel@tonic-gate }
2890Sstevel@tonic-gate
2900Sstevel@tonic-gate /*
2910Sstevel@tonic-gate * If the walk is not supposed to cross a mount point,
2920Sstevel@tonic-gate * and it did, get ready to return.
2930Sstevel@tonic-gate */
2940Sstevel@tonic-gate if ((vp->curflags & FTW_MOUNT) && type != FTW_NS &&
2950Sstevel@tonic-gate statb.st_dev != vp->cur_mount)
2960Sstevel@tonic-gate goto quit;
2970Sstevel@tonic-gate vp->state.quit = 0;
2980Sstevel@tonic-gate
2990Sstevel@tonic-gate /*
3000Sstevel@tonic-gate * If current component is not a directory, call user
3010Sstevel@tonic-gate * specified function and get ready to return.
3020Sstevel@tonic-gate */
3030Sstevel@tonic-gate if (type != FTW_D || (vp->curflags & FTW_DEPTH) == 0)
3040Sstevel@tonic-gate rc = (*fn)(vp->tmppath, &statb, type, &vp->state);
3050Sstevel@tonic-gate if (rc > 0)
3060Sstevel@tonic-gate val = rc;
3070Sstevel@tonic-gate skip = (vp->state.quit & FTW_SKD);
3080Sstevel@tonic-gate if (rc != 0 || type != FTW_D || (vp->state.quit & FTW_PRUNE))
3090Sstevel@tonic-gate goto quit;
3100Sstevel@tonic-gate
3110Sstevel@tonic-gate if (vp->tmppath[0] != '\0' && component[-1] != '/')
3120Sstevel@tonic-gate *component++ = '/';
3133523Scf46844 *component = 0;
3140Sstevel@tonic-gate if (vp->curflags & FTW_CHDIR) {
3150Sstevel@tonic-gate struct stat statb2;
3160Sstevel@tonic-gate
3170Sstevel@tonic-gate /*
3180Sstevel@tonic-gate * Security check (there is a window between
3190Sstevel@tonic-gate * (*vp->statf)() and opendir() above).
3200Sstevel@tonic-gate */
3210Sstevel@tonic-gate if ((vp->curflags & FTW_PHYS) &&
3220Sstevel@tonic-gate (fstat(this.fd->dd_fd, &statb2) < 0 ||
3230Sstevel@tonic-gate statb2.st_ino != statb.st_ino ||
3240Sstevel@tonic-gate statb2.st_dev != statb.st_dev)) {
3250Sstevel@tonic-gate errno = EAGAIN;
3260Sstevel@tonic-gate rc = -1;
3270Sstevel@tonic-gate goto quit;
3280Sstevel@tonic-gate }
3290Sstevel@tonic-gate
3300Sstevel@tonic-gate if ((cdval = fchdir(this.fd->dd_fd)) >= 0) {
3310Sstevel@tonic-gate this.comp = component;
3320Sstevel@tonic-gate } else {
3330Sstevel@tonic-gate type = FTW_DNR;
3340Sstevel@tonic-gate rc = (*fn)(vp->tmppath, &statb, type, &vp->state);
3350Sstevel@tonic-gate goto quit;
3360Sstevel@tonic-gate }
3370Sstevel@tonic-gate }
3380Sstevel@tonic-gate
3390Sstevel@tonic-gate /*
3403523Scf46844 * If the walk has followed a symbolic link (FTW_PHYS is not set),
3413523Scf46844 * traverse the walk back to make sure there is not a loop.
3423523Scf46844 * The find utility (FTW_NOLOOP is set) detects infinite loops
3433523Scf46844 * in both symbolic and hard linked directories.
3440Sstevel@tonic-gate */
3453523Scf46844 if ((vp->curflags & FTW_NOLOOP) ||
3463523Scf46844 ((vp->curflags & FTW_PHYS) == 0)) {
3470Sstevel@tonic-gate struct Save *sp = last;
3480Sstevel@tonic-gate while (sp) {
3490Sstevel@tonic-gate /*
3500Sstevel@tonic-gate * If the same node has already been visited, there
3510Sstevel@tonic-gate * is a loop. Get ready to return.
3520Sstevel@tonic-gate */
3530Sstevel@tonic-gate if (sp->dev == statb.st_dev &&
3543523Scf46844 sp->inode == statb.st_ino) {
3553523Scf46844 if (vp->curflags & FTW_NOLOOP) {
3563523Scf46844 /* private interface for find util */
3573523Scf46844 type = FTW_DL;
3583523Scf46844 goto fail;
3593523Scf46844 }
3600Sstevel@tonic-gate goto quit;
3613523Scf46844 }
3620Sstevel@tonic-gate sp = sp->last;
3630Sstevel@tonic-gate }
3640Sstevel@tonic-gate }
3650Sstevel@tonic-gate this.dev = statb.st_dev;
3660Sstevel@tonic-gate this.inode = statb.st_ino;
3670Sstevel@tonic-gate oldbase = vp->state.base;
3680Sstevel@tonic-gate vp->state.base = (int)(component - vp->tmppath);
3690Sstevel@tonic-gate while (dir = readdir(this.fd)) {
3700Sstevel@tonic-gate if (dir->d_ino == 0)
3710Sstevel@tonic-gate continue;
3720Sstevel@tonic-gate q = dir->d_name;
3730Sstevel@tonic-gate if (*q == '.') {
3740Sstevel@tonic-gate if (q[1] == 0)
3750Sstevel@tonic-gate continue;
3760Sstevel@tonic-gate else if (q[1] == '.' && q[2] == 0)
3770Sstevel@tonic-gate continue;
3780Sstevel@tonic-gate }
3793523Scf46844 if (last != NULL && last->comp != NULL) {
3803523Scf46844 base_last_comp = last->comp - vp->home;
3813523Scf46844 }
3823523Scf46844 base_comp = comp - vp->home;
3833523Scf46844 base_component = component - vp->home;
3843523Scf46844 if ((strlen(q) + strlen(vp->home) + 1) > vp->len) {
3853523Scf46844 /*
3863523Scf46844 * When the space needed for vp->home has
3873523Scf46844 * exceeded the amount of space that has
3883523Scf46844 * been allocated, realloc() more space
3893523Scf46844 * and adjust pointers to point to the
3903523Scf46844 * (possibly moved) new block for vp->home
3913523Scf46844 */
3923523Scf46844 base_this_comp = this.comp - vp->home;
3933523Scf46844 base_fullpath = vp->fullpath - vp->home;
3943523Scf46844 base_tmppath = vp->tmppath - vp->home;
3953523Scf46844 vp->len *= 2;
3963523Scf46844 tmp = (char *)realloc(vp->home, vp->len);
3973523Scf46844 if (tmp == NULL) {
3983523Scf46844 rc = -1;
3993523Scf46844 goto quit;
4003523Scf46844 }
4013523Scf46844 vp->home = tmp;
4023523Scf46844 comp = vp->home + base_comp;
4033523Scf46844 component = vp->home + base_component;
4043523Scf46844 this.comp = vp->home + base_this_comp;
4053523Scf46844 vp->fullpath = vp->home + base_fullpath;
4063523Scf46844 vp->tmppath = vp->home + base_tmppath;
4073523Scf46844 if (last != NULL && last->comp != NULL) {
4083523Scf46844 last->comp = vp->home + base_last_comp;
4093523Scf46844 }
4103523Scf46844 }
4110Sstevel@tonic-gate p = component;
4123523Scf46844 while (*q != '\0')
4130Sstevel@tonic-gate *p++ = *q++;
4140Sstevel@tonic-gate *p = '\0';
4150Sstevel@tonic-gate vp->state.level++;
4160Sstevel@tonic-gate
4170Sstevel@tonic-gate /* Call walk() recursively. */
4180Sstevel@tonic-gate rc = walk(p, fn, depth-1, &this, vp);
4193523Scf46844 if (last != NULL && last->comp != NULL) {
4203523Scf46844 last->comp = vp->home + base_last_comp;
4213523Scf46844 }
4223523Scf46844 comp = vp->home + base_comp;
4233523Scf46844 component = vp->home + base_component;
4240Sstevel@tonic-gate vp->state.level--;
4250Sstevel@tonic-gate if (this.fd == 0) {
4260Sstevel@tonic-gate *component = 0;
4270Sstevel@tonic-gate if (vp->curflags & FTW_CHDIR) {
4280Sstevel@tonic-gate this.fd = opendir(".");
4290Sstevel@tonic-gate } else {
4303523Scf46844 this.fd = (*vp->opendirf)(comp);
4310Sstevel@tonic-gate }
4320Sstevel@tonic-gate if (this.fd == 0) {
4330Sstevel@tonic-gate rc = -1;
4340Sstevel@tonic-gate goto quit;
4350Sstevel@tonic-gate }
4360Sstevel@tonic-gate seekdir(this.fd, this.here);
4370Sstevel@tonic-gate }
4380Sstevel@tonic-gate if (rc != 0) {
4390Sstevel@tonic-gate if (errno == ENOENT) {
4400Sstevel@tonic-gate (void) fprintf(stderr, "cannot open %s: %s\n",
4410Sstevel@tonic-gate vp->tmppath, strerror(errno));
4420Sstevel@tonic-gate val = rc;
4430Sstevel@tonic-gate continue;
4440Sstevel@tonic-gate }
4450Sstevel@tonic-gate goto quit; /* this seems extreme */
4460Sstevel@tonic-gate }
4470Sstevel@tonic-gate }
4480Sstevel@tonic-gate vp->state.base = oldbase;
4490Sstevel@tonic-gate *--component = 0;
4500Sstevel@tonic-gate type = FTW_DP;
4510Sstevel@tonic-gate if ((vp->tmppath[0] != '\0') && (vp->curflags & FTW_DEPTH) && !skip)
4520Sstevel@tonic-gate rc = (*fn)(vp->tmppath, &statb, type, &vp->state);
4530Sstevel@tonic-gate quit:
4540Sstevel@tonic-gate if (cdval >= 0 && last) {
4550Sstevel@tonic-gate /* try to change back to previous directory */
4560Sstevel@tonic-gate if (last->fd != NULL) {
4570Sstevel@tonic-gate if (fchdir(last->fd->dd_fd) < 0) {
4580Sstevel@tonic-gate rc = -1;
4590Sstevel@tonic-gate }
4600Sstevel@tonic-gate } else {
4610Sstevel@tonic-gate if ((cdval = chdir("..")) >= 0) {
4625302Sth199096 if ((*vp->statf)(".", &statb, last, 0) < 0 ||
4630Sstevel@tonic-gate statb.st_ino != last->inode ||
4640Sstevel@tonic-gate statb.st_dev != last->dev)
4650Sstevel@tonic-gate cdval = -1;
4660Sstevel@tonic-gate }
4670Sstevel@tonic-gate *comp = 0;
4680Sstevel@tonic-gate if (cdval < 0) {
4690Sstevel@tonic-gate if (chdir(vp->fullpath) < 0) {
4700Sstevel@tonic-gate rc = -1;
4710Sstevel@tonic-gate } else {
4725302Sth199096 /* Security check */
4735302Sth199096 if ((vp->curflags & FTW_PHYS) &&
4745302Sth199096 ((*vp->statf)(".", &statb,
4755302Sth199096 last, 0) < 0 ||
4765302Sth199096 statb.st_ino != last->inode ||
4775302Sth199096 statb.st_dev != last->dev)) {
4785302Sth199096 errno = EAGAIN;
4795302Sth199096 rc = -1;
4805302Sth199096 }
4810Sstevel@tonic-gate }
4820Sstevel@tonic-gate }
4830Sstevel@tonic-gate }
4840Sstevel@tonic-gate }
4855302Sth199096
4860Sstevel@tonic-gate if (this.fd)
4870Sstevel@tonic-gate (void) closedir(this.fd);
4880Sstevel@tonic-gate if (val > rc)
4890Sstevel@tonic-gate return (val);
4900Sstevel@tonic-gate else
4910Sstevel@tonic-gate return (rc);
4920Sstevel@tonic-gate }
4930Sstevel@tonic-gate
4940Sstevel@tonic-gate int
nftw(const char * path,int (* fn)(const char *,const struct stat *,int,struct FTW *),int depth,int flags)495*6812Sraf nftw(const char *path,
4960Sstevel@tonic-gate int (*fn)(const char *, const struct stat *, int, struct FTW *),
4970Sstevel@tonic-gate int depth, int flags)
4980Sstevel@tonic-gate {
4990Sstevel@tonic-gate struct Var var;
5000Sstevel@tonic-gate struct stat statb;
5010Sstevel@tonic-gate int rc = -1;
5020Sstevel@tonic-gate char *dp;
5030Sstevel@tonic-gate char *base;
5040Sstevel@tonic-gate char *endhome;
5050Sstevel@tonic-gate const char *savepath = path;
5060Sstevel@tonic-gate int save_errno;
5070Sstevel@tonic-gate
5083523Scf46844 var.walklevel = 0;
5093523Scf46844 var.len = 2*(PATH_MAX+1);
5103523Scf46844 var.home = (char *)malloc(var.len);
5113523Scf46844 if (var.home == NULL)
5123523Scf46844 return (-1);
5133523Scf46844
5143523Scf46844 var.home[0] = 0;
5150Sstevel@tonic-gate
5160Sstevel@tonic-gate /*
5170Sstevel@tonic-gate * If the walk is going to change directory before
5183523Scf46844 * reading it, save current working directory.
5190Sstevel@tonic-gate */
5200Sstevel@tonic-gate if (flags & FTW_CHDIR) {
5213523Scf46844 if (getcwd(var.home, PATH_MAX+1) == 0) {
5223523Scf46844 free(var.home);
5230Sstevel@tonic-gate return (-1);
5243523Scf46844 }
5250Sstevel@tonic-gate }
5263523Scf46844 endhome = dp = var.home + strlen(var.home);
5270Sstevel@tonic-gate if (*path == '/')
5280Sstevel@tonic-gate var.fullpath = dp;
5290Sstevel@tonic-gate else {
5300Sstevel@tonic-gate *dp++ = '/';
5313523Scf46844 var.fullpath = var.home;
5320Sstevel@tonic-gate }
5330Sstevel@tonic-gate var.tmppath = dp;
5340Sstevel@tonic-gate base = dp-1;
5353523Scf46844 while (*path) {
5360Sstevel@tonic-gate *dp = *path;
5370Sstevel@tonic-gate if (*dp == '/')
5380Sstevel@tonic-gate base = dp;
5390Sstevel@tonic-gate dp++, path++;
5400Sstevel@tonic-gate }
5410Sstevel@tonic-gate *dp = 0;
5420Sstevel@tonic-gate var.state.base = (int)(base + 1 - var.tmppath);
5430Sstevel@tonic-gate if (*path) {
5443523Scf46844 free(var.home);
5450Sstevel@tonic-gate errno = ENAMETOOLONG;
5460Sstevel@tonic-gate return (-1);
5470Sstevel@tonic-gate }
5480Sstevel@tonic-gate var.curflags = flags;
5490Sstevel@tonic-gate
5500Sstevel@tonic-gate /*
5513523Scf46844 * If doing chdir()'s, set var.opendirf to cdopendir.
5523523Scf46844 * If not doing chdir()'s and if nftw()'s depth arg >= 2,
5533523Scf46844 * set var.opendirf to nocdopendir. In order to
5543523Scf46844 * descend to arbitrary depths without doing chdir()'s, nftw()
5553523Scf46844 * requires a depth arg >= 2 so that nocdopendir() can use openat()
5563523Scf46844 * to traverse the directories. So when not doing
5573523Scf46844 * chdir()'s if nftw()'s depth arg <= 1, set var.opendirf to
5583523Scf46844 * cdopendir.
5590Sstevel@tonic-gate * If doing a physical walk (not following symbolic link), set
5603523Scf46844 * var.statf to cdlstat() or nocdlstat(). Otherwise, set var.statf
5613523Scf46844 * to cdstat() or nocdstat().
5620Sstevel@tonic-gate */
5633523Scf46844 if (((flags & FTW_CHDIR) == 0) && (depth >= 2)) {
5643523Scf46844 var.opendirf = nocdopendir;
5653523Scf46844 if (flags & FTW_PHYS)
5663523Scf46844 var.statf = nocdlstat;
5673523Scf46844 else
5683523Scf46844 var.statf = nocdstat;
5693523Scf46844 } else {
5703523Scf46844 var.opendirf = cdopendir;
5713523Scf46844 if (flags & FTW_PHYS)
5723523Scf46844 var.statf = cdlstat;
5733523Scf46844 else
5743523Scf46844 var.statf = cdstat;
5753523Scf46844 }
5760Sstevel@tonic-gate
5770Sstevel@tonic-gate /*
5780Sstevel@tonic-gate * If walk is not going to cross a mount point,
5790Sstevel@tonic-gate * save the current mount point.
5800Sstevel@tonic-gate */
5810Sstevel@tonic-gate if (flags & FTW_MOUNT) {
5825302Sth199096 if ((*var.statf)(savepath, &statb, NULL, 0) >= 0)
5830Sstevel@tonic-gate var.cur_mount = statb.st_dev;
5840Sstevel@tonic-gate else
5850Sstevel@tonic-gate goto done;
5860Sstevel@tonic-gate }
5870Sstevel@tonic-gate var.state.level = 0;
5880Sstevel@tonic-gate
5890Sstevel@tonic-gate /*
5900Sstevel@tonic-gate * Call walk() which does most of the work.
5910Sstevel@tonic-gate * walk() uses errno in a rather obtuse way
5920Sstevel@tonic-gate * so we shield any incoming errno.
5930Sstevel@tonic-gate */
5940Sstevel@tonic-gate save_errno = errno;
5950Sstevel@tonic-gate errno = 0;
5960Sstevel@tonic-gate var.savedstatf = NULL;
5970Sstevel@tonic-gate rc = walk(dp, fn, depth, (struct Save *)0, &var);
5980Sstevel@tonic-gate if (errno == 0)
5990Sstevel@tonic-gate errno = save_errno;
6000Sstevel@tonic-gate done:
6010Sstevel@tonic-gate *endhome = 0;
6020Sstevel@tonic-gate if (flags & FTW_CHDIR)
6033523Scf46844 (void) chdir(var.home);
6043523Scf46844 free(var.home);
6050Sstevel@tonic-gate return (rc);
6060Sstevel@tonic-gate }
6070Sstevel@tonic-gate
6080Sstevel@tonic-gate /*
6093523Scf46844 * Get stat info on path when FTW_CHDIR is set.
6103523Scf46844 */
6113523Scf46844 /*ARGSUSED1*/
6123523Scf46844 static int
cdstat(const char * path,struct stat * statp,struct Save * lp,int flags)6135302Sth199096 cdstat(const char *path, struct stat *statp, struct Save *lp, int flags)
6145302Sth199096 {
6155302Sth199096 return (fstatat(AT_FDCWD, path, statp, flags));
6163523Scf46844 }
6173523Scf46844
6183523Scf46844 /*
6193523Scf46844 * Get lstat info on path when FTW_CHDIR is set.
6203523Scf46844 */
6213523Scf46844 /*ARGSUSED1*/
6223523Scf46844 static int
cdlstat(const char * path,struct stat * statp,struct Save * lp,int flags)6235302Sth199096 cdlstat(const char *path, struct stat *statp, struct Save *lp, int flags)
6243523Scf46844 {
6255302Sth199096 return (fstatat(AT_FDCWD, path, statp,
6265302Sth199096 flags | AT_SYMLINK_NOFOLLOW));
6273523Scf46844 }
6283523Scf46844
6293523Scf46844 /*
6303523Scf46844 * Get stat info on path when FTW_CHDIR is not set.
6313523Scf46844 */
6323523Scf46844 static int
nocdstat(const char * path,struct stat * statp,struct Save * lp,int flags)6335302Sth199096 nocdstat(const char *path, struct stat *statp, struct Save *lp, int flags)
6343523Scf46844 {
6355302Sth199096 int fd;
6365302Sth199096 const char *basepath;
6373523Scf46844
6383523Scf46844 if (lp && lp->fd) {
6393523Scf46844 /* get basename of path */
6405302Sth199096 basepath = get_unrooted(path);
6415302Sth199096
6425302Sth199096 fd = lp->fd->dd_fd;
6433523Scf46844 } else {
6445302Sth199096 basepath = path;
6455302Sth199096
6465302Sth199096 fd = AT_FDCWD;
6473523Scf46844 }
6485302Sth199096
6495302Sth199096 return (fstatat(fd, basepath, statp, flags));
6503523Scf46844 }
6513523Scf46844
6523523Scf46844 /*
6533523Scf46844 * Get lstat info on path when FTW_CHDIR is not set.
6543523Scf46844 */
6553523Scf46844 static int
nocdlstat(const char * path,struct stat * statp,struct Save * lp,int flags)6565302Sth199096 nocdlstat(const char *path, struct stat *statp, struct Save *lp, int flags)
6573523Scf46844 {
6585302Sth199096 int fd;
6595302Sth199096 const char *basepath;
6603523Scf46844
6613523Scf46844 if (lp && lp->fd) {
6623523Scf46844 /* get basename of path */
6635302Sth199096 basepath = get_unrooted(path);
6645302Sth199096
6655302Sth199096 fd = lp->fd->dd_fd;
6663523Scf46844 } else {
6675302Sth199096 basepath = path;
6685302Sth199096
6695302Sth199096 fd = AT_FDCWD;
6703523Scf46844 }
6715302Sth199096
6725302Sth199096 return (fstatat(fd, basepath, statp, flags | AT_SYMLINK_NOFOLLOW));
6733523Scf46844 }
6743523Scf46844
6753523Scf46844 /*
6763523Scf46844 * Open path directory when FTW_CHDIR is set.
6773523Scf46844 *
6783523Scf46844 */
6793523Scf46844 static DIR *
cdopendir(const char * path)6803523Scf46844 cdopendir(const char *path)
6813523Scf46844 {
6823523Scf46844 return (opendir(path));
6833523Scf46844 }
6843523Scf46844
6853523Scf46844 /*
6863523Scf46844 * Open path directory when FTW_CHDIR is not set.
6873523Scf46844 */
6883523Scf46844 static DIR *
nocdopendir(const char * path)6893523Scf46844 nocdopendir(const char *path)
6903523Scf46844 {
6913523Scf46844 int fd, cfd;
6923523Scf46844 DIR *fdd;
6933523Scf46844 char *dirp, *token, *ptr;
6943523Scf46844
6953523Scf46844 if (((fdd = opendir(path)) == NULL) && (errno == ENAMETOOLONG)) {
6963523Scf46844 if ((dirp = strdup(path)) == NULL) {
6973523Scf46844 errno = ENAMETOOLONG;
6983523Scf46844 return (NULL);
6993523Scf46844 }
7003523Scf46844 if ((token = strtok_r(dirp, "/", &ptr)) != NULL) {
7015302Sth199096 if ((fd = openat(AT_FDCWD, dirp, O_RDONLY)) < 0) {
7025302Sth199096 (void) free(dirp);
7035302Sth199096 errno = ENAMETOOLONG;
7045302Sth199096 return (NULL);
7055302Sth199096 }
7065302Sth199096 while ((token = strtok_r(NULL, "/", &ptr)) != NULL) {
7075302Sth199096 if ((cfd = openat(fd, token, O_RDONLY)) < 0) {
7085302Sth199096 (void) close(fd);
7095302Sth199096 (void) free(dirp);
7105302Sth199096 errno = ENAMETOOLONG;
7115302Sth199096 return (NULL);
7125302Sth199096 }
7135302Sth199096 (void) close(fd);
7145302Sth199096 fd = cfd;
7155302Sth199096 }
7163523Scf46844 (void) free(dirp);
7175302Sth199096 return (fdopendir(fd));
7183523Scf46844 }
7193523Scf46844 (void) free(dirp);
7203523Scf46844 errno = ENAMETOOLONG;
7213523Scf46844 }
7223523Scf46844 return (fdd);
7233523Scf46844 }
7243523Scf46844
7255302Sth199096 /*
7265302Sth199096 * return pointer basename of path, which may contain trailing slashes
7275302Sth199096 *
7285302Sth199096 * We do this when we do not chdir() on the input.
7295302Sth199096 */
7303523Scf46844 static const char *
get_unrooted(const char * path)7313523Scf46844 get_unrooted(const char *path)
7323523Scf46844 {
7333523Scf46844 const char *ptr;
7343523Scf46844
7353523Scf46844 if (!path || !*path)
7363523Scf46844 return (NULL);
7373523Scf46844
7383523Scf46844 ptr = path + strlen(path);
7393523Scf46844 /* find last char in path before any trailing slashes */
7403523Scf46844 while (ptr != path && *--ptr == '/')
7413523Scf46844 ;
7423523Scf46844
7433523Scf46844 if (ptr == path) /* all slashes */
7443523Scf46844 return (ptr);
7453523Scf46844
7463523Scf46844 while (ptr != path)
7473523Scf46844 if (*--ptr == '/')
7483523Scf46844 return (++ptr);
7493523Scf46844
7503523Scf46844 return (ptr);
7513523Scf46844 }
7523523Scf46844
7533523Scf46844 /*
7540Sstevel@tonic-gate * close the oldest directory. It saves the seek offset.
7550Sstevel@tonic-gate * return value is 0 unless it was unable to close any descriptor
7560Sstevel@tonic-gate */
7570Sstevel@tonic-gate
7580Sstevel@tonic-gate static int
oldclose(struct Save * sp)7590Sstevel@tonic-gate oldclose(struct Save *sp)
7600Sstevel@tonic-gate {
7610Sstevel@tonic-gate struct Save *spnext;
7620Sstevel@tonic-gate while (sp) {
7630Sstevel@tonic-gate spnext = sp->last;
7640Sstevel@tonic-gate if (spnext == 0 || spnext->fd == 0)
7650Sstevel@tonic-gate break;
7660Sstevel@tonic-gate sp = spnext;
7670Sstevel@tonic-gate }
7680Sstevel@tonic-gate if (sp == 0 || sp->fd == 0)
7690Sstevel@tonic-gate return (0);
7700Sstevel@tonic-gate sp->here = telldir(sp->fd);
7710Sstevel@tonic-gate (void) closedir(sp->fd);
7720Sstevel@tonic-gate sp->fd = 0;
7730Sstevel@tonic-gate return (1);
7740Sstevel@tonic-gate }
775