1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1988 AT&T */ 28 /* All Rights Reserved */ 29 30 /* 31 * nftw - new file tree walk 32 * 33 * int nftw(char *path, int (*fn)(), int depth, int flags); 34 * 35 * Derived from System V ftw() by David Korn 36 * 37 * nftw visits each file and directory in the tree starting at 38 * path. It uses the generic directory reading library so it works 39 * for any file system type. The flags field is used to specify: 40 * FTW_PHYS Physical walk, does not follow symbolic links 41 * Otherwise, nftw will follow links but will not 42 * walk down any path the crosses itself. 43 * FTW_MOUNT The walk will not cross a mount point. 44 * FTW_DEPTH All subdirectories will be visited before the 45 * directory itself. 46 * FTW_CHDIR The walk will change to each directory before 47 * reading it. This is faster but core dumps 48 * may not get generated. 49 * 50 * The following flags are private, and are used by the find 51 * utility: 52 * FTW_ANYERR Call the callback function and return 53 * FTW_NS on any stat failure, not just 54 * lack of permission. 55 * FTW_HOPTION Use stat the first time the walk 56 * function is called, regardless of 57 * whether or not FTW_PHYS is specified. 58 * FTW_NOLOOP Allow find utility to detect infinite loops created 59 * by both symbolic and hard linked directories. 60 * 61 * fn is called with four arguments at each file and directory. 62 * The first argument is the pathname of the object, the second 63 * is a pointer to the stat buffer and the third is an integer 64 * giving additional information as follows: 65 * 66 * FTW_F The object is a file. 67 * FTW_D The object is a directory. 68 * FTW_DP The object is a directory and subdirectories 69 * have been visited. 70 * FTW_SL The object is a symbolic link. 71 * FTW_SLN The object is a symbolic link pointing at a 72 * non-existing file. 73 * FTW_DNR The object is a directory that cannot be read. 74 * fn will not be called for any of its descendants. 75 * FTW_NS Stat failed on the object because of lack of 76 * appropriate permission. The stat buffer passed to fn 77 * is undefined. Stat failure for any reason is 78 * considered an error and nftw will return -1. 79 * The following value is private, and is used by the find utility: 80 * FTW_DL An infinite loop has been detected. 81 * The fourth argument is a struct FTW* which contains the depth 82 * and the offset into pathname to the base name. 83 * If fn returns nonzero, nftw returns this value to its caller. 84 * 85 * depth limits the number of open directories that ftw uses 86 * before it starts recycling file descriptors. In general, 87 * a file descriptor is used for each level. When FTW_CHDIR isn't set, 88 * in order to descend to arbitrary depths, nftw requires 2 file 89 * descriptors to be open during the call to openat(), therefore if 90 * the depth argument is less than 2 nftw will not use openat(), and 91 * it will fail with ENAMETOOLONG if it descends to a directory that 92 * exceeds PATH_MAX. 93 * 94 */ 95 96 #include "lint.h" 97 #include <mtlib.h> 98 #include <sys/types.h> 99 #include <sys/stat.h> 100 #include <dirent.h> 101 #include <errno.h> 102 #include <limits.h> 103 #include <ftw.h> 104 #include <stdlib.h> 105 #include <string.h> 106 #include <unistd.h> 107 #include <thread.h> 108 #include <synch.h> 109 #include <stdio.h> 110 #include <strings.h> 111 #include <fcntl.h> 112 113 #if !defined(_LP64) && _FILE_OFFSET_BITS == 64 114 #define nftw nftw64 115 #define stat stat64 116 #define fstat fstat64 117 #define fstatat fstatat64 118 #pragma weak _nftw64 = nftw64 119 #else 120 #pragma weak _nftw = nftw 121 #endif /* !_LP64 && _FILE_OFFSET_BITS == 64 */ 122 123 #ifndef PATH_MAX 124 #define PATH_MAX 1023 125 #endif 126 127 /* 128 * Local variables (used to be static local). 129 * Putting them into a structure that is passed 130 * around makes nftw() MT-safe with no locking required. 131 */ 132 struct Save { 133 struct Save *last; 134 DIR *fd; 135 char *comp; 136 long here; 137 dev_t dev; 138 ino_t inode; 139 }; 140 141 struct Var { 142 char *home; 143 size_t len; 144 char *fullpath; 145 char *tmppath; 146 int curflags; 147 dev_t cur_mount; 148 struct FTW state; 149 int walklevel; 150 int (*statf)(const char *, struct stat *, struct Save *, int flags); 151 int (*savedstatf)(const char *, struct stat *, struct Save *, 152 int flags); 153 DIR *(*opendirf)(const char *); 154 }; 155 156 static int oldclose(struct Save *); 157 static int cdlstat(const char *, struct stat *, struct Save *, int flags); 158 static int cdstat(const char *, struct stat *, struct Save *, int flags); 159 static int nocdlstat(const char *, struct stat *, struct Save *, int flags); 160 static int nocdstat(const char *, struct stat *, struct Save *, int flags); 161 static DIR *cdopendir(const char *); 162 static DIR *nocdopendir(const char *); 163 static const char *get_unrooted(const char *); 164 165 /* 166 * This is the recursive walker. 167 */ 168 static int 169 walk(char *component, 170 int (*fn)(const char *, const struct stat *, int, struct FTW *), 171 int depth, struct Save *last, struct Var *vp) 172 { 173 struct stat statb; 174 char *p, *tmp; 175 int type; 176 char *comp; 177 struct dirent *dir; 178 char *q; 179 int rc = 0; 180 int val = -1; 181 int cdval = -1; 182 int oldbase; 183 int skip; 184 struct Save this; 185 size_t base_comp, base_component, base_this_comp, base_last_comp; 186 size_t base_fullpath, base_tmppath; 187 188 this.last = last; 189 this.fd = 0; 190 if ((vp->curflags & FTW_CHDIR) && last) 191 comp = last->comp; 192 else 193 comp = vp->tmppath; 194 195 if (vp->savedstatf == NULL) 196 vp->savedstatf = vp->statf; 197 198 if ((vp->walklevel++ == 0) && (vp->curflags & FTW_HOPTION)) { 199 if (((vp->curflags & FTW_CHDIR) == 0) && (depth >= 2)) { 200 vp->statf = nocdstat; 201 } else { 202 vp->statf = cdstat; 203 } 204 } else { 205 vp->statf = vp->savedstatf; 206 } 207 208 /* 209 * Determine the type of the component. 210 * 211 * Note that if the component is a trigger mount, this 212 * will cause it to load. 213 */ 214 if ((*vp->statf)(comp, &statb, last, _AT_TRIGGER) >= 0) { 215 if ((statb.st_mode & S_IFMT) == S_IFDIR) { 216 type = FTW_D; 217 if (depth <= 1) 218 (void) oldclose(last); 219 if ((this.fd = (*vp->opendirf)(comp)) == 0) { 220 if (errno == EMFILE && oldclose(last) && 221 (this.fd = (*vp->opendirf)(comp)) != 0) { 222 /* 223 * If opendirf fails because there 224 * are OPEN_MAX fd in the calling 225 * process, and we close the oldest 226 * fd, and another opendirf doesn't 227 * fail, depth is set to 1. 228 */ 229 depth = 1; 230 } else { 231 type = FTW_DNR; 232 goto fail; 233 } 234 } 235 } else if ((statb.st_mode & S_IFMT) == S_IFLNK) { 236 type = FTW_SL; 237 } else { 238 type = FTW_F; 239 } 240 } else if ((vp->curflags & FTW_ANYERR) && errno != ENOENT) { 241 /* 242 * If FTW_ANYERR is specified, then a stat error 243 * other than ENOENT automatically results in 244 * failure. This allows the callback function 245 * to properly handle ENAMETOOLONG and ELOOP and 246 * things of that nature, that would be masked 247 * by calling lstat before failing. 248 */ 249 type = FTW_NS; 250 goto fail; 251 } else { 252 /* 253 * Statf has failed. If stat was used instead of lstat, 254 * try using lstat. If lstat doesn't fail, "comp" 255 * must be a symbolic link pointing to a non-existent 256 * file. Such a symbolic link should be ignored. 257 * Also check the file type, if possible, for symbolic 258 * link. 259 */ 260 if (((vp->statf == cdstat) && 261 (cdlstat(comp, &statb, last, 0) >= 0) && 262 ((statb.st_mode & S_IFMT) == S_IFLNK)) || 263 ((vp->statf == nocdstat) && 264 (nocdlstat(comp, &statb, last, 0) >= 0) && 265 ((statb.st_mode & S_IFMT) == S_IFLNK))) { 266 267 /* 268 * Ignore bad symbolic link, let "fn" 269 * report it. 270 */ 271 272 errno = ENOENT; 273 type = FTW_SLN; 274 } else { 275 type = FTW_NS; 276 fail: 277 /* 278 * if FTW_ANYERR is set in flags, we call 279 * the user function with FTW_NS set, regardless 280 * of the reason stat failed. 281 */ 282 if (!(vp->curflags & FTW_ANYERR)) 283 if (errno != EACCES) 284 return (-1); 285 } 286 } 287 288 /* 289 * If the walk is not supposed to cross a mount point, 290 * and it did, get ready to return. 291 */ 292 if ((vp->curflags & FTW_MOUNT) && type != FTW_NS && 293 statb.st_dev != vp->cur_mount) 294 goto quit; 295 vp->state.quit = 0; 296 297 /* 298 * If current component is not a directory, call user 299 * specified function and get ready to return. 300 */ 301 if (type != FTW_D || (vp->curflags & FTW_DEPTH) == 0) 302 rc = (*fn)(vp->tmppath, &statb, type, &vp->state); 303 if (rc > 0) 304 val = rc; 305 skip = (vp->state.quit & FTW_SKD); 306 if (rc != 0 || type != FTW_D || (vp->state.quit & FTW_PRUNE)) 307 goto quit; 308 309 if (vp->tmppath[0] != '\0' && component[-1] != '/') 310 *component++ = '/'; 311 *component = 0; 312 if (vp->curflags & FTW_CHDIR) { 313 struct stat statb2; 314 315 /* 316 * Security check (there is a window between 317 * (*vp->statf)() and opendir() above). 318 */ 319 if ((vp->curflags & FTW_PHYS) && 320 (fstat(this.fd->dd_fd, &statb2) < 0 || 321 statb2.st_ino != statb.st_ino || 322 statb2.st_dev != statb.st_dev)) { 323 errno = EAGAIN; 324 rc = -1; 325 goto quit; 326 } 327 328 if ((cdval = fchdir(this.fd->dd_fd)) >= 0) { 329 this.comp = component; 330 } else { 331 type = FTW_DNR; 332 rc = (*fn)(vp->tmppath, &statb, type, &vp->state); 333 goto quit; 334 } 335 } 336 337 /* 338 * If the walk has followed a symbolic link (FTW_PHYS is not set), 339 * traverse the walk back to make sure there is not a loop. 340 * The find utility (FTW_NOLOOP is set) detects infinite loops 341 * in both symbolic and hard linked directories. 342 */ 343 if ((vp->curflags & FTW_NOLOOP) || 344 ((vp->curflags & FTW_PHYS) == 0)) { 345 struct Save *sp = last; 346 while (sp) { 347 /* 348 * If the same node has already been visited, there 349 * is a loop. Get ready to return. 350 */ 351 if (sp->dev == statb.st_dev && 352 sp->inode == statb.st_ino) { 353 if (vp->curflags & FTW_NOLOOP) { 354 /* private interface for find util */ 355 type = FTW_DL; 356 goto fail; 357 } 358 goto quit; 359 } 360 sp = sp->last; 361 } 362 } 363 this.dev = statb.st_dev; 364 this.inode = statb.st_ino; 365 oldbase = vp->state.base; 366 vp->state.base = (int)(component - vp->tmppath); 367 while ((dir = readdir(this.fd)) != NULL) { 368 if (dir->d_ino == 0) 369 continue; 370 q = dir->d_name; 371 if (*q == '.') { 372 if (q[1] == 0) 373 continue; 374 else if (q[1] == '.' && q[2] == 0) 375 continue; 376 } 377 if (last != NULL && last->comp != NULL) { 378 base_last_comp = last->comp - vp->home; 379 } 380 base_comp = comp - vp->home; 381 base_component = component - vp->home; 382 if ((strlen(q) + strlen(vp->home) + 1) > vp->len) { 383 /* 384 * When the space needed for vp->home has 385 * exceeded the amount of space that has 386 * been allocated, realloc() more space 387 * and adjust pointers to point to the 388 * (possibly moved) new block for vp->home 389 */ 390 base_this_comp = this.comp - vp->home; 391 base_fullpath = vp->fullpath - vp->home; 392 base_tmppath = vp->tmppath - vp->home; 393 vp->len *= 2; 394 tmp = (char *)realloc(vp->home, vp->len); 395 if (tmp == NULL) { 396 rc = -1; 397 goto quit; 398 } 399 vp->home = tmp; 400 comp = vp->home + base_comp; 401 component = vp->home + base_component; 402 this.comp = vp->home + base_this_comp; 403 vp->fullpath = vp->home + base_fullpath; 404 vp->tmppath = vp->home + base_tmppath; 405 if (last != NULL && last->comp != NULL) { 406 last->comp = vp->home + base_last_comp; 407 } 408 } 409 p = component; 410 while (*q != '\0') 411 *p++ = *q++; 412 *p = '\0'; 413 vp->state.level++; 414 415 /* Call walk() recursively. */ 416 rc = walk(p, fn, depth-1, &this, vp); 417 if (last != NULL && last->comp != NULL) { 418 last->comp = vp->home + base_last_comp; 419 } 420 comp = vp->home + base_comp; 421 component = vp->home + base_component; 422 vp->state.level--; 423 if (this.fd == 0) { 424 *component = 0; 425 if (vp->curflags & FTW_CHDIR) { 426 this.fd = opendir("."); 427 } else { 428 this.fd = (*vp->opendirf)(comp); 429 } 430 if (this.fd == 0) { 431 rc = -1; 432 goto quit; 433 } 434 seekdir(this.fd, this.here); 435 } 436 if (rc != 0) { 437 if (errno == ENOENT) { 438 (void) fprintf(stderr, "cannot open %s: %s\n", 439 vp->tmppath, strerror(errno)); 440 val = rc; 441 continue; 442 } 443 goto quit; /* this seems extreme */ 444 } 445 } 446 vp->state.base = oldbase; 447 *--component = 0; 448 type = FTW_DP; 449 if ((vp->tmppath[0] != '\0') && (vp->curflags & FTW_DEPTH) && !skip) 450 rc = (*fn)(vp->tmppath, &statb, type, &vp->state); 451 quit: 452 if (cdval >= 0 && last) { 453 /* try to change back to previous directory */ 454 if (last->fd != NULL) { 455 if (fchdir(last->fd->dd_fd) < 0) { 456 rc = -1; 457 } 458 } else { 459 if ((cdval = chdir("..")) >= 0) { 460 if ((*vp->statf)(".", &statb, last, 0) < 0 || 461 statb.st_ino != last->inode || 462 statb.st_dev != last->dev) 463 cdval = -1; 464 } 465 *comp = 0; 466 if (cdval < 0) { 467 if (chdir(vp->fullpath) < 0) { 468 rc = -1; 469 } else { 470 /* Security check */ 471 if ((vp->curflags & FTW_PHYS) && 472 ((*vp->statf)(".", &statb, 473 last, 0) < 0 || 474 statb.st_ino != last->inode || 475 statb.st_dev != last->dev)) { 476 errno = EAGAIN; 477 rc = -1; 478 } 479 } 480 } 481 } 482 } 483 484 if (this.fd) 485 (void) closedir(this.fd); 486 if (val > rc) 487 return (val); 488 else 489 return (rc); 490 } 491 492 int 493 nftw(const char *path, 494 int (*fn)(const char *, const struct stat *, int, struct FTW *), 495 int depth, int flags) 496 { 497 struct Var var; 498 struct stat statb; 499 int rc = -1; 500 char *dp; 501 char *base; 502 char *endhome; 503 const char *savepath = path; 504 int save_errno; 505 506 var.walklevel = 0; 507 var.len = 2*(PATH_MAX+1); 508 var.home = (char *)malloc(var.len); 509 if (var.home == NULL) 510 return (-1); 511 512 var.home[0] = 0; 513 514 /* 515 * If the walk is going to change directory before 516 * reading it, save current working directory. 517 */ 518 if (flags & FTW_CHDIR) { 519 if (getcwd(var.home, PATH_MAX+1) == 0) { 520 free(var.home); 521 return (-1); 522 } 523 } 524 endhome = dp = var.home + strlen(var.home); 525 if (*path == '/') 526 var.fullpath = dp; 527 else { 528 *dp++ = '/'; 529 var.fullpath = var.home; 530 } 531 var.tmppath = dp; 532 base = dp-1; 533 while (*path) { 534 *dp = *path; 535 if (*dp == '/') 536 base = dp; 537 dp++, path++; 538 } 539 *dp = 0; 540 var.state.base = (int)(base + 1 - var.tmppath); 541 if (*path) { 542 free(var.home); 543 errno = ENAMETOOLONG; 544 return (-1); 545 } 546 var.curflags = flags; 547 548 /* 549 * If doing chdir()'s, set var.opendirf to cdopendir. 550 * If not doing chdir()'s and if nftw()'s depth arg >= 2, 551 * set var.opendirf to nocdopendir. In order to 552 * descend to arbitrary depths without doing chdir()'s, nftw() 553 * requires a depth arg >= 2 so that nocdopendir() can use openat() 554 * to traverse the directories. So when not doing 555 * chdir()'s if nftw()'s depth arg <= 1, set var.opendirf to 556 * cdopendir. 557 * If doing a physical walk (not following symbolic link), set 558 * var.statf to cdlstat() or nocdlstat(). Otherwise, set var.statf 559 * to cdstat() or nocdstat(). 560 */ 561 if (((flags & FTW_CHDIR) == 0) && (depth >= 2)) { 562 var.opendirf = nocdopendir; 563 if (flags & FTW_PHYS) 564 var.statf = nocdlstat; 565 else 566 var.statf = nocdstat; 567 } else { 568 var.opendirf = cdopendir; 569 if (flags & FTW_PHYS) 570 var.statf = cdlstat; 571 else 572 var.statf = cdstat; 573 } 574 575 /* 576 * If walk is not going to cross a mount point, 577 * save the current mount point. 578 */ 579 if (flags & FTW_MOUNT) { 580 if ((*var.statf)(savepath, &statb, NULL, 0) >= 0) 581 var.cur_mount = statb.st_dev; 582 else 583 goto done; 584 } 585 var.state.level = 0; 586 587 /* 588 * Call walk() which does most of the work. 589 * walk() uses errno in a rather obtuse way 590 * so we shield any incoming errno. 591 */ 592 save_errno = errno; 593 errno = 0; 594 var.savedstatf = NULL; 595 rc = walk(dp, fn, depth, (struct Save *)0, &var); 596 if (errno == 0) 597 errno = save_errno; 598 done: 599 *endhome = 0; 600 if (flags & FTW_CHDIR) 601 (void) chdir(var.home); 602 free(var.home); 603 return (rc); 604 } 605 606 /* 607 * Get stat info on path when FTW_CHDIR is set. 608 */ 609 static int 610 cdstat(const char *path, struct stat *statp, struct Save *lp __unused, 611 int flags) 612 { 613 return (fstatat(AT_FDCWD, path, statp, flags)); 614 } 615 616 /* 617 * Get lstat info on path when FTW_CHDIR is set. 618 */ 619 static int 620 cdlstat(const char *path, struct stat *statp, struct Save *lp __unused, 621 int flags) 622 { 623 return (fstatat(AT_FDCWD, path, statp, 624 flags | AT_SYMLINK_NOFOLLOW)); 625 } 626 627 /* 628 * Get stat info on path when FTW_CHDIR is not set. 629 */ 630 static int 631 nocdstat(const char *path, struct stat *statp, struct Save *lp, int flags) 632 { 633 int fd; 634 const char *basepath; 635 636 if (lp && lp->fd) { 637 /* get basename of path */ 638 basepath = get_unrooted(path); 639 640 fd = lp->fd->dd_fd; 641 } else { 642 basepath = path; 643 644 fd = AT_FDCWD; 645 } 646 647 return (fstatat(fd, basepath, statp, flags)); 648 } 649 650 /* 651 * Get lstat info on path when FTW_CHDIR is not set. 652 */ 653 static int 654 nocdlstat(const char *path, struct stat *statp, struct Save *lp, int flags) 655 { 656 int fd; 657 const char *basepath; 658 659 if (lp && lp->fd) { 660 /* get basename of path */ 661 basepath = get_unrooted(path); 662 663 fd = lp->fd->dd_fd; 664 } else { 665 basepath = path; 666 667 fd = AT_FDCWD; 668 } 669 670 return (fstatat(fd, basepath, statp, flags | AT_SYMLINK_NOFOLLOW)); 671 } 672 673 /* 674 * Open path directory when FTW_CHDIR is set. 675 * 676 */ 677 static DIR * 678 cdopendir(const char *path) 679 { 680 return (opendir(path)); 681 } 682 683 /* 684 * Open path directory when FTW_CHDIR is not set. 685 */ 686 static DIR * 687 nocdopendir(const char *path) 688 { 689 int fd, cfd; 690 DIR *fdd; 691 char *dirp, *token, *ptr; 692 693 if (((fdd = opendir(path)) == NULL) && (errno == ENAMETOOLONG)) { 694 if ((dirp = strdup(path)) == NULL) { 695 errno = ENAMETOOLONG; 696 return (NULL); 697 } 698 if ((token = strtok_r(dirp, "/", &ptr)) != NULL) { 699 if ((fd = openat(AT_FDCWD, dirp, O_RDONLY)) < 0) { 700 (void) free(dirp); 701 errno = ENAMETOOLONG; 702 return (NULL); 703 } 704 while ((token = strtok_r(NULL, "/", &ptr)) != NULL) { 705 if ((cfd = openat(fd, token, O_RDONLY)) < 0) { 706 (void) close(fd); 707 (void) free(dirp); 708 errno = ENAMETOOLONG; 709 return (NULL); 710 } 711 (void) close(fd); 712 fd = cfd; 713 } 714 (void) free(dirp); 715 return (fdopendir(fd)); 716 } 717 (void) free(dirp); 718 errno = ENAMETOOLONG; 719 } 720 return (fdd); 721 } 722 723 /* 724 * return pointer basename of path, which may contain trailing slashes 725 * 726 * We do this when we do not chdir() on the input. 727 */ 728 static const char * 729 get_unrooted(const char *path) 730 { 731 const char *ptr; 732 733 if (!path || !*path) 734 return (NULL); 735 736 ptr = path + strlen(path); 737 /* find last char in path before any trailing slashes */ 738 while (ptr != path && *--ptr == '/') 739 ; 740 741 if (ptr == path) /* all slashes */ 742 return (ptr); 743 744 while (ptr != path) 745 if (*--ptr == '/') 746 return (++ptr); 747 748 return (ptr); 749 } 750 751 /* 752 * close the oldest directory. It saves the seek offset. 753 * return value is 0 unless it was unable to close any descriptor 754 */ 755 756 static int 757 oldclose(struct Save *sp) 758 { 759 struct Save *spnext; 760 while (sp) { 761 spnext = sp->last; 762 if (spnext == 0 || spnext->fd == 0) 763 break; 764 sp = spnext; 765 } 766 if (sp == 0 || sp->fd == 0) 767 return (0); 768 sp->here = telldir(sp->fd); 769 (void) closedir(sp->fd); 770 sp->fd = 0; 771 return (1); 772 } 773