1 /*
2 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
3 */
4
5 /*
6 * BSD 3 Clause License
7 *
8 * Copyright (c) 2007, The Storage Networking Industry Association.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * - Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * - Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in
18 * the documentation and/or other materials provided with the
19 * distribution.
20 *
21 * - Neither the name of The Storage Networking Industry Association (SNIA)
22 * nor the names of its contributors may be used to endorse or promote
23 * products derived from this software without specific prior written
24 * permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
27 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
30 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38 /*
39 * This file implemets the post-order, pre-order and level-order
40 * traversing of the file system. The related macros and constants
41 * are defined in traverse.h.
42 */
43
44 #include <sys/stat.h>
45 #include <sys/types.h>
46 #include <sys/param.h>
47 #include <assert.h>
48 #include <cstack.h>
49 #include <dirent.h>
50 #include <errno.h>
51 #include <traverse.h>
52 #include <limits.h>
53 #include <stdarg.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <syslog.h>
58 #include <fcntl.h>
59 #include <unistd.h>
60 #include <tlm.h>
61 #include "tlm_proto.h"
62
63 /*
64 * Check if it's "." or ".."
65 */
66 boolean_t
rootfs_dot_or_dotdot(char * name)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
fs_populate_dents(void * arg,int namelen,char * name,long * countp,struct stat64 * attr,fs_fhandle_t * fh)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 *
new_tsp(char * path)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
fs_getstat(char * path,fs_fhandle_t * fh,struct stat64 * st)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
fs_getdents(int fildes,struct dirent * buf,size_t * nbyte,char * pn_path,long * dpos,longlong_t * cookie,long * n_entries,dent_arg_t * darg)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
fs_readdir(fs_fhandle_t * ts_fh,char * path,long * dpos,char * nm,int * el,fs_fhandle_t * efh,struct stat64 * est)286 fs_readdir(fs_fhandle_t *ts_fh, char *path, long *dpos,
287 char *nm, int *el, fs_fhandle_t *efh, struct stat64 *est)
288 {
289 struct dirent *dp;
290 char file_path[PATH_MAX + 1];
291 DIR *dirp;
292 int rv;
293
294 if ((dirp = opendir(ts_fh->fh_fpath)) == NULL)
295 return (errno);
296
297 seekdir(dirp, *dpos);
298 if ((dp = readdir(dirp)) == NULL) {
299 rv = 0; /* skip this dir */
300 *el = 0;
301 } else {
302 (void) snprintf(file_path, PATH_MAX, "%s/", path);
303 (void) strlcat(file_path, dp->d_name, PATH_MAX + 1);
304
305 rv = fs_getstat(file_path, efh, est);
306 if (rv == 0) {
307 *dpos = telldir(dirp);
308 (void) strlcpy(nm, dp->d_name, NAME_MAX + 1);
309 *el = strlen(dp->d_name);
310 } else {
311 *el = 0;
312 }
313 }
314 (void) closedir(dirp);
315 return (rv);
316 }
317
318 /*
319 * Traverse the file system in the post-order way. The description
320 * and example is in the header file.
321 *
322 * The callback function should return 0, on success and non-zero on
323 * failure. If the callback function returns non-zero return value,
324 * the traversing stops.
325 */
326 int
traverse_post(struct fs_traverse * ftp)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
traverse_level_nondir(struct fs_traverse * ftp,traverse_state_t * tsp,struct fst_node * pnp,dent_arg_t * darg)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
traverse_level(struct fs_traverse * ftp)745 traverse_level(struct fs_traverse *ftp)
746 {
747 char path[PATH_MAX + 1]; /* full path name of the current dir */
748 char nm[NAME_MAX + 1]; /* directory entry name */
749 char *lp; /* last position on the path */
750 int next_dir, rv;
751 int pl, el; /* path and directory entry length */
752
753 cstack_t *sp;
754 fs_fhandle_t pfh, efh;
755 struct stat64 pst, est;
756 traverse_state_t *tsp;
757 struct fst_node pn, en; /* parent and entry nodes */
758 dent_arg_t darg;
759
760 if (!ftp || !ftp->ft_path || !*ftp->ft_path || !ftp->ft_callbk) {
761 NDMP_LOG(LOG_DEBUG, "Invalid argument");
762 errno = EINVAL;
763 return (-1);
764 }
765 /* set the default log function if it's not already set */
766 if (!ftp->ft_logfp) {
767 ftp->ft_logfp = (ft_log_t)syslog;
768 NDMP_LOG(LOG_DEBUG, "Log to system log \"%s\"", ftp->ft_path);
769 }
770 if (!ftp->ft_lpath) {
771 NDMP_LOG(LOG_DEBUG,
772 "report the same paths \"%s\"", ftp->ft_path);
773 ftp->ft_lpath = ftp->ft_path;
774 }
775
776 pl = strlen(ftp->ft_lpath);
777 if (pl + 1 > PATH_MAX) { /* +1 for the '/' */
778 NDMP_LOG(LOG_DEBUG, "lpath too long \"%s\"", ftp->ft_path);
779 errno = ENAMETOOLONG;
780 return (-1);
781 }
782 (void) strcpy(path, ftp->ft_lpath);
783 (void) memset(&pfh, 0, sizeof (pfh));
784 rv = fs_getstat(ftp->ft_lpath, &pfh, &pst);
785 if (rv != 0) {
786 NDMP_LOG(LOG_DEBUG,
787 "Error %d on fs_getstat(%s)", rv, ftp->ft_path);
788 return (-1);
789 }
790
791 en.tn_path = NULL;
792 en.tn_fh = NULL;
793 en.tn_st = NULL;
794 if (!S_ISDIR(pst.st_mode)) {
795 pn.tn_path = ftp->ft_lpath;
796 pn.tn_fh = &pfh;
797 pn.tn_st = &pst;
798 rv = CALLBACK(&pn, &en);
799 if (VERBOSE(ftp))
800 NDMP_LOG(LOG_DEBUG, "CALLBACK(%s): %d", pn.tn_path, rv);
801
802 free(pfh.fh_fpath);
803 return (rv);
804 }
805
806 sp = cstack_new();
807 if (!sp) {
808 free(pfh.fh_fpath);
809 errno = ENOMEM;
810 return (-1);
811 }
812 tsp = new_tsp(path);
813 if (!tsp) {
814 cstack_delete(sp);
815 free(pfh.fh_fpath);
816 errno = ENOMEM;
817 return (-1);
818 }
819
820 darg.da_buf = ndmp_malloc(MAX_DENT_BUF_SIZE);
821 if (!darg.da_buf) {
822 cstack_delete(sp);
823 free(pfh.fh_fpath);
824 free(tsp);
825 errno = ENOMEM;
826 return (-1);
827 }
828 darg.da_size = MAX_DENT_BUF_SIZE;
829
830 tsp->ts_ent = tsp->ts_end;
831 tsp->ts_fh = pfh;
832 tsp->ts_st = pst;
833 pn.tn_path = path;
834 pn.tn_fh = &tsp->ts_fh;
835 pn.tn_st = &tsp->ts_st;
836
837 /* call the callback function on the path itself */
838 traverse_stats.fss_dir_calls++;
839 rv = CALLBACK(&pn, &en);
840 if (rv < 0) {
841 free(tsp);
842 goto end;
843 }
844 if (rv == FST_SKIP) {
845 traverse_stats.fss_dir_skipped++;
846 free(tsp);
847 rv = 0;
848 goto end;
849 }
850
851 rv = 0;
852 next_dir = 1;
853 do {
854 if (next_dir) {
855 traverse_stats.fss_newdirs++;
856
857 *tsp->ts_end = '\0';
858 if (VERBOSE(ftp))
859 NDMP_LOG(LOG_DEBUG, "pl %d \"%s\"", pl, path);
860
861 rv = traverse_level_nondir(ftp, tsp, &pn, &darg);
862 if (rv < 0) {
863 NEGATE(rv);
864 free(tsp->ts_fh.fh_fpath);
865 free(tsp);
866 break;
867 }
868 /*
869 * If skipped by the callback function or
870 * error happened reading the information
871 */
872 if (rv == FST_SKIP || rv == SKIP_ENTRY) {
873 /*
874 * N.B. next_dir should be set to 0 as
875 * well. This prevents the infinite loop.
876 * If it's not set the same directory will
877 * be poped from the stack and will be
878 * scanned again.
879 */
880 next_dir = 0;
881 rv = 0;
882 goto skip_dir;
883 }
884
885 /* re-start reading entries of the directory */
886 tsp->ts_dpos = 0;
887 }
888
889 next_dir = 0;
890 do {
891 el = NAME_MAX;
892 rv = fs_readdir(&tsp->ts_fh, pn.tn_path,
893 &tsp->ts_dpos, nm, &el, &efh,
894 &est);
895 if (rv != 0) {
896 traverse_stats.fss_readdir_err++;
897
898 NDMP_LOG(LOG_DEBUG,
899 "Error %d on readdir(%s) pos %d",
900 rv, path, tsp->ts_dpos);
901 if (STOP_ONERR(ftp))
902 break;
903 rv = SKIP_ENTRY;
904 continue;
905 }
906
907 /* done with this directory */
908 if (el == 0)
909 break;
910
911 nm[el] = '\0';
912
913 if (rootfs_dot_or_dotdot(nm)) {
914 free(efh.fh_fpath);
915 continue;
916 }
917
918 if (VERBOSE(ftp))
919 NDMP_LOG(LOG_DEBUG, "%u dname: \"%s\"",
920 tsp->ts_dpos, nm);
921
922 if (pl + 1 + el > PATH_MAX) {
923 /*
924 * The long paths were already encountered
925 * when processing non-dir entries in.
926 * traverse_level_nondir.
927 * We don't increase fss_longpath_err
928 * counter for them again here.
929 */
930 NDMP_LOG(LOG_ERR, "Path %s/%s is too long.",
931 path, nm);
932 if (STOP_ONLONG(ftp))
933 rv = ENAMETOOLONG;
934 free(efh.fh_fpath);
935 continue;
936 }
937
938 if (!S_ISDIR(est.st_mode))
939 continue;
940
941 /*
942 * Call the callback function for the new
943 * directory found, then push the current
944 * directory on to the stack. Then dive
945 * into the entry found.
946 */
947 traverse_stats.fss_dir_calls++;
948 en.tn_path = nm;
949 en.tn_fh = &efh;
950 en.tn_st = &est;
951 rv = CALLBACK(&pn, &en);
952
953 if (rv < 0) {
954 NEGATE(rv);
955 free(efh.fh_fpath);
956 break;
957 }
958 if (rv == FST_SKIP) {
959 traverse_stats.fss_dir_skipped++;
960 free(efh.fh_fpath);
961 rv = 0;
962 continue;
963 }
964
965 /*
966 * Push the current directory on to the stack and
967 * dive into the entry found.
968 */
969 if (cstack_push(sp, tsp, 0)) {
970 rv = ENOMEM;
971 } else {
972 traverse_stats.fss_pushes++;
973
974 lp = tsp->ts_end;
975 *tsp->ts_end = '/';
976 (void) strcpy(tsp->ts_end + 1, nm);
977
978 tsp = new_tsp(path);
979 if (!tsp)
980 rv = ENOMEM;
981 else {
982 next_dir = 1;
983 pl += el + 1;
984 tsp->ts_fh = efh;
985 tsp->ts_st = est;
986 tsp->ts_ent = lp;
987 pn.tn_fh = &tsp->ts_fh;
988 pn.tn_st = &tsp->ts_st;
989 }
990 }
991 break;
992
993 } while (rv == 0);
994
995 /*
996 * A new directory must be processed, go to the start of
997 * the loop, open it and process it.
998 */
999 if (next_dir)
1000 continue;
1001 skip_dir:
1002 if (tsp) {
1003 free(tsp->ts_fh.fh_fpath);
1004 free(tsp);
1005 }
1006
1007 if (rv == SKIP_ENTRY)
1008 rv = 0;
1009
1010 if (rv == 0) {
1011 if (cstack_pop(sp, (void **)&tsp, (int *)NULL))
1012 break;
1013
1014 traverse_stats.fss_pops++;
1015
1016 if (VERBOSE(ftp))
1017 NDMP_LOG(LOG_DEBUG,
1018 "Poped pl %d \"%s\"", pl, path);
1019
1020 *tsp->ts_end = '\0';
1021 pl = tsp->ts_end - path;
1022 pn.tn_fh = &tsp->ts_fh;
1023 pn.tn_st = &tsp->ts_st;
1024 }
1025 } while (rv == 0);
1026
1027 /*
1028 * Pop and free all the remaining entries on the stack.
1029 */
1030 while (!cstack_pop(sp, (void **)&tsp, (int *)NULL)) {
1031 traverse_stats.fss_stack_residue++;
1032
1033 free(tsp->ts_fh.fh_fpath);
1034 free(tsp);
1035 }
1036 end:
1037 free(darg.da_buf);
1038 cstack_delete(sp);
1039 return (rv);
1040 }
1041
1042 /*
1043 * filecopy - Copy a file
1044 *
1045 * Parameters:
1046 * char *dest - Destination path
1047 * char *src - Source path
1048 *
1049 * Returns:
1050 * 0 - No errors
1051 * #0 - Error occured
1052 * -4 - read/write error
1053 * -5 - source modified during copy
1054 *
1055 * Simplified version for Solaris
1056 */
1057 #define BUFSIZE 32768
1058 int
filecopy(char * dest,char * src)1059 filecopy(char *dest, char *src)
1060 {
1061 FILE *src_fh = 0;
1062 FILE *dst_fh = 0;
1063 struct stat64 src_attr;
1064 struct stat64 dst_attr;
1065 char *buf = 0;
1066 u_longlong_t bytes_to_copy;
1067 size_t nbytes;
1068 int file_copied = 0;
1069
1070 buf = ndmp_malloc(BUFSIZE);
1071 if (!buf)
1072 return (-1);
1073
1074 src_fh = fopen(src, "r");
1075 if (src_fh == 0) {
1076 free(buf);
1077 return (-2);
1078 }
1079
1080 dst_fh = fopen(dest, "w");
1081 if (dst_fh == NULL) {
1082 free(buf);
1083 (void) fclose(src_fh);
1084 return (-3);
1085 }
1086
1087 if (stat64(src, &src_attr) < 0) {
1088 free(buf);
1089 (void) fclose(src_fh);
1090 (void) fclose(dst_fh);
1091 return (-2);
1092 }
1093
1094 bytes_to_copy = src_attr.st_size;
1095 while (bytes_to_copy) {
1096 if (bytes_to_copy > BUFSIZE)
1097 nbytes = BUFSIZE;
1098 else
1099 nbytes = bytes_to_copy;
1100
1101 if ((fread(buf, nbytes, 1, src_fh) != 1) ||
1102 (fwrite(buf, nbytes, 1, dst_fh) != 1))
1103 break;
1104 bytes_to_copy -= nbytes;
1105 }
1106
1107 (void) fclose(src_fh);
1108 (void) fclose(dst_fh);
1109
1110 if (bytes_to_copy > 0) {
1111 free(buf);
1112 /* short read/write, remove the partial file */
1113 return (-4);
1114 }
1115
1116 if (stat64(src, &dst_attr) < 0) {
1117 free(buf);
1118 return (-2);
1119 }
1120
1121 free(buf);
1122
1123 if (!file_copied)
1124 return (-5); /* source modified during copy */
1125 else
1126 return (0);
1127 }
1128