1 /*- 2 * Copyright (c) 1990 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #if defined(LIBC_SCCS) && !defined(lint) 9 static char sccsid[] = "@(#)fts.c 5.8 (Berkeley) 05/24/90"; 10 #endif /* LIBC_SCCS and not lint */ 11 12 #include <sys/param.h> 13 #include <sys/stat.h> 14 #include <fcntl.h> 15 #include <dirent.h> 16 #include <errno.h> 17 #include <fts.h> 18 #include <string.h> 19 #include <stdlib.h> 20 21 FTSENT *fts_alloc(), *fts_build(), *fts_cycle(), *fts_sort(), *fts_root(); 22 short fts_stat(); 23 24 /* 25 * Special case a root of "/" so that slashes aren't appended causing 26 * paths to be written as "//foo". 27 */ 28 #define NAPPEND(p) \ 29 (p->fts_level == ROOTLEVEL && p->fts_pathlen == 1 && \ 30 p->fts_path[0] == '/' ? 0 : p->fts_pathlen) 31 32 #define CHDIR(sp, path) (!(sp->fts_options & FTS_NOCHDIR) && chdir(path)) 33 #define FCHDIR(sp, fd) (!(sp->fts_options & FTS_NOCHDIR) && fchdir(fd)) 34 35 #define ROOTLEVEL 0 36 #define ROOTPARENTLEVEL -1 37 38 /* fts_build flags */ 39 #define BCHILD 1 /* from ftschildren */ 40 #define BREAD 2 /* from ftsread */ 41 42 static FTS *stream; /* current stream pointer */ 43 44 FTS * 45 ftsopen(argv, options, compar) 46 char *argv[]; 47 register int options; 48 int (*compar)(); 49 { 50 register FTS *sp; 51 register FTSENT *p, *root; 52 register int nitems, maxlen; 53 FTSENT *parent, *tmp; 54 char *fts_path(); 55 56 /* allocate/initialize the stream */ 57 if (!(stream = sp = (FTS *)malloc((u_int)sizeof(FTS)))) 58 return(NULL); 59 bzero(sp, sizeof(FTS)); 60 sp->fts_compar = compar; 61 62 /* 63 * logical walks turn on NOCHDIR; symbolic links are just too 64 * hard to deal with. 65 */ 66 sp->fts_options = options; 67 if (options & FTS_LOGICAL) 68 sp->fts_options |= FTS_NOCHDIR; 69 70 /* allocate/initialize root's parent */ 71 if (!(parent = fts_alloc("", 0))) 72 goto mem1; 73 parent->fts_level = ROOTPARENTLEVEL; 74 75 /* allocate/initialize root(s) */ 76 if (options & FTS_MULTIPLE) { 77 maxlen = -1; 78 for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) { 79 if (!(p = fts_root(*argv))) 80 goto mem2; 81 if (maxlen < p->fts_namelen) 82 maxlen = p->fts_namelen; 83 /* 84 * if comparison routine supplied, traverse in sorted 85 * order; otherwise traverse in the order specified. 86 */ 87 if (compar) { 88 p->fts_link = root; 89 root = p; 90 p->fts_accpath = p->fts_name; 91 p->fts_info = fts_stat(p, 0); 92 } else { 93 p->fts_link = NULL; 94 if (!root) 95 tmp = root = p; 96 else { 97 tmp->fts_link = p; 98 tmp = p; 99 } 100 } 101 p->fts_level = ROOTLEVEL; 102 p->fts_parent = parent; 103 } 104 if (compar && nitems > 1) 105 root = fts_sort(root, nitems); 106 } else { 107 if (!(root = fts_root((char *)argv))) 108 goto mem2; 109 maxlen = root->fts_namelen; 110 root->fts_link = NULL; 111 root->fts_level = ROOTLEVEL; 112 root->fts_parent = parent; 113 } 114 115 /* 116 * allocate a dummy pointer and make ftsread think that we've just 117 * finished the node before the root(s); set p->fts_info to FTS_NS 118 * so that everything about the "current" node is ignored. 119 */ 120 if (!(sp->fts_cur = fts_alloc("", 0))) 121 goto mem2; 122 sp->fts_cur->fts_link = root; 123 sp->fts_cur->fts_info = FTS_NS; 124 125 /* start out with at least 1K+ of path space */ 126 if (!fts_path(MAX(maxlen, MAXPATHLEN))) 127 goto mem3; 128 129 /* 130 * if using chdir(2), grab a file descriptor pointing to dot to insure 131 * that we can get back here; this could be avoided for some paths, 132 * but almost certainly not worth the effort. Slashes, symbolic links, 133 * and ".." are all fairly nasty problems. Note, if we can't get the 134 * descriptor we run anyway, just more slowly. 135 */ 136 if (!(options & FTS_NOCHDIR) && 137 (sp->fts_sd = open(".", O_RDONLY, 0)) < 0) 138 sp->fts_options |= FTS_NOCHDIR; 139 140 return(sp); 141 142 mem3: (void)free((void *)sp->fts_cur); 143 mem2: fts_lfree(root); 144 (void)free((void *)parent); 145 mem1: (void)free((void *)sp); 146 return(NULL); 147 } 148 149 static 150 fts_load(p) 151 register FTSENT *p; 152 { 153 register int len; 154 register char *cp; 155 156 /* 157 * load the stream structure for the next traversal; set the 158 * accpath field specially so the chdir gets done to the right 159 * place and the user can access the first node. 160 */ 161 len = p->fts_pathlen = p->fts_namelen; 162 bcopy(p->fts_name, stream->fts_path, len + 1); 163 if ((cp = rindex(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) { 164 len = strlen(++cp); 165 bcopy(cp, p->fts_name, len + 1); 166 p->fts_namelen = len; 167 } 168 p->fts_accpath = p->fts_path = stream->fts_path; 169 } 170 171 ftsclose(sp) 172 FTS *sp; 173 { 174 register FTSENT *freep, *p; 175 int saved_errno; 176 177 if (sp->fts_cur) { 178 /* 179 * this still works if we haven't read anything -- the dummy 180 * structure points to the root list, so we step through to 181 * the end of the root list which has a valid parent pointer. 182 */ 183 for (p = sp->fts_cur; p->fts_level > ROOTPARENTLEVEL;) { 184 freep = p; 185 p = p->fts_link ? p->fts_link : p->fts_parent; 186 (void)free((void *)freep); 187 } 188 (void)free((void *)p); 189 } 190 191 /* free up child linked list, sort array, path buffer */ 192 if (sp->fts_child) 193 fts_lfree(sp->fts_child); 194 if (sp->fts_array) 195 (void)free((void *)sp->fts_array); 196 (void)free((char *)sp->fts_path); 197 198 /* 199 * return to original directory, save errno if necessary; 200 * free up the directory buffer 201 */ 202 if (!(sp->fts_options & FTS_NOCHDIR)) { 203 saved_errno = fchdir(sp->fts_sd) ? errno : 0; 204 (void)close(sp->fts_sd); 205 } 206 207 /* free up the stream pointer */ 208 (void)free((void *)sp); 209 210 /* set errno and return */ 211 if (!(sp->fts_options & FTS_NOCHDIR) && saved_errno) { 212 errno = saved_errno; 213 return(-1); 214 } 215 return(0); 216 } 217 218 FTSENT * 219 ftsread(sp) 220 register FTS *sp; 221 { 222 register FTSENT *p, *tmp; 223 register int instr; 224 register char *cp; 225 static int cd; 226 227 /* if finished or unrecoverable error, return NULL */ 228 if (!sp->fts_cur || sp->fts_options & FTS__STOP) 229 return(NULL); 230 231 /* set global stream pointer, and current node pointer */ 232 stream = sp; 233 p = sp->fts_cur; 234 235 /* save and zero out user instructions */ 236 instr = p->fts_instr; 237 p->fts_instr = 0; 238 239 /* if used link pointer for cycle detection, restore it */ 240 if (sp->fts_savelink) { 241 p->fts_link = sp->fts_savelink; 242 sp->fts_savelink = NULL; 243 } 244 245 /* any type of file may be re-visited; re-stat and return */ 246 if (instr == FTS_AGAIN) { 247 p->fts_info = fts_stat(p, 0); 248 return(p); 249 } 250 251 /* following a symbolic link */ 252 if (p->fts_info == FTS_SL && instr == FTS_FOLLOW) { 253 p->fts_info = fts_stat(p, 1); 254 return(p); 255 } 256 257 /* directory in pre-order */ 258 if (p->fts_info == FTS_D) { 259 /* if skipped or crossed mount point, do post-order visit */ 260 if (instr == FTS_SKIP || sp->fts_options & FTS_XDEV && 261 p->fts_statb.st_dev != sp->sdev) { 262 if (sp->fts_child) { 263 fts_lfree(sp->fts_child); 264 sp->fts_child = NULL; 265 } 266 p->fts_info = FTS_DP; 267 return(p); 268 } 269 270 /* read the directory if necessary, and return first entry */ 271 if (sp->fts_child) 272 if (CHDIR(sp, p->fts_accpath)) { 273 fts_lfree(sp->fts_child); 274 p->fts_info = FTS_DNX; 275 } else { 276 p = sp->fts_child; 277 cp = sp->fts_path + NAPPEND(p->fts_parent); 278 *cp++ = '/'; 279 bcopy(p->fts_name, cp, p->fts_namelen + 1); 280 } 281 else { 282 if (!(sp->fts_child = fts_build(sp, BREAD))) 283 return(p); 284 p = sp->fts_child; 285 } 286 sp->fts_child = NULL; 287 return(sp->fts_cur = p); 288 } 289 290 /* move to next node on this level */ 291 next: tmp = p; 292 if (p = p->fts_link) { 293 /* 294 * if root level node, set up paths and return. If not the 295 * first time, and it's not an absolute pathname, get back 296 * to starting directory. 297 */ 298 if (p->fts_level == ROOTLEVEL) { 299 fts_load(p); 300 (void)free((void *)tmp); 301 if (cd && 302 p->fts_path[0] != '/' && FCHDIR(sp, sp->fts_sd)) { 303 /* should never happen... */ 304 p->fts_path = "starting directory"; 305 p->fts_info = FTS_ERR; 306 sp->fts_options |= FTS__STOP; 307 return(sp->fts_cur = p); 308 } 309 cd = 1; 310 p->fts_info = fts_stat(p, 0); 311 sp->sdev = p->fts_statb.st_dev; 312 } else { 313 (void)free((void *)tmp); 314 315 /* user may have called ftsset on node */ 316 if (p->fts_instr == FTS_SKIP) 317 goto next; 318 if (p->fts_instr == FTS_FOLLOW) { 319 p->fts_info = fts_stat(p, 1); 320 p->fts_instr = 0; 321 } 322 323 /* fill in the paths */ 324 cp = sp->fts_path + NAPPEND(p->fts_parent); 325 *cp++ = '/'; 326 bcopy(p->fts_name, cp, p->fts_namelen + 1); 327 328 /* check for directory cycles */ 329 if (p->fts_info == FTS_D && (tmp = fts_cycle(p))) { 330 sp->fts_savelink = p->fts_link; 331 p->fts_link = tmp; 332 p->fts_info = FTS_DC; 333 } 334 } 335 return(sp->fts_cur = p); 336 } 337 338 /* move to parent */ 339 p = tmp->fts_parent; 340 (void)free((void *)tmp); 341 342 if (p->fts_level == ROOTPARENTLEVEL) { 343 /* 344 * done; free everything up and set errno to 0 so the user 345 * can distinguish between error and EOF. 346 */ 347 (void)free((void *)p); 348 errno = 0; 349 return(sp->fts_cur = NULL); 350 } 351 352 sp->fts_path[p->fts_pathlen] = '\0'; 353 if (CHDIR(sp, "..")) { 354 sp->fts_options |= FTS__STOP; 355 p->fts_info = FTS_ERR; 356 } else 357 p->fts_info = FTS_DP; 358 return(sp->fts_cur = p); 359 } 360 361 /* 362 * ftsset takes the stream as an argument although it's not used in this 363 * implementation; it would be necessary if anyone wanted to add global 364 * semantics to fts using ftsset. A possible error return is allowed for 365 * similar reasons. 366 */ 367 /* ARGSUSED */ 368 ftsset(sp, p, instr) 369 FTS *sp; 370 FTSENT *p; 371 int instr; 372 { 373 p->fts_instr = instr; 374 return(0); 375 } 376 377 FTSENT * 378 ftschildren(sp) 379 register FTS *sp; 380 { 381 register FTSENT *p; 382 int fd; 383 384 /* 385 * set errno to 0 so that user can tell the difference between an 386 * error and a directory without entries. 387 */ 388 errno = 0; 389 p = sp->fts_cur; 390 if (p->fts_info != FTS_D || sp->fts_options & FTS__STOP) 391 return(NULL); 392 if (sp->fts_child) 393 fts_lfree(sp->fts_child); 394 395 /* 396 * if using chdir on a relative path and called BEFORE ftsread on the 397 * root of a traversal, we can lose because we need to chdir into the 398 * subdirectory, and we don't know where the current directory is to 399 * get back so that the upcoming chdir by ftsread will work. 400 */ 401 if (p->fts_level != ROOTLEVEL || p->fts_accpath[0] == '/' || 402 sp->fts_options & FTS_NOCHDIR) 403 return(sp->fts_child = fts_build(sp, BCHILD)); 404 405 if ((fd = open(".", O_RDONLY, 0)) < 0) 406 return(NULL); 407 sp->fts_child = fts_build(sp, BCHILD); 408 if (fchdir(fd)) 409 return(NULL); 410 (void)close(fd); 411 return(sp->fts_child); 412 } 413 414 #define ISDOT(a) (a[0] == '.' && (!a[1] || a[1] == '.' && !a[2])) 415 416 FTSENT * 417 fts_build(sp, type) 418 register FTS *sp; 419 int type; 420 { 421 register struct dirent *dp; 422 register FTSENT *p, *head; 423 register int nitems; 424 DIR *dirp; 425 int descend, len, level, maxlen, nlinks, saved_errno; 426 char *cp; 427 428 p = sp->fts_cur; 429 if (!(dirp = opendir(p->fts_accpath))) { 430 if (type == BREAD) { 431 errno = 0; 432 p->fts_info = FTS_DNR; 433 } 434 return(NULL); 435 } 436 437 /* 438 * the real slowdown in walking the tree is the stat calls. If 439 * FTS_NOSTAT is set and it's a physical walk (so that symbolic 440 * links can't be directories), fts assumes that the number of 441 * subdirectories in a node is equal to the number of links to 442 * the parent. This allows stat calls to be skipped in any leaf 443 * directories and for any nodes after the directories in the 444 * parent node have been found. This empirically cuts the stat 445 * calls by about 2/3. 446 */ 447 nlinks = 448 sp->fts_options & FTS_NOSTAT && sp->fts_options & FTS_PHYSICAL ? 449 p->fts_statb.st_nlink - (sp->fts_options & FTS_SEEDOT ? 0 : 2) : -1; 450 451 /* if told to descend or found links and not told not to descend. */ 452 if (nlinks || type == BREAD) { 453 if (FCHDIR(sp, dirfd(dirp))) { 454 if (type == BREAD) { 455 errno = 0; 456 p->fts_info = FTS_DNX; 457 } 458 (void)closedir(dirp); 459 return(NULL); 460 } 461 descend = 1; 462 } else 463 descend = 0; 464 465 /* get max file name length that can be stored in current path */ 466 maxlen = sp->fts_pathlen - p->fts_pathlen - 1; 467 468 cp = sp->fts_path + (len = NAPPEND(p)); 469 *cp++ = '/'; 470 471 level = p->fts_level + 1; 472 473 /* read the directory, attching each new entry to the `link' pointer */ 474 for (head = NULL, nitems = 0; dp = readdir(dirp);) { 475 if (ISDOT(dp->d_name) && !(sp->fts_options & FTS_SEEDOT)) 476 continue; 477 478 if (!(p = fts_alloc(dp->d_name, dp->d_namlen))) { 479 saved_errno = errno; 480 goto mem1; 481 } 482 if (dp->d_namlen > maxlen) { 483 if (!fts_path((int)dp->d_namlen)) { 484 /* quit: this stream no longer has a path */ 485 sp->fts_options |= FTS__STOP; 486 saved_errno = errno; 487 (void)free((void *)p); 488 mem1: fts_lfree(head); 489 if (type == BREAD) 490 p->fts_info = FTS_ERR; 491 if (descend && CHDIR(sp, "..")) { 492 saved_errno = errno; 493 sp->fts_options |= FTS__STOP; 494 } 495 errno = saved_errno; 496 (void)closedir(dirp); 497 return(NULL); 498 } 499 maxlen = sp->fts_pathlen - sp->fts_cur->fts_pathlen - 1; 500 } 501 502 p->fts_pathlen = len + dp->d_namlen + 1; 503 p->fts_accpath = 504 sp->fts_options & FTS_NOCHDIR ? p->fts_path : p->fts_name; 505 506 p->fts_parent = sp->fts_cur; 507 p->fts_level = level; 508 509 if (nlinks) { 510 /* make sure fts_stat has a filename to stat */ 511 if (sp->fts_options & FTS_NOCHDIR) 512 bcopy(p->fts_name, cp, p->fts_namelen + 1); 513 p->fts_info = fts_stat(p, 0); 514 if (nlinks > 0 && (p->fts_info == FTS_D || 515 p->fts_info == FTS_DNR || p->fts_info == FTS_DNX)) 516 --nlinks; 517 } else 518 p->fts_info = FTS_NS; 519 520 p->fts_link = head; 521 head = p; 522 ++nitems; 523 } 524 (void)closedir(dirp); 525 526 /* reset the path */ 527 if (cp - 1 > sp->fts_path) 528 --cp; 529 *cp = '\0'; 530 531 /* 532 * if descended: if were called from ftsread and didn't find anything, 533 * or were called from ftschildren, get back. 534 */ 535 if (descend && (!nitems || type == BCHILD) && CHDIR(sp, "..")) { 536 sp->fts_options |= FTS__STOP; 537 p->fts_info = FTS_ERR; 538 return(NULL); 539 } 540 541 if (!nitems) { 542 if (type == BREAD) 543 p->fts_info = FTS_DP; 544 return(NULL); 545 } 546 547 if (sp->fts_compar && nitems > 1) 548 head = fts_sort(head, nitems); 549 550 if (type == BREAD) { 551 *cp = '/'; 552 bcopy(head->fts_name, cp + 1, head->fts_namelen + 1); 553 } 554 return(head); 555 } 556 557 static short 558 fts_stat(p, symflag) 559 register FTSENT *p; 560 int symflag; 561 { 562 /* 563 * detection of symbolic links w/o targets. If FTS_FOLLOW is set, 564 * the symlink structure is overwritten with the stat structure of 565 * the target. 566 */ 567 if (stream->fts_options & FTS_LOGICAL || symflag) { 568 if (stat(p->fts_accpath, &p->fts_statb)) 569 return(symflag || !lstat(p->fts_accpath, 570 &p->fts_statb) ? FTS_SLNONE : FTS_ERR); 571 } else if (lstat(p->fts_accpath, &p->fts_statb)) 572 return(FTS_ERR); 573 574 switch(p->fts_statb.st_mode&S_IFMT) { 575 case S_IFDIR: 576 return(FTS_D); 577 case S_IFLNK: 578 return(FTS_SL); 579 case S_IFREG: 580 return(FTS_F); 581 } 582 return(FTS_DEFAULT); 583 } 584 585 static FTSENT * 586 fts_cycle(p) 587 register FTSENT *p; 588 { 589 register dev_t dev; 590 register ino_t ino; 591 592 /* 593 * cycle detection is brute force; if the tree gets deep enough or 594 * the number of symbolic links to directories is really high 595 * something faster might be worthwhile. 596 */ 597 dev = p->fts_statb.st_dev; 598 ino = p->fts_statb.st_ino; 599 for (p = p->fts_parent; p->fts_level > ROOTLEVEL; p = p->fts_parent) 600 if (ino == p->fts_statb.st_ino && dev == p->fts_statb.st_dev) 601 return(p); 602 return(NULL); 603 } 604 605 #define R(type, nelem, ptr) \ 606 (type *)realloc((void *)ptr, (u_int)((nelem) * sizeof(type))) 607 608 static FTSENT * 609 fts_sort(head, nitems) 610 FTSENT *head; 611 register int nitems; 612 { 613 register FTSENT **ap, *p; 614 615 /* 616 * construct an array of pointers to the structures and call qsort(3). 617 * Reassemble the array in the order returned by qsort. If unable to 618 * sort for memory reasons, return the directory entries in their 619 * current order. Allocate enough space for the current needs plus 620 * 40 so we don't realloc one entry at a time. 621 */ 622 if (nitems > stream->fts_nitems) { 623 stream->fts_nitems = nitems + 40; 624 if (!(stream->fts_array = 625 R(FTSENT *, stream->fts_nitems, stream->fts_array))) { 626 stream->fts_nitems = 0; 627 return(head); 628 } 629 } 630 for (ap = stream->fts_array, p = head; p; p = p->fts_link) 631 *ap++ = p; 632 qsort((void *)stream->fts_array, nitems, sizeof(FTSENT *), 633 stream->fts_compar); 634 for (head = *(ap = stream->fts_array); --nitems; ++ap) 635 ap[0]->fts_link = ap[1]; 636 ap[0]->fts_link = NULL; 637 return(head); 638 } 639 640 static FTSENT * 641 fts_alloc(name, len) 642 char *name; 643 register int len; 644 { 645 register FTSENT *p; 646 647 /* 648 * variable sized structures; the name is the last element so 649 * allocate enough extra space after the structure to hold it. 650 */ 651 if (!(p = (FTSENT *)malloc((size_t)(sizeof(FTSENT) + len)))) 652 return(NULL); 653 bcopy(name, p->fts_name, len + 1); 654 p->fts_namelen = len; 655 p->fts_path = stream->fts_path; 656 p->fts_instr = 0; 657 p->fts_local.number = 0; 658 p->fts_local.pointer = NULL; 659 return(p); 660 } 661 662 static 663 fts_lfree(head) 664 register FTSENT *head; 665 { 666 register FTSENT *p; 667 668 while (p = head) { 669 head = head->fts_link; 670 (void)free((void *)p); 671 } 672 } 673 674 /* 675 * allow essentially unlimited paths; certain programs (find, remove, ls) 676 * need to work on any tree. Most systems will allow creation of paths 677 * much longer than MAXPATHLEN, even though the kernel won't resolve them. 678 * Add an extra 128 bytes to the requested size so that we don't realloc 679 * the path 2 bytes at a time. 680 */ 681 static char * 682 fts_path(size) 683 int size; 684 { 685 stream->fts_pathlen += size + 128; 686 return(stream->fts_path = 687 R(char, stream->fts_pathlen, stream->fts_path)); 688 } 689 690 static FTSENT * 691 fts_root(name) 692 register char *name; 693 { 694 register char *cp; 695 696 /* 697 * rip trailing slashes; it's somewhat unclear in POSIX 1003.1 what 698 * /a/b/ really is, they don't talk about what a null path component 699 * resolves to. This hopefully does what the user intended. Don't 700 * allow null pathnames. 701 */ 702 for (cp = name; *cp; ++cp); 703 if (cp == name) { 704 errno = ENOENT; 705 return(NULL); 706 } 707 while (--cp > name && *cp == '/'); 708 *++cp = '\0'; 709 return(fts_alloc(name, cp - name)); 710 } 711