1f9f848faSopenharmony_ci/*-
2f9f848faSopenharmony_ci * SPDX-License-Identifier: BSD-3-Clause
3f9f848faSopenharmony_ci *
4f9f848faSopenharmony_ci * Copyright (c) 1990, 1993, 1994
5f9f848faSopenharmony_ci *	The Regents of the University of California.  All rights reserved.
6f9f848faSopenharmony_ci *
7f9f848faSopenharmony_ci * Redistribution and use in source and binary forms, with or without
8f9f848faSopenharmony_ci * modification, are permitted provided that the following conditions
9f9f848faSopenharmony_ci * are met:
10f9f848faSopenharmony_ci * 1. Redistributions of source code must retain the above copyright
11f9f848faSopenharmony_ci *    notice, this list of conditions and the following disclaimer.
12f9f848faSopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright
13f9f848faSopenharmony_ci *    notice, this list of conditions and the following disclaimer in the
14f9f848faSopenharmony_ci *    documentation and/or other materials provided with the distribution.
15f9f848faSopenharmony_ci * 3. Neither the name of the University nor the names of its contributors
16f9f848faSopenharmony_ci *    may be used to endorse or promote products derived from this software
17f9f848faSopenharmony_ci *    without specific prior written permission.
18f9f848faSopenharmony_ci *
19f9f848faSopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20f9f848faSopenharmony_ci * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21f9f848faSopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22f9f848faSopenharmony_ci * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23f9f848faSopenharmony_ci * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24f9f848faSopenharmony_ci * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25f9f848faSopenharmony_ci * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26f9f848faSopenharmony_ci * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27f9f848faSopenharmony_ci * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28f9f848faSopenharmony_ci * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29f9f848faSopenharmony_ci * SUCH DAMAGE.
30f9f848faSopenharmony_ci *
31f9f848faSopenharmony_ci * $OpenBSD: fts.c,v 1.22 1999/10/03 19:22:22 millert Exp $
32f9f848faSopenharmony_ci */
33f9f848faSopenharmony_ci
34f9f848faSopenharmony_ci#include <sys/param.h>
35f9f848faSopenharmony_ci#include <sys/mount.h>
36f9f848faSopenharmony_ci#include <sys/stat.h>
37f9f848faSopenharmony_ci#include <sys/statfs.h>
38f9f848faSopenharmony_ci
39f9f848faSopenharmony_ci#include <dirent.h>
40f9f848faSopenharmony_ci#include <errno.h>
41f9f848faSopenharmony_ci#include <fcntl.h>
42f9f848faSopenharmony_ci#include <stdlib.h>
43f9f848faSopenharmony_ci#include <string.h>
44f9f848faSopenharmony_ci#include <unistd.h>
45f9f848faSopenharmony_ci#include <sys/sys/cdefs.h>
46f9f848faSopenharmony_ci
47f9f848faSopenharmony_ci#include <linux/magic.h>
48f9f848faSopenharmony_ci#include "include/fts.h"
49f9f848faSopenharmony_ci
50f9f848faSopenharmony_cistatic FTSENT	*fts_alloc(FTS *, char *, size_t);
51f9f848faSopenharmony_cistatic FTSENT	*fts_build(FTS *, int);
52f9f848faSopenharmony_cistatic void	 fts_lfree(FTSENT *);
53f9f848faSopenharmony_cistatic void	 fts_load(FTS *, FTSENT *);
54f9f848faSopenharmony_cistatic size_t	 fts_maxarglen(char * const *);
55f9f848faSopenharmony_cistatic void	 fts_padjust(FTS *, FTSENT *);
56f9f848faSopenharmony_cistatic int	 fts_palloc(FTS *, size_t);
57f9f848faSopenharmony_cistatic FTSENT	*fts_sort(FTS *, FTSENT *, size_t);
58f9f848faSopenharmony_cistatic int	 fts_stat(FTS *, FTSENT *, int, int);
59f9f848faSopenharmony_cistatic int	 fts_safe_changedir(FTS *, FTSENT *, int, char *);
60f9f848faSopenharmony_cistatic int	 fts_ufslinks(FTS *, const FTSENT *);
61f9f848faSopenharmony_ci
62f9f848faSopenharmony_ci#define	ISDOT(a)	(a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))
63f9f848faSopenharmony_ci
64f9f848faSopenharmony_ci#define	CLR(opt)	(sp->fts_options &= ~(opt))
65f9f848faSopenharmony_ci#define	ISSET(opt)	(sp->fts_options & (opt))
66f9f848faSopenharmony_ci#define	SET(opt)	(sp->fts_options |= (opt))
67f9f848faSopenharmony_ci
68f9f848faSopenharmony_ci#define	FCHDIR(sp, fd)	(!ISSET(FTS_NOCHDIR) && fchdir(fd))
69f9f848faSopenharmony_ci
70f9f848faSopenharmony_ci/* fts_build flags */
71f9f848faSopenharmony_ci#define	BCHILD		1		/* fts_children */
72f9f848faSopenharmony_ci#define	BNAMES		2		/* fts_children, names only */
73f9f848faSopenharmony_ci#define	BREAD		3		/* fts_read */
74f9f848faSopenharmony_ci
75f9f848faSopenharmony_ci/*
76f9f848faSopenharmony_ci * Internal representation of an FTS, including extra implementation
77f9f848faSopenharmony_ci * details.  The FTS returned from fts_open points to this structure's
78f9f848faSopenharmony_ci * ftsp_fts member (and can be cast to an _fts_private as required)
79f9f848faSopenharmony_ci */
80f9f848faSopenharmony_cistruct _fts_private {
81f9f848faSopenharmony_ci	FTS		ftsp_fts;
82f9f848faSopenharmony_ci	struct statfs	ftsp_statfs;
83f9f848faSopenharmony_ci	dev_t		ftsp_dev;
84f9f848faSopenharmony_ci	int		ftsp_linksreliable;
85f9f848faSopenharmony_ci};
86f9f848faSopenharmony_ci
87f9f848faSopenharmony_ci/*
88f9f848faSopenharmony_ci * The "FTS_NOSTAT" option can avoid a lot of calls to stat(2) if it
89f9f848faSopenharmony_ci * knows that a directory could not possibly have subdirectories.  This
90f9f848faSopenharmony_ci * is decided by looking at the link count: a subdirectory would
91f9f848faSopenharmony_ci * increment its parent's link count by virtue of its own ".." entry.
92f9f848faSopenharmony_ci * This assumption only holds for UFS-like filesystems that implement
93f9f848faSopenharmony_ci * links and directories this way, so we must punt for others.
94f9f848faSopenharmony_ci */
95f9f848faSopenharmony_ci
96f9f848faSopenharmony_ci//
97f9f848faSopenharmony_ci// Make it works on Linux compiler
98f9f848faSopenharmony_ci//
99f9f848faSopenharmony_ci//static const char *ufslike_filesystems[] = {
100f9f848faSopenharmony_ci//       "ufs",
101f9f848faSopenharmony_ci//       "zfs",
102f9f848faSopenharmony_ci//       "nfs",
103f9f848faSopenharmony_ci//       "ext2fs",
104f9f848faSopenharmony_ci//       0
105f9f848faSopenharmony_ci//};
106f9f848faSopenharmony_ci//
107f9f848faSopenharmony_ci// Values from "man 2 statfs". Linux not support zfs.
108f9f848faSopenharmony_cistatic const uint64_t ufslike_filesystems[] = {
109f9f848faSopenharmony_ci	0x00011954, // ufs
110f9f848faSopenharmony_ci	0x6969,     // nfs
111f9f848faSopenharmony_ci	0xef53,     // ext2fs
112f9f848faSopenharmony_ci};
113f9f848faSopenharmony_ci
114f9f848faSopenharmony_ciFTS *
115f9f848faSopenharmony_cifts_open(char * const *argv, int options,
116f9f848faSopenharmony_ci    int (*compar)(const FTSENT * const *, const FTSENT * const *))
117f9f848faSopenharmony_ci{
118f9f848faSopenharmony_ci	struct _fts_private *priv;
119f9f848faSopenharmony_ci	FTS *sp;
120f9f848faSopenharmony_ci	FTSENT *p, *root;
121f9f848faSopenharmony_ci	FTSENT *parent, *tmp;
122f9f848faSopenharmony_ci	size_t len, nitems;
123f9f848faSopenharmony_ci
124f9f848faSopenharmony_ci	/* Options check. */
125f9f848faSopenharmony_ci	if (options & ~FTS_OPTIONMASK) {
126f9f848faSopenharmony_ci		errno = EINVAL;
127f9f848faSopenharmony_ci		return (NULL);
128f9f848faSopenharmony_ci	}
129f9f848faSopenharmony_ci
130f9f848faSopenharmony_ci	/* fts_open() requires at least one path */
131f9f848faSopenharmony_ci	if (*argv == NULL) {
132f9f848faSopenharmony_ci		errno = EINVAL;
133f9f848faSopenharmony_ci		return (NULL);
134f9f848faSopenharmony_ci	}
135f9f848faSopenharmony_ci
136f9f848faSopenharmony_ci	/* Allocate/initialize the stream. */
137f9f848faSopenharmony_ci	if ((priv = calloc(1, sizeof(*priv))) == NULL)
138f9f848faSopenharmony_ci		return (NULL);
139f9f848faSopenharmony_ci	sp = &priv->ftsp_fts;
140f9f848faSopenharmony_ci	sp->fts_compar = compar;
141f9f848faSopenharmony_ci	sp->fts_options = options;
142f9f848faSopenharmony_ci
143f9f848faSopenharmony_ci	/* Logical walks turn on NOCHDIR; symbolic links are too hard. */
144f9f848faSopenharmony_ci	if (ISSET(FTS_LOGICAL))
145f9f848faSopenharmony_ci		SET(FTS_NOCHDIR);
146f9f848faSopenharmony_ci
147f9f848faSopenharmony_ci	/*
148f9f848faSopenharmony_ci	 * Start out with 1K of path space, and enough, in any case,
149f9f848faSopenharmony_ci	 * to hold the user's paths.
150f9f848faSopenharmony_ci	 */
151f9f848faSopenharmony_ci	if (fts_palloc(sp, MAX(fts_maxarglen(argv), MAXPATHLEN)))
152f9f848faSopenharmony_ci		goto mem1;
153f9f848faSopenharmony_ci
154f9f848faSopenharmony_ci	/* Allocate/initialize root's parent. */
155f9f848faSopenharmony_ci	if ((parent = fts_alloc(sp, "", 0)) == NULL)
156f9f848faSopenharmony_ci		goto mem2;
157f9f848faSopenharmony_ci	parent->fts_level = FTS_ROOTPARENTLEVEL;
158f9f848faSopenharmony_ci
159f9f848faSopenharmony_ci	/* Shush, GCC. */
160f9f848faSopenharmony_ci	tmp = NULL;
161f9f848faSopenharmony_ci
162f9f848faSopenharmony_ci	/* Allocate/initialize root(s). */
163f9f848faSopenharmony_ci	for (root = NULL, nitems = 0; *argv != NULL; ++argv, ++nitems) {
164f9f848faSopenharmony_ci		len = strlen(*argv);
165f9f848faSopenharmony_ci
166f9f848faSopenharmony_ci		p = fts_alloc(sp, *argv, len);
167f9f848faSopenharmony_ci		p->fts_level = FTS_ROOTLEVEL;
168f9f848faSopenharmony_ci		p->fts_parent = parent;
169f9f848faSopenharmony_ci		p->fts_accpath = p->fts_name;
170f9f848faSopenharmony_ci		p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW), -1);
171f9f848faSopenharmony_ci
172f9f848faSopenharmony_ci		/* Command-line "." and ".." are real directories. */
173f9f848faSopenharmony_ci		if (p->fts_info == FTS_DOT)
174f9f848faSopenharmony_ci			p->fts_info = FTS_D;
175f9f848faSopenharmony_ci
176f9f848faSopenharmony_ci		/*
177f9f848faSopenharmony_ci		 * If comparison routine supplied, traverse in sorted
178f9f848faSopenharmony_ci		 * order; otherwise traverse in the order specified.
179f9f848faSopenharmony_ci		 */
180f9f848faSopenharmony_ci		if (compar) {
181f9f848faSopenharmony_ci			p->fts_link = root;
182f9f848faSopenharmony_ci			root = p;
183f9f848faSopenharmony_ci		} else {
184f9f848faSopenharmony_ci			p->fts_link = NULL;
185f9f848faSopenharmony_ci			if (root == NULL)
186f9f848faSopenharmony_ci				tmp = root = p;
187f9f848faSopenharmony_ci			else {
188f9f848faSopenharmony_ci				tmp->fts_link = p;
189f9f848faSopenharmony_ci				tmp = p;
190f9f848faSopenharmony_ci			}
191f9f848faSopenharmony_ci		}
192f9f848faSopenharmony_ci	}
193f9f848faSopenharmony_ci	if (compar && nitems > 1)
194f9f848faSopenharmony_ci		root = fts_sort(sp, root, nitems);
195f9f848faSopenharmony_ci
196f9f848faSopenharmony_ci	/*
197f9f848faSopenharmony_ci	 * Allocate a dummy pointer and make fts_read think that we've just
198f9f848faSopenharmony_ci	 * finished the node before the root(s); set p->fts_info to FTS_INIT
199f9f848faSopenharmony_ci	 * so that everything about the "current" node is ignored.
200f9f848faSopenharmony_ci	 */
201f9f848faSopenharmony_ci	if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL)
202f9f848faSopenharmony_ci		goto mem3;
203f9f848faSopenharmony_ci	sp->fts_cur->fts_link = root;
204f9f848faSopenharmony_ci	sp->fts_cur->fts_info = FTS_INIT;
205f9f848faSopenharmony_ci
206f9f848faSopenharmony_ci	/*
207f9f848faSopenharmony_ci	 * If using chdir(2), grab a file descriptor pointing to dot to ensure
208f9f848faSopenharmony_ci	 * that we can get back here; this could be avoided for some paths,
209f9f848faSopenharmony_ci	 * but almost certainly not worth the effort.  Slashes, symbolic links,
210f9f848faSopenharmony_ci	 * and ".." are all fairly nasty problems.  Note, if we can't get the
211f9f848faSopenharmony_ci	 * descriptor we run anyway, just more slowly.
212f9f848faSopenharmony_ci	 */
213f9f848faSopenharmony_ci	if (!ISSET(FTS_NOCHDIR) &&
214f9f848faSopenharmony_ci	    (sp->fts_rfd = _open(".", O_RDONLY | O_CLOEXEC, 0)) < 0)
215f9f848faSopenharmony_ci		SET(FTS_NOCHDIR);
216f9f848faSopenharmony_ci
217f9f848faSopenharmony_ci	return (sp);
218f9f848faSopenharmony_ci
219f9f848faSopenharmony_cimem3:	fts_lfree(root);
220f9f848faSopenharmony_ci	free(parent);
221f9f848faSopenharmony_cimem2:	free(sp->fts_path);
222f9f848faSopenharmony_cimem1:	free(sp);
223f9f848faSopenharmony_ci	return (NULL);
224f9f848faSopenharmony_ci}
225f9f848faSopenharmony_ci
226f9f848faSopenharmony_cistatic void
227f9f848faSopenharmony_cifts_load(FTS *sp, FTSENT *p)
228f9f848faSopenharmony_ci{
229f9f848faSopenharmony_ci	size_t len;
230f9f848faSopenharmony_ci	char *cp;
231f9f848faSopenharmony_ci
232f9f848faSopenharmony_ci	/*
233f9f848faSopenharmony_ci	 * Load the stream structure for the next traversal.  Since we don't
234f9f848faSopenharmony_ci	 * actually enter the directory until after the preorder visit, set
235f9f848faSopenharmony_ci	 * the fts_accpath field specially so the chdir gets done to the right
236f9f848faSopenharmony_ci	 * place and the user can access the first node.  From fts_open it's
237f9f848faSopenharmony_ci	 * known that the path will fit.
238f9f848faSopenharmony_ci	 */
239f9f848faSopenharmony_ci	len = p->fts_pathlen = p->fts_namelen;
240f9f848faSopenharmony_ci	memmove(sp->fts_path, p->fts_name, len + 1);
241f9f848faSopenharmony_ci	if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) {
242f9f848faSopenharmony_ci		len = strlen(++cp);
243f9f848faSopenharmony_ci		memmove(p->fts_name, cp, len + 1);
244f9f848faSopenharmony_ci		p->fts_namelen = len;
245f9f848faSopenharmony_ci	}
246f9f848faSopenharmony_ci	p->fts_accpath = p->fts_path = sp->fts_path;
247f9f848faSopenharmony_ci	sp->fts_dev = p->fts_dev;
248f9f848faSopenharmony_ci}
249f9f848faSopenharmony_ci
250f9f848faSopenharmony_ciint
251f9f848faSopenharmony_cifts_close(FTS *sp)
252f9f848faSopenharmony_ci{
253f9f848faSopenharmony_ci	FTSENT *freep, *p;
254f9f848faSopenharmony_ci	int saved_errno;
255f9f848faSopenharmony_ci
256f9f848faSopenharmony_ci	/*
257f9f848faSopenharmony_ci	 * This still works if we haven't read anything -- the dummy structure
258f9f848faSopenharmony_ci	 * points to the root list, so we step through to the end of the root
259f9f848faSopenharmony_ci	 * list which has a valid parent pointer.
260f9f848faSopenharmony_ci	 */
261f9f848faSopenharmony_ci	if (sp->fts_cur) {
262f9f848faSopenharmony_ci		for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {
263f9f848faSopenharmony_ci			freep = p;
264f9f848faSopenharmony_ci			p = p->fts_link != NULL ? p->fts_link : p->fts_parent;
265f9f848faSopenharmony_ci			free(freep);
266f9f848faSopenharmony_ci		}
267f9f848faSopenharmony_ci		free(p);
268f9f848faSopenharmony_ci	}
269f9f848faSopenharmony_ci
270f9f848faSopenharmony_ci	/* Free up child linked list, sort array, path buffer. */
271f9f848faSopenharmony_ci	if (sp->fts_child)
272f9f848faSopenharmony_ci		fts_lfree(sp->fts_child);
273f9f848faSopenharmony_ci	if (sp->fts_array)
274f9f848faSopenharmony_ci		free(sp->fts_array);
275f9f848faSopenharmony_ci	free(sp->fts_path);
276f9f848faSopenharmony_ci
277f9f848faSopenharmony_ci	/* Return to original directory, save errno if necessary. */
278f9f848faSopenharmony_ci	if (!ISSET(FTS_NOCHDIR)) {
279f9f848faSopenharmony_ci		saved_errno = fchdir(sp->fts_rfd) ? errno : 0;
280f9f848faSopenharmony_ci		(void)_close(sp->fts_rfd);
281f9f848faSopenharmony_ci
282f9f848faSopenharmony_ci		/* Set errno and return. */
283f9f848faSopenharmony_ci		if (saved_errno != 0) {
284f9f848faSopenharmony_ci			/* Free up the stream pointer. */
285f9f848faSopenharmony_ci			free(sp);
286f9f848faSopenharmony_ci			errno = saved_errno;
287f9f848faSopenharmony_ci			return (-1);
288f9f848faSopenharmony_ci		}
289f9f848faSopenharmony_ci	}
290f9f848faSopenharmony_ci
291f9f848faSopenharmony_ci	/* Free up the stream pointer. */
292f9f848faSopenharmony_ci	free(sp);
293f9f848faSopenharmony_ci	return (0);
294f9f848faSopenharmony_ci}
295f9f848faSopenharmony_ci
296f9f848faSopenharmony_ci/*
297f9f848faSopenharmony_ci * Special case of "/" at the end of the path so that slashes aren't
298f9f848faSopenharmony_ci * appended which would cause paths to be written as "....//foo".
299f9f848faSopenharmony_ci */
300f9f848faSopenharmony_ci#define	NAPPEND(p)							\
301f9f848faSopenharmony_ci	(p->fts_path[p->fts_pathlen - 1] == '/'				\
302f9f848faSopenharmony_ci	    ? p->fts_pathlen - 1 : p->fts_pathlen)
303f9f848faSopenharmony_ci
304f9f848faSopenharmony_ciFTSENT *
305f9f848faSopenharmony_cifts_read(FTS *sp)
306f9f848faSopenharmony_ci{
307f9f848faSopenharmony_ci	FTSENT *p, *tmp;
308f9f848faSopenharmony_ci	int instr;
309f9f848faSopenharmony_ci	char *t;
310f9f848faSopenharmony_ci	int saved_errno;
311f9f848faSopenharmony_ci
312f9f848faSopenharmony_ci	/* If finished or unrecoverable error, return NULL. */
313f9f848faSopenharmony_ci	if (sp->fts_cur == NULL || ISSET(FTS_STOP))
314f9f848faSopenharmony_ci		return (NULL);
315f9f848faSopenharmony_ci
316f9f848faSopenharmony_ci	/* Set current node pointer. */
317f9f848faSopenharmony_ci	p = sp->fts_cur;
318f9f848faSopenharmony_ci
319f9f848faSopenharmony_ci	/* Save and zero out user instructions. */
320f9f848faSopenharmony_ci	instr = p->fts_instr;
321f9f848faSopenharmony_ci	p->fts_instr = FTS_NOINSTR;
322f9f848faSopenharmony_ci
323f9f848faSopenharmony_ci	/* Any type of file may be re-visited; re-stat and re-turn. */
324f9f848faSopenharmony_ci	if (instr == FTS_AGAIN) {
325f9f848faSopenharmony_ci		p->fts_info = fts_stat(sp, p, 0, -1);
326f9f848faSopenharmony_ci		return (p);
327f9f848faSopenharmony_ci	}
328f9f848faSopenharmony_ci
329f9f848faSopenharmony_ci	/*
330f9f848faSopenharmony_ci	 * Following a symlink -- SLNONE test allows application to see
331f9f848faSopenharmony_ci	 * SLNONE and recover.  If indirecting through a symlink, have
332f9f848faSopenharmony_ci	 * keep a pointer to current location.  If unable to get that
333f9f848faSopenharmony_ci	 * pointer, follow fails.
334f9f848faSopenharmony_ci	 */
335f9f848faSopenharmony_ci	if (instr == FTS_FOLLOW &&
336f9f848faSopenharmony_ci	    (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {
337f9f848faSopenharmony_ci		p->fts_info = fts_stat(sp, p, 1, -1);
338f9f848faSopenharmony_ci		if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
339f9f848faSopenharmony_ci			if ((p->fts_symfd = _open(".", O_RDONLY | O_CLOEXEC,
340f9f848faSopenharmony_ci			    0)) < 0) {
341f9f848faSopenharmony_ci				p->fts_errno = errno;
342f9f848faSopenharmony_ci				p->fts_info = FTS_ERR;
343f9f848faSopenharmony_ci			} else
344f9f848faSopenharmony_ci				p->fts_flags |= FTS_SYMFOLLOW;
345f9f848faSopenharmony_ci		}
346f9f848faSopenharmony_ci		return (p);
347f9f848faSopenharmony_ci	}
348f9f848faSopenharmony_ci
349f9f848faSopenharmony_ci	/* Directory in pre-order. */
350f9f848faSopenharmony_ci	if (p->fts_info == FTS_D) {
351f9f848faSopenharmony_ci		/* If skipped or crossed mount point, do post-order visit. */
352f9f848faSopenharmony_ci		if (instr == FTS_SKIP ||
353f9f848faSopenharmony_ci		    (ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev)) {
354f9f848faSopenharmony_ci			if (p->fts_flags & FTS_SYMFOLLOW)
355f9f848faSopenharmony_ci				(void)_close(p->fts_symfd);
356f9f848faSopenharmony_ci			if (sp->fts_child) {
357f9f848faSopenharmony_ci				fts_lfree(sp->fts_child);
358f9f848faSopenharmony_ci				sp->fts_child = NULL;
359f9f848faSopenharmony_ci			}
360f9f848faSopenharmony_ci			p->fts_info = FTS_DP;
361f9f848faSopenharmony_ci			return (p);
362f9f848faSopenharmony_ci		}
363f9f848faSopenharmony_ci
364f9f848faSopenharmony_ci		/* Rebuild if only read the names and now traversing. */
365f9f848faSopenharmony_ci		if (sp->fts_child != NULL && ISSET(FTS_NAMEONLY)) {
366f9f848faSopenharmony_ci			CLR(FTS_NAMEONLY);
367f9f848faSopenharmony_ci			fts_lfree(sp->fts_child);
368f9f848faSopenharmony_ci			sp->fts_child = NULL;
369f9f848faSopenharmony_ci		}
370f9f848faSopenharmony_ci
371f9f848faSopenharmony_ci		/*
372f9f848faSopenharmony_ci		 * Cd to the subdirectory.
373f9f848faSopenharmony_ci		 *
374f9f848faSopenharmony_ci		 * If have already read and now fail to chdir, whack the list
375f9f848faSopenharmony_ci		 * to make the names come out right, and set the parent errno
376f9f848faSopenharmony_ci		 * so the application will eventually get an error condition.
377f9f848faSopenharmony_ci		 * Set the FTS_DONTCHDIR flag so that when we logically change
378f9f848faSopenharmony_ci		 * directories back to the parent we don't do a chdir.
379f9f848faSopenharmony_ci		 *
380f9f848faSopenharmony_ci		 * If haven't read do so.  If the read fails, fts_build sets
381f9f848faSopenharmony_ci		 * FTS_STOP or the fts_info field of the node.
382f9f848faSopenharmony_ci		 */
383f9f848faSopenharmony_ci		if (sp->fts_child != NULL) {
384f9f848faSopenharmony_ci			if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) {
385f9f848faSopenharmony_ci				p->fts_errno = errno;
386f9f848faSopenharmony_ci				p->fts_flags |= FTS_DONTCHDIR;
387f9f848faSopenharmony_ci				for (p = sp->fts_child; p != NULL;
388f9f848faSopenharmony_ci				    p = p->fts_link)
389f9f848faSopenharmony_ci					p->fts_accpath =
390f9f848faSopenharmony_ci					    p->fts_parent->fts_accpath;
391f9f848faSopenharmony_ci			}
392f9f848faSopenharmony_ci		} else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) {
393f9f848faSopenharmony_ci			if (ISSET(FTS_STOP))
394f9f848faSopenharmony_ci				return (NULL);
395f9f848faSopenharmony_ci			return (p);
396f9f848faSopenharmony_ci		}
397f9f848faSopenharmony_ci		p = sp->fts_child;
398f9f848faSopenharmony_ci		sp->fts_child = NULL;
399f9f848faSopenharmony_ci		goto name;
400f9f848faSopenharmony_ci	}
401f9f848faSopenharmony_ci
402f9f848faSopenharmony_ci	/* Move to the next node on this level. */
403f9f848faSopenharmony_cinext:	tmp = p;
404f9f848faSopenharmony_ci	if ((p = p->fts_link) != NULL) {
405f9f848faSopenharmony_ci		/*
406f9f848faSopenharmony_ci		 * If reached the top, return to the original directory (or
407f9f848faSopenharmony_ci		 * the root of the tree), and load the paths for the next root.
408f9f848faSopenharmony_ci		 */
409f9f848faSopenharmony_ci		if (p->fts_level == FTS_ROOTLEVEL) {
410f9f848faSopenharmony_ci			if (FCHDIR(sp, sp->fts_rfd)) {
411f9f848faSopenharmony_ci				SET(FTS_STOP);
412f9f848faSopenharmony_ci				return (NULL);
413f9f848faSopenharmony_ci			}
414f9f848faSopenharmony_ci			free(tmp);
415f9f848faSopenharmony_ci			fts_load(sp, p);
416f9f848faSopenharmony_ci			return (sp->fts_cur = p);
417f9f848faSopenharmony_ci		}
418f9f848faSopenharmony_ci
419f9f848faSopenharmony_ci		/*
420f9f848faSopenharmony_ci		 * User may have called fts_set on the node.  If skipped,
421f9f848faSopenharmony_ci		 * ignore.  If followed, get a file descriptor so we can
422f9f848faSopenharmony_ci		 * get back if necessary.
423f9f848faSopenharmony_ci		 */
424f9f848faSopenharmony_ci		if (p->fts_instr == FTS_SKIP) {
425f9f848faSopenharmony_ci			free(tmp);
426f9f848faSopenharmony_ci			goto next;
427f9f848faSopenharmony_ci		}
428f9f848faSopenharmony_ci		if (p->fts_instr == FTS_FOLLOW) {
429f9f848faSopenharmony_ci			p->fts_info = fts_stat(sp, p, 1, -1);
430f9f848faSopenharmony_ci			if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
431f9f848faSopenharmony_ci				if ((p->fts_symfd =
432f9f848faSopenharmony_ci				    _open(".", O_RDONLY | O_CLOEXEC, 0)) < 0) {
433f9f848faSopenharmony_ci					p->fts_errno = errno;
434f9f848faSopenharmony_ci					p->fts_info = FTS_ERR;
435f9f848faSopenharmony_ci				} else
436f9f848faSopenharmony_ci					p->fts_flags |= FTS_SYMFOLLOW;
437f9f848faSopenharmony_ci			}
438f9f848faSopenharmony_ci			p->fts_instr = FTS_NOINSTR;
439f9f848faSopenharmony_ci		}
440f9f848faSopenharmony_ci
441f9f848faSopenharmony_ci		free(tmp);
442f9f848faSopenharmony_ci
443f9f848faSopenharmony_ciname:		t = sp->fts_path + NAPPEND(p->fts_parent);
444f9f848faSopenharmony_ci		*t++ = '/';
445f9f848faSopenharmony_ci		memmove(t, p->fts_name, p->fts_namelen + 1);
446f9f848faSopenharmony_ci		return (sp->fts_cur = p);
447f9f848faSopenharmony_ci	}
448f9f848faSopenharmony_ci
449f9f848faSopenharmony_ci	/* Move up to the parent node. */
450f9f848faSopenharmony_ci	p = tmp->fts_parent;
451f9f848faSopenharmony_ci
452f9f848faSopenharmony_ci	if (p->fts_level == FTS_ROOTPARENTLEVEL) {
453f9f848faSopenharmony_ci		/*
454f9f848faSopenharmony_ci		 * Done; free everything up and set errno to 0 so the user
455f9f848faSopenharmony_ci		 * can distinguish between error and EOF.
456f9f848faSopenharmony_ci		 */
457f9f848faSopenharmony_ci		free(tmp);
458f9f848faSopenharmony_ci		free(p);
459f9f848faSopenharmony_ci		errno = 0;
460f9f848faSopenharmony_ci		return (sp->fts_cur = NULL);
461f9f848faSopenharmony_ci	}
462f9f848faSopenharmony_ci
463f9f848faSopenharmony_ci	/* NUL terminate the pathname. */
464f9f848faSopenharmony_ci	sp->fts_path[p->fts_pathlen] = '\0';
465f9f848faSopenharmony_ci
466f9f848faSopenharmony_ci	/*
467f9f848faSopenharmony_ci	 * Return to the parent directory.  If at a root node or came through
468f9f848faSopenharmony_ci	 * a symlink, go back through the file descriptor.  Otherwise, cd up
469f9f848faSopenharmony_ci	 * one directory.
470f9f848faSopenharmony_ci	 */
471f9f848faSopenharmony_ci	if (p->fts_level == FTS_ROOTLEVEL) {
472f9f848faSopenharmony_ci		if (FCHDIR(sp, sp->fts_rfd)) {
473f9f848faSopenharmony_ci			SET(FTS_STOP);
474f9f848faSopenharmony_ci			return (NULL);
475f9f848faSopenharmony_ci		}
476f9f848faSopenharmony_ci	} else if (p->fts_flags & FTS_SYMFOLLOW) {
477f9f848faSopenharmony_ci		if (FCHDIR(sp, p->fts_symfd)) {
478f9f848faSopenharmony_ci			saved_errno = errno;
479f9f848faSopenharmony_ci			(void)_close(p->fts_symfd);
480f9f848faSopenharmony_ci			errno = saved_errno;
481f9f848faSopenharmony_ci			SET(FTS_STOP);
482f9f848faSopenharmony_ci			return (NULL);
483f9f848faSopenharmony_ci		}
484f9f848faSopenharmony_ci		(void)_close(p->fts_symfd);
485f9f848faSopenharmony_ci	} else if (!(p->fts_flags & FTS_DONTCHDIR) &&
486f9f848faSopenharmony_ci	    fts_safe_changedir(sp, p->fts_parent, -1, "..")) {
487f9f848faSopenharmony_ci		SET(FTS_STOP);
488f9f848faSopenharmony_ci		return (NULL);
489f9f848faSopenharmony_ci	}
490f9f848faSopenharmony_ci	free(tmp);
491f9f848faSopenharmony_ci	p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;
492f9f848faSopenharmony_ci	return (sp->fts_cur = p);
493f9f848faSopenharmony_ci}
494f9f848faSopenharmony_ci
495f9f848faSopenharmony_ci/*
496f9f848faSopenharmony_ci * Fts_set takes the stream as an argument although it's not used in this
497f9f848faSopenharmony_ci * implementation; it would be necessary if anyone wanted to add global
498f9f848faSopenharmony_ci * semantics to fts using fts_set.  An error return is allowed for similar
499f9f848faSopenharmony_ci * reasons.
500f9f848faSopenharmony_ci */
501f9f848faSopenharmony_ci/* ARGSUSED */
502f9f848faSopenharmony_ciint
503f9f848faSopenharmony_cifts_set(FTS *sp, FTSENT *p, int instr)
504f9f848faSopenharmony_ci{
505f9f848faSopenharmony_ci	if (instr != 0 && instr != FTS_AGAIN && instr != FTS_FOLLOW &&
506f9f848faSopenharmony_ci	    instr != FTS_NOINSTR && instr != FTS_SKIP) {
507f9f848faSopenharmony_ci		errno = EINVAL;
508f9f848faSopenharmony_ci		return (1);
509f9f848faSopenharmony_ci	}
510f9f848faSopenharmony_ci	p->fts_instr = instr;
511f9f848faSopenharmony_ci	return (0);
512f9f848faSopenharmony_ci}
513f9f848faSopenharmony_ci
514f9f848faSopenharmony_ciFTSENT *
515f9f848faSopenharmony_cifts_children(FTS *sp, int instr)
516f9f848faSopenharmony_ci{
517f9f848faSopenharmony_ci	FTSENT *p;
518f9f848faSopenharmony_ci	int fd, rc, serrno;
519f9f848faSopenharmony_ci
520f9f848faSopenharmony_ci	if (instr != 0 && instr != FTS_NAMEONLY) {
521f9f848faSopenharmony_ci		errno = EINVAL;
522f9f848faSopenharmony_ci		return (NULL);
523f9f848faSopenharmony_ci	}
524f9f848faSopenharmony_ci
525f9f848faSopenharmony_ci	/* Set current node pointer. */
526f9f848faSopenharmony_ci	p = sp->fts_cur;
527f9f848faSopenharmony_ci
528f9f848faSopenharmony_ci	/*
529f9f848faSopenharmony_ci	 * Errno set to 0 so user can distinguish empty directory from
530f9f848faSopenharmony_ci	 * an error.
531f9f848faSopenharmony_ci	 */
532f9f848faSopenharmony_ci	errno = 0;
533f9f848faSopenharmony_ci
534f9f848faSopenharmony_ci	/* Fatal errors stop here. */
535f9f848faSopenharmony_ci	if (ISSET(FTS_STOP))
536f9f848faSopenharmony_ci		return (NULL);
537f9f848faSopenharmony_ci
538f9f848faSopenharmony_ci	/* Return logical hierarchy of user's arguments. */
539f9f848faSopenharmony_ci	if (p->fts_info == FTS_INIT)
540f9f848faSopenharmony_ci		return (p->fts_link);
541f9f848faSopenharmony_ci
542f9f848faSopenharmony_ci	/*
543f9f848faSopenharmony_ci	 * If not a directory being visited in pre-order, stop here.  Could
544f9f848faSopenharmony_ci	 * allow FTS_DNR, assuming the user has fixed the problem, but the
545f9f848faSopenharmony_ci	 * same effect is available with FTS_AGAIN.
546f9f848faSopenharmony_ci	 */
547f9f848faSopenharmony_ci	if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */)
548f9f848faSopenharmony_ci		return (NULL);
549f9f848faSopenharmony_ci
550f9f848faSopenharmony_ci	/* Free up any previous child list. */
551f9f848faSopenharmony_ci	if (sp->fts_child != NULL)
552f9f848faSopenharmony_ci		fts_lfree(sp->fts_child);
553f9f848faSopenharmony_ci
554f9f848faSopenharmony_ci	if (instr == FTS_NAMEONLY) {
555f9f848faSopenharmony_ci		SET(FTS_NAMEONLY);
556f9f848faSopenharmony_ci		instr = BNAMES;
557f9f848faSopenharmony_ci	} else
558f9f848faSopenharmony_ci		instr = BCHILD;
559f9f848faSopenharmony_ci
560f9f848faSopenharmony_ci	/*
561f9f848faSopenharmony_ci	 * If using chdir on a relative path and called BEFORE fts_read does
562f9f848faSopenharmony_ci	 * its chdir to the root of a traversal, we can lose -- we need to
563f9f848faSopenharmony_ci	 * chdir into the subdirectory, and we don't know where the current
564f9f848faSopenharmony_ci	 * directory is, so we can't get back so that the upcoming chdir by
565f9f848faSopenharmony_ci	 * fts_read will work.
566f9f848faSopenharmony_ci	 */
567f9f848faSopenharmony_ci	if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' ||
568f9f848faSopenharmony_ci	    ISSET(FTS_NOCHDIR))
569f9f848faSopenharmony_ci		return (sp->fts_child = fts_build(sp, instr));
570f9f848faSopenharmony_ci
571f9f848faSopenharmony_ci	if ((fd = _open(".", O_RDONLY | O_CLOEXEC, 0)) < 0)
572f9f848faSopenharmony_ci		return (NULL);
573f9f848faSopenharmony_ci	sp->fts_child = fts_build(sp, instr);
574f9f848faSopenharmony_ci	serrno = (sp->fts_child == NULL) ? errno : 0;
575f9f848faSopenharmony_ci	rc = fchdir(fd);
576f9f848faSopenharmony_ci	if (rc < 0 && serrno == 0)
577f9f848faSopenharmony_ci		serrno = errno;
578f9f848faSopenharmony_ci	(void)_close(fd);
579f9f848faSopenharmony_ci	errno = serrno;
580f9f848faSopenharmony_ci	if (rc < 0)
581f9f848faSopenharmony_ci		return (NULL);
582f9f848faSopenharmony_ci	return (sp->fts_child);
583f9f848faSopenharmony_ci}
584f9f848faSopenharmony_ci
585f9f848faSopenharmony_ci#ifndef fts_get_clientptr
586f9f848faSopenharmony_ci#error "fts_get_clientptr not defined"
587f9f848faSopenharmony_ci#endif
588f9f848faSopenharmony_ci
589f9f848faSopenharmony_civoid *
590f9f848faSopenharmony_ci(fts_get_clientptr)(FTS *sp)
591f9f848faSopenharmony_ci{
592f9f848faSopenharmony_ci
593f9f848faSopenharmony_ci	return (fts_get_clientptr(sp));
594f9f848faSopenharmony_ci}
595f9f848faSopenharmony_ci
596f9f848faSopenharmony_ci#ifndef fts_get_stream
597f9f848faSopenharmony_ci#error "fts_get_stream not defined"
598f9f848faSopenharmony_ci#endif
599f9f848faSopenharmony_ci
600f9f848faSopenharmony_ciFTS *
601f9f848faSopenharmony_ci(fts_get_stream)(FTSENT *p)
602f9f848faSopenharmony_ci{
603f9f848faSopenharmony_ci	return (fts_get_stream(p));
604f9f848faSopenharmony_ci}
605f9f848faSopenharmony_ci
606f9f848faSopenharmony_civoid
607f9f848faSopenharmony_cifts_set_clientptr(FTS *sp, void *clientptr)
608f9f848faSopenharmony_ci{
609f9f848faSopenharmony_ci
610f9f848faSopenharmony_ci	sp->fts_clientptr = clientptr;
611f9f848faSopenharmony_ci}
612f9f848faSopenharmony_ci
613f9f848faSopenharmony_ci/*
614f9f848faSopenharmony_ci * This is the tricky part -- do not casually change *anything* in here.  The
615f9f848faSopenharmony_ci * idea is to build the linked list of entries that are used by fts_children
616f9f848faSopenharmony_ci * and fts_read.  There are lots of special cases.
617f9f848faSopenharmony_ci *
618f9f848faSopenharmony_ci * The real slowdown in walking the tree is the stat calls.  If FTS_NOSTAT is
619f9f848faSopenharmony_ci * set and it's a physical walk (so that symbolic links can't be directories),
620f9f848faSopenharmony_ci * we can do things quickly.  First, if it's a 4.4BSD file system, the type
621f9f848faSopenharmony_ci * of the file is in the directory entry.  Otherwise, we assume that the number
622f9f848faSopenharmony_ci * of subdirectories in a node is equal to the number of links to the parent.
623f9f848faSopenharmony_ci * The former skips all stat calls.  The latter skips stat calls in any leaf
624f9f848faSopenharmony_ci * directories and for any files after the subdirectories in the directory have
625f9f848faSopenharmony_ci * been found, cutting the stat calls by about 2/3.
626f9f848faSopenharmony_ci */
627f9f848faSopenharmony_cistatic FTSENT *
628f9f848faSopenharmony_cifts_build(FTS *sp, int type)
629f9f848faSopenharmony_ci{
630f9f848faSopenharmony_ci	struct dirent *dp;
631f9f848faSopenharmony_ci	FTSENT *p, *head;
632f9f848faSopenharmony_ci	FTSENT *cur, *tail;
633f9f848faSopenharmony_ci	DIR *dirp;
634f9f848faSopenharmony_ci	void *oldaddr;
635f9f848faSopenharmony_ci	char *cp;
636f9f848faSopenharmony_ci	int cderrno, descend, oflag, saved_errno, nostat, doadjust;
637f9f848faSopenharmony_ci	long level;
638f9f848faSopenharmony_ci	long nlinks;	/* has to be signed because -1 is a magic value */
639f9f848faSopenharmony_ci	size_t dnamlen, len, maxlen, nitems;
640f9f848faSopenharmony_ci
641f9f848faSopenharmony_ci	/* Set current node pointer. */
642f9f848faSopenharmony_ci	cur = sp->fts_cur;
643f9f848faSopenharmony_ci
644f9f848faSopenharmony_ci	/*
645f9f848faSopenharmony_ci	 * Open the directory for reading.  If this fails, we're done.
646f9f848faSopenharmony_ci	 * If being called from fts_read, set the fts_info field.
647f9f848faSopenharmony_ci	 */
648f9f848faSopenharmony_ci#ifdef FTS_WHITEOUT
649f9f848faSopenharmony_ci	if (ISSET(FTS_WHITEOUT))
650f9f848faSopenharmony_ci		oflag = DTF_NODUP;
651f9f848faSopenharmony_ci	else
652f9f848faSopenharmony_ci		oflag = DTF_HIDEW | DTF_NODUP;
653f9f848faSopenharmony_ci#else
654f9f848faSopenharmony_ci#define __opendir2(path, flag) opendir(path)
655f9f848faSopenharmony_ci#endif
656f9f848faSopenharmony_ci	if ((dirp = __opendir2(cur->fts_accpath, oflag)) == NULL) {
657f9f848faSopenharmony_ci		if (type == BREAD) {
658f9f848faSopenharmony_ci			cur->fts_info = FTS_DNR;
659f9f848faSopenharmony_ci			cur->fts_errno = errno;
660f9f848faSopenharmony_ci		}
661f9f848faSopenharmony_ci		return (NULL);
662f9f848faSopenharmony_ci	}
663f9f848faSopenharmony_ci
664f9f848faSopenharmony_ci	/*
665f9f848faSopenharmony_ci	 * Nlinks is the number of possible entries of type directory in the
666f9f848faSopenharmony_ci	 * directory if we're cheating on stat calls, 0 if we're not doing
667f9f848faSopenharmony_ci	 * any stat calls at all, -1 if we're doing stats on everything.
668f9f848faSopenharmony_ci	 */
669f9f848faSopenharmony_ci	if (type == BNAMES) {
670f9f848faSopenharmony_ci		nlinks = 0;
671f9f848faSopenharmony_ci		/* Be quiet about nostat, GCC. */
672f9f848faSopenharmony_ci		nostat = 0;
673f9f848faSopenharmony_ci	} else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) {
674f9f848faSopenharmony_ci		if (fts_ufslinks(sp, cur))
675f9f848faSopenharmony_ci			nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2);
676f9f848faSopenharmony_ci		else
677f9f848faSopenharmony_ci			nlinks = -1;
678f9f848faSopenharmony_ci		nostat = 1;
679f9f848faSopenharmony_ci	} else {
680f9f848faSopenharmony_ci		nlinks = -1;
681f9f848faSopenharmony_ci		nostat = 0;
682f9f848faSopenharmony_ci	}
683f9f848faSopenharmony_ci
684f9f848faSopenharmony_ci#ifdef notdef
685f9f848faSopenharmony_ci	(void)printf("nlinks == %d (cur: %d)\n", nlinks, cur->fts_nlink);
686f9f848faSopenharmony_ci	(void)printf("NOSTAT %d PHYSICAL %d SEEDOT %d\n",
687f9f848faSopenharmony_ci	    ISSET(FTS_NOSTAT), ISSET(FTS_PHYSICAL), ISSET(FTS_SEEDOT));
688f9f848faSopenharmony_ci#endif
689f9f848faSopenharmony_ci	/*
690f9f848faSopenharmony_ci	 * If we're going to need to stat anything or we want to descend
691f9f848faSopenharmony_ci	 * and stay in the directory, chdir.  If this fails we keep going,
692f9f848faSopenharmony_ci	 * but set a flag so we don't chdir after the post-order visit.
693f9f848faSopenharmony_ci	 * We won't be able to stat anything, but we can still return the
694f9f848faSopenharmony_ci	 * names themselves.  Note, that since fts_read won't be able to
695f9f848faSopenharmony_ci	 * chdir into the directory, it will have to return different path
696f9f848faSopenharmony_ci	 * names than before, i.e. "a/b" instead of "b".  Since the node
697f9f848faSopenharmony_ci	 * has already been visited in pre-order, have to wait until the
698f9f848faSopenharmony_ci	 * post-order visit to return the error.  There is a special case
699f9f848faSopenharmony_ci	 * here, if there was nothing to stat then it's not an error to
700f9f848faSopenharmony_ci	 * not be able to stat.  This is all fairly nasty.  If a program
701f9f848faSopenharmony_ci	 * needed sorted entries or stat information, they had better be
702f9f848faSopenharmony_ci	 * checking FTS_NS on the returned nodes.
703f9f848faSopenharmony_ci	 */
704f9f848faSopenharmony_ci	cderrno = 0;
705f9f848faSopenharmony_ci	if (nlinks || type == BREAD) {
706f9f848faSopenharmony_ci		if (fts_safe_changedir(sp, cur, _dirfd(dirp), NULL)) {
707f9f848faSopenharmony_ci			if (nlinks && type == BREAD)
708f9f848faSopenharmony_ci				cur->fts_errno = errno;
709f9f848faSopenharmony_ci			cur->fts_flags |= FTS_DONTCHDIR;
710f9f848faSopenharmony_ci			descend = 0;
711f9f848faSopenharmony_ci			cderrno = errno;
712f9f848faSopenharmony_ci		} else
713f9f848faSopenharmony_ci			descend = 1;
714f9f848faSopenharmony_ci	} else
715f9f848faSopenharmony_ci		descend = 0;
716f9f848faSopenharmony_ci
717f9f848faSopenharmony_ci	/*
718f9f848faSopenharmony_ci	 * Figure out the max file name length that can be stored in the
719f9f848faSopenharmony_ci	 * current path -- the inner loop allocates more path as necessary.
720f9f848faSopenharmony_ci	 * We really wouldn't have to do the maxlen calculations here, we
721f9f848faSopenharmony_ci	 * could do them in fts_read before returning the path, but it's a
722f9f848faSopenharmony_ci	 * lot easier here since the length is part of the dirent structure.
723f9f848faSopenharmony_ci	 *
724f9f848faSopenharmony_ci	 * If not changing directories set a pointer so that can just append
725f9f848faSopenharmony_ci	 * each new name into the path.
726f9f848faSopenharmony_ci	 */
727f9f848faSopenharmony_ci	len = NAPPEND(cur);
728f9f848faSopenharmony_ci	if (ISSET(FTS_NOCHDIR)) {
729f9f848faSopenharmony_ci		cp = sp->fts_path + len;
730f9f848faSopenharmony_ci		*cp++ = '/';
731f9f848faSopenharmony_ci	} else {
732f9f848faSopenharmony_ci		/* GCC, you're too verbose. */
733f9f848faSopenharmony_ci		cp = NULL;
734f9f848faSopenharmony_ci	}
735f9f848faSopenharmony_ci	len++;
736f9f848faSopenharmony_ci	maxlen = sp->fts_pathlen - len;
737f9f848faSopenharmony_ci
738f9f848faSopenharmony_ci	level = cur->fts_level + 1;
739f9f848faSopenharmony_ci
740f9f848faSopenharmony_ci	/* Read the directory, attaching each entry to the `link' pointer. */
741f9f848faSopenharmony_ci	doadjust = 0;
742f9f848faSopenharmony_ci	for (head = tail = NULL, nitems = 0; dirp && (dp = readdir(dirp));) {
743f9f848faSopenharmony_ci		dnamlen = strlen(dp->d_name);
744f9f848faSopenharmony_ci		if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))
745f9f848faSopenharmony_ci			continue;
746f9f848faSopenharmony_ci
747f9f848faSopenharmony_ci		if ((p = fts_alloc(sp, dp->d_name, dnamlen)) == NULL)
748f9f848faSopenharmony_ci			goto mem1;
749f9f848faSopenharmony_ci		if (dnamlen >= maxlen) {	/* include space for NUL */
750f9f848faSopenharmony_ci			oldaddr = sp->fts_path;
751f9f848faSopenharmony_ci			if (fts_palloc(sp, dnamlen + len + 1)) {
752f9f848faSopenharmony_ci				/*
753f9f848faSopenharmony_ci				 * No more memory for path or structures.  Save
754f9f848faSopenharmony_ci				 * errno, free up the current structure and the
755f9f848faSopenharmony_ci				 * structures already allocated.
756f9f848faSopenharmony_ci				 */
757f9f848faSopenharmony_cimem1:				saved_errno = errno;
758f9f848faSopenharmony_ci				if (p)
759f9f848faSopenharmony_ci					free(p);
760f9f848faSopenharmony_ci				fts_lfree(head);
761f9f848faSopenharmony_ci				(void)closedir(dirp);
762f9f848faSopenharmony_ci				cur->fts_info = FTS_ERR;
763f9f848faSopenharmony_ci				SET(FTS_STOP);
764f9f848faSopenharmony_ci				errno = saved_errno;
765f9f848faSopenharmony_ci				return (NULL);
766f9f848faSopenharmony_ci			}
767f9f848faSopenharmony_ci			/* Did realloc() change the pointer? */
768f9f848faSopenharmony_ci			if (oldaddr != sp->fts_path) {
769f9f848faSopenharmony_ci				doadjust = 1;
770f9f848faSopenharmony_ci				if (ISSET(FTS_NOCHDIR))
771f9f848faSopenharmony_ci					cp = sp->fts_path + len;
772f9f848faSopenharmony_ci			}
773f9f848faSopenharmony_ci			maxlen = sp->fts_pathlen - len;
774f9f848faSopenharmony_ci		}
775f9f848faSopenharmony_ci
776f9f848faSopenharmony_ci		p->fts_level = level;
777f9f848faSopenharmony_ci		p->fts_parent = sp->fts_cur;
778f9f848faSopenharmony_ci		p->fts_pathlen = len + dnamlen;
779f9f848faSopenharmony_ci
780f9f848faSopenharmony_ci#ifdef FTS_WHITEOUT
781f9f848faSopenharmony_ci		if (dp->d_type == DT_WHT)
782f9f848faSopenharmony_ci			p->fts_flags |= FTS_ISW;
783f9f848faSopenharmony_ci#endif
784f9f848faSopenharmony_ci
785f9f848faSopenharmony_ci		if (cderrno) {
786f9f848faSopenharmony_ci			if (nlinks) {
787f9f848faSopenharmony_ci				p->fts_info = FTS_NS;
788f9f848faSopenharmony_ci				p->fts_errno = cderrno;
789f9f848faSopenharmony_ci			} else
790f9f848faSopenharmony_ci				p->fts_info = FTS_NSOK;
791f9f848faSopenharmony_ci			p->fts_accpath = cur->fts_accpath;
792f9f848faSopenharmony_ci		} else if (nlinks == 0
793f9f848faSopenharmony_ci#ifdef DT_DIR
794f9f848faSopenharmony_ci		    || (nostat &&
795f9f848faSopenharmony_ci		    dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN)
796f9f848faSopenharmony_ci#endif
797f9f848faSopenharmony_ci		    ) {
798f9f848faSopenharmony_ci			p->fts_accpath =
799f9f848faSopenharmony_ci			    ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name;
800f9f848faSopenharmony_ci			p->fts_info = FTS_NSOK;
801f9f848faSopenharmony_ci		} else {
802f9f848faSopenharmony_ci			/* Build a file name for fts_stat to stat. */
803f9f848faSopenharmony_ci			if (ISSET(FTS_NOCHDIR)) {
804f9f848faSopenharmony_ci				p->fts_accpath = p->fts_path;
805f9f848faSopenharmony_ci				memmove(cp, p->fts_name, p->fts_namelen + 1);
806f9f848faSopenharmony_ci				p->fts_info = fts_stat(sp, p, 0, _dirfd(dirp));
807f9f848faSopenharmony_ci			} else {
808f9f848faSopenharmony_ci				p->fts_accpath = p->fts_name;
809f9f848faSopenharmony_ci				p->fts_info = fts_stat(sp, p, 0, -1);
810f9f848faSopenharmony_ci			}
811f9f848faSopenharmony_ci
812f9f848faSopenharmony_ci			/* Decrement link count if applicable. */
813f9f848faSopenharmony_ci			if (nlinks > 0 && (p->fts_info == FTS_D ||
814f9f848faSopenharmony_ci			    p->fts_info == FTS_DC || p->fts_info == FTS_DOT))
815f9f848faSopenharmony_ci				--nlinks;
816f9f848faSopenharmony_ci		}
817f9f848faSopenharmony_ci
818f9f848faSopenharmony_ci		/* We walk in directory order so "ls -f" doesn't get upset. */
819f9f848faSopenharmony_ci		p->fts_link = NULL;
820f9f848faSopenharmony_ci		if (head == NULL)
821f9f848faSopenharmony_ci			head = tail = p;
822f9f848faSopenharmony_ci		else {
823f9f848faSopenharmony_ci			tail->fts_link = p;
824f9f848faSopenharmony_ci			tail = p;
825f9f848faSopenharmony_ci		}
826f9f848faSopenharmony_ci		++nitems;
827f9f848faSopenharmony_ci	}
828f9f848faSopenharmony_ci	if (dirp)
829f9f848faSopenharmony_ci		(void)closedir(dirp);
830f9f848faSopenharmony_ci
831f9f848faSopenharmony_ci	/*
832f9f848faSopenharmony_ci	 * If realloc() changed the address of the path, adjust the
833f9f848faSopenharmony_ci	 * addresses for the rest of the tree and the dir list.
834f9f848faSopenharmony_ci	 */
835f9f848faSopenharmony_ci	if (doadjust)
836f9f848faSopenharmony_ci		fts_padjust(sp, head);
837f9f848faSopenharmony_ci
838f9f848faSopenharmony_ci	/*
839f9f848faSopenharmony_ci	 * If not changing directories, reset the path back to original
840f9f848faSopenharmony_ci	 * state.
841f9f848faSopenharmony_ci	 */
842f9f848faSopenharmony_ci	if (ISSET(FTS_NOCHDIR))
843f9f848faSopenharmony_ci		sp->fts_path[cur->fts_pathlen] = '\0';
844f9f848faSopenharmony_ci
845f9f848faSopenharmony_ci	/*
846f9f848faSopenharmony_ci	 * If descended after called from fts_children or after called from
847f9f848faSopenharmony_ci	 * fts_read and nothing found, get back.  At the root level we use
848f9f848faSopenharmony_ci	 * the saved fd; if one of fts_open()'s arguments is a relative path
849f9f848faSopenharmony_ci	 * to an empty directory, we wind up here with no other way back.  If
850f9f848faSopenharmony_ci	 * can't get back, we're done.
851f9f848faSopenharmony_ci	 */
852f9f848faSopenharmony_ci	if (descend && (type == BCHILD || !nitems) &&
853f9f848faSopenharmony_ci	    (cur->fts_level == FTS_ROOTLEVEL ?
854f9f848faSopenharmony_ci	    FCHDIR(sp, sp->fts_rfd) :
855f9f848faSopenharmony_ci	    fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) {
856f9f848faSopenharmony_ci		fts_lfree(head);
857f9f848faSopenharmony_ci		cur->fts_info = FTS_ERR;
858f9f848faSopenharmony_ci		SET(FTS_STOP);
859f9f848faSopenharmony_ci		return (NULL);
860f9f848faSopenharmony_ci	}
861f9f848faSopenharmony_ci
862f9f848faSopenharmony_ci	/* If didn't find anything, return NULL. */
863f9f848faSopenharmony_ci	if (!nitems) {
864f9f848faSopenharmony_ci		if (type == BREAD)
865f9f848faSopenharmony_ci			cur->fts_info = FTS_DP;
866f9f848faSopenharmony_ci		return (NULL);
867f9f848faSopenharmony_ci	}
868f9f848faSopenharmony_ci
869f9f848faSopenharmony_ci	/* Sort the entries. */
870f9f848faSopenharmony_ci	if (sp->fts_compar && nitems > 1)
871f9f848faSopenharmony_ci		head = fts_sort(sp, head, nitems);
872f9f848faSopenharmony_ci	return (head);
873f9f848faSopenharmony_ci}
874f9f848faSopenharmony_ci
875f9f848faSopenharmony_cistatic int
876f9f848faSopenharmony_cifts_stat(FTS *sp, FTSENT *p, int follow, int dfd)
877f9f848faSopenharmony_ci{
878f9f848faSopenharmony_ci	FTSENT *t;
879f9f848faSopenharmony_ci	dev_t dev;
880f9f848faSopenharmony_ci	ino_t ino;
881f9f848faSopenharmony_ci	struct stat *sbp, sb;
882f9f848faSopenharmony_ci	int saved_errno;
883f9f848faSopenharmony_ci	const char *path;
884f9f848faSopenharmony_ci
885f9f848faSopenharmony_ci	if (dfd == -1)
886f9f848faSopenharmony_ci		path = p->fts_accpath, dfd = AT_FDCWD;
887f9f848faSopenharmony_ci	else
888f9f848faSopenharmony_ci		path = p->fts_name;
889f9f848faSopenharmony_ci
890f9f848faSopenharmony_ci	/* If user needs stat info, stat buffer already allocated. */
891f9f848faSopenharmony_ci	sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp;
892f9f848faSopenharmony_ci
893f9f848faSopenharmony_ci#ifdef FTS_WHITEOUT
894f9f848faSopenharmony_ci	/* Check for whiteout. */
895f9f848faSopenharmony_ci	if (p->fts_flags & FTS_ISW) {
896f9f848faSopenharmony_ci		if (sbp != &sb) {
897f9f848faSopenharmony_ci			memset(sbp, '\0', sizeof(*sbp));
898f9f848faSopenharmony_ci			sbp->st_mode = S_IFWHT;
899f9f848faSopenharmony_ci		}
900f9f848faSopenharmony_ci		return (FTS_W);
901f9f848faSopenharmony_ci	}
902f9f848faSopenharmony_ci#endif
903f9f848faSopenharmony_ci
904f9f848faSopenharmony_ci	/*
905f9f848faSopenharmony_ci	 * If doing a logical walk, or application requested FTS_FOLLOW, do
906f9f848faSopenharmony_ci	 * a stat(2).  If that fails, check for a non-existent symlink.  If
907f9f848faSopenharmony_ci	 * fail, set the errno from the stat call.
908f9f848faSopenharmony_ci	 */
909f9f848faSopenharmony_ci	if (ISSET(FTS_LOGICAL) || follow) {
910f9f848faSopenharmony_ci		if (fstatat(dfd, path, sbp, 0)) {
911f9f848faSopenharmony_ci			saved_errno = errno;
912f9f848faSopenharmony_ci			if (fstatat(dfd, path, sbp, AT_SYMLINK_NOFOLLOW)) {
913f9f848faSopenharmony_ci				p->fts_errno = saved_errno;
914f9f848faSopenharmony_ci				goto err;
915f9f848faSopenharmony_ci			}
916f9f848faSopenharmony_ci			errno = 0;
917f9f848faSopenharmony_ci			if (S_ISLNK(sbp->st_mode))
918f9f848faSopenharmony_ci				return (FTS_SLNONE);
919f9f848faSopenharmony_ci		}
920f9f848faSopenharmony_ci	} else if (fstatat(dfd, path, sbp, AT_SYMLINK_NOFOLLOW)) {
921f9f848faSopenharmony_ci		p->fts_errno = errno;
922f9f848faSopenharmony_cierr:		memset(sbp, 0, sizeof(struct stat));
923f9f848faSopenharmony_ci		return (FTS_NS);
924f9f848faSopenharmony_ci	}
925f9f848faSopenharmony_ci
926f9f848faSopenharmony_ci	if (S_ISDIR(sbp->st_mode)) {
927f9f848faSopenharmony_ci		/*
928f9f848faSopenharmony_ci		 * Set the device/inode.  Used to find cycles and check for
929f9f848faSopenharmony_ci		 * crossing mount points.  Also remember the link count, used
930f9f848faSopenharmony_ci		 * in fts_build to limit the number of stat calls.  It is
931f9f848faSopenharmony_ci		 * understood that these fields are only referenced if fts_info
932f9f848faSopenharmony_ci		 * is set to FTS_D.
933f9f848faSopenharmony_ci		 */
934f9f848faSopenharmony_ci		dev = p->fts_dev = sbp->st_dev;
935f9f848faSopenharmony_ci		ino = p->fts_ino = sbp->st_ino;
936f9f848faSopenharmony_ci		p->fts_nlink = sbp->st_nlink;
937f9f848faSopenharmony_ci
938f9f848faSopenharmony_ci		if (ISDOT(p->fts_name))
939f9f848faSopenharmony_ci			return (FTS_DOT);
940f9f848faSopenharmony_ci
941f9f848faSopenharmony_ci		/*
942f9f848faSopenharmony_ci		 * Cycle detection is done by brute force when the directory
943f9f848faSopenharmony_ci		 * is first encountered.  If the tree gets deep enough or the
944f9f848faSopenharmony_ci		 * number of symbolic links to directories is high enough,
945f9f848faSopenharmony_ci		 * something faster might be worthwhile.
946f9f848faSopenharmony_ci		 */
947f9f848faSopenharmony_ci		for (t = p->fts_parent;
948f9f848faSopenharmony_ci		    t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent)
949f9f848faSopenharmony_ci			if (ino == t->fts_ino && dev == t->fts_dev) {
950f9f848faSopenharmony_ci				p->fts_cycle = t;
951f9f848faSopenharmony_ci				return (FTS_DC);
952f9f848faSopenharmony_ci			}
953f9f848faSopenharmony_ci		return (FTS_D);
954f9f848faSopenharmony_ci	}
955f9f848faSopenharmony_ci	if (S_ISLNK(sbp->st_mode))
956f9f848faSopenharmony_ci		return (FTS_SL);
957f9f848faSopenharmony_ci	if (S_ISREG(sbp->st_mode))
958f9f848faSopenharmony_ci		return (FTS_F);
959f9f848faSopenharmony_ci	return (FTS_DEFAULT);
960f9f848faSopenharmony_ci}
961f9f848faSopenharmony_ci
962f9f848faSopenharmony_ci/*
963f9f848faSopenharmony_ci * The comparison function takes pointers to pointers to FTSENT structures.
964f9f848faSopenharmony_ci * Qsort wants a comparison function that takes pointers to void.
965f9f848faSopenharmony_ci * (Both with appropriate levels of const-poisoning, of course!)
966f9f848faSopenharmony_ci * Use a trampoline function to deal with the difference.
967f9f848faSopenharmony_ci */
968f9f848faSopenharmony_cistatic int
969f9f848faSopenharmony_cifts_compar(const void *a, const void *b)
970f9f848faSopenharmony_ci{
971f9f848faSopenharmony_ci	FTS *parent;
972f9f848faSopenharmony_ci
973f9f848faSopenharmony_ci	parent = (*(const FTSENT * const *)a)->fts_fts;
974f9f848faSopenharmony_ci	return (*parent->fts_compar)(a, b);
975f9f848faSopenharmony_ci}
976f9f848faSopenharmony_ci
977f9f848faSopenharmony_cistatic FTSENT *
978f9f848faSopenharmony_cifts_sort(FTS *sp, FTSENT *head, size_t nitems)
979f9f848faSopenharmony_ci{
980f9f848faSopenharmony_ci	FTSENT **ap, *p;
981f9f848faSopenharmony_ci
982f9f848faSopenharmony_ci	/*
983f9f848faSopenharmony_ci	 * Construct an array of pointers to the structures and call qsort(3).
984f9f848faSopenharmony_ci	 * Reassemble the array in the order returned by qsort.  If unable to
985f9f848faSopenharmony_ci	 * sort for memory reasons, return the directory entries in their
986f9f848faSopenharmony_ci	 * current order.  Allocate enough space for the current needs plus
987f9f848faSopenharmony_ci	 * 40 so don't realloc one entry at a time.
988f9f848faSopenharmony_ci	 */
989f9f848faSopenharmony_ci	if (nitems > sp->fts_nitems) {
990f9f848faSopenharmony_ci		sp->fts_nitems = nitems + 40;
991f9f848faSopenharmony_ci		if ((sp->fts_array = reallocf(sp->fts_array,
992f9f848faSopenharmony_ci		    sp->fts_nitems * sizeof(FTSENT *))) == NULL) {
993f9f848faSopenharmony_ci			sp->fts_nitems = 0;
994f9f848faSopenharmony_ci			return (head);
995f9f848faSopenharmony_ci		}
996f9f848faSopenharmony_ci	}
997f9f848faSopenharmony_ci	for (ap = sp->fts_array, p = head; p; p = p->fts_link)
998f9f848faSopenharmony_ci		*ap++ = p;
999f9f848faSopenharmony_ci	qsort(sp->fts_array, nitems, sizeof(FTSENT *), fts_compar);
1000f9f848faSopenharmony_ci	for (head = *(ap = sp->fts_array); --nitems; ++ap)
1001f9f848faSopenharmony_ci		ap[0]->fts_link = ap[1];
1002f9f848faSopenharmony_ci	ap[0]->fts_link = NULL;
1003f9f848faSopenharmony_ci	return (head);
1004f9f848faSopenharmony_ci}
1005f9f848faSopenharmony_ci
1006f9f848faSopenharmony_cistatic FTSENT *
1007f9f848faSopenharmony_cifts_alloc(FTS *sp, char *name, size_t namelen)
1008f9f848faSopenharmony_ci{
1009f9f848faSopenharmony_ci	FTSENT *p;
1010f9f848faSopenharmony_ci	size_t len;
1011f9f848faSopenharmony_ci
1012f9f848faSopenharmony_ci	struct ftsent_withstat {
1013f9f848faSopenharmony_ci		FTSENT	ent;
1014f9f848faSopenharmony_ci		struct	stat statbuf;
1015f9f848faSopenharmony_ci	};
1016f9f848faSopenharmony_ci
1017f9f848faSopenharmony_ci	/*
1018f9f848faSopenharmony_ci	 * The file name is a variable length array and no stat structure is
1019f9f848faSopenharmony_ci	 * necessary if the user has set the nostat bit.  Allocate the FTSENT
1020f9f848faSopenharmony_ci	 * structure, the file name and the stat structure in one chunk, but
1021f9f848faSopenharmony_ci	 * be careful that the stat structure is reasonably aligned.
1022f9f848faSopenharmony_ci	 */
1023f9f848faSopenharmony_ci	if (ISSET(FTS_NOSTAT))
1024f9f848faSopenharmony_ci		len = sizeof(FTSENT) + namelen + 1;
1025f9f848faSopenharmony_ci	else
1026f9f848faSopenharmony_ci		len = sizeof(struct ftsent_withstat) + namelen + 1;
1027f9f848faSopenharmony_ci
1028f9f848faSopenharmony_ci	if ((p = malloc(len)) == NULL)
1029f9f848faSopenharmony_ci		return (NULL);
1030f9f848faSopenharmony_ci
1031f9f848faSopenharmony_ci	if (ISSET(FTS_NOSTAT)) {
1032f9f848faSopenharmony_ci		p->fts_name = (char *)(p + 1);
1033f9f848faSopenharmony_ci		p->fts_statp = NULL;
1034f9f848faSopenharmony_ci	} else {
1035f9f848faSopenharmony_ci		p->fts_name = (char *)((struct ftsent_withstat *)p + 1);
1036f9f848faSopenharmony_ci		p->fts_statp = &((struct ftsent_withstat *)p)->statbuf;
1037f9f848faSopenharmony_ci	}
1038f9f848faSopenharmony_ci
1039f9f848faSopenharmony_ci	/* Copy the name and guarantee NUL termination. */
1040f9f848faSopenharmony_ci	memcpy(p->fts_name, name, namelen);
1041f9f848faSopenharmony_ci	p->fts_name[namelen] = '\0';
1042f9f848faSopenharmony_ci	p->fts_namelen = namelen;
1043f9f848faSopenharmony_ci	p->fts_path = sp->fts_path;
1044f9f848faSopenharmony_ci	p->fts_errno = 0;
1045f9f848faSopenharmony_ci	p->fts_flags = 0;
1046f9f848faSopenharmony_ci	p->fts_instr = FTS_NOINSTR;
1047f9f848faSopenharmony_ci	p->fts_number = 0;
1048f9f848faSopenharmony_ci	p->fts_pointer = NULL;
1049f9f848faSopenharmony_ci	p->fts_fts = sp;
1050f9f848faSopenharmony_ci	return (p);
1051f9f848faSopenharmony_ci}
1052f9f848faSopenharmony_ci
1053f9f848faSopenharmony_cistatic void
1054f9f848faSopenharmony_cifts_lfree(FTSENT *head)
1055f9f848faSopenharmony_ci{
1056f9f848faSopenharmony_ci	FTSENT *p;
1057f9f848faSopenharmony_ci
1058f9f848faSopenharmony_ci	/* Free a linked list of structures. */
1059f9f848faSopenharmony_ci	while ((p = head)) {
1060f9f848faSopenharmony_ci		head = head->fts_link;
1061f9f848faSopenharmony_ci		free(p);
1062f9f848faSopenharmony_ci	}
1063f9f848faSopenharmony_ci}
1064f9f848faSopenharmony_ci
1065f9f848faSopenharmony_ci/*
1066f9f848faSopenharmony_ci * Allow essentially unlimited paths; find, rm, ls should all work on any tree.
1067f9f848faSopenharmony_ci * Most systems will allow creation of paths much longer than MAXPATHLEN, even
1068f9f848faSopenharmony_ci * though the kernel won't resolve them.  Add the size (not just what's needed)
1069f9f848faSopenharmony_ci * plus 256 bytes so don't realloc the path 2 bytes at a time.
1070f9f848faSopenharmony_ci */
1071f9f848faSopenharmony_cistatic int
1072f9f848faSopenharmony_cifts_palloc(FTS *sp, size_t more)
1073f9f848faSopenharmony_ci{
1074f9f848faSopenharmony_ci
1075f9f848faSopenharmony_ci	sp->fts_pathlen += more + 256;
1076f9f848faSopenharmony_ci	sp->fts_path = reallocf(sp->fts_path, sp->fts_pathlen);
1077f9f848faSopenharmony_ci	return (sp->fts_path == NULL);
1078f9f848faSopenharmony_ci}
1079f9f848faSopenharmony_ci
1080f9f848faSopenharmony_ci/*
1081f9f848faSopenharmony_ci * When the path is realloc'd, have to fix all of the pointers in structures
1082f9f848faSopenharmony_ci * already returned.
1083f9f848faSopenharmony_ci */
1084f9f848faSopenharmony_cistatic void
1085f9f848faSopenharmony_cifts_padjust(FTS *sp, FTSENT *head)
1086f9f848faSopenharmony_ci{
1087f9f848faSopenharmony_ci	FTSENT *p;
1088f9f848faSopenharmony_ci	char *addr = sp->fts_path;
1089f9f848faSopenharmony_ci
1090f9f848faSopenharmony_ci#define	ADJUST(p) do {							\
1091f9f848faSopenharmony_ci	if ((p)->fts_accpath != (p)->fts_name) {			\
1092f9f848faSopenharmony_ci		(p)->fts_accpath =					\
1093f9f848faSopenharmony_ci		    (char *)addr + ((p)->fts_accpath - (p)->fts_path);	\
1094f9f848faSopenharmony_ci	}								\
1095f9f848faSopenharmony_ci	(p)->fts_path = addr;						\
1096f9f848faSopenharmony_ci} while (0)
1097f9f848faSopenharmony_ci	/* Adjust the current set of children. */
1098f9f848faSopenharmony_ci	for (p = sp->fts_child; p; p = p->fts_link)
1099f9f848faSopenharmony_ci		ADJUST(p);
1100f9f848faSopenharmony_ci
1101f9f848faSopenharmony_ci	/* Adjust the rest of the tree, including the current level. */
1102f9f848faSopenharmony_ci	for (p = head; p->fts_level >= FTS_ROOTLEVEL;) {
1103f9f848faSopenharmony_ci		ADJUST(p);
1104f9f848faSopenharmony_ci		p = p->fts_link ? p->fts_link : p->fts_parent;
1105f9f848faSopenharmony_ci	}
1106f9f848faSopenharmony_ci}
1107f9f848faSopenharmony_ci
1108f9f848faSopenharmony_cistatic size_t
1109f9f848faSopenharmony_cifts_maxarglen(char * const *argv)
1110f9f848faSopenharmony_ci{
1111f9f848faSopenharmony_ci	size_t len, max;
1112f9f848faSopenharmony_ci
1113f9f848faSopenharmony_ci	for (max = 0; *argv; ++argv)
1114f9f848faSopenharmony_ci		if ((len = strlen(*argv)) > max)
1115f9f848faSopenharmony_ci			max = len;
1116f9f848faSopenharmony_ci	return (max + 1);
1117f9f848faSopenharmony_ci}
1118f9f848faSopenharmony_ci
1119f9f848faSopenharmony_ci/*
1120f9f848faSopenharmony_ci * Change to dir specified by fd or p->fts_accpath without getting
1121f9f848faSopenharmony_ci * tricked by someone changing the world out from underneath us.
1122f9f848faSopenharmony_ci * Assumes p->fts_dev and p->fts_ino are filled in.
1123f9f848faSopenharmony_ci */
1124f9f848faSopenharmony_cistatic int
1125f9f848faSopenharmony_cifts_safe_changedir(FTS *sp, FTSENT *p, int fd, char *path)
1126f9f848faSopenharmony_ci{
1127f9f848faSopenharmony_ci	int ret, oerrno, newfd;
1128f9f848faSopenharmony_ci	struct stat sb;
1129f9f848faSopenharmony_ci
1130f9f848faSopenharmony_ci	newfd = fd;
1131f9f848faSopenharmony_ci	if (ISSET(FTS_NOCHDIR))
1132f9f848faSopenharmony_ci		return (0);
1133f9f848faSopenharmony_ci	if (fd < 0 && (newfd = _open(path, O_RDONLY | O_DIRECTORY |
1134f9f848faSopenharmony_ci	    O_CLOEXEC, 0)) < 0)
1135f9f848faSopenharmony_ci		return (-1);
1136f9f848faSopenharmony_ci	if (_fstat(newfd, &sb)) {
1137f9f848faSopenharmony_ci		ret = -1;
1138f9f848faSopenharmony_ci		goto bail;
1139f9f848faSopenharmony_ci	}
1140f9f848faSopenharmony_ci	if (p->fts_dev != sb.st_dev || p->fts_ino != sb.st_ino) {
1141f9f848faSopenharmony_ci		errno = ENOENT;		/* disinformation */
1142f9f848faSopenharmony_ci		ret = -1;
1143f9f848faSopenharmony_ci		goto bail;
1144f9f848faSopenharmony_ci	}
1145f9f848faSopenharmony_ci	ret = fchdir(newfd);
1146f9f848faSopenharmony_cibail:
1147f9f848faSopenharmony_ci	oerrno = errno;
1148f9f848faSopenharmony_ci	if (fd < 0)
1149f9f848faSopenharmony_ci		(void)_close(newfd);
1150f9f848faSopenharmony_ci	errno = oerrno;
1151f9f848faSopenharmony_ci	return (ret);
1152f9f848faSopenharmony_ci}
1153f9f848faSopenharmony_ci
1154f9f848faSopenharmony_ci/*
1155f9f848faSopenharmony_ci * Check if the filesystem for "ent" has UFS-style links.
1156f9f848faSopenharmony_ci */
1157f9f848faSopenharmony_cistatic int
1158f9f848faSopenharmony_cifts_ufslinks(FTS *sp, const FTSENT *ent)
1159f9f848faSopenharmony_ci{
1160f9f848faSopenharmony_ci	struct _fts_private *priv;
1161f9f848faSopenharmony_ci	size_t fsidx = 0;
1162f9f848faSopenharmony_ci
1163f9f848faSopenharmony_ci	priv = (struct _fts_private *)sp;
1164f9f848faSopenharmony_ci	/*
1165f9f848faSopenharmony_ci	 * If this node's device is different from the previous, grab
1166f9f848faSopenharmony_ci	 * the filesystem information, and decide on the reliability
1167f9f848faSopenharmony_ci	 * of the link information from this filesystem for stat(2)
1168f9f848faSopenharmony_ci	 * avoidance.
1169f9f848faSopenharmony_ci	 */
1170f9f848faSopenharmony_ci	if (priv->ftsp_dev != ent->fts_dev) {
1171f9f848faSopenharmony_ci		if (statfs(ent->fts_path, &priv->ftsp_statfs) != -1) {
1172f9f848faSopenharmony_ci			priv->ftsp_dev = ent->fts_dev;
1173f9f848faSopenharmony_ci			priv->ftsp_linksreliable = 0;
1174f9f848faSopenharmony_ci			for (fsidx = 0; fsidx < sizeof(ufslike_filesystems) / sizeof(ssize_t);
1175f9f848faSopenharmony_ci				++fsidx) {
1176f9f848faSopenharmony_ci				if ((uint64_t)priv->ftsp_statfs.f_type == ufslike_filesystems[fsidx]) {
1177f9f848faSopenharmony_ci				    priv->ftsp_linksreliable = 1;
1178f9f848faSopenharmony_ci					break;
1179f9f848faSopenharmony_ci				}
1180f9f848faSopenharmony_ci			}
1181f9f848faSopenharmony_ci		} else {
1182f9f848faSopenharmony_ci			priv->ftsp_linksreliable = 0;
1183f9f848faSopenharmony_ci		}
1184f9f848faSopenharmony_ci	}
1185f9f848faSopenharmony_ci	return (priv->ftsp_linksreliable);
1186f9f848faSopenharmony_ci}
1187