xref: /onnv-gate/usr/src/lib/libc/port/gen/nftw.c (revision 1219:f89f56c2d9ac)
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
50Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
60Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
70Sstevel@tonic-gate  * with the License.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
100Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
110Sstevel@tonic-gate  * See the License for the specific language governing permissions
120Sstevel@tonic-gate  * and limitations under the License.
130Sstevel@tonic-gate  *
140Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
150Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
160Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
170Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
180Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
190Sstevel@tonic-gate  *
200Sstevel@tonic-gate  * CDDL HEADER END
210Sstevel@tonic-gate  */
22*1219Sraf 
230Sstevel@tonic-gate /*
24*1219Sraf  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
250Sstevel@tonic-gate  * Use is subject to license terms.
260Sstevel@tonic-gate  */
270Sstevel@tonic-gate 
280Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
290Sstevel@tonic-gate 
300Sstevel@tonic-gate /*	Copyright (c) 1988 AT&T	*/
310Sstevel@tonic-gate /*	  All Rights Reserved  	*/
320Sstevel@tonic-gate 
330Sstevel@tonic-gate 
340Sstevel@tonic-gate /*
350Sstevel@tonic-gate  *	nftw - new file tree walk
360Sstevel@tonic-gate  *
370Sstevel@tonic-gate  *	int nftw(char *path, int (*fn)(), int depth, int flags);
380Sstevel@tonic-gate  *
390Sstevel@tonic-gate  *	Derived from System V ftw() by David Korn
400Sstevel@tonic-gate  *
410Sstevel@tonic-gate  *	nftw visits each file and directory in the tree starting at
420Sstevel@tonic-gate  *	path. It uses the generic directory reading library so it works
430Sstevel@tonic-gate  *	for any file system type.  The flags field is used to specify:
440Sstevel@tonic-gate  *		FTW_PHYS  Physical walk, does not follow symblolic links
450Sstevel@tonic-gate  *			  Otherwise, nftw will follow links but will not
460Sstevel@tonic-gate  *			  walk down any path the crosses itself.
470Sstevel@tonic-gate  *		FTW_MOUNT The walk will not cross a mount point.
480Sstevel@tonic-gate  *		FTW_DEPTH All subdirectories will be visited before the
490Sstevel@tonic-gate  *			  directory itself.
500Sstevel@tonic-gate  *		FTW_CHDIR The walk will change to each directory before
510Sstevel@tonic-gate  *			  reading it.  This is faster but core dumps
520Sstevel@tonic-gate  *			  may not get generated.
530Sstevel@tonic-gate  *
540Sstevel@tonic-gate  *	The following flags are private, and are used by the find
550Sstevel@tonic-gate  *	utility:
560Sstevel@tonic-gate  *		FTW_ANYERR Call the callback function and return
570Sstevel@tonic-gate  *			   FTW_NS on any stat failure, not just
580Sstevel@tonic-gate  *			   lack of permission.
590Sstevel@tonic-gate  *		FTW_HOPTION Use stat the first time the walk
600Sstevel@tonic-gate  *			    function is called, regardless of
610Sstevel@tonic-gate  *			    whether or not FTW_PHYS is specified.
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.
810Sstevel@tonic-gate  *	The fourth argument is a struct FTW* which contains the depth
820Sstevel@tonic-gate  *	and the offset into pathname to the base name.
830Sstevel@tonic-gate  *	If fn returns nonzero, nftw returns this value to its caller.
840Sstevel@tonic-gate  *
850Sstevel@tonic-gate  *	depth limits the number of open directories that ftw uses
860Sstevel@tonic-gate  *	before it starts recycling file descriptors.  In general,
870Sstevel@tonic-gate  *	a file descriptor is used for each level.
880Sstevel@tonic-gate  *
890Sstevel@tonic-gate  */
900Sstevel@tonic-gate 
910Sstevel@tonic-gate #include <sys/feature_tests.h>
920Sstevel@tonic-gate 
930Sstevel@tonic-gate #if !defined(_LP64) && _FILE_OFFSET_BITS == 64
940Sstevel@tonic-gate #pragma weak nftw64 = _nftw64
950Sstevel@tonic-gate #define	_nftw		_nftw64
960Sstevel@tonic-gate #define	fstat64		_fstat64
970Sstevel@tonic-gate #define	lstat64		_lstat64
980Sstevel@tonic-gate #define	readdir64	_readdir64
990Sstevel@tonic-gate #define	stat64		_stat64
1000Sstevel@tonic-gate #else
1010Sstevel@tonic-gate #pragma weak nftw = _nftw
1020Sstevel@tonic-gate #define	fstat		_fstat
1030Sstevel@tonic-gate #define	lstat		_lstat
1040Sstevel@tonic-gate #define	readdir		_readdir
1050Sstevel@tonic-gate #define	stat		_stat
1060Sstevel@tonic-gate #endif /* !_LP64 && _FILE_OFFSET_BITS == 64 */
1070Sstevel@tonic-gate 
1080Sstevel@tonic-gate #define	chdir		_chdir
1090Sstevel@tonic-gate #define	closedir	_closedir
1100Sstevel@tonic-gate #define	fchdir		_fchdir
1110Sstevel@tonic-gate #define	fprintf		_fprintf
1120Sstevel@tonic-gate #define	getcwd		_getcwd
1130Sstevel@tonic-gate #define	opendir		_opendir
1140Sstevel@tonic-gate #define	seekdir		_seekdir
115*1219Sraf #define	strerror	_strerror
1160Sstevel@tonic-gate #define	telldir		_telldir
1170Sstevel@tonic-gate 
1180Sstevel@tonic-gate #include "lint.h"
1190Sstevel@tonic-gate #include <mtlib.h>
1200Sstevel@tonic-gate #include <sys/types.h>
1210Sstevel@tonic-gate #include <sys/stat.h>
1220Sstevel@tonic-gate #include <dirent.h>
1230Sstevel@tonic-gate #include <errno.h>
1240Sstevel@tonic-gate #include <limits.h>
1250Sstevel@tonic-gate #include <ftw.h>
1260Sstevel@tonic-gate #include <stdlib.h>
1270Sstevel@tonic-gate #include <string.h>
1280Sstevel@tonic-gate #include <unistd.h>
1290Sstevel@tonic-gate #include <thread.h>
1300Sstevel@tonic-gate #include <synch.h>
1310Sstevel@tonic-gate #include <stdio.h>
1320Sstevel@tonic-gate 
1330Sstevel@tonic-gate #ifndef PATH_MAX
1340Sstevel@tonic-gate #define	PATH_MAX	1023
1350Sstevel@tonic-gate #endif
1360Sstevel@tonic-gate 
1370Sstevel@tonic-gate /*
1380Sstevel@tonic-gate  * Local variables (used to be static local).
1390Sstevel@tonic-gate  * Putting them into a structure that is passed
1400Sstevel@tonic-gate  * around makes nftw() MT-safe with no locking required.
1410Sstevel@tonic-gate  */
1420Sstevel@tonic-gate struct Var {
1430Sstevel@tonic-gate 	char	*fullpath;
1440Sstevel@tonic-gate 	char	*tmppath;
1450Sstevel@tonic-gate 	int	curflags;
1460Sstevel@tonic-gate 	dev_t	cur_mount;
1470Sstevel@tonic-gate 	struct FTW state;
1480Sstevel@tonic-gate 	int	walklevel;
1490Sstevel@tonic-gate 	int	(*statf)(const char *, struct stat *);
1500Sstevel@tonic-gate 	int	(*savedstatf)(const char *, struct stat *);
1510Sstevel@tonic-gate };
1520Sstevel@tonic-gate 
1530Sstevel@tonic-gate struct Save {
1540Sstevel@tonic-gate 	struct Save *last;
1550Sstevel@tonic-gate 	DIR	*fd;
1560Sstevel@tonic-gate 	char	*comp;
1570Sstevel@tonic-gate 	long	here;
1580Sstevel@tonic-gate 	dev_t	dev;
1590Sstevel@tonic-gate 	ino_t	inode;
1600Sstevel@tonic-gate };
1610Sstevel@tonic-gate 
1620Sstevel@tonic-gate static int oldclose(struct Save *);
1630Sstevel@tonic-gate 
1640Sstevel@tonic-gate /*
1650Sstevel@tonic-gate  * This is the recursive walker.
1660Sstevel@tonic-gate  */
1670Sstevel@tonic-gate static int
1680Sstevel@tonic-gate walk(char *component,
1690Sstevel@tonic-gate     int (*fn)(const char *, const struct stat *, int, struct FTW *),
1700Sstevel@tonic-gate     int depth, struct Save *last, struct Var *vp)
1710Sstevel@tonic-gate {
1720Sstevel@tonic-gate 	struct stat statb;
1730Sstevel@tonic-gate 	char *p;
1740Sstevel@tonic-gate 	int type;
1750Sstevel@tonic-gate 	char *comp;
1760Sstevel@tonic-gate 	struct dirent *dir;
1770Sstevel@tonic-gate 	char *q;
1780Sstevel@tonic-gate 	int rc = 0;
1790Sstevel@tonic-gate 	int val = -1;
1800Sstevel@tonic-gate 	int cdval = -1;
1810Sstevel@tonic-gate 	int oldbase;
1820Sstevel@tonic-gate 	int skip;
1830Sstevel@tonic-gate 	struct Save this;
1840Sstevel@tonic-gate 
1850Sstevel@tonic-gate 	this.last = last;
1860Sstevel@tonic-gate 	this.fd = 0;
1870Sstevel@tonic-gate 	if ((vp->curflags & FTW_CHDIR) && last)
1880Sstevel@tonic-gate 		comp = last->comp;
1890Sstevel@tonic-gate 	else
1900Sstevel@tonic-gate 		comp = vp->tmppath;
1910Sstevel@tonic-gate 
1920Sstevel@tonic-gate 	if (vp->savedstatf == NULL)
1930Sstevel@tonic-gate 		vp->savedstatf = vp->statf;
1940Sstevel@tonic-gate 
1950Sstevel@tonic-gate 	if ((vp->walklevel++ == 0) && (vp->curflags & FTW_HOPTION))
1960Sstevel@tonic-gate 		vp->statf = stat;
1970Sstevel@tonic-gate 	else
1980Sstevel@tonic-gate 		vp->statf = vp->savedstatf;
1990Sstevel@tonic-gate 
2000Sstevel@tonic-gate 	/*
2010Sstevel@tonic-gate 	 * Determine the type of the component.
2020Sstevel@tonic-gate 	 */
2030Sstevel@tonic-gate 	if ((*vp->statf)(comp, &statb) >= 0) {
2040Sstevel@tonic-gate 		if ((statb.st_mode & S_IFMT) == S_IFDIR) {
2050Sstevel@tonic-gate 			type = FTW_D;
2060Sstevel@tonic-gate 			if (depth <= 1)
2070Sstevel@tonic-gate 				(void) oldclose(last);
2080Sstevel@tonic-gate 			if ((this.fd = opendir(comp)) == 0) {
2090Sstevel@tonic-gate 				if (errno == EMFILE && oldclose(last) &&
2100Sstevel@tonic-gate 				    (this.fd = opendir(comp)) != 0) {
2110Sstevel@tonic-gate 					depth = 1;
2120Sstevel@tonic-gate 				} else {
2130Sstevel@tonic-gate 					type = FTW_DNR;
2140Sstevel@tonic-gate 					goto fail;
2150Sstevel@tonic-gate 				}
2160Sstevel@tonic-gate 			}
2170Sstevel@tonic-gate 			if (statb.st_fstype[0] == 'a' &&
2180Sstevel@tonic-gate 			    strcmp(statb.st_fstype, "autofs") == 0) {
2190Sstevel@tonic-gate 				/*
2200Sstevel@tonic-gate 				 * this dir is on autofs
2210Sstevel@tonic-gate 				 */
2220Sstevel@tonic-gate 				if (fstat(this.fd->dd_fd, &statb) < 0) {
2230Sstevel@tonic-gate 					(void) closedir(this.fd);
2240Sstevel@tonic-gate 					type = FTW_NS;
2250Sstevel@tonic-gate 					goto fail;
2260Sstevel@tonic-gate 				}
2270Sstevel@tonic-gate 			}
2280Sstevel@tonic-gate 		} else if ((statb.st_mode & S_IFMT) == S_IFLNK) {
2290Sstevel@tonic-gate 			type = FTW_SL;
2300Sstevel@tonic-gate 		} else {
2310Sstevel@tonic-gate 			type = FTW_F;
2320Sstevel@tonic-gate 		}
2330Sstevel@tonic-gate 	} else if ((vp->curflags & FTW_ANYERR) && errno != ENOENT) {
2340Sstevel@tonic-gate 		/*
2350Sstevel@tonic-gate 		 * If FTW_ANYERR is specified, then a stat error
2360Sstevel@tonic-gate 		 * other than ENOENT automatically results in
2370Sstevel@tonic-gate 		 * failure.  This allows the callback function
2380Sstevel@tonic-gate 		 * to properly handle ENAMETOOLONG and ELOOP and
2390Sstevel@tonic-gate 		 * things of that nature, that would be masked
2400Sstevel@tonic-gate 		 * by calling lstat before failing.
2410Sstevel@tonic-gate 		 */
2420Sstevel@tonic-gate 		type = FTW_NS;
2430Sstevel@tonic-gate 		goto fail;
2440Sstevel@tonic-gate 	} else {
2450Sstevel@tonic-gate 		/*
2460Sstevel@tonic-gate 		 * Statf has failed. If stat was used instead of lstat,
2470Sstevel@tonic-gate 		 * try using lstat. If lstat doesn't fail, "comp"
2480Sstevel@tonic-gate 		 * must be a symbolic link pointing to a non-existent
2490Sstevel@tonic-gate 		 * file. Such a symbolic link should be ignored.
2500Sstevel@tonic-gate 		 * Also check the file type, if possible, for symbolic
2510Sstevel@tonic-gate 		 * link.
2520Sstevel@tonic-gate 		 */
2530Sstevel@tonic-gate 		if ((vp->statf == stat) && (lstat(comp, &statb) >= 0) &&
2540Sstevel@tonic-gate 		    ((statb.st_mode & S_IFMT) == S_IFLNK)) {
2550Sstevel@tonic-gate 
2560Sstevel@tonic-gate 			/*
2570Sstevel@tonic-gate 			 * Ignore bad symbolic link, let "fn"
2580Sstevel@tonic-gate 			 * report it.
2590Sstevel@tonic-gate 			 */
2600Sstevel@tonic-gate 
2610Sstevel@tonic-gate 			errno = ENOENT;
2620Sstevel@tonic-gate 			type = FTW_SLN;
2630Sstevel@tonic-gate 		} else {
2640Sstevel@tonic-gate 			type = FTW_NS;
2650Sstevel@tonic-gate 	fail:
2660Sstevel@tonic-gate 			/*
2670Sstevel@tonic-gate 			 * if FTW_ANYERR is set in flags, we call
2680Sstevel@tonic-gate 			 * the user function with FTW_NS set, regardless
2690Sstevel@tonic-gate 			 * of the reason stat failed.
2700Sstevel@tonic-gate 			 */
2710Sstevel@tonic-gate 			if (!(vp->curflags & FTW_ANYERR))
2720Sstevel@tonic-gate 				if (errno != EACCES)
2730Sstevel@tonic-gate 					return (-1);
2740Sstevel@tonic-gate 		}
2750Sstevel@tonic-gate 	}
2760Sstevel@tonic-gate 
2770Sstevel@tonic-gate 	/*
2780Sstevel@tonic-gate 	 * If the walk is not supposed to cross a mount point,
2790Sstevel@tonic-gate 	 * and it did, get ready to return.
2800Sstevel@tonic-gate 	 */
2810Sstevel@tonic-gate 	if ((vp->curflags & FTW_MOUNT) && type != FTW_NS &&
2820Sstevel@tonic-gate 	    statb.st_dev != vp->cur_mount)
2830Sstevel@tonic-gate 		goto quit;
2840Sstevel@tonic-gate 	vp->state.quit = 0;
2850Sstevel@tonic-gate 
2860Sstevel@tonic-gate 	/*
2870Sstevel@tonic-gate 	 * If current component is not a directory, call user
2880Sstevel@tonic-gate 	 * specified function and get ready to return.
2890Sstevel@tonic-gate 	 */
2900Sstevel@tonic-gate 	if (type != FTW_D || (vp->curflags & FTW_DEPTH) == 0)
2910Sstevel@tonic-gate 		rc = (*fn)(vp->tmppath, &statb, type, &vp->state);
2920Sstevel@tonic-gate 	if (rc > 0)
2930Sstevel@tonic-gate 		val = rc;
2940Sstevel@tonic-gate 	skip = (vp->state.quit & FTW_SKD);
2950Sstevel@tonic-gate 	if (rc != 0 || type != FTW_D || (vp->state.quit & FTW_PRUNE))
2960Sstevel@tonic-gate 		goto quit;
2970Sstevel@tonic-gate 
2980Sstevel@tonic-gate 	if (vp->tmppath[0] != '\0' && component[-1] != '/')
2990Sstevel@tonic-gate 		*component++ = '/';
3000Sstevel@tonic-gate 	if (vp->curflags & FTW_CHDIR) {
3010Sstevel@tonic-gate 		struct stat statb2;
3020Sstevel@tonic-gate 
3030Sstevel@tonic-gate 		*component = 0;
3040Sstevel@tonic-gate 		/*
3050Sstevel@tonic-gate 		 * Security check (there is a window between
3060Sstevel@tonic-gate 		 * (*vp->statf)() and opendir() above).
3070Sstevel@tonic-gate 		 */
3080Sstevel@tonic-gate 		if ((vp->curflags & FTW_PHYS) &&
3090Sstevel@tonic-gate 		    (fstat(this.fd->dd_fd, &statb2) < 0 ||
3100Sstevel@tonic-gate 		    statb2.st_ino != statb.st_ino ||
3110Sstevel@tonic-gate 		    statb2.st_dev != statb.st_dev)) {
3120Sstevel@tonic-gate 			errno = EAGAIN;
3130Sstevel@tonic-gate 			rc = -1;
3140Sstevel@tonic-gate 			goto quit;
3150Sstevel@tonic-gate 		}
3160Sstevel@tonic-gate 
3170Sstevel@tonic-gate 		if ((cdval = fchdir(this.fd->dd_fd)) >= 0) {
3180Sstevel@tonic-gate 			this.comp = component;
3190Sstevel@tonic-gate 		} else {
3200Sstevel@tonic-gate 			type = FTW_DNR;
3210Sstevel@tonic-gate 			rc = (*fn)(vp->tmppath, &statb, type, &vp->state);
3220Sstevel@tonic-gate 			goto quit;
3230Sstevel@tonic-gate 		}
3240Sstevel@tonic-gate 	}
3250Sstevel@tonic-gate 
3260Sstevel@tonic-gate 	/*
3270Sstevel@tonic-gate 	 * If the walk has followed a symbolic link, traverse
3280Sstevel@tonic-gate 	 * the walk back to make sure there is not a loop.
3290Sstevel@tonic-gate 	 *
3300Sstevel@tonic-gate 	 * XXX - may need to look at this
3310Sstevel@tonic-gate 	 * There's code to check for cycles, but only for FTW_PHYS
3320Sstevel@tonic-gate 	 * (find -L flag).  However, all directories should be
3330Sstevel@tonic-gate 	 * checked, even if not following links because of hardlinks
3340Sstevel@tonic-gate 	 * to directories (not recommended, but can exist).
3350Sstevel@tonic-gate 	 *
3360Sstevel@tonic-gate 	 * We might have added AVL tree routines here to store and search
3370Sstevel@tonic-gate 	 * the inodes and devices, as is done for du/ls/chgrp/chown,
3380Sstevel@tonic-gate 	 * but libcmdutils is for for internal use only, so we can't
3390Sstevel@tonic-gate 	 * add it to a public libc function (nftw()).
3400Sstevel@tonic-gate 	 */
3410Sstevel@tonic-gate 	if ((vp->curflags & FTW_PHYS) == 0) {
3420Sstevel@tonic-gate 		struct Save *sp = last;
3430Sstevel@tonic-gate 		while (sp) {
3440Sstevel@tonic-gate 			/*
3450Sstevel@tonic-gate 			 * If the same node has already been visited, there
3460Sstevel@tonic-gate 			 * is a loop. Get ready to return.
3470Sstevel@tonic-gate 			 */
3480Sstevel@tonic-gate 			if (sp->dev == statb.st_dev &&
3490Sstevel@tonic-gate 			    sp->inode == statb.st_ino)
3500Sstevel@tonic-gate 				goto quit;
3510Sstevel@tonic-gate 			sp = sp->last;
3520Sstevel@tonic-gate 		}
3530Sstevel@tonic-gate 	}
3540Sstevel@tonic-gate 	this.dev = statb.st_dev;
3550Sstevel@tonic-gate 	this.inode = statb.st_ino;
3560Sstevel@tonic-gate 	oldbase = vp->state.base;
3570Sstevel@tonic-gate 	vp->state.base = (int)(component - vp->tmppath);
3580Sstevel@tonic-gate 	while (dir = readdir(this.fd)) {
3590Sstevel@tonic-gate 		if (dir->d_ino == 0)
3600Sstevel@tonic-gate 			continue;
3610Sstevel@tonic-gate 		q = dir->d_name;
3620Sstevel@tonic-gate 		if (*q == '.') {
3630Sstevel@tonic-gate 			if (q[1] == 0)
3640Sstevel@tonic-gate 				continue;
3650Sstevel@tonic-gate 			else if (q[1] == '.' && q[2] == 0)
3660Sstevel@tonic-gate 				continue;
3670Sstevel@tonic-gate 		}
3680Sstevel@tonic-gate 		p = component;
3690Sstevel@tonic-gate 		while (p < &vp->tmppath[PATH_MAX] && *q != '\0')
3700Sstevel@tonic-gate 			*p++ = *q++;
3710Sstevel@tonic-gate 		*p = '\0';
3720Sstevel@tonic-gate 		vp->state.level++;
3730Sstevel@tonic-gate 
3740Sstevel@tonic-gate 		/* Call walk() recursively.  */
3750Sstevel@tonic-gate 		rc = walk(p, fn, depth-1, &this, vp);
3760Sstevel@tonic-gate 		vp->state.level--;
3770Sstevel@tonic-gate 		if (this.fd == 0) {
3780Sstevel@tonic-gate 			*component = 0;
3790Sstevel@tonic-gate 			if (vp->curflags & FTW_CHDIR) {
3800Sstevel@tonic-gate 				this.fd = opendir(".");
3810Sstevel@tonic-gate 			} else {
3820Sstevel@tonic-gate 				this.fd = opendir(comp);
3830Sstevel@tonic-gate 			}
3840Sstevel@tonic-gate 			if (this.fd == 0) {
3850Sstevel@tonic-gate 				rc = -1;
3860Sstevel@tonic-gate 				goto quit;
3870Sstevel@tonic-gate 			}
3880Sstevel@tonic-gate 			seekdir(this.fd, this.here);
3890Sstevel@tonic-gate 		}
3900Sstevel@tonic-gate 		if (rc != 0) {
3910Sstevel@tonic-gate 			if (errno == ENOENT) {
3920Sstevel@tonic-gate 				(void) fprintf(stderr, "cannot open %s: %s\n",
3930Sstevel@tonic-gate 				    vp->tmppath, strerror(errno));
3940Sstevel@tonic-gate 				val = rc;
3950Sstevel@tonic-gate 				continue;
3960Sstevel@tonic-gate 			}
3970Sstevel@tonic-gate 			goto quit;	/* this seems extreme */
3980Sstevel@tonic-gate 		}
3990Sstevel@tonic-gate 	}
4000Sstevel@tonic-gate 	vp->state.base = oldbase;
4010Sstevel@tonic-gate 	*--component = 0;
4020Sstevel@tonic-gate 	type = FTW_DP;
4030Sstevel@tonic-gate 	if ((vp->tmppath[0] != '\0') && (vp->curflags & FTW_DEPTH) && !skip)
4040Sstevel@tonic-gate 		rc = (*fn)(vp->tmppath, &statb, type, &vp->state);
4050Sstevel@tonic-gate quit:
4060Sstevel@tonic-gate 	if (cdval >= 0 && last) {
4070Sstevel@tonic-gate 		/* try to change back to previous directory */
4080Sstevel@tonic-gate 		if (last->fd != NULL) {
4090Sstevel@tonic-gate 			if (fchdir(last->fd->dd_fd) < 0) {
4100Sstevel@tonic-gate 				rc = -1;
4110Sstevel@tonic-gate 			}
4120Sstevel@tonic-gate 		} else {
4130Sstevel@tonic-gate 			if ((cdval = chdir("..")) >= 0) {
4140Sstevel@tonic-gate 				if ((*vp->statf)(".", &statb) < 0 ||
4150Sstevel@tonic-gate 				    statb.st_ino != last->inode ||
4160Sstevel@tonic-gate 				    statb.st_dev != last->dev)
4170Sstevel@tonic-gate 					cdval = -1;
4180Sstevel@tonic-gate 			}
4190Sstevel@tonic-gate 			*comp = 0;
4200Sstevel@tonic-gate 			if (cdval < 0) {
4210Sstevel@tonic-gate 				if (chdir(vp->fullpath) < 0) {
4220Sstevel@tonic-gate 					rc = -1;
4230Sstevel@tonic-gate 				} else {
4240Sstevel@tonic-gate 					/* Security check */
4250Sstevel@tonic-gate 					if ((vp->curflags & FTW_PHYS) &&
4260Sstevel@tonic-gate 					    ((*vp->statf)(".", &statb) < 0 ||
4270Sstevel@tonic-gate 					    statb.st_ino != last->inode ||
4280Sstevel@tonic-gate 					    statb.st_dev != last->dev)) {
4290Sstevel@tonic-gate 						errno = EAGAIN;
4300Sstevel@tonic-gate 						rc = -1;
4310Sstevel@tonic-gate 					}
4320Sstevel@tonic-gate 				}
4330Sstevel@tonic-gate 			}
4340Sstevel@tonic-gate 		}
4350Sstevel@tonic-gate 	}
4360Sstevel@tonic-gate 	if (this.fd)
4370Sstevel@tonic-gate 		(void) closedir(this.fd);
4380Sstevel@tonic-gate 	if (val > rc)
4390Sstevel@tonic-gate 		return (val);
4400Sstevel@tonic-gate 	else
4410Sstevel@tonic-gate 		return (rc);
4420Sstevel@tonic-gate }
4430Sstevel@tonic-gate 
4440Sstevel@tonic-gate int
4450Sstevel@tonic-gate _nftw(const char *path,
4460Sstevel@tonic-gate     int (*fn)(const char *, const struct stat *, int, struct FTW *),
4470Sstevel@tonic-gate     int depth, int flags)
4480Sstevel@tonic-gate {
4490Sstevel@tonic-gate 	struct Var var;
4500Sstevel@tonic-gate 	struct stat statb;
4510Sstevel@tonic-gate 	char home[2*(PATH_MAX+1)];
4520Sstevel@tonic-gate 	int rc = -1;
4530Sstevel@tonic-gate 	char *dp;
4540Sstevel@tonic-gate 	char *base;
4550Sstevel@tonic-gate 	char *endhome;
4560Sstevel@tonic-gate 	const char *savepath = path;
4570Sstevel@tonic-gate 	int save_errno;
4580Sstevel@tonic-gate 
4590Sstevel@tonic-gate 	home[0] = 0;
4600Sstevel@tonic-gate 
4610Sstevel@tonic-gate 	/*
4620Sstevel@tonic-gate 	 * If the walk is going to change directory before
4630Sstevel@tonic-gate 	 * reading it, save current woring directory.
4640Sstevel@tonic-gate 	 */
4650Sstevel@tonic-gate 	if (flags & FTW_CHDIR) {
4660Sstevel@tonic-gate 		if (getcwd(home, PATH_MAX+1) == 0)
4670Sstevel@tonic-gate 			return (-1);
4680Sstevel@tonic-gate 	}
4690Sstevel@tonic-gate 	endhome = dp = home + strlen(home);
4700Sstevel@tonic-gate 	if (*path == '/')
4710Sstevel@tonic-gate 		var.fullpath = dp;
4720Sstevel@tonic-gate 	else {
4730Sstevel@tonic-gate 		*dp++ = '/';
4740Sstevel@tonic-gate 		var.fullpath = home;
4750Sstevel@tonic-gate 	}
4760Sstevel@tonic-gate 	var.tmppath =  dp;
4770Sstevel@tonic-gate 	base = dp-1;
4780Sstevel@tonic-gate 	while (*path && dp < &var.tmppath[PATH_MAX]) {
4790Sstevel@tonic-gate 		*dp = *path;
4800Sstevel@tonic-gate 		if (*dp == '/')
4810Sstevel@tonic-gate 			base = dp;
4820Sstevel@tonic-gate 		dp++, path++;
4830Sstevel@tonic-gate 	}
4840Sstevel@tonic-gate 	*dp = 0;
4850Sstevel@tonic-gate 	var.state.base = (int)(base + 1 - var.tmppath);
4860Sstevel@tonic-gate 	if (*path) {
4870Sstevel@tonic-gate 		errno = ENAMETOOLONG;
4880Sstevel@tonic-gate 		return (-1);
4890Sstevel@tonic-gate 	}
4900Sstevel@tonic-gate 	var.curflags = flags;
4910Sstevel@tonic-gate 
4920Sstevel@tonic-gate 	/*
4930Sstevel@tonic-gate 	 * If doing a physical walk (not following symbolic link), set
4940Sstevel@tonic-gate 	 * var.statf to lstat(). Otherwise, set var.statf to stat().
4950Sstevel@tonic-gate 	 */
4960Sstevel@tonic-gate 	if ((flags & FTW_PHYS) == 0)
4970Sstevel@tonic-gate 		var.statf = stat;
4980Sstevel@tonic-gate 	else
4990Sstevel@tonic-gate 		var.statf = lstat;
5000Sstevel@tonic-gate 
5010Sstevel@tonic-gate 	/*
5020Sstevel@tonic-gate 	 * If walk is not going to cross a mount point,
5030Sstevel@tonic-gate 	 * save the current mount point.
5040Sstevel@tonic-gate 	 */
5050Sstevel@tonic-gate 	if (flags & FTW_MOUNT) {
5060Sstevel@tonic-gate 		if ((*var.statf)(savepath, &statb) >= 0)
5070Sstevel@tonic-gate 			var.cur_mount = statb.st_dev;
5080Sstevel@tonic-gate 		else
5090Sstevel@tonic-gate 			goto done;
5100Sstevel@tonic-gate 	}
5110Sstevel@tonic-gate 	var.state.level = 0;
5120Sstevel@tonic-gate 
5130Sstevel@tonic-gate 	/*
5140Sstevel@tonic-gate 	 * Call walk() which does most of the work.
5150Sstevel@tonic-gate 	 * walk() uses errno in a rather obtuse way
5160Sstevel@tonic-gate 	 * so we shield any incoming errno.
5170Sstevel@tonic-gate 	 */
5180Sstevel@tonic-gate 	save_errno = errno;
5190Sstevel@tonic-gate 	errno = 0;
5200Sstevel@tonic-gate 	var.savedstatf = NULL;
5210Sstevel@tonic-gate 	var.walklevel = 0;
5220Sstevel@tonic-gate 	rc = walk(dp, fn, depth, (struct Save *)0, &var);
5230Sstevel@tonic-gate 	if (errno == 0)
5240Sstevel@tonic-gate 		errno = save_errno;
5250Sstevel@tonic-gate done:
5260Sstevel@tonic-gate 	*endhome = 0;
5270Sstevel@tonic-gate 	if (flags & FTW_CHDIR)
5280Sstevel@tonic-gate 		(void) chdir(home);
5290Sstevel@tonic-gate 	return (rc);
5300Sstevel@tonic-gate }
5310Sstevel@tonic-gate 
5320Sstevel@tonic-gate /*
5330Sstevel@tonic-gate  * close the oldest directory.  It saves the seek offset.
5340Sstevel@tonic-gate  * return value is 0 unless it was unable to close any descriptor
5350Sstevel@tonic-gate  */
5360Sstevel@tonic-gate 
5370Sstevel@tonic-gate static int
5380Sstevel@tonic-gate oldclose(struct Save *sp)
5390Sstevel@tonic-gate {
5400Sstevel@tonic-gate 	struct Save *spnext;
5410Sstevel@tonic-gate 	while (sp) {
5420Sstevel@tonic-gate 		spnext = sp->last;
5430Sstevel@tonic-gate 		if (spnext == 0 || spnext->fd == 0)
5440Sstevel@tonic-gate 			break;
5450Sstevel@tonic-gate 		sp = spnext;
5460Sstevel@tonic-gate 	}
5470Sstevel@tonic-gate 	if (sp == 0 || sp->fd == 0)
5480Sstevel@tonic-gate 		return (0);
5490Sstevel@tonic-gate 	sp->here = telldir(sp->fd);
5500Sstevel@tonic-gate 	(void) closedir(sp->fd);
5510Sstevel@tonic-gate 	sp->fd = 0;
5520Sstevel@tonic-gate 	return (1);
5530Sstevel@tonic-gate }
554