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