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