142273Sbostic /*- 266811Sbostic * Copyright (c) 1990, 1993, 1994 361111Sbostic * The Regents of the University of California. All rights reserved. 439800Sbostic * 542273Sbostic * %sccs.include.redist.c% 639800Sbostic */ 739800Sbostic 839800Sbostic #if defined(LIBC_SCCS) && !defined(lint) 9*67575Spendry static char sccsid[] = "@(#)fts.c 8.5 (Berkeley) 07/28/94"; 1039800Sbostic #endif /* LIBC_SCCS and not lint */ 1139800Sbostic 1239800Sbostic #include <sys/param.h> 1339800Sbostic #include <sys/stat.h> 1466811Sbostic 1539800Sbostic #include <dirent.h> 1639800Sbostic #include <errno.h> 1766811Sbostic #include <fcntl.h> 1852209Sbostic #include <fts.h> 1947194Sbostic #include <stdlib.h> 2042273Sbostic #include <string.h> 2147155Sbostic #include <unistd.h> 2239800Sbostic 2352061Sbostic static FTSENT *fts_alloc __P((FTS *, char *, int)); 2452061Sbostic static FTSENT *fts_build __P((FTS *, int)); 2552061Sbostic static void fts_lfree __P((FTSENT *)); 2652061Sbostic static void fts_load __P((FTS *, FTSENT *)); 2755557Sbostic static size_t fts_maxarglen __P((char * const *)); 2852209Sbostic static void fts_padjust __P((FTS *, void *)); 2953801Sbostic static int fts_palloc __P((FTS *, size_t)); 3052061Sbostic static FTSENT *fts_sort __P((FTS *, FTSENT *, int)); 3152061Sbostic static u_short fts_stat __P((FTS *, FTSENT *, int)); 3239800Sbostic 3352209Sbostic #define ISDOT(a) (a[0] == '.' && (!a[1] || a[1] == '.' && !a[2])) 3452209Sbostic 3545600Sbostic #define ISSET(opt) (sp->fts_options & opt) 3645600Sbostic #define SET(opt) (sp->fts_options |= opt) 3739800Sbostic 3845600Sbostic #define CHDIR(sp, path) (!ISSET(FTS_NOCHDIR) && chdir(path)) 3945600Sbostic #define FCHDIR(sp, fd) (!ISSET(FTS_NOCHDIR) && fchdir(fd)) 4039800Sbostic 4142299Sbostic /* fts_build flags */ 4252770Sbostic #define BCHILD 1 /* fts_children */ 4352770Sbostic #define BNAMES 2 /* fts_children, names only */ 4452770Sbostic #define BREAD 3 /* fts_read */ 4542299Sbostic 4639800Sbostic FTS * 4745600Sbostic fts_open(argv, options, compar) 4847155Sbostic char * const *argv; 4939800Sbostic register int options; 5047194Sbostic int (*compar)(); 5139800Sbostic { 5239800Sbostic register FTS *sp; 5339800Sbostic register FTSENT *p, *root; 5455557Sbostic register int nitems; 5539800Sbostic FTSENT *parent, *tmp; 5647194Sbostic int len; 5739800Sbostic 5852770Sbostic /* Options check. */ 5952770Sbostic if (options & ~FTS_OPTIONMASK) { 6052770Sbostic errno = EINVAL; 6152770Sbostic return (NULL); 6252770Sbostic } 6352770Sbostic 6445600Sbostic /* Allocate/initialize the stream */ 6552150Sbostic if ((sp = malloc((u_int)sizeof(FTS))) == NULL) 6652150Sbostic return (NULL); 6766813Sbostic memset(sp, 0, sizeof(FTS)); 6842273Sbostic sp->fts_compar = compar; 6942273Sbostic sp->fts_options = options; 7039800Sbostic 7145600Sbostic /* Logical walks turn on NOCHDIR; symbolic links are too hard. */ 7245600Sbostic if (ISSET(FTS_LOGICAL)) 7345600Sbostic SET(FTS_NOCHDIR); 7445600Sbostic 7553801Sbostic /* 7655557Sbostic * Start out with 1K of path space, and enough, in any case, 7755557Sbostic * to hold the user's paths. 7853801Sbostic */ 7955557Sbostic if (fts_palloc(sp, MAX(fts_maxarglen(argv), MAXPATHLEN))) 8053801Sbostic goto mem1; 8153801Sbostic 8245600Sbostic /* Allocate/initialize root's parent. */ 8352155Sbostic if ((parent = fts_alloc(sp, "", 0)) == NULL) 8453801Sbostic goto mem2; 8547212Sbostic parent->fts_level = FTS_ROOTPARENTLEVEL; 8639800Sbostic 8745600Sbostic /* Allocate/initialize root(s). */ 8855557Sbostic for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) { 8952160Sbostic /* Don't allow zero-length paths. */ 9052209Sbostic if ((len = strlen(*argv)) == 0) { 9147194Sbostic errno = ENOENT; 9253801Sbostic goto mem3; 9347194Sbostic } 9452209Sbostic 9547194Sbostic p = fts_alloc(sp, *argv, len); 9649519Sbostic p->fts_level = FTS_ROOTLEVEL; 9749519Sbostic p->fts_parent = parent; 9852218Sbostic p->fts_accpath = p->fts_name; 9952344Sbostic p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW)); 10052209Sbostic 10152209Sbostic /* Command-line "." and ".." are real directories. */ 10252209Sbostic if (p->fts_info == FTS_DOT) 10352209Sbostic p->fts_info = FTS_D; 10452209Sbostic 10543070Sbostic /* 10645600Sbostic * If comparison routine supplied, traverse in sorted 10743070Sbostic * order; otherwise traverse in the order specified. 10843070Sbostic */ 10943070Sbostic if (compar) { 11043070Sbostic p->fts_link = root; 11143070Sbostic root = p; 11243070Sbostic } else { 11343070Sbostic p->fts_link = NULL; 11452155Sbostic if (root == NULL) 11543070Sbostic tmp = root = p; 11643070Sbostic else { 11743070Sbostic tmp->fts_link = p; 11843070Sbostic tmp = p; 11939800Sbostic } 12039800Sbostic } 12139800Sbostic } 12243070Sbostic if (compar && nitems > 1) 12345600Sbostic root = fts_sort(sp, root, nitems); 12439800Sbostic 12542281Sbostic /* 12645600Sbostic * Allocate a dummy pointer and make fts_read think that we've just 12752160Sbostic * finished the node before the root(s); set p->fts_info to FTS_INIT 12842281Sbostic * so that everything about the "current" node is ignored. 12942281Sbostic */ 13052155Sbostic if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL) 13153801Sbostic goto mem3; 13242281Sbostic sp->fts_cur->fts_link = root; 13352160Sbostic sp->fts_cur->fts_info = FTS_INIT; 13442281Sbostic 13552160Sbostic /* 13645600Sbostic * If using chdir(2), grab a file descriptor pointing to dot to insure 13742299Sbostic * that we can get back here; this could be avoided for some paths, 13842299Sbostic * but almost certainly not worth the effort. Slashes, symbolic links, 13942299Sbostic * and ".." are all fairly nasty problems. Note, if we can't get the 14042299Sbostic * descriptor we run anyway, just more slowly. 14139800Sbostic */ 14247194Sbostic if (!ISSET(FTS_NOCHDIR) && (sp->fts_rfd = open(".", O_RDONLY, 0)) < 0) 14345600Sbostic SET(FTS_NOCHDIR); 14439800Sbostic 14552150Sbostic return (sp); 14639800Sbostic 14753801Sbostic mem3: fts_lfree(root); 14847194Sbostic free(parent); 14953801Sbostic mem2: free(sp->fts_path); 15047194Sbostic mem1: free(sp); 15152150Sbostic return (NULL); 15239800Sbostic } 15339800Sbostic 15447404Sbostic static void 15545600Sbostic fts_load(sp, p) 15645600Sbostic FTS *sp; 15739800Sbostic register FTSENT *p; 15839800Sbostic { 15939800Sbostic register int len; 16039800Sbostic register char *cp; 16139800Sbostic 16239800Sbostic /* 16347220Sbostic * Load the stream structure for the next traversal. Since we don't 16447220Sbostic * actually enter the directory until after the preorder visit, set 16547220Sbostic * the fts_accpath field specially so the chdir gets done to the right 16652209Sbostic * place and the user can access the first node. From fts_open it's 16752209Sbostic * known that the path will fit. 16839800Sbostic */ 16942273Sbostic len = p->fts_pathlen = p->fts_namelen; 17066813Sbostic memmove(sp->fts_path, p->fts_name, len + 1); 17166813Sbostic if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) { 17239800Sbostic len = strlen(++cp); 17366813Sbostic memmove(p->fts_name, cp, len + 1); 17442273Sbostic p->fts_namelen = len; 17539800Sbostic } 17645600Sbostic p->fts_accpath = p->fts_path = sp->fts_path; 17752209Sbostic sp->fts_dev = p->fts_dev; 17839800Sbostic } 17939800Sbostic 18052209Sbostic int 18145600Sbostic fts_close(sp) 18239800Sbostic FTS *sp; 18339800Sbostic { 18439800Sbostic register FTSENT *freep, *p; 18539800Sbostic int saved_errno; 18639800Sbostic 18752209Sbostic /* 18852209Sbostic * This still works if we haven't read anything -- the dummy structure 18952209Sbostic * points to the root list, so we step through to the end of the root 19052209Sbostic * list which has a valid parent pointer. 19152209Sbostic */ 19242281Sbostic if (sp->fts_cur) { 19352209Sbostic for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) { 19442281Sbostic freep = p; 19542281Sbostic p = p->fts_link ? p->fts_link : p->fts_parent; 19647194Sbostic free(freep); 19739800Sbostic } 19847194Sbostic free(p); 19942281Sbostic } 20039800Sbostic 20145600Sbostic /* Free up child linked list, sort array, path buffer. */ 20242273Sbostic if (sp->fts_child) 20342273Sbostic fts_lfree(sp->fts_child); 20442273Sbostic if (sp->fts_array) 20547194Sbostic free(sp->fts_array); 20647194Sbostic free(sp->fts_path); 20739800Sbostic 20847194Sbostic /* Return to original directory, save errno if necessary. */ 20945600Sbostic if (!ISSET(FTS_NOCHDIR)) { 21047194Sbostic saved_errno = fchdir(sp->fts_rfd) ? errno : 0; 21147194Sbostic (void)close(sp->fts_rfd); 21239800Sbostic } 21339800Sbostic 21445600Sbostic /* Free up the stream pointer. */ 21547194Sbostic free(sp); 21639800Sbostic 21745600Sbostic /* Set errno and return. */ 21845600Sbostic if (!ISSET(FTS_NOCHDIR) && saved_errno) { 21939800Sbostic errno = saved_errno; 22052150Sbostic return (-1); 22139800Sbostic } 22252150Sbostic return (0); 22339800Sbostic } 22439800Sbostic 22547194Sbostic /* 22652160Sbostic * Special case a root of "/" so that slashes aren't appended which would 22752160Sbostic * cause paths to be written as "//foo". 22847194Sbostic */ 22955557Sbostic #define NAPPEND(p) \ 23055557Sbostic (p->fts_level == FTS_ROOTLEVEL && p->fts_pathlen == 1 && \ 23147194Sbostic p->fts_path[0] == '/' ? 0 : p->fts_pathlen) 23247194Sbostic 23339800Sbostic FTSENT * 23445600Sbostic fts_read(sp) 23539800Sbostic register FTS *sp; 23639800Sbostic { 23742299Sbostic register FTSENT *p, *tmp; 23839800Sbostic register int instr; 23947194Sbostic register char *t; 24053032Sbostic int saved_errno; 24139800Sbostic 24245600Sbostic /* If finished or unrecoverable error, return NULL. */ 24352155Sbostic if (sp->fts_cur == NULL || ISSET(FTS_STOP)) 24452150Sbostic return (NULL); 24539800Sbostic 24645600Sbostic /* Set current node pointer. */ 24742273Sbostic p = sp->fts_cur; 24839800Sbostic 24945600Sbostic /* Save and zero out user instructions. */ 25042273Sbostic instr = p->fts_instr; 25147194Sbostic p->fts_instr = FTS_NOINSTR; 25239800Sbostic 25347194Sbostic /* Any type of file may be re-visited; re-stat and re-turn. */ 25439800Sbostic if (instr == FTS_AGAIN) { 25545600Sbostic p->fts_info = fts_stat(sp, p, 0); 25652150Sbostic return (p); 25739800Sbostic } 25839800Sbostic 25947194Sbostic /* 26047194Sbostic * Following a symlink -- SLNONE test allows application to see 26152290Sbostic * SLNONE and recover. If indirecting through a symlink, have 26252290Sbostic * keep a pointer to current location. If unable to get that 26352290Sbostic * pointer, follow fails. 26447194Sbostic */ 26547194Sbostic if (instr == FTS_FOLLOW && 26647194Sbostic (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) { 26745600Sbostic p->fts_info = fts_stat(sp, p, 1); 26852290Sbostic if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) 26952290Sbostic if ((p->fts_symfd = open(".", O_RDONLY, 0)) < 0) { 27052290Sbostic p->fts_errno = errno; 27152290Sbostic p->fts_info = FTS_ERR; 27252290Sbostic } else 27352290Sbostic p->fts_flags |= FTS_SYMFOLLOW; 27452150Sbostic return (p); 27542299Sbostic } 27642299Sbostic 27745600Sbostic /* Directory in pre-order. */ 27842299Sbostic if (p->fts_info == FTS_D) { 27945600Sbostic /* If skipped or crossed mount point, do post-order visit. */ 28047194Sbostic if (instr == FTS_SKIP || 28152209Sbostic ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev) { 28252290Sbostic if (p->fts_flags & FTS_SYMFOLLOW) 28352290Sbostic (void)close(p->fts_symfd); 28442273Sbostic if (sp->fts_child) { 28542273Sbostic fts_lfree(sp->fts_child); 28642273Sbostic sp->fts_child = NULL; 28739800Sbostic } 28842301Sbostic p->fts_info = FTS_DP; 28952150Sbostic return (p); 29042299Sbostic } 29142299Sbostic 29253096Sbostic /* Rebuild if only read the names and now traversing. */ 29352770Sbostic if (sp->fts_child && sp->fts_options & FTS_NAMEONLY) { 29452770Sbostic sp->fts_options &= ~FTS_NAMEONLY; 29552770Sbostic fts_lfree(sp->fts_child); 29652770Sbostic sp->fts_child = NULL; 29752770Sbostic } 29852770Sbostic 29947194Sbostic /* 30053096Sbostic * Cd to the subdirectory. 30153096Sbostic * 30247220Sbostic * If have already read and now fail to chdir, whack the list 30353096Sbostic * to make the names come out right, and set the parent errno 30447220Sbostic * so the application will eventually get an error condition. 30553096Sbostic * Set the FTS_DONTCHDIR flag so that when we logically change 30653096Sbostic * directories back to the parent we don't do a chdir. 30753096Sbostic * 30853096Sbostic * If haven't read do so. If the read fails, fts_build sets 30953096Sbostic * FTS_STOP or the fts_info field of the node. 31047194Sbostic */ 31147194Sbostic if (sp->fts_child) { 31242299Sbostic if (CHDIR(sp, p->fts_accpath)) { 31352278Sbostic p->fts_errno = errno; 31452290Sbostic p->fts_flags |= FTS_DONTCHDIR; 31547194Sbostic for (p = sp->fts_child; p; p = p->fts_link) 31647194Sbostic p->fts_accpath = 31747194Sbostic p->fts_parent->fts_accpath; 31842299Sbostic } 31952155Sbostic } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) { 32052290Sbostic if (ISSET(FTS_STOP)) 32152150Sbostic return (NULL); 32252150Sbostic return (p); 32347220Sbostic } 32447194Sbostic p = sp->fts_child; 32542299Sbostic sp->fts_child = NULL; 32647194Sbostic goto name; 32739800Sbostic } 32839800Sbostic 32952278Sbostic /* Move to the next node on this level. */ 33042299Sbostic next: tmp = p; 33142299Sbostic if (p = p->fts_link) { 33247194Sbostic free(tmp); 33347194Sbostic 33458532Sbostic /* 33558532Sbostic * If reached the top, return to the original directory, and 33658532Sbostic * load the paths for the next root. 33758532Sbostic */ 33847212Sbostic if (p->fts_level == FTS_ROOTLEVEL) { 33958532Sbostic if (!ISSET(FTS_NOCHDIR) && FCHDIR(sp, sp->fts_rfd)) { 34058532Sbostic SET(FTS_STOP); 34158532Sbostic return (NULL); 34258532Sbostic } 34347404Sbostic fts_load(sp, p); 34452150Sbostic return (sp->fts_cur = p); 34547194Sbostic } 34642299Sbostic 34752290Sbostic /* 34852290Sbostic * User may have called fts_set on the node. If skipped, 34952290Sbostic * ignore. If followed, get a file descriptor so we can 35052290Sbostic * get back if necessary. 35152290Sbostic */ 35247194Sbostic if (p->fts_instr == FTS_SKIP) 35347194Sbostic goto next; 35447194Sbostic if (p->fts_instr == FTS_FOLLOW) { 35547194Sbostic p->fts_info = fts_stat(sp, p, 1); 35652290Sbostic if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) 35752290Sbostic if ((p->fts_symfd = 35852290Sbostic open(".", O_RDONLY, 0)) < 0) { 35952290Sbostic p->fts_errno = errno; 36052290Sbostic p->fts_info = FTS_ERR; 36152290Sbostic } else 36252290Sbostic p->fts_flags |= FTS_SYMFOLLOW; 36347194Sbostic p->fts_instr = FTS_NOINSTR; 36447194Sbostic } 36542299Sbostic 36647194Sbostic name: t = sp->fts_path + NAPPEND(p->fts_parent); 36747194Sbostic *t++ = '/'; 36866813Sbostic memmove(t, p->fts_name, p->fts_namelen + 1); 36952150Sbostic return (sp->fts_cur = p); 37039800Sbostic } 37139800Sbostic 37247220Sbostic /* Move up to the parent node. */ 37342299Sbostic p = tmp->fts_parent; 37447194Sbostic free(tmp); 37542299Sbostic 37647212Sbostic if (p->fts_level == FTS_ROOTPARENTLEVEL) { 37739800Sbostic /* 37845600Sbostic * Done; free everything up and set errno to 0 so the user 37939800Sbostic * can distinguish between error and EOF. 38039800Sbostic */ 38147194Sbostic free(p); 38239800Sbostic errno = 0; 38352150Sbostic return (sp->fts_cur = NULL); 38439800Sbostic } 38539800Sbostic 38653096Sbostic /* Nul terminate the pathname. */ 38742273Sbostic sp->fts_path[p->fts_pathlen] = '\0'; 38847194Sbostic 38947194Sbostic /* 39053096Sbostic * Return to the parent directory. If at a root node or came through 39153096Sbostic * a symlink, go back through the file descriptor. Otherwise, cd up 39253096Sbostic * one directory. 39347194Sbostic */ 39447220Sbostic if (p->fts_level == FTS_ROOTLEVEL) { 39553096Sbostic if (!ISSET(FTS_NOCHDIR) && FCHDIR(sp, sp->fts_rfd)) { 39647220Sbostic SET(FTS_STOP); 39752150Sbostic return (NULL); 39847220Sbostic } 39952290Sbostic } else if (p->fts_flags & FTS_SYMFOLLOW) { 40052290Sbostic if (FCHDIR(sp, p->fts_symfd)) { 40153032Sbostic saved_errno = errno; 40252290Sbostic (void)close(p->fts_symfd); 40353032Sbostic errno = saved_errno; 40452290Sbostic SET(FTS_STOP); 40552290Sbostic return (NULL); 40652290Sbostic } 40752290Sbostic (void)close(p->fts_symfd); 40852290Sbostic } else if (!(p->fts_flags & FTS_DONTCHDIR)) { 40952278Sbostic if (CHDIR(sp, "..")) { 41052278Sbostic SET(FTS_STOP); 41152278Sbostic return (NULL); 41252278Sbostic } 41352278Sbostic } 41452290Sbostic p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP; 41552150Sbostic return (sp->fts_cur = p); 41639800Sbostic } 41739800Sbostic 41839800Sbostic /* 41947220Sbostic * Fts_set takes the stream as an argument although it's not used in this 42039800Sbostic * implementation; it would be necessary if anyone wanted to add global 42145600Sbostic * semantics to fts using fts_set. An error return is allowed for similar 42245600Sbostic * reasons. 42339800Sbostic */ 42439800Sbostic /* ARGSUSED */ 42552209Sbostic int 42645600Sbostic fts_set(sp, p, instr) 42739800Sbostic FTS *sp; 42839800Sbostic FTSENT *p; 42939800Sbostic int instr; 43039800Sbostic { 43152770Sbostic if (instr && instr != FTS_AGAIN && instr != FTS_FOLLOW && 43252770Sbostic instr != FTS_NOINSTR && instr != FTS_SKIP) { 43352770Sbostic errno = EINVAL; 43452770Sbostic return (1); 43552770Sbostic } 43642273Sbostic p->fts_instr = instr; 43752150Sbostic return (0); 43839800Sbostic } 43939800Sbostic 44039800Sbostic FTSENT * 44152770Sbostic fts_children(sp, instr) 44239800Sbostic register FTS *sp; 44352770Sbostic int instr; 44439800Sbostic { 44542299Sbostic register FTSENT *p; 44642299Sbostic int fd; 44742299Sbostic 44852770Sbostic if (instr && instr != FTS_NAMEONLY) { 44952770Sbostic errno = EINVAL; 45052770Sbostic return (NULL); 45152770Sbostic } 45252770Sbostic 45345600Sbostic /* Set current node pointer. */ 45445600Sbostic p = sp->fts_cur; 45545600Sbostic 45639800Sbostic /* 45752160Sbostic * Errno set to 0 so user can distinguish empty directory from 45852160Sbostic * an error. 45939800Sbostic */ 46039800Sbostic errno = 0; 46152160Sbostic 46252160Sbostic /* Fatal errors stop here. */ 46352160Sbostic if (ISSET(FTS_STOP)) 46452150Sbostic return (NULL); 46545600Sbostic 46652160Sbostic /* Return logical hierarchy of user's arguments. */ 46752160Sbostic if (p->fts_info == FTS_INIT) 46852160Sbostic return (p->fts_link); 46952160Sbostic 47052290Sbostic /* 47152290Sbostic * If not a directory being visited in pre-order, stop here. Could 47252290Sbostic * allow FTS_DNR, assuming the user has fixed the problem, but the 47352290Sbostic * same effect is available with FTS_AGAIN. 47452290Sbostic */ 47552290Sbostic if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */) 47652160Sbostic return (NULL); 47752160Sbostic 47845600Sbostic /* Free up any previous child list. */ 47942273Sbostic if (sp->fts_child) 48042273Sbostic fts_lfree(sp->fts_child); 48142299Sbostic 48252770Sbostic if (instr == FTS_NAMEONLY) { 48352770Sbostic sp->fts_options |= FTS_NAMEONLY; 48452770Sbostic instr = BNAMES; 48552770Sbostic } else 48652770Sbostic instr = BCHILD; 48752770Sbostic 48842299Sbostic /* 48945600Sbostic * If using chdir on a relative path and called BEFORE fts_read does 49047194Sbostic * its chdir to the root of a traversal, we can lose -- we need to 49147194Sbostic * chdir into the subdirectory, and we don't know where the current 49247194Sbostic * directory is, so we can't get back so that the upcoming chdir by 49347194Sbostic * fts_read will work. 49442299Sbostic */ 49547212Sbostic if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' || 49645600Sbostic ISSET(FTS_NOCHDIR)) 49752770Sbostic return (sp->fts_child = fts_build(sp, instr)); 49842299Sbostic 49942299Sbostic if ((fd = open(".", O_RDONLY, 0)) < 0) 50052150Sbostic return (NULL); 50152770Sbostic sp->fts_child = fts_build(sp, instr); 50242299Sbostic if (fchdir(fd)) 50352150Sbostic return (NULL); 50442299Sbostic (void)close(fd); 50552150Sbostic return (sp->fts_child); 50639800Sbostic } 50739800Sbostic 50847194Sbostic /* 50947194Sbostic * This is the tricky part -- do not casually change *anything* in here. The 51047194Sbostic * idea is to build the linked list of entries that are used by fts_children 51147194Sbostic * and fts_read. There are lots of special cases. 51247194Sbostic * 51347194Sbostic * The real slowdown in walking the tree is the stat calls. If FTS_NOSTAT is 51447194Sbostic * set and it's a physical walk (so that symbolic links can't be directories), 51559307Sbostic * we can do things quickly. First, if it's a 4.4BSD file system, the type 51659307Sbostic * of the file is in the directory entry. Otherwise, we assume that the number 51759307Sbostic * of subdirectories in a node is equal to the number of links to the parent. 51859307Sbostic * The former skips all stat calls. The latter skips stat calls in any leaf 51959307Sbostic * directories and for any files after the subdirectories in the directory have 52059307Sbostic * been found, cutting the stat calls by about 2/3. 52147194Sbostic */ 52247155Sbostic static FTSENT * 52342299Sbostic fts_build(sp, type) 52439800Sbostic register FTS *sp; 52542299Sbostic int type; 52639800Sbostic { 52739800Sbostic register struct dirent *dp; 52839800Sbostic register FTSENT *p, *head; 52939800Sbostic register int nitems; 53052290Sbostic FTSENT *cur, *tail; 53139800Sbostic DIR *dirp; 53252209Sbostic void *adjaddr; 533*67575Spendry int cderrno, descend, len, level, maxlen, nlinks, oflag, saved_errno; 53439800Sbostic char *cp; 53539800Sbostic 53645600Sbostic /* Set current node pointer. */ 53747194Sbostic cur = sp->fts_cur; 53845600Sbostic 53947194Sbostic /* 54047194Sbostic * Open the directory for reading. If this fails, we're done. 54147194Sbostic * If being called from fts_read, set the fts_info field. 54247194Sbostic */ 543*67575Spendry #ifdef FTS_WHITEOUT 544*67575Spendry if (ISSET(FTS_WHITEOUT)) 545*67575Spendry oflag = DTF_NODUP|DTF_REWIND; 546*67575Spendry else 547*67575Spendry oflag = DTF_HIDEW|DTF_NODUP|DTF_REWIND; 548*67575Spendry #else 549*67575Spendry #define __opendir2(path, flag) opendir(path) 550*67575Spendry #endif 551*67575Spendry if ((dirp = __opendir2(cur->fts_accpath, oflag)) == NULL) { 55252206Sbostic if (type == BREAD) { 55347194Sbostic cur->fts_info = FTS_DNR; 55452206Sbostic cur->fts_errno = errno; 55552206Sbostic } 55652150Sbostic return (NULL); 55739800Sbostic } 55839800Sbostic 55939800Sbostic /* 56047194Sbostic * Nlinks is the number of possible entries of type directory in the 56147194Sbostic * directory if we're cheating on stat calls, 0 if we're not doing 56247194Sbostic * any stat calls at all, -1 if we're doing stats on everything. 56339800Sbostic */ 56452770Sbostic if (type == BNAMES) 56552770Sbostic nlinks = 0; 56652770Sbostic else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) 56752770Sbostic nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2); 56852770Sbostic else 56952770Sbostic nlinks = -1; 57039800Sbostic 57152290Sbostic #ifdef notdef 57252290Sbostic (void)printf("nlinks == %d (cur: %d)\n", nlinks, cur->fts_nlink); 57352290Sbostic (void)printf("NOSTAT %d PHYSICAL %d SEEDOT %d\n", 57452290Sbostic ISSET(FTS_NOSTAT), ISSET(FTS_PHYSICAL), ISSET(FTS_SEEDOT)); 57552290Sbostic #endif 57647194Sbostic /* 57747194Sbostic * If we're going to need to stat anything or we want to descend 57853379Sbostic * and stay in the directory, chdir. If this fails we keep going, 57953379Sbostic * but set a flag so we don't chdir after the post-order visit. 58047194Sbostic * We won't be able to stat anything, but we can still return the 58147194Sbostic * names themselves. Note, that since fts_read won't be able to 58247194Sbostic * chdir into the directory, it will have to return different path 58347194Sbostic * names than before, i.e. "a/b" instead of "b". Since the node 58447194Sbostic * has already been visited in pre-order, have to wait until the 58552290Sbostic * post-order visit to return the error. There is a special case 58652290Sbostic * here, if there was nothing to stat then it's not an error to 58752290Sbostic * not be able to stat. This is all fairly nasty. If a program 58852290Sbostic * needed sorted entries or stat information, they had better be 58952290Sbostic * checking FTS_NS on the returned nodes. 59047194Sbostic */ 59158532Sbostic cderrno = 0; 59245600Sbostic if (nlinks || type == BREAD) 59347194Sbostic if (FCHDIR(sp, dirfd(dirp))) { 59453380Sbostic if (nlinks && type == BREAD) 59552160Sbostic cur->fts_errno = errno; 59653380Sbostic cur->fts_flags |= FTS_DONTCHDIR; 59752290Sbostic descend = 0; 59852278Sbostic cderrno = errno; 59958532Sbostic } else 60045600Sbostic descend = 1; 60147194Sbostic else 60247194Sbostic descend = 0; 60339962Sbostic 60447194Sbostic /* 60547194Sbostic * Figure out the max file name length that can be stored in the 60647194Sbostic * current path -- the inner loop allocates more path as necessary. 60747194Sbostic * We really wouldn't have to do the maxlen calculations here, we 60847194Sbostic * could do them in fts_read before returning the path, but it's a 60947194Sbostic * lot easier here since the length is part of the dirent structure. 61047194Sbostic * 61152209Sbostic * If not changing directories set a pointer so that can just append 61252209Sbostic * each new name into the path. 61347194Sbostic */ 61447194Sbostic maxlen = sp->fts_pathlen - cur->fts_pathlen - 1; 61547194Sbostic len = NAPPEND(cur); 61647194Sbostic if (ISSET(FTS_NOCHDIR)) { 61747194Sbostic cp = sp->fts_path + len; 61847194Sbostic *cp++ = '/'; 61947194Sbostic } 62040939Sbostic 62147194Sbostic level = cur->fts_level + 1; 62240939Sbostic 62347194Sbostic /* Read the directory, attaching each entry to the `link' pointer. */ 62452209Sbostic adjaddr = NULL; 62552290Sbostic for (head = tail = NULL, nitems = 0; dp = readdir(dirp);) { 62647194Sbostic if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name)) 62739800Sbostic continue; 62839800Sbostic 62952155Sbostic if ((p = fts_alloc(sp, dp->d_name, (int)dp->d_namlen)) == NULL) 63039800Sbostic goto mem1; 63139800Sbostic if (dp->d_namlen > maxlen) { 63253801Sbostic if (fts_palloc(sp, (size_t)dp->d_namlen)) { 63347194Sbostic /* 63447194Sbostic * No more memory for path or structures. Save 63547194Sbostic * errno, free up the current structure and the 63647194Sbostic * structures already allocated. 63747194Sbostic */ 63847194Sbostic mem1: saved_errno = errno; 63947194Sbostic if (p) 64047194Sbostic free(p); 64147194Sbostic fts_lfree(head); 64247194Sbostic (void)closedir(dirp); 64339800Sbostic errno = saved_errno; 64447194Sbostic cur->fts_info = FTS_ERR; 64547194Sbostic SET(FTS_STOP); 64652150Sbostic return (NULL); 64739800Sbostic } 64852209Sbostic adjaddr = sp->fts_path; 64942273Sbostic maxlen = sp->fts_pathlen - sp->fts_cur->fts_pathlen - 1; 65039800Sbostic } 65139800Sbostic 65242273Sbostic p->fts_pathlen = len + dp->d_namlen + 1; 65342273Sbostic p->fts_parent = sp->fts_cur; 65442273Sbostic p->fts_level = level; 65539800Sbostic 656*67575Spendry #ifdef FTS_WHITEOUT 657*67575Spendry switch (dp->d_type) { 658*67575Spendry case DT_WHT: 659*67575Spendry p->fts_flags |= FTS_ISW; 660*67575Spendry break; 661*67575Spendry case DT_WHTD: 662*67575Spendry p->fts_flags |= FTS_ISWD; 663*67575Spendry break; 664*67575Spendry } 665*67575Spendry #endif 666*67575Spendry 66752290Sbostic if (cderrno) { 66852290Sbostic if (nlinks) { 66952290Sbostic p->fts_info = FTS_NS; 67052290Sbostic p->fts_errno = cderrno; 67152290Sbostic } else 67252290Sbostic p->fts_info = FTS_NSOK; 67352290Sbostic p->fts_accpath = cur->fts_accpath; 67459307Sbostic } else if (nlinks == 0 67559307Sbostic #ifdef DT_DIR 67659307Sbostic || nlinks > 0 && 67759307Sbostic dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN 67859307Sbostic #endif 67959307Sbostic ) { 68059307Sbostic p->fts_accpath = 68159307Sbostic ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name; 68259307Sbostic p->fts_info = FTS_NSOK; 68359307Sbostic } else { 68447194Sbostic /* Build a file name for fts_stat to stat. */ 68547194Sbostic if (ISSET(FTS_NOCHDIR)) { 68647194Sbostic p->fts_accpath = p->fts_path; 68766813Sbostic memmove(cp, p->fts_name, p->fts_namelen + 1); 68847194Sbostic } else 68947194Sbostic p->fts_accpath = p->fts_name; 69059307Sbostic /* Stat it. */ 69145600Sbostic p->fts_info = fts_stat(sp, p, 0); 69259307Sbostic 69359307Sbostic /* Decrement link count if applicable. */ 69452209Sbostic if (nlinks > 0 && (p->fts_info == FTS_D || 69552209Sbostic p->fts_info == FTS_DC || p->fts_info == FTS_DOT)) 69639800Sbostic --nlinks; 69747194Sbostic } 69839800Sbostic 69952290Sbostic /* We walk in directory order so "ls -f" doesn't get upset. */ 70052290Sbostic p->fts_link = NULL; 70152290Sbostic if (head == NULL) 70252290Sbostic head = tail = p; 70352290Sbostic else { 70452290Sbostic tail->fts_link = p; 70552290Sbostic tail = p; 70652290Sbostic } 70739800Sbostic ++nitems; 70839800Sbostic } 70939800Sbostic (void)closedir(dirp); 71039800Sbostic 71147194Sbostic /* 71252209Sbostic * If had to realloc the path, adjust the addresses for the rest 71352209Sbostic * of the tree. 71452209Sbostic */ 71552209Sbostic if (adjaddr) 71652209Sbostic fts_padjust(sp, adjaddr); 71752209Sbostic 71852209Sbostic /* 71947194Sbostic * If not changing directories, reset the path back to original 72047194Sbostic * state. 72147194Sbostic */ 72247194Sbostic if (ISSET(FTS_NOCHDIR)) { 72347194Sbostic if (cp - 1 > sp->fts_path) 72447194Sbostic --cp; 72547194Sbostic *cp = '\0'; 72647194Sbostic } 72742299Sbostic 72842299Sbostic /* 72966811Sbostic * If descended after called from fts_children or after called from 73066811Sbostic * fts_read and nothing found, get back. At the root level we use 73166811Sbostic * the saved fd; if one of fts_open()'s arguments is a relative path 73266811Sbostic * to an empty directory, we wind up here with no other way back. If 73366811Sbostic * can't get back, we're done. 73442299Sbostic */ 73566811Sbostic if (descend && (type == BCHILD || !nitems) && 73666811Sbostic (cur->fts_level == FTS_ROOTLEVEL ? 73766811Sbostic FCHDIR(sp, sp->fts_rfd) : CHDIR(sp, ".."))) { 73847194Sbostic cur->fts_info = FTS_ERR; 73947194Sbostic SET(FTS_STOP); 74052150Sbostic return (NULL); 74139962Sbostic } 74239962Sbostic 74353032Sbostic /* If didn't find anything, return NULL. */ 74453096Sbostic if (!nitems) { 74553096Sbostic if (type == BREAD) 74653096Sbostic cur->fts_info = FTS_DP; 74752150Sbostic return (NULL); 74853096Sbostic } 74939800Sbostic 75047194Sbostic /* Sort the entries. */ 75142273Sbostic if (sp->fts_compar && nitems > 1) 75245600Sbostic head = fts_sort(sp, head, nitems); 75352150Sbostic return (head); 75439800Sbostic } 75539800Sbostic 75645600Sbostic static u_short 75745600Sbostic fts_stat(sp, p, follow) 75845600Sbostic FTS *sp; 75939800Sbostic register FTSENT *p; 76045600Sbostic int follow; 76139800Sbostic { 76252160Sbostic register FTSENT *t; 76352160Sbostic register dev_t dev; 76452160Sbostic register ino_t ino; 76552209Sbostic struct stat *sbp, sb; 76647194Sbostic int saved_errno; 76747194Sbostic 76852209Sbostic /* If user needs stat info, stat buffer already allocated. */ 76952209Sbostic sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp; 770*67575Spendry 771*67575Spendry #ifdef FTS_WHITEOUT 772*67575Spendry /* check for whiteout */ 773*67575Spendry if (p->fts_flags & (FTS_ISW|FTS_ISWD)) { 774*67575Spendry if (sbp != &sb) { 775*67575Spendry memset(sbp, '\0', sizeof (*sbp)); 776*67575Spendry sbp->st_mode = S_IFWHT; 777*67575Spendry } 778*67575Spendry return (FTS_W); 779*67575Spendry } 780*67575Spendry #endif 78152209Sbostic 78239800Sbostic /* 78345600Sbostic * If doing a logical walk, or application requested FTS_FOLLOW, do 78447194Sbostic * a stat(2). If that fails, check for a non-existent symlink. If 78552160Sbostic * fail, set the errno from the stat call. 78639800Sbostic */ 78745600Sbostic if (ISSET(FTS_LOGICAL) || follow) { 78852209Sbostic if (stat(p->fts_accpath, sbp)) { 78947194Sbostic saved_errno = errno; 79052209Sbostic if (!lstat(p->fts_accpath, sbp)) { 79147194Sbostic errno = 0; 79252150Sbostic return (FTS_SLNONE); 79347194Sbostic } 79452160Sbostic p->fts_errno = saved_errno; 79552209Sbostic goto err; 79645600Sbostic } 79752209Sbostic } else if (lstat(p->fts_accpath, sbp)) { 79852160Sbostic p->fts_errno = errno; 79966813Sbostic err: memset(sbp, 0, sizeof(struct stat)); 80052150Sbostic return (FTS_NS); 80145600Sbostic } 80239800Sbostic 80352209Sbostic if (S_ISDIR(sbp->st_mode)) { 80452209Sbostic /* 80552209Sbostic * Set the device/inode. Used to find cycles and check for 80652209Sbostic * crossing mount points. Also remember the link count, used 80752209Sbostic * in fts_build to limit the number of stat calls. It is 80852209Sbostic * understood that these fields are only referenced if fts_info 80952209Sbostic * is set to FTS_D. 81052209Sbostic */ 81152209Sbostic dev = p->fts_dev = sbp->st_dev; 81252209Sbostic ino = p->fts_ino = sbp->st_ino; 81352209Sbostic p->fts_nlink = sbp->st_nlink; 81452209Sbostic 81552290Sbostic if (ISDOT(p->fts_name)) 81652290Sbostic return (FTS_DOT); 81752290Sbostic 81852209Sbostic /* 81952209Sbostic * Cycle detection is done by brute force when the directory 82052209Sbostic * is first encountered. If the tree gets deep enough or the 82152209Sbostic * number of symbolic links to directories is high enough, 82252209Sbostic * something faster might be worthwhile. 82352209Sbostic */ 82452209Sbostic for (t = p->fts_parent; 82552209Sbostic t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent) 82652209Sbostic if (ino == t->fts_ino && dev == t->fts_dev) { 82752073Sbostic p->fts_cycle = t; 82852150Sbostic return (FTS_DC); 82947194Sbostic } 83052150Sbostic return (FTS_D); 83147194Sbostic } 83252209Sbostic if (S_ISLNK(sbp->st_mode)) 83352150Sbostic return (FTS_SL); 83452209Sbostic if (S_ISREG(sbp->st_mode)) 83552150Sbostic return (FTS_F); 83652150Sbostic return (FTS_DEFAULT); 83739800Sbostic } 83839800Sbostic 83939800Sbostic static FTSENT * 84045600Sbostic fts_sort(sp, head, nitems) 84145600Sbostic FTS *sp; 84239800Sbostic FTSENT *head; 84339800Sbostic register int nitems; 84439800Sbostic { 84539800Sbostic register FTSENT **ap, *p; 84639800Sbostic 84739800Sbostic /* 84845600Sbostic * Construct an array of pointers to the structures and call qsort(3). 84939800Sbostic * Reassemble the array in the order returned by qsort. If unable to 85039800Sbostic * sort for memory reasons, return the directory entries in their 85139800Sbostic * current order. Allocate enough space for the current needs plus 85252209Sbostic * 40 so don't realloc one entry at a time. 85339800Sbostic */ 85445600Sbostic if (nitems > sp->fts_nitems) { 85545600Sbostic sp->fts_nitems = nitems + 40; 85652209Sbostic if ((sp->fts_array = realloc(sp->fts_array, 85752209Sbostic (size_t)(sp->fts_nitems * sizeof(FTSENT *)))) == NULL) { 85845600Sbostic sp->fts_nitems = 0; 85952150Sbostic return (head); 86039800Sbostic } 86139800Sbostic } 86245600Sbostic for (ap = sp->fts_array, p = head; p; p = p->fts_link) 86339800Sbostic *ap++ = p; 86445600Sbostic qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *), sp->fts_compar); 86545600Sbostic for (head = *(ap = sp->fts_array); --nitems; ++ap) 86642273Sbostic ap[0]->fts_link = ap[1]; 86742273Sbostic ap[0]->fts_link = NULL; 86852150Sbostic return (head); 86939800Sbostic } 87039800Sbostic 87139800Sbostic static FTSENT * 87253801Sbostic fts_alloc(sp, name, namelen) 87345600Sbostic FTS *sp; 87439800Sbostic char *name; 87553801Sbostic register int namelen; 87639800Sbostic { 87739800Sbostic register FTSENT *p; 87853801Sbostic size_t len; 87939800Sbostic 88039800Sbostic /* 88153801Sbostic * The file name is a variable length array and no stat structure is 88253801Sbostic * necessary if the user has set the nostat bit. Allocate the FTSENT 88353801Sbostic * structure, the file name and the stat structure in one chunk, but 88453801Sbostic * be careful that the stat structure is reasonably aligned. Since the 88553801Sbostic * fts_name field is declared to be of size 1, the fts_name pointer is 88653801Sbostic * namelen + 2 before the first possible address of the stat structure. 88739800Sbostic */ 88853801Sbostic len = sizeof(FTSENT) + namelen; 88953801Sbostic if (!ISSET(FTS_NOSTAT)) 89053801Sbostic len += sizeof(struct stat) + ALIGNBYTES; 89153801Sbostic if ((p = malloc(len)) == NULL) 89252150Sbostic return (NULL); 89353801Sbostic 89453801Sbostic /* Copy the name plus the trailing NULL. */ 89566813Sbostic memmove(p->fts_name, name, namelen + 1); 89653801Sbostic 89753801Sbostic if (!ISSET(FTS_NOSTAT)) 89853801Sbostic p->fts_statp = (struct stat *)ALIGN(p->fts_name + namelen + 2); 89953801Sbostic p->fts_namelen = namelen; 90045600Sbostic p->fts_path = sp->fts_path; 90152290Sbostic p->fts_errno = 0; 90252290Sbostic p->fts_flags = 0; 90347194Sbostic p->fts_instr = FTS_NOINSTR; 90445600Sbostic p->fts_number = 0; 90545600Sbostic p->fts_pointer = NULL; 90652150Sbostic return (p); 90739800Sbostic } 90839800Sbostic 90945600Sbostic static void 91039800Sbostic fts_lfree(head) 91139800Sbostic register FTSENT *head; 91239800Sbostic { 91339800Sbostic register FTSENT *p; 91439800Sbostic 91545600Sbostic /* Free a linked list of structures. */ 91639800Sbostic while (p = head) { 91742273Sbostic head = head->fts_link; 91847194Sbostic free(p); 91939800Sbostic } 92039800Sbostic } 92139800Sbostic 92239800Sbostic /* 92352209Sbostic * Allow essentially unlimited paths; find, rm, ls should all work on any tree. 92452209Sbostic * Most systems will allow creation of paths much longer than MAXPATHLEN, even 92552209Sbostic * though the kernel won't resolve them. Add the size (not just what's needed) 92652209Sbostic * plus 256 bytes so don't realloc the path 2 bytes at a time. 92739800Sbostic */ 92852209Sbostic static int 92952209Sbostic fts_palloc(sp, more) 93045600Sbostic FTS *sp; 93153801Sbostic size_t more; 93239800Sbostic { 93352209Sbostic sp->fts_pathlen += more + 256; 93452209Sbostic sp->fts_path = realloc(sp->fts_path, (size_t)sp->fts_pathlen); 93552209Sbostic return (sp->fts_path == NULL); 93647155Sbostic } 93752209Sbostic 93852209Sbostic /* 93952209Sbostic * When the path is realloc'd, have to fix all of the pointers in structures 94052209Sbostic * already returned. 94152209Sbostic */ 94252209Sbostic static void 94352209Sbostic fts_padjust(sp, addr) 94452209Sbostic FTS *sp; 94552209Sbostic void *addr; 94652209Sbostic { 94752209Sbostic FTSENT *p; 94852209Sbostic 94955557Sbostic #define ADJUST(p) { \ 95065345Sbostic (p)->fts_accpath = \ 95165345Sbostic (char *)addr + ((p)->fts_accpath - (p)->fts_path); \ 95255557Sbostic (p)->fts_path = addr; \ 95352209Sbostic } 95452209Sbostic /* Adjust the current set of children. */ 95552209Sbostic for (p = sp->fts_child; p; p = p->fts_link) 95652209Sbostic ADJUST(p); 95752209Sbostic 95852209Sbostic /* Adjust the rest of the tree. */ 95952209Sbostic for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) { 96052209Sbostic ADJUST(p); 96152209Sbostic p = p->fts_link ? p->fts_link : p->fts_parent; 96252209Sbostic } 96352209Sbostic } 96455557Sbostic 96555557Sbostic static size_t 96655557Sbostic fts_maxarglen(argv) 96755557Sbostic char * const *argv; 96855557Sbostic { 96955557Sbostic size_t len, max; 97055557Sbostic 97155557Sbostic for (max = 0; *argv; ++argv) 97255557Sbostic if ((len = strlen(*argv)) > max) 97355557Sbostic max = len; 97455557Sbostic return (max); 97555557Sbostic } 976