Path: blob/main/core/posix-wasm/src/lib/fts/fts.c
1398 views
/* $NetBSD: fts.c,v 1.48 2015/01/29 15:55:21 manu Exp $ */12/*-3* Copyright (c) 1990, 1993, 19944* The Regents of the University of California. All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11* 2. Redistributions in binary form must reproduce the above copyright12* notice, this list of conditions and the following disclaimer in the13* documentation and/or other materials provided with the distribution.14* 3. Neither the name of the University nor the names of its contributors15* may be used to endorse or promote products derived from this software16* without specific prior written permission.17*18* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND19* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE20* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE21* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE22* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL23* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS24* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)25* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT26* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY27* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF28* SUCH DAMAGE.29*/3031#if defined(LIBC_SCCS) && !defined(lint)32#if 033static char sccsid[] = "@(#)fts.c 8.6 (Berkeley) 8/14/94";34#else35__RCSID("$NetBSD: fts.c,v 1.48 2015/01/29 15:55:21 manu Exp $");36#endif37#endif /* LIBC_SCCS and not lint */3839#include "config.h"4041#include <sys/param.h>42#include <sys/stat.h>4344#include <assert.h>45#define _DIAGASSERT(e)46#include <dirent.h>47#include <errno.h>48#include <fcntl.h>49#include "fts.h"50#include <stdlib.h>51#include <string.h>52#include <unistd.h>53#include <stdio.h>5455#if !defined(HAVE_DECL_MAX) || (HAVE_DECL_MAX == 0)56#define MAX(a, b) ((a) > (b) ? (a) : (b))57#endif5859#if !defined(UINT_MAX) && (HAVE_DECL_UINTMAX_MAX == 1)60#define UINT_MAX UINTMAX_MAX61#endif6263#if !defined(HAVE_DIRFD)64#if defined(HAVE_DIR_DD_FD)65#define dirfd(dirp) ((dirp)->dd_fd)66#endif67#if defined(HAVE_DIR_D_FD)68#define dirfd(dirp) ((dirp)->d_fd)69#endif70#endif7172static FTSENT *fts_alloc(FTS *, const char *, size_t);73static FTSENT *fts_build(FTS *, int);74static void fts_free(FTSENT *);75static void fts_lfree(FTSENT *);76static void fts_load(FTS *, FTSENT *);77static size_t fts_maxarglen(char *const *);78static size_t fts_pow2(size_t);79static int fts_palloc(FTS *, size_t);80static void fts_padjust(FTS *, FTSENT *);81static FTSENT *fts_sort(FTS *, FTSENT *, size_t);82static unsigned short fts_stat(FTS *, FTSENT *, int);83static int fts_safe_changedir(const FTS *, const FTSENT *, int, const char *);8485#if defined(ALIGNBYTES) && defined(ALIGN)86#define FTS_ALLOC_ALIGNED 187#else88#undef FTS_ALLOC_ALIGNED89#endif9091#ifndef ftsent_namelen_truncate92#define ftsent_namelen_truncate(a) \93((a) > UINT_MAX ? UINT_MAX : (unsigned int)(a))94#endif95#ifndef ftsent_pathlen_truncate96#define ftsent_pathlen_truncate(a) \97((a) > UINT_MAX ? UINT_MAX : (unsigned int)(a))98#endif99#ifndef fts_pathlen_truncate100#define fts_pathlen_truncate(a) ((a) > UINT_MAX ? UINT_MAX : (unsigned int)(a))101#endif102#ifndef fts_nitems_truncate103#define fts_nitems_truncate(a) ((a) > UINT_MAX ? UINT_MAX : (unsigned int)(a))104#endif105106#define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))107108#define CLR(opt) (sp->fts_options &= ~(opt))109#define ISSET(opt) (sp->fts_options & (opt))110#define SET(opt) (sp->fts_options |= (opt))111112#define CHDIR(sp, path) (!ISSET(FTS_NOCHDIR) && chdir(path))113#define FCHDIR(sp, fd) (!ISSET(FTS_NOCHDIR) && fchdir(fd))114115/* fts_build flags */116#define BCHILD 1 /* fts_children */117#define BNAMES 2 /* fts_children, names only */118#define BREAD 3 /* fts_read */119120#ifndef DTF_HIDEW121#undef FTS_WHITEOUT122#endif123124FTS *fts_open(char *const *argv, int options,125int (*compar)(const FTSENT **, const FTSENT **)) {126FTS *sp;127FTSENT *p, *root;128size_t nitems;129FTSENT *parent, *tmp = NULL; /* pacify gcc */130size_t len;131132_DIAGASSERT(argv != NULL);133134/* Options check. */135if (options & ~FTS_OPTIONMASK) {136errno = EINVAL;137return (NULL);138}139140/* Allocate/initialize the stream */141if ((sp = malloc(sizeof(FTS))) == NULL) return (NULL);142memset(sp, 0, sizeof(FTS));143sp->fts_compar = compar;144sp->fts_options = options;145146/* Logical walks turn on NOCHDIR; symbolic links are too hard. */147if (ISSET(FTS_LOGICAL)) SET(FTS_NOCHDIR);148149/*150* Start out with 1K of path space, and enough, in any case,151* to hold the user's paths.152*/153if (fts_palloc(sp, MAX(fts_maxarglen(argv), MAXPATHLEN))) goto mem1;154155/* Allocate/initialize root's parent. */156if ((parent = fts_alloc(sp, "", 0)) == NULL) goto mem2;157parent->fts_level = FTS_ROOTPARENTLEVEL;158159/* Allocate/initialize root(s). */160for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) {161/* Don't allow zero-length paths. */162if ((len = strlen(*argv)) == 0) {163errno = ENOENT;164goto mem3;165}166167if ((p = fts_alloc(sp, *argv, len)) == NULL) goto mem3;168p->fts_level = FTS_ROOTLEVEL;169p->fts_parent = parent;170p->fts_accpath = p->fts_name;171p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW));172173/* Command-line "." and ".." are real directories. */174if (p->fts_info == FTS_DOT) p->fts_info = FTS_D;175176/*177* If comparison routine supplied, traverse in sorted178* order; otherwise traverse in the order specified.179*/180if (compar) {181p->fts_link = root;182root = p;183} else {184p->fts_link = NULL;185if (root == NULL)186tmp = root = p;187else {188tmp->fts_link = p;189tmp = p;190}191}192}193if (compar && nitems > 1) root = fts_sort(sp, root, nitems);194195/*196* Allocate a dummy pointer and make fts_read think that we've just197* finished the node before the root(s); set p->fts_info to FTS_INIT198* so that everything about the "current" node is ignored.199*/200if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL) goto mem3;201sp->fts_cur->fts_link = root;202sp->fts_cur->fts_info = FTS_INIT;203204/*205* If using chdir(2), grab a file descriptor pointing to dot to ensure206* that we can get back here; this could be avoided for some paths,207* but almost certainly not worth the effort. Slashes, symbolic links,208* and ".." are all fairly nasty problems. Note, if we can't get the209* descriptor we run anyway, just more slowly.210*/211#ifndef O_CLOEXEC212#define O_CLOEXEC 0213#endif214if (!ISSET(FTS_NOCHDIR)) {215if ((sp->fts_rfd = open(".", O_RDONLY | O_CLOEXEC, 0)) == -1)216SET(FTS_NOCHDIR);217}218219if (nitems == 0) fts_free(parent);220221return (sp);222223mem3:224fts_lfree(root);225fts_free(parent);226mem2:227free(sp->fts_path);228mem1:229free(sp);230return (NULL);231}232233static void fts_load(FTS *sp, FTSENT *p) {234size_t len;235char *cp;236237_DIAGASSERT(sp != NULL);238_DIAGASSERT(p != NULL);239240/*241* Load the stream structure for the next traversal. Since we don't242* actually enter the directory until after the preorder visit, set243* the fts_accpath field specially so the chdir gets done to the right244* place and the user can access the first node. From fts_open it's245* known that the path will fit.246*/247len = p->fts_pathlen = p->fts_namelen;248memmove(sp->fts_path, p->fts_name, len + 1);249if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) {250len = strlen(++cp);251memmove(p->fts_name, cp, len + 1);252p->fts_namelen = ftsent_namelen_truncate(len);253}254p->fts_accpath = p->fts_path = sp->fts_path;255sp->fts_dev = p->fts_dev;256}257258int fts_close(FTS *sp) {259FTSENT *freep, *p;260int saved_errno = 0;261262_DIAGASSERT(sp != NULL);263264/*265* This still works if we haven't read anything -- the dummy structure266* points to the root list, so we step through to the end of the root267* list which has a valid parent pointer.268*/269if (sp->fts_cur) {270if (sp->fts_cur->fts_flags & FTS_SYMFOLLOW)271(void)close(sp->fts_cur->fts_symfd);272for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {273freep = p;274p = p->fts_link ? p->fts_link : p->fts_parent;275fts_free(freep);276}277fts_free(p);278}279280/* Free up child linked list, sort array, path buffer. */281if (sp->fts_child) fts_lfree(sp->fts_child);282if (sp->fts_array) free(sp->fts_array);283free(sp->fts_path);284285/* Return to original directory, save errno if necessary. */286if (!ISSET(FTS_NOCHDIR)) {287if (fchdir(sp->fts_rfd) == -1) saved_errno = errno;288(void)close(sp->fts_rfd);289}290291/* Free up the stream pointer. */292free(sp);293if (saved_errno) {294errno = saved_errno;295return -1;296}297298return 0;299}300301#if !defined(__FTS_COMPAT_TAILINGSLASH)302303/*304* Special case of "/" at the end of the path so that slashes aren't305* appended which would cause paths to be written as "....//foo".306*/307#define NAPPEND(p) \308(p->fts_path[p->fts_pathlen - 1] == '/' ? p->fts_pathlen - 1 : p->fts_pathlen)309310#else /* !defined(__FTS_COMPAT_TAILINGSLASH) */311312/*313* compatibility with the old behaviour.314*315* Special case a root of "/" so that slashes aren't appended which would316* cause paths to be written as "//foo".317*/318319#define NAPPEND(p) \320(p->fts_level == FTS_ROOTLEVEL && p->fts_pathlen == 1 && \321p->fts_path[0] == '/' \322? 0 \323: p->fts_pathlen)324325#endif /* !defined(__FTS_COMPAT_TAILINGSLASH) */326327FTSENT *fts_read(FTS *sp) {328FTSENT *p, *tmp;329int instr;330char *t;331int saved_errno;332333_DIAGASSERT(sp != NULL);334335/* If finished or unrecoverable error, return NULL. */336if (sp->fts_cur == NULL || ISSET(FTS_STOP)) return (NULL);337338/* Set current node pointer. */339p = sp->fts_cur;340341/* Save and zero out user instructions. */342instr = p->fts_instr;343p->fts_instr = FTS_NOINSTR;344345/* Any type of file may be re-visited; re-stat and re-turn. */346if (instr == FTS_AGAIN) {347p->fts_info = fts_stat(sp, p, 0);348return (p);349}350351/*352* Following a symlink -- SLNONE test allows application to see353* SLNONE and recover. If indirecting through a symlink, have354* keep a pointer to current location. If unable to get that355* pointer, follow fails.356*/357if (instr == FTS_FOLLOW &&358(p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {359p->fts_info = fts_stat(sp, p, 1);360if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {361if ((p->fts_symfd = open(".", O_RDONLY | O_CLOEXEC, 0)) == -1) {362p->fts_errno = errno;363p->fts_info = FTS_ERR;364} else365p->fts_flags |= FTS_SYMFOLLOW;366}367return (p);368}369370/* Directory in pre-order. */371if (p->fts_info == FTS_D) {372/* If skipped or crossed mount point, do post-order visit. */373if (instr == FTS_SKIP || (ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev)) {374if (p->fts_flags & FTS_SYMFOLLOW) (void)close(p->fts_symfd);375if (sp->fts_child) {376fts_lfree(sp->fts_child);377sp->fts_child = NULL;378}379p->fts_info = FTS_DP;380return (p);381}382383/* Rebuild if only read the names and now traversing. */384if (sp->fts_child && ISSET(FTS_NAMEONLY)) {385CLR(FTS_NAMEONLY);386fts_lfree(sp->fts_child);387sp->fts_child = NULL;388}389390/*391* Cd to the subdirectory.392*393* If have already read and now fail to chdir, whack the list394* to make the names come out right, and set the parent errno395* so the application will eventually get an error condition.396* Set the FTS_DONTCHDIR flag so that when we logically change397* directories back to the parent we don't do a chdir.398*399* If haven't read do so. If the read fails, fts_build sets400* FTS_STOP or the fts_info field of the node.401*/402if (sp->fts_child) {403if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) {404p->fts_errno = errno;405p->fts_flags |= FTS_DONTCHDIR;406for (p = sp->fts_child; p; p = p->fts_link)407p->fts_accpath = p->fts_parent->fts_accpath;408}409} else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) {410if (ISSET(FTS_STOP)) return (NULL);411return (p);412}413p = sp->fts_child;414sp->fts_child = NULL;415goto name;416}417418next:419/* Move to the next node on this level. */420tmp = p;421422/*423* We are going to free sp->fts_cur, set it to NULL so424* that fts_close() does not attempt to free it again425* if we exit without setting it to a new value because426* FCHDIR() failed below.427*/428assert(tmp == sp->fts_cur);429sp->fts_cur = NULL;430431if ((p = p->fts_link) != NULL) {432fts_free(tmp);433434/*435* If reached the top, return to the original directory, and436* load the paths for the next root.437*/438if (p->fts_level == FTS_ROOTLEVEL) {439if (FCHDIR(sp, sp->fts_rfd)) {440SET(FTS_STOP);441return (NULL);442}443fts_load(sp, p);444return (sp->fts_cur = p);445}446447/*448* User may have called fts_set on the node. If skipped,449* ignore. If followed, get a file descriptor so we can450* get back if necessary.451*/452if (p->fts_instr == FTS_SKIP) goto next;453if (p->fts_instr == FTS_FOLLOW) {454p->fts_info = fts_stat(sp, p, 1);455if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {456if ((p->fts_symfd = open(".", O_RDONLY | O_CLOEXEC, 0)) == -1) {457p->fts_errno = errno;458p->fts_info = FTS_ERR;459} else460p->fts_flags |= FTS_SYMFOLLOW;461}462p->fts_instr = FTS_NOINSTR;463}464465name:466t = sp->fts_path + NAPPEND(p->fts_parent);467*t++ = '/';468memmove(t, p->fts_name, (size_t)(p->fts_namelen + 1));469return (sp->fts_cur = p);470}471472/* Move up to the parent node. */473p = tmp->fts_parent;474fts_free(tmp);475476if (p->fts_level == FTS_ROOTPARENTLEVEL) {477/*478* Done; free everything up and set errno to 0 so the user479* can distinguish between error and EOF.480*/481fts_free(p);482errno = 0;483return (sp->fts_cur = NULL);484}485486/* NUL terminate the pathname. */487sp->fts_path[p->fts_pathlen] = '\0';488489/*490* Return to the parent directory. If at a root node or came through491* a symlink, go back through the file descriptor. Otherwise, cd up492* one directory.493*/494if (p->fts_level == FTS_ROOTLEVEL) {495if (FCHDIR(sp, sp->fts_rfd)) {496SET(FTS_STOP);497return (NULL);498}499} else if (p->fts_flags & FTS_SYMFOLLOW) {500if (FCHDIR(sp, p->fts_symfd)) {501saved_errno = errno;502(void)close(p->fts_symfd);503errno = saved_errno;504SET(FTS_STOP);505return (NULL);506}507(void)close(p->fts_symfd);508} else if (!(p->fts_flags & FTS_DONTCHDIR) &&509fts_safe_changedir(sp, p->fts_parent, -1, "..")) {510SET(FTS_STOP);511return (NULL);512}513p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;514return (sp->fts_cur = p);515}516517/*518* Fts_set takes the stream as an argument although it's not used in this519* implementation; it would be necessary if anyone wanted to add global520* semantics to fts using fts_set. An error return is allowed for similar521* reasons.522*/523/* ARGSUSED */524int fts_set(FTS *sp, FTSENT *p, int instr) {525_DIAGASSERT(sp != NULL);526_DIAGASSERT(p != NULL);527528if (instr && instr != FTS_AGAIN && instr != FTS_FOLLOW &&529instr != FTS_NOINSTR && instr != FTS_SKIP) {530errno = EINVAL;531return (1);532}533p->fts_instr = instr;534return (0);535}536537FTSENT *fts_children(FTS *sp, int instr) {538FTSENT *p;539int fd;540541_DIAGASSERT(sp != NULL);542543if (instr && instr != FTS_NAMEONLY) {544errno = EINVAL;545return (NULL);546}547548/* Set current node pointer. */549p = sp->fts_cur;550551/*552* Errno set to 0 so user can distinguish empty directory from553* an error.554*/555errno = 0;556557/* Fatal errors stop here. */558if (ISSET(FTS_STOP)) return (NULL);559560/* Return logical hierarchy of user's arguments. */561if (p->fts_info == FTS_INIT) return (p->fts_link);562563/*564* If not a directory being visited in pre-order, stop here. Could565* allow FTS_DNR, assuming the user has fixed the problem, but the566* same effect is available with FTS_AGAIN.567*/568if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */) return (NULL);569570/* Free up any previous child list. */571if (sp->fts_child) fts_lfree(sp->fts_child);572573if (instr == FTS_NAMEONLY) {574SET(FTS_NAMEONLY);575instr = BNAMES;576} else577instr = BCHILD;578579/*580* If using chdir on a relative path and called BEFORE fts_read does581* its chdir to the root of a traversal, we can lose -- we need to582* chdir into the subdirectory, and we don't know where the current583* directory is, so we can't get back so that the upcoming chdir by584* fts_read will work.585*/586if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' ||587ISSET(FTS_NOCHDIR))588return (sp->fts_child = fts_build(sp, instr));589590if ((fd = open(".", O_RDONLY | O_CLOEXEC, 0)) == -1)591return (sp->fts_child = NULL);592sp->fts_child = fts_build(sp, instr);593if (fchdir(fd)) {594(void)close(fd);595return (NULL);596}597(void)close(fd);598return (sp->fts_child);599}600601/*602* This is the tricky part -- do not casually change *anything* in here. The603* idea is to build the linked list of entries that are used by fts_children604* and fts_read. There are lots of special cases.605*606* The real slowdown in walking the tree is the stat calls. If FTS_NOSTAT is607* set and it's a physical walk (so that symbolic links can't be directories),608* we can do things quickly. First, if it's a 4.4BSD file system, the type609* of the file is in the directory entry. Otherwise, we assume that the number610* of subdirectories in a node is equal to the number of links to the parent.611* The former skips all stat calls. The latter skips stat calls in any leaf612* directories and for any files after the subdirectories in the directory have613* been found, cutting the stat calls by about 2/3.614*/615static FTSENT *fts_build(FTS *sp, int type) {616struct dirent *dp;617FTSENT *p, *head;618size_t nitems;619FTSENT *cur, *tail;620DIR *dirp;621void *oldaddr;622size_t dnamlen;623int cderrno, descend, level, nlinks, saved_errno, nostat, doadjust;624size_t len, maxlen;625#ifdef FTS_WHITEOUT626int oflag;627#endif628char *cp = NULL; /* pacify gcc */629630_DIAGASSERT(sp != NULL);631632/* Set current node pointer. */633cur = sp->fts_cur;634635/*636* Open the directory for reading. If this fails, we're done.637* If being called from fts_read, set the fts_info field.638*/639#ifdef FTS_WHITEOUT640if (ISSET(FTS_WHITEOUT))641oflag = DTF_NODUP | DTF_REWIND;642else643oflag = DTF_HIDEW | DTF_NODUP | DTF_REWIND;644#else645#define __opendir2(path, flag) opendir(path)646#endif647if ((dirp = __opendir2(cur->fts_accpath, oflag)) == NULL) {648if (type == BREAD) {649cur->fts_info = FTS_DNR;650cur->fts_errno = errno;651}652return (NULL);653}654655/*656* Nlinks is the number of possible entries of type directory in the657* directory if we're cheating on stat calls, 0 if we're not doing658* any stat calls at all, -1 if we're doing stats on everything.659*/660if (type == BNAMES) {661nlinks = 0;662nostat = 1;663} else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) {664nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2);665nostat = 1;666} else {667nlinks = -1;668nostat = 0;669}670671#ifdef notdef672(void)printf("nlinks == %d (cur: %d)\n", nlinks, cur->fts_nlink);673(void)printf("NOSTAT %d PHYSICAL %d SEEDOT %d\n", ISSET(FTS_NOSTAT),674ISSET(FTS_PHYSICAL), ISSET(FTS_SEEDOT));675#endif676/*677* If we're going to need to stat anything or we want to descend678* and stay in the directory, chdir. If this fails we keep going,679* but set a flag so we don't chdir after the post-order visit.680* We won't be able to stat anything, but we can still return the681* names themselves. Note, that since fts_read won't be able to682* chdir into the directory, it will have to return different path683* names than before, i.e. "a/b" instead of "b". Since the node684* has already been visited in pre-order, have to wait until the685* post-order visit to return the error. There is a special case686* here, if there was nothing to stat then it's not an error to687* not be able to stat. This is all fairly nasty. If a program688* needed sorted entries or stat information, they had better be689* checking FTS_NS on the returned nodes.690*/691cderrno = 0;692if (nlinks || type == BREAD) {693if (fts_safe_changedir(sp, cur, dirfd(dirp), NULL)) {694if (nlinks && type == BREAD) cur->fts_errno = errno;695cur->fts_flags |= FTS_DONTCHDIR;696descend = 0;697cderrno = errno;698} else699descend = 1;700} else701descend = 0;702703/*704* Figure out the max file name length that can be stored in the705* current path -- the inner loop allocates more path as necessary.706* We really wouldn't have to do the maxlen calculations here, we707* could do them in fts_read before returning the path, but it's a708* lot easier here since the length is part of the dirent structure.709*710* If not changing directories set a pointer so that can just append711* each new name into the path.712*/713len = NAPPEND(cur);714if (ISSET(FTS_NOCHDIR)) {715cp = sp->fts_path + len;716*cp++ = '/';717}718len++;719maxlen = sp->fts_pathlen - len;720721#if defined(__FTS_COMPAT_LEVEL)722if (cur->fts_level == SHRT_MAX) {723(void)closedir(dirp);724cur->fts_info = FTS_ERR;725SET(FTS_STOP);726errno = ENAMETOOLONG;727return (NULL);728}729#endif730731level = cur->fts_level + 1;732733/* Read the directory, attaching each entry to the `link' pointer. */734doadjust = 0;735for (head = tail = NULL, nitems = 0; (dp = readdir(dirp)) != NULL;) {736if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name)) continue;737738#if defined(HAVE_STRUCT_DIRENT_D_NAMLEN)739dnamlen = dp->d_namlen;740#else741dnamlen = strlen(dp->d_name);742#endif743if ((p = fts_alloc(sp, dp->d_name, dnamlen)) == NULL) goto mem1;744if (dnamlen >= maxlen) { /* include space for NUL */745oldaddr = sp->fts_path;746if (fts_palloc(sp, dnamlen + len + 1)) {747/*748* No more memory for path or structures. Save749* errno, free up the current structure and the750* structures already allocated.751*/752mem1:753saved_errno = errno;754if (p) fts_free(p);755fts_lfree(head);756(void)closedir(dirp);757errno = saved_errno;758cur->fts_info = FTS_ERR;759SET(FTS_STOP);760return (NULL);761}762/* Did realloc() change the pointer? */763if (oldaddr != sp->fts_path) {764doadjust = 1;765if (ISSET(FTS_NOCHDIR)) cp = sp->fts_path + len;766}767maxlen = sp->fts_pathlen - len;768}769770#if defined(__FTS_COMPAT_LENGTH)771if (len + dnamlen >= USHRT_MAX) {772/*773* In an FTSENT, fts_pathlen is an unsigned short774* so it is possible to wraparound here.775* If we do, free up the current structure and the776* structures already allocated, then error out777* with ENAMETOOLONG.778*/779fts_free(p);780fts_lfree(head);781(void)closedir(dirp);782cur->fts_info = FTS_ERR;783SET(FTS_STOP);784errno = ENAMETOOLONG;785return (NULL);786}787#endif788p->fts_level = level;789p->fts_pathlen = ftsent_pathlen_truncate(len + dnamlen);790p->fts_parent = sp->fts_cur;791792#ifdef FTS_WHITEOUT793if (dp->d_type == DT_WHT) p->fts_flags |= FTS_ISW;794#endif795796if (cderrno) {797if (nlinks) {798p->fts_info = FTS_NS;799p->fts_errno = cderrno;800} else801p->fts_info = FTS_NSOK;802p->fts_accpath = cur->fts_accpath;803} else if (nlinks == 0804#ifdef DT_DIR805|| (nostat && dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN)806#endif807) {808p->fts_accpath = ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name;809p->fts_info = FTS_NSOK;810} else {811/* Build a file name for fts_stat to stat. */812if (ISSET(FTS_NOCHDIR)) {813p->fts_accpath = p->fts_path;814memmove(cp, p->fts_name, (size_t)(p->fts_namelen + 1));815} else816p->fts_accpath = p->fts_name;817/* Stat it. */818p->fts_info = fts_stat(sp, p, 0);819820/* Decrement link count if applicable. */821if (nlinks > 0 && (p->fts_info == FTS_D || p->fts_info == FTS_DC ||822p->fts_info == FTS_DOT))823--nlinks;824}825826/* We walk in directory order so "ls -f" doesn't get upset. */827p->fts_link = NULL;828if (head == NULL)829head = tail = p;830else {831tail->fts_link = p;832tail = p;833}834++nitems;835}836(void)closedir(dirp);837838/*839* If had to realloc the path, adjust the addresses for the rest840* of the tree.841*/842if (doadjust) fts_padjust(sp, head);843844/*845* If not changing directories, reset the path back to original846* state.847*/848if (ISSET(FTS_NOCHDIR)) {849if (len == sp->fts_pathlen || nitems == 0) --cp;850*cp = '\0';851}852853/*854* If descended after called from fts_children or after called from855* fts_read and nothing found, get back. At the root level we use856* the saved fd; if one of fts_open()'s arguments is a relative path857* to an empty directory, we wind up here with no other way back. If858* can't get back, we're done.859*/860if (descend && (type == BCHILD || !nitems) &&861(cur->fts_level == FTS_ROOTLEVEL862? FCHDIR(sp, sp->fts_rfd)863: fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) {864cur->fts_info = FTS_ERR;865SET(FTS_STOP);866return (NULL);867}868869/* If didn't find anything, return NULL. */870if (!nitems) {871if (type == BREAD) cur->fts_info = FTS_DP;872return (NULL);873}874875/* Sort the entries. */876if (sp->fts_compar && nitems > 1) head = fts_sort(sp, head, nitems);877return (head);878}879880static unsigned short fts_stat(FTS *sp, FTSENT *p, int follow) {881FTSENT *t;882dev_t dev;883__fts_ino_t ino;884__fts_stat_t *sbp, sb;885int saved_errno;886887_DIAGASSERT(sp != NULL);888_DIAGASSERT(p != NULL);889890/* If user needs stat info, stat buffer already allocated. */891sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp;892893#ifdef FTS_WHITEOUT894/* check for whiteout */895if (p->fts_flags & FTS_ISW) {896if (sbp != &sb) {897memset(sbp, '\0', sizeof(*sbp));898sbp->st_mode = S_IFWHT;899}900return (FTS_W);901}902#endif903904/*905* If doing a logical walk, or application requested FTS_FOLLOW, do906* a stat(2). If that fails, check for a non-existent symlink. If907* fail, set the errno from the stat call.908*/909if (ISSET(FTS_LOGICAL) || follow) {910if (cowasm_stat(p->fts_accpath, sbp)) {911saved_errno = errno;912if (!cowasm_lstat(p->fts_accpath, sbp)) {913errno = 0;914return (FTS_SLNONE);915}916p->fts_errno = saved_errno;917goto err;918}919} else if (cowasm_lstat(p->fts_accpath, sbp)) {920p->fts_errno = errno;921err:922memset(sbp, 0, sizeof(*sbp));923return (FTS_NS);924}925926if (S_ISDIR(sbp->st_mode)) {927/*928* Set the device/inode. Used to find cycles and check for929* crossing mount points. Also remember the link count, used930* in fts_build to limit the number of stat calls. It is931* understood that these fields are only referenced if fts_info932* is set to FTS_D.933*/934dev = p->fts_dev = sbp->st_dev;935ino = p->fts_ino = sbp->st_ino;936p->fts_nlink = sbp->st_nlink;937938if (ISDOT(p->fts_name)) return (FTS_DOT);939940/*941* Cycle detection is done by brute force when the directory942* is first encountered. If the tree gets deep enough or the943* number of symbolic links to directories is high enough,944* something faster might be worthwhile.945*/946for (t = p->fts_parent; t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent)947if (ino == t->fts_ino && dev == t->fts_dev) {948p->fts_cycle = t;949return (FTS_DC);950}951return (FTS_D);952}953if (S_ISLNK(sbp->st_mode)) return (FTS_SL);954if (S_ISREG(sbp->st_mode)) return (FTS_F);955return (FTS_DEFAULT);956}957958static FTSENT *fts_sort(FTS *sp, FTSENT *head, size_t nitems) {959FTSENT **ap, *p;960961_DIAGASSERT(sp != NULL);962_DIAGASSERT(head != NULL);963964/*965* Construct an array of pointers to the structures and call qsort(3).966* Reassemble the array in the order returned by qsort. If unable to967* sort for memory reasons, return the directory entries in their968* current order. Allocate enough space for the current needs plus969* 40 so don't realloc one entry at a time.970*/971if (nitems > sp->fts_nitems) {972FTSENT **new;973974new = realloc(sp->fts_array, sizeof(FTSENT *) * (nitems + 40));975if (new == 0) return (head);976sp->fts_array = new;977sp->fts_nitems = fts_nitems_truncate(nitems + 40);978}979for (ap = sp->fts_array, p = head; p; p = p->fts_link) *ap++ = p;980qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *),981(int (*)(const void *, const void *))sp->fts_compar);982for (head = *(ap = sp->fts_array); --nitems; ++ap) ap[0]->fts_link = ap[1];983ap[0]->fts_link = NULL;984return (head);985}986987static FTSENT *fts_alloc(FTS *sp, const char *name, size_t namelen) {988FTSENT *p;989#if defined(FTS_ALLOC_ALIGNED)990size_t len;991#endif992993_DIAGASSERT(sp != NULL);994_DIAGASSERT(name != NULL);995996#if defined(FTS_ALLOC_ALIGNED)997/*998* The file name is a variable length array and no stat structure is999* necessary if the user has set the nostat bit. Allocate the FTSENT1000* structure, the file name and the stat structure in one chunk, but1001* be careful that the stat structure is reasonably aligned. Since the1002* fts_name field is declared to be of size 1, the fts_name pointer is1003* namelen + 2 before the first possible address of the stat structure.1004*/1005len = sizeof(FTSENT) + namelen;1006if (!ISSET(FTS_NOSTAT)) len += sizeof(*(p->fts_statp)) + ALIGNBYTES;1007if ((p = malloc(len)) == NULL) return (NULL);10081009if (!ISSET(FTS_NOSTAT))1010p->fts_statp =1011(__fts_stat_t *)ALIGN((unsigned long)(p->fts_name + namelen + 2));1012#else1013if ((p = malloc(sizeof(FTSENT) + namelen)) == NULL) return (NULL);10141015if (!ISSET(FTS_NOSTAT))1016if ((p->fts_statp = malloc(sizeof(*(p->fts_statp)))) == NULL) {1017free(p);1018return (NULL);1019}1020#endif10211022if (ISSET(FTS_NOSTAT)) p->fts_statp = NULL;10231024/* Copy the name plus the trailing NULL. */1025memmove(p->fts_name, name, namelen + 1);10261027p->fts_namelen = ftsent_namelen_truncate(namelen);1028p->fts_path = sp->fts_path;1029p->fts_errno = 0;1030p->fts_flags = 0;1031p->fts_instr = FTS_NOINSTR;1032p->fts_number = 0;1033p->fts_pointer = NULL;1034return (p);1035}10361037static void fts_free(FTSENT *p) {1038#if !defined(FTS_ALLOC_ALIGNED)1039if (p->fts_statp) free(p->fts_statp);1040#endif1041free(p);1042}10431044static void fts_lfree(FTSENT *head) {1045FTSENT *p;10461047/* XXX: head may be NULL ? */10481049/* Free a linked list of structures. */1050while ((p = head) != NULL) {1051head = head->fts_link;1052fts_free(p);1053}1054}10551056static size_t fts_pow2(size_t x) {1057x--;1058x |= x >> 1;1059x |= x >> 2;1060x |= x >> 4;1061x |= x >> 8;1062x |= x >> 16;1063#if LONG_BIT > 321064x |= x >> 32;1065#endif1066#if LONG_BIT > 641067x |= x >> 64;1068#endif1069x++;1070return (x);1071}10721073/*1074* Allow essentially unlimited paths; find, rm, ls should all work on any tree.1075* Most systems will allow creation of paths much longer than MAXPATHLEN, even1076* though the kernel won't resolve them. Round up the new size to a power of 2,1077* so we don't realloc the path 2 bytes at a time.1078*/1079static int fts_palloc(FTS *sp, size_t size) {1080char *new;10811082_DIAGASSERT(sp != NULL);10831084#ifdef __FTS_COMPAT_LENGTH1085/* Protect against fts_pathlen overflow. */1086if (size > USHRT_MAX + 1) {1087errno = ENAMETOOLONG;1088return (1);1089}1090#endif1091size = fts_pow2(size);1092new = realloc(sp->fts_path, size);1093if (new == 0) return (1);1094sp->fts_path = new;1095sp->fts_pathlen = fts_pathlen_truncate(size);1096return (0);1097}10981099/*1100* When the path is realloc'd, have to fix all of the pointers in structures1101* already returned.1102*/1103static void fts_padjust(FTS *sp, FTSENT *head) {1104FTSENT *p;1105char *addr;11061107_DIAGASSERT(sp != NULL);11081109#define ADJUST(p) \1110do { \1111if ((p)->fts_accpath != (p)->fts_name) \1112(p)->fts_accpath = addr + ((p)->fts_accpath - (p)->fts_path); \1113(p)->fts_path = addr; \1114} while (/*CONSTCOND*/ 0)11151116addr = sp->fts_path;11171118/* Adjust the current set of children. */1119for (p = sp->fts_child; p; p = p->fts_link) ADJUST(p);11201121/* Adjust the rest of the tree, including the current level. */1122for (p = head; p->fts_level >= FTS_ROOTLEVEL;) {1123ADJUST(p);1124p = p->fts_link ? p->fts_link : p->fts_parent;1125}1126}11271128static size_t fts_maxarglen(char *const *argv) {1129size_t len, max;11301131_DIAGASSERT(argv != NULL);11321133for (max = 0; *argv; ++argv)1134if ((len = strlen(*argv)) > max) max = len;1135return (max + 1);1136}11371138/*1139* Change to dir specified by fd or p->fts_accpath without getting1140* tricked by someone changing the world out from underneath us.1141* Assumes p->fts_dev and p->fts_ino are filled in.1142*/1143#include<stdio.h>1144static int fts_safe_changedir(const FTS *sp, const FTSENT *p, int fd,1145const char *path) {1146int oldfd = fd, ret = -1;1147__fts_stat_t sb;11481149if (ISSET(FTS_NOCHDIR)) return 0;11501151if (oldfd < 0 && (fd = open(path, O_RDONLY | O_CLOEXEC)) == -1) return -1;11521153if (cowasm_fstat(fd, &sb) == -1) goto bail;11541155if (sb.st_ino != p->fts_ino || sb.st_dev != p->fts_dev) {1156errno = ENOENT;1157goto bail;1158}11591160ret = fchdir(fd);11611162bail:11631164if (oldfd < 0) {1165int save_errno = errno;1166(void)close(fd);1167errno = save_errno;1168}1169return ret;1170}117111721173