xref: /illumos-gate/usr/src/lib/libc/port/gen/nftw.c (revision b9238976491622ad75a67ab0c12edf99e36212b9)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*	Copyright (c) 1988 AT&T	*/
30 /*	  All Rights Reserved  	*/
31 
32 
33 /*
34  *	nftw - new file tree walk
35  *
36  *	int nftw(char *path, int (*fn)(), int depth, int flags);
37  *
38  *	Derived from System V ftw() by David Korn
39  *
40  *	nftw visits each file and directory in the tree starting at
41  *	path. It uses the generic directory reading library so it works
42  *	for any file system type.  The flags field is used to specify:
43  *		FTW_PHYS  Physical walk, does not follow symbolic links
44  *			  Otherwise, nftw will follow links but will not
45  *			  walk down any path the crosses itself.
46  *		FTW_MOUNT The walk will not cross a mount point.
47  *		FTW_DEPTH All subdirectories will be visited before the
48  *			  directory itself.
49  *		FTW_CHDIR The walk will change to each directory before
50  *			  reading it.  This is faster but core dumps
51  *			  may not get generated.
52  *
53  *	The following flags are private, and are used by the find
54  *	utility:
55  *		FTW_ANYERR Call the callback function and return
56  *			   FTW_NS on any stat failure, not just
57  *			   lack of permission.
58  *		FTW_HOPTION Use stat the first time the walk
59  *			    function is called, regardless of
60  *			    whether or not FTW_PHYS is specified.
61  *		FTW_NOLOOP Allow find utility to detect infinite loops created
62  *			   by both symbolic and hard linked directories.
63  *
64  *	fn is called with four arguments at each file and directory.
65  *	The first argument is the pathname of the object, the second
66  *	is a pointer to the stat buffer and the third is an integer
67  *	giving additional information as follows:
68  *
69  *		FTW_F	The object is a file.
70  *		FTW_D	The object is a directory.
71  *		FTW_DP	The object is a directory and subdirectories
72  *			have been visited.
73  *		FTW_SL	The object is a symbolic link.
74  *		FTW_SLN The object is a symbolic link pointing at a
75  *		        non-existing file.
76  *		FTW_DNR	The object is a directory that cannot be read.
77  *			fn will not be called for any of its descendants.
78  *		FTW_NS	Stat failed on the object because of lack of
79  *			appropriate permission. The stat buffer passed to fn
80  *			is undefined.  Stat failure for any reason is
81  *			considered an error and nftw will return -1.
82  *	The following value is private, and is used by the find utility:
83  *		FTW_DL	An infinite loop has been detected.
84  *	The fourth argument is a struct FTW* which contains the depth
85  *	and the offset into pathname to the base name.
86  *	If fn returns nonzero, nftw returns this value to its caller.
87  *
88  *	depth limits the number of open directories that ftw uses
89  *	before it starts recycling file descriptors.  In general,
90  *	a file descriptor is used for each level.  When FTW_CHDIR isn't set,
91  *	in order to descend to arbitrary depths, nftw requires 2 file
92  *	descriptors to be open during the call to openat(), therefore if
93  *	the depth argument is less than 2 nftw will not use openat(), and
94  *	it will fail with ENAMETOOLONG if it descends to a directory that
95  *	exceeds PATH_MAX.
96  *
97  */
98 
99 #include <sys/feature_tests.h>
100 
101 #if !defined(_LP64) && _FILE_OFFSET_BITS == 64
102 #pragma weak nftw64 = _nftw64
103 #define	_nftw		_nftw64
104 #define	fstat64		_fstat64
105 #define	fstatat64	_fstatat64
106 #define	lstat64		_lstat64
107 #define	openat64	_openat64
108 #define	readdir64	_readdir64
109 #define	stat64		_stat64
110 #else
111 #pragma weak nftw = _nftw
112 #define	fstat		_fstat
113 #define	fstatat		_fstatat
114 #define	lstat		_lstat
115 #define	openat		_openat
116 #define	readdir		_readdir
117 #define	stat		_stat
118 #endif /* !_LP64 && _FILE_OFFSET_BITS == 64 */
119 
120 #define	chdir		_chdir
121 #define	close		_close
122 #define	closedir	_closedir
123 #define	fchdir		_fchdir
124 #define	fdopendir	_fdopendir
125 #define	fprintf		_fprintf
126 #define	getcwd		_getcwd
127 #define	opendir		_opendir
128 #define	seekdir		_seekdir
129 #define	strdup		_strdup
130 #define	strerror	_strerror
131 #define	strtok_r	_strtok_r
132 #define	telldir		_telldir
133 
134 #include "lint.h"
135 #include <mtlib.h>
136 #include <sys/types.h>
137 #include <sys/stat.h>
138 #include <dirent.h>
139 #include <errno.h>
140 #include <limits.h>
141 #include <ftw.h>
142 #include <stdlib.h>
143 #include <string.h>
144 #include <unistd.h>
145 #include <thread.h>
146 #include <synch.h>
147 #include <stdio.h>
148 #include <strings.h>
149 #include <fcntl.h>
150 
151 #ifndef PATH_MAX
152 #define	PATH_MAX	1023
153 #endif
154 
155 /*
156  * Local variables (used to be static local).
157  * Putting them into a structure that is passed
158  * around makes nftw() MT-safe with no locking required.
159  */
160 struct Save {
161 	struct Save *last;
162 	DIR	*fd;
163 	char	*comp;
164 	long	here;
165 	dev_t	dev;
166 	ino_t	inode;
167 };
168 
169 struct Var {
170 	char	*home;
171 	size_t	len;
172 	char	*fullpath;
173 	char	*tmppath;
174 	int	curflags;
175 	dev_t	cur_mount;
176 	struct FTW state;
177 	int	walklevel;
178 	int	(*statf)(const char *, struct stat *, struct Save *, int flags);
179 	int	(*savedstatf)(const char *, struct stat *, struct Save *,
180 	    int flags);
181 	DIR	*(*opendirf)(const char *);
182 };
183 
184 static int oldclose(struct Save *);
185 static int cdlstat(const char *, struct stat *, struct Save *, int flags);
186 static int cdstat(const char *, struct stat *, struct Save *, int flags);
187 static int nocdlstat(const char *, struct stat *, struct Save *, int flags);
188 static int nocdstat(const char *, struct stat *, struct Save *, int flags);
189 static DIR *cdopendir(const char *);
190 static DIR *nocdopendir(const char *);
191 static const char *get_unrooted(const char *);
192 
193 /*
194  * This is the recursive walker.
195  */
196 static int
197 walk(char *component,
198     int (*fn)(const char *, const struct stat *, int, struct FTW *),
199     int depth, struct Save *last, struct Var *vp)
200 {
201 	struct stat statb;
202 	char *p, *tmp;
203 	int type;
204 	char *comp;
205 	struct dirent *dir;
206 	char *q;
207 	int rc = 0;
208 	int val = -1;
209 	int cdval = -1;
210 	int oldbase;
211 	int skip;
212 	struct Save this;
213 	size_t base_comp, base_component, base_this_comp, base_last_comp;
214 	size_t base_fullpath, base_tmppath;
215 
216 	this.last = last;
217 	this.fd = 0;
218 	if ((vp->curflags & FTW_CHDIR) && last)
219 		comp = last->comp;
220 	else
221 		comp = vp->tmppath;
222 
223 	if (vp->savedstatf == NULL)
224 		vp->savedstatf = vp->statf;
225 
226 	if ((vp->walklevel++ == 0) && (vp->curflags & FTW_HOPTION)) {
227 		if (((vp->curflags & FTW_CHDIR) == 0) && (depth >= 2)) {
228 			vp->statf = nocdstat;
229 		} else {
230 			vp->statf = cdstat;
231 		}
232 	} else {
233 		vp->statf = vp->savedstatf;
234 	}
235 
236 	/*
237 	 * Determine the type of the component.
238 	 *
239 	 * Note that if the component is a trigger mount, this
240 	 * will cause it to load.
241 	 */
242 	if ((*vp->statf)(comp, &statb, last, _AT_TRIGGER) >= 0) {
243 		if ((statb.st_mode & S_IFMT) == S_IFDIR) {
244 			type = FTW_D;
245 			if (depth <= 1)
246 				(void) oldclose(last);
247 			if ((this.fd = (*vp->opendirf)(comp)) == 0) {
248 				if (errno == EMFILE && oldclose(last) &&
249 				    (this.fd = (*vp->opendirf)(comp)) != 0) {
250 					/*
251 					 * If opendirf fails because there
252 					 * are OPEN_MAX fd in the calling
253 					 * process, and we close the oldest
254 					 * fd, and another opendirf doesn't
255 					 * fail, depth is set to 1.
256 					 */
257 					depth = 1;
258 				} else {
259 					type = FTW_DNR;
260 					goto fail;
261 				}
262 			}
263 		} else if ((statb.st_mode & S_IFMT) == S_IFLNK) {
264 			type = FTW_SL;
265 		} else {
266 			type = FTW_F;
267 		}
268 	} else if ((vp->curflags & FTW_ANYERR) && errno != ENOENT) {
269 		/*
270 		 * If FTW_ANYERR is specified, then a stat error
271 		 * other than ENOENT automatically results in
272 		 * failure.  This allows the callback function
273 		 * to properly handle ENAMETOOLONG and ELOOP and
274 		 * things of that nature, that would be masked
275 		 * by calling lstat before failing.
276 		 */
277 		type = FTW_NS;
278 		goto fail;
279 	} else {
280 		/*
281 		 * Statf has failed. If stat was used instead of lstat,
282 		 * try using lstat. If lstat doesn't fail, "comp"
283 		 * must be a symbolic link pointing to a non-existent
284 		 * file. Such a symbolic link should be ignored.
285 		 * Also check the file type, if possible, for symbolic
286 		 * link.
287 		 */
288 		if (((vp->statf == cdstat) &&
289 		    (cdlstat(comp, &statb, last, 0) >= 0) &&
290 		    ((statb.st_mode & S_IFMT) == S_IFLNK)) ||
291 		    ((vp->statf == nocdstat) &&
292 		    (nocdlstat(comp, &statb, last, 0) >= 0) &&
293 		    ((statb.st_mode & S_IFMT) == S_IFLNK))) {
294 
295 			/*
296 			 * Ignore bad symbolic link, let "fn"
297 			 * report it.
298 			 */
299 
300 			errno = ENOENT;
301 			type = FTW_SLN;
302 		} else {
303 			type = FTW_NS;
304 	fail:
305 			/*
306 			 * if FTW_ANYERR is set in flags, we call
307 			 * the user function with FTW_NS set, regardless
308 			 * of the reason stat failed.
309 			 */
310 			if (!(vp->curflags & FTW_ANYERR))
311 				if (errno != EACCES)
312 					return (-1);
313 		}
314 	}
315 
316 	/*
317 	 * If the walk is not supposed to cross a mount point,
318 	 * and it did, get ready to return.
319 	 */
320 	if ((vp->curflags & FTW_MOUNT) && type != FTW_NS &&
321 	    statb.st_dev != vp->cur_mount)
322 		goto quit;
323 	vp->state.quit = 0;
324 
325 	/*
326 	 * If current component is not a directory, call user
327 	 * specified function and get ready to return.
328 	 */
329 	if (type != FTW_D || (vp->curflags & FTW_DEPTH) == 0)
330 		rc = (*fn)(vp->tmppath, &statb, type, &vp->state);
331 	if (rc > 0)
332 		val = rc;
333 	skip = (vp->state.quit & FTW_SKD);
334 	if (rc != 0 || type != FTW_D || (vp->state.quit & FTW_PRUNE))
335 		goto quit;
336 
337 	if (vp->tmppath[0] != '\0' && component[-1] != '/')
338 		*component++ = '/';
339 	*component = 0;
340 	if (vp->curflags & FTW_CHDIR) {
341 		struct stat statb2;
342 
343 		/*
344 		 * Security check (there is a window between
345 		 * (*vp->statf)() and opendir() above).
346 		 */
347 		if ((vp->curflags & FTW_PHYS) &&
348 		    (fstat(this.fd->dd_fd, &statb2) < 0 ||
349 		    statb2.st_ino != statb.st_ino ||
350 		    statb2.st_dev != statb.st_dev)) {
351 			errno = EAGAIN;
352 			rc = -1;
353 			goto quit;
354 		}
355 
356 		if ((cdval = fchdir(this.fd->dd_fd)) >= 0) {
357 			this.comp = component;
358 		} else {
359 			type = FTW_DNR;
360 			rc = (*fn)(vp->tmppath, &statb, type, &vp->state);
361 			goto quit;
362 		}
363 	}
364 
365 	/*
366 	 * If the walk has followed a symbolic link (FTW_PHYS is not set),
367 	 * traverse the walk back to make sure there is not a loop.
368 	 * The find utility (FTW_NOLOOP is set) detects infinite loops
369 	 * in both symbolic and hard linked directories.
370 	 */
371 	if ((vp->curflags & FTW_NOLOOP) ||
372 	    ((vp->curflags & FTW_PHYS) == 0)) {
373 		struct Save *sp = last;
374 		while (sp) {
375 			/*
376 			 * If the same node has already been visited, there
377 			 * is a loop. Get ready to return.
378 			 */
379 			if (sp->dev == statb.st_dev &&
380 			    sp->inode == statb.st_ino) {
381 				if (vp->curflags & FTW_NOLOOP) {
382 					/* private interface for find util */
383 					type = FTW_DL;
384 					goto fail;
385 				}
386 				goto quit;
387 			}
388 			sp = sp->last;
389 		}
390 	}
391 	this.dev = statb.st_dev;
392 	this.inode = statb.st_ino;
393 	oldbase = vp->state.base;
394 	vp->state.base = (int)(component - vp->tmppath);
395 	while (dir = readdir(this.fd)) {
396 		if (dir->d_ino == 0)
397 			continue;
398 		q = dir->d_name;
399 		if (*q == '.') {
400 			if (q[1] == 0)
401 				continue;
402 			else if (q[1] == '.' && q[2] == 0)
403 				continue;
404 		}
405 		if (last != NULL && last->comp != NULL) {
406 			base_last_comp = last->comp - vp->home;
407 		}
408 		base_comp = comp - vp->home;
409 		base_component = component - vp->home;
410 		if ((strlen(q) + strlen(vp->home) + 1) > vp->len) {
411 			/*
412 			 * When the space needed for vp->home has
413 			 * exceeded the amount of space that has
414 			 * been allocated, realloc() more space
415 			 * and adjust pointers to point to the
416 			 * (possibly moved) new block for vp->home
417 			 */
418 			base_this_comp = this.comp - vp->home;
419 			base_fullpath = vp->fullpath - vp->home;
420 			base_tmppath = vp->tmppath - vp->home;
421 			vp->len *= 2;
422 			tmp = (char *)realloc(vp->home, vp->len);
423 			if (tmp == NULL) {
424 				rc = -1;
425 				goto quit;
426 			}
427 			vp->home = tmp;
428 			comp = vp->home + base_comp;
429 			component = vp->home + base_component;
430 			this.comp = vp->home + base_this_comp;
431 			vp->fullpath = vp->home + base_fullpath;
432 			vp->tmppath = vp->home + base_tmppath;
433 			if (last != NULL && last->comp != NULL) {
434 				last->comp = vp->home + base_last_comp;
435 			}
436 		}
437 		p = component;
438 		while (*q != '\0')
439 			*p++ = *q++;
440 		*p = '\0';
441 		vp->state.level++;
442 
443 		/* Call walk() recursively.  */
444 		rc = walk(p, fn, depth-1, &this, vp);
445 		if (last != NULL && last->comp != NULL) {
446 			last->comp = vp->home + base_last_comp;
447 		}
448 		comp = vp->home + base_comp;
449 		component = vp->home + base_component;
450 		vp->state.level--;
451 		if (this.fd == 0) {
452 			*component = 0;
453 			if (vp->curflags & FTW_CHDIR) {
454 				this.fd = opendir(".");
455 			} else {
456 				this.fd = (*vp->opendirf)(comp);
457 			}
458 			if (this.fd == 0) {
459 				rc = -1;
460 				goto quit;
461 			}
462 			seekdir(this.fd, this.here);
463 		}
464 		if (rc != 0) {
465 			if (errno == ENOENT) {
466 				(void) fprintf(stderr, "cannot open %s: %s\n",
467 				    vp->tmppath, strerror(errno));
468 				val = rc;
469 				continue;
470 			}
471 			goto quit;	/* this seems extreme */
472 		}
473 	}
474 	vp->state.base = oldbase;
475 	*--component = 0;
476 	type = FTW_DP;
477 	if ((vp->tmppath[0] != '\0') && (vp->curflags & FTW_DEPTH) && !skip)
478 		rc = (*fn)(vp->tmppath, &statb, type, &vp->state);
479 quit:
480 	if (cdval >= 0 && last) {
481 		/* try to change back to previous directory */
482 		if (last->fd != NULL) {
483 			if (fchdir(last->fd->dd_fd) < 0) {
484 				rc = -1;
485 			}
486 		} else {
487 			if ((cdval = chdir("..")) >= 0) {
488 				if ((*vp->statf)(".", &statb, last, 0) < 0 ||
489 				    statb.st_ino != last->inode ||
490 				    statb.st_dev != last->dev)
491 					cdval = -1;
492 			}
493 			*comp = 0;
494 			if (cdval < 0) {
495 				if (chdir(vp->fullpath) < 0) {
496 					rc = -1;
497 				} else {
498 					/* Security check */
499 					if ((vp->curflags & FTW_PHYS) &&
500 					    ((*vp->statf)(".", &statb,
501 					    last, 0) < 0 ||
502 					    statb.st_ino != last->inode ||
503 					    statb.st_dev != last->dev)) {
504 						errno = EAGAIN;
505 						rc = -1;
506 					}
507 				}
508 			}
509 		}
510 	}
511 
512 	if (this.fd)
513 		(void) closedir(this.fd);
514 	if (val > rc)
515 		return (val);
516 	else
517 		return (rc);
518 }
519 
520 int
521 _nftw(const char *path,
522     int (*fn)(const char *, const struct stat *, int, struct FTW *),
523     int depth, int flags)
524 {
525 	struct Var var;
526 	struct stat statb;
527 	int rc = -1;
528 	char *dp;
529 	char *base;
530 	char *endhome;
531 	const char *savepath = path;
532 	int save_errno;
533 
534 	var.walklevel = 0;
535 	var.len = 2*(PATH_MAX+1);
536 	var.home = (char *)malloc(var.len);
537 	if (var.home == NULL)
538 		return (-1);
539 
540 	var.home[0] = 0;
541 
542 	/*
543 	 * If the walk is going to change directory before
544 	 * reading it, save current working directory.
545 	 */
546 	if (flags & FTW_CHDIR) {
547 		if (getcwd(var.home, PATH_MAX+1) == 0) {
548 			free(var.home);
549 			return (-1);
550 		}
551 	}
552 	endhome = dp = var.home + strlen(var.home);
553 	if (*path == '/')
554 		var.fullpath = dp;
555 	else {
556 		*dp++ = '/';
557 		var.fullpath = var.home;
558 	}
559 	var.tmppath =  dp;
560 	base = dp-1;
561 	while (*path) {
562 		*dp = *path;
563 		if (*dp == '/')
564 			base = dp;
565 		dp++, path++;
566 	}
567 	*dp = 0;
568 	var.state.base = (int)(base + 1 - var.tmppath);
569 	if (*path) {
570 		free(var.home);
571 		errno = ENAMETOOLONG;
572 		return (-1);
573 	}
574 	var.curflags = flags;
575 
576 	/*
577 	 * If doing chdir()'s, set var.opendirf to cdopendir.
578 	 * If not doing chdir()'s and if nftw()'s depth arg >= 2,
579 	 * set var.opendirf to nocdopendir.  In order to
580 	 * descend to arbitrary depths without doing chdir()'s, nftw()
581 	 * requires a depth arg >= 2 so that nocdopendir() can use openat()
582 	 * to traverse the directories.  So when not doing
583 	 * chdir()'s if nftw()'s depth arg <= 1, set var.opendirf to
584 	 * cdopendir.
585 	 * If doing a physical walk (not following symbolic link), set
586 	 * var.statf to cdlstat() or nocdlstat(). Otherwise, set var.statf
587 	 * to cdstat() or nocdstat().
588 	 */
589 	if (((flags & FTW_CHDIR) == 0) && (depth >= 2)) {
590 		var.opendirf = nocdopendir;
591 		if (flags & FTW_PHYS)
592 			var.statf = nocdlstat;
593 		else
594 			var.statf = nocdstat;
595 	} else {
596 		var.opendirf = cdopendir;
597 		if (flags & FTW_PHYS)
598 			var.statf = cdlstat;
599 		else
600 			var.statf = cdstat;
601 	}
602 
603 	/*
604 	 * If walk is not going to cross a mount point,
605 	 * save the current mount point.
606 	 */
607 	if (flags & FTW_MOUNT) {
608 		if ((*var.statf)(savepath, &statb, NULL, 0) >= 0)
609 			var.cur_mount = statb.st_dev;
610 		else
611 			goto done;
612 	}
613 	var.state.level = 0;
614 
615 	/*
616 	 * Call walk() which does most of the work.
617 	 * walk() uses errno in a rather obtuse way
618 	 * so we shield any incoming errno.
619 	 */
620 	save_errno = errno;
621 	errno = 0;
622 	var.savedstatf = NULL;
623 	rc = walk(dp, fn, depth, (struct Save *)0, &var);
624 	if (errno == 0)
625 		errno = save_errno;
626 done:
627 	*endhome = 0;
628 	if (flags & FTW_CHDIR)
629 		(void) chdir(var.home);
630 	free(var.home);
631 	return (rc);
632 }
633 
634 /*
635  * Get stat info on path when FTW_CHDIR is set.
636  */
637 /*ARGSUSED1*/
638 static int
639 cdstat(const char *path, struct stat *statp, struct Save *lp, int flags)
640 {
641 	return (fstatat(AT_FDCWD, path, statp, flags));
642 }
643 
644 /*
645  * Get lstat info on path when FTW_CHDIR is set.
646  */
647 /*ARGSUSED1*/
648 static int
649 cdlstat(const char *path, struct stat *statp, struct Save *lp, int flags)
650 {
651 	return (fstatat(AT_FDCWD, path, statp,
652 	    flags | AT_SYMLINK_NOFOLLOW));
653 }
654 
655 /*
656  * Get stat info on path when FTW_CHDIR is not set.
657  */
658 static int
659 nocdstat(const char *path, struct stat *statp, struct Save *lp, int flags)
660 {
661 	int		fd;
662 	const char	*basepath;
663 
664 	if (lp && lp->fd) {
665 		/* get basename of path */
666 		basepath = get_unrooted(path);
667 
668 		fd = lp->fd->dd_fd;
669 	} else {
670 		basepath = path;
671 
672 		fd = AT_FDCWD;
673 	}
674 
675 	return (fstatat(fd, basepath, statp, flags));
676 }
677 
678 /*
679  * Get lstat info on path when FTW_CHDIR is not set.
680  */
681 static int
682 nocdlstat(const char *path, struct stat *statp, struct Save *lp, int flags)
683 {
684 	int		fd;
685 	const char	*basepath;
686 
687 	if (lp && lp->fd) {
688 		/* get basename of path */
689 		basepath = get_unrooted(path);
690 
691 		fd = lp->fd->dd_fd;
692 	} else {
693 		basepath = path;
694 
695 		fd = AT_FDCWD;
696 	}
697 
698 	return (fstatat(fd, basepath, statp, flags | AT_SYMLINK_NOFOLLOW));
699 }
700 
701 /*
702  * Open path directory when FTW_CHDIR is set.
703  *
704  */
705 static DIR *
706 cdopendir(const char *path)
707 {
708 	return (opendir(path));
709 }
710 
711 /*
712  * Open path directory when FTW_CHDIR is not set.
713  */
714 static DIR *
715 nocdopendir(const char *path)
716 {
717 	int fd, cfd;
718 	DIR *fdd;
719 	char *dirp, *token, *ptr;
720 
721 	if (((fdd = opendir(path)) == NULL) && (errno == ENAMETOOLONG)) {
722 		if ((dirp = strdup(path)) == NULL) {
723 			errno = ENAMETOOLONG;
724 			return (NULL);
725 		}
726 		if ((token = strtok_r(dirp, "/", &ptr)) != NULL) {
727 			if ((fd = openat(AT_FDCWD, dirp, O_RDONLY)) < 0) {
728 				(void) free(dirp);
729 				errno = ENAMETOOLONG;
730 				return (NULL);
731 			}
732 			while ((token = strtok_r(NULL, "/", &ptr)) != NULL) {
733 				if ((cfd = openat(fd, token, O_RDONLY)) < 0) {
734 					(void) close(fd);
735 					(void) free(dirp);
736 					errno = ENAMETOOLONG;
737 					return (NULL);
738 				}
739 				(void) close(fd);
740 				fd = cfd;
741 			}
742 			(void) free(dirp);
743 			return (fdopendir(fd));
744 		}
745 		(void) free(dirp);
746 		errno = ENAMETOOLONG;
747 	}
748 	return (fdd);
749 }
750 
751 /*
752  * return pointer basename of path, which may contain trailing slashes
753  *
754  * We do this when we do not chdir() on the input.
755  */
756 static const char *
757 get_unrooted(const char *path)
758 {
759 	const char *ptr;
760 
761 	if (!path || !*path)
762 		return (NULL);
763 
764 	ptr = path + strlen(path);
765 	/* find last char in path before any trailing slashes */
766 	while (ptr != path && *--ptr == '/')
767 		;
768 
769 	if (ptr == path)	/* all slashes */
770 		return (ptr);
771 
772 	while (ptr != path)
773 		if (*--ptr == '/')
774 			return (++ptr);
775 
776 	return (ptr);
777 }
778 
779 /*
780  * close the oldest directory.  It saves the seek offset.
781  * return value is 0 unless it was unable to close any descriptor
782  */
783 
784 static int
785 oldclose(struct Save *sp)
786 {
787 	struct Save *spnext;
788 	while (sp) {
789 		spnext = sp->last;
790 		if (spnext == 0 || spnext->fd == 0)
791 			break;
792 		sp = spnext;
793 	}
794 	if (sp == 0 || sp->fd == 0)
795 		return (0);
796 	sp->here = telldir(sp->fd);
797 	(void) closedir(sp->fd);
798 	sp->fd = 0;
799 	return (1);
800 }
801