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