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