1 /* 2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* 7 * BSD 3 Clause License 8 * 9 * Copyright (c) 2007, The Storage Networking Industry Association. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * - Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 17 * - Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in 19 * the documentation and/or other materials provided with the 20 * distribution. 21 * 22 * - Neither the name of The Storage Networking Industry Association (SNIA) 23 * nor the names of its contributors may be used to endorse or promote 24 * products derived from this software without specific prior written 25 * permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 31 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGE. 38 */ 39 /* 40 * This file implemets the post-order, pre-order and level-order 41 * traversing of the file system. The related macros and constants 42 * are defined in traverse.h. 43 */ 44 45 #include <sys/stat.h> 46 #include <sys/types.h> 47 #include <sys/param.h> 48 #include <assert.h> 49 #include <cstack.h> 50 #include <dirent.h> 51 #include <errno.h> 52 #include <traverse.h> 53 #include <limits.h> 54 #include <stdarg.h> 55 #include <stdio.h> 56 #include <stdlib.h> 57 #include <string.h> 58 #include <syslog.h> 59 #include <fcntl.h> 60 #include <unistd.h> 61 #include <tlm.h> 62 #include "tlm_proto.h" 63 64 /* 65 * Check if it's "." or ".." 66 */ 67 boolean_t 68 rootfs_dot_or_dotdot(char *name) 69 { 70 if (*name != '.') 71 return (FALSE); 72 73 if ((name[1] == 0) || (name[1] == '.' && name[2] == 0)) 74 return (TRUE); 75 76 return (FALSE); 77 } 78 79 /* 80 * Macros on fs_traverse flags. 81 */ 82 #define STOP_ONERR(f) ((f)->ft_flags & FST_STOP_ONERR) 83 #define STOP_ONLONG(f) ((f)->ft_flags & FST_STOP_ONLONG) 84 #define VERBOSE(f) ((f)->ft_flags & FST_VERBOSE) 85 86 #define CALLBACK(pp, ep) \ 87 (*(ftp)->ft_callbk)((ftp)->ft_arg, pp, ep) 88 89 #define NEGATE(rv) ((rv) = -(rv)) 90 91 /* 92 * The traversing state that is pushed onto the stack. 93 * This include: 94 * - The end of the path of the current directory. 95 * - The position of the last component on it. 96 * - The read position in the directory. 97 * - The file handle of the directory. 98 * - The stat of the directory. 99 */ 100 typedef struct traverse_state { 101 char *ts_end; 102 char *ts_ent; 103 long ts_dpos; /* position in the directory when reading its entries */ 104 fs_fhandle_t ts_fh; 105 struct stat64 ts_st; 106 } traverse_state_t; 107 108 /* 109 * Statistics gathering structure. 110 */ 111 typedef struct traverse_statistics { 112 ulong_t fss_newdirs; 113 ulong_t fss_readdir_err; 114 ulong_t fss_longpath_err; 115 ulong_t fss_lookup_err; 116 ulong_t fss_nondir_calls; 117 ulong_t fss_dir_calls; 118 ulong_t fss_nondir_skipped; 119 ulong_t fss_dir_skipped; 120 ulong_t fss_pushes; 121 ulong_t fss_pops; 122 ulong_t fss_stack_residue; 123 } traverse_statistics_t; 124 125 /* 126 * Global instance of statistics variable. 127 */ 128 traverse_statistics_t traverse_stats; 129 130 #define MAX_DENT_BUF_SIZE (8 * 1024) 131 132 typedef struct { 133 struct stat64 fd_attr; 134 fs_fhandle_t fd_fh; 135 short fd_len; 136 char fd_name[1]; 137 } fs_dent_info_t; 138 139 typedef struct dent_arg { 140 char *da_buf; 141 int da_end; 142 int da_size; 143 } dent_arg_t; 144 145 static int traverse_level_nondir(struct fs_traverse *ftp, 146 traverse_state_t *tsp, struct fst_node *pnp, 147 dent_arg_t *darg); 148 149 /* 150 * Gather some directory entry information and return them 151 */ 152 static int 153 fs_populate_dents(void *arg, int namelen, 154 char *name, long *countp, struct stat64 *attr, 155 fs_fhandle_t *fh) 156 { 157 dent_arg_t *darg = (dent_arg_t *)arg; 158 int reclen = sizeof (fs_dent_info_t) + namelen; 159 fs_dent_info_t *dent; 160 161 if ((darg->da_end + reclen) > darg->da_size) 162 return (-1); 163 164 /* LINTED improper alignment */ 165 dent = (fs_dent_info_t *)(darg->da_buf + darg->da_end); 166 167 dent->fd_attr = *attr; 168 dent->fd_fh = *fh; 169 (void) strcpy(dent->fd_name, name); 170 171 dent->fd_len = reclen; 172 darg->da_end += reclen; 173 174 if (countp) 175 (*countp)++; 176 177 return (0); 178 } 179 180 /* 181 * Creates a new traversing state based on the path passed to it. 182 */ 183 static traverse_state_t * 184 new_tsp(char *path) 185 { 186 traverse_state_t *tsp; 187 tsp = ndmp_malloc(sizeof (traverse_state_t)); 188 if (!tsp) 189 return (NULL); 190 191 tsp->ts_end = strchr(path, '\0'); 192 if (*(tsp->ts_end-1) == '/') 193 *--tsp->ts_end = '\0'; 194 tsp->ts_ent = NULL; 195 tsp->ts_dpos = 0; 196 197 return (tsp); 198 } 199 200 /* 201 * Initialize a list for path names 202 */ 203 path_list_t * 204 fs_init_pathlist() 205 { 206 path_list_t *pl_head; 207 208 pl_head = ndmp_malloc(sizeof (path_list_t)); 209 return (pl_head); 210 } 211 212 /* 213 * Free the list of path names 214 */ 215 void 216 fs_free_pathlist(path_list_t *pl_head) 217 { 218 path_list_t *p = pl_head; 219 220 while (p) { 221 p = pl_head->pl_next; 222 free(pl_head->pl_path); 223 free(pl_head); 224 pl_head = p; 225 } 226 } 227 228 /* 229 * Add a path in the list of path names 230 */ 231 char * 232 fs_add_pathlist(char *path, path_list_t **pp) 233 { 234 char *tpath; 235 236 if (*pp) { 237 (*pp)->pl_path = strdup(path); 238 if ((*pp)->pl_path == NULL) 239 return (NULL); 240 tpath = (*pp)->pl_path; 241 (*pp)->pl_next = ndmp_malloc(sizeof (path_list_t)); 242 if ((*pp)->pl_next == NULL) 243 return (NULL); 244 *pp = (*pp)->pl_next; 245 (*pp)->pl_path = NULL; 246 (*pp)->pl_next = NULL; 247 return (tpath); 248 } 249 return (NULL); 250 } 251 252 /* 253 * Create a file handle and get stats for the given path 254 */ 255 int 256 fs_getstat(char *path, fs_fhandle_t *fh, struct stat64 *st, path_list_t **pl) 257 { 258 if (lstat64(path, st) == -1) 259 return (errno); 260 261 fh->fh_fid = st->st_ino; 262 if (pl) 263 fh->fh_fpath = fs_add_pathlist(path, pl); 264 else 265 fh->fh_fpath = strdup(path); 266 return (0); 267 } 268 269 /* 270 * Get directory entries info and return in the buffer. Cookie 271 * will keep the state of each call 272 */ 273 static int 274 fs_getdents(int fildes, struct dirent *buf, size_t *nbyte, 275 char *pn_path, long *dpos, longlong_t *cookie, 276 long *n_entries, dent_arg_t *darg, path_list_t **pl) 277 { 278 struct dirent *ptr; 279 char file_path[PATH_MAX + 1]; 280 fs_fhandle_t fh; 281 struct stat64 st; 282 char *p; 283 int len; 284 int rv; 285 286 if (*nbyte == 0) { 287 (void) memset((char *)buf, 0, MAX_DENT_BUF_SIZE); 288 *nbyte = rv = getdents(fildes, buf, darg->da_size); 289 *cookie = 0LL; 290 291 if (rv <= 0) 292 return (rv); 293 } 294 295 p = (char *)buf + *cookie; 296 len = *nbyte; 297 do { 298 /* LINTED improper alignment */ 299 ptr = (struct dirent *)p; 300 *dpos = ptr->d_off; 301 (void) snprintf(file_path, PATH_MAX, "%s/", pn_path); 302 (void) strlcat(file_path, ptr->d_name, PATH_MAX); 303 (void) memset(&fh, 0, sizeof (fs_fhandle_t)); 304 305 rv = fs_getstat(file_path, &fh, &st, pl); 306 if (rv != 0) 307 break; 308 309 rv = fs_populate_dents(darg, strlen(ptr->d_name), 310 (char *)ptr->d_name, n_entries, &st, &fh); 311 312 if (rv != 0) { 313 rv = 0; 314 break; 315 } 316 317 p = p + ptr->d_reclen; 318 len -= ptr->d_reclen; 319 } while (len); 320 321 *cookie = (longlong_t)(p - (char *)buf); 322 *nbyte = len; 323 return (rv); 324 } 325 326 /* 327 * Read the directory entries and return the information about 328 * each entry 329 */ 330 int 331 fs_readdir(fs_fhandle_t *ts_fh, char *path, long *dpos, 332 char *nm, int *el, fs_fhandle_t *efh, struct stat64 *est, 333 path_list_t **pl) 334 { 335 struct dirent *dp; 336 char file_path[PATH_MAX + 1]; 337 DIR *dirp; 338 int rv; 339 340 if ((dirp = opendir(ts_fh->fh_fpath)) == NULL) 341 return (errno); 342 343 seekdir(dirp, *dpos); 344 if ((dp = readdir(dirp)) == NULL) { 345 rv = 0; /* skip this dir */ 346 *el = 0; 347 } else { 348 (void) snprintf(file_path, PATH_MAX, "%s/", path); 349 (void) strlcat(file_path, dp->d_name, PATH_MAX); 350 351 rv = fs_getstat(file_path, efh, est, pl); 352 if (rv == 0) { 353 *dpos = telldir(dirp); 354 (void) strlcpy(nm, dp->d_name, NAME_MAX); 355 *el = strlen(dp->d_name); 356 } else { 357 *el = 0; 358 } 359 } 360 (void) closedir(dirp); 361 return (rv); 362 } 363 364 /* 365 * Traverse the file system in the post-order way. The description 366 * and example is in the header file. 367 * 368 * The callback function should return 0, on success and non-zero on 369 * failure. If the callback function returns non-zero return value, 370 * the traversing stops. 371 */ 372 int 373 traverse_post(struct fs_traverse *ftp) 374 { 375 char path[PATH_MAX + 1]; /* full path name of the current dir */ 376 char nm[NAME_MAX + 1]; /* directory entry name */ 377 char *lp; /* last position on the path */ 378 int next_dir, rv; 379 int pl, el; /* path and directory entry length */ 380 cstack_t *sp; 381 fs_fhandle_t pfh, efh; 382 struct stat64 pst, est; 383 traverse_state_t *tsp; 384 struct fst_node pn, en; /* parent and entry nodes */ 385 path_list_t *plhead, *plist; 386 387 if (!ftp || !ftp->ft_path || !*ftp->ft_path || !ftp->ft_callbk) { 388 NDMP_LOG(LOG_DEBUG, "Invalid argument"); 389 errno = EINVAL; 390 return (-1); 391 } 392 393 /* set the default log function if it's not already set */ 394 if (!ftp->ft_logfp) { 395 ftp->ft_logfp = (ft_log_t)syslog; 396 NDMP_LOG(LOG_DEBUG, "Log to system log \"%s\"", ftp->ft_path); 397 } 398 399 /* set the logical path to physical path if it's not already set */ 400 if (!ftp->ft_lpath) { 401 NDMP_LOG(LOG_DEBUG, 402 "report the same paths: \"%s\"", ftp->ft_path); 403 ftp->ft_lpath = ftp->ft_path; 404 } 405 406 pl = strlen(ftp->ft_lpath); 407 if (pl + 1 > PATH_MAX) { /* +1 for the '/' */ 408 NDMP_LOG(LOG_DEBUG, "lpath too long \"%s\"", ftp->ft_path); 409 errno = ENAMETOOLONG; 410 return (-1); 411 } 412 (void) strcpy(path, ftp->ft_lpath); 413 (void) memset(&pfh, 0, sizeof (pfh)); 414 rv = fs_getstat(ftp->ft_lpath, &pfh, &pst, NULL); 415 416 if (rv != 0) { 417 NDMP_LOG(LOG_DEBUG, 418 "Error %d on fs_getstat(%s)", rv, ftp->ft_path); 419 return (rv); 420 } 421 422 if (!S_ISDIR(pst.st_mode)) { 423 pn.tn_path = ftp->ft_lpath; 424 pn.tn_fh = &pfh; 425 pn.tn_st = &pst; 426 en.tn_path = NULL; 427 en.tn_fh = NULL; 428 en.tn_st = NULL; 429 rv = CALLBACK(&pn, &en); 430 if (VERBOSE(ftp)) 431 NDMP_LOG(LOG_DEBUG, "CALLBACK(%s): %d", pn.tn_path, rv); 432 free(pfh.fh_fpath); 433 return (rv); 434 } 435 436 sp = cstack_new(); 437 if (!sp) { 438 errno = ENOMEM; 439 free(pfh.fh_fpath); 440 return (-1); 441 } 442 tsp = new_tsp(path); 443 if (!tsp) { 444 cstack_delete(sp); 445 errno = ENOMEM; 446 free(pfh.fh_fpath); 447 return (-1); 448 } 449 tsp->ts_ent = tsp->ts_end; 450 tsp->ts_fh = pfh; 451 tsp->ts_st = pst; 452 pn.tn_path = path; 453 pn.tn_fh = &tsp->ts_fh; 454 pn.tn_st = &tsp->ts_st; 455 456 if ((plist = fs_init_pathlist()) == NULL) { 457 errno = ENOMEM; 458 free(pfh.fh_fpath); 459 return (-1); 460 } 461 plhead = plist; 462 463 rv = 0; 464 next_dir = 1; 465 do { 466 if (next_dir) { 467 traverse_stats.fss_newdirs++; 468 469 *tsp->ts_end = '\0'; 470 if (VERBOSE(ftp)) 471 NDMP_LOG(LOG_DEBUG, "pl %d \"%s\"", pl, path); 472 } 473 474 next_dir = 0; 475 do { 476 el = NAME_MAX; 477 rv = fs_readdir(&tsp->ts_fh, pn.tn_path, 478 &tsp->ts_dpos, nm, &el, 479 &efh, &est, &plist); 480 481 if (rv != 0) { 482 efh.fh_fpath = NULL; 483 traverse_stats.fss_readdir_err++; 484 485 NDMP_LOG(LOG_DEBUG, 486 "Error %d on readdir(%s) pos %d", 487 rv, path, tsp->ts_dpos); 488 if (STOP_ONERR(ftp)) 489 break; 490 rv = SKIP_ENTRY; 491 492 continue; 493 } 494 495 /* done with this directory */ 496 if (el == 0) { 497 if (VERBOSE(ftp)) 498 NDMP_LOG(LOG_DEBUG, 499 "Done(%s)", pn.tn_path); 500 break; 501 } 502 nm[el] = '\0'; 503 504 if (rootfs_dot_or_dotdot(nm)) { 505 efh.fh_fpath = NULL; 506 continue; 507 } 508 509 if (VERBOSE(ftp)) 510 NDMP_LOG(LOG_DEBUG, "%u dname: \"%s\"", 511 tsp->ts_dpos, nm); 512 513 if (pl + 1 + el > PATH_MAX) { 514 traverse_stats.fss_longpath_err++; 515 516 NDMP_LOG(LOG_ERR, "Path %s/%s is too long.", 517 path, nm); 518 if (STOP_ONLONG(ftp)) 519 rv = ENAMETOOLONG; 520 efh.fh_fpath = NULL; 521 continue; 522 } 523 524 /* 525 * Push the current directory on to the stack and 526 * dive into the entry found. 527 */ 528 if (S_ISDIR(est.st_mode)) { 529 530 assert(tsp != NULL); 531 if (cstack_push(sp, tsp, 0)) { 532 rv = ENOMEM; 533 efh.fh_fpath = NULL; 534 break; 535 } 536 traverse_stats.fss_pushes++; 537 538 /* 539 * Concatenate the current entry with the 540 * current path. This will be the path of 541 * the new directory to be scanned. 542 * 543 * Note: 544 * sprintf(tsp->ts_end, "/%s", de->d_name); 545 * could be used here, but concatenating 546 * strings like this might be faster. 547 * The length of the new path has been 548 * checked above. So strcpy() can be 549 * safe and should not lead to a buffer 550 * over-run. 551 */ 552 lp = tsp->ts_end; 553 *tsp->ts_end = '/'; 554 (void) strcpy(tsp->ts_end + 1, nm); 555 556 tsp = new_tsp(path); 557 if (!tsp) { 558 efh.fh_fpath = NULL; 559 rv = ENOMEM; 560 } else { 561 next_dir = 1; 562 pl += el; 563 tsp->ts_fh = efh; 564 tsp->ts_st = est; 565 tsp->ts_ent = lp; 566 pn.tn_fh = &tsp->ts_fh; 567 pn.tn_st = &tsp->ts_st; 568 } 569 break; 570 } else { 571 /* 572 * The entry is not a directory so the 573 * callback function must be called. 574 */ 575 traverse_stats.fss_nondir_calls++; 576 577 en.tn_path = nm; 578 en.tn_fh = &efh; 579 en.tn_st = &est; 580 rv = CALLBACK(&pn, &en); 581 efh.fh_fpath = NULL; 582 if (VERBOSE(ftp)) 583 NDMP_LOG(LOG_DEBUG, 584 "CALLBACK(%s/%s): %d", 585 pn.tn_path, en.tn_path, rv); 586 587 if (rv != 0) 588 break; 589 } 590 } while (rv == 0); 591 592 /* 593 * A new directory must be processed, go to the start of 594 * the loop, open it and process it. 595 */ 596 if (next_dir) 597 continue; 598 599 if (rv == SKIP_ENTRY) 600 rv = 0; /* We should skip the current directory */ 601 602 if (rv == 0) { 603 /* 604 * Remove the ent from the end of path and send it 605 * as an entry of the path. 606 */ 607 lp = tsp->ts_ent; 608 *lp = '\0'; 609 efh = tsp->ts_fh; 610 est = tsp->ts_st; 611 free(tsp); 612 if (cstack_pop(sp, (void **)&tsp, (int *)NULL)) 613 break; 614 615 assert(tsp != NULL); 616 pl = tsp->ts_end - path; 617 618 if (VERBOSE(ftp)) 619 NDMP_LOG(LOG_DEBUG, "poped pl %d 0x%p \"%s\"", 620 pl, tsp, path); 621 622 traverse_stats.fss_pops++; 623 traverse_stats.fss_dir_calls++; 624 625 pn.tn_fh = &tsp->ts_fh; 626 pn.tn_st = &tsp->ts_st; 627 en.tn_path = lp + 1; 628 en.tn_fh = &efh; 629 en.tn_st = &est; 630 631 rv = CALLBACK(&pn, &en); 632 efh.fh_fpath = NULL; 633 if (VERBOSE(ftp)) 634 NDMP_LOG(LOG_DEBUG, "CALLBACK(%s/%s): %d", 635 pn.tn_path, en.tn_path, rv); 636 /* 637 * Does not need to free tsp here. It will be released 638 * later. 639 */ 640 } 641 642 if (rv != 0 && tsp) 643 free(tsp); 644 645 } while (rv == 0); 646 647 /* 648 * For the 'ftp->ft_path' directory itself. 649 */ 650 if (rv == 0) { 651 traverse_stats.fss_dir_calls++; 652 653 pn.tn_fh = &efh; 654 pn.tn_st = &est; 655 en.tn_path = NULL; 656 en.tn_fh = NULL; 657 en.tn_st = NULL; 658 rv = CALLBACK(&pn, &en); 659 if (VERBOSE(ftp)) 660 NDMP_LOG(LOG_DEBUG, "CALLBACK(%s): %d", pn.tn_path, rv); 661 } 662 663 /* 664 * Pop and free all the remaining entries on the stack. 665 */ 666 while (!cstack_pop(sp, (void **)&tsp, (int *)NULL)) { 667 traverse_stats.fss_stack_residue++; 668 669 free(tsp); 670 } 671 672 fs_free_pathlist(plhead); 673 free(pfh.fh_fpath); 674 cstack_delete(sp); 675 return (rv); 676 } 677 678 /* 679 * In one pass, read all the directory entries of the specified 680 * directory and call the callback function for non-directory 681 * entries. 682 * 683 * On return: 684 * 0: Lets the directory to be scanned for directory entries. 685 * < 0: Completely stops traversing. 686 * FST_SKIP: stops further scanning of the directory. Traversing 687 * will continue with the next directory in the hierarchy. 688 * SKIP_ENTRY: Failed to get the directory entries, so the caller 689 * should skip this entry. 690 */ 691 static int 692 traverse_level_nondir(struct fs_traverse *ftp, 693 traverse_state_t *tsp, struct fst_node *pnp, dent_arg_t *darg) 694 { 695 int pl; /* patth length */ 696 int rv; 697 struct fst_node en; /* entry node */ 698 longlong_t cookie_verf; 699 fs_dent_info_t *dent; 700 struct dirent *buf; 701 size_t len = 0; 702 path_list_t *plhead, *plist; 703 int fd; 704 705 rv = 0; 706 pl = strlen(pnp->tn_path); 707 708 buf = ndmp_malloc(MAX_DENT_BUF_SIZE); 709 if (buf == NULL) 710 return (errno); 711 712 fd = open(tsp->ts_fh.fh_fpath, O_RDONLY); 713 if (fd == -1) { 714 free(buf); 715 return (errno); 716 } 717 if ((plist = fs_init_pathlist()) == NULL) { 718 free(buf); 719 (void) close(fd); 720 return (errno); 721 } 722 plhead = plist; 723 724 while (rv == 0) { 725 long i, n_entries; 726 727 darg->da_end = 0; 728 n_entries = 0; 729 rv = fs_getdents(fd, buf, &len, pnp->tn_path, &tsp->ts_dpos, 730 &cookie_verf, &n_entries, darg, &plist); 731 if (n_entries == 0) 732 break; 733 if (rv != 0) { 734 traverse_stats.fss_readdir_err++; 735 736 NDMP_LOG(LOG_DEBUG, "Error %d on readdir(%s) pos %d", 737 rv, pnp->tn_path, tsp->ts_dpos); 738 if (STOP_ONERR(ftp)) { 739 NEGATE(rv); 740 break; 741 } 742 /* 743 * We cannot read the directory entry, we should 744 * skip to the next directory. 745 */ 746 rv = SKIP_ENTRY; 747 continue; 748 } 749 750 /* LINTED imporper alignment */ 751 dent = (fs_dent_info_t *)darg->da_buf; 752 /* LINTED imporper alignment */ 753 for (i = 0; i < n_entries; i++, dent = (fs_dent_info_t *) 754 ((char *)dent + dent->fd_len)) { 755 756 if (rootfs_dot_or_dotdot(dent->fd_name)) { 757 dent->fd_fh.fh_fpath = NULL; 758 continue; 759 } 760 761 if (VERBOSE(ftp)) 762 NDMP_LOG(LOG_DEBUG, "i %u dname: \"%s\"", 763 dent->fd_fh.fh_fid, dent->fd_name); 764 765 if ((pl + strlen(dent->fd_name)) > PATH_MAX) { 766 traverse_stats.fss_longpath_err++; 767 768 NDMP_LOG(LOG_ERR, "Path %s/%s is too long.", 769 pnp->tn_path, dent->fd_name); 770 if (STOP_ONLONG(ftp)) 771 rv = -ENAMETOOLONG; 772 continue; 773 } 774 775 /* 776 * The entry is not a directory so the callback 777 * function must be called. 778 */ 779 if (!S_ISDIR(dent->fd_attr.st_mode)) { 780 traverse_stats.fss_nondir_calls++; 781 782 en.tn_path = dent->fd_name; 783 en.tn_fh = &dent->fd_fh; 784 en.tn_st = &dent->fd_attr; 785 rv = CALLBACK(pnp, &en); 786 dent->fd_fh.fh_fpath = NULL; 787 if (rv < 0) 788 break; 789 if (rv == FST_SKIP) { 790 traverse_stats.fss_nondir_skipped++; 791 break; 792 } 793 } else { 794 dent->fd_fh.fh_fpath = NULL; 795 } 796 } 797 } 798 799 fs_free_pathlist(plhead); 800 free(buf); 801 (void) close(fd); 802 return (rv); 803 } 804 805 /* 806 * Traverse the file system in the level-order way. The description 807 * and example is in the header file. 808 */ 809 int 810 traverse_level(struct fs_traverse *ftp) 811 { 812 char path[PATH_MAX + 1]; /* full path name of the current dir */ 813 char nm[NAME_MAX + 1]; /* directory entry name */ 814 char *lp; /* last position on the path */ 815 int next_dir, rv; 816 int pl, el; /* path and directory entry length */ 817 818 cstack_t *sp; 819 fs_fhandle_t pfh, efh; 820 struct stat64 pst, est; 821 traverse_state_t *tsp; 822 struct fst_node pn, en; /* parent and entry nodes */ 823 dent_arg_t darg; 824 path_list_t *plhead, *plist; 825 826 if (!ftp || !ftp->ft_path || !*ftp->ft_path || !ftp->ft_callbk) { 827 NDMP_LOG(LOG_DEBUG, "Invalid argument"); 828 errno = EINVAL; 829 return (-1); 830 } 831 /* set the default log function if it's not already set */ 832 if (!ftp->ft_logfp) { 833 ftp->ft_logfp = (ft_log_t)syslog; 834 NDMP_LOG(LOG_DEBUG, "Log to system log \"%s\"", ftp->ft_path); 835 } 836 if (!ftp->ft_lpath) { 837 NDMP_LOG(LOG_DEBUG, 838 "report the same paths \"%s\"", ftp->ft_path); 839 ftp->ft_lpath = ftp->ft_path; 840 } 841 842 pl = strlen(ftp->ft_lpath); 843 if (pl + 1 > PATH_MAX) { /* +1 for the '/' */ 844 NDMP_LOG(LOG_DEBUG, "lpath too long \"%s\"", ftp->ft_path); 845 errno = ENAMETOOLONG; 846 return (-1); 847 } 848 (void) strcpy(path, ftp->ft_lpath); 849 (void) memset(&pfh, 0, sizeof (pfh)); 850 rv = fs_getstat(ftp->ft_lpath, &pfh, &pst, NULL); 851 if (rv != 0) { 852 NDMP_LOG(LOG_DEBUG, 853 "Error %d on fs_getstat(%s)", rv, ftp->ft_path); 854 return (-1); 855 } 856 857 en.tn_path = NULL; 858 en.tn_fh = NULL; 859 en.tn_st = NULL; 860 if (!S_ISDIR(pst.st_mode)) { 861 pn.tn_path = ftp->ft_lpath; 862 pn.tn_fh = &pfh; 863 pn.tn_st = &pst; 864 rv = CALLBACK(&pn, &en); 865 if (VERBOSE(ftp)) 866 NDMP_LOG(LOG_DEBUG, "CALLBACK(%s): %d", pn.tn_path, rv); 867 868 free(pfh.fh_fpath); 869 return (rv); 870 } 871 872 sp = cstack_new(); 873 if (!sp) { 874 free(pfh.fh_fpath); 875 errno = ENOMEM; 876 return (-1); 877 } 878 tsp = new_tsp(path); 879 if (!tsp) { 880 cstack_delete(sp); 881 free(pfh.fh_fpath); 882 errno = ENOMEM; 883 return (-1); 884 } 885 886 darg.da_buf = ndmp_malloc(MAX_DENT_BUF_SIZE); 887 if (!darg.da_buf) { 888 cstack_delete(sp); 889 free(pfh.fh_fpath); 890 free(tsp); 891 errno = ENOMEM; 892 return (-1); 893 } 894 darg.da_size = MAX_DENT_BUF_SIZE; 895 896 tsp->ts_ent = tsp->ts_end; 897 tsp->ts_fh = pfh; 898 tsp->ts_st = pst; 899 pn.tn_path = path; 900 pn.tn_fh = &tsp->ts_fh; 901 pn.tn_st = &tsp->ts_st; 902 903 if ((plist = fs_init_pathlist()) == NULL) { 904 cstack_delete(sp); 905 free(pfh.fh_fpath); 906 free(tsp); 907 errno = ENOMEM; 908 return (-1); 909 } 910 plhead = plist; 911 912 /* call the callback function on the path itself */ 913 traverse_stats.fss_dir_calls++; 914 rv = CALLBACK(&pn, &en); 915 if (rv < 0) { 916 free(tsp); 917 goto end; 918 } 919 if (rv == FST_SKIP) { 920 traverse_stats.fss_dir_skipped++; 921 free(tsp); 922 rv = 0; 923 goto end; 924 } 925 926 rv = 0; 927 next_dir = 1; 928 do { 929 if (next_dir) { 930 traverse_stats.fss_newdirs++; 931 932 *tsp->ts_end = '\0'; 933 if (VERBOSE(ftp)) 934 NDMP_LOG(LOG_DEBUG, "pl %d \"%s\"", pl, path); 935 936 rv = traverse_level_nondir(ftp, tsp, &pn, &darg); 937 if (rv < 0) { 938 NEGATE(rv); 939 free(tsp); 940 break; 941 } 942 /* 943 * If skipped by the callback function or 944 * error happened reading the information 945 */ 946 if (rv == FST_SKIP || rv == SKIP_ENTRY) { 947 /* 948 * N.B. next_dir should be set to 0 as 949 * well. This prevents the infinite loop. 950 * If it's not set the same directory will 951 * be poped from the stack and will be 952 * scanned again. 953 */ 954 next_dir = 0; 955 rv = 0; 956 goto skip_dir; 957 } 958 959 /* re-start reading entries of the directory */ 960 tsp->ts_dpos = 0; 961 } 962 963 next_dir = 0; 964 do { 965 el = NAME_MAX; 966 rv = fs_readdir(&tsp->ts_fh, pn.tn_path, 967 &tsp->ts_dpos, nm, &el, &efh, 968 &est, &plist); 969 if (rv != 0) { 970 traverse_stats.fss_readdir_err++; 971 972 NDMP_LOG(LOG_DEBUG, 973 "Error %d on readdir(%s) pos %d", 974 rv, path, tsp->ts_dpos); 975 if (STOP_ONERR(ftp)) 976 break; 977 rv = SKIP_ENTRY; 978 continue; 979 } 980 981 /* done with this directory */ 982 if (el == 0) 983 break; 984 985 nm[el] = '\0'; 986 987 if (rootfs_dot_or_dotdot(nm)) { 988 efh.fh_fpath = NULL; 989 continue; 990 } 991 992 if (VERBOSE(ftp)) 993 NDMP_LOG(LOG_DEBUG, "%u dname: \"%s\"", 994 tsp->ts_dpos, nm); 995 996 if (pl + 1 + el > PATH_MAX) { 997 /* 998 * The long paths were already encountered 999 * when processing non-dir entries in. 1000 * traverse_level_nondir. 1001 * We don't increase fss_longpath_err 1002 * counter for them again here. 1003 */ 1004 NDMP_LOG(LOG_ERR, "Path %s/%s is too long.", 1005 path, nm); 1006 if (STOP_ONLONG(ftp)) 1007 rv = ENAMETOOLONG; 1008 efh.fh_fpath = NULL; 1009 continue; 1010 } 1011 1012 if (!S_ISDIR(est.st_mode)) { 1013 efh.fh_fpath = NULL; 1014 continue; 1015 } 1016 1017 /* 1018 * Call the callback function for the new 1019 * directory found, then push the current 1020 * directory on to the stack. Then dive 1021 * into the entry found. 1022 */ 1023 traverse_stats.fss_dir_calls++; 1024 en.tn_path = nm; 1025 en.tn_fh = &efh; 1026 en.tn_st = &est; 1027 rv = CALLBACK(&pn, &en); 1028 1029 if (rv < 0) { 1030 NEGATE(rv); 1031 break; 1032 } 1033 if (rv == FST_SKIP) { 1034 traverse_stats.fss_dir_skipped++; 1035 rv = 0; 1036 continue; 1037 } 1038 1039 /* 1040 * Push the current directory on to the stack and 1041 * dive into the entry found. 1042 */ 1043 if (cstack_push(sp, tsp, 0)) 1044 rv = ENOMEM; 1045 else { 1046 traverse_stats.fss_pushes++; 1047 1048 lp = tsp->ts_end; 1049 *tsp->ts_end = '/'; 1050 (void) strcpy(tsp->ts_end + 1, nm); 1051 1052 tsp = new_tsp(path); 1053 if (!tsp) 1054 rv = ENOMEM; 1055 else { 1056 next_dir = 1; 1057 pl += el + 1; 1058 tsp->ts_fh = efh; 1059 tsp->ts_st = est; 1060 tsp->ts_ent = lp; 1061 pn.tn_fh = &tsp->ts_fh; 1062 pn.tn_st = &tsp->ts_st; 1063 } 1064 } 1065 break; 1066 1067 } while (rv == 0); 1068 1069 /* 1070 * A new directory must be processed, go to the start of 1071 * the loop, open it and process it. 1072 */ 1073 if (next_dir) 1074 continue; 1075 skip_dir: 1076 if (tsp) 1077 free(tsp); 1078 1079 if (rv == SKIP_ENTRY) 1080 rv = 0; 1081 1082 if (rv == 0) { 1083 if (cstack_pop(sp, (void **)&tsp, (int *)NULL)) 1084 break; 1085 1086 traverse_stats.fss_pops++; 1087 1088 if (VERBOSE(ftp)) 1089 NDMP_LOG(LOG_DEBUG, 1090 "Poped pl %d \"%s\"", pl, path); 1091 1092 *tsp->ts_end = '\0'; 1093 pl = tsp->ts_end - path; 1094 pn.tn_fh = &tsp->ts_fh; 1095 pn.tn_st = &tsp->ts_st; 1096 } 1097 } while (rv == 0); 1098 1099 /* 1100 * Pop and free all the remaining entries on the stack. 1101 */ 1102 while (!cstack_pop(sp, (void **)&tsp, (int *)NULL)) { 1103 traverse_stats.fss_stack_residue++; 1104 1105 free(tsp); 1106 } 1107 end: 1108 free(darg.da_buf); 1109 free(pfh.fh_fpath); 1110 fs_free_pathlist(plhead); 1111 cstack_delete(sp); 1112 return (rv); 1113 } 1114 1115 /* 1116 * filecopy - Copy a file 1117 * 1118 * Parameters: 1119 * char *dest - Destination path 1120 * char *src - Source path 1121 * 1122 * Returns: 1123 * 0 - No errors 1124 * #0 - Error occured 1125 * -4 - read/write error 1126 * -5 - source modified during copy 1127 * 1128 * Simplified version for Solaris 1129 */ 1130 #define BUFSIZE 32768 1131 int 1132 filecopy(char *dest, char *src) 1133 { 1134 FILE *src_fh = 0; 1135 FILE *dst_fh = 0; 1136 struct stat64 src_attr; 1137 struct stat64 dst_attr; 1138 char *buf = 0; 1139 u_longlong_t bytes_to_copy; 1140 size_t nbytes; 1141 int file_copied = 0; 1142 1143 buf = ndmp_malloc(BUFSIZE); 1144 if (!buf) 1145 return (-1); 1146 1147 src_fh = fopen(src, "r"); 1148 if (src_fh == 0) { 1149 free(buf); 1150 return (-2); 1151 } 1152 1153 dst_fh = fopen(dest, "w"); 1154 if (dst_fh == NULL) { 1155 free(buf); 1156 (void) fclose(src_fh); 1157 return (-3); 1158 } 1159 1160 if (stat64(src, &src_attr) < 0) { 1161 free(buf); 1162 (void) fclose(src_fh); 1163 (void) fclose(dst_fh); 1164 return (-2); 1165 } 1166 1167 bytes_to_copy = src_attr.st_size; 1168 while (bytes_to_copy) { 1169 if (bytes_to_copy > BUFSIZE) 1170 nbytes = BUFSIZE; 1171 else 1172 nbytes = bytes_to_copy; 1173 1174 if ((fread(buf, nbytes, 1, src_fh) != 1) || 1175 (fwrite(buf, nbytes, 1, dst_fh) != 1)) 1176 break; 1177 bytes_to_copy -= nbytes; 1178 } 1179 1180 (void) fclose(src_fh); 1181 (void) fclose(dst_fh); 1182 1183 if (bytes_to_copy > 0) { 1184 free(buf); 1185 /* short read/write, remove the partial file */ 1186 return (-4); 1187 } 1188 1189 if (stat64(src, &dst_attr) < 0) { 1190 free(buf); 1191 return (-2); 1192 } 1193 1194 free(buf); 1195 1196 if (!file_copied) 1197 return (-5); /* source modified during copy */ 1198 else 1199 return (0); 1200 } 1201