1 /* 2 * Copyright 2009 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 263 if (!S_ISDIR(st->st_mode)) { 264 fh->fh_fpath = NULL; 265 return (0); 266 } 267 268 if (pl) 269 fh->fh_fpath = fs_add_pathlist(path, pl); 270 else 271 fh->fh_fpath = strdup(path); 272 return (0); 273 } 274 275 /* 276 * Get directory entries info and return in the buffer. Cookie 277 * will keep the state of each call 278 */ 279 static int 280 fs_getdents(int fildes, struct dirent *buf, size_t *nbyte, 281 char *pn_path, long *dpos, longlong_t *cookie, 282 long *n_entries, dent_arg_t *darg, path_list_t **pl) 283 { 284 struct dirent *ptr; 285 char file_path[PATH_MAX + 1]; 286 fs_fhandle_t fh; 287 struct stat64 st; 288 char *p; 289 int len; 290 int rv; 291 292 if (*nbyte == 0) { 293 (void) memset((char *)buf, 0, MAX_DENT_BUF_SIZE); 294 *nbyte = rv = getdents(fildes, buf, darg->da_size); 295 *cookie = 0LL; 296 297 if (rv <= 0) 298 return (rv); 299 } 300 301 p = (char *)buf + *cookie; 302 len = *nbyte; 303 do { 304 /* LINTED improper alignment */ 305 ptr = (struct dirent *)p; 306 *dpos = ptr->d_off; 307 (void) snprintf(file_path, PATH_MAX, "%s/", pn_path); 308 (void) strlcat(file_path, ptr->d_name, PATH_MAX); 309 (void) memset(&fh, 0, sizeof (fs_fhandle_t)); 310 311 rv = fs_getstat(file_path, &fh, &st, pl); 312 if (rv != 0) 313 break; 314 315 rv = fs_populate_dents(darg, strlen(ptr->d_name), 316 (char *)ptr->d_name, n_entries, &st, &fh); 317 318 if (rv != 0) { 319 rv = 0; 320 break; 321 } 322 323 p = p + ptr->d_reclen; 324 len -= ptr->d_reclen; 325 } while (len); 326 327 *cookie = (longlong_t)(p - (char *)buf); 328 *nbyte = len; 329 return (rv); 330 } 331 332 /* 333 * Read the directory entries and return the information about 334 * each entry 335 */ 336 int 337 fs_readdir(fs_fhandle_t *ts_fh, char *path, long *dpos, 338 char *nm, int *el, fs_fhandle_t *efh, struct stat64 *est, 339 path_list_t **pl) 340 { 341 struct dirent *dp; 342 char file_path[PATH_MAX + 1]; 343 DIR *dirp; 344 int rv; 345 346 if ((dirp = opendir(ts_fh->fh_fpath)) == NULL) 347 return (errno); 348 349 seekdir(dirp, *dpos); 350 if ((dp = readdir(dirp)) == NULL) { 351 rv = 0; /* skip this dir */ 352 *el = 0; 353 } else { 354 (void) snprintf(file_path, PATH_MAX, "%s/", path); 355 (void) strlcat(file_path, dp->d_name, PATH_MAX); 356 357 rv = fs_getstat(file_path, efh, est, pl); 358 if (rv == 0) { 359 *dpos = telldir(dirp); 360 (void) strlcpy(nm, dp->d_name, NAME_MAX); 361 *el = strlen(dp->d_name); 362 } else { 363 *el = 0; 364 } 365 } 366 (void) closedir(dirp); 367 return (rv); 368 } 369 370 /* 371 * Traverse the file system in the post-order way. The description 372 * and example is in the header file. 373 * 374 * The callback function should return 0, on success and non-zero on 375 * failure. If the callback function returns non-zero return value, 376 * the traversing stops. 377 */ 378 int 379 traverse_post(struct fs_traverse *ftp) 380 { 381 char path[PATH_MAX + 1]; /* full path name of the current dir */ 382 char nm[NAME_MAX + 1]; /* directory entry name */ 383 char *lp; /* last position on the path */ 384 int next_dir, rv; 385 int pl, el; /* path and directory entry length */ 386 cstack_t *sp; 387 fs_fhandle_t pfh, efh; 388 struct stat64 pst, est; 389 traverse_state_t *tsp; 390 struct fst_node pn, en; /* parent and entry nodes */ 391 path_list_t *plhead, *plist; 392 393 if (!ftp || !ftp->ft_path || !*ftp->ft_path || !ftp->ft_callbk) { 394 NDMP_LOG(LOG_DEBUG, "Invalid argument"); 395 errno = EINVAL; 396 return (-1); 397 } 398 399 /* set the default log function if it's not already set */ 400 if (!ftp->ft_logfp) { 401 ftp->ft_logfp = (ft_log_t)syslog; 402 NDMP_LOG(LOG_DEBUG, "Log to system log \"%s\"", ftp->ft_path); 403 } 404 405 /* set the logical path to physical path if it's not already set */ 406 if (!ftp->ft_lpath) { 407 NDMP_LOG(LOG_DEBUG, 408 "report the same paths: \"%s\"", ftp->ft_path); 409 ftp->ft_lpath = ftp->ft_path; 410 } 411 412 pl = strlen(ftp->ft_lpath); 413 if (pl + 1 > PATH_MAX) { /* +1 for the '/' */ 414 NDMP_LOG(LOG_DEBUG, "lpath too long \"%s\"", ftp->ft_path); 415 errno = ENAMETOOLONG; 416 return (-1); 417 } 418 (void) strcpy(path, ftp->ft_lpath); 419 (void) memset(&pfh, 0, sizeof (pfh)); 420 rv = fs_getstat(ftp->ft_lpath, &pfh, &pst, NULL); 421 422 if (rv != 0) { 423 NDMP_LOG(LOG_DEBUG, 424 "Error %d on fs_getstat(%s)", rv, ftp->ft_path); 425 return (rv); 426 } 427 428 if (!S_ISDIR(pst.st_mode)) { 429 pn.tn_path = ftp->ft_lpath; 430 pn.tn_fh = &pfh; 431 pn.tn_st = &pst; 432 en.tn_path = NULL; 433 en.tn_fh = NULL; 434 en.tn_st = NULL; 435 rv = CALLBACK(&pn, &en); 436 if (VERBOSE(ftp)) 437 NDMP_LOG(LOG_DEBUG, "CALLBACK(%s): %d", pn.tn_path, rv); 438 free(pfh.fh_fpath); 439 return (rv); 440 } 441 442 sp = cstack_new(); 443 if (!sp) { 444 errno = ENOMEM; 445 free(pfh.fh_fpath); 446 return (-1); 447 } 448 tsp = new_tsp(path); 449 if (!tsp) { 450 cstack_delete(sp); 451 errno = ENOMEM; 452 free(pfh.fh_fpath); 453 return (-1); 454 } 455 tsp->ts_ent = tsp->ts_end; 456 tsp->ts_fh = pfh; 457 tsp->ts_st = pst; 458 pn.tn_path = path; 459 pn.tn_fh = &tsp->ts_fh; 460 pn.tn_st = &tsp->ts_st; 461 462 if ((plist = fs_init_pathlist()) == NULL) { 463 errno = ENOMEM; 464 free(pfh.fh_fpath); 465 return (-1); 466 } 467 plhead = plist; 468 469 rv = 0; 470 next_dir = 1; 471 do { 472 if (next_dir) { 473 traverse_stats.fss_newdirs++; 474 475 *tsp->ts_end = '\0'; 476 if (VERBOSE(ftp)) 477 NDMP_LOG(LOG_DEBUG, "pl %d \"%s\"", pl, path); 478 } 479 480 next_dir = 0; 481 do { 482 el = NAME_MAX; 483 rv = fs_readdir(&tsp->ts_fh, pn.tn_path, 484 &tsp->ts_dpos, nm, &el, 485 &efh, &est, &plist); 486 487 if (rv != 0) { 488 efh.fh_fpath = NULL; 489 traverse_stats.fss_readdir_err++; 490 491 NDMP_LOG(LOG_DEBUG, 492 "Error %d on readdir(%s) pos %d", 493 rv, path, tsp->ts_dpos); 494 if (STOP_ONERR(ftp)) 495 break; 496 rv = SKIP_ENTRY; 497 498 continue; 499 } 500 501 /* done with this directory */ 502 if (el == 0) { 503 if (VERBOSE(ftp)) 504 NDMP_LOG(LOG_DEBUG, 505 "Done(%s)", pn.tn_path); 506 break; 507 } 508 nm[el] = '\0'; 509 510 if (rootfs_dot_or_dotdot(nm)) { 511 efh.fh_fpath = NULL; 512 continue; 513 } 514 515 if (VERBOSE(ftp)) 516 NDMP_LOG(LOG_DEBUG, "%u dname: \"%s\"", 517 tsp->ts_dpos, nm); 518 519 if (pl + 1 + el > PATH_MAX) { 520 traverse_stats.fss_longpath_err++; 521 522 NDMP_LOG(LOG_ERR, "Path %s/%s is too long.", 523 path, nm); 524 if (STOP_ONLONG(ftp)) 525 rv = ENAMETOOLONG; 526 efh.fh_fpath = NULL; 527 continue; 528 } 529 530 /* 531 * Push the current directory on to the stack and 532 * dive into the entry found. 533 */ 534 if (S_ISDIR(est.st_mode)) { 535 536 assert(tsp != NULL); 537 if (cstack_push(sp, tsp, 0)) { 538 rv = ENOMEM; 539 efh.fh_fpath = NULL; 540 break; 541 } 542 traverse_stats.fss_pushes++; 543 544 /* 545 * Concatenate the current entry with the 546 * current path. This will be the path of 547 * the new directory to be scanned. 548 * 549 * Note: 550 * sprintf(tsp->ts_end, "/%s", de->d_name); 551 * could be used here, but concatenating 552 * strings like this might be faster. 553 * The length of the new path has been 554 * checked above. So strcpy() can be 555 * safe and should not lead to a buffer 556 * over-run. 557 */ 558 lp = tsp->ts_end; 559 *tsp->ts_end = '/'; 560 (void) strcpy(tsp->ts_end + 1, nm); 561 562 tsp = new_tsp(path); 563 if (!tsp) { 564 efh.fh_fpath = NULL; 565 rv = ENOMEM; 566 } else { 567 next_dir = 1; 568 pl += el; 569 tsp->ts_fh = efh; 570 tsp->ts_st = est; 571 tsp->ts_ent = lp; 572 pn.tn_fh = &tsp->ts_fh; 573 pn.tn_st = &tsp->ts_st; 574 } 575 break; 576 } else { 577 /* 578 * The entry is not a directory so the 579 * callback function must be called. 580 */ 581 traverse_stats.fss_nondir_calls++; 582 583 en.tn_path = nm; 584 en.tn_fh = &efh; 585 en.tn_st = &est; 586 rv = CALLBACK(&pn, &en); 587 efh.fh_fpath = NULL; 588 if (VERBOSE(ftp)) 589 NDMP_LOG(LOG_DEBUG, 590 "CALLBACK(%s/%s): %d", 591 pn.tn_path, en.tn_path, rv); 592 593 if (rv != 0) 594 break; 595 } 596 } while (rv == 0); 597 598 /* 599 * A new directory must be processed, go to the start of 600 * the loop, open it and process it. 601 */ 602 if (next_dir) 603 continue; 604 605 if (rv == SKIP_ENTRY) 606 rv = 0; /* We should skip the current directory */ 607 608 if (rv == 0) { 609 /* 610 * Remove the ent from the end of path and send it 611 * as an entry of the path. 612 */ 613 lp = tsp->ts_ent; 614 *lp = '\0'; 615 efh = tsp->ts_fh; 616 est = tsp->ts_st; 617 free(tsp); 618 if (cstack_pop(sp, (void **)&tsp, (int *)NULL)) 619 break; 620 621 assert(tsp != NULL); 622 pl = tsp->ts_end - path; 623 624 if (VERBOSE(ftp)) 625 NDMP_LOG(LOG_DEBUG, "poped pl %d 0x%p \"%s\"", 626 pl, tsp, path); 627 628 traverse_stats.fss_pops++; 629 traverse_stats.fss_dir_calls++; 630 631 pn.tn_fh = &tsp->ts_fh; 632 pn.tn_st = &tsp->ts_st; 633 en.tn_path = lp + 1; 634 en.tn_fh = &efh; 635 en.tn_st = &est; 636 637 rv = CALLBACK(&pn, &en); 638 efh.fh_fpath = NULL; 639 if (VERBOSE(ftp)) 640 NDMP_LOG(LOG_DEBUG, "CALLBACK(%s/%s): %d", 641 pn.tn_path, en.tn_path, rv); 642 /* 643 * Does not need to free tsp here. It will be released 644 * later. 645 */ 646 } 647 648 if (rv != 0 && tsp) 649 free(tsp); 650 651 } while (rv == 0); 652 653 /* 654 * For the 'ftp->ft_path' directory itself. 655 */ 656 if (rv == 0) { 657 traverse_stats.fss_dir_calls++; 658 659 pn.tn_fh = &efh; 660 pn.tn_st = &est; 661 en.tn_path = NULL; 662 en.tn_fh = NULL; 663 en.tn_st = NULL; 664 rv = CALLBACK(&pn, &en); 665 if (VERBOSE(ftp)) 666 NDMP_LOG(LOG_DEBUG, "CALLBACK(%s): %d", pn.tn_path, rv); 667 } 668 669 /* 670 * Pop and free all the remaining entries on the stack. 671 */ 672 while (!cstack_pop(sp, (void **)&tsp, (int *)NULL)) { 673 traverse_stats.fss_stack_residue++; 674 675 free(tsp); 676 } 677 678 fs_free_pathlist(plhead); 679 free(pfh.fh_fpath); 680 cstack_delete(sp); 681 return (rv); 682 } 683 684 /* 685 * In one pass, read all the directory entries of the specified 686 * directory and call the callback function for non-directory 687 * entries. 688 * 689 * On return: 690 * 0: Lets the directory to be scanned for directory entries. 691 * < 0: Completely stops traversing. 692 * FST_SKIP: stops further scanning of the directory. Traversing 693 * will continue with the next directory in the hierarchy. 694 * SKIP_ENTRY: Failed to get the directory entries, so the caller 695 * should skip this entry. 696 */ 697 static int 698 traverse_level_nondir(struct fs_traverse *ftp, 699 traverse_state_t *tsp, struct fst_node *pnp, dent_arg_t *darg) 700 { 701 int pl; /* patth length */ 702 int rv; 703 struct fst_node en; /* entry node */ 704 longlong_t cookie_verf; 705 fs_dent_info_t *dent; 706 struct dirent *buf; 707 size_t len = 0; 708 path_list_t *plhead, *plist; 709 int fd; 710 711 rv = 0; 712 pl = strlen(pnp->tn_path); 713 714 buf = ndmp_malloc(MAX_DENT_BUF_SIZE); 715 if (buf == NULL) 716 return (errno); 717 718 fd = open(tsp->ts_fh.fh_fpath, O_RDONLY); 719 if (fd == -1) { 720 free(buf); 721 return (errno); 722 } 723 if ((plist = fs_init_pathlist()) == NULL) { 724 free(buf); 725 (void) close(fd); 726 return (errno); 727 } 728 plhead = plist; 729 730 while (rv == 0) { 731 long i, n_entries; 732 733 darg->da_end = 0; 734 n_entries = 0; 735 rv = fs_getdents(fd, buf, &len, pnp->tn_path, &tsp->ts_dpos, 736 &cookie_verf, &n_entries, darg, &plist); 737 if (n_entries == 0) 738 break; 739 if (rv != 0) { 740 traverse_stats.fss_readdir_err++; 741 742 NDMP_LOG(LOG_DEBUG, "Error %d on readdir(%s) pos %d", 743 rv, pnp->tn_path, tsp->ts_dpos); 744 if (STOP_ONERR(ftp)) { 745 NEGATE(rv); 746 break; 747 } 748 /* 749 * We cannot read the directory entry, we should 750 * skip to the next directory. 751 */ 752 rv = SKIP_ENTRY; 753 continue; 754 } 755 756 /* LINTED imporper alignment */ 757 dent = (fs_dent_info_t *)darg->da_buf; 758 /* LINTED imporper alignment */ 759 for (i = 0; i < n_entries; i++, dent = (fs_dent_info_t *) 760 ((char *)dent + dent->fd_len)) { 761 762 if (rootfs_dot_or_dotdot(dent->fd_name)) { 763 dent->fd_fh.fh_fpath = NULL; 764 continue; 765 } 766 767 if (VERBOSE(ftp)) 768 NDMP_LOG(LOG_DEBUG, "i %u dname: \"%s\"", 769 dent->fd_fh.fh_fid, dent->fd_name); 770 771 if ((pl + strlen(dent->fd_name)) > PATH_MAX) { 772 traverse_stats.fss_longpath_err++; 773 774 NDMP_LOG(LOG_ERR, "Path %s/%s is too long.", 775 pnp->tn_path, dent->fd_name); 776 if (STOP_ONLONG(ftp)) 777 rv = -ENAMETOOLONG; 778 continue; 779 } 780 781 /* 782 * The entry is not a directory so the callback 783 * function must be called. 784 */ 785 if (!S_ISDIR(dent->fd_attr.st_mode)) { 786 traverse_stats.fss_nondir_calls++; 787 788 en.tn_path = dent->fd_name; 789 en.tn_fh = &dent->fd_fh; 790 en.tn_st = &dent->fd_attr; 791 rv = CALLBACK(pnp, &en); 792 dent->fd_fh.fh_fpath = NULL; 793 if (rv < 0) 794 break; 795 if (rv == FST_SKIP) { 796 traverse_stats.fss_nondir_skipped++; 797 break; 798 } 799 } else { 800 dent->fd_fh.fh_fpath = NULL; 801 } 802 } 803 } 804 805 fs_free_pathlist(plhead); 806 free(buf); 807 (void) close(fd); 808 return (rv); 809 } 810 811 /* 812 * Traverse the file system in the level-order way. The description 813 * and example is in the header file. 814 */ 815 int 816 traverse_level(struct fs_traverse *ftp) 817 { 818 char path[PATH_MAX + 1]; /* full path name of the current dir */ 819 char nm[NAME_MAX + 1]; /* directory entry name */ 820 char *lp; /* last position on the path */ 821 int next_dir, rv; 822 int pl, el; /* path and directory entry length */ 823 824 cstack_t *sp; 825 fs_fhandle_t pfh, efh; 826 struct stat64 pst, est; 827 traverse_state_t *tsp; 828 struct fst_node pn, en; /* parent and entry nodes */ 829 dent_arg_t darg; 830 path_list_t *plhead, *plist; 831 832 if (!ftp || !ftp->ft_path || !*ftp->ft_path || !ftp->ft_callbk) { 833 NDMP_LOG(LOG_DEBUG, "Invalid argument"); 834 errno = EINVAL; 835 return (-1); 836 } 837 /* set the default log function if it's not already set */ 838 if (!ftp->ft_logfp) { 839 ftp->ft_logfp = (ft_log_t)syslog; 840 NDMP_LOG(LOG_DEBUG, "Log to system log \"%s\"", ftp->ft_path); 841 } 842 if (!ftp->ft_lpath) { 843 NDMP_LOG(LOG_DEBUG, 844 "report the same paths \"%s\"", ftp->ft_path); 845 ftp->ft_lpath = ftp->ft_path; 846 } 847 848 pl = strlen(ftp->ft_lpath); 849 if (pl + 1 > PATH_MAX) { /* +1 for the '/' */ 850 NDMP_LOG(LOG_DEBUG, "lpath too long \"%s\"", ftp->ft_path); 851 errno = ENAMETOOLONG; 852 return (-1); 853 } 854 (void) strcpy(path, ftp->ft_lpath); 855 (void) memset(&pfh, 0, sizeof (pfh)); 856 rv = fs_getstat(ftp->ft_lpath, &pfh, &pst, NULL); 857 if (rv != 0) { 858 NDMP_LOG(LOG_DEBUG, 859 "Error %d on fs_getstat(%s)", rv, ftp->ft_path); 860 return (-1); 861 } 862 863 en.tn_path = NULL; 864 en.tn_fh = NULL; 865 en.tn_st = NULL; 866 if (!S_ISDIR(pst.st_mode)) { 867 pn.tn_path = ftp->ft_lpath; 868 pn.tn_fh = &pfh; 869 pn.tn_st = &pst; 870 rv = CALLBACK(&pn, &en); 871 if (VERBOSE(ftp)) 872 NDMP_LOG(LOG_DEBUG, "CALLBACK(%s): %d", pn.tn_path, rv); 873 874 free(pfh.fh_fpath); 875 return (rv); 876 } 877 878 sp = cstack_new(); 879 if (!sp) { 880 free(pfh.fh_fpath); 881 errno = ENOMEM; 882 return (-1); 883 } 884 tsp = new_tsp(path); 885 if (!tsp) { 886 cstack_delete(sp); 887 free(pfh.fh_fpath); 888 errno = ENOMEM; 889 return (-1); 890 } 891 892 darg.da_buf = ndmp_malloc(MAX_DENT_BUF_SIZE); 893 if (!darg.da_buf) { 894 cstack_delete(sp); 895 free(pfh.fh_fpath); 896 free(tsp); 897 errno = ENOMEM; 898 return (-1); 899 } 900 darg.da_size = MAX_DENT_BUF_SIZE; 901 902 tsp->ts_ent = tsp->ts_end; 903 tsp->ts_fh = pfh; 904 tsp->ts_st = pst; 905 pn.tn_path = path; 906 pn.tn_fh = &tsp->ts_fh; 907 pn.tn_st = &tsp->ts_st; 908 909 if ((plist = fs_init_pathlist()) == NULL) { 910 cstack_delete(sp); 911 free(pfh.fh_fpath); 912 free(tsp); 913 errno = ENOMEM; 914 return (-1); 915 } 916 plhead = plist; 917 918 /* call the callback function on the path itself */ 919 traverse_stats.fss_dir_calls++; 920 rv = CALLBACK(&pn, &en); 921 if (rv < 0) { 922 free(tsp); 923 goto end; 924 } 925 if (rv == FST_SKIP) { 926 traverse_stats.fss_dir_skipped++; 927 free(tsp); 928 rv = 0; 929 goto end; 930 } 931 932 rv = 0; 933 next_dir = 1; 934 do { 935 if (next_dir) { 936 traverse_stats.fss_newdirs++; 937 938 *tsp->ts_end = '\0'; 939 if (VERBOSE(ftp)) 940 NDMP_LOG(LOG_DEBUG, "pl %d \"%s\"", pl, path); 941 942 rv = traverse_level_nondir(ftp, tsp, &pn, &darg); 943 if (rv < 0) { 944 NEGATE(rv); 945 free(tsp); 946 break; 947 } 948 /* 949 * If skipped by the callback function or 950 * error happened reading the information 951 */ 952 if (rv == FST_SKIP || rv == SKIP_ENTRY) { 953 /* 954 * N.B. next_dir should be set to 0 as 955 * well. This prevents the infinite loop. 956 * If it's not set the same directory will 957 * be poped from the stack and will be 958 * scanned again. 959 */ 960 next_dir = 0; 961 rv = 0; 962 goto skip_dir; 963 } 964 965 /* re-start reading entries of the directory */ 966 tsp->ts_dpos = 0; 967 } 968 969 next_dir = 0; 970 do { 971 el = NAME_MAX; 972 rv = fs_readdir(&tsp->ts_fh, pn.tn_path, 973 &tsp->ts_dpos, nm, &el, &efh, 974 &est, &plist); 975 if (rv != 0) { 976 traverse_stats.fss_readdir_err++; 977 978 NDMP_LOG(LOG_DEBUG, 979 "Error %d on readdir(%s) pos %d", 980 rv, path, tsp->ts_dpos); 981 if (STOP_ONERR(ftp)) 982 break; 983 rv = SKIP_ENTRY; 984 continue; 985 } 986 987 /* done with this directory */ 988 if (el == 0) 989 break; 990 991 nm[el] = '\0'; 992 993 if (rootfs_dot_or_dotdot(nm)) { 994 efh.fh_fpath = NULL; 995 continue; 996 } 997 998 if (VERBOSE(ftp)) 999 NDMP_LOG(LOG_DEBUG, "%u dname: \"%s\"", 1000 tsp->ts_dpos, nm); 1001 1002 if (pl + 1 + el > PATH_MAX) { 1003 /* 1004 * The long paths were already encountered 1005 * when processing non-dir entries in. 1006 * traverse_level_nondir. 1007 * We don't increase fss_longpath_err 1008 * counter for them again here. 1009 */ 1010 NDMP_LOG(LOG_ERR, "Path %s/%s is too long.", 1011 path, nm); 1012 if (STOP_ONLONG(ftp)) 1013 rv = ENAMETOOLONG; 1014 efh.fh_fpath = NULL; 1015 continue; 1016 } 1017 1018 if (!S_ISDIR(est.st_mode)) { 1019 efh.fh_fpath = NULL; 1020 continue; 1021 } 1022 1023 /* 1024 * Call the callback function for the new 1025 * directory found, then push the current 1026 * directory on to the stack. Then dive 1027 * into the entry found. 1028 */ 1029 traverse_stats.fss_dir_calls++; 1030 en.tn_path = nm; 1031 en.tn_fh = &efh; 1032 en.tn_st = &est; 1033 rv = CALLBACK(&pn, &en); 1034 1035 if (rv < 0) { 1036 NEGATE(rv); 1037 break; 1038 } 1039 if (rv == FST_SKIP) { 1040 traverse_stats.fss_dir_skipped++; 1041 rv = 0; 1042 continue; 1043 } 1044 1045 /* 1046 * Push the current directory on to the stack and 1047 * dive into the entry found. 1048 */ 1049 if (cstack_push(sp, tsp, 0)) 1050 rv = ENOMEM; 1051 else { 1052 traverse_stats.fss_pushes++; 1053 1054 lp = tsp->ts_end; 1055 *tsp->ts_end = '/'; 1056 (void) strcpy(tsp->ts_end + 1, nm); 1057 1058 tsp = new_tsp(path); 1059 if (!tsp) 1060 rv = ENOMEM; 1061 else { 1062 next_dir = 1; 1063 pl += el + 1; 1064 tsp->ts_fh = efh; 1065 tsp->ts_st = est; 1066 tsp->ts_ent = lp; 1067 pn.tn_fh = &tsp->ts_fh; 1068 pn.tn_st = &tsp->ts_st; 1069 } 1070 } 1071 break; 1072 1073 } while (rv == 0); 1074 1075 /* 1076 * A new directory must be processed, go to the start of 1077 * the loop, open it and process it. 1078 */ 1079 if (next_dir) 1080 continue; 1081 skip_dir: 1082 if (tsp) 1083 free(tsp); 1084 1085 if (rv == SKIP_ENTRY) 1086 rv = 0; 1087 1088 if (rv == 0) { 1089 if (cstack_pop(sp, (void **)&tsp, (int *)NULL)) 1090 break; 1091 1092 traverse_stats.fss_pops++; 1093 1094 if (VERBOSE(ftp)) 1095 NDMP_LOG(LOG_DEBUG, 1096 "Poped pl %d \"%s\"", pl, path); 1097 1098 *tsp->ts_end = '\0'; 1099 pl = tsp->ts_end - path; 1100 pn.tn_fh = &tsp->ts_fh; 1101 pn.tn_st = &tsp->ts_st; 1102 } 1103 } while (rv == 0); 1104 1105 /* 1106 * Pop and free all the remaining entries on the stack. 1107 */ 1108 while (!cstack_pop(sp, (void **)&tsp, (int *)NULL)) { 1109 traverse_stats.fss_stack_residue++; 1110 1111 free(tsp); 1112 } 1113 end: 1114 free(darg.da_buf); 1115 free(pfh.fh_fpath); 1116 fs_free_pathlist(plhead); 1117 cstack_delete(sp); 1118 return (rv); 1119 } 1120 1121 /* 1122 * filecopy - Copy a file 1123 * 1124 * Parameters: 1125 * char *dest - Destination path 1126 * char *src - Source path 1127 * 1128 * Returns: 1129 * 0 - No errors 1130 * #0 - Error occured 1131 * -4 - read/write error 1132 * -5 - source modified during copy 1133 * 1134 * Simplified version for Solaris 1135 */ 1136 #define BUFSIZE 32768 1137 int 1138 filecopy(char *dest, char *src) 1139 { 1140 FILE *src_fh = 0; 1141 FILE *dst_fh = 0; 1142 struct stat64 src_attr; 1143 struct stat64 dst_attr; 1144 char *buf = 0; 1145 u_longlong_t bytes_to_copy; 1146 size_t nbytes; 1147 int file_copied = 0; 1148 1149 buf = ndmp_malloc(BUFSIZE); 1150 if (!buf) 1151 return (-1); 1152 1153 src_fh = fopen(src, "r"); 1154 if (src_fh == 0) { 1155 free(buf); 1156 return (-2); 1157 } 1158 1159 dst_fh = fopen(dest, "w"); 1160 if (dst_fh == NULL) { 1161 free(buf); 1162 (void) fclose(src_fh); 1163 return (-3); 1164 } 1165 1166 if (stat64(src, &src_attr) < 0) { 1167 free(buf); 1168 (void) fclose(src_fh); 1169 (void) fclose(dst_fh); 1170 return (-2); 1171 } 1172 1173 bytes_to_copy = src_attr.st_size; 1174 while (bytes_to_copy) { 1175 if (bytes_to_copy > BUFSIZE) 1176 nbytes = BUFSIZE; 1177 else 1178 nbytes = bytes_to_copy; 1179 1180 if ((fread(buf, nbytes, 1, src_fh) != 1) || 1181 (fwrite(buf, nbytes, 1, dst_fh) != 1)) 1182 break; 1183 bytes_to_copy -= nbytes; 1184 } 1185 1186 (void) fclose(src_fh); 1187 (void) fclose(dst_fh); 1188 1189 if (bytes_to_copy > 0) { 1190 free(buf); 1191 /* short read/write, remove the partial file */ 1192 return (-4); 1193 } 1194 1195 if (stat64(src, &dst_attr) < 0) { 1196 free(buf); 1197 return (-2); 1198 } 1199 1200 free(buf); 1201 1202 if (!file_copied) 1203 return (-5); /* source modified during copy */ 1204 else 1205 return (0); 1206 } 1207