xref: /freebsd-src/lib/libc/gen/fts-compat11.c (revision dc36d6f9bb1753f3808552f3afd30eda9a7b206a)
169921123SKonstantin Belousov /*-
269921123SKonstantin Belousov  * Copyright (c) 1990, 1993, 1994
369921123SKonstantin Belousov  *	The Regents of the University of California.  All rights reserved.
469921123SKonstantin Belousov  *
569921123SKonstantin Belousov  * Redistribution and use in source and binary forms, with or without
669921123SKonstantin Belousov  * modification, are permitted provided that the following conditions
769921123SKonstantin Belousov  * are met:
869921123SKonstantin Belousov  * 1. Redistributions of source code must retain the above copyright
969921123SKonstantin Belousov  *    notice, this list of conditions and the following disclaimer.
1069921123SKonstantin Belousov  * 2. Redistributions in binary form must reproduce the above copyright
1169921123SKonstantin Belousov  *    notice, this list of conditions and the following disclaimer in the
1269921123SKonstantin Belousov  *    documentation and/or other materials provided with the distribution.
1369921123SKonstantin Belousov  * 3. Neither the name of the University nor the names of its contributors
1469921123SKonstantin Belousov  *    may be used to endorse or promote products derived from this software
1569921123SKonstantin Belousov  *    without specific prior written permission.
1669921123SKonstantin Belousov  *
1769921123SKonstantin Belousov  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
1869921123SKonstantin Belousov  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1969921123SKonstantin Belousov  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2069921123SKonstantin Belousov  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2169921123SKonstantin Belousov  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2269921123SKonstantin Belousov  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2369921123SKonstantin Belousov  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2469921123SKonstantin Belousov  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2569921123SKonstantin Belousov  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2669921123SKonstantin Belousov  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2769921123SKonstantin Belousov  * SUCH DAMAGE.
28c1920558SJohn Baldwin  * From: $OpenBSD: fts.c,v 1.22 1999/10/03 19:22:22 millert Exp $
2969921123SKonstantin Belousov  */
3069921123SKonstantin Belousov 
3169921123SKonstantin Belousov #include "namespace.h"
3269921123SKonstantin Belousov #include <sys/param.h>
3369921123SKonstantin Belousov #define	_WANT_FREEBSD11_STATFS
3469921123SKonstantin Belousov #include <sys/mount.h>
3569921123SKonstantin Belousov #define	_WANT_FREEBSD11_STAT
3669921123SKonstantin Belousov #include <sys/stat.h>
3769921123SKonstantin Belousov 
3869921123SKonstantin Belousov #define	_WANT_FREEBSD11_DIRENT
3969921123SKonstantin Belousov #include <dirent.h>
4069921123SKonstantin Belousov #include <errno.h>
4169921123SKonstantin Belousov #include <fcntl.h>
4269921123SKonstantin Belousov #include <fts.h>
4369921123SKonstantin Belousov #include <stdlib.h>
4469921123SKonstantin Belousov #include <string.h>
4569921123SKonstantin Belousov #include <unistd.h>
4669921123SKonstantin Belousov #include "gen-compat.h"
4769921123SKonstantin Belousov #include "fts-compat11.h"
4869921123SKonstantin Belousov #include "un-namespace.h"
4969921123SKonstantin Belousov 
5069921123SKonstantin Belousov #include "gen-private.h"
5169921123SKonstantin Belousov 
5269921123SKonstantin Belousov static FTSENT11	*fts_alloc(FTS11 *, char *, size_t);
5369921123SKonstantin Belousov static FTSENT11	*fts_build(FTS11 *, int);
5469921123SKonstantin Belousov static void	 fts_lfree(FTSENT11 *);
5569921123SKonstantin Belousov static void	 fts_load(FTS11 *, FTSENT11 *);
5669921123SKonstantin Belousov static size_t	 fts_maxarglen(char * const *);
5769921123SKonstantin Belousov static void	 fts_padjust(FTS11 *, FTSENT11 *);
5869921123SKonstantin Belousov static int	 fts_palloc(FTS11 *, size_t);
5969921123SKonstantin Belousov static FTSENT11	*fts_sort(FTS11 *, FTSENT11 *, size_t);
6069921123SKonstantin Belousov static int	 fts_stat(FTS11 *, FTSENT11 *, int, int);
6169921123SKonstantin Belousov static int	 fts_safe_changedir(FTS11 *, FTSENT11 *, int, char *);
6269921123SKonstantin Belousov static int	 fts_ufslinks(FTS11 *, const FTSENT11 *);
6369921123SKonstantin Belousov 
6469921123SKonstantin Belousov #define	ISDOT(a)	(a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))
6569921123SKonstantin Belousov 
6669921123SKonstantin Belousov #define	CLR(opt)	(sp->fts_options &= ~(opt))
6769921123SKonstantin Belousov #define	ISSET(opt)	(sp->fts_options & (opt))
6869921123SKonstantin Belousov #define	SET(opt)	(sp->fts_options |= (opt))
6969921123SKonstantin Belousov 
7069921123SKonstantin Belousov #define	FCHDIR(sp, fd)	(!ISSET(FTS_NOCHDIR) && fchdir(fd))
7169921123SKonstantin Belousov 
7269921123SKonstantin Belousov /* fts_build flags */
7369921123SKonstantin Belousov #define	BCHILD		1		/* fts_children */
7469921123SKonstantin Belousov #define	BNAMES		2		/* fts_children, names only */
7569921123SKonstantin Belousov #define	BREAD		3		/* fts_read */
7669921123SKonstantin Belousov 
7769921123SKonstantin Belousov /*
7869921123SKonstantin Belousov  * Internal representation of an FTS, including extra implementation
7969921123SKonstantin Belousov  * details.  The FTS returned from fts_open points to this structure's
8069921123SKonstantin Belousov  * ftsp_fts member (and can be cast to an _fts_private as required)
8169921123SKonstantin Belousov  */
8269921123SKonstantin Belousov struct _fts_private11 {
8369921123SKonstantin Belousov 	FTS11		ftsp_fts;
8469921123SKonstantin Belousov 	struct freebsd11_statfs	ftsp_statfs;
8569921123SKonstantin Belousov 	uint32_t	ftsp_dev;
8669921123SKonstantin Belousov 	int		ftsp_linksreliable;
8769921123SKonstantin Belousov };
8869921123SKonstantin Belousov 
8969921123SKonstantin Belousov /*
9069921123SKonstantin Belousov  * The "FTS_NOSTAT" option can avoid a lot of calls to stat(2) if it
9169921123SKonstantin Belousov  * knows that a directory could not possibly have subdirectories.  This
9269921123SKonstantin Belousov  * is decided by looking at the link count: a subdirectory would
9369921123SKonstantin Belousov  * increment its parent's link count by virtue of its own ".." entry.
9469921123SKonstantin Belousov  * This assumption only holds for UFS-like filesystems that implement
9569921123SKonstantin Belousov  * links and directories this way, so we must punt for others.
9669921123SKonstantin Belousov  */
9769921123SKonstantin Belousov 
9869921123SKonstantin Belousov static const char *ufslike_filesystems[] = {
9969921123SKonstantin Belousov 	"ufs",
10069921123SKonstantin Belousov 	"zfs",
10169921123SKonstantin Belousov 	"nfs",
10269921123SKonstantin Belousov 	"ext2fs",
10369921123SKonstantin Belousov 	0
10469921123SKonstantin Belousov };
10569921123SKonstantin Belousov 
10669921123SKonstantin Belousov FTS11 *
freebsd11_fts_open(char * const * argv,int options,int (* compar)(const FTSENT11 * const *,const FTSENT11 * const *))10769921123SKonstantin Belousov freebsd11_fts_open(char * const *argv, int options,
10869921123SKonstantin Belousov     int (*compar)(const FTSENT11 * const *, const FTSENT11 * const *))
10969921123SKonstantin Belousov {
11069921123SKonstantin Belousov 	struct _fts_private11 *priv;
11169921123SKonstantin Belousov 	FTS11 *sp;
11269921123SKonstantin Belousov 	FTSENT11 *p, *root;
11369921123SKonstantin Belousov 	FTSENT11 *parent, *tmp;
11469921123SKonstantin Belousov 	size_t len, nitems;
11569921123SKonstantin Belousov 
11669921123SKonstantin Belousov 	/* Options check. */
11769921123SKonstantin Belousov 	if (options & ~FTS_OPTIONMASK) {
11869921123SKonstantin Belousov 		errno = EINVAL;
11969921123SKonstantin Belousov 		return (NULL);
12069921123SKonstantin Belousov 	}
12169921123SKonstantin Belousov 
12269921123SKonstantin Belousov 	/* fts_open() requires at least one path */
12369921123SKonstantin Belousov 	if (*argv == NULL) {
12469921123SKonstantin Belousov 		errno = EINVAL;
12569921123SKonstantin Belousov 		return (NULL);
12669921123SKonstantin Belousov 	}
12769921123SKonstantin Belousov 
12869921123SKonstantin Belousov 	/* Allocate/initialize the stream. */
12969921123SKonstantin Belousov 	if ((priv = calloc(1, sizeof(*priv))) == NULL)
13069921123SKonstantin Belousov 		return (NULL);
13169921123SKonstantin Belousov 	sp = &priv->ftsp_fts;
13269921123SKonstantin Belousov 	sp->fts_compar = compar;
13369921123SKonstantin Belousov 	sp->fts_options = options;
13469921123SKonstantin Belousov 
13569921123SKonstantin Belousov 	/* Logical walks turn on NOCHDIR; symbolic links are too hard. */
13669921123SKonstantin Belousov 	if (ISSET(FTS_LOGICAL))
13769921123SKonstantin Belousov 		SET(FTS_NOCHDIR);
13869921123SKonstantin Belousov 
13969921123SKonstantin Belousov 	/*
14069921123SKonstantin Belousov 	 * Start out with 1K of path space, and enough, in any case,
14169921123SKonstantin Belousov 	 * to hold the user's paths.
14269921123SKonstantin Belousov 	 */
14369921123SKonstantin Belousov 	if (fts_palloc(sp, MAX(fts_maxarglen(argv), MAXPATHLEN)))
14469921123SKonstantin Belousov 		goto mem1;
14569921123SKonstantin Belousov 
14669921123SKonstantin Belousov 	/* Allocate/initialize root's parent. */
14769921123SKonstantin Belousov 	if ((parent = fts_alloc(sp, "", 0)) == NULL)
14869921123SKonstantin Belousov 		goto mem2;
14969921123SKonstantin Belousov 	parent->fts_level = FTS_ROOTPARENTLEVEL;
15069921123SKonstantin Belousov 
15105231117SPedro F. Giffuni 	/* Shush, GCC. */
15205231117SPedro F. Giffuni 	tmp = NULL;
15305231117SPedro F. Giffuni 
15469921123SKonstantin Belousov 	/* Allocate/initialize root(s). */
15569921123SKonstantin Belousov 	for (root = NULL, nitems = 0; *argv != NULL; ++argv, ++nitems) {
15669921123SKonstantin Belousov 		len = strlen(*argv);
15769921123SKonstantin Belousov 
15869921123SKonstantin Belousov 		p = fts_alloc(sp, *argv, len);
15969921123SKonstantin Belousov 		p->fts_level = FTS_ROOTLEVEL;
16069921123SKonstantin Belousov 		p->fts_parent = parent;
16169921123SKonstantin Belousov 		p->fts_accpath = p->fts_name;
16269921123SKonstantin Belousov 		p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW), -1);
16369921123SKonstantin Belousov 
16469921123SKonstantin Belousov 		/* Command-line "." and ".." are real directories. */
16569921123SKonstantin Belousov 		if (p->fts_info == FTS_DOT)
16669921123SKonstantin Belousov 			p->fts_info = FTS_D;
16769921123SKonstantin Belousov 
16869921123SKonstantin Belousov 		/*
16969921123SKonstantin Belousov 		 * If comparison routine supplied, traverse in sorted
17069921123SKonstantin Belousov 		 * order; otherwise traverse in the order specified.
17169921123SKonstantin Belousov 		 */
17269921123SKonstantin Belousov 		if (compar) {
17369921123SKonstantin Belousov 			p->fts_link = root;
17469921123SKonstantin Belousov 			root = p;
17569921123SKonstantin Belousov 		} else {
17669921123SKonstantin Belousov 			p->fts_link = NULL;
17769921123SKonstantin Belousov 			if (root == NULL)
17869921123SKonstantin Belousov 				tmp = root = p;
17969921123SKonstantin Belousov 			else {
18069921123SKonstantin Belousov 				tmp->fts_link = p;
18169921123SKonstantin Belousov 				tmp = p;
18269921123SKonstantin Belousov 			}
18369921123SKonstantin Belousov 		}
18469921123SKonstantin Belousov 	}
18569921123SKonstantin Belousov 	if (compar && nitems > 1)
18669921123SKonstantin Belousov 		root = fts_sort(sp, root, nitems);
18769921123SKonstantin Belousov 
18869921123SKonstantin Belousov 	/*
18969921123SKonstantin Belousov 	 * Allocate a dummy pointer and make fts_read think that we've just
19069921123SKonstantin Belousov 	 * finished the node before the root(s); set p->fts_info to FTS_INIT
19169921123SKonstantin Belousov 	 * so that everything about the "current" node is ignored.
19269921123SKonstantin Belousov 	 */
19369921123SKonstantin Belousov 	if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL)
19469921123SKonstantin Belousov 		goto mem3;
19569921123SKonstantin Belousov 	sp->fts_cur->fts_link = root;
19669921123SKonstantin Belousov 	sp->fts_cur->fts_info = FTS_INIT;
19769921123SKonstantin Belousov 
19869921123SKonstantin Belousov 	/*
19969921123SKonstantin Belousov 	 * If using chdir(2), grab a file descriptor pointing to dot to ensure
20069921123SKonstantin Belousov 	 * that we can get back here; this could be avoided for some paths,
20169921123SKonstantin Belousov 	 * but almost certainly not worth the effort.  Slashes, symbolic links,
20269921123SKonstantin Belousov 	 * and ".." are all fairly nasty problems.  Note, if we can't get the
20369921123SKonstantin Belousov 	 * descriptor we run anyway, just more slowly.
20469921123SKonstantin Belousov 	 */
20569921123SKonstantin Belousov 	if (!ISSET(FTS_NOCHDIR) &&
20669921123SKonstantin Belousov 	    (sp->fts_rfd = _open(".", O_RDONLY | O_CLOEXEC, 0)) < 0)
20769921123SKonstantin Belousov 		SET(FTS_NOCHDIR);
20869921123SKonstantin Belousov 
20969921123SKonstantin Belousov 	return (sp);
21069921123SKonstantin Belousov 
21169921123SKonstantin Belousov mem3:	fts_lfree(root);
21269921123SKonstantin Belousov 	free(parent);
21369921123SKonstantin Belousov mem2:	free(sp->fts_path);
21469921123SKonstantin Belousov mem1:	free(sp);
21569921123SKonstantin Belousov 	return (NULL);
21669921123SKonstantin Belousov }
21769921123SKonstantin Belousov 
21869921123SKonstantin Belousov static void
fts_load(FTS11 * sp,FTSENT11 * p)21969921123SKonstantin Belousov fts_load(FTS11 *sp, FTSENT11 *p)
22069921123SKonstantin Belousov {
22169921123SKonstantin Belousov 	size_t len;
22269921123SKonstantin Belousov 	char *cp;
22369921123SKonstantin Belousov 
22469921123SKonstantin Belousov 	/*
22569921123SKonstantin Belousov 	 * Load the stream structure for the next traversal.  Since we don't
22669921123SKonstantin Belousov 	 * actually enter the directory until after the preorder visit, set
22769921123SKonstantin Belousov 	 * the fts_accpath field specially so the chdir gets done to the right
22869921123SKonstantin Belousov 	 * place and the user can access the first node.  From fts_open it's
22969921123SKonstantin Belousov 	 * known that the path will fit.
23069921123SKonstantin Belousov 	 */
23169921123SKonstantin Belousov 	len = p->fts_pathlen = p->fts_namelen;
23269921123SKonstantin Belousov 	memmove(sp->fts_path, p->fts_name, len + 1);
23369921123SKonstantin Belousov 	if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) {
23469921123SKonstantin Belousov 		len = strlen(++cp);
23569921123SKonstantin Belousov 		memmove(p->fts_name, cp, len + 1);
23669921123SKonstantin Belousov 		p->fts_namelen = len;
23769921123SKonstantin Belousov 	}
23869921123SKonstantin Belousov 	p->fts_accpath = p->fts_path = sp->fts_path;
23969921123SKonstantin Belousov 	sp->fts_dev = p->fts_dev;
24069921123SKonstantin Belousov }
24169921123SKonstantin Belousov 
24269921123SKonstantin Belousov int
freebsd11_fts_close(FTS11 * sp)24369921123SKonstantin Belousov freebsd11_fts_close(FTS11 *sp)
24469921123SKonstantin Belousov {
24569921123SKonstantin Belousov 	FTSENT11 *freep, *p;
24669921123SKonstantin Belousov 	int saved_errno;
24769921123SKonstantin Belousov 
24869921123SKonstantin Belousov 	/*
24969921123SKonstantin Belousov 	 * This still works if we haven't read anything -- the dummy structure
25069921123SKonstantin Belousov 	 * points to the root list, so we step through to the end of the root
25169921123SKonstantin Belousov 	 * list which has a valid parent pointer.
25269921123SKonstantin Belousov 	 */
25369921123SKonstantin Belousov 	if (sp->fts_cur) {
25469921123SKonstantin Belousov 		for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {
25569921123SKonstantin Belousov 			freep = p;
25669921123SKonstantin Belousov 			p = p->fts_link != NULL ? p->fts_link : p->fts_parent;
25769921123SKonstantin Belousov 			free(freep);
25869921123SKonstantin Belousov 		}
25969921123SKonstantin Belousov 		free(p);
26069921123SKonstantin Belousov 	}
26169921123SKonstantin Belousov 
26269921123SKonstantin Belousov 	/* Free up child linked list, sort array, path buffer. */
26369921123SKonstantin Belousov 	if (sp->fts_child)
26469921123SKonstantin Belousov 		fts_lfree(sp->fts_child);
26569921123SKonstantin Belousov 	if (sp->fts_array)
26669921123SKonstantin Belousov 		free(sp->fts_array);
26769921123SKonstantin Belousov 	free(sp->fts_path);
26869921123SKonstantin Belousov 
26969921123SKonstantin Belousov 	/* Return to original directory, save errno if necessary. */
27069921123SKonstantin Belousov 	if (!ISSET(FTS_NOCHDIR)) {
27169921123SKonstantin Belousov 		saved_errno = fchdir(sp->fts_rfd) ? errno : 0;
27269921123SKonstantin Belousov 		(void)_close(sp->fts_rfd);
27369921123SKonstantin Belousov 
27469921123SKonstantin Belousov 		/* Set errno and return. */
27569921123SKonstantin Belousov 		if (saved_errno != 0) {
27669921123SKonstantin Belousov 			/* Free up the stream pointer. */
27769921123SKonstantin Belousov 			free(sp);
27869921123SKonstantin Belousov 			errno = saved_errno;
27969921123SKonstantin Belousov 			return (-1);
28069921123SKonstantin Belousov 		}
28169921123SKonstantin Belousov 	}
28269921123SKonstantin Belousov 
28369921123SKonstantin Belousov 	/* Free up the stream pointer. */
28469921123SKonstantin Belousov 	free(sp);
28569921123SKonstantin Belousov 	return (0);
28669921123SKonstantin Belousov }
28769921123SKonstantin Belousov 
28869921123SKonstantin Belousov /*
28969921123SKonstantin Belousov  * Special case of "/" at the end of the path so that slashes aren't
29069921123SKonstantin Belousov  * appended which would cause paths to be written as "....//foo".
29169921123SKonstantin Belousov  */
29269921123SKonstantin Belousov #define	NAPPEND(p)							\
29369921123SKonstantin Belousov 	(p->fts_path[p->fts_pathlen - 1] == '/'				\
29469921123SKonstantin Belousov 	    ? p->fts_pathlen - 1 : p->fts_pathlen)
29569921123SKonstantin Belousov 
29669921123SKonstantin Belousov FTSENT11 *
freebsd11_fts_read(FTS11 * sp)29769921123SKonstantin Belousov freebsd11_fts_read(FTS11 *sp)
29869921123SKonstantin Belousov {
29969921123SKonstantin Belousov 	FTSENT11 *p, *tmp;
30069921123SKonstantin Belousov 	int instr;
30169921123SKonstantin Belousov 	char *t;
30269921123SKonstantin Belousov 	int saved_errno;
30369921123SKonstantin Belousov 
30469921123SKonstantin Belousov 	/* If finished or unrecoverable error, return NULL. */
30569921123SKonstantin Belousov 	if (sp->fts_cur == NULL || ISSET(FTS_STOP))
30669921123SKonstantin Belousov 		return (NULL);
30769921123SKonstantin Belousov 
30869921123SKonstantin Belousov 	/* Set current node pointer. */
30969921123SKonstantin Belousov 	p = sp->fts_cur;
31069921123SKonstantin Belousov 
31169921123SKonstantin Belousov 	/* Save and zero out user instructions. */
31269921123SKonstantin Belousov 	instr = p->fts_instr;
31369921123SKonstantin Belousov 	p->fts_instr = FTS_NOINSTR;
31469921123SKonstantin Belousov 
31569921123SKonstantin Belousov 	/* Any type of file may be re-visited; re-stat and re-turn. */
31669921123SKonstantin Belousov 	if (instr == FTS_AGAIN) {
31769921123SKonstantin Belousov 		p->fts_info = fts_stat(sp, p, 0, -1);
31869921123SKonstantin Belousov 		return (p);
31969921123SKonstantin Belousov 	}
32069921123SKonstantin Belousov 
32169921123SKonstantin Belousov 	/*
32269921123SKonstantin Belousov 	 * Following a symlink -- SLNONE test allows application to see
32369921123SKonstantin Belousov 	 * SLNONE and recover.  If indirecting through a symlink, have
32469921123SKonstantin Belousov 	 * keep a pointer to current location.  If unable to get that
32569921123SKonstantin Belousov 	 * pointer, follow fails.
32669921123SKonstantin Belousov 	 */
32769921123SKonstantin Belousov 	if (instr == FTS_FOLLOW &&
32869921123SKonstantin Belousov 	    (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {
32969921123SKonstantin Belousov 		p->fts_info = fts_stat(sp, p, 1, -1);
33069921123SKonstantin Belousov 		if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
33169921123SKonstantin Belousov 			if ((p->fts_symfd = _open(".", O_RDONLY | O_CLOEXEC,
33269921123SKonstantin Belousov 			    0)) < 0) {
33369921123SKonstantin Belousov 				p->fts_errno = errno;
33469921123SKonstantin Belousov 				p->fts_info = FTS_ERR;
33569921123SKonstantin Belousov 			} else
33669921123SKonstantin Belousov 				p->fts_flags |= FTS_SYMFOLLOW;
33769921123SKonstantin Belousov 		}
33869921123SKonstantin Belousov 		return (p);
33969921123SKonstantin Belousov 	}
34069921123SKonstantin Belousov 
34169921123SKonstantin Belousov 	/* Directory in pre-order. */
34269921123SKonstantin Belousov 	if (p->fts_info == FTS_D) {
34369921123SKonstantin Belousov 		/* If skipped or crossed mount point, do post-order visit. */
34469921123SKonstantin Belousov 		if (instr == FTS_SKIP ||
34569921123SKonstantin Belousov 		    (ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev)) {
34669921123SKonstantin Belousov 			if (p->fts_flags & FTS_SYMFOLLOW)
34769921123SKonstantin Belousov 				(void)_close(p->fts_symfd);
34869921123SKonstantin Belousov 			if (sp->fts_child) {
34969921123SKonstantin Belousov 				fts_lfree(sp->fts_child);
35069921123SKonstantin Belousov 				sp->fts_child = NULL;
35169921123SKonstantin Belousov 			}
35269921123SKonstantin Belousov 			p->fts_info = FTS_DP;
35369921123SKonstantin Belousov 			return (p);
35469921123SKonstantin Belousov 		}
35569921123SKonstantin Belousov 
35669921123SKonstantin Belousov 		/* Rebuild if only read the names and now traversing. */
35769921123SKonstantin Belousov 		if (sp->fts_child != NULL && ISSET(FTS_NAMEONLY)) {
35869921123SKonstantin Belousov 			CLR(FTS_NAMEONLY);
35969921123SKonstantin Belousov 			fts_lfree(sp->fts_child);
36069921123SKonstantin Belousov 			sp->fts_child = NULL;
36169921123SKonstantin Belousov 		}
36269921123SKonstantin Belousov 
36369921123SKonstantin Belousov 		/*
36469921123SKonstantin Belousov 		 * Cd to the subdirectory.
36569921123SKonstantin Belousov 		 *
36669921123SKonstantin Belousov 		 * If have already read and now fail to chdir, whack the list
36769921123SKonstantin Belousov 		 * to make the names come out right, and set the parent errno
36869921123SKonstantin Belousov 		 * so the application will eventually get an error condition.
36969921123SKonstantin Belousov 		 * Set the FTS_DONTCHDIR flag so that when we logically change
37069921123SKonstantin Belousov 		 * directories back to the parent we don't do a chdir.
37169921123SKonstantin Belousov 		 *
37269921123SKonstantin Belousov 		 * If haven't read do so.  If the read fails, fts_build sets
37369921123SKonstantin Belousov 		 * FTS_STOP or the fts_info field of the node.
37469921123SKonstantin Belousov 		 */
37569921123SKonstantin Belousov 		if (sp->fts_child != NULL) {
37669921123SKonstantin Belousov 			if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) {
37769921123SKonstantin Belousov 				p->fts_errno = errno;
37869921123SKonstantin Belousov 				p->fts_flags |= FTS_DONTCHDIR;
37969921123SKonstantin Belousov 				for (p = sp->fts_child; p != NULL;
38069921123SKonstantin Belousov 				    p = p->fts_link)
38169921123SKonstantin Belousov 					p->fts_accpath =
38269921123SKonstantin Belousov 					    p->fts_parent->fts_accpath;
38369921123SKonstantin Belousov 			}
38469921123SKonstantin Belousov 		} else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) {
38569921123SKonstantin Belousov 			if (ISSET(FTS_STOP))
38669921123SKonstantin Belousov 				return (NULL);
38769921123SKonstantin Belousov 			return (p);
38869921123SKonstantin Belousov 		}
38969921123SKonstantin Belousov 		p = sp->fts_child;
39069921123SKonstantin Belousov 		sp->fts_child = NULL;
39169921123SKonstantin Belousov 		goto name;
39269921123SKonstantin Belousov 	}
39369921123SKonstantin Belousov 
39469921123SKonstantin Belousov 	/* Move to the next node on this level. */
39569921123SKonstantin Belousov next:	tmp = p;
39669921123SKonstantin Belousov 	if ((p = p->fts_link) != NULL) {
39769921123SKonstantin Belousov 		/*
39869921123SKonstantin Belousov 		 * If reached the top, return to the original directory (or
39969921123SKonstantin Belousov 		 * the root of the tree), and load the paths for the next root.
40069921123SKonstantin Belousov 		 */
40169921123SKonstantin Belousov 		if (p->fts_level == FTS_ROOTLEVEL) {
40269921123SKonstantin Belousov 			if (FCHDIR(sp, sp->fts_rfd)) {
40369921123SKonstantin Belousov 				SET(FTS_STOP);
40469921123SKonstantin Belousov 				return (NULL);
40569921123SKonstantin Belousov 			}
40669921123SKonstantin Belousov 			free(tmp);
40769921123SKonstantin Belousov 			fts_load(sp, p);
40869921123SKonstantin Belousov 			return (sp->fts_cur = p);
40969921123SKonstantin Belousov 		}
41069921123SKonstantin Belousov 
41169921123SKonstantin Belousov 		/*
41269921123SKonstantin Belousov 		 * User may have called fts_set on the node.  If skipped,
41369921123SKonstantin Belousov 		 * ignore.  If followed, get a file descriptor so we can
41469921123SKonstantin Belousov 		 * get back if necessary.
41569921123SKonstantin Belousov 		 */
41669921123SKonstantin Belousov 		if (p->fts_instr == FTS_SKIP) {
41769921123SKonstantin Belousov 			free(tmp);
41869921123SKonstantin Belousov 			goto next;
41969921123SKonstantin Belousov 		}
42069921123SKonstantin Belousov 		if (p->fts_instr == FTS_FOLLOW) {
42169921123SKonstantin Belousov 			p->fts_info = fts_stat(sp, p, 1, -1);
42269921123SKonstantin Belousov 			if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
42369921123SKonstantin Belousov 				if ((p->fts_symfd =
42469921123SKonstantin Belousov 				    _open(".", O_RDONLY | O_CLOEXEC, 0)) < 0) {
42569921123SKonstantin Belousov 					p->fts_errno = errno;
42669921123SKonstantin Belousov 					p->fts_info = FTS_ERR;
42769921123SKonstantin Belousov 				} else
42869921123SKonstantin Belousov 					p->fts_flags |= FTS_SYMFOLLOW;
42969921123SKonstantin Belousov 			}
43069921123SKonstantin Belousov 			p->fts_instr = FTS_NOINSTR;
43169921123SKonstantin Belousov 		}
43269921123SKonstantin Belousov 
43369921123SKonstantin Belousov 		free(tmp);
43469921123SKonstantin Belousov 
43569921123SKonstantin Belousov name:		t = sp->fts_path + NAPPEND(p->fts_parent);
43669921123SKonstantin Belousov 		*t++ = '/';
43769921123SKonstantin Belousov 		memmove(t, p->fts_name, p->fts_namelen + 1);
43869921123SKonstantin Belousov 		return (sp->fts_cur = p);
43969921123SKonstantin Belousov 	}
44069921123SKonstantin Belousov 
44169921123SKonstantin Belousov 	/* Move up to the parent node. */
44269921123SKonstantin Belousov 	p = tmp->fts_parent;
44369921123SKonstantin Belousov 
44469921123SKonstantin Belousov 	if (p->fts_level == FTS_ROOTPARENTLEVEL) {
44569921123SKonstantin Belousov 		/*
44669921123SKonstantin Belousov 		 * Done; free everything up and set errno to 0 so the user
44769921123SKonstantin Belousov 		 * can distinguish between error and EOF.
44869921123SKonstantin Belousov 		 */
44969921123SKonstantin Belousov 		free(tmp);
45069921123SKonstantin Belousov 		free(p);
45169921123SKonstantin Belousov 		errno = 0;
45269921123SKonstantin Belousov 		return (sp->fts_cur = NULL);
45369921123SKonstantin Belousov 	}
45469921123SKonstantin Belousov 
45569921123SKonstantin Belousov 	/* NUL terminate the pathname. */
45669921123SKonstantin Belousov 	sp->fts_path[p->fts_pathlen] = '\0';
45769921123SKonstantin Belousov 
45869921123SKonstantin Belousov 	/*
45969921123SKonstantin Belousov 	 * Return to the parent directory.  If at a root node or came through
46069921123SKonstantin Belousov 	 * a symlink, go back through the file descriptor.  Otherwise, cd up
46169921123SKonstantin Belousov 	 * one directory.
46269921123SKonstantin Belousov 	 */
46369921123SKonstantin Belousov 	if (p->fts_level == FTS_ROOTLEVEL) {
46469921123SKonstantin Belousov 		if (FCHDIR(sp, sp->fts_rfd)) {
46569921123SKonstantin Belousov 			SET(FTS_STOP);
46669921123SKonstantin Belousov 			return (NULL);
46769921123SKonstantin Belousov 		}
46869921123SKonstantin Belousov 	} else if (p->fts_flags & FTS_SYMFOLLOW) {
46969921123SKonstantin Belousov 		if (FCHDIR(sp, p->fts_symfd)) {
47069921123SKonstantin Belousov 			saved_errno = errno;
47169921123SKonstantin Belousov 			(void)_close(p->fts_symfd);
47269921123SKonstantin Belousov 			errno = saved_errno;
47369921123SKonstantin Belousov 			SET(FTS_STOP);
47469921123SKonstantin Belousov 			return (NULL);
47569921123SKonstantin Belousov 		}
47669921123SKonstantin Belousov 		(void)_close(p->fts_symfd);
47769921123SKonstantin Belousov 	} else if (!(p->fts_flags & FTS_DONTCHDIR) &&
47869921123SKonstantin Belousov 	    fts_safe_changedir(sp, p->fts_parent, -1, "..")) {
47969921123SKonstantin Belousov 		SET(FTS_STOP);
48069921123SKonstantin Belousov 		return (NULL);
48169921123SKonstantin Belousov 	}
48269921123SKonstantin Belousov 	free(tmp);
48369921123SKonstantin Belousov 	p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;
48469921123SKonstantin Belousov 	return (sp->fts_cur = p);
48569921123SKonstantin Belousov }
48669921123SKonstantin Belousov 
48769921123SKonstantin Belousov /*
48869921123SKonstantin Belousov  * Fts_set takes the stream as an argument although it's not used in this
48969921123SKonstantin Belousov  * implementation; it would be necessary if anyone wanted to add global
49069921123SKonstantin Belousov  * semantics to fts using fts_set.  An error return is allowed for similar
49169921123SKonstantin Belousov  * reasons.
49269921123SKonstantin Belousov  */
49369921123SKonstantin Belousov /* ARGSUSED */
49469921123SKonstantin Belousov int
freebsd11_fts_set(FTS11 * sp,FTSENT11 * p,int instr)49569921123SKonstantin Belousov freebsd11_fts_set(FTS11 *sp, FTSENT11 *p, int instr)
49669921123SKonstantin Belousov {
49769921123SKonstantin Belousov 	if (instr != 0 && instr != FTS_AGAIN && instr != FTS_FOLLOW &&
49869921123SKonstantin Belousov 	    instr != FTS_NOINSTR && instr != FTS_SKIP) {
49969921123SKonstantin Belousov 		errno = EINVAL;
50069921123SKonstantin Belousov 		return (1);
50169921123SKonstantin Belousov 	}
50269921123SKonstantin Belousov 	p->fts_instr = instr;
50369921123SKonstantin Belousov 	return (0);
50469921123SKonstantin Belousov }
50569921123SKonstantin Belousov 
50669921123SKonstantin Belousov FTSENT11 *
freebsd11_fts_children(FTS11 * sp,int instr)50769921123SKonstantin Belousov freebsd11_fts_children(FTS11 *sp, int instr)
50869921123SKonstantin Belousov {
50969921123SKonstantin Belousov 	FTSENT11 *p;
51069921123SKonstantin Belousov 	int fd, rc, serrno;
51169921123SKonstantin Belousov 
51269921123SKonstantin Belousov 	if (instr != 0 && instr != FTS_NAMEONLY) {
51369921123SKonstantin Belousov 		errno = EINVAL;
51469921123SKonstantin Belousov 		return (NULL);
51569921123SKonstantin Belousov 	}
51669921123SKonstantin Belousov 
51769921123SKonstantin Belousov 	/* Set current node pointer. */
51869921123SKonstantin Belousov 	p = sp->fts_cur;
51969921123SKonstantin Belousov 
52069921123SKonstantin Belousov 	/*
52169921123SKonstantin Belousov 	 * Errno set to 0 so user can distinguish empty directory from
52269921123SKonstantin Belousov 	 * an error.
52369921123SKonstantin Belousov 	 */
52469921123SKonstantin Belousov 	errno = 0;
52569921123SKonstantin Belousov 
52669921123SKonstantin Belousov 	/* Fatal errors stop here. */
52769921123SKonstantin Belousov 	if (ISSET(FTS_STOP))
52869921123SKonstantin Belousov 		return (NULL);
52969921123SKonstantin Belousov 
53069921123SKonstantin Belousov 	/* Return logical hierarchy of user's arguments. */
53169921123SKonstantin Belousov 	if (p->fts_info == FTS_INIT)
53269921123SKonstantin Belousov 		return (p->fts_link);
53369921123SKonstantin Belousov 
53469921123SKonstantin Belousov 	/*
53569921123SKonstantin Belousov 	 * If not a directory being visited in pre-order, stop here.  Could
53669921123SKonstantin Belousov 	 * allow FTS_DNR, assuming the user has fixed the problem, but the
53769921123SKonstantin Belousov 	 * same effect is available with FTS_AGAIN.
53869921123SKonstantin Belousov 	 */
53969921123SKonstantin Belousov 	if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */)
54069921123SKonstantin Belousov 		return (NULL);
54169921123SKonstantin Belousov 
54269921123SKonstantin Belousov 	/* Free up any previous child list. */
54369921123SKonstantin Belousov 	if (sp->fts_child != NULL)
54469921123SKonstantin Belousov 		fts_lfree(sp->fts_child);
54569921123SKonstantin Belousov 
54669921123SKonstantin Belousov 	if (instr == FTS_NAMEONLY) {
54769921123SKonstantin Belousov 		SET(FTS_NAMEONLY);
54869921123SKonstantin Belousov 		instr = BNAMES;
54969921123SKonstantin Belousov 	} else
55069921123SKonstantin Belousov 		instr = BCHILD;
55169921123SKonstantin Belousov 
55269921123SKonstantin Belousov 	/*
55369921123SKonstantin Belousov 	 * If using chdir on a relative path and called BEFORE fts_read does
55469921123SKonstantin Belousov 	 * its chdir to the root of a traversal, we can lose -- we need to
55569921123SKonstantin Belousov 	 * chdir into the subdirectory, and we don't know where the current
55669921123SKonstantin Belousov 	 * directory is, so we can't get back so that the upcoming chdir by
55769921123SKonstantin Belousov 	 * fts_read will work.
55869921123SKonstantin Belousov 	 */
55969921123SKonstantin Belousov 	if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' ||
56069921123SKonstantin Belousov 	    ISSET(FTS_NOCHDIR))
56169921123SKonstantin Belousov 		return (sp->fts_child = fts_build(sp, instr));
56269921123SKonstantin Belousov 
56369921123SKonstantin Belousov 	if ((fd = _open(".", O_RDONLY | O_CLOEXEC, 0)) < 0)
56469921123SKonstantin Belousov 		return (NULL);
56569921123SKonstantin Belousov 	sp->fts_child = fts_build(sp, instr);
56669921123SKonstantin Belousov 	serrno = (sp->fts_child == NULL) ? errno : 0;
56769921123SKonstantin Belousov 	rc = fchdir(fd);
56869921123SKonstantin Belousov 	if (rc < 0 && serrno == 0)
56969921123SKonstantin Belousov 		serrno = errno;
57069921123SKonstantin Belousov 	(void)_close(fd);
57169921123SKonstantin Belousov 	errno = serrno;
57269921123SKonstantin Belousov 	if (rc < 0)
57369921123SKonstantin Belousov 		return (NULL);
57469921123SKonstantin Belousov 	return (sp->fts_child);
57569921123SKonstantin Belousov }
57669921123SKonstantin Belousov 
57769921123SKonstantin Belousov #ifndef freebsd11_fts_get_clientptr
57869921123SKonstantin Belousov #error "freebsd11_fts_get_clientptr not defined"
57969921123SKonstantin Belousov #endif
58069921123SKonstantin Belousov 
58169921123SKonstantin Belousov void *
58269921123SKonstantin Belousov (freebsd11_fts_get_clientptr)(FTS11 *sp)
58369921123SKonstantin Belousov {
58469921123SKonstantin Belousov 
58569921123SKonstantin Belousov 	return (freebsd11_fts_get_clientptr(sp));
58669921123SKonstantin Belousov }
58769921123SKonstantin Belousov 
58869921123SKonstantin Belousov #ifndef freebsd11_fts_get_stream
58969921123SKonstantin Belousov #error "freebsd11_fts_get_stream not defined"
59069921123SKonstantin Belousov #endif
59169921123SKonstantin Belousov 
59269921123SKonstantin Belousov FTS11 *
59369921123SKonstantin Belousov (freebsd11_fts_get_stream)(FTSENT11 *p)
59469921123SKonstantin Belousov {
59569921123SKonstantin Belousov 	return (freebsd11_fts_get_stream(p));
59669921123SKonstantin Belousov }
59769921123SKonstantin Belousov 
59869921123SKonstantin Belousov void
freebsd11_fts_set_clientptr(FTS11 * sp,void * clientptr)59969921123SKonstantin Belousov freebsd11_fts_set_clientptr(FTS11 *sp, void *clientptr)
60069921123SKonstantin Belousov {
60169921123SKonstantin Belousov 
60269921123SKonstantin Belousov 	sp->fts_clientptr = clientptr;
60369921123SKonstantin Belousov }
60469921123SKonstantin Belousov 
605*0cff70caSGanael LAPLANCHE static struct freebsd11_dirent *
fts_safe_readdir(DIR * dirp,int * readdir_errno)606*0cff70caSGanael LAPLANCHE fts_safe_readdir(DIR *dirp, int *readdir_errno)
607*0cff70caSGanael LAPLANCHE {
608*0cff70caSGanael LAPLANCHE 	struct freebsd11_dirent *ret;
609*0cff70caSGanael LAPLANCHE 
610*0cff70caSGanael LAPLANCHE 	errno = 0;
611*0cff70caSGanael LAPLANCHE 	if (!dirp)
612*0cff70caSGanael LAPLANCHE 		return (NULL);
613*0cff70caSGanael LAPLANCHE 	ret = freebsd11_readdir(dirp);
614*0cff70caSGanael LAPLANCHE 	*readdir_errno = errno;
615*0cff70caSGanael LAPLANCHE 	return (ret);
616*0cff70caSGanael LAPLANCHE }
617*0cff70caSGanael LAPLANCHE 
61869921123SKonstantin Belousov /*
61969921123SKonstantin Belousov  * This is the tricky part -- do not casually change *anything* in here.  The
62069921123SKonstantin Belousov  * idea is to build the linked list of entries that are used by fts_children
62169921123SKonstantin Belousov  * and fts_read.  There are lots of special cases.
62269921123SKonstantin Belousov  *
62369921123SKonstantin Belousov  * The real slowdown in walking the tree is the stat calls.  If FTS_NOSTAT is
62469921123SKonstantin Belousov  * set and it's a physical walk (so that symbolic links can't be directories),
62569921123SKonstantin Belousov  * we can do things quickly.  First, if it's a 4.4BSD file system, the type
62669921123SKonstantin Belousov  * of the file is in the directory entry.  Otherwise, we assume that the number
62769921123SKonstantin Belousov  * of subdirectories in a node is equal to the number of links to the parent.
62869921123SKonstantin Belousov  * The former skips all stat calls.  The latter skips stat calls in any leaf
62969921123SKonstantin Belousov  * directories and for any files after the subdirectories in the directory have
63069921123SKonstantin Belousov  * been found, cutting the stat calls by about 2/3.
63169921123SKonstantin Belousov  */
63269921123SKonstantin Belousov static FTSENT11 *
fts_build(FTS11 * sp,int type)63369921123SKonstantin Belousov fts_build(FTS11 *sp, int type)
63469921123SKonstantin Belousov {
63569921123SKonstantin Belousov 	struct freebsd11_dirent *dp;
63669921123SKonstantin Belousov 	FTSENT11 *p, *head;
63769921123SKonstantin Belousov 	FTSENT11 *cur, *tail;
63869921123SKonstantin Belousov 	DIR *dirp;
63969921123SKonstantin Belousov 	void *oldaddr;
64069921123SKonstantin Belousov 	char *cp;
641*0cff70caSGanael LAPLANCHE 	int cderrno, descend, oflag, saved_errno, nostat, doadjust,
642*0cff70caSGanael LAPLANCHE 	    readdir_errno;
64369921123SKonstantin Belousov 	long level;
64469921123SKonstantin Belousov 	long nlinks;	/* has to be signed because -1 is a magic value */
64569921123SKonstantin Belousov 	size_t dnamlen, len, maxlen, nitems;
64669921123SKonstantin Belousov 
64769921123SKonstantin Belousov 	/* Set current node pointer. */
64869921123SKonstantin Belousov 	cur = sp->fts_cur;
64969921123SKonstantin Belousov 
65069921123SKonstantin Belousov 	/*
65169921123SKonstantin Belousov 	 * Open the directory for reading.  If this fails, we're done.
65269921123SKonstantin Belousov 	 * If being called from fts_read, set the fts_info field.
65369921123SKonstantin Belousov 	 */
65469921123SKonstantin Belousov #ifdef FTS_WHITEOUT
65569921123SKonstantin Belousov 	if (ISSET(FTS_WHITEOUT))
656ed5e102aSEd Maste 		oflag = DTF_NODUP;
65769921123SKonstantin Belousov 	else
658ed5e102aSEd Maste 		oflag = DTF_HIDEW | DTF_NODUP;
65969921123SKonstantin Belousov #else
66069921123SKonstantin Belousov #define __opendir2(path, flag) opendir(path)
66169921123SKonstantin Belousov #endif
66269921123SKonstantin Belousov 	if ((dirp = __opendir2(cur->fts_accpath, oflag)) == NULL) {
66369921123SKonstantin Belousov 		if (type == BREAD) {
66469921123SKonstantin Belousov 			cur->fts_info = FTS_DNR;
66569921123SKonstantin Belousov 			cur->fts_errno = errno;
66669921123SKonstantin Belousov 		}
66769921123SKonstantin Belousov 		return (NULL);
66869921123SKonstantin Belousov 	}
66969921123SKonstantin Belousov 
67069921123SKonstantin Belousov 	/*
67169921123SKonstantin Belousov 	 * Nlinks is the number of possible entries of type directory in the
67269921123SKonstantin Belousov 	 * directory if we're cheating on stat calls, 0 if we're not doing
67369921123SKonstantin Belousov 	 * any stat calls at all, -1 if we're doing stats on everything.
67469921123SKonstantin Belousov 	 */
67569921123SKonstantin Belousov 	if (type == BNAMES) {
67669921123SKonstantin Belousov 		nlinks = 0;
67769921123SKonstantin Belousov 		/* Be quiet about nostat, GCC. */
67869921123SKonstantin Belousov 		nostat = 0;
67969921123SKonstantin Belousov 	} else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) {
68069921123SKonstantin Belousov 		if (fts_ufslinks(sp, cur))
68169921123SKonstantin Belousov 			nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2);
68269921123SKonstantin Belousov 		else
68369921123SKonstantin Belousov 			nlinks = -1;
68469921123SKonstantin Belousov 		nostat = 1;
68569921123SKonstantin Belousov 	} else {
68669921123SKonstantin Belousov 		nlinks = -1;
68769921123SKonstantin Belousov 		nostat = 0;
68869921123SKonstantin Belousov 	}
68969921123SKonstantin Belousov 
69069921123SKonstantin Belousov #ifdef notdef
69169921123SKonstantin Belousov 	(void)printf("nlinks == %d (cur: %d)\n", nlinks, cur->fts_nlink);
69269921123SKonstantin Belousov 	(void)printf("NOSTAT %d PHYSICAL %d SEEDOT %d\n",
69369921123SKonstantin Belousov 	    ISSET(FTS_NOSTAT), ISSET(FTS_PHYSICAL), ISSET(FTS_SEEDOT));
69469921123SKonstantin Belousov #endif
69569921123SKonstantin Belousov 	/*
69669921123SKonstantin Belousov 	 * If we're going to need to stat anything or we want to descend
69769921123SKonstantin Belousov 	 * and stay in the directory, chdir.  If this fails we keep going,
69869921123SKonstantin Belousov 	 * but set a flag so we don't chdir after the post-order visit.
69969921123SKonstantin Belousov 	 * We won't be able to stat anything, but we can still return the
70069921123SKonstantin Belousov 	 * names themselves.  Note, that since fts_read won't be able to
70169921123SKonstantin Belousov 	 * chdir into the directory, it will have to return different path
70269921123SKonstantin Belousov 	 * names than before, i.e. "a/b" instead of "b".  Since the node
70369921123SKonstantin Belousov 	 * has already been visited in pre-order, have to wait until the
70469921123SKonstantin Belousov 	 * post-order visit to return the error.  There is a special case
70569921123SKonstantin Belousov 	 * here, if there was nothing to stat then it's not an error to
70669921123SKonstantin Belousov 	 * not be able to stat.  This is all fairly nasty.  If a program
70769921123SKonstantin Belousov 	 * needed sorted entries or stat information, they had better be
70869921123SKonstantin Belousov 	 * checking FTS_NS on the returned nodes.
70969921123SKonstantin Belousov 	 */
71069921123SKonstantin Belousov 	cderrno = 0;
71169921123SKonstantin Belousov 	if (nlinks || type == BREAD) {
71269921123SKonstantin Belousov 		if (fts_safe_changedir(sp, cur, _dirfd(dirp), NULL)) {
71369921123SKonstantin Belousov 			if (nlinks && type == BREAD)
71469921123SKonstantin Belousov 				cur->fts_errno = errno;
71569921123SKonstantin Belousov 			cur->fts_flags |= FTS_DONTCHDIR;
71669921123SKonstantin Belousov 			descend = 0;
71769921123SKonstantin Belousov 			cderrno = errno;
71869921123SKonstantin Belousov 		} else
71969921123SKonstantin Belousov 			descend = 1;
72069921123SKonstantin Belousov 	} else
72169921123SKonstantin Belousov 		descend = 0;
72269921123SKonstantin Belousov 
72369921123SKonstantin Belousov 	/*
72469921123SKonstantin Belousov 	 * Figure out the max file name length that can be stored in the
72569921123SKonstantin Belousov 	 * current path -- the inner loop allocates more path as necessary.
72669921123SKonstantin Belousov 	 * We really wouldn't have to do the maxlen calculations here, we
72769921123SKonstantin Belousov 	 * could do them in fts_read before returning the path, but it's a
72869921123SKonstantin Belousov 	 * lot easier here since the length is part of the dirent structure.
72969921123SKonstantin Belousov 	 *
73069921123SKonstantin Belousov 	 * If not changing directories set a pointer so that can just append
73169921123SKonstantin Belousov 	 * each new name into the path.
73269921123SKonstantin Belousov 	 */
73369921123SKonstantin Belousov 	len = NAPPEND(cur);
73469921123SKonstantin Belousov 	if (ISSET(FTS_NOCHDIR)) {
73569921123SKonstantin Belousov 		cp = sp->fts_path + len;
73669921123SKonstantin Belousov 		*cp++ = '/';
73769921123SKonstantin Belousov 	} else {
73869921123SKonstantin Belousov 		/* GCC, you're too verbose. */
73969921123SKonstantin Belousov 		cp = NULL;
74069921123SKonstantin Belousov 	}
74169921123SKonstantin Belousov 	len++;
74269921123SKonstantin Belousov 	maxlen = sp->fts_pathlen - len;
74369921123SKonstantin Belousov 
74469921123SKonstantin Belousov 	level = cur->fts_level + 1;
74569921123SKonstantin Belousov 
74669921123SKonstantin Belousov 	/* Read the directory, attaching each entry to the `link' pointer. */
74769921123SKonstantin Belousov 	doadjust = 0;
748*0cff70caSGanael LAPLANCHE 	readdir_errno = 0;
74969921123SKonstantin Belousov 	for (head = tail = NULL, nitems = 0;
750*0cff70caSGanael LAPLANCHE 	    (dp = fts_safe_readdir(dirp, &readdir_errno));) {
75169921123SKonstantin Belousov 		dnamlen = dp->d_namlen;
75269921123SKonstantin Belousov 		if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))
75369921123SKonstantin Belousov 			continue;
75469921123SKonstantin Belousov 
75569921123SKonstantin Belousov 		if ((p = fts_alloc(sp, dp->d_name, dnamlen)) == NULL)
75669921123SKonstantin Belousov 			goto mem1;
75769921123SKonstantin Belousov 		if (dnamlen >= maxlen) {	/* include space for NUL */
75869921123SKonstantin Belousov 			oldaddr = sp->fts_path;
75969921123SKonstantin Belousov 			if (fts_palloc(sp, dnamlen + len + 1)) {
76069921123SKonstantin Belousov 				/*
76169921123SKonstantin Belousov 				 * No more memory for path or structures.  Save
76269921123SKonstantin Belousov 				 * errno, free up the current structure and the
76369921123SKonstantin Belousov 				 * structures already allocated.
76469921123SKonstantin Belousov 				 */
76569921123SKonstantin Belousov mem1:				saved_errno = errno;
76669921123SKonstantin Belousov 				if (p)
76769921123SKonstantin Belousov 					free(p);
76869921123SKonstantin Belousov 				fts_lfree(head);
76969921123SKonstantin Belousov 				(void)closedir(dirp);
77069921123SKonstantin Belousov 				cur->fts_info = FTS_ERR;
77169921123SKonstantin Belousov 				SET(FTS_STOP);
77269921123SKonstantin Belousov 				errno = saved_errno;
77369921123SKonstantin Belousov 				return (NULL);
77469921123SKonstantin Belousov 			}
77569921123SKonstantin Belousov 			/* Did realloc() change the pointer? */
77669921123SKonstantin Belousov 			if (oldaddr != sp->fts_path) {
77769921123SKonstantin Belousov 				doadjust = 1;
77869921123SKonstantin Belousov 				if (ISSET(FTS_NOCHDIR))
77969921123SKonstantin Belousov 					cp = sp->fts_path + len;
78069921123SKonstantin Belousov 			}
78169921123SKonstantin Belousov 			maxlen = sp->fts_pathlen - len;
78269921123SKonstantin Belousov 		}
78369921123SKonstantin Belousov 
78469921123SKonstantin Belousov 		p->fts_level = level;
78569921123SKonstantin Belousov 		p->fts_parent = sp->fts_cur;
78669921123SKonstantin Belousov 		p->fts_pathlen = len + dnamlen;
78769921123SKonstantin Belousov 
78869921123SKonstantin Belousov #ifdef FTS_WHITEOUT
78969921123SKonstantin Belousov 		if (dp->d_type == DT_WHT)
79069921123SKonstantin Belousov 			p->fts_flags |= FTS_ISW;
79169921123SKonstantin Belousov #endif
79269921123SKonstantin Belousov 
79369921123SKonstantin Belousov 		if (cderrno) {
79469921123SKonstantin Belousov 			if (nlinks) {
79569921123SKonstantin Belousov 				p->fts_info = FTS_NS;
79669921123SKonstantin Belousov 				p->fts_errno = cderrno;
79769921123SKonstantin Belousov 			} else
79869921123SKonstantin Belousov 				p->fts_info = FTS_NSOK;
79969921123SKonstantin Belousov 			p->fts_accpath = cur->fts_accpath;
80069921123SKonstantin Belousov 		} else if (nlinks == 0
80169921123SKonstantin Belousov #ifdef DT_DIR
80269921123SKonstantin Belousov 		    || (nostat &&
80369921123SKonstantin Belousov 		    dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN)
80469921123SKonstantin Belousov #endif
80569921123SKonstantin Belousov 		    ) {
80669921123SKonstantin Belousov 			p->fts_accpath =
80769921123SKonstantin Belousov 			    ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name;
80869921123SKonstantin Belousov 			p->fts_info = FTS_NSOK;
80969921123SKonstantin Belousov 		} else {
81069921123SKonstantin Belousov 			/* Build a file name for fts_stat to stat. */
81169921123SKonstantin Belousov 			if (ISSET(FTS_NOCHDIR)) {
81269921123SKonstantin Belousov 				p->fts_accpath = p->fts_path;
81369921123SKonstantin Belousov 				memmove(cp, p->fts_name, p->fts_namelen + 1);
81469921123SKonstantin Belousov 				p->fts_info = fts_stat(sp, p, 0, _dirfd(dirp));
81569921123SKonstantin Belousov 			} else {
81669921123SKonstantin Belousov 				p->fts_accpath = p->fts_name;
81769921123SKonstantin Belousov 				p->fts_info = fts_stat(sp, p, 0, -1);
81869921123SKonstantin Belousov 			}
81969921123SKonstantin Belousov 
82069921123SKonstantin Belousov 			/* Decrement link count if applicable. */
82169921123SKonstantin Belousov 			if (nlinks > 0 && (p->fts_info == FTS_D ||
82269921123SKonstantin Belousov 			    p->fts_info == FTS_DC || p->fts_info == FTS_DOT))
82369921123SKonstantin Belousov 				--nlinks;
82469921123SKonstantin Belousov 		}
82569921123SKonstantin Belousov 
82669921123SKonstantin Belousov 		/* We walk in directory order so "ls -f" doesn't get upset. */
82769921123SKonstantin Belousov 		p->fts_link = NULL;
82869921123SKonstantin Belousov 		if (head == NULL)
82969921123SKonstantin Belousov 			head = tail = p;
83069921123SKonstantin Belousov 		else {
83169921123SKonstantin Belousov 			tail->fts_link = p;
83269921123SKonstantin Belousov 			tail = p;
83369921123SKonstantin Belousov 		}
83469921123SKonstantin Belousov 		++nitems;
83569921123SKonstantin Belousov 	}
836*0cff70caSGanael LAPLANCHE 
837*0cff70caSGanael LAPLANCHE 	if (readdir_errno) {
838*0cff70caSGanael LAPLANCHE 		cur->fts_errno = readdir_errno;
839*0cff70caSGanael LAPLANCHE 		/*
840*0cff70caSGanael LAPLANCHE 		 * If we've not read any items yet, treat
841*0cff70caSGanael LAPLANCHE 		 * the error as if we can't access the dir.
842*0cff70caSGanael LAPLANCHE 		 */
843*0cff70caSGanael LAPLANCHE 		cur->fts_info = nitems ? FTS_ERR : FTS_DNR;
844*0cff70caSGanael LAPLANCHE 	}
845*0cff70caSGanael LAPLANCHE 
84669921123SKonstantin Belousov 	if (dirp)
84769921123SKonstantin Belousov 		(void)closedir(dirp);
84869921123SKonstantin Belousov 
84969921123SKonstantin Belousov 	/*
85069921123SKonstantin Belousov 	 * If realloc() changed the address of the path, adjust the
85169921123SKonstantin Belousov 	 * addresses for the rest of the tree and the dir list.
85269921123SKonstantin Belousov 	 */
85369921123SKonstantin Belousov 	if (doadjust)
85469921123SKonstantin Belousov 		fts_padjust(sp, head);
85569921123SKonstantin Belousov 
85669921123SKonstantin Belousov 	/*
85769921123SKonstantin Belousov 	 * If not changing directories, reset the path back to original
85869921123SKonstantin Belousov 	 * state.
85969921123SKonstantin Belousov 	 */
86069921123SKonstantin Belousov 	if (ISSET(FTS_NOCHDIR))
86169921123SKonstantin Belousov 		sp->fts_path[cur->fts_pathlen] = '\0';
86269921123SKonstantin Belousov 
86369921123SKonstantin Belousov 	/*
86469921123SKonstantin Belousov 	 * If descended after called from fts_children or after called from
86569921123SKonstantin Belousov 	 * fts_read and nothing found, get back.  At the root level we use
86669921123SKonstantin Belousov 	 * the saved fd; if one of fts_open()'s arguments is a relative path
86769921123SKonstantin Belousov 	 * to an empty directory, we wind up here with no other way back.  If
86869921123SKonstantin Belousov 	 * can't get back, we're done.
86969921123SKonstantin Belousov 	 */
87069921123SKonstantin Belousov 	if (descend && (type == BCHILD || !nitems) &&
87169921123SKonstantin Belousov 	    (cur->fts_level == FTS_ROOTLEVEL ?
87269921123SKonstantin Belousov 	    FCHDIR(sp, sp->fts_rfd) :
87369921123SKonstantin Belousov 	    fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) {
87469921123SKonstantin Belousov 		fts_lfree(head);
87569921123SKonstantin Belousov 		cur->fts_info = FTS_ERR;
87669921123SKonstantin Belousov 		SET(FTS_STOP);
87769921123SKonstantin Belousov 		return (NULL);
87869921123SKonstantin Belousov 	}
87969921123SKonstantin Belousov 
88069921123SKonstantin Belousov 	/* If didn't find anything, return NULL. */
88169921123SKonstantin Belousov 	if (!nitems) {
882*0cff70caSGanael LAPLANCHE 		if (type == BREAD &&
883*0cff70caSGanael LAPLANCHE 		    cur->fts_info != FTS_DNR && cur->fts_info != FTS_ERR)
88469921123SKonstantin Belousov 			cur->fts_info = FTS_DP;
88569921123SKonstantin Belousov 		return (NULL);
88669921123SKonstantin Belousov 	}
88769921123SKonstantin Belousov 
88869921123SKonstantin Belousov 	/* Sort the entries. */
88969921123SKonstantin Belousov 	if (sp->fts_compar && nitems > 1)
89069921123SKonstantin Belousov 		head = fts_sort(sp, head, nitems);
89169921123SKonstantin Belousov 	return (head);
89269921123SKonstantin Belousov }
89369921123SKonstantin Belousov 
89469921123SKonstantin Belousov static int
fts_stat(FTS11 * sp,FTSENT11 * p,int follow,int dfd)89569921123SKonstantin Belousov fts_stat(FTS11 *sp, FTSENT11 *p, int follow, int dfd)
89669921123SKonstantin Belousov {
89769921123SKonstantin Belousov 	FTSENT11 *t;
89869921123SKonstantin Belousov 	uint32_t dev;
89969921123SKonstantin Belousov 	uint32_t ino;
90069921123SKonstantin Belousov 	struct freebsd11_stat *sbp, sb;
90169921123SKonstantin Belousov 	int saved_errno;
90269921123SKonstantin Belousov 	const char *path;
90369921123SKonstantin Belousov 
90469921123SKonstantin Belousov 	if (dfd == -1)
90569921123SKonstantin Belousov 		path = p->fts_accpath, dfd = AT_FDCWD;
90669921123SKonstantin Belousov 	else
90769921123SKonstantin Belousov 		path = p->fts_name;
90869921123SKonstantin Belousov 
90969921123SKonstantin Belousov 	/* If user needs stat info, stat buffer already allocated. */
91069921123SKonstantin Belousov 	sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp;
91169921123SKonstantin Belousov 
91269921123SKonstantin Belousov #ifdef FTS_WHITEOUT
91369921123SKonstantin Belousov 	/* Check for whiteout. */
91469921123SKonstantin Belousov 	if (p->fts_flags & FTS_ISW) {
91569921123SKonstantin Belousov 		if (sbp != &sb) {
91669921123SKonstantin Belousov 			memset(sbp, '\0', sizeof(*sbp));
91769921123SKonstantin Belousov 			sbp->st_mode = S_IFWHT;
91869921123SKonstantin Belousov 		}
91969921123SKonstantin Belousov 		return (FTS_W);
92069921123SKonstantin Belousov 	}
92169921123SKonstantin Belousov #endif
92269921123SKonstantin Belousov 
92369921123SKonstantin Belousov 	/*
92469921123SKonstantin Belousov 	 * If doing a logical walk, or application requested FTS_FOLLOW, do
92569921123SKonstantin Belousov 	 * a stat(2).  If that fails, check for a non-existent symlink.  If
92669921123SKonstantin Belousov 	 * fail, set the errno from the stat call.
92769921123SKonstantin Belousov 	 */
92869921123SKonstantin Belousov 	if (ISSET(FTS_LOGICAL) || follow) {
92969921123SKonstantin Belousov 		if (freebsd11_fstatat(dfd, path, sbp, 0)) {
93069921123SKonstantin Belousov 			saved_errno = errno;
93169921123SKonstantin Belousov 			if (freebsd11_fstatat(dfd, path, sbp,
93269921123SKonstantin Belousov 			    AT_SYMLINK_NOFOLLOW)) {
93369921123SKonstantin Belousov 				p->fts_errno = saved_errno;
93469921123SKonstantin Belousov 				goto err;
93569921123SKonstantin Belousov 			}
93669921123SKonstantin Belousov 			errno = 0;
93769921123SKonstantin Belousov 			if (S_ISLNK(sbp->st_mode))
93869921123SKonstantin Belousov 				return (FTS_SLNONE);
93969921123SKonstantin Belousov 		}
94069921123SKonstantin Belousov 	} else if (freebsd11_fstatat(dfd, path, sbp, AT_SYMLINK_NOFOLLOW)) {
94169921123SKonstantin Belousov 		p->fts_errno = errno;
94269921123SKonstantin Belousov err:		memset(sbp, 0, sizeof(*sbp));
94369921123SKonstantin Belousov 		return (FTS_NS);
94469921123SKonstantin Belousov 	}
94569921123SKonstantin Belousov 
94669921123SKonstantin Belousov 	if (S_ISDIR(sbp->st_mode)) {
94769921123SKonstantin Belousov 		/*
94869921123SKonstantin Belousov 		 * Set the device/inode.  Used to find cycles and check for
94969921123SKonstantin Belousov 		 * crossing mount points.  Also remember the link count, used
95069921123SKonstantin Belousov 		 * in fts_build to limit the number of stat calls.  It is
95169921123SKonstantin Belousov 		 * understood that these fields are only referenced if fts_info
95269921123SKonstantin Belousov 		 * is set to FTS_D.
95369921123SKonstantin Belousov 		 */
95469921123SKonstantin Belousov 		dev = p->fts_dev = sbp->st_dev;
95569921123SKonstantin Belousov 		ino = p->fts_ino = sbp->st_ino;
95669921123SKonstantin Belousov 		p->fts_nlink = sbp->st_nlink;
95769921123SKonstantin Belousov 
95869921123SKonstantin Belousov 		if (ISDOT(p->fts_name))
95969921123SKonstantin Belousov 			return (FTS_DOT);
96069921123SKonstantin Belousov 
96169921123SKonstantin Belousov 		/*
96269921123SKonstantin Belousov 		 * Cycle detection is done by brute force when the directory
96369921123SKonstantin Belousov 		 * is first encountered.  If the tree gets deep enough or the
96469921123SKonstantin Belousov 		 * number of symbolic links to directories is high enough,
96569921123SKonstantin Belousov 		 * something faster might be worthwhile.
96669921123SKonstantin Belousov 		 */
96769921123SKonstantin Belousov 		for (t = p->fts_parent;
96869921123SKonstantin Belousov 		    t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent)
96969921123SKonstantin Belousov 			if (ino == t->fts_ino && dev == t->fts_dev) {
97069921123SKonstantin Belousov 				p->fts_cycle = t;
97169921123SKonstantin Belousov 				return (FTS_DC);
97269921123SKonstantin Belousov 			}
97369921123SKonstantin Belousov 		return (FTS_D);
97469921123SKonstantin Belousov 	}
97569921123SKonstantin Belousov 	if (S_ISLNK(sbp->st_mode))
97669921123SKonstantin Belousov 		return (FTS_SL);
97769921123SKonstantin Belousov 	if (S_ISREG(sbp->st_mode))
97869921123SKonstantin Belousov 		return (FTS_F);
97969921123SKonstantin Belousov 	return (FTS_DEFAULT);
98069921123SKonstantin Belousov }
98169921123SKonstantin Belousov 
98269921123SKonstantin Belousov /*
98369921123SKonstantin Belousov  * The comparison function takes pointers to pointers to FTSENT structures.
98469921123SKonstantin Belousov  * Qsort wants a comparison function that takes pointers to void.
98569921123SKonstantin Belousov  * (Both with appropriate levels of const-poisoning, of course!)
98669921123SKonstantin Belousov  * Use a trampoline function to deal with the difference.
98769921123SKonstantin Belousov  */
98869921123SKonstantin Belousov static int
fts_compar(const void * a,const void * b)98969921123SKonstantin Belousov fts_compar(const void *a, const void *b)
99069921123SKonstantin Belousov {
99169921123SKonstantin Belousov 	FTS11 *parent;
99269921123SKonstantin Belousov 
99369921123SKonstantin Belousov 	parent = (*(const FTSENT11 * const *)a)->fts_fts;
99469921123SKonstantin Belousov 	return (*parent->fts_compar)(a, b);
99569921123SKonstantin Belousov }
99669921123SKonstantin Belousov 
99769921123SKonstantin Belousov static FTSENT11 *
fts_sort(FTS11 * sp,FTSENT11 * head,size_t nitems)99869921123SKonstantin Belousov fts_sort(FTS11 *sp, FTSENT11 *head, size_t nitems)
99969921123SKonstantin Belousov {
100069921123SKonstantin Belousov 	FTSENT11 **ap, *p;
100169921123SKonstantin Belousov 
100269921123SKonstantin Belousov 	/*
100369921123SKonstantin Belousov 	 * Construct an array of pointers to the structures and call qsort(3).
100469921123SKonstantin Belousov 	 * Reassemble the array in the order returned by qsort.  If unable to
100569921123SKonstantin Belousov 	 * sort for memory reasons, return the directory entries in their
100669921123SKonstantin Belousov 	 * current order.  Allocate enough space for the current needs plus
100769921123SKonstantin Belousov 	 * 40 so don't realloc one entry at a time.
100869921123SKonstantin Belousov 	 */
100969921123SKonstantin Belousov 	if (nitems > sp->fts_nitems) {
101069921123SKonstantin Belousov 		sp->fts_nitems = nitems + 40;
101169921123SKonstantin Belousov 		if ((sp->fts_array = reallocf(sp->fts_array,
101269921123SKonstantin Belousov 		    sp->fts_nitems * sizeof(FTSENT11 *))) == NULL) {
101369921123SKonstantin Belousov 			sp->fts_nitems = 0;
101469921123SKonstantin Belousov 			return (head);
101569921123SKonstantin Belousov 		}
101669921123SKonstantin Belousov 	}
101769921123SKonstantin Belousov 	for (ap = sp->fts_array, p = head; p; p = p->fts_link)
101869921123SKonstantin Belousov 		*ap++ = p;
101969921123SKonstantin Belousov 	qsort(sp->fts_array, nitems, sizeof(FTSENT11 *), fts_compar);
102069921123SKonstantin Belousov 	for (head = *(ap = sp->fts_array); --nitems; ++ap)
102169921123SKonstantin Belousov 		ap[0]->fts_link = ap[1];
102269921123SKonstantin Belousov 	ap[0]->fts_link = NULL;
102369921123SKonstantin Belousov 	return (head);
102469921123SKonstantin Belousov }
102569921123SKonstantin Belousov 
102669921123SKonstantin Belousov static FTSENT11 *
fts_alloc(FTS11 * sp,char * name,size_t namelen)102769921123SKonstantin Belousov fts_alloc(FTS11 *sp, char *name, size_t namelen)
102869921123SKonstantin Belousov {
102969921123SKonstantin Belousov 	FTSENT11 *p;
103069921123SKonstantin Belousov 	size_t len;
103169921123SKonstantin Belousov 
103269921123SKonstantin Belousov 	struct ftsent11_withstat {
103369921123SKonstantin Belousov 		FTSENT11	ent;
103469921123SKonstantin Belousov 		struct	freebsd11_stat statbuf;
103569921123SKonstantin Belousov 	};
103669921123SKonstantin Belousov 
103769921123SKonstantin Belousov 	/*
103869921123SKonstantin Belousov 	 * The file name is a variable length array and no stat structure is
103969921123SKonstantin Belousov 	 * necessary if the user has set the nostat bit.  Allocate the FTSENT
104069921123SKonstantin Belousov 	 * structure, the file name and the stat structure in one chunk, but
104169921123SKonstantin Belousov 	 * be careful that the stat structure is reasonably aligned.
104269921123SKonstantin Belousov 	 */
104369921123SKonstantin Belousov 	if (ISSET(FTS_NOSTAT))
104469921123SKonstantin Belousov 		len = sizeof(FTSENT11) + namelen + 1;
104569921123SKonstantin Belousov 	else
104669921123SKonstantin Belousov 		len = sizeof(struct ftsent11_withstat) + namelen + 1;
104769921123SKonstantin Belousov 
104869921123SKonstantin Belousov 	if ((p = malloc(len)) == NULL)
104969921123SKonstantin Belousov 		return (NULL);
105069921123SKonstantin Belousov 
105169921123SKonstantin Belousov 	if (ISSET(FTS_NOSTAT)) {
105269921123SKonstantin Belousov 		p->fts_name = (char *)(p + 1);
105369921123SKonstantin Belousov 		p->fts_statp = NULL;
105469921123SKonstantin Belousov 	} else {
105569921123SKonstantin Belousov 		p->fts_name = (char *)((struct ftsent11_withstat *)p + 1);
105669921123SKonstantin Belousov 		p->fts_statp = &((struct ftsent11_withstat *)p)->statbuf;
105769921123SKonstantin Belousov 	}
105869921123SKonstantin Belousov 
105969921123SKonstantin Belousov 	/* Copy the name and guarantee NUL termination. */
106069921123SKonstantin Belousov 	memcpy(p->fts_name, name, namelen);
106169921123SKonstantin Belousov 	p->fts_name[namelen] = '\0';
106269921123SKonstantin Belousov 	p->fts_namelen = namelen;
106369921123SKonstantin Belousov 	p->fts_path = sp->fts_path;
106469921123SKonstantin Belousov 	p->fts_errno = 0;
106569921123SKonstantin Belousov 	p->fts_flags = 0;
106669921123SKonstantin Belousov 	p->fts_instr = FTS_NOINSTR;
106769921123SKonstantin Belousov 	p->fts_number = 0;
106869921123SKonstantin Belousov 	p->fts_pointer = NULL;
106969921123SKonstantin Belousov 	p->fts_fts = sp;
107069921123SKonstantin Belousov 	return (p);
107169921123SKonstantin Belousov }
107269921123SKonstantin Belousov 
107369921123SKonstantin Belousov static void
fts_lfree(FTSENT11 * head)107469921123SKonstantin Belousov fts_lfree(FTSENT11 *head)
107569921123SKonstantin Belousov {
107669921123SKonstantin Belousov 	FTSENT11 *p;
107769921123SKonstantin Belousov 
107869921123SKonstantin Belousov 	/* Free a linked list of structures. */
107969921123SKonstantin Belousov 	while ((p = head)) {
108069921123SKonstantin Belousov 		head = head->fts_link;
108169921123SKonstantin Belousov 		free(p);
108269921123SKonstantin Belousov 	}
108369921123SKonstantin Belousov }
108469921123SKonstantin Belousov 
108569921123SKonstantin Belousov /*
108669921123SKonstantin Belousov  * Allow essentially unlimited paths; find, rm, ls should all work on any tree.
108769921123SKonstantin Belousov  * Most systems will allow creation of paths much longer than MAXPATHLEN, even
108869921123SKonstantin Belousov  * though the kernel won't resolve them.  Add the size (not just what's needed)
108969921123SKonstantin Belousov  * plus 256 bytes so don't realloc the path 2 bytes at a time.
109069921123SKonstantin Belousov  */
109169921123SKonstantin Belousov static int
fts_palloc(FTS11 * sp,size_t more)109269921123SKonstantin Belousov fts_palloc(FTS11 *sp, size_t more)
109369921123SKonstantin Belousov {
109469921123SKonstantin Belousov 
109569921123SKonstantin Belousov 	sp->fts_pathlen += more + 256;
109669921123SKonstantin Belousov 	sp->fts_path = reallocf(sp->fts_path, sp->fts_pathlen);
109769921123SKonstantin Belousov 	return (sp->fts_path == NULL);
109869921123SKonstantin Belousov }
109969921123SKonstantin Belousov 
110069921123SKonstantin Belousov /*
110169921123SKonstantin Belousov  * When the path is realloc'd, have to fix all of the pointers in structures
110269921123SKonstantin Belousov  * already returned.
110369921123SKonstantin Belousov  */
110469921123SKonstantin Belousov static void
fts_padjust(FTS11 * sp,FTSENT11 * head)110569921123SKonstantin Belousov fts_padjust(FTS11 *sp, FTSENT11 *head)
110669921123SKonstantin Belousov {
110769921123SKonstantin Belousov 	FTSENT11 *p;
110869921123SKonstantin Belousov 	char *addr = sp->fts_path;
110969921123SKonstantin Belousov 
111069921123SKonstantin Belousov #define	ADJUST(p) do {							\
111169921123SKonstantin Belousov 	if ((p)->fts_accpath != (p)->fts_name) {			\
111269921123SKonstantin Belousov 		(p)->fts_accpath =					\
111369921123SKonstantin Belousov 		    (char *)addr + ((p)->fts_accpath - (p)->fts_path);	\
111469921123SKonstantin Belousov 	}								\
111569921123SKonstantin Belousov 	(p)->fts_path = addr;						\
111669921123SKonstantin Belousov } while (0)
111769921123SKonstantin Belousov 	/* Adjust the current set of children. */
111869921123SKonstantin Belousov 	for (p = sp->fts_child; p; p = p->fts_link)
111969921123SKonstantin Belousov 		ADJUST(p);
112069921123SKonstantin Belousov 
112169921123SKonstantin Belousov 	/* Adjust the rest of the tree, including the current level. */
112269921123SKonstantin Belousov 	for (p = head; p->fts_level >= FTS_ROOTLEVEL;) {
112369921123SKonstantin Belousov 		ADJUST(p);
112469921123SKonstantin Belousov 		p = p->fts_link ? p->fts_link : p->fts_parent;
112569921123SKonstantin Belousov 	}
112669921123SKonstantin Belousov }
112769921123SKonstantin Belousov 
112869921123SKonstantin Belousov static size_t
fts_maxarglen(char * const * argv)112969921123SKonstantin Belousov fts_maxarglen(char * const *argv)
113069921123SKonstantin Belousov {
113169921123SKonstantin Belousov 	size_t len, max;
113269921123SKonstantin Belousov 
113369921123SKonstantin Belousov 	for (max = 0; *argv; ++argv)
113469921123SKonstantin Belousov 		if ((len = strlen(*argv)) > max)
113569921123SKonstantin Belousov 			max = len;
113669921123SKonstantin Belousov 	return (max + 1);
113769921123SKonstantin Belousov }
113869921123SKonstantin Belousov 
113969921123SKonstantin Belousov /*
114069921123SKonstantin Belousov  * Change to dir specified by fd or p->fts_accpath without getting
114169921123SKonstantin Belousov  * tricked by someone changing the world out from underneath us.
114269921123SKonstantin Belousov  * Assumes p->fts_dev and p->fts_ino are filled in.
114369921123SKonstantin Belousov  */
114469921123SKonstantin Belousov static int
fts_safe_changedir(FTS11 * sp,FTSENT11 * p,int fd,char * path)114569921123SKonstantin Belousov fts_safe_changedir(FTS11 *sp, FTSENT11 *p, int fd, char *path)
114669921123SKonstantin Belousov {
114769921123SKonstantin Belousov 	int ret, oerrno, newfd;
114869921123SKonstantin Belousov 	struct freebsd11_stat sb;
114969921123SKonstantin Belousov 
115069921123SKonstantin Belousov 	newfd = fd;
115169921123SKonstantin Belousov 	if (ISSET(FTS_NOCHDIR))
115269921123SKonstantin Belousov 		return (0);
115369921123SKonstantin Belousov 	if (fd < 0 && (newfd = _open(path, O_RDONLY | O_DIRECTORY |
115469921123SKonstantin Belousov 	    O_CLOEXEC, 0)) < 0)
115569921123SKonstantin Belousov 		return (-1);
115669921123SKonstantin Belousov 	if (freebsd11_fstat(newfd, &sb)) {
115769921123SKonstantin Belousov 		ret = -1;
115869921123SKonstantin Belousov 		goto bail;
115969921123SKonstantin Belousov 	}
116069921123SKonstantin Belousov 	if (p->fts_dev != sb.st_dev || p->fts_ino != sb.st_ino) {
116169921123SKonstantin Belousov 		errno = ENOENT;		/* disinformation */
116269921123SKonstantin Belousov 		ret = -1;
116369921123SKonstantin Belousov 		goto bail;
116469921123SKonstantin Belousov 	}
116569921123SKonstantin Belousov 	ret = fchdir(newfd);
116669921123SKonstantin Belousov bail:
116769921123SKonstantin Belousov 	oerrno = errno;
116869921123SKonstantin Belousov 	if (fd < 0)
116969921123SKonstantin Belousov 		(void)_close(newfd);
117069921123SKonstantin Belousov 	errno = oerrno;
117169921123SKonstantin Belousov 	return (ret);
117269921123SKonstantin Belousov }
117369921123SKonstantin Belousov 
117469921123SKonstantin Belousov /*
117569921123SKonstantin Belousov  * Check if the filesystem for "ent" has UFS-style links.
117669921123SKonstantin Belousov  */
117769921123SKonstantin Belousov static int
fts_ufslinks(FTS11 * sp,const FTSENT11 * ent)117869921123SKonstantin Belousov fts_ufslinks(FTS11 *sp, const FTSENT11 *ent)
117969921123SKonstantin Belousov {
118069921123SKonstantin Belousov 	struct _fts_private11 *priv;
118169921123SKonstantin Belousov 	const char **cpp;
118269921123SKonstantin Belousov 
118369921123SKonstantin Belousov 	priv = (struct _fts_private11 *)sp;
118469921123SKonstantin Belousov 	/*
118569921123SKonstantin Belousov 	 * If this node's device is different from the previous, grab
118669921123SKonstantin Belousov 	 * the filesystem information, and decide on the reliability
118769921123SKonstantin Belousov 	 * of the link information from this filesystem for stat(2)
118869921123SKonstantin Belousov 	 * avoidance.
118969921123SKonstantin Belousov 	 */
119069921123SKonstantin Belousov 	if (priv->ftsp_dev != ent->fts_dev) {
119169921123SKonstantin Belousov 		if (freebsd11_statfs(ent->fts_path, &priv->ftsp_statfs) != -1) {
119269921123SKonstantin Belousov 			priv->ftsp_dev = ent->fts_dev;
119369921123SKonstantin Belousov 			priv->ftsp_linksreliable = 0;
119469921123SKonstantin Belousov 			for (cpp = ufslike_filesystems; *cpp; cpp++) {
119569921123SKonstantin Belousov 				if (strcmp(priv->ftsp_statfs.f_fstypename,
119669921123SKonstantin Belousov 				    *cpp) == 0) {
119769921123SKonstantin Belousov 					priv->ftsp_linksreliable = 1;
119869921123SKonstantin Belousov 					break;
119969921123SKonstantin Belousov 				}
120069921123SKonstantin Belousov 			}
120169921123SKonstantin Belousov 		} else {
120269921123SKonstantin Belousov 			priv->ftsp_linksreliable = 0;
120369921123SKonstantin Belousov 		}
120469921123SKonstantin Belousov 	}
120569921123SKonstantin Belousov 	return (priv->ftsp_linksreliable);
120669921123SKonstantin Belousov }
120769921123SKonstantin Belousov 
120869921123SKonstantin Belousov __sym_compat(fts_open, freebsd11_fts_open, FBSD_1.1);
120969921123SKonstantin Belousov __sym_compat(fts_close, freebsd11_fts_close, FBSD_1.1);
121069921123SKonstantin Belousov __sym_compat(fts_read, freebsd11_fts_read, FBSD_1.1);
121169921123SKonstantin Belousov __sym_compat(fts_set, freebsd11_fts_set, FBSD_1.1);
121269921123SKonstantin Belousov __sym_compat(fts_children, freebsd11_fts_children, FBSD_1.1);
121369921123SKonstantin Belousov __sym_compat(fts_get_clientptr, freebsd11_fts_get_clientptr, FBSD_1.1);
121469921123SKonstantin Belousov __sym_compat(fts_get_stream, freebsd11_fts_get_stream, FBSD_1.1);
121569921123SKonstantin Belousov __sym_compat(fts_set_clientptr, freebsd11_fts_set_clientptr, FBSD_1.1);
1216