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