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