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