xref: /illumos-gate/usr/src/lib/libc/port/gen/nftw.c (revision 7257d1b4)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
568a94df1Scf  * Common Development and Distribution License (the "License").
668a94df1Scf  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
21e8031f0aSraf 
227c478bd9Sstevel@tonic-gate /*
23*7257d1b4Sraf  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate /*	Copyright (c) 1988 AT&T	*/
287c478bd9Sstevel@tonic-gate /*	  All Rights Reserved  	*/
297c478bd9Sstevel@tonic-gate 
30*7257d1b4Sraf #pragma ident	"%Z%%M%	%I%	%E% SMI"
317c478bd9Sstevel@tonic-gate 
327c478bd9Sstevel@tonic-gate /*
337c478bd9Sstevel@tonic-gate  *	nftw - new file tree walk
347c478bd9Sstevel@tonic-gate  *
357c478bd9Sstevel@tonic-gate  *	int nftw(char *path, int (*fn)(), int depth, int flags);
367c478bd9Sstevel@tonic-gate  *
377c478bd9Sstevel@tonic-gate  *	Derived from System V ftw() by David Korn
387c478bd9Sstevel@tonic-gate  *
397c478bd9Sstevel@tonic-gate  *	nftw visits each file and directory in the tree starting at
407c478bd9Sstevel@tonic-gate  *	path. It uses the generic directory reading library so it works
417c478bd9Sstevel@tonic-gate  *	for any file system type.  The flags field is used to specify:
4268a94df1Scf  *		FTW_PHYS  Physical walk, does not follow symbolic links
437c478bd9Sstevel@tonic-gate  *			  Otherwise, nftw will follow links but will not
447c478bd9Sstevel@tonic-gate  *			  walk down any path the crosses itself.
457c478bd9Sstevel@tonic-gate  *		FTW_MOUNT The walk will not cross a mount point.
467c478bd9Sstevel@tonic-gate  *		FTW_DEPTH All subdirectories will be visited before the
477c478bd9Sstevel@tonic-gate  *			  directory itself.
487c478bd9Sstevel@tonic-gate  *		FTW_CHDIR The walk will change to each directory before
497c478bd9Sstevel@tonic-gate  *			  reading it.  This is faster but core dumps
507c478bd9Sstevel@tonic-gate  *			  may not get generated.
517c478bd9Sstevel@tonic-gate  *
527c478bd9Sstevel@tonic-gate  *	The following flags are private, and are used by the find
537c478bd9Sstevel@tonic-gate  *	utility:
547c478bd9Sstevel@tonic-gate  *		FTW_ANYERR Call the callback function and return
557c478bd9Sstevel@tonic-gate  *			   FTW_NS on any stat failure, not just
567c478bd9Sstevel@tonic-gate  *			   lack of permission.
577c478bd9Sstevel@tonic-gate  *		FTW_HOPTION Use stat the first time the walk
587c478bd9Sstevel@tonic-gate  *			    function is called, regardless of
597c478bd9Sstevel@tonic-gate  *			    whether or not FTW_PHYS is specified.
6068a94df1Scf  *		FTW_NOLOOP Allow find utility to detect infinite loops created
6168a94df1Scf  *			   by both symbolic and hard linked directories.
627c478bd9Sstevel@tonic-gate  *
637c478bd9Sstevel@tonic-gate  *	fn is called with four arguments at each file and directory.
647c478bd9Sstevel@tonic-gate  *	The first argument is the pathname of the object, the second
657c478bd9Sstevel@tonic-gate  *	is a pointer to the stat buffer and the third is an integer
667c478bd9Sstevel@tonic-gate  *	giving additional information as follows:
677c478bd9Sstevel@tonic-gate  *
687c478bd9Sstevel@tonic-gate  *		FTW_F	The object is a file.
697c478bd9Sstevel@tonic-gate  *		FTW_D	The object is a directory.
707c478bd9Sstevel@tonic-gate  *		FTW_DP	The object is a directory and subdirectories
717c478bd9Sstevel@tonic-gate  *			have been visited.
727c478bd9Sstevel@tonic-gate  *		FTW_SL	The object is a symbolic link.
737c478bd9Sstevel@tonic-gate  *		FTW_SLN The object is a symbolic link pointing at a
747c478bd9Sstevel@tonic-gate  *		        non-existing file.
757c478bd9Sstevel@tonic-gate  *		FTW_DNR	The object is a directory that cannot be read.
767c478bd9Sstevel@tonic-gate  *			fn will not be called for any of its descendants.
777c478bd9Sstevel@tonic-gate  *		FTW_NS	Stat failed on the object because of lack of
787c478bd9Sstevel@tonic-gate  *			appropriate permission. The stat buffer passed to fn
797c478bd9Sstevel@tonic-gate  *			is undefined.  Stat failure for any reason is
807c478bd9Sstevel@tonic-gate  *			considered an error and nftw will return -1.
8168a94df1Scf  *	The following value is private, and is used by the find utility:
8268a94df1Scf  *		FTW_DL	An infinite loop has been detected.
837c478bd9Sstevel@tonic-gate  *	The fourth argument is a struct FTW* which contains the depth
847c478bd9Sstevel@tonic-gate  *	and the offset into pathname to the base name.
857c478bd9Sstevel@tonic-gate  *	If fn returns nonzero, nftw returns this value to its caller.
867c478bd9Sstevel@tonic-gate  *
877c478bd9Sstevel@tonic-gate  *	depth limits the number of open directories that ftw uses
887c478bd9Sstevel@tonic-gate  *	before it starts recycling file descriptors.  In general,
8968a94df1Scf  *	a file descriptor is used for each level.  When FTW_CHDIR isn't set,
9068a94df1Scf  *	in order to descend to arbitrary depths, nftw requires 2 file
9168a94df1Scf  *	descriptors to be open during the call to openat(), therefore if
9268a94df1Scf  *	the depth argument is less than 2 nftw will not use openat(), and
9368a94df1Scf  *	it will fail with ENAMETOOLONG if it descends to a directory that
9468a94df1Scf  *	exceeds PATH_MAX.
957c478bd9Sstevel@tonic-gate  *
967c478bd9Sstevel@tonic-gate  */
977c478bd9Sstevel@tonic-gate 
987c478bd9Sstevel@tonic-gate #include "lint.h"
997c478bd9Sstevel@tonic-gate #include <mtlib.h>
1007c478bd9Sstevel@tonic-gate #include <sys/types.h>
1017c478bd9Sstevel@tonic-gate #include <sys/stat.h>
1027c478bd9Sstevel@tonic-gate #include <dirent.h>
1037c478bd9Sstevel@tonic-gate #include <errno.h>
1047c478bd9Sstevel@tonic-gate #include <limits.h>
1057c478bd9Sstevel@tonic-gate #include <ftw.h>
1067c478bd9Sstevel@tonic-gate #include <stdlib.h>
1077c478bd9Sstevel@tonic-gate #include <string.h>
1087c478bd9Sstevel@tonic-gate #include <unistd.h>
1097c478bd9Sstevel@tonic-gate #include <thread.h>
1107c478bd9Sstevel@tonic-gate #include <synch.h>
1117c478bd9Sstevel@tonic-gate #include <stdio.h>
11268a94df1Scf #include <strings.h>
11368a94df1Scf #include <fcntl.h>
1147c478bd9Sstevel@tonic-gate 
115*7257d1b4Sraf #if !defined(_LP64) && _FILE_OFFSET_BITS == 64
116*7257d1b4Sraf #define	nftw	nftw64
117*7257d1b4Sraf #define	stat	stat64
118*7257d1b4Sraf #define	fstat	fstat64
119*7257d1b4Sraf #define	fstatat	fstatat64
120*7257d1b4Sraf #pragma weak _nftw64 = nftw64
121*7257d1b4Sraf #else
122*7257d1b4Sraf #pragma weak _nftw = nftw
123*7257d1b4Sraf #endif /* !_LP64 && _FILE_OFFSET_BITS == 64 */
124*7257d1b4Sraf 
1257c478bd9Sstevel@tonic-gate #ifndef PATH_MAX
1267c478bd9Sstevel@tonic-gate #define	PATH_MAX	1023
1277c478bd9Sstevel@tonic-gate #endif
1287c478bd9Sstevel@tonic-gate 
1297c478bd9Sstevel@tonic-gate /*
1307c478bd9Sstevel@tonic-gate  * Local variables (used to be static local).
1317c478bd9Sstevel@tonic-gate  * Putting them into a structure that is passed
1327c478bd9Sstevel@tonic-gate  * around makes nftw() MT-safe with no locking required.
1337c478bd9Sstevel@tonic-gate  */
1347c478bd9Sstevel@tonic-gate struct Save {
1357c478bd9Sstevel@tonic-gate 	struct Save *last;
1367c478bd9Sstevel@tonic-gate 	DIR	*fd;
1377c478bd9Sstevel@tonic-gate 	char	*comp;
1387c478bd9Sstevel@tonic-gate 	long	here;
1397c478bd9Sstevel@tonic-gate 	dev_t	dev;
1407c478bd9Sstevel@tonic-gate 	ino_t	inode;
1417c478bd9Sstevel@tonic-gate };
1427c478bd9Sstevel@tonic-gate 
14368a94df1Scf struct Var {
14468a94df1Scf 	char	*home;
14568a94df1Scf 	size_t	len;
14668a94df1Scf 	char	*fullpath;
14768a94df1Scf 	char	*tmppath;
14868a94df1Scf 	int	curflags;
14968a94df1Scf 	dev_t	cur_mount;
15068a94df1Scf 	struct FTW state;
15168a94df1Scf 	int	walklevel;
152b9238976Sth 	int	(*statf)(const char *, struct stat *, struct Save *, int flags);
153b9238976Sth 	int	(*savedstatf)(const char *, struct stat *, struct Save *,
154b9238976Sth 	    int flags);
15568a94df1Scf 	DIR	*(*opendirf)(const char *);
15668a94df1Scf };
15768a94df1Scf 
1587c478bd9Sstevel@tonic-gate static int oldclose(struct Save *);
159b9238976Sth static int cdlstat(const char *, struct stat *, struct Save *, int flags);
160b9238976Sth static int cdstat(const char *, struct stat *, struct Save *, int flags);
161b9238976Sth static int nocdlstat(const char *, struct stat *, struct Save *, int flags);
162b9238976Sth static int nocdstat(const char *, struct stat *, struct Save *, int flags);
16368a94df1Scf static DIR *cdopendir(const char *);
16468a94df1Scf static DIR *nocdopendir(const char *);
16568a94df1Scf static const char *get_unrooted(const char *);
1667c478bd9Sstevel@tonic-gate 
1677c478bd9Sstevel@tonic-gate /*
1687c478bd9Sstevel@tonic-gate  * This is the recursive walker.
1697c478bd9Sstevel@tonic-gate  */
1707c478bd9Sstevel@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)1717c478bd9Sstevel@tonic-gate walk(char *component,
1727c478bd9Sstevel@tonic-gate     int (*fn)(const char *, const struct stat *, int, struct FTW *),
1737c478bd9Sstevel@tonic-gate     int depth, struct Save *last, struct Var *vp)
1747c478bd9Sstevel@tonic-gate {
1757c478bd9Sstevel@tonic-gate 	struct stat statb;
17668a94df1Scf 	char *p, *tmp;
1777c478bd9Sstevel@tonic-gate 	int type;
1787c478bd9Sstevel@tonic-gate 	char *comp;
1797c478bd9Sstevel@tonic-gate 	struct dirent *dir;
1807c478bd9Sstevel@tonic-gate 	char *q;
1817c478bd9Sstevel@tonic-gate 	int rc = 0;
1827c478bd9Sstevel@tonic-gate 	int val = -1;
1837c478bd9Sstevel@tonic-gate 	int cdval = -1;
1847c478bd9Sstevel@tonic-gate 	int oldbase;
1857c478bd9Sstevel@tonic-gate 	int skip;
1867c478bd9Sstevel@tonic-gate 	struct Save this;
18768a94df1Scf 	size_t base_comp, base_component, base_this_comp, base_last_comp;
18868a94df1Scf 	size_t base_fullpath, base_tmppath;
1897c478bd9Sstevel@tonic-gate 
1907c478bd9Sstevel@tonic-gate 	this.last = last;
1917c478bd9Sstevel@tonic-gate 	this.fd = 0;
1927c478bd9Sstevel@tonic-gate 	if ((vp->curflags & FTW_CHDIR) && last)
1937c478bd9Sstevel@tonic-gate 		comp = last->comp;
1947c478bd9Sstevel@tonic-gate 	else
1957c478bd9Sstevel@tonic-gate 		comp = vp->tmppath;
1967c478bd9Sstevel@tonic-gate 
1977c478bd9Sstevel@tonic-gate 	if (vp->savedstatf == NULL)
1987c478bd9Sstevel@tonic-gate 		vp->savedstatf = vp->statf;
1997c478bd9Sstevel@tonic-gate 
20068a94df1Scf 	if ((vp->walklevel++ == 0) && (vp->curflags & FTW_HOPTION)) {
20168a94df1Scf 		if (((vp->curflags & FTW_CHDIR) == 0) && (depth >= 2)) {
20268a94df1Scf 			vp->statf = nocdstat;
20368a94df1Scf 		} else {
20468a94df1Scf 			vp->statf = cdstat;
20568a94df1Scf 		}
20668a94df1Scf 	} else {
2077c478bd9Sstevel@tonic-gate 		vp->statf = vp->savedstatf;
20868a94df1Scf 	}
2097c478bd9Sstevel@tonic-gate 
2107c478bd9Sstevel@tonic-gate 	/*
2117c478bd9Sstevel@tonic-gate 	 * Determine the type of the component.
212b9238976Sth 	 *
213b9238976Sth 	 * Note that if the component is a trigger mount, this
214b9238976Sth 	 * will cause it to load.
2157c478bd9Sstevel@tonic-gate 	 */
216b9238976Sth 	if ((*vp->statf)(comp, &statb, last, _AT_TRIGGER) >= 0) {
2177c478bd9Sstevel@tonic-gate 		if ((statb.st_mode & S_IFMT) == S_IFDIR) {
2187c478bd9Sstevel@tonic-gate 			type = FTW_D;
2197c478bd9Sstevel@tonic-gate 			if (depth <= 1)
2207c478bd9Sstevel@tonic-gate 				(void) oldclose(last);
22168a94df1Scf 			if ((this.fd = (*vp->opendirf)(comp)) == 0) {
2227c478bd9Sstevel@tonic-gate 				if (errno == EMFILE && oldclose(last) &&
22368a94df1Scf 				    (this.fd = (*vp->opendirf)(comp)) != 0) {
22468a94df1Scf 					/*
22568a94df1Scf 					 * If opendirf fails because there
22668a94df1Scf 					 * are OPEN_MAX fd in the calling
22768a94df1Scf 					 * process, and we close the oldest
22868a94df1Scf 					 * fd, and another opendirf doesn't
22968a94df1Scf 					 * fail, depth is set to 1.
23068a94df1Scf 					 */
2317c478bd9Sstevel@tonic-gate 					depth = 1;
2327c478bd9Sstevel@tonic-gate 				} else {
2337c478bd9Sstevel@tonic-gate 					type = FTW_DNR;
2347c478bd9Sstevel@tonic-gate 					goto fail;
2357c478bd9Sstevel@tonic-gate 				}
2367c478bd9Sstevel@tonic-gate 			}
2377c478bd9Sstevel@tonic-gate 		} else if ((statb.st_mode & S_IFMT) == S_IFLNK) {
2387c478bd9Sstevel@tonic-gate 			type = FTW_SL;
2397c478bd9Sstevel@tonic-gate 		} else {
2407c478bd9Sstevel@tonic-gate 			type = FTW_F;
2417c478bd9Sstevel@tonic-gate 		}
2427c478bd9Sstevel@tonic-gate 	} else if ((vp->curflags & FTW_ANYERR) && errno != ENOENT) {
2437c478bd9Sstevel@tonic-gate 		/*
2447c478bd9Sstevel@tonic-gate 		 * If FTW_ANYERR is specified, then a stat error
2457c478bd9Sstevel@tonic-gate 		 * other than ENOENT automatically results in
2467c478bd9Sstevel@tonic-gate 		 * failure.  This allows the callback function
2477c478bd9Sstevel@tonic-gate 		 * to properly handle ENAMETOOLONG and ELOOP and
2487c478bd9Sstevel@tonic-gate 		 * things of that nature, that would be masked
2497c478bd9Sstevel@tonic-gate 		 * by calling lstat before failing.
2507c478bd9Sstevel@tonic-gate 		 */
2517c478bd9Sstevel@tonic-gate 		type = FTW_NS;
2527c478bd9Sstevel@tonic-gate 		goto fail;
2537c478bd9Sstevel@tonic-gate 	} else {
2547c478bd9Sstevel@tonic-gate 		/*
2557c478bd9Sstevel@tonic-gate 		 * Statf has failed. If stat was used instead of lstat,
2567c478bd9Sstevel@tonic-gate 		 * try using lstat. If lstat doesn't fail, "comp"
2577c478bd9Sstevel@tonic-gate 		 * must be a symbolic link pointing to a non-existent
2587c478bd9Sstevel@tonic-gate 		 * file. Such a symbolic link should be ignored.
2597c478bd9Sstevel@tonic-gate 		 * Also check the file type, if possible, for symbolic
2607c478bd9Sstevel@tonic-gate 		 * link.
2617c478bd9Sstevel@tonic-gate 		 */
26268a94df1Scf 		if (((vp->statf == cdstat) &&
263b9238976Sth 		    (cdlstat(comp, &statb, last, 0) >= 0) &&
26468a94df1Scf 		    ((statb.st_mode & S_IFMT) == S_IFLNK)) ||
26568a94df1Scf 		    ((vp->statf == nocdstat) &&
266b9238976Sth 		    (nocdlstat(comp, &statb, last, 0) >= 0) &&
26768a94df1Scf 		    ((statb.st_mode & S_IFMT) == S_IFLNK))) {
2687c478bd9Sstevel@tonic-gate 
2697c478bd9Sstevel@tonic-gate 			/*
2707c478bd9Sstevel@tonic-gate 			 * Ignore bad symbolic link, let "fn"
2717c478bd9Sstevel@tonic-gate 			 * report it.
2727c478bd9Sstevel@tonic-gate 			 */
2737c478bd9Sstevel@tonic-gate 
2747c478bd9Sstevel@tonic-gate 			errno = ENOENT;
2757c478bd9Sstevel@tonic-gate 			type = FTW_SLN;
2767c478bd9Sstevel@tonic-gate 		} else {
2777c478bd9Sstevel@tonic-gate 			type = FTW_NS;
2787c478bd9Sstevel@tonic-gate 	fail:
2797c478bd9Sstevel@tonic-gate 			/*
2807c478bd9Sstevel@tonic-gate 			 * if FTW_ANYERR is set in flags, we call
2817c478bd9Sstevel@tonic-gate 			 * the user function with FTW_NS set, regardless
2827c478bd9Sstevel@tonic-gate 			 * of the reason stat failed.
2837c478bd9Sstevel@tonic-gate 			 */
2847c478bd9Sstevel@tonic-gate 			if (!(vp->curflags & FTW_ANYERR))
2857c478bd9Sstevel@tonic-gate 				if (errno != EACCES)
2867c478bd9Sstevel@tonic-gate 					return (-1);
2877c478bd9Sstevel@tonic-gate 		}
2887c478bd9Sstevel@tonic-gate 	}
2897c478bd9Sstevel@tonic-gate 
2907c478bd9Sstevel@tonic-gate 	/*
2917c478bd9Sstevel@tonic-gate 	 * If the walk is not supposed to cross a mount point,
2927c478bd9Sstevel@tonic-gate 	 * and it did, get ready to return.
2937c478bd9Sstevel@tonic-gate 	 */
2947c478bd9Sstevel@tonic-gate 	if ((vp->curflags & FTW_MOUNT) && type != FTW_NS &&
2957c478bd9Sstevel@tonic-gate 	    statb.st_dev != vp->cur_mount)
2967c478bd9Sstevel@tonic-gate 		goto quit;
2977c478bd9Sstevel@tonic-gate 	vp->state.quit = 0;
2987c478bd9Sstevel@tonic-gate 
2997c478bd9Sstevel@tonic-gate 	/*
3007c478bd9Sstevel@tonic-gate 	 * If current component is not a directory, call user
3017c478bd9Sstevel@tonic-gate 	 * specified function and get ready to return.
3027c478bd9Sstevel@tonic-gate 	 */
3037c478bd9Sstevel@tonic-gate 	if (type != FTW_D || (vp->curflags & FTW_DEPTH) == 0)
3047c478bd9Sstevel@tonic-gate 		rc = (*fn)(vp->tmppath, &statb, type, &vp->state);
3057c478bd9Sstevel@tonic-gate 	if (rc > 0)
3067c478bd9Sstevel@tonic-gate 		val = rc;
3077c478bd9Sstevel@tonic-gate 	skip = (vp->state.quit & FTW_SKD);
3087c478bd9Sstevel@tonic-gate 	if (rc != 0 || type != FTW_D || (vp->state.quit & FTW_PRUNE))
3097c478bd9Sstevel@tonic-gate 		goto quit;
3107c478bd9Sstevel@tonic-gate 
3117c478bd9Sstevel@tonic-gate 	if (vp->tmppath[0] != '\0' && component[-1] != '/')
3127c478bd9Sstevel@tonic-gate 		*component++ = '/';
31368a94df1Scf 	*component = 0;
3147c478bd9Sstevel@tonic-gate 	if (vp->curflags & FTW_CHDIR) {
3157c478bd9Sstevel@tonic-gate 		struct stat statb2;
3167c478bd9Sstevel@tonic-gate 
3177c478bd9Sstevel@tonic-gate 		/*
3187c478bd9Sstevel@tonic-gate 		 * Security check (there is a window between
3197c478bd9Sstevel@tonic-gate 		 * (*vp->statf)() and opendir() above).
3207c478bd9Sstevel@tonic-gate 		 */
3217c478bd9Sstevel@tonic-gate 		if ((vp->curflags & FTW_PHYS) &&
3227c478bd9Sstevel@tonic-gate 		    (fstat(this.fd->dd_fd, &statb2) < 0 ||
3237c478bd9Sstevel@tonic-gate 		    statb2.st_ino != statb.st_ino ||
3247c478bd9Sstevel@tonic-gate 		    statb2.st_dev != statb.st_dev)) {
3257c478bd9Sstevel@tonic-gate 			errno = EAGAIN;
3267c478bd9Sstevel@tonic-gate 			rc = -1;
3277c478bd9Sstevel@tonic-gate 			goto quit;
3287c478bd9Sstevel@tonic-gate 		}
3297c478bd9Sstevel@tonic-gate 
3307c478bd9Sstevel@tonic-gate 		if ((cdval = fchdir(this.fd->dd_fd)) >= 0) {
3317c478bd9Sstevel@tonic-gate 			this.comp = component;
3327c478bd9Sstevel@tonic-gate 		} else {
3337c478bd9Sstevel@tonic-gate 			type = FTW_DNR;
3347c478bd9Sstevel@tonic-gate 			rc = (*fn)(vp->tmppath, &statb, type, &vp->state);
3357c478bd9Sstevel@tonic-gate 			goto quit;
3367c478bd9Sstevel@tonic-gate 		}
3377c478bd9Sstevel@tonic-gate 	}
3387c478bd9Sstevel@tonic-gate 
3397c478bd9Sstevel@tonic-gate 	/*
34068a94df1Scf 	 * If the walk has followed a symbolic link (FTW_PHYS is not set),
34168a94df1Scf 	 * traverse the walk back to make sure there is not a loop.
34268a94df1Scf 	 * The find utility (FTW_NOLOOP is set) detects infinite loops
34368a94df1Scf 	 * in both symbolic and hard linked directories.
3447c478bd9Sstevel@tonic-gate 	 */
34568a94df1Scf 	if ((vp->curflags & FTW_NOLOOP) ||
34668a94df1Scf 	    ((vp->curflags & FTW_PHYS) == 0)) {
3477c478bd9Sstevel@tonic-gate 		struct Save *sp = last;
3487c478bd9Sstevel@tonic-gate 		while (sp) {
3497c478bd9Sstevel@tonic-gate 			/*
3507c478bd9Sstevel@tonic-gate 			 * If the same node has already been visited, there
3517c478bd9Sstevel@tonic-gate 			 * is a loop. Get ready to return.
3527c478bd9Sstevel@tonic-gate 			 */
3537c478bd9Sstevel@tonic-gate 			if (sp->dev == statb.st_dev &&
35468a94df1Scf 			    sp->inode == statb.st_ino) {
35568a94df1Scf 				if (vp->curflags & FTW_NOLOOP) {
35668a94df1Scf 					/* private interface for find util */
35768a94df1Scf 					type = FTW_DL;
35868a94df1Scf 					goto fail;
35968a94df1Scf 				}
3607c478bd9Sstevel@tonic-gate 				goto quit;
36168a94df1Scf 			}
3627c478bd9Sstevel@tonic-gate 			sp = sp->last;
3637c478bd9Sstevel@tonic-gate 		}
3647c478bd9Sstevel@tonic-gate 	}
3657c478bd9Sstevel@tonic-gate 	this.dev = statb.st_dev;
3667c478bd9Sstevel@tonic-gate 	this.inode = statb.st_ino;
3677c478bd9Sstevel@tonic-gate 	oldbase = vp->state.base;
3687c478bd9Sstevel@tonic-gate 	vp->state.base = (int)(component - vp->tmppath);
3697c478bd9Sstevel@tonic-gate 	while (dir = readdir(this.fd)) {
3707c478bd9Sstevel@tonic-gate 		if (dir->d_ino == 0)
3717c478bd9Sstevel@tonic-gate 			continue;
3727c478bd9Sstevel@tonic-gate 		q = dir->d_name;
3737c478bd9Sstevel@tonic-gate 		if (*q == '.') {
3747c478bd9Sstevel@tonic-gate 			if (q[1] == 0)
3757c478bd9Sstevel@tonic-gate 				continue;
3767c478bd9Sstevel@tonic-gate 			else if (q[1] == '.' && q[2] == 0)
3777c478bd9Sstevel@tonic-gate 				continue;
3787c478bd9Sstevel@tonic-gate 		}
37968a94df1Scf 		if (last != NULL && last->comp != NULL) {
38068a94df1Scf 			base_last_comp = last->comp - vp->home;
38168a94df1Scf 		}
38268a94df1Scf 		base_comp = comp - vp->home;
38368a94df1Scf 		base_component = component - vp->home;
38468a94df1Scf 		if ((strlen(q) + strlen(vp->home) + 1) > vp->len) {
38568a94df1Scf 			/*
38668a94df1Scf 			 * When the space needed for vp->home has
38768a94df1Scf 			 * exceeded the amount of space that has
38868a94df1Scf 			 * been allocated, realloc() more space
38968a94df1Scf 			 * and adjust pointers to point to the
39068a94df1Scf 			 * (possibly moved) new block for vp->home
39168a94df1Scf 			 */
39268a94df1Scf 			base_this_comp = this.comp - vp->home;
39368a94df1Scf 			base_fullpath = vp->fullpath - vp->home;
39468a94df1Scf 			base_tmppath = vp->tmppath - vp->home;
39568a94df1Scf 			vp->len *= 2;
39668a94df1Scf 			tmp = (char *)realloc(vp->home, vp->len);
39768a94df1Scf 			if (tmp == NULL) {
39868a94df1Scf 				rc = -1;
39968a94df1Scf 				goto quit;
40068a94df1Scf 			}
40168a94df1Scf 			vp->home = tmp;
40268a94df1Scf 			comp = vp->home + base_comp;
40368a94df1Scf 			component = vp->home + base_component;
40468a94df1Scf 			this.comp = vp->home + base_this_comp;
40568a94df1Scf 			vp->fullpath = vp->home + base_fullpath;
40668a94df1Scf 			vp->tmppath = vp->home + base_tmppath;
40768a94df1Scf 			if (last != NULL && last->comp != NULL) {
40868a94df1Scf 				last->comp = vp->home + base_last_comp;
40968a94df1Scf 			}
41068a94df1Scf 		}
4117c478bd9Sstevel@tonic-gate 		p = component;
41268a94df1Scf 		while (*q != '\0')
4137c478bd9Sstevel@tonic-gate 			*p++ = *q++;
4147c478bd9Sstevel@tonic-gate 		*p = '\0';
4157c478bd9Sstevel@tonic-gate 		vp->state.level++;
4167c478bd9Sstevel@tonic-gate 
4177c478bd9Sstevel@tonic-gate 		/* Call walk() recursively.  */
4187c478bd9Sstevel@tonic-gate 		rc = walk(p, fn, depth-1, &this, vp);
41968a94df1Scf 		if (last != NULL && last->comp != NULL) {
42068a94df1Scf 			last->comp = vp->home + base_last_comp;
42168a94df1Scf 		}
42268a94df1Scf 		comp = vp->home + base_comp;
42368a94df1Scf 		component = vp->home + base_component;
4247c478bd9Sstevel@tonic-gate 		vp->state.level--;
4257c478bd9Sstevel@tonic-gate 		if (this.fd == 0) {
4267c478bd9Sstevel@tonic-gate 			*component = 0;
4277c478bd9Sstevel@tonic-gate 			if (vp->curflags & FTW_CHDIR) {
4287c478bd9Sstevel@tonic-gate 				this.fd = opendir(".");
4297c478bd9Sstevel@tonic-gate 			} else {
43068a94df1Scf 				this.fd = (*vp->opendirf)(comp);
4317c478bd9Sstevel@tonic-gate 			}
4327c478bd9Sstevel@tonic-gate 			if (this.fd == 0) {
4337c478bd9Sstevel@tonic-gate 				rc = -1;
4347c478bd9Sstevel@tonic-gate 				goto quit;
4357c478bd9Sstevel@tonic-gate 			}
4367c478bd9Sstevel@tonic-gate 			seekdir(this.fd, this.here);
4377c478bd9Sstevel@tonic-gate 		}
4387c478bd9Sstevel@tonic-gate 		if (rc != 0) {
4397c478bd9Sstevel@tonic-gate 			if (errno == ENOENT) {
4407c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr, "cannot open %s: %s\n",
4417c478bd9Sstevel@tonic-gate 				    vp->tmppath, strerror(errno));
4427c478bd9Sstevel@tonic-gate 				val = rc;
4437c478bd9Sstevel@tonic-gate 				continue;
4447c478bd9Sstevel@tonic-gate 			}
4457c478bd9Sstevel@tonic-gate 			goto quit;	/* this seems extreme */
4467c478bd9Sstevel@tonic-gate 		}
4477c478bd9Sstevel@tonic-gate 	}
4487c478bd9Sstevel@tonic-gate 	vp->state.base = oldbase;
4497c478bd9Sstevel@tonic-gate 	*--component = 0;
4507c478bd9Sstevel@tonic-gate 	type = FTW_DP;
4517c478bd9Sstevel@tonic-gate 	if ((vp->tmppath[0] != '\0') && (vp->curflags & FTW_DEPTH) && !skip)
4527c478bd9Sstevel@tonic-gate 		rc = (*fn)(vp->tmppath, &statb, type, &vp->state);
4537c478bd9Sstevel@tonic-gate quit:
4547c478bd9Sstevel@tonic-gate 	if (cdval >= 0 && last) {
4557c478bd9Sstevel@tonic-gate 		/* try to change back to previous directory */
4567c478bd9Sstevel@tonic-gate 		if (last->fd != NULL) {
4577c478bd9Sstevel@tonic-gate 			if (fchdir(last->fd->dd_fd) < 0) {
4587c478bd9Sstevel@tonic-gate 				rc = -1;
4597c478bd9Sstevel@tonic-gate 			}
4607c478bd9Sstevel@tonic-gate 		} else {
4617c478bd9Sstevel@tonic-gate 			if ((cdval = chdir("..")) >= 0) {
462b9238976Sth 				if ((*vp->statf)(".", &statb, last, 0) < 0 ||
4637c478bd9Sstevel@tonic-gate 				    statb.st_ino != last->inode ||
4647c478bd9Sstevel@tonic-gate 				    statb.st_dev != last->dev)
4657c478bd9Sstevel@tonic-gate 					cdval = -1;
4667c478bd9Sstevel@tonic-gate 			}
4677c478bd9Sstevel@tonic-gate 			*comp = 0;
4687c478bd9Sstevel@tonic-gate 			if (cdval < 0) {
4697c478bd9Sstevel@tonic-gate 				if (chdir(vp->fullpath) < 0) {
4707c478bd9Sstevel@tonic-gate 					rc = -1;
4717c478bd9Sstevel@tonic-gate 				} else {
472b9238976Sth 					/* Security check */
473b9238976Sth 					if ((vp->curflags & FTW_PHYS) &&
474b9238976Sth 					    ((*vp->statf)(".", &statb,
475b9238976Sth 					    last, 0) < 0 ||
476b9238976Sth 					    statb.st_ino != last->inode ||
477b9238976Sth 					    statb.st_dev != last->dev)) {
478b9238976Sth 						errno = EAGAIN;
479b9238976Sth 						rc = -1;
480b9238976Sth 					}
4817c478bd9Sstevel@tonic-gate 				}
4827c478bd9Sstevel@tonic-gate 			}
4837c478bd9Sstevel@tonic-gate 		}
4847c478bd9Sstevel@tonic-gate 	}
485b9238976Sth 
4867c478bd9Sstevel@tonic-gate 	if (this.fd)
4877c478bd9Sstevel@tonic-gate 		(void) closedir(this.fd);
4887c478bd9Sstevel@tonic-gate 	if (val > rc)
4897c478bd9Sstevel@tonic-gate 		return (val);
4907c478bd9Sstevel@tonic-gate 	else
4917c478bd9Sstevel@tonic-gate 		return (rc);
4927c478bd9Sstevel@tonic-gate }
4937c478bd9Sstevel@tonic-gate 
4947c478bd9Sstevel@tonic-gate int
nftw(const char * path,int (* fn)(const char *,const struct stat *,int,struct FTW *),int depth,int flags)495*7257d1b4Sraf nftw(const char *path,
4967c478bd9Sstevel@tonic-gate     int (*fn)(const char *, const struct stat *, int, struct FTW *),
4977c478bd9Sstevel@tonic-gate     int depth, int flags)
4987c478bd9Sstevel@tonic-gate {
4997c478bd9Sstevel@tonic-gate 	struct Var var;
5007c478bd9Sstevel@tonic-gate 	struct stat statb;
5017c478bd9Sstevel@tonic-gate 	int rc = -1;
5027c478bd9Sstevel@tonic-gate 	char *dp;
5037c478bd9Sstevel@tonic-gate 	char *base;
5047c478bd9Sstevel@tonic-gate 	char *endhome;
5057c478bd9Sstevel@tonic-gate 	const char *savepath = path;
5067c478bd9Sstevel@tonic-gate 	int save_errno;
5077c478bd9Sstevel@tonic-gate 
50868a94df1Scf 	var.walklevel = 0;
50968a94df1Scf 	var.len = 2*(PATH_MAX+1);
51068a94df1Scf 	var.home = (char *)malloc(var.len);
51168a94df1Scf 	if (var.home == NULL)
51268a94df1Scf 		return (-1);
51368a94df1Scf 
51468a94df1Scf 	var.home[0] = 0;
5157c478bd9Sstevel@tonic-gate 
5167c478bd9Sstevel@tonic-gate 	/*
5177c478bd9Sstevel@tonic-gate 	 * If the walk is going to change directory before
51868a94df1Scf 	 * reading it, save current working directory.
5197c478bd9Sstevel@tonic-gate 	 */
5207c478bd9Sstevel@tonic-gate 	if (flags & FTW_CHDIR) {
52168a94df1Scf 		if (getcwd(var.home, PATH_MAX+1) == 0) {
52268a94df1Scf 			free(var.home);
5237c478bd9Sstevel@tonic-gate 			return (-1);
52468a94df1Scf 		}
5257c478bd9Sstevel@tonic-gate 	}
52668a94df1Scf 	endhome = dp = var.home + strlen(var.home);
5277c478bd9Sstevel@tonic-gate 	if (*path == '/')
5287c478bd9Sstevel@tonic-gate 		var.fullpath = dp;
5297c478bd9Sstevel@tonic-gate 	else {
5307c478bd9Sstevel@tonic-gate 		*dp++ = '/';
53168a94df1Scf 		var.fullpath = var.home;
5327c478bd9Sstevel@tonic-gate 	}
5337c478bd9Sstevel@tonic-gate 	var.tmppath =  dp;
5347c478bd9Sstevel@tonic-gate 	base = dp-1;
53568a94df1Scf 	while (*path) {
5367c478bd9Sstevel@tonic-gate 		*dp = *path;
5377c478bd9Sstevel@tonic-gate 		if (*dp == '/')
5387c478bd9Sstevel@tonic-gate 			base = dp;
5397c478bd9Sstevel@tonic-gate 		dp++, path++;
5407c478bd9Sstevel@tonic-gate 	}
5417c478bd9Sstevel@tonic-gate 	*dp = 0;
5427c478bd9Sstevel@tonic-gate 	var.state.base = (int)(base + 1 - var.tmppath);
5437c478bd9Sstevel@tonic-gate 	if (*path) {
54468a94df1Scf 		free(var.home);
5457c478bd9Sstevel@tonic-gate 		errno = ENAMETOOLONG;
5467c478bd9Sstevel@tonic-gate 		return (-1);
5477c478bd9Sstevel@tonic-gate 	}
5487c478bd9Sstevel@tonic-gate 	var.curflags = flags;
5497c478bd9Sstevel@tonic-gate 
5507c478bd9Sstevel@tonic-gate 	/*
55168a94df1Scf 	 * If doing chdir()'s, set var.opendirf to cdopendir.
55268a94df1Scf 	 * If not doing chdir()'s and if nftw()'s depth arg >= 2,
55368a94df1Scf 	 * set var.opendirf to nocdopendir.  In order to
55468a94df1Scf 	 * descend to arbitrary depths without doing chdir()'s, nftw()
55568a94df1Scf 	 * requires a depth arg >= 2 so that nocdopendir() can use openat()
55668a94df1Scf 	 * to traverse the directories.  So when not doing
55768a94df1Scf 	 * chdir()'s if nftw()'s depth arg <= 1, set var.opendirf to
55868a94df1Scf 	 * cdopendir.
5597c478bd9Sstevel@tonic-gate 	 * If doing a physical walk (not following symbolic link), set
56068a94df1Scf 	 * var.statf to cdlstat() or nocdlstat(). Otherwise, set var.statf
56168a94df1Scf 	 * to cdstat() or nocdstat().
5627c478bd9Sstevel@tonic-gate 	 */
56368a94df1Scf 	if (((flags & FTW_CHDIR) == 0) && (depth >= 2)) {
56468a94df1Scf 		var.opendirf = nocdopendir;
56568a94df1Scf 		if (flags & FTW_PHYS)
56668a94df1Scf 			var.statf = nocdlstat;
56768a94df1Scf 		else
56868a94df1Scf 			var.statf = nocdstat;
56968a94df1Scf 	} else {
57068a94df1Scf 		var.opendirf = cdopendir;
57168a94df1Scf 		if (flags & FTW_PHYS)
57268a94df1Scf 			var.statf = cdlstat;
57368a94df1Scf 		else
57468a94df1Scf 			var.statf = cdstat;
57568a94df1Scf 	}
5767c478bd9Sstevel@tonic-gate 
5777c478bd9Sstevel@tonic-gate 	/*
5787c478bd9Sstevel@tonic-gate 	 * If walk is not going to cross a mount point,
5797c478bd9Sstevel@tonic-gate 	 * save the current mount point.
5807c478bd9Sstevel@tonic-gate 	 */
5817c478bd9Sstevel@tonic-gate 	if (flags & FTW_MOUNT) {
582b9238976Sth 		if ((*var.statf)(savepath, &statb, NULL, 0) >= 0)
5837c478bd9Sstevel@tonic-gate 			var.cur_mount = statb.st_dev;
5847c478bd9Sstevel@tonic-gate 		else
5857c478bd9Sstevel@tonic-gate 			goto done;
5867c478bd9Sstevel@tonic-gate 	}
5877c478bd9Sstevel@tonic-gate 	var.state.level = 0;
5887c478bd9Sstevel@tonic-gate 
5897c478bd9Sstevel@tonic-gate 	/*
5907c478bd9Sstevel@tonic-gate 	 * Call walk() which does most of the work.
5917c478bd9Sstevel@tonic-gate 	 * walk() uses errno in a rather obtuse way
5927c478bd9Sstevel@tonic-gate 	 * so we shield any incoming errno.
5937c478bd9Sstevel@tonic-gate 	 */
5947c478bd9Sstevel@tonic-gate 	save_errno = errno;
5957c478bd9Sstevel@tonic-gate 	errno = 0;
5967c478bd9Sstevel@tonic-gate 	var.savedstatf = NULL;
5977c478bd9Sstevel@tonic-gate 	rc = walk(dp, fn, depth, (struct Save *)0, &var);
5987c478bd9Sstevel@tonic-gate 	if (errno == 0)
5997c478bd9Sstevel@tonic-gate 		errno = save_errno;
6007c478bd9Sstevel@tonic-gate done:
6017c478bd9Sstevel@tonic-gate 	*endhome = 0;
6027c478bd9Sstevel@tonic-gate 	if (flags & FTW_CHDIR)
60368a94df1Scf 		(void) chdir(var.home);
60468a94df1Scf 	free(var.home);
6057c478bd9Sstevel@tonic-gate 	return (rc);
6067c478bd9Sstevel@tonic-gate }
6077c478bd9Sstevel@tonic-gate 
60868a94df1Scf /*
60968a94df1Scf  * Get stat info on path when FTW_CHDIR is set.
61068a94df1Scf  */
61168a94df1Scf /*ARGSUSED1*/
61268a94df1Scf static int
cdstat(const char * path,struct stat * statp,struct Save * lp,int flags)613b9238976Sth cdstat(const char *path, struct stat *statp, struct Save *lp, int flags)
614b9238976Sth {
615b9238976Sth 	return (fstatat(AT_FDCWD, path, statp, flags));
61668a94df1Scf }
61768a94df1Scf 
61868a94df1Scf /*
61968a94df1Scf  * Get lstat info on path when FTW_CHDIR is set.
62068a94df1Scf  */
62168a94df1Scf /*ARGSUSED1*/
62268a94df1Scf static int
cdlstat(const char * path,struct stat * statp,struct Save * lp,int flags)623b9238976Sth cdlstat(const char *path, struct stat *statp, struct Save *lp, int flags)
62468a94df1Scf {
625b9238976Sth 	return (fstatat(AT_FDCWD, path, statp,
626b9238976Sth 	    flags | AT_SYMLINK_NOFOLLOW));
62768a94df1Scf }
62868a94df1Scf 
62968a94df1Scf /*
63068a94df1Scf  * Get stat info on path when FTW_CHDIR is not set.
63168a94df1Scf  */
63268a94df1Scf static int
nocdstat(const char * path,struct stat * statp,struct Save * lp,int flags)633b9238976Sth nocdstat(const char *path, struct stat *statp, struct Save *lp, int flags)
63468a94df1Scf {
635b9238976Sth 	int		fd;
636b9238976Sth 	const char	*basepath;
63768a94df1Scf 
63868a94df1Scf 	if (lp && lp->fd) {
63968a94df1Scf 		/* get basename of path */
640b9238976Sth 		basepath = get_unrooted(path);
641b9238976Sth 
642b9238976Sth 		fd = lp->fd->dd_fd;
64368a94df1Scf 	} else {
644b9238976Sth 		basepath = path;
645b9238976Sth 
646b9238976Sth 		fd = AT_FDCWD;
64768a94df1Scf 	}
648b9238976Sth 
649b9238976Sth 	return (fstatat(fd, basepath, statp, flags));
65068a94df1Scf }
65168a94df1Scf 
65268a94df1Scf /*
65368a94df1Scf  * Get lstat info on path when FTW_CHDIR is not set.
65468a94df1Scf  */
65568a94df1Scf static int
nocdlstat(const char * path,struct stat * statp,struct Save * lp,int flags)656b9238976Sth nocdlstat(const char *path, struct stat *statp, struct Save *lp, int flags)
65768a94df1Scf {
658b9238976Sth 	int		fd;
659b9238976Sth 	const char	*basepath;
66068a94df1Scf 
66168a94df1Scf 	if (lp && lp->fd) {
66268a94df1Scf 		/* get basename of path */
663b9238976Sth 		basepath = get_unrooted(path);
664b9238976Sth 
665b9238976Sth 		fd = lp->fd->dd_fd;
66668a94df1Scf 	} else {
667b9238976Sth 		basepath = path;
668b9238976Sth 
669b9238976Sth 		fd = AT_FDCWD;
67068a94df1Scf 	}
671b9238976Sth 
672b9238976Sth 	return (fstatat(fd, basepath, statp, flags | AT_SYMLINK_NOFOLLOW));
67368a94df1Scf }
67468a94df1Scf 
67568a94df1Scf /*
67668a94df1Scf  * Open path directory when FTW_CHDIR is set.
67768a94df1Scf  *
67868a94df1Scf  */
67968a94df1Scf static DIR *
cdopendir(const char * path)68068a94df1Scf cdopendir(const char *path)
68168a94df1Scf {
68268a94df1Scf 	return (opendir(path));
68368a94df1Scf }
68468a94df1Scf 
68568a94df1Scf /*
68668a94df1Scf  * Open path directory when FTW_CHDIR is not set.
68768a94df1Scf  */
68868a94df1Scf static DIR *
nocdopendir(const char * path)68968a94df1Scf nocdopendir(const char *path)
69068a94df1Scf {
69168a94df1Scf 	int fd, cfd;
69268a94df1Scf 	DIR *fdd;
69368a94df1Scf 	char *dirp, *token, *ptr;
69468a94df1Scf 
69568a94df1Scf 	if (((fdd = opendir(path)) == NULL) && (errno == ENAMETOOLONG)) {
69668a94df1Scf 		if ((dirp = strdup(path)) == NULL) {
69768a94df1Scf 			errno = ENAMETOOLONG;
69868a94df1Scf 			return (NULL);
69968a94df1Scf 		}
70068a94df1Scf 		if ((token = strtok_r(dirp, "/", &ptr)) != NULL) {
701b9238976Sth 			if ((fd = openat(AT_FDCWD, dirp, O_RDONLY)) < 0) {
702b9238976Sth 				(void) free(dirp);
703b9238976Sth 				errno = ENAMETOOLONG;
704b9238976Sth 				return (NULL);
705b9238976Sth 			}
706b9238976Sth 			while ((token = strtok_r(NULL, "/", &ptr)) != NULL) {
707b9238976Sth 				if ((cfd = openat(fd, token, O_RDONLY)) < 0) {
708b9238976Sth 					(void) close(fd);
709b9238976Sth 					(void) free(dirp);
710b9238976Sth 					errno = ENAMETOOLONG;
711b9238976Sth 					return (NULL);
712b9238976Sth 				}
713b9238976Sth 				(void) close(fd);
714b9238976Sth 				fd = cfd;
71568a94df1Scf 			}
716b9238976Sth 			(void) free(dirp);
717b9238976Sth 			return (fdopendir(fd));
71868a94df1Scf 		}
71968a94df1Scf 		(void) free(dirp);
72068a94df1Scf 		errno = ENAMETOOLONG;
72168a94df1Scf 	}
72268a94df1Scf 	return (fdd);
72368a94df1Scf }
72468a94df1Scf 
725b9238976Sth /*
726b9238976Sth  * return pointer basename of path, which may contain trailing slashes
727b9238976Sth  *
728b9238976Sth  * We do this when we do not chdir() on the input.
729b9238976Sth  */
73068a94df1Scf static const char *
get_unrooted(const char * path)73168a94df1Scf get_unrooted(const char *path)
73268a94df1Scf {
73368a94df1Scf 	const char *ptr;
73468a94df1Scf 
73568a94df1Scf 	if (!path || !*path)
73668a94df1Scf 		return (NULL);
73768a94df1Scf 
73868a94df1Scf 	ptr = path + strlen(path);
73968a94df1Scf 	/* find last char in path before any trailing slashes */
74068a94df1Scf 	while (ptr != path && *--ptr == '/')
74168a94df1Scf 		;
74268a94df1Scf 
74368a94df1Scf 	if (ptr == path)	/* all slashes */
74468a94df1Scf 		return (ptr);
74568a94df1Scf 
74668a94df1Scf 	while (ptr != path)
74768a94df1Scf 		if (*--ptr == '/')
74868a94df1Scf 			return (++ptr);
74968a94df1Scf 
75068a94df1Scf 	return (ptr);
75168a94df1Scf }
75268a94df1Scf 
7537c478bd9Sstevel@tonic-gate /*
7547c478bd9Sstevel@tonic-gate  * close the oldest directory.  It saves the seek offset.
7557c478bd9Sstevel@tonic-gate  * return value is 0 unless it was unable to close any descriptor
7567c478bd9Sstevel@tonic-gate  */
7577c478bd9Sstevel@tonic-gate 
7587c478bd9Sstevel@tonic-gate static int
oldclose(struct Save * sp)7597c478bd9Sstevel@tonic-gate oldclose(struct Save *sp)
7607c478bd9Sstevel@tonic-gate {
7617c478bd9Sstevel@tonic-gate 	struct Save *spnext;
7627c478bd9Sstevel@tonic-gate 	while (sp) {
7637c478bd9Sstevel@tonic-gate 		spnext = sp->last;
7647c478bd9Sstevel@tonic-gate 		if (spnext == 0 || spnext->fd == 0)
7657c478bd9Sstevel@tonic-gate 			break;
7667c478bd9Sstevel@tonic-gate 		sp = spnext;
7677c478bd9Sstevel@tonic-gate 	}
7687c478bd9Sstevel@tonic-gate 	if (sp == 0 || sp->fd == 0)
7697c478bd9Sstevel@tonic-gate 		return (0);
7707c478bd9Sstevel@tonic-gate 	sp->here = telldir(sp->fd);
7717c478bd9Sstevel@tonic-gate 	(void) closedir(sp->fd);
7727c478bd9Sstevel@tonic-gate 	sp->fd = 0;
7737c478bd9Sstevel@tonic-gate 	return (1);
7747c478bd9Sstevel@tonic-gate }
775