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