|
|||
Prex Home / Browse Source - Prex Version: 0.9.0 |
|||
root/usr/lib/libc/gen/fts.c/* [<][>][^][v][top][bottom][index][help] */DEFINITIONSThis source file includes following definitions.
1 /*- 2 * Copyright (c) 1990, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <sys/param.h> 31 #include <sys/stat.h> 32 33 #include <dirent.h> 34 #include <errno.h> 35 #include <fcntl.h> 36 #include <fts.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <unistd.h> 40 41 static FTSENT *fts_alloc(FTS *, char *, int); 42 static FTSENT *fts_build(FTS *, int); 43 static void fts_lfree(FTSENT *); 44 static void fts_load(FTS *, FTSENT *); 45 static size_t fts_maxarglen(char * const *); 46 static void fts_padjust(FTS *, void *); 47 static int fts_palloc(FTS *, size_t); 48 static FTSENT *fts_sort(FTS *, FTSENT *, int); 49 static u_short fts_stat(FTS *, FTSENT *, int); 50 51 52 #define ALIGNBYTES 7 53 54 55 #define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2]))) 56 57 #define ISSET(opt) (sp->fts_options & (opt)) 58 #define SET(opt) (sp->fts_options |= (opt)) 59 60 #define CHDIR(sp, path) (!ISSET(FTS_NOCHDIR) && chdir(path)) 61 #define FCHDIR(sp, fd) (!ISSET(FTS_NOCHDIR) && fchdir(fd)) 62 63 /* fts_build flags */ 64 #define BCHILD 1 /* fts_children */ 65 #define BNAMES 2 /* fts_children, names only */ 66 #define BREAD 3 /* fts_read */ 67 68 FTS * 69 fts_open(char * const *argv, int options, 70 int (*compar)(const FTSENT **, const FTSENT **)) 71 { 72 FTS *sp; 73 FTSENT *p, *root; 74 int nitems; 75 FTSENT *parent, *tmp = NULL; 76 int len; 77 78 /* Options check. */ 79 if (options & ~FTS_OPTIONMASK) { 80 errno = EINVAL; 81 return (NULL); 82 } 83 84 /* Allocate/initialize the stream */ 85 if ((sp = malloc((u_int)sizeof(FTS))) == NULL) 86 return (NULL); 87 memset(sp, 0, sizeof(FTS)); 88 sp->fts_compar = compar; 89 sp->fts_options = options; 90 91 /* Logical walks turn on NOCHDIR; symbolic links are too hard. */ 92 if (ISSET(FTS_LOGICAL)) 93 SET(FTS_NOCHDIR); 94 95 /* 96 * Start out with 1K of path space, and enough, in any case, 97 * to hold the user's paths. 98 */ 99 if (fts_palloc(sp, MAX(fts_maxarglen(argv), MAXPATHLEN))) 100 goto mem1; 101 102 /* Allocate/initialize root's parent. */ 103 if ((parent = fts_alloc(sp, "", 0)) == NULL) 104 goto mem2; 105 parent->fts_level = FTS_ROOTPARENTLEVEL; 106 107 /* Allocate/initialize root(s). */ 108 for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) { 109 /* Don't allow zero-length paths. */ 110 if ((len = strlen(*argv)) == 0) { 111 errno = ENOENT; 112 goto mem3; 113 } 114 115 p = fts_alloc(sp, *argv, len); 116 p->fts_level = FTS_ROOTLEVEL; 117 p->fts_parent = parent; 118 p->fts_accpath = p->fts_name; 119 p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW)); 120 121 /* Command-line "." and ".." are real directories. */ 122 if (p->fts_info == FTS_DOT) 123 p->fts_info = FTS_D; 124 125 /* 126 * If comparison routine supplied, traverse in sorted 127 * order; otherwise traverse in the order specified. 128 */ 129 if (compar) { 130 p->fts_link = root; 131 root = p; 132 } else { 133 p->fts_link = NULL; 134 if (root == NULL) 135 tmp = root = p; 136 else { 137 tmp->fts_link = p; 138 tmp = p; 139 } 140 } 141 } 142 if (compar && nitems > 1) 143 root = fts_sort(sp, root, nitems); 144 145 /* 146 * Allocate a dummy pointer and make fts_read think that we've just 147 * finished the node before the root(s); set p->fts_info to FTS_INIT 148 * so that everything about the "current" node is ignored. 149 */ 150 if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL) 151 goto mem3; 152 sp->fts_cur->fts_link = root; 153 sp->fts_cur->fts_info = FTS_INIT; 154 155 /* 156 * If using chdir(2), grab a file descriptor pointing to dot to insure 157 * that we can get back here; this could be avoided for some paths, 158 * but almost certainly not worth the effort. Slashes, symbolic links, 159 * and ".." are all fairly nasty problems. Note, if we can't get the 160 * descriptor we run anyway, just more slowly. 161 */ 162 if (!ISSET(FTS_NOCHDIR) && (sp->fts_rfd = open(".", O_RDONLY, 0)) < 0) 163 SET(FTS_NOCHDIR); 164 165 return (sp); 166 167 mem3: fts_lfree(root); 168 free(parent); 169 mem2: free(sp->fts_path); 170 mem1: free(sp); 171 return (NULL); 172 } 173 174 static void 175 fts_load(FTS *sp, FTSENT *p) 176 { 177 int len; 178 char *cp; 179 180 /* 181 * Load the stream structure for the next traversal. Since we don't 182 * actually enter the directory until after the preorder visit, set 183 * the fts_accpath field specially so the chdir gets done to the right 184 * place and the user can access the first node. From fts_open it's 185 * known that the path will fit. 186 */ 187 len = p->fts_pathlen = p->fts_namelen; 188 memmove(sp->fts_path, p->fts_name, len + 1); 189 if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) { 190 len = strlen(++cp); 191 memmove(p->fts_name, cp, len + 1); 192 p->fts_namelen = len; 193 } 194 p->fts_accpath = p->fts_path = sp->fts_path; 195 sp->fts_dev = p->fts_dev; 196 } 197 198 int 199 fts_close(FTS *sp) 200 { 201 FTSENT *freep, *p; 202 int saved_errno = 0; 203 204 /* 205 * This still works if we haven't read anything -- the dummy structure 206 * points to the root list, so we step through to the end of the root 207 * list which has a valid parent pointer. 208 */ 209 if (sp->fts_cur) { 210 for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) { 211 freep = p; 212 p = p->fts_link ? p->fts_link : p->fts_parent; 213 free(freep); 214 } 215 free(p); 216 } 217 218 /* Free up child linked list, sort array, path buffer. */ 219 if (sp->fts_child) 220 fts_lfree(sp->fts_child); 221 if (sp->fts_array) 222 free(sp->fts_array); 223 free(sp->fts_path); 224 225 /* Return to original directory, save errno if necessary. */ 226 if (!ISSET(FTS_NOCHDIR)) { 227 saved_errno = fchdir(sp->fts_rfd) ? errno : 0; 228 (void)close(sp->fts_rfd); 229 } 230 231 /* Free up the stream pointer. */ 232 free(sp); 233 234 /* Set errno and return. */ 235 if (!ISSET(FTS_NOCHDIR) && saved_errno) { 236 errno = saved_errno; 237 return (-1); 238 } 239 return (0); 240 } 241 242 /* 243 * Special case a root of "/" so that slashes aren't appended which would 244 * cause paths to be written as "//foo". 245 */ 246 #define NAPPEND(p) \ 247 (p->fts_level == FTS_ROOTLEVEL && p->fts_pathlen == 1 && \ 248 p->fts_path[0] == '/' ? 0 : p->fts_pathlen) 249 250 FTSENT * 251 fts_read(FTS *sp) 252 { 253 FTSENT *p, *tmp; 254 int instr; 255 char *t; 256 int saved_errno; 257 258 /* If finished or unrecoverable error, return NULL. */ 259 if (sp->fts_cur == NULL || ISSET(FTS_STOP)) 260 return (NULL); 261 262 /* Set current node pointer. */ 263 p = sp->fts_cur; 264 265 /* Save and zero out user instructions. */ 266 instr = p->fts_instr; 267 p->fts_instr = FTS_NOINSTR; 268 269 /* Any type of file may be re-visited; re-stat and re-turn. */ 270 if (instr == FTS_AGAIN) { 271 p->fts_info = fts_stat(sp, p, 0); 272 return (p); 273 } 274 275 /* 276 * Following a symlink -- SLNONE test allows application to see 277 * SLNONE and recover. If indirecting through a symlink, have 278 * keep a pointer to current location. If unable to get that 279 * pointer, follow fails. 280 */ 281 if (instr == FTS_FOLLOW && 282 (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) { 283 p->fts_info = fts_stat(sp, p, 1); 284 if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) { 285 if ((p->fts_symfd = open(".", O_RDONLY, 0)) < 0) { 286 p->fts_errno = errno; 287 p->fts_info = FTS_ERR; 288 } else 289 p->fts_flags |= FTS_SYMFOLLOW; 290 } 291 return (p); 292 } 293 294 /* Directory in pre-order. */ 295 if (p->fts_info == FTS_D) { 296 /* If skipped or crossed mount point, do post-order visit. */ 297 if (instr == FTS_SKIP || 298 (ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev)) { 299 if (p->fts_flags & FTS_SYMFOLLOW) 300 (void)close(p->fts_symfd); 301 if (sp->fts_child) { 302 fts_lfree(sp->fts_child); 303 sp->fts_child = NULL; 304 } 305 p->fts_info = FTS_DP; 306 return (p); 307 } 308 309 /* Rebuild if only read the names and now traversing. */ 310 if (sp->fts_child && sp->fts_options & FTS_NAMEONLY) { 311 sp->fts_options &= ~FTS_NAMEONLY; 312 fts_lfree(sp->fts_child); 313 sp->fts_child = NULL; 314 } 315 316 /* 317 * Cd to the subdirectory. 318 * 319 * If have already read and now fail to chdir, whack the list 320 * to make the names come out right, and set the parent errno 321 * so the application will eventually get an error condition. 322 * Set the FTS_DONTCHDIR flag so that when we logically change 323 * directories back to the parent we don't do a chdir. 324 * 325 * If haven't read do so. If the read fails, fts_build sets 326 * FTS_STOP or the fts_info field of the node. 327 */ 328 if (sp->fts_child) { 329 if (CHDIR(sp, p->fts_accpath)) { 330 p->fts_errno = errno; 331 p->fts_flags |= FTS_DONTCHDIR; 332 for (p = sp->fts_child; p; p = p->fts_link) 333 p->fts_accpath = 334 p->fts_parent->fts_accpath; 335 } 336 } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) { 337 if (ISSET(FTS_STOP)) 338 return (NULL); 339 return (p); 340 } 341 p = sp->fts_child; 342 sp->fts_child = NULL; 343 goto name; 344 } 345 346 /* Move to the next node on this level. */ 347 next: tmp = p; 348 if ((p = p->fts_link) != NULL) { 349 free(tmp); 350 351 /* 352 * If reached the top, return to the original directory, and 353 * load the paths for the next root. 354 */ 355 if (p->fts_level == FTS_ROOTLEVEL) { 356 if (!ISSET(FTS_NOCHDIR) && FCHDIR(sp, sp->fts_rfd)) { 357 SET(FTS_STOP); 358 return (NULL); 359 } 360 fts_load(sp, p); 361 return (sp->fts_cur = p); 362 } 363 364 /* 365 * User may have called fts_set on the node. If skipped, 366 * ignore. If followed, get a file descriptor so we can 367 * get back if necessary. 368 */ 369 if (p->fts_instr == FTS_SKIP) 370 goto next; 371 if (p->fts_instr == FTS_FOLLOW) { 372 p->fts_info = fts_stat(sp, p, 1); 373 if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) { 374 if ((p->fts_symfd = 375 open(".", O_RDONLY, 0)) < 0) { 376 p->fts_errno = errno; 377 p->fts_info = FTS_ERR; 378 } else 379 p->fts_flags |= FTS_SYMFOLLOW; 380 } 381 p->fts_instr = FTS_NOINSTR; 382 } 383 384 name: t = sp->fts_path + NAPPEND(p->fts_parent); 385 *t++ = '/'; 386 memmove(t, p->fts_name, p->fts_namelen + 1); 387 return (sp->fts_cur = p); 388 } 389 390 /* Move up to the parent node. */ 391 p = tmp->fts_parent; 392 free(tmp); 393 394 if (p->fts_level == FTS_ROOTPARENTLEVEL) { 395 /* 396 * Done; free everything up and set errno to 0 so the user 397 * can distinguish between error and EOF. 398 */ 399 free(p); 400 errno = 0; 401 return (sp->fts_cur = NULL); 402 } 403 404 /* Nul terminate the pathname. */ 405 sp->fts_path[p->fts_pathlen] = '\0'; 406 407 /* 408 * Return to the parent directory. If at a root node or came through 409 * a symlink, go back through the file descriptor. Otherwise, cd up 410 * one directory. 411 */ 412 if (p->fts_level == FTS_ROOTLEVEL) { 413 if (!ISSET(FTS_NOCHDIR) && FCHDIR(sp, sp->fts_rfd)) { 414 SET(FTS_STOP); 415 return (NULL); 416 } 417 } else if (p->fts_flags & FTS_SYMFOLLOW) { 418 if (FCHDIR(sp, p->fts_symfd)) { 419 saved_errno = errno; 420 (void)close(p->fts_symfd); 421 errno = saved_errno; 422 SET(FTS_STOP); 423 return (NULL); 424 } 425 (void)close(p->fts_symfd); 426 } else if (!(p->fts_flags & FTS_DONTCHDIR)) { 427 if (CHDIR(sp, "..")) { 428 SET(FTS_STOP); 429 return (NULL); 430 } 431 } 432 p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP; 433 return (sp->fts_cur = p); 434 } 435 436 /* 437 * Fts_set takes the stream as an argument although it's not used in this 438 * implementation; it would be necessary if anyone wanted to add global 439 * semantics to fts using fts_set. An error return is allowed for similar 440 * reasons. 441 */ 442 /* ARGSUSED */ 443 int 444 fts_set(FTS *sp, FTSENT *p, int instr) 445 { 446 if (instr && instr != FTS_AGAIN && instr != FTS_FOLLOW && 447 instr != FTS_NOINSTR && instr != FTS_SKIP) { 448 errno = EINVAL; 449 return (1); 450 } 451 p->fts_instr = instr; 452 return (0); 453 } 454 455 FTSENT * 456 fts_children(FTS *sp, int instr) 457 { 458 FTSENT *p; 459 int fd; 460 461 if (instr && instr != FTS_NAMEONLY) { 462 errno = EINVAL; 463 return (NULL); 464 } 465 466 /* Set current node pointer. */ 467 p = sp->fts_cur; 468 469 /* 470 * Errno set to 0 so user can distinguish empty directory from 471 * an error. 472 */ 473 errno = 0; 474 475 /* Fatal errors stop here. */ 476 if (ISSET(FTS_STOP)) 477 return (NULL); 478 479 /* Return logical hierarchy of user's arguments. */ 480 if (p->fts_info == FTS_INIT) 481 return (p->fts_link); 482 483 /* 484 * If not a directory being visited in pre-order, stop here. Could 485 * allow FTS_DNR, assuming the user has fixed the problem, but the 486 * same effect is available with FTS_AGAIN. 487 */ 488 if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */) 489 return (NULL); 490 491 /* Free up any previous child list. */ 492 if (sp->fts_child) 493 fts_lfree(sp->fts_child); 494 495 if (instr == FTS_NAMEONLY) { 496 sp->fts_options |= FTS_NAMEONLY; 497 instr = BNAMES; 498 } else 499 instr = BCHILD; 500 501 /* 502 * If using chdir on a relative path and called BEFORE fts_read does 503 * its chdir to the root of a traversal, we can lose -- we need to 504 * chdir into the subdirectory, and we don't know where the current 505 * directory is, so we can't get back so that the upcoming chdir by 506 * fts_read will work. 507 */ 508 if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' || 509 ISSET(FTS_NOCHDIR)) 510 return (sp->fts_child = fts_build(sp, instr)); 511 512 if ((fd = open(".", O_RDONLY, 0)) < 0) 513 return (NULL); 514 sp->fts_child = fts_build(sp, instr); 515 if (fchdir(fd)) 516 return (NULL); 517 (void)close(fd); 518 return (sp->fts_child); 519 } 520 521 /* 522 * This is the tricky part -- do not casually change *anything* in here. The 523 * idea is to build the linked list of entries that are used by fts_children 524 * and fts_read. There are lots of special cases. 525 * 526 * The real slowdown in walking the tree is the stat calls. If FTS_NOSTAT is 527 * set and it's a physical walk (so that symbolic links can't be directories), 528 * we can do things quickly. First, if it's a 4.4BSD file system, the type 529 * of the file is in the directory entry. Otherwise, we assume that the number 530 * of subdirectories in a node is equal to the number of links to the parent. 531 * The former skips all stat calls. The latter skips stat calls in any leaf 532 * directories and for any files after the subdirectories in the directory have 533 * been found, cutting the stat calls by about 2/3. 534 */ 535 static FTSENT * 536 fts_build(FTS *sp, int type) 537 { 538 struct dirent *dp; 539 FTSENT *p, *head; 540 int nitems; 541 FTSENT *cur, *tail; 542 DIR *dirp; 543 void *adjaddr; 544 int cderrno, descend, len, level, maxlen, nlinks, saved_errno; 545 #ifdef FTS_WHITEOUT 546 int oflag; 547 #endif 548 char *cp = NULL; /* pacify gcc */ 549 550 /* Set current node pointer. */ 551 cur = sp->fts_cur; 552 553 /* 554 * Open the directory for reading. If this fails, we're done. 555 * If being called from fts_read, set the fts_info field. 556 */ 557 #ifdef FTS_WHITEOUT 558 if (ISSET(FTS_WHITEOUT)) 559 oflag = DTF_NODUP|DTF_REWIND; 560 else 561 oflag = DTF_HIDEW|DTF_NODUP|DTF_REWIND; 562 #else 563 #define __opendir2(path, flag) opendir(path) 564 #endif 565 if ((dirp = __opendir2(cur->fts_accpath, oflag)) == NULL) { 566 if (type == BREAD) { 567 cur->fts_info = FTS_DNR; 568 cur->fts_errno = errno; 569 } 570 return (NULL); 571 } 572 573 /* 574 * Nlinks is the number of possible entries of type directory in the 575 * directory if we're cheating on stat calls, 0 if we're not doing 576 * any stat calls at all, -1 if we're doing stats on everything. 577 */ 578 if (type == BNAMES) 579 nlinks = 0; 580 else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) 581 nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2); 582 else 583 nlinks = -1; 584 585 #ifdef notdef 586 (void)printf("nlinks == %d (cur: %d)\n", nlinks, cur->fts_nlink); 587 (void)printf("NOSTAT %d PHYSICAL %d SEEDOT %d\n", 588 ISSET(FTS_NOSTAT), ISSET(FTS_PHYSICAL), ISSET(FTS_SEEDOT)); 589 #endif 590 /* 591 * If we're going to need to stat anything or we want to descend 592 * and stay in the directory, chdir. If this fails we keep going, 593 * but set a flag so we don't chdir after the post-order visit. 594 * We won't be able to stat anything, but we can still return the 595 * names themselves. Note, that since fts_read won't be able to 596 * chdir into the directory, it will have to return different path 597 * names than before, i.e. "a/b" instead of "b". Since the node 598 * has already been visited in pre-order, have to wait until the 599 * post-order visit to return the error. There is a special case 600 * here, if there was nothing to stat then it's not an error to 601 * not be able to stat. This is all fairly nasty. If a program 602 * needed sorted entries or stat information, they had better be 603 * checking FTS_NS on the returned nodes. 604 */ 605 cderrno = 0; 606 if (nlinks || type == BREAD) 607 if (FCHDIR(sp, dirfd(dirp))) { 608 if (nlinks && type == BREAD) 609 cur->fts_errno = errno; 610 cur->fts_flags |= FTS_DONTCHDIR; 611 descend = 0; 612 cderrno = errno; 613 } else 614 descend = 1; 615 else 616 descend = 0; 617 618 /* 619 * Figure out the max file name length that can be stored in the 620 * current path -- the inner loop allocates more path as necessary. 621 * We really wouldn't have to do the maxlen calculations here, we 622 * could do them in fts_read before returning the path, but it's a 623 * lot easier here since the length is part of the dirent structure. 624 * 625 * If not changing directories set a pointer so that can just append 626 * each new name into the path. 627 */ 628 maxlen = sp->fts_pathlen - cur->fts_pathlen - 1; 629 len = NAPPEND(cur); 630 if (ISSET(FTS_NOCHDIR)) { 631 cp = sp->fts_path + len; 632 *cp++ = '/'; 633 } 634 635 level = cur->fts_level + 1; 636 637 /* Read the directory, attaching each entry to the `link' pointer. */ 638 adjaddr = NULL; 639 for (head = tail = NULL, nitems = 0; (dp = readdir(dirp)) != NULL;) { 640 641 if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name)) 642 continue; 643 644 if ((p = fts_alloc(sp, dp->d_name, (int)dp->d_namlen)) == NULL) 645 goto mem1; 646 if (dp->d_namlen > maxlen) { 647 if (fts_palloc(sp, (size_t)dp->d_namlen)) { 648 /* 649 * No more memory for path or structures. Save 650 * errno, free up the current structure and the 651 * structures already allocated. 652 */ 653 mem1: saved_errno = errno; 654 if (p) 655 free(p); 656 fts_lfree(head); 657 (void)closedir(dirp); 658 errno = saved_errno; 659 cur->fts_info = FTS_ERR; 660 SET(FTS_STOP); 661 return (NULL); 662 } 663 adjaddr = sp->fts_path; 664 maxlen = sp->fts_pathlen - sp->fts_cur->fts_pathlen - 1; 665 } 666 667 p->fts_pathlen = len + dp->d_namlen + 1; 668 p->fts_parent = sp->fts_cur; 669 p->fts_level = level; 670 671 #ifdef FTS_WHITEOUT 672 if (dp->d_type == DT_WHT) 673 p->fts_flags |= FTS_ISW; 674 #endif 675 676 if (cderrno) { 677 if (nlinks) { 678 p->fts_info = FTS_NS; 679 p->fts_errno = cderrno; 680 } else 681 p->fts_info = FTS_NSOK; 682 p->fts_accpath = cur->fts_accpath; 683 } else if (nlinks == 0 684 #ifdef DT_DIR 685 || (nlinks > 0 && 686 dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN) 687 #endif 688 ) { 689 p->fts_accpath = 690 ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name; 691 p->fts_info = FTS_NSOK; 692 } else { 693 /* Build a file name for fts_stat to stat. */ 694 if (ISSET(FTS_NOCHDIR)) { 695 p->fts_accpath = p->fts_path; 696 memmove(cp, p->fts_name, p->fts_namelen + 1); 697 } else 698 p->fts_accpath = p->fts_name; 699 /* Stat it. */ 700 p->fts_info = fts_stat(sp, p, 0); 701 702 /* Decrement link count if applicable. */ 703 if (nlinks > 0 && (p->fts_info == FTS_D || 704 p->fts_info == FTS_DC || p->fts_info == FTS_DOT)) 705 --nlinks; 706 } 707 708 /* We walk in directory order so "ls -f" doesn't get upset. */ 709 p->fts_link = NULL; 710 if (head == NULL) 711 head = tail = p; 712 else { 713 tail->fts_link = p; 714 tail = p; 715 } 716 ++nitems; 717 } 718 (void)closedir(dirp); 719 720 /* 721 * If had to realloc the path, adjust the addresses for the rest 722 * of the tree. 723 */ 724 if (adjaddr) 725 fts_padjust(sp, adjaddr); 726 727 /* 728 * If not changing directories, reset the path back to original 729 * state. 730 */ 731 if (ISSET(FTS_NOCHDIR)) { 732 if (cp - 1 > sp->fts_path) 733 --cp; 734 *cp = '\0'; 735 } 736 737 /* 738 * If descended after called from fts_children or after called from 739 * fts_read and nothing found, get back. At the root level we use 740 * the saved fd; if one of fts_open()'s arguments is a relative path 741 * to an empty directory, we wind up here with no other way back. If 742 * can't get back, we're done. 743 */ 744 if (descend && (type == BCHILD || !nitems) && 745 (cur->fts_level == FTS_ROOTLEVEL ? 746 FCHDIR(sp, sp->fts_rfd) : CHDIR(sp, ".."))) { 747 cur->fts_info = FTS_ERR; 748 SET(FTS_STOP); 749 return (NULL); 750 } 751 752 /* If didn't find anything, return NULL. */ 753 if (!nitems) { 754 if (type == BREAD) 755 cur->fts_info = FTS_DP; 756 return (NULL); 757 } 758 759 /* Sort the entries. */ 760 if (sp->fts_compar && nitems > 1) 761 head = fts_sort(sp, head, nitems); 762 return (head); 763 } 764 765 static u_short 766 fts_stat(FTS *sp, FTSENT *p, int follow) 767 { 768 FTSENT *t; 769 dev_t dev; 770 ino_t ino; 771 struct stat *sbp, sb; 772 int saved_errno; 773 774 /* If user needs stat info, stat buffer already allocated. */ 775 sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp; 776 777 #ifdef FTS_WHITEOUT 778 /* check for whiteout */ 779 if (p->fts_flags & FTS_ISW) { 780 if (sbp != &sb) { 781 memset(sbp, '\0', sizeof (*sbp)); 782 sbp->st_mode = S_IFWHT; 783 } 784 return (FTS_W); 785 } 786 #endif 787 788 /* 789 * If doing a logical walk, or application requested FTS_FOLLOW, do 790 * a stat(2). If that fails, check for a non-existent symlink. If 791 * fail, set the errno from the stat call. 792 */ 793 if (ISSET(FTS_LOGICAL) || follow) { 794 if (stat(p->fts_accpath, sbp)) { 795 saved_errno = errno; 796 if (!lstat(p->fts_accpath, sbp)) { 797 errno = 0; 798 return (FTS_SLNONE); 799 } 800 p->fts_errno = saved_errno; 801 goto err; 802 } 803 } else if (lstat(p->fts_accpath, sbp)) { 804 p->fts_errno = errno; 805 err: memset(sbp, 0, sizeof(struct stat)); 806 return (FTS_NS); 807 } 808 809 if (S_ISDIR(sbp->st_mode)) { 810 /* 811 * Set the device/inode. Used to find cycles and check for 812 * crossing mount points. Also remember the link count, used 813 * in fts_build to limit the number of stat calls. It is 814 * understood that these fields are only referenced if fts_info 815 * is set to FTS_D. 816 */ 817 dev = p->fts_dev = sbp->st_dev; 818 ino = p->fts_ino = sbp->st_ino; 819 p->fts_nlink = sbp->st_nlink; 820 821 if (ISDOT(p->fts_name)) 822 return (FTS_DOT); 823 824 /* 825 * Cycle detection is done by brute force when the directory 826 * is first encountered. If the tree gets deep enough or the 827 * number of symbolic links to directories is high enough, 828 * something faster might be worthwhile. 829 */ 830 for (t = p->fts_parent; 831 t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent) 832 if (ino == t->fts_ino && dev == t->fts_dev) { 833 p->fts_cycle = t; 834 return (FTS_DC); 835 } 836 return (FTS_D); 837 } 838 if (S_ISLNK(sbp->st_mode)) 839 return (FTS_SL); 840 if (S_ISREG(sbp->st_mode)) 841 return (FTS_F); 842 return (FTS_DEFAULT); 843 } 844 845 static FTSENT * 846 fts_sort(FTS *sp, FTSENT *head, int nitems) 847 { 848 FTSENT **ap, *p; 849 850 /* 851 * Construct an array of pointers to the structures and call qsort(3). 852 * Reassemble the array in the order returned by qsort. If unable to 853 * sort for memory reasons, return the directory entries in their 854 * current order. Allocate enough space for the current needs plus 855 * 40 so don't realloc one entry at a time. 856 */ 857 if (nitems > sp->fts_nitems) { 858 sp->fts_nitems = nitems + 40; 859 if ((sp->fts_array = realloc(sp->fts_array, 860 (size_t)(sp->fts_nitems * sizeof(FTSENT *)))) == NULL) { 861 sp->fts_nitems = 0; 862 return (head); 863 } 864 } 865 for (ap = sp->fts_array, p = head; p; p = p->fts_link) 866 *ap++ = p; 867 qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *), 868 (int (*)(const void *, const void *))sp->fts_compar); 869 870 for (head = *(ap = sp->fts_array); --nitems; ++ap) 871 ap[0]->fts_link = ap[1]; 872 ap[0]->fts_link = NULL; 873 return (head); 874 } 875 876 static FTSENT * 877 fts_alloc(FTS *sp, char *name, int namelen) 878 { 879 FTSENT *p; 880 size_t len; 881 882 /* 883 * The file name is a variable length array and no stat structure is 884 * necessary if the user has set the nostat bit. Allocate the FTSENT 885 * structure, the file name and the stat structure in one chunk, but 886 * be careful that the stat structure is reasonably aligned. Since the 887 * fts_name field is declared to be of size 1, the fts_name pointer is 888 * namelen + 2 before the first possible address of the stat structure. 889 */ 890 len = sizeof(FTSENT) + namelen; 891 if (!ISSET(FTS_NOSTAT)) 892 len += sizeof(struct stat) + ALIGNBYTES; 893 if ((p = malloc(len)) == NULL) 894 return (NULL); 895 896 /* Copy the name plus the trailing NULL. */ 897 memmove(p->fts_name, name, namelen + 1); 898 899 if (!ISSET(FTS_NOSTAT)) 900 p->fts_statp = (struct stat *)ALIGN(p->fts_name + namelen + 2); 901 p->fts_namelen = namelen; 902 p->fts_path = sp->fts_path; 903 p->fts_errno = 0; 904 p->fts_flags = 0; 905 p->fts_instr = FTS_NOINSTR; 906 p->fts_number = 0; 907 p->fts_pointer = NULL; 908 return (p); 909 } 910 911 static void 912 fts_lfree(FTSENT *head) 913 { 914 FTSENT *p; 915 916 /* Free a linked list of structures. */ 917 while ((p = head) != NULL) { 918 head = head->fts_link; 919 free(p); 920 } 921 } 922 923 /* 924 * Allow essentially unlimited paths; find, rm, ls should all work on any tree. 925 * Most systems will allow creation of paths much longer than MAXPATHLEN, even 926 * though the kernel won't resolve them. Add the size (not just what's needed) 927 * plus 256 bytes so don't realloc the path 2 bytes at a time. 928 */ 929 static int 930 fts_palloc(FTS *sp, size_t more) 931 { 932 sp->fts_pathlen += more + 256; 933 sp->fts_path = realloc(sp->fts_path, (size_t)sp->fts_pathlen); 934 return (sp->fts_path == NULL); 935 } 936 937 /* 938 * When the path is realloc'd, have to fix all of the pointers in structures 939 * already returned. 940 */ 941 static void 942 fts_padjust(FTS *sp, void *addr) 943 { 944 FTSENT *p; 945 946 #define ADJUST(p) { \ 947 (p)->fts_accpath = \ 948 (char *)addr + ((p)->fts_accpath - (p)->fts_path); \ 949 (p)->fts_path = addr; \ 950 } 951 /* Adjust the current set of children. */ 952 for (p = sp->fts_child; p; p = p->fts_link) 953 ADJUST(p); 954 955 /* Adjust the rest of the tree. */ 956 for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) { 957 ADJUST(p); 958 p = p->fts_link ? p->fts_link : p->fts_parent; 959 } 960 } 961 962 static size_t 963 fts_maxarglen(char * const *argv) 964 { 965 size_t len, max; 966 967 for (max = 0; *argv; ++argv) 968 if ((len = strlen(*argv)) > max) 969 max = len; 970 return (max); 971 } /* [<][>][^][v][top][bottom][index][help] */ | |||
Copyright© 2005-2009 Kohsuke Ohtani |