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 }