Annotation of mandoc/compat_fts.c, Revision 1.1
1.1 ! schwarze 1: #include "config.h"
! 2:
! 3: #ifdef HAVE_FTS
! 4:
! 5: int dummy;
! 6:
! 7: #else
! 8:
! 9: /* $OpenBSD: fts.c,v 1.46 2014/05/25 17:47:04 tedu Exp $ */
! 10:
! 11: /*-
! 12: * Copyright (c) 1990, 1993, 1994
! 13: * The Regents of the University of California. All rights reserved.
! 14: *
! 15: * Redistribution and use in source and binary forms, with or without
! 16: * modification, are permitted provided that the following conditions
! 17: * are met:
! 18: * 1. Redistributions of source code must retain the above copyright
! 19: * notice, this list of conditions and the following disclaimer.
! 20: * 2. Redistributions in binary form must reproduce the above copyright
! 21: * notice, this list of conditions and the following disclaimer in the
! 22: * documentation and/or other materials provided with the distribution.
! 23: * 3. Neither the name of the University nor the names of its contributors
! 24: * may be used to endorse or promote products derived from this software
! 25: * without specific prior written permission.
! 26: *
! 27: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
! 28: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 29: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 30: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
! 31: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 32: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 33: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 34: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 35: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 36: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 37: * SUCH DAMAGE.
! 38: */
! 39:
! 40: #include <sys/param.h>
! 41: #include <sys/stat.h>
! 42: #include <sys/types.h>
! 43:
! 44: #include <dirent.h>
! 45: #include <errno.h>
! 46: #include <fcntl.h>
! 47: #include <limits.h>
! 48: #include <stdlib.h>
! 49: #include <string.h>
! 50: #include <unistd.h>
! 51: #include "compat_fts.h"
! 52:
! 53: static FTSENT *fts_alloc(FTS *, const char *, size_t);
! 54: static FTSENT *fts_build(FTS *);
! 55: static void fts_lfree(FTSENT *);
! 56: static void fts_load(FTS *, FTSENT *);
! 57: static size_t fts_maxarglen(char * const *);
! 58: static void fts_padjust(FTS *, FTSENT *);
! 59: static int fts_palloc(FTS *, size_t);
! 60: static unsigned short fts_stat(FTS *, FTSENT *);
! 61: static int fts_safe_changedir(FTS *, FTSENT *, int, const char *);
! 62:
! 63: #define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))
! 64:
! 65: #define CLR(opt) (sp->fts_options &= ~(opt))
! 66: #define ISSET(opt) (sp->fts_options & (opt))
! 67: #define SET(opt) (sp->fts_options |= (opt))
! 68:
! 69: #define FCHDIR(sp, fd) (!ISSET(FTS_NOCHDIR) && fchdir(fd))
! 70:
! 71: FTS *
! 72: fts_open(char * const *argv, int options, void *dummy)
! 73: {
! 74: FTS *sp;
! 75: FTSENT *p, *root;
! 76: int nitems;
! 77: FTSENT *parent, *tmp;
! 78: size_t len;
! 79:
! 80: /* Options check. */
! 81: if (options & ~FTS_OPTIONMASK) {
! 82: errno = EINVAL;
! 83: return (NULL);
! 84: }
! 85:
! 86: /* Allocate/initialize the stream */
! 87: if ((sp = calloc(1, sizeof(FTS))) == NULL)
! 88: return (NULL);
! 89: sp->fts_options = options;
! 90:
! 91: /*
! 92: * Start out with 1K of path space, and enough, in any case,
! 93: * to hold the user's paths.
! 94: */
! 95: if (fts_palloc(sp, MAX(fts_maxarglen(argv), PATH_MAX)))
! 96: goto mem1;
! 97:
! 98: /* Allocate/initialize root's parent. */
! 99: if ((parent = fts_alloc(sp, "", 0)) == NULL)
! 100: goto mem2;
! 101: parent->fts_level = FTS_ROOTPARENTLEVEL;
! 102:
! 103: /* Allocate/initialize root(s). */
! 104: for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) {
! 105: /* Don't allow zero-length paths. */
! 106: if ((len = strlen(*argv)) == 0) {
! 107: errno = ENOENT;
! 108: goto mem3;
! 109: }
! 110:
! 111: if ((p = fts_alloc(sp, *argv, len)) == NULL)
! 112: goto mem3;
! 113: p->fts_level = FTS_ROOTLEVEL;
! 114: p->fts_parent = parent;
! 115: p->fts_accpath = p->fts_name;
! 116: p->fts_info = fts_stat(sp, p);
! 117:
! 118: /* Command-line "." and ".." are real directories. */
! 119: if (p->fts_info == FTS_DOT)
! 120: p->fts_info = FTS_D;
! 121:
! 122: p->fts_link = NULL;
! 123: if (root == NULL)
! 124: tmp = root = p;
! 125: else {
! 126: tmp->fts_link = p;
! 127: tmp = p;
! 128: }
! 129: }
! 130:
! 131: /*
! 132: * Allocate a dummy pointer and make fts_read think that we've just
! 133: * finished the node before the root(s); set p->fts_info to FTS_INIT
! 134: * so that everything about the "current" node is ignored.
! 135: */
! 136: if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL)
! 137: goto mem3;
! 138: sp->fts_cur->fts_link = root;
! 139: sp->fts_cur->fts_info = FTS_INIT;
! 140:
! 141: /*
! 142: * If using chdir(2), grab a file descriptor pointing to dot to ensure
! 143: * that we can get back here; this could be avoided for some paths,
! 144: * but almost certainly not worth the effort. Slashes, symbolic links,
! 145: * and ".." are all fairly nasty problems. Note, if we can't get the
! 146: * descriptor we run anyway, just more slowly.
! 147: */
! 148: if (!ISSET(FTS_NOCHDIR) && (sp->fts_rfd = open(".", O_RDONLY, 0)) < 0)
! 149: SET(FTS_NOCHDIR);
! 150:
! 151: if (nitems == 0)
! 152: free(parent);
! 153:
! 154: return (sp);
! 155:
! 156: mem3: fts_lfree(root);
! 157: free(parent);
! 158: mem2: free(sp->fts_path);
! 159: mem1: free(sp);
! 160: return (NULL);
! 161: }
! 162:
! 163: static void
! 164: fts_load(FTS *sp, FTSENT *p)
! 165: {
! 166: size_t len;
! 167: char *cp;
! 168:
! 169: /*
! 170: * Load the stream structure for the next traversal. Since we don't
! 171: * actually enter the directory until after the preorder visit, set
! 172: * the fts_accpath field specially so the chdir gets done to the right
! 173: * place and the user can access the first node. From fts_open it's
! 174: * known that the path will fit.
! 175: */
! 176: len = p->fts_pathlen = p->fts_namelen;
! 177: memmove(sp->fts_path, p->fts_name, len + 1);
! 178: if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) {
! 179: len = strlen(++cp);
! 180: memmove(p->fts_name, cp, len + 1);
! 181: p->fts_namelen = len;
! 182: }
! 183: p->fts_accpath = p->fts_path = sp->fts_path;
! 184: sp->fts_dev = p->fts_dev;
! 185: }
! 186:
! 187: int
! 188: fts_close(FTS *sp)
! 189: {
! 190: FTSENT *freep, *p;
! 191: int rfd, error = 0;
! 192:
! 193: /*
! 194: * This still works if we haven't read anything -- the dummy structure
! 195: * points to the root list, so we step through to the end of the root
! 196: * list which has a valid parent pointer.
! 197: */
! 198: if (sp->fts_cur) {
! 199: for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {
! 200: freep = p;
! 201: p = p->fts_link ? p->fts_link : p->fts_parent;
! 202: free(freep);
! 203: }
! 204: free(p);
! 205: }
! 206:
! 207: /* Stash the original directory fd if needed. */
! 208: rfd = ISSET(FTS_NOCHDIR) ? -1 : sp->fts_rfd;
! 209:
! 210: /* Free up child linked list, sort array, path buffer, stream ptr.*/
! 211: if (sp->fts_child)
! 212: fts_lfree(sp->fts_child);
! 213: free(sp->fts_path);
! 214: free(sp);
! 215:
! 216: /* Return to original directory, checking for error. */
! 217: if (rfd != -1) {
! 218: int saved_errno;
! 219: error = fchdir(rfd);
! 220: saved_errno = errno;
! 221: (void)close(rfd);
! 222: errno = saved_errno;
! 223: }
! 224:
! 225: return (error);
! 226: }
! 227:
! 228: /*
! 229: * Special case of "/" at the end of the path so that slashes aren't
! 230: * appended which would cause paths to be written as "....//foo".
! 231: */
! 232: #define NAPPEND(p) \
! 233: (p->fts_path[p->fts_pathlen - 1] == '/' \
! 234: ? p->fts_pathlen - 1 : p->fts_pathlen)
! 235:
! 236: FTSENT *
! 237: fts_read(FTS *sp)
! 238: {
! 239: FTSENT *p, *tmp;
! 240: int instr;
! 241: char *t;
! 242:
! 243: /* If finished or unrecoverable error, return NULL. */
! 244: if (sp->fts_cur == NULL || ISSET(FTS_STOP))
! 245: return (NULL);
! 246:
! 247: /* Set current node pointer. */
! 248: p = sp->fts_cur;
! 249:
! 250: /* Save and zero out user instructions. */
! 251: instr = p->fts_instr;
! 252: p->fts_instr = FTS_NOINSTR;
! 253:
! 254: /* Directory in pre-order. */
! 255: if (p->fts_info == FTS_D) {
! 256: /* If skipped or crossed mount point, do post-order visit. */
! 257: if (instr == FTS_SKIP ||
! 258: (ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev)) {
! 259: if (sp->fts_child) {
! 260: fts_lfree(sp->fts_child);
! 261: sp->fts_child = NULL;
! 262: }
! 263: p->fts_info = FTS_DP;
! 264: return (p);
! 265: }
! 266:
! 267: /*
! 268: * Cd to the subdirectory.
! 269: *
! 270: * If have already read and now fail to chdir, whack the list
! 271: * to make the names come out right, and set the parent errno
! 272: * so the application will eventually get an error condition.
! 273: * Set the FTS_DONTCHDIR flag so that when we logically change
! 274: * directories back to the parent we don't do a chdir.
! 275: *
! 276: * If haven't read do so. If the read fails, fts_build sets
! 277: * FTS_STOP or the fts_info field of the node.
! 278: */
! 279: if (sp->fts_child) {
! 280: if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) {
! 281: p->fts_errno = errno;
! 282: p->fts_flags |= FTS_DONTCHDIR;
! 283: for (p = sp->fts_child; p; p = p->fts_link)
! 284: p->fts_accpath =
! 285: p->fts_parent->fts_accpath;
! 286: }
! 287: } else if ((sp->fts_child = fts_build(sp)) == NULL) {
! 288: if (ISSET(FTS_STOP))
! 289: return (NULL);
! 290: return (p);
! 291: }
! 292: p = sp->fts_child;
! 293: sp->fts_child = NULL;
! 294: goto name;
! 295: }
! 296:
! 297: /* Move to the next node on this level. */
! 298: next: tmp = p;
! 299: if ((p = p->fts_link)) {
! 300: free(tmp);
! 301:
! 302: /*
! 303: * If reached the top, return to the original directory (or
! 304: * the root of the tree), and load the paths for the next root.
! 305: */
! 306: if (p->fts_level == FTS_ROOTLEVEL) {
! 307: if (FCHDIR(sp, sp->fts_rfd)) {
! 308: SET(FTS_STOP);
! 309: return (NULL);
! 310: }
! 311: fts_load(sp, p);
! 312: return (sp->fts_cur = p);
! 313: }
! 314:
! 315: /*
! 316: * User may have called fts_set on the node. If skipped,
! 317: * ignore. If followed, get a file descriptor so we can
! 318: * get back if necessary.
! 319: */
! 320: if (p->fts_instr == FTS_SKIP)
! 321: goto next;
! 322:
! 323: name: t = sp->fts_path + NAPPEND(p->fts_parent);
! 324: *t++ = '/';
! 325: memmove(t, p->fts_name, p->fts_namelen + 1);
! 326: return (sp->fts_cur = p);
! 327: }
! 328:
! 329: /* Move up to the parent node. */
! 330: p = tmp->fts_parent;
! 331: free(tmp);
! 332:
! 333: if (p->fts_level == FTS_ROOTPARENTLEVEL) {
! 334: /*
! 335: * Done; free everything up and set errno to 0 so the user
! 336: * can distinguish between error and EOF.
! 337: */
! 338: free(p);
! 339: errno = 0;
! 340: return (sp->fts_cur = NULL);
! 341: }
! 342:
! 343: /* NUL terminate the pathname. */
! 344: sp->fts_path[p->fts_pathlen] = '\0';
! 345:
! 346: /*
! 347: * Return to the parent directory. If at a root node or came through
! 348: * a symlink, go back through the file descriptor. Otherwise, cd up
! 349: * one directory.
! 350: */
! 351: if (p->fts_level == FTS_ROOTLEVEL) {
! 352: if (FCHDIR(sp, sp->fts_rfd)) {
! 353: SET(FTS_STOP);
! 354: sp->fts_cur = p;
! 355: return (NULL);
! 356: }
! 357: } else if (!(p->fts_flags & FTS_DONTCHDIR) &&
! 358: fts_safe_changedir(sp, p->fts_parent, -1, "..")) {
! 359: SET(FTS_STOP);
! 360: sp->fts_cur = p;
! 361: return (NULL);
! 362: }
! 363: p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;
! 364: return (sp->fts_cur = p);
! 365: }
! 366:
! 367: /*
! 368: * Fts_set takes the stream as an argument although it's not used in this
! 369: * implementation; it would be necessary if anyone wanted to add global
! 370: * semantics to fts using fts_set. An error return is allowed for similar
! 371: * reasons.
! 372: */
! 373: /* ARGSUSED */
! 374: int
! 375: fts_set(FTS *sp, FTSENT *p, int instr)
! 376: {
! 377: if (instr && instr != FTS_NOINSTR && instr != FTS_SKIP) {
! 378: errno = EINVAL;
! 379: return (1);
! 380: }
! 381: p->fts_instr = instr;
! 382: return (0);
! 383: }
! 384:
! 385: /*
! 386: * This is the tricky part -- do not casually change *anything* in here. The
! 387: * idea is to build the linked list of entries that are used by fts_children
! 388: * and fts_read. There are lots of special cases.
! 389: *
! 390: * The real slowdown in walking the tree is the stat calls. If FTS_NOSTAT is
! 391: * set and it's a physical walk (so that symbolic links can't be directories),
! 392: * we can do things quickly. First, if it's a 4.4BSD file system, the type
! 393: * of the file is in the directory entry. Otherwise, we assume that the number
! 394: * of subdirectories in a node is equal to the number of links to the parent.
! 395: * The former skips all stat calls. The latter skips stat calls in any leaf
! 396: * directories and for any files after the subdirectories in the directory have
! 397: * been found, cutting the stat calls by about 2/3.
! 398: */
! 399: static FTSENT *
! 400: fts_build(FTS *sp)
! 401: {
! 402: struct dirent *dp;
! 403: FTSENT *p, *head;
! 404: FTSENT *cur, *tail;
! 405: DIR *dirp;
! 406: void *oldaddr;
! 407: size_t len, maxlen;
! 408: int nitems, cderrno, descend, level, nlinks, nostat, doadjust;
! 409: int saved_errno;
! 410: char *cp;
! 411:
! 412: /* Set current node pointer. */
! 413: cur = sp->fts_cur;
! 414:
! 415: /*
! 416: * Open the directory for reading. If this fails, we're done.
! 417: * If being called from fts_read, set the fts_info field.
! 418: */
! 419: if ((dirp = opendir(cur->fts_accpath)) == NULL) {
! 420: cur->fts_info = FTS_DNR;
! 421: cur->fts_errno = errno;
! 422: return (NULL);
! 423: }
! 424:
! 425: /*
! 426: * Nlinks is the number of possible entries of type directory in the
! 427: * directory if we're cheating on stat calls, 0 if we're not doing
! 428: * any stat calls at all, -1 if we're doing stats on everything.
! 429: */
! 430: nlinks = -1;
! 431: nostat = 0;
! 432:
! 433: /*
! 434: * If we're going to need to stat anything or we want to descend
! 435: * and stay in the directory, chdir. If this fails we keep going,
! 436: * but set a flag so we don't chdir after the post-order visit.
! 437: * We won't be able to stat anything, but we can still return the
! 438: * names themselves. Note, that since fts_read won't be able to
! 439: * chdir into the directory, it will have to return different path
! 440: * names than before, i.e. "a/b" instead of "b". Since the node
! 441: * has already been visited in pre-order, have to wait until the
! 442: * post-order visit to return the error. There is a special case
! 443: * here, if there was nothing to stat then it's not an error to
! 444: * not be able to stat. This is all fairly nasty. If a program
! 445: * needed sorted entries or stat information, they had better be
! 446: * checking FTS_NS on the returned nodes.
! 447: */
! 448: cderrno = 0;
! 449: if (fts_safe_changedir(sp, cur, dirfd(dirp), NULL)) {
! 450: if (nlinks)
! 451: cur->fts_errno = errno;
! 452: cur->fts_flags |= FTS_DONTCHDIR;
! 453: descend = 0;
! 454: cderrno = errno;
! 455: (void)closedir(dirp);
! 456: dirp = NULL;
! 457: } else
! 458: descend = 1;
! 459:
! 460: /*
! 461: * Figure out the max file name length that can be stored in the
! 462: * current path -- the inner loop allocates more path as necessary.
! 463: * We really wouldn't have to do the maxlen calculations here, we
! 464: * could do them in fts_read before returning the path, but it's a
! 465: * lot easier here since the length is part of the dirent structure.
! 466: *
! 467: * If not changing directories set a pointer so that can just append
! 468: * each new name into the path.
! 469: */
! 470: len = NAPPEND(cur);
! 471: if (ISSET(FTS_NOCHDIR)) {
! 472: cp = sp->fts_path + len;
! 473: *cp++ = '/';
! 474: }
! 475: len++;
! 476: maxlen = sp->fts_pathlen - len;
! 477:
! 478: /*
! 479: * fts_level is signed so we must prevent it from wrapping
! 480: * around to FTS_ROOTLEVEL and FTS_ROOTPARENTLEVEL.
! 481: */
! 482: level = cur->fts_level;
! 483: if (level < FTS_MAXLEVEL)
! 484: level++;
! 485:
! 486: /* Read the directory, attaching each entry to the `link' pointer. */
! 487: doadjust = 0;
! 488: for (head = tail = NULL, nitems = 0; dirp && (dp = readdir(dirp));) {
! 489: if (ISDOT(dp->d_name))
! 490: continue;
! 491:
! 492: if (!(p = fts_alloc(sp, dp->d_name, (size_t)dp->d_namlen)))
! 493: goto mem1;
! 494: if (dp->d_namlen >= maxlen) { /* include space for NUL */
! 495: oldaddr = sp->fts_path;
! 496: if (fts_palloc(sp, dp->d_namlen +len + 1)) {
! 497: /*
! 498: * No more memory for path or structures. Save
! 499: * errno, free up the current structure and the
! 500: * structures already allocated.
! 501: */
! 502: mem1: saved_errno = errno;
! 503: if (p)
! 504: free(p);
! 505: fts_lfree(head);
! 506: (void)closedir(dirp);
! 507: cur->fts_info = FTS_ERR;
! 508: SET(FTS_STOP);
! 509: errno = saved_errno;
! 510: return (NULL);
! 511: }
! 512: /* Did realloc() change the pointer? */
! 513: if (oldaddr != sp->fts_path) {
! 514: doadjust = 1;
! 515: if (ISSET(FTS_NOCHDIR))
! 516: cp = sp->fts_path + len;
! 517: }
! 518: maxlen = sp->fts_pathlen - len;
! 519: }
! 520:
! 521: p->fts_level = level;
! 522: p->fts_parent = sp->fts_cur;
! 523: p->fts_pathlen = len + dp->d_namlen;
! 524: if (p->fts_pathlen < len) {
! 525: /*
! 526: * If we wrap, free up the current structure and
! 527: * the structures already allocated, then error
! 528: * out with ENAMETOOLONG.
! 529: */
! 530: free(p);
! 531: fts_lfree(head);
! 532: (void)closedir(dirp);
! 533: cur->fts_info = FTS_ERR;
! 534: SET(FTS_STOP);
! 535: errno = ENAMETOOLONG;
! 536: return (NULL);
! 537: }
! 538:
! 539: if (cderrno) {
! 540: if (nlinks) {
! 541: p->fts_info = FTS_NS;
! 542: p->fts_errno = cderrno;
! 543: } else
! 544: p->fts_info = FTS_NSOK;
! 545: p->fts_accpath = cur->fts_accpath;
! 546: } else if (nlinks == 0
! 547: #ifdef DT_DIR
! 548: || (nostat &&
! 549: dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN)
! 550: #endif
! 551: ) {
! 552: p->fts_accpath =
! 553: ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name;
! 554: p->fts_info = FTS_NSOK;
! 555: } else {
! 556: /* Build a file name for fts_stat to stat. */
! 557: if (ISSET(FTS_NOCHDIR)) {
! 558: p->fts_accpath = p->fts_path;
! 559: memmove(cp, p->fts_name, p->fts_namelen + 1);
! 560: } else
! 561: p->fts_accpath = p->fts_name;
! 562: /* Stat it. */
! 563: p->fts_info = fts_stat(sp, p);
! 564:
! 565: /* Decrement link count if applicable. */
! 566: if (nlinks > 0 && (p->fts_info == FTS_D ||
! 567: p->fts_info == FTS_DC || p->fts_info == FTS_DOT))
! 568: --nlinks;
! 569: }
! 570:
! 571: /* We walk in directory order so "ls -f" doesn't get upset. */
! 572: p->fts_link = NULL;
! 573: if (head == NULL)
! 574: head = tail = p;
! 575: else {
! 576: tail->fts_link = p;
! 577: tail = p;
! 578: }
! 579: ++nitems;
! 580: }
! 581: if (dirp)
! 582: (void)closedir(dirp);
! 583:
! 584: /*
! 585: * If realloc() changed the address of the path, adjust the
! 586: * addresses for the rest of the tree and the dir list.
! 587: */
! 588: if (doadjust)
! 589: fts_padjust(sp, head);
! 590:
! 591: /*
! 592: * If not changing directories, reset the path back to original
! 593: * state.
! 594: */
! 595: if (ISSET(FTS_NOCHDIR)) {
! 596: if (len == sp->fts_pathlen || nitems == 0)
! 597: --cp;
! 598: *cp = '\0';
! 599: }
! 600:
! 601: /*
! 602: * If descended after called from fts_children or after called from
! 603: * fts_read and nothing found, get back. At the root level we use
! 604: * the saved fd; if one of fts_open()'s arguments is a relative path
! 605: * to an empty directory, we wind up here with no other way back. If
! 606: * can't get back, we're done.
! 607: */
! 608: if (descend && !nitems &&
! 609: (cur->fts_level == FTS_ROOTLEVEL ? FCHDIR(sp, sp->fts_rfd) :
! 610: fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) {
! 611: cur->fts_info = FTS_ERR;
! 612: SET(FTS_STOP);
! 613: return (NULL);
! 614: }
! 615:
! 616: /* If didn't find anything, return NULL. */
! 617: if (!nitems) {
! 618: cur->fts_info = FTS_DP;
! 619: return (NULL);
! 620: }
! 621: return (head);
! 622: }
! 623:
! 624: static unsigned short
! 625: fts_stat(FTS *sp, FTSENT *p)
! 626: {
! 627: FTSENT *t;
! 628: dev_t dev;
! 629: ino_t ino;
! 630: struct stat *sbp;
! 631:
! 632: /* If user needs stat info, stat buffer already allocated. */
! 633: sbp = p->fts_statp;
! 634:
! 635: if (lstat(p->fts_accpath, sbp)) {
! 636: p->fts_errno = errno;
! 637: memset(sbp, 0, sizeof(struct stat));
! 638: return (FTS_NS);
! 639: }
! 640:
! 641: if (S_ISDIR(sbp->st_mode)) {
! 642: /*
! 643: * Set the device/inode. Used to find cycles and check for
! 644: * crossing mount points. Also remember the link count, used
! 645: * in fts_build to limit the number of stat calls. It is
! 646: * understood that these fields are only referenced if fts_info
! 647: * is set to FTS_D.
! 648: */
! 649: dev = p->fts_dev = sbp->st_dev;
! 650: ino = p->fts_ino = sbp->st_ino;
! 651: p->fts_nlink = sbp->st_nlink;
! 652:
! 653: if (ISDOT(p->fts_name))
! 654: return (FTS_DOT);
! 655:
! 656: /*
! 657: * Cycle detection is done by brute force when the directory
! 658: * is first encountered. If the tree gets deep enough or the
! 659: * number of symbolic links to directories is high enough,
! 660: * something faster might be worthwhile.
! 661: */
! 662: for (t = p->fts_parent;
! 663: t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent)
! 664: if (ino == t->fts_ino && dev == t->fts_dev) {
! 665: p->fts_cycle = t;
! 666: return (FTS_DC);
! 667: }
! 668: return (FTS_D);
! 669: }
! 670: if (S_ISLNK(sbp->st_mode))
! 671: return (FTS_SL);
! 672: if (S_ISREG(sbp->st_mode))
! 673: return (FTS_F);
! 674: return (FTS_DEFAULT);
! 675: }
! 676:
! 677: static FTSENT *
! 678: fts_alloc(FTS *sp, const char *name, size_t namelen)
! 679: {
! 680: FTSENT *p;
! 681: size_t len;
! 682:
! 683: /*
! 684: * The file name is a variable length array and no stat structure is
! 685: * necessary if the user has set the nostat bit. Allocate the FTSENT
! 686: * structure, the file name and the stat structure in one chunk, but
! 687: * be careful that the stat structure is reasonably aligned. Since the
! 688: * fts_name field is declared to be of size 1, the fts_name pointer is
! 689: * namelen + 2 before the first possible address of the stat structure.
! 690: */
! 691: len = sizeof(FTSENT) + namelen;
! 692: len += sizeof(struct stat) + ALIGNBYTES;
! 693: if ((p = calloc(1, len)) == NULL)
! 694: return (NULL);
! 695:
! 696: p->fts_path = sp->fts_path;
! 697: p->fts_namelen = namelen;
! 698: p->fts_instr = FTS_NOINSTR;
! 699: p->fts_statp = (struct stat *)ALIGN(p->fts_name + namelen + 2);
! 700: memcpy(p->fts_name, name, namelen);
! 701:
! 702: return (p);
! 703: }
! 704:
! 705: static void
! 706: fts_lfree(FTSENT *head)
! 707: {
! 708: FTSENT *p;
! 709:
! 710: /* Free a linked list of structures. */
! 711: while ((p = head)) {
! 712: head = head->fts_link;
! 713: free(p);
! 714: }
! 715: }
! 716:
! 717: /*
! 718: * Allow essentially unlimited paths; find, rm, ls should all work on any tree.
! 719: * Most systems will allow creation of paths much longer than PATH_MAX, even
! 720: * though the kernel won't resolve them. Add the size (not just what's needed)
! 721: * plus 256 bytes so don't realloc the path 2 bytes at a time.
! 722: */
! 723: static int
! 724: fts_palloc(FTS *sp, size_t more)
! 725: {
! 726: char *p;
! 727:
! 728: /*
! 729: * Check for possible wraparound.
! 730: */
! 731: more += 256;
! 732: if (sp->fts_pathlen + more < sp->fts_pathlen) {
! 733: if (sp->fts_path)
! 734: free(sp->fts_path);
! 735: sp->fts_path = NULL;
! 736: errno = ENAMETOOLONG;
! 737: return (1);
! 738: }
! 739: sp->fts_pathlen += more;
! 740: p = realloc(sp->fts_path, sp->fts_pathlen);
! 741: if (p == NULL) {
! 742: if (sp->fts_path)
! 743: free(sp->fts_path);
! 744: sp->fts_path = NULL;
! 745: return (1);
! 746: }
! 747: sp->fts_path = p;
! 748: return (0);
! 749: }
! 750:
! 751: /*
! 752: * When the path is realloc'd, have to fix all of the pointers in structures
! 753: * already returned.
! 754: */
! 755: static void
! 756: fts_padjust(FTS *sp, FTSENT *head)
! 757: {
! 758: FTSENT *p;
! 759: char *addr = sp->fts_path;
! 760:
! 761: #define ADJUST(p) { \
! 762: if ((p)->fts_accpath != (p)->fts_name) { \
! 763: (p)->fts_accpath = \
! 764: (char *)addr + ((p)->fts_accpath - (p)->fts_path); \
! 765: } \
! 766: (p)->fts_path = addr; \
! 767: }
! 768: /* Adjust the current set of children. */
! 769: for (p = sp->fts_child; p; p = p->fts_link)
! 770: ADJUST(p);
! 771:
! 772: /* Adjust the rest of the tree, including the current level. */
! 773: for (p = head; p->fts_level >= FTS_ROOTLEVEL;) {
! 774: ADJUST(p);
! 775: p = p->fts_link ? p->fts_link : p->fts_parent;
! 776: }
! 777: }
! 778:
! 779: static size_t
! 780: fts_maxarglen(char * const *argv)
! 781: {
! 782: size_t len, max;
! 783:
! 784: for (max = 0; *argv; ++argv)
! 785: if ((len = strlen(*argv)) > max)
! 786: max = len;
! 787: return (max + 1);
! 788: }
! 789:
! 790: /*
! 791: * Change to dir specified by fd or p->fts_accpath without getting
! 792: * tricked by someone changing the world out from underneath us.
! 793: * Assumes p->fts_dev and p->fts_ino are filled in.
! 794: */
! 795: static int
! 796: fts_safe_changedir(FTS *sp, FTSENT *p, int fd, const char *path)
! 797: {
! 798: int ret, oerrno, newfd;
! 799: struct stat sb;
! 800:
! 801: newfd = fd;
! 802: if (ISSET(FTS_NOCHDIR))
! 803: return (0);
! 804: if (fd < 0 && (newfd = open(path, O_RDONLY, 0)) < 0)
! 805: return (-1);
! 806: if (fstat(newfd, &sb)) {
! 807: ret = -1;
! 808: goto bail;
! 809: }
! 810: if (p->fts_dev != sb.st_dev || p->fts_ino != sb.st_ino) {
! 811: errno = ENOENT; /* disinformation */
! 812: ret = -1;
! 813: goto bail;
! 814: }
! 815: ret = fchdir(newfd);
! 816: bail:
! 817: oerrno = errno;
! 818: if (fd < 0)
! 819: (void)close(newfd);
! 820: errno = oerrno;
! 821: return (ret);
! 822: }
! 823:
! 824: #endif
CVSweb