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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* Copyright (c) 1988 AT&T */ 30 /* All Rights Reserved */ 31 32 33 /* 34 * nftw - new file tree walk 35 * 36 * int nftw(char *path, int (*fn)(), int depth, int flags); 37 * 38 * Derived from System V ftw() by David Korn 39 * 40 * nftw visits each file and directory in the tree starting at 41 * path. It uses the generic directory reading library so it works 42 * for any file system type. The flags field is used to specify: 43 * FTW_PHYS Physical walk, does not follow symblolic links 44 * Otherwise, nftw will follow links but will not 45 * walk down any path the crosses itself. 46 * FTW_MOUNT The walk will not cross a mount point. 47 * FTW_DEPTH All subdirectories will be visited before the 48 * directory itself. 49 * FTW_CHDIR The walk will change to each directory before 50 * reading it. This is faster but core dumps 51 * may not get generated. 52 * 53 * The following flags are private, and are used by the find 54 * utility: 55 * FTW_ANYERR Call the callback function and return 56 * FTW_NS on any stat failure, not just 57 * lack of permission. 58 * FTW_HOPTION Use stat the first time the walk 59 * function is called, regardless of 60 * whether or not FTW_PHYS is specified. 61 * 62 * fn is called with four arguments at each file and directory. 63 * The first argument is the pathname of the object, the second 64 * is a pointer to the stat buffer and the third is an integer 65 * giving additional information as follows: 66 * 67 * FTW_F The object is a file. 68 * FTW_D The object is a directory. 69 * FTW_DP The object is a directory and subdirectories 70 * have been visited. 71 * FTW_SL The object is a symbolic link. 72 * FTW_SLN The object is a symbolic link pointing at a 73 * non-existing file. 74 * FTW_DNR The object is a directory that cannot be read. 75 * fn will not be called for any of its descendants. 76 * FTW_NS Stat failed on the object because of lack of 77 * appropriate permission. The stat buffer passed to fn 78 * is undefined. Stat failure for any reason is 79 * considered an error and nftw will return -1. 80 * The fourth argument is a struct FTW* which contains the depth 81 * and the offset into pathname to the base name. 82 * If fn returns nonzero, nftw returns this value to its caller. 83 * 84 * depth limits the number of open directories that ftw uses 85 * before it starts recycling file descriptors. In general, 86 * a file descriptor is used for each level. 87 * 88 */ 89 90 #include <sys/feature_tests.h> 91 92 #if !defined(_LP64) && _FILE_OFFSET_BITS == 64 93 #pragma weak nftw64 = _nftw64 94 #define _nftw _nftw64 95 #define fstat64 _fstat64 96 #define lstat64 _lstat64 97 #define readdir64 _readdir64 98 #define stat64 _stat64 99 #else 100 #pragma weak nftw = _nftw 101 #define fstat _fstat 102 #define lstat _lstat 103 #define readdir _readdir 104 #define stat _stat 105 #endif /* !_LP64 && _FILE_OFFSET_BITS == 64 */ 106 107 #define chdir _chdir 108 #define closedir _closedir 109 #define fchdir _fchdir 110 #define fprintf _fprintf 111 #define getcwd _getcwd 112 #define opendir _opendir 113 #define seekdir _seekdir 114 #define telldir _telldir 115 116 #include "lint.h" 117 #include <mtlib.h> 118 #include <sys/types.h> 119 #include <sys/stat.h> 120 #include <dirent.h> 121 #include <errno.h> 122 #include <limits.h> 123 #include <ftw.h> 124 #include <stdlib.h> 125 #include <string.h> 126 #include <unistd.h> 127 #include <thread.h> 128 #include <synch.h> 129 #include <stdio.h> 130 131 #ifndef PATH_MAX 132 #define PATH_MAX 1023 133 #endif 134 135 /* 136 * Local variables (used to be static local). 137 * Putting them into a structure that is passed 138 * around makes nftw() MT-safe with no locking required. 139 */ 140 struct Var { 141 char *fullpath; 142 char *tmppath; 143 int curflags; 144 dev_t cur_mount; 145 struct FTW state; 146 int walklevel; 147 int (*statf)(const char *, struct stat *); 148 int (*savedstatf)(const char *, struct stat *); 149 }; 150 151 struct Save { 152 struct Save *last; 153 DIR *fd; 154 char *comp; 155 long here; 156 dev_t dev; 157 ino_t inode; 158 }; 159 160 static int oldclose(struct Save *); 161 162 /* 163 * This is the recursive walker. 164 */ 165 static int 166 walk(char *component, 167 int (*fn)(const char *, const struct stat *, int, struct FTW *), 168 int depth, struct Save *last, struct Var *vp) 169 { 170 struct stat statb; 171 char *p; 172 int type; 173 char *comp; 174 struct dirent *dir; 175 char *q; 176 int rc = 0; 177 int val = -1; 178 int cdval = -1; 179 int oldbase; 180 int skip; 181 struct Save this; 182 183 this.last = last; 184 this.fd = 0; 185 if ((vp->curflags & FTW_CHDIR) && last) 186 comp = last->comp; 187 else 188 comp = vp->tmppath; 189 190 if (vp->savedstatf == NULL) 191 vp->savedstatf = vp->statf; 192 193 if ((vp->walklevel++ == 0) && (vp->curflags & FTW_HOPTION)) 194 vp->statf = stat; 195 else 196 vp->statf = vp->savedstatf; 197 198 /* 199 * Determine the type of the component. 200 */ 201 if ((*vp->statf)(comp, &statb) >= 0) { 202 if ((statb.st_mode & S_IFMT) == S_IFDIR) { 203 type = FTW_D; 204 if (depth <= 1) 205 (void) oldclose(last); 206 if ((this.fd = opendir(comp)) == 0) { 207 if (errno == EMFILE && oldclose(last) && 208 (this.fd = opendir(comp)) != 0) { 209 depth = 1; 210 } else { 211 type = FTW_DNR; 212 goto fail; 213 } 214 } 215 if (statb.st_fstype[0] == 'a' && 216 strcmp(statb.st_fstype, "autofs") == 0) { 217 /* 218 * this dir is on autofs 219 */ 220 if (fstat(this.fd->dd_fd, &statb) < 0) { 221 (void) closedir(this.fd); 222 type = FTW_NS; 223 goto fail; 224 } 225 } 226 } else if ((statb.st_mode & S_IFMT) == S_IFLNK) { 227 type = FTW_SL; 228 } else { 229 type = FTW_F; 230 } 231 } else if ((vp->curflags & FTW_ANYERR) && errno != ENOENT) { 232 /* 233 * If FTW_ANYERR is specified, then a stat error 234 * other than ENOENT automatically results in 235 * failure. This allows the callback function 236 * to properly handle ENAMETOOLONG and ELOOP and 237 * things of that nature, that would be masked 238 * by calling lstat before failing. 239 */ 240 type = FTW_NS; 241 goto fail; 242 } else { 243 /* 244 * Statf has failed. If stat was used instead of lstat, 245 * try using lstat. If lstat doesn't fail, "comp" 246 * must be a symbolic link pointing to a non-existent 247 * file. Such a symbolic link should be ignored. 248 * Also check the file type, if possible, for symbolic 249 * link. 250 */ 251 if ((vp->statf == stat) && (lstat(comp, &statb) >= 0) && 252 ((statb.st_mode & S_IFMT) == S_IFLNK)) { 253 254 /* 255 * Ignore bad symbolic link, let "fn" 256 * report it. 257 */ 258 259 errno = ENOENT; 260 type = FTW_SLN; 261 } else { 262 type = FTW_NS; 263 fail: 264 /* 265 * if FTW_ANYERR is set in flags, we call 266 * the user function with FTW_NS set, regardless 267 * of the reason stat failed. 268 */ 269 if (!(vp->curflags & FTW_ANYERR)) 270 if (errno != EACCES) 271 return (-1); 272 } 273 } 274 275 /* 276 * If the walk is not supposed to cross a mount point, 277 * and it did, get ready to return. 278 */ 279 if ((vp->curflags & FTW_MOUNT) && type != FTW_NS && 280 statb.st_dev != vp->cur_mount) 281 goto quit; 282 vp->state.quit = 0; 283 284 /* 285 * If current component is not a directory, call user 286 * specified function and get ready to return. 287 */ 288 if (type != FTW_D || (vp->curflags & FTW_DEPTH) == 0) 289 rc = (*fn)(vp->tmppath, &statb, type, &vp->state); 290 if (rc > 0) 291 val = rc; 292 skip = (vp->state.quit & FTW_SKD); 293 if (rc != 0 || type != FTW_D || (vp->state.quit & FTW_PRUNE)) 294 goto quit; 295 296 if (vp->tmppath[0] != '\0' && component[-1] != '/') 297 *component++ = '/'; 298 if (vp->curflags & FTW_CHDIR) { 299 struct stat statb2; 300 301 *component = 0; 302 /* 303 * Security check (there is a window between 304 * (*vp->statf)() and opendir() above). 305 */ 306 if ((vp->curflags & FTW_PHYS) && 307 (fstat(this.fd->dd_fd, &statb2) < 0 || 308 statb2.st_ino != statb.st_ino || 309 statb2.st_dev != statb.st_dev)) { 310 errno = EAGAIN; 311 rc = -1; 312 goto quit; 313 } 314 315 if ((cdval = fchdir(this.fd->dd_fd)) >= 0) { 316 this.comp = component; 317 } else { 318 type = FTW_DNR; 319 rc = (*fn)(vp->tmppath, &statb, type, &vp->state); 320 goto quit; 321 } 322 } 323 324 /* 325 * If the walk has followed a symbolic link, traverse 326 * the walk back to make sure there is not a loop. 327 * 328 * XXX - may need to look at this 329 * There's code to check for cycles, but only for FTW_PHYS 330 * (find -L flag). However, all directories should be 331 * checked, even if not following links because of hardlinks 332 * to directories (not recommended, but can exist). 333 * 334 * We might have added AVL tree routines here to store and search 335 * the inodes and devices, as is done for du/ls/chgrp/chown, 336 * but libcmdutils is for for internal use only, so we can't 337 * add it to a public libc function (nftw()). 338 */ 339 if ((vp->curflags & FTW_PHYS) == 0) { 340 struct Save *sp = last; 341 while (sp) { 342 /* 343 * If the same node has already been visited, there 344 * is a loop. Get ready to return. 345 */ 346 if (sp->dev == statb.st_dev && 347 sp->inode == statb.st_ino) 348 goto quit; 349 sp = sp->last; 350 } 351 } 352 this.dev = statb.st_dev; 353 this.inode = statb.st_ino; 354 oldbase = vp->state.base; 355 vp->state.base = (int)(component - vp->tmppath); 356 while (dir = readdir(this.fd)) { 357 if (dir->d_ino == 0) 358 continue; 359 q = dir->d_name; 360 if (*q == '.') { 361 if (q[1] == 0) 362 continue; 363 else if (q[1] == '.' && q[2] == 0) 364 continue; 365 } 366 p = component; 367 while (p < &vp->tmppath[PATH_MAX] && *q != '\0') 368 *p++ = *q++; 369 *p = '\0'; 370 vp->state.level++; 371 372 /* Call walk() recursively. */ 373 rc = walk(p, fn, depth-1, &this, vp); 374 vp->state.level--; 375 if (this.fd == 0) { 376 *component = 0; 377 if (vp->curflags & FTW_CHDIR) { 378 this.fd = opendir("."); 379 } else { 380 this.fd = opendir(comp); 381 } 382 if (this.fd == 0) { 383 rc = -1; 384 goto quit; 385 } 386 seekdir(this.fd, this.here); 387 } 388 if (rc != 0) { 389 if (errno == ENOENT) { 390 (void) fprintf(stderr, "cannot open %s: %s\n", 391 vp->tmppath, strerror(errno)); 392 val = rc; 393 continue; 394 } 395 goto quit; /* this seems extreme */ 396 } 397 } 398 vp->state.base = oldbase; 399 *--component = 0; 400 type = FTW_DP; 401 if ((vp->tmppath[0] != '\0') && (vp->curflags & FTW_DEPTH) && !skip) 402 rc = (*fn)(vp->tmppath, &statb, type, &vp->state); 403 quit: 404 if (cdval >= 0 && last) { 405 /* try to change back to previous directory */ 406 if (last->fd != NULL) { 407 if (fchdir(last->fd->dd_fd) < 0) { 408 rc = -1; 409 } 410 } else { 411 if ((cdval = chdir("..")) >= 0) { 412 if ((*vp->statf)(".", &statb) < 0 || 413 statb.st_ino != last->inode || 414 statb.st_dev != last->dev) 415 cdval = -1; 416 } 417 *comp = 0; 418 if (cdval < 0) { 419 if (chdir(vp->fullpath) < 0) { 420 rc = -1; 421 } else { 422 /* Security check */ 423 if ((vp->curflags & FTW_PHYS) && 424 ((*vp->statf)(".", &statb) < 0 || 425 statb.st_ino != last->inode || 426 statb.st_dev != last->dev)) { 427 errno = EAGAIN; 428 rc = -1; 429 } 430 } 431 } 432 } 433 } 434 if (this.fd) 435 (void) closedir(this.fd); 436 if (val > rc) 437 return (val); 438 else 439 return (rc); 440 } 441 442 int 443 _nftw(const char *path, 444 int (*fn)(const char *, const struct stat *, int, struct FTW *), 445 int depth, int flags) 446 { 447 struct Var var; 448 struct stat statb; 449 char home[2*(PATH_MAX+1)]; 450 int rc = -1; 451 char *dp; 452 char *base; 453 char *endhome; 454 const char *savepath = path; 455 int save_errno; 456 457 home[0] = 0; 458 459 /* 460 * If the walk is going to change directory before 461 * reading it, save current woring directory. 462 */ 463 if (flags & FTW_CHDIR) { 464 if (getcwd(home, PATH_MAX+1) == 0) 465 return (-1); 466 } 467 endhome = dp = home + strlen(home); 468 if (*path == '/') 469 var.fullpath = dp; 470 else { 471 *dp++ = '/'; 472 var.fullpath = home; 473 } 474 var.tmppath = dp; 475 base = dp-1; 476 while (*path && dp < &var.tmppath[PATH_MAX]) { 477 *dp = *path; 478 if (*dp == '/') 479 base = dp; 480 dp++, path++; 481 } 482 *dp = 0; 483 var.state.base = (int)(base + 1 - var.tmppath); 484 if (*path) { 485 errno = ENAMETOOLONG; 486 return (-1); 487 } 488 var.curflags = flags; 489 490 /* 491 * If doing a physical walk (not following symbolic link), set 492 * var.statf to lstat(). Otherwise, set var.statf to stat(). 493 */ 494 if ((flags & FTW_PHYS) == 0) 495 var.statf = stat; 496 else 497 var.statf = lstat; 498 499 /* 500 * If walk is not going to cross a mount point, 501 * save the current mount point. 502 */ 503 if (flags & FTW_MOUNT) { 504 if ((*var.statf)(savepath, &statb) >= 0) 505 var.cur_mount = statb.st_dev; 506 else 507 goto done; 508 } 509 var.state.level = 0; 510 511 /* 512 * Call walk() which does most of the work. 513 * walk() uses errno in a rather obtuse way 514 * so we shield any incoming errno. 515 */ 516 save_errno = errno; 517 errno = 0; 518 var.savedstatf = NULL; 519 var.walklevel = 0; 520 rc = walk(dp, fn, depth, (struct Save *)0, &var); 521 if (errno == 0) 522 errno = save_errno; 523 done: 524 *endhome = 0; 525 if (flags & FTW_CHDIR) 526 (void) chdir(home); 527 return (rc); 528 } 529 530 /* 531 * close the oldest directory. It saves the seek offset. 532 * return value is 0 unless it was unable to close any descriptor 533 */ 534 535 static int 536 oldclose(struct Save *sp) 537 { 538 struct Save *spnext; 539 while (sp) { 540 spnext = sp->last; 541 if (spnext == 0 || spnext->fd == 0) 542 break; 543 sp = spnext; 544 } 545 if (sp == 0 || sp->fd == 0) 546 return (0); 547 sp->here = telldir(sp->fd); 548 (void) closedir(sp->fd); 549 sp->fd = 0; 550 return (1); 551 } 552