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
|