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