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