Print this page
NEX-1348 It takes 23 hours and 37 minutes to run NDMP backup 43.9 GB with10000000 3KB files
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
NEX-13094 Netbackup 8.0 failed to back up files in NDMP certification test
Reviewed by: Alex Deiter <alex.deiter@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
NEX-9532 NDMP: readdir errors when file/directory has special characters
Reviewed by: Peer Dampmann <peer.dampmann@nexenta.com>
Reviewed by: Alexander Eremin <alexander.eremin@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
NEX-5801 Snapshots left over after failed backups
Reviewed by: Rick Mesta <rick.mesta@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Revert "NEX-5801 Snapshots left over after failed backups"
This reverts commit f182fb95f09036db71fbfc6f0a6b90469b761f21.
NEX-5801 Snapshots left over after failed backups
Reviewed by: Rick Mesta <rick.mesta@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
NEX-2947 Volumes with more than a small fixed about don't traverse properly
NEX-2911 NDMP logging should use syslog and is too chatty
NEX-2692 ndmpd intermittently dumps core due to SIGABRT in umem
SUP-898 nscd is extremely slow when a local file is missing
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
Reviewed by: Josef Sipek <josef.sipek@nexenta.com>
NEX-2500 Conflict between NDMP backup job and 'zfs send' leads to NDMP job abort.


  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>


  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 {


 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




  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 /* Copyright 2017 Nexenta Systems, Inc. All rights reserved. */
  39 
  40 /*
  41  * This file implemets the post-order, pre-order and level-order
  42  * traversing of the file system.  The related macros and constants
  43  * are defined in traverse.h.
  44  */
  45 
  46 #include <sys/stat.h>
  47 #include <sys/types.h>
  48 #include <sys/param.h>
  49 #include <assert.h>
  50 #include <cstack.h>
  51 #include <dirent.h>
  52 #include <errno.h>
  53 #include <traverse.h>
  54 #include <limits.h>
  55 #include <stdarg.h>
  56 #include <stdio.h>
  57 #include <stdlib.h>
  58 #include <string.h>
  59 #include <syslog.h>


  65 /*
  66  * Check if it's "." or ".."
  67  */
  68 boolean_t
  69 rootfs_dot_or_dotdot(char *name)
  70 {
  71         if (*name != '.')
  72                 return (FALSE);
  73 
  74         if ((name[1] == 0) || (name[1] == '.' && name[2] == 0))
  75                 return (TRUE);
  76 
  77         return (FALSE);
  78 }
  79 
  80 /*
  81  * Macros on fs_traverse flags.
  82  */
  83 #define STOP_ONERR(f)   ((f)->ft_flags & FST_STOP_ONERR)
  84 #define STOP_ONLONG(f)  ((f)->ft_flags & FST_STOP_ONLONG)

  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 typedef struct {
 109         struct stat64 fd_attr;
 110         fs_fhandle_t fd_fh;
 111         short fd_len;
 112         char fd_name[1];
 113 } fs_dent_info_t;
 114 
 115 typedef struct dent_arg {
 116         char *da_buf;
 117         int da_end;
 118         int da_size;
 119 } dent_arg_t;
 120 
 121 static int traverse_level_nondir(struct fs_traverse *ftp,
 122     traverse_state_t *tsp, struct fst_node *pnp);

 123 
 124 /*































 125  * Creates a new traversing state based on the path passed to it.
 126  */
 127 static traverse_state_t *
 128 new_tsp(char *path)
 129 {
 130         traverse_state_t *tsp;
 131         tsp = ndmp_malloc(sizeof (traverse_state_t));
 132         if (!tsp)
 133                 return (NULL);
 134 
 135         tsp->ts_end = strchr(path, '\0');
 136         if (*(tsp->ts_end-1) == '/')
 137                 *--tsp->ts_end = '\0';
 138         tsp->ts_ent = NULL;
 139         tsp->ts_dpos = 0;
 140 
 141         return (tsp);
 142 }
 143 
 144 /*
 145  * Create a file handle and get stats for the given path
 146  */
 147 int
 148 fs_getstat(char *path, fs_fhandle_t *fh, struct stat64 *st)
 149 {
 150         if (lstat64(path, st) == -1) {
 151                 syslog(LOG_INFO,
 152                     "lstat64() says [%s] not found errno=(%d)", path, errno);
 153                 return (errno);
 154         }
 155 
 156         fh->fh_fid = st->st_ino;
 157 
 158         if (!S_ISDIR(st->st_mode))
 159                 fh->fh_fpath = NULL;
 160         else
 161                 fh->fh_fpath = strdup(path);
 162         return (0);
 163 }
 164 
 165 /*
































































 166  * Read the directory entries and return the information about
 167  * each entry
 168  */
 169 int
 170 fs_readdir(fs_fhandle_t *ts_fh, char *path, long *dpos,
 171     char *nm, int *el, fs_fhandle_t *efh, struct stat64 *est)
 172 {
 173         struct dirent *dp;
 174         char  file_path[PATH_MAX + 1];
 175         DIR *dirp;
 176         int rv;
 177 
 178         if ((dirp = opendir(ts_fh->fh_fpath)) == NULL)
 179                 return (errno);
 180 
 181         seekdir(dirp, *dpos);
 182         if ((dp = readdir(dirp)) == NULL) {
 183                 rv = 0;  /* skip this dir */
 184                 *el = 0;
 185         } else {


 205  *
 206  * The callback function should return 0, on success and non-zero on
 207  * failure.  If the callback function returns non-zero return value,
 208  * the traversing stops.
 209  */
 210 int
 211 traverse_post(struct fs_traverse *ftp)
 212 {
 213         char path[PATH_MAX + 1]; /* full path name of the current dir */
 214         char nm[NAME_MAX + 1]; /* directory entry name */
 215         char *lp; /* last position on the path */
 216         int next_dir, rv;
 217         int pl, el; /* path and directory entry length */
 218         cstack_t *sp;
 219         fs_fhandle_t pfh, efh;
 220         struct stat64 pst, est;
 221         traverse_state_t *tsp;
 222         struct fst_node pn, en; /* parent and entry nodes */
 223 
 224         if (!ftp || !ftp->ft_path || !*ftp->ft_path || !ftp->ft_callbk) {

 225                 errno = EINVAL;
 226                 return (-1);
 227         }
 228 
 229         /* set the default log function if it's not already set */
 230         if (!ftp->ft_logfp) {
 231                 ftp->ft_logfp = (ft_log_t)syslog;
 232                 syslog(LOG_DEBUG, "Log to system log \"%s\"", ftp->ft_path);
 233         }
 234 
 235         /* set the logical path to physical path if it's not already set */
 236         if (!ftp->ft_lpath) {
 237                 syslog(LOG_DEBUG,
 238                     "report the same paths: \"%s\"", ftp->ft_path);
 239                 ftp->ft_lpath = ftp->ft_path;
 240         }
 241 
 242         pl = strlen(ftp->ft_lpath);
 243         if (pl + 1 > PATH_MAX) { /* +1 for the '/' */
 244                 syslog(LOG_ERR, "lpath too long \"%s\"", ftp->ft_path);
 245                 errno = ENAMETOOLONG;
 246                 return (-1);
 247         }
 248         (void) strcpy(path, ftp->ft_lpath);
 249         (void) memset(&pfh, 0, sizeof (pfh));
 250         rv = fs_getstat(ftp->ft_lpath, &pfh, &pst);
 251 
 252         if (rv != 0) {
 253                 syslog(LOG_ERR,
 254                     "Error %d on fs_getstat(%s)", rv, ftp->ft_path);
 255                 return (rv);
 256         }
 257 
 258         if (!S_ISDIR(pst.st_mode)) {
 259                 pn.tn_path = ftp->ft_lpath;
 260                 pn.tn_fh = &pfh;
 261                 pn.tn_st = &pst;
 262                 en.tn_path = NULL;
 263                 en.tn_fh = NULL;
 264                 en.tn_st = NULL;
 265                 rv = CALLBACK(&pn, &en);


 266                 free(pfh.fh_fpath);
 267                 return (rv);
 268         }
 269 
 270         sp = cstack_new();
 271         if (!sp) {
 272                 errno = ENOMEM;
 273                 free(pfh.fh_fpath);
 274                 return (-1);
 275         }
 276         tsp = new_tsp(path);
 277         if (!tsp) {
 278                 cstack_delete(sp);
 279                 errno = ENOMEM;
 280                 free(pfh.fh_fpath);
 281                 return (-1);
 282         }
 283         tsp->ts_ent = tsp->ts_end;
 284         tsp->ts_fh = pfh;
 285         tsp->ts_st = pst;
 286         pn.tn_path = path;
 287         pn.tn_fh = &tsp->ts_fh;
 288         pn.tn_st = &tsp->ts_st;
 289 
 290         rv = 0;
 291         next_dir = 1;
 292         do {
 293                 if (next_dir) {


 294                         *tsp->ts_end = '\0';


 295                 }
 296 
 297                 next_dir = 0;
 298                 do {
 299                         el = NAME_MAX;
 300                         rv = fs_readdir(&tsp->ts_fh, pn.tn_path,
 301                             &tsp->ts_dpos, nm, &el,
 302                             &efh, &est);
 303 
 304                         if (rv != 0) {
 305                                 syslog(LOG_ERR,



 306                                     "Error %d on readdir(%s) pos %d",
 307                                     rv, path, tsp->ts_dpos);
 308                                 if (STOP_ONERR(ftp))
 309                                         break;
 310                                 rv = SKIP_ENTRY;
 311 
 312                                 continue;
 313                         }
 314 
 315                         /* done with this directory */
 316                         if (el == 0) {



 317                                 break;
 318                         }
 319                         nm[el] = '\0';
 320 
 321                         if (rootfs_dot_or_dotdot(nm)) {
 322                                 free(efh.fh_fpath);
 323                                 continue;
 324                         }
 325 




 326                         if (pl + 1 + el > PATH_MAX) {
 327                                 syslog(LOG_ERR, "Path %s/%s is too long.",


 328                                     path, nm);
 329                                 if (STOP_ONLONG(ftp))
 330                                         rv = ENAMETOOLONG;
 331                                 free(efh.fh_fpath);
 332                                 continue;
 333                         }
 334 
 335                         /*
 336                          * Push the current directory on to the stack and
 337                          * dive into the entry found.
 338                          */
 339                         if (S_ISDIR(est.st_mode)) {
 340 
 341                                 assert(tsp != NULL);
 342                                 if (cstack_push(sp, tsp, 0)) {
 343                                         rv = ENOMEM;
 344                                         free(efh.fh_fpath);
 345                                         break;
 346                                 }

 347 
 348                                 /*
 349                                  * Concatenate the current entry with the
 350                                  * current path.  This will be the path of
 351                                  * the new directory to be scanned.
 352                                  *
 353                                  * Note:
 354                                  * sprintf(tsp->ts_end, "/%s", de->d_name);
 355                                  * could be used here, but concatenating
 356                                  * strings like this might be faster.
 357                                  * The length of the new path has been
 358                                  * checked above.  So strcpy() can be
 359                                  * safe and should not lead to a buffer
 360                                  * over-run.
 361                                  */
 362                                 lp = tsp->ts_end;
 363                                 *tsp->ts_end = '/';
 364                                 (void) strcpy(tsp->ts_end + 1, nm);
 365 
 366                                 tsp = new_tsp(path);
 367                                 if (!tsp) {
 368                                         free(efh.fh_fpath);
 369                                         rv = ENOMEM;
 370                                 } else {
 371                                         next_dir = 1;
 372                                         pl += el;
 373                                         tsp->ts_fh = efh;
 374                                         tsp->ts_st = est;
 375                                         tsp->ts_ent = lp;
 376                                         pn.tn_fh = &tsp->ts_fh;
 377                                         pn.tn_st = &tsp->ts_st;
 378                                 }
 379                                 break;
 380                         } else {
 381                                 /*
 382                                  * The entry is not a directory so the
 383                                  * callback function must be called.
 384                                  */


 385                                 en.tn_path = nm;
 386                                 en.tn_fh = &efh;
 387                                 en.tn_st = &est;
 388                                 rv = CALLBACK(&pn, &en);
 389                                 free(efh.fh_fpath);





 390                                 if (rv != 0)
 391                                         break;
 392                         }
 393                 } while (rv == 0);
 394 
 395                 /*
 396                  * A new directory must be processed, go to the start of
 397                  * the loop, open it and process it.
 398                  */
 399                 if (next_dir)
 400                         continue;
 401 
 402                 if (rv == SKIP_ENTRY)
 403                         rv = 0; /* We should skip the current directory */
 404 
 405                 if (rv == 0) {
 406                         /*
 407                          * Remove the ent from the end of path and send it
 408                          * as an entry of the path.
 409                          */
 410                         lp = tsp->ts_ent;
 411                         *lp = '\0';
 412                         efh = tsp->ts_fh;
 413                         est = tsp->ts_st;
 414                         free(tsp);
 415                         if (cstack_pop(sp, (void **)&tsp, (int *)NULL))
 416                                 break;
 417 
 418                         assert(tsp != NULL);
 419                         pl = tsp->ts_end - path;
 420 







 421                         pn.tn_fh = &tsp->ts_fh;
 422                         pn.tn_st = &tsp->ts_st;
 423                         en.tn_path = lp + 1;
 424                         en.tn_fh = &efh;
 425                         en.tn_st = &est;
 426 
 427                         rv = CALLBACK(&pn, &en);
 428                         free(efh.fh_fpath);



 429                         /*
 430                          * Does not need to free tsp here.  It will be released
 431                          * later.
 432                          */
 433                 }
 434 
 435                 if (rv != 0 && tsp) {
 436                         free(tsp->ts_fh.fh_fpath);
 437                         free(tsp);
 438                 }
 439 
 440         } while (rv == 0);
 441 
 442         /*
 443          * For the 'ftp->ft_path' directory itself.
 444          */
 445         if (rv == 0) {


 446                 pn.tn_fh = &efh;
 447                 pn.tn_st = &est;
 448                 en.tn_path = NULL;
 449                 en.tn_fh = NULL;
 450                 en.tn_st = NULL;
 451                 rv = CALLBACK(&pn, &en);


 452         }
 453 
 454         /*
 455          * Pop and free all the remaining entries on the stack.
 456          */
 457         while (!cstack_pop(sp, (void **)&tsp, (int *)NULL)) {


 458                 free(tsp->ts_fh.fh_fpath);
 459                 free(tsp);
 460         }
 461 
 462         cstack_delete(sp);
 463         return (rv);
 464 }
 465 
 466 /*
 467  * In one pass, read all the directory entries of the specified
 468  * directory and call the callback function for non-directory
 469  * entries.
 470  *
 471  * On return:
 472  *    0: Lets the directory to be scanned for directory entries.
 473  *    < 0: Completely stops traversing.
 474  *    FST_SKIP: stops further scanning of the directory.  Traversing
 475  *        will continue with the next directory in the hierarchy.
 476  *    SKIP_ENTRY: Failed to get the directory entries, so the caller
 477  *        should skip this entry.
 478  */
 479 static int
 480 traverse_level_nondir(struct fs_traverse *ftp,
 481     traverse_state_t *tsp, struct fst_node *pnp)
 482 {
 483         struct stat64 st;
 484         fs_fhandle_t fh;
 485         DIR *dp;
 486         struct dirent *dirp;
 487         struct fst_node en; /* entry node */
 488         char path[MAXPATHLEN+MAXNAMELEN+2];
 489         int rv = 0;



 490 
 491         if ((dp = opendir(tsp->ts_fh.fh_fpath)) == NULL) {
 492                 syslog(LOG_ERR,
 493                     "traverse_level_nondir: open directory "
 494                     "%s failed: %m", tsp->ts_fh.fh_fpath);

 495                 return (errno);
 496         }
 497 
 498         while ((dirp = readdir(dp)) != NULL) {
 499                 if ((strcmp(dirp->d_name, ".") == 0) ||
 500                     (strcmp(dirp->d_name, "..") == 0)) {
 501                         continue;
 502                 }
 503 
 504                 if (!tlm_cat_path(path, tsp->ts_fh.fh_fpath,
 505                     dirp->d_name)) {

















 506                         continue;






 507                 }
 508 
 509                 if (lstat64(path, &st) != 0) {
 510                         syslog(LOG_ERR,
 511                             "traverse_level_nondir: failed to get file"
 512                             " status for %s skipping: %m", tsp->ts_fh.fh_fpath);














 513                         continue;
 514                 }
 515                 fh.fh_fid = st.st_ino;
 516 
 517                 /*
 518                  * The entry is not a directory so the callback
 519                  * function must be called.
 520                  */
 521                 if (!S_ISDIR(st.st_mode)) {
 522                         en.tn_path = dirp->d_name;
 523                         en.tn_fh = &fh;
 524                         en.tn_st = &st;


 525                         rv = CALLBACK(pnp, &en);
 526                         if (rv < 0) {
 527                                 syslog(LOG_DEBUG,
 528                                     "traverse_level_nondir: result is %d "
 529                                     "with %s", rv, path);
 530                                 break;
 531                         }
 532                         if (rv == FST_SKIP) {
 533                                 syslog(LOG_DEBUG,
 534                                     "traverse_level_nondir: skipping "
 535                                     "%s", path);
 536                                 break;
 537                         }
 538                 }
 539         }

 540 
 541         (void) closedir(dp);

 542         return (rv);
 543 }
 544 
 545 /*
 546  * Traverse the file system in the level-order way.  The description
 547  * and example is in the header file.
 548  */
 549 int
 550 traverse_level(struct fs_traverse *ftp)
 551 {
 552         char path[PATH_MAX + 1];        /* full path name of the current dir */
 553         char nm[NAME_MAX + 1];  /* directory entry name */
 554         char *lp;               /* last position on the path */
 555         int next_dir, rv;
 556         int pl, el;             /* path and directory entry length */
 557 
 558         cstack_t *sp;
 559         fs_fhandle_t pfh, efh;
 560         struct stat64 pst, est;
 561         traverse_state_t *tsp;
 562         struct fst_node pn, en;  /* parent and entry nodes */

 563 
 564         if (!ftp || !ftp->ft_path || !*ftp->ft_path || !ftp->ft_callbk) {

 565                 errno = EINVAL;
 566                 return (-1);
 567         }
 568         /* set the default log function if it's not already set */
 569         if (!ftp->ft_logfp) {
 570                 ftp->ft_logfp = (ft_log_t)syslog;
 571                 syslog(LOG_DEBUG, "Log to system log \"%s\"", ftp->ft_path);
 572         }
 573         if (!ftp->ft_lpath) {
 574                 syslog(LOG_DEBUG,
 575                     "report the same paths \"%s\"", ftp->ft_path);
 576                 ftp->ft_lpath = ftp->ft_path;
 577         }
 578 
 579         pl = strlen(ftp->ft_lpath);
 580         if (pl + 1 > PATH_MAX) { /* +1 for the '/' */
 581                 syslog(LOG_ERR, "lpath too long \"%s\"", ftp->ft_path);
 582                 errno = ENAMETOOLONG;
 583                 return (-1);
 584         }
 585         (void) strcpy(path, ftp->ft_lpath);
 586         (void) memset(&pfh, 0, sizeof (pfh));
 587         rv = fs_getstat(ftp->ft_lpath, &pfh, &pst);
 588         if (rv != 0) {
 589                 syslog(LOG_DEBUG,
 590                     "Error %d on fs_getstat(%s)", rv, ftp->ft_lpath);
 591                 return (-1);
 592         }
 593 
 594         en.tn_path = NULL;
 595         en.tn_fh = NULL;
 596         en.tn_st = NULL;
 597         if (!S_ISDIR(pst.st_mode)) {
 598                 pn.tn_path = ftp->ft_lpath;
 599                 pn.tn_fh = &pfh;
 600                 pn.tn_st = &pst;
 601                 rv = CALLBACK(&pn, &en);



 602                 free(pfh.fh_fpath);
 603                 return (rv);
 604         }
 605 
 606         sp = cstack_new();
 607         if (!sp) {
 608                 free(pfh.fh_fpath);
 609                 errno = ENOMEM;
 610                 return (-1);
 611         }
 612         tsp = new_tsp(path);
 613         if (!tsp) {
 614                 cstack_delete(sp);
 615                 free(pfh.fh_fpath);
 616                 errno = ENOMEM;
 617                 return (-1);
 618         }
 619 










 620         tsp->ts_ent = tsp->ts_end;
 621         tsp->ts_fh = pfh;
 622         tsp->ts_st = pst;
 623         pn.tn_path = path;
 624         pn.tn_fh = &tsp->ts_fh;
 625         pn.tn_st = &tsp->ts_st;
 626 
 627         /* call the callback function on the path itself */

 628         rv = CALLBACK(&pn, &en);
 629         if (rv < 0) {
 630                 free(tsp);
 631                 goto end;
 632         }
 633         if (rv == FST_SKIP) {

 634                 free(tsp);
 635                 rv = 0;
 636                 goto end;
 637         }
 638 
 639         rv = 0;
 640         next_dir = 1;
 641         do {
 642                 if (next_dir) {


 643                         *tsp->ts_end = '\0';
 644                         rv = traverse_level_nondir(ftp, tsp, &pn);



 645                         if (rv < 0) {
 646                                 NEGATE(rv);
 647                                 free(tsp->ts_fh.fh_fpath);
 648                                 free(tsp);
 649                                 break;
 650                         }
 651                         /*
 652                          * If skipped by the callback function or
 653                          * error happened reading the information
 654                          */
 655                         if (rv == FST_SKIP || rv == SKIP_ENTRY) {
 656                                 /*
 657                                  * N.B. next_dir should be set to 0 as
 658                                  * well. This prevents the infinite loop.
 659                                  * If it's not set the same directory will
 660                                  * be poped from the stack and will be
 661                                  * scanned again.
 662                                  */
 663                                 next_dir = 0;
 664                                 rv = 0;
 665                                 goto skip_dir;
 666                         }
 667 
 668                         /* re-start reading entries of the directory */
 669                         tsp->ts_dpos = 0;
 670                 }
 671 
 672                 next_dir = 0;
 673                 do {
 674                         el = NAME_MAX;
 675                         rv = fs_readdir(&tsp->ts_fh, pn.tn_path,
 676                             &tsp->ts_dpos, nm, &el, &efh,
 677                             &est);
 678                         if (rv != 0) {
 679                                 syslog(LOG_DEBUG,


 680                                     "Error %d on readdir(%s) pos %d",
 681                                     rv, path, tsp->ts_dpos);
 682                                 if (STOP_ONERR(ftp))
 683                                         break;
 684                                 rv = SKIP_ENTRY;
 685                                 continue;
 686                         }
 687 
 688                         /* done with this directory */
 689                         if (el == 0)
 690                                 break;
 691 
 692                         nm[el] = '\0';
 693 
 694                         if (rootfs_dot_or_dotdot(nm)) {
 695                                 free(efh.fh_fpath);
 696                                 continue;
 697                         }
 698 




 699                         if (pl + 1 + el > PATH_MAX) {
 700                                 /*
 701                                  * The long paths were already encountered
 702                                  * when processing non-dir entries in.
 703                                  * traverse_level_nondir.
 704                                  * We don't increase fss_longpath_err
 705                                  * counter for them again here.
 706                                  */
 707                                 syslog(LOG_ERR, "Path %s/%s is too long.",
 708                                     path, nm);
 709                                 if (STOP_ONLONG(ftp))
 710                                         rv = ENAMETOOLONG;
 711                                 free(efh.fh_fpath);
 712                                 continue;
 713                         }
 714 
 715                         if (!S_ISDIR(est.st_mode))
 716                                 continue;
 717 
 718                         /*
 719                          * Call the callback function for the new
 720                          * directory found, then push the current
 721                          * directory on to the stack.  Then dive
 722                          * into the entry found.
 723                          */

 724                         en.tn_path = nm;
 725                         en.tn_fh = &efh;
 726                         en.tn_st = &est;
 727                         rv = CALLBACK(&pn, &en);
 728 
 729                         if (rv < 0) {
 730                                 NEGATE(rv);
 731                                 free(efh.fh_fpath);
 732                                 break;
 733                         }
 734                         if (rv == FST_SKIP) {

 735                                 free(efh.fh_fpath);
 736                                 rv = 0;
 737                                 continue;
 738                         }
 739 
 740                         /*
 741                          * Push the current directory on to the stack and
 742                          * dive into the entry found.
 743                          */
 744                         if (cstack_push(sp, tsp, 0)) {
 745                                 rv = ENOMEM;
 746                         } else {


 747                                 lp = tsp->ts_end;
 748                                 *tsp->ts_end = '/';
 749                                 (void) strcpy(tsp->ts_end + 1, nm);
 750 
 751                                 tsp = new_tsp(path);
 752                                 if (!tsp)
 753                                         rv = ENOMEM;
 754                                 else {
 755                                         next_dir = 1;
 756                                         pl += el + 1;
 757                                         tsp->ts_fh = efh;
 758                                         tsp->ts_st = est;
 759                                         tsp->ts_ent = lp;
 760                                         pn.tn_fh = &tsp->ts_fh;
 761                                         pn.tn_st = &tsp->ts_st;
 762                                 }
 763                         }
 764                         break;
 765 
 766                 } while (rv == 0);
 767 
 768                 /*
 769                  * A new directory must be processed, go to the start of
 770                  * the loop, open it and process it.
 771                  */
 772                 if (next_dir)
 773                         continue;
 774 skip_dir:
 775                 if (tsp) {
 776                         free(tsp->ts_fh.fh_fpath);
 777                         free(tsp);
 778                 }
 779 
 780                 if (rv == SKIP_ENTRY)
 781                         rv = 0;
 782 
 783                 if (rv == 0) {
 784                         if (cstack_pop(sp, (void **)&tsp, (int *)NULL))
 785                                 break;
 786 






 787                         *tsp->ts_end = '\0';
 788                         pl = tsp->ts_end - path;
 789                         pn.tn_fh = &tsp->ts_fh;
 790                         pn.tn_st = &tsp->ts_st;
 791                 }
 792         } while (rv == 0);
 793 
 794         /*
 795          * Pop and free all the remaining entries on the stack.
 796          */
 797         while (!cstack_pop(sp, (void **)&tsp, (int *)NULL)) {


 798                 free(tsp->ts_fh.fh_fpath);
 799                 free(tsp);
 800         }
 801 end:

 802         cstack_delete(sp);
 803         return (rv);
 804 }
 805 
 806 /*
 807  * filecopy - Copy a file
 808  *
 809  * Parameters:
 810  *  char *dest  - Destination path
 811  *  char *src   - Source path
 812  *
 813  * Returns:
 814  *  0    - No errors
 815  *  #0   - Error occured
 816  *              -4   - read/write error
 817  *              -5   - source modified during copy
 818  *
 819  * Simplified version for Solaris
 820  */
 821 #define BUFSIZE 32768