xref: /illumos-gate/usr/src/cmd/ndmpd/tlm/tlm_traverse.c (revision 2654012f83cec5dc15b61dfe3e4a4915f186e7a6)
1 /*
2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * BSD 3 Clause License
8  *
9  * Copyright (c) 2007, The Storage Networking Industry Association.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 	- Redistributions of source code must retain the above copyright
15  *	  notice, this list of conditions and the following disclaimer.
16  *
17  * 	- Redistributions in binary form must reproduce the above copyright
18  *	  notice, this list of conditions and the following disclaimer in
19  *	  the documentation and/or other materials provided with the
20  *	  distribution.
21  *
22  *	- Neither the name of The Storage Networking Industry Association (SNIA)
23  *	  nor the names of its contributors may be used to endorse or promote
24  *	  products derived from this software without specific prior written
25  *	  permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
28  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
31  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37  * POSSIBILITY OF SUCH DAMAGE.
38  */
39 /*
40  * This file implemets the post-order, pre-order and level-order
41  * traversing of the file system.  The related macros and constants
42  * are defined in traverse.h.
43  */
44 
45 #include <sys/stat.h>
46 #include <sys/types.h>
47 #include <sys/param.h>
48 #include <assert.h>
49 #include <cstack.h>
50 #include <dirent.h>
51 #include <errno.h>
52 #include <traverse.h>
53 #include <limits.h>
54 #include <stdarg.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <syslog.h>
59 #include <fcntl.h>
60 #include <unistd.h>
61 #include <tlm.h>
62 #include "tlm_proto.h"
63 
64 /*
65  * Check if it's "." or ".."
66  */
67 boolean_t
68 rootfs_dot_or_dotdot(char *name)
69 {
70 	if (*name != '.')
71 		return (FALSE);
72 
73 	if ((name[1] == 0) || (name[1] == '.' && name[2] == 0))
74 		return (TRUE);
75 
76 	return (FALSE);
77 }
78 
79 /*
80  * Macros on fs_traverse flags.
81  */
82 #define	STOP_ONERR(f)	((f)->ft_flags & FST_STOP_ONERR)
83 #define	STOP_ONLONG(f)	((f)->ft_flags & FST_STOP_ONLONG)
84 #define	VERBOSE(f)	((f)->ft_flags & FST_VERBOSE)
85 
86 #define	CALLBACK(pp, ep)	\
87 	(*(ftp)->ft_callbk)((ftp)->ft_arg, pp, ep)
88 
89 #define	NEGATE(rv)	((rv) =	-(rv))
90 
91 /*
92  * The traversing state that is pushed onto the stack.
93  * This include:
94  * 	- The end of the path of the current directory.
95  *	- The position of the last component on it.
96  *	- The read position in the directory.
97  *	- The file handle of the directory.
98  *	- The stat of the directory.
99  */
100 typedef struct traverse_state {
101 	char *ts_end;
102 	char *ts_ent;
103 	long ts_dpos; /* position in the directory when reading its entries */
104 	fs_fhandle_t ts_fh;
105 	struct stat64 ts_st;
106 } traverse_state_t;
107 
108 /*
109  * Statistics gathering structure.
110  */
111 typedef struct traverse_statistics {
112 	ulong_t fss_newdirs;
113 	ulong_t fss_readdir_err;
114 	ulong_t fss_longpath_err;
115 	ulong_t fss_lookup_err;
116 	ulong_t fss_nondir_calls;
117 	ulong_t fss_dir_calls;
118 	ulong_t fss_nondir_skipped;
119 	ulong_t fss_dir_skipped;
120 	ulong_t fss_pushes;
121 	ulong_t fss_pops;
122 	ulong_t fss_stack_residue;
123 } traverse_statistics_t;
124 
125 /*
126  * Global instance of statistics variable.
127  */
128 traverse_statistics_t traverse_stats;
129 
130 #define	MAX_DENT_BUF_SIZE	(8 * 1024)
131 
132 typedef struct {
133 	struct stat64 fd_attr;
134 	fs_fhandle_t fd_fh;
135 	short fd_len;
136 	char fd_name[1];
137 } fs_dent_info_t;
138 
139 typedef struct dent_arg {
140 	char *da_buf;
141 	int da_end;
142 	int da_size;
143 } dent_arg_t;
144 
145 static int traverse_level_nondir(struct fs_traverse *ftp,
146     traverse_state_t *tsp, struct fst_node *pnp,
147     dent_arg_t *darg);
148 
149 /*
150  * Gather some directory entry information and return them
151  */
152 static int
153 fs_populate_dents(void *arg, int namelen,
154     char *name, long *countp, struct stat64 *attr,
155     fs_fhandle_t *fh)
156 {
157 	dent_arg_t *darg = (dent_arg_t *)arg;
158 	int reclen = sizeof (fs_dent_info_t) + namelen;
159 	fs_dent_info_t *dent;
160 
161 	if ((darg->da_end + reclen) > darg->da_size)
162 		return (-1);
163 
164 	/* LINTED improper alignment */
165 	dent = (fs_dent_info_t *)(darg->da_buf + darg->da_end);
166 
167 	dent->fd_attr = *attr;
168 	dent->fd_fh = *fh;
169 	(void) strcpy(dent->fd_name, name);
170 
171 	dent->fd_len = reclen;
172 	darg->da_end += reclen;
173 
174 	if (countp)
175 		(*countp)++;
176 
177 	return (0);
178 }
179 
180 /*
181  * Creates a new traversing state based on the path passed to it.
182  */
183 static traverse_state_t *
184 new_tsp(char *path)
185 {
186 	traverse_state_t *tsp;
187 	tsp = ndmp_malloc(sizeof (traverse_state_t));
188 	if (!tsp)
189 		return (NULL);
190 
191 	tsp->ts_end = strchr(path, '\0');
192 	if (*(tsp->ts_end-1) == '/')
193 		*--tsp->ts_end = '\0';
194 	tsp->ts_ent = NULL;
195 	tsp->ts_dpos = 0;
196 
197 	return (tsp);
198 }
199 
200 /*
201  * Initialize a list for path names
202  */
203 path_list_t *
204 fs_init_pathlist()
205 {
206 	path_list_t *pl_head;
207 
208 	pl_head = ndmp_malloc(sizeof (path_list_t));
209 	return (pl_head);
210 }
211 
212 /*
213  * Free the list of path names
214  */
215 void
216 fs_free_pathlist(path_list_t *pl_head)
217 {
218 	path_list_t *p = pl_head;
219 
220 	while (p) {
221 		p = pl_head->pl_next;
222 		free(pl_head->pl_path);
223 		free(pl_head);
224 		pl_head = p;
225 	}
226 }
227 
228 /*
229  * Add a path in the list of path names
230  */
231 char *
232 fs_add_pathlist(char *path, path_list_t **pp)
233 {
234 	char *tpath;
235 
236 	if (*pp) {
237 		(*pp)->pl_path = strdup(path);
238 		if ((*pp)->pl_path == NULL)
239 			return (NULL);
240 		tpath = (*pp)->pl_path;
241 		(*pp)->pl_next = ndmp_malloc(sizeof (path_list_t));
242 		if ((*pp)->pl_next == NULL)
243 			return (NULL);
244 		*pp = (*pp)->pl_next;
245 		(*pp)->pl_path = NULL;
246 		(*pp)->pl_next = NULL;
247 		return (tpath);
248 	}
249 	return (NULL);
250 }
251 
252 /*
253  * Create a file handle and get stats for the given path
254  */
255 int
256 fs_getstat(char *path, fs_fhandle_t *fh, struct stat64 *st, path_list_t **pl)
257 {
258 	if (lstat64(path, st) == -1)
259 		return (errno);
260 
261 	fh->fh_fid = st->st_ino;
262 	if (pl)
263 		fh->fh_fpath = fs_add_pathlist(path, pl);
264 	else
265 		fh->fh_fpath = strdup(path);
266 	return (0);
267 }
268 
269 /*
270  * Get directory entries info and return in the buffer. Cookie
271  * will keep the state of each call
272  */
273 static int
274 fs_getdents(int fildes, struct dirent *buf, size_t *nbyte,
275     char *pn_path, long *dpos, longlong_t *cookie,
276     long *n_entries, dent_arg_t *darg, path_list_t **pl)
277 {
278 	struct dirent *ptr;
279 	char file_path[PATH_MAX + 1];
280 	fs_fhandle_t fh;
281 	struct stat64 st;
282 	char *p;
283 	int len;
284 	int rv;
285 
286 	if (*nbyte == 0) {
287 		(void) memset((char *)buf, 0, MAX_DENT_BUF_SIZE);
288 		*nbyte = rv = getdents(fildes, buf, darg->da_size);
289 		*cookie = 0LL;
290 
291 		if (rv <= 0)
292 			return (rv);
293 	}
294 
295 	p = (char *)buf + *cookie;
296 	len = *nbyte;
297 	do {
298 		/* LINTED improper alignment */
299 		ptr = (struct dirent *)p;
300 		*dpos =  ptr->d_off;
301 		(void) snprintf(file_path, PATH_MAX, "%s/", pn_path);
302 		(void) strlcat(file_path, ptr->d_name, PATH_MAX);
303 		(void) memset(&fh, 0, sizeof (fs_fhandle_t));
304 
305 		rv = fs_getstat(file_path, &fh, &st, pl);
306 		if (rv != 0)
307 			break;
308 
309 		rv = fs_populate_dents(darg, strlen(ptr->d_name),
310 		    (char *)ptr->d_name, n_entries, &st, &fh);
311 
312 		if (rv != 0) {
313 			rv = 0;
314 			break;
315 		}
316 
317 		p = p + ptr->d_reclen;
318 		len -= ptr->d_reclen;
319 	} while (len);
320 
321 	*cookie = (longlong_t)(p - (char *)buf);
322 	*nbyte = len;
323 	return (rv);
324 }
325 
326 /*
327  * Read the directory entries and return the information about
328  * each entry
329  */
330 int
331 fs_readdir(fs_fhandle_t *ts_fh, char *path, long *dpos,
332     char *nm, int *el, fs_fhandle_t *efh, struct stat64 *est,
333     path_list_t **pl)
334 {
335 	struct dirent *dp;
336 	char  file_path[PATH_MAX + 1];
337 	DIR *dirp;
338 	int rv;
339 
340 	if ((dirp = opendir(ts_fh->fh_fpath)) == NULL)
341 		return (errno);
342 
343 	seekdir(dirp, *dpos);
344 	if ((dp = readdir(dirp)) == NULL) {
345 		rv = 0;  /* skip this dir */
346 		*el = 0;
347 	} else {
348 		(void) snprintf(file_path, PATH_MAX, "%s/", path);
349 		(void) strlcat(file_path, dp->d_name, PATH_MAX);
350 
351 		rv = fs_getstat(file_path, efh, est, pl);
352 		if (rv == 0) {
353 			*dpos = telldir(dirp);
354 			(void) strlcpy(nm, dp->d_name, NAME_MAX);
355 			*el = strlen(dp->d_name);
356 		} else {
357 			*el = 0;
358 		}
359 	}
360 	(void) closedir(dirp);
361 	return (rv);
362 }
363 
364 /*
365  * Traverse the file system in the post-order way.  The description
366  * and example is in the header file.
367  *
368  * The callback function should return 0, on success and non-zero on
369  * failure.  If the callback function returns non-zero return value,
370  * the traversing stops.
371  */
372 int
373 traverse_post(struct fs_traverse *ftp)
374 {
375 	char path[PATH_MAX + 1]; /* full path name of the current dir */
376 	char nm[NAME_MAX + 1]; /* directory entry name */
377 	char *lp; /* last position on the path */
378 	int next_dir, rv;
379 	int pl, el; /* path and directory entry length */
380 	cstack_t *sp;
381 	fs_fhandle_t pfh, efh;
382 	struct stat64 pst, est;
383 	traverse_state_t *tsp;
384 	struct fst_node pn, en; /* parent and entry nodes */
385 	path_list_t *plhead, *plist;
386 
387 	if (!ftp || !ftp->ft_path || !*ftp->ft_path || !ftp->ft_callbk) {
388 		NDMP_LOG(LOG_DEBUG, "Invalid argument");
389 		errno = EINVAL;
390 		return (-1);
391 	}
392 
393 	/* set the default log function if it's not already set */
394 	if (!ftp->ft_logfp) {
395 		ftp->ft_logfp = (ft_log_t)syslog;
396 		NDMP_LOG(LOG_DEBUG, "Log to system log \"%s\"", ftp->ft_path);
397 	}
398 
399 	/* set the logical path to physical path if it's not already set */
400 	if (!ftp->ft_lpath) {
401 		NDMP_LOG(LOG_DEBUG,
402 		    "report the same paths: \"%s\"", ftp->ft_path);
403 		ftp->ft_lpath = ftp->ft_path;
404 	}
405 
406 	pl = strlen(ftp->ft_lpath);
407 	if (pl + 1 > PATH_MAX) { /* +1 for the '/' */
408 		NDMP_LOG(LOG_DEBUG, "lpath too long \"%s\"", ftp->ft_path);
409 		errno = ENAMETOOLONG;
410 		return (-1);
411 	}
412 	(void) strcpy(path, ftp->ft_lpath);
413 	(void) memset(&pfh, 0, sizeof (pfh));
414 	rv = fs_getstat(ftp->ft_lpath, &pfh, &pst, NULL);
415 
416 	if (rv != 0) {
417 		NDMP_LOG(LOG_DEBUG,
418 		    "Error %d on fs_getstat(%s)", rv, ftp->ft_path);
419 		return (rv);
420 	}
421 
422 	if (!S_ISDIR(pst.st_mode)) {
423 		pn.tn_path = ftp->ft_lpath;
424 		pn.tn_fh = &pfh;
425 		pn.tn_st = &pst;
426 		en.tn_path = NULL;
427 		en.tn_fh = NULL;
428 		en.tn_st = NULL;
429 		rv = CALLBACK(&pn, &en);
430 		if (VERBOSE(ftp))
431 			NDMP_LOG(LOG_DEBUG, "CALLBACK(%s): %d", pn.tn_path, rv);
432 		free(pfh.fh_fpath);
433 		return (rv);
434 	}
435 
436 	sp = cstack_new();
437 	if (!sp) {
438 		errno = ENOMEM;
439 		free(pfh.fh_fpath);
440 		return (-1);
441 	}
442 	tsp = new_tsp(path);
443 	if (!tsp) {
444 		cstack_delete(sp);
445 		errno = ENOMEM;
446 		free(pfh.fh_fpath);
447 		return (-1);
448 	}
449 	tsp->ts_ent = tsp->ts_end;
450 	tsp->ts_fh = pfh;
451 	tsp->ts_st = pst;
452 	pn.tn_path = path;
453 	pn.tn_fh = &tsp->ts_fh;
454 	pn.tn_st = &tsp->ts_st;
455 
456 	if ((plist = fs_init_pathlist()) == NULL) {
457 		errno = ENOMEM;
458 		free(pfh.fh_fpath);
459 		return (-1);
460 	}
461 	plhead = plist;
462 
463 	rv = 0;
464 	next_dir = 1;
465 	do {
466 		if (next_dir) {
467 			traverse_stats.fss_newdirs++;
468 
469 			*tsp->ts_end = '\0';
470 			if (VERBOSE(ftp))
471 				NDMP_LOG(LOG_DEBUG, "pl %d \"%s\"", pl, path);
472 		}
473 
474 		next_dir = 0;
475 		do {
476 			el = NAME_MAX;
477 			rv = fs_readdir(&tsp->ts_fh, pn.tn_path,
478 			    &tsp->ts_dpos, nm, &el,
479 			    &efh, &est, &plist);
480 
481 			if (rv != 0) {
482 				efh.fh_fpath = NULL;
483 				traverse_stats.fss_readdir_err++;
484 
485 				NDMP_LOG(LOG_DEBUG,
486 				    "Error %d on readdir(%s) pos %d",
487 				    rv, path, tsp->ts_dpos);
488 				if (STOP_ONERR(ftp))
489 					break;
490 				rv = SKIP_ENTRY;
491 
492 				continue;
493 			}
494 
495 			/* done with this directory */
496 			if (el == 0) {
497 				if (VERBOSE(ftp))
498 					NDMP_LOG(LOG_DEBUG,
499 					    "Done(%s)", pn.tn_path);
500 				break;
501 			}
502 			nm[el] = '\0';
503 
504 			if (rootfs_dot_or_dotdot(nm)) {
505 				efh.fh_fpath = NULL;
506 				continue;
507 			}
508 
509 			if (VERBOSE(ftp))
510 				NDMP_LOG(LOG_DEBUG, "%u dname: \"%s\"",
511 				    tsp->ts_dpos, nm);
512 
513 			if (pl + 1 + el > PATH_MAX) {
514 				traverse_stats.fss_longpath_err++;
515 
516 				NDMP_LOG(LOG_ERR, "Path %s/%s is too long.",
517 				    path, nm);
518 				if (STOP_ONLONG(ftp))
519 					rv = ENAMETOOLONG;
520 				efh.fh_fpath = NULL;
521 				continue;
522 			}
523 
524 			/*
525 			 * Push the current directory on to the stack and
526 			 * dive into the entry found.
527 			 */
528 			if (S_ISDIR(est.st_mode)) {
529 
530 				assert(tsp != NULL);
531 				if (cstack_push(sp, tsp, 0)) {
532 					rv = ENOMEM;
533 					efh.fh_fpath = NULL;
534 					break;
535 				}
536 				traverse_stats.fss_pushes++;
537 
538 				/*
539 				 * Concatenate the current entry with the
540 				 * current path.  This will be the path of
541 				 * the new directory to be scanned.
542 				 *
543 				 * Note:
544 				 * sprintf(tsp->ts_end, "/%s", de->d_name);
545 				 * could be used here, but concatenating
546 				 * strings like this might be faster.
547 				 * The length of the new path has been
548 				 * checked above.  So strcpy() can be
549 				 * safe and should not lead to a buffer
550 				 * over-run.
551 				 */
552 				lp = tsp->ts_end;
553 				*tsp->ts_end = '/';
554 				(void) strcpy(tsp->ts_end + 1, nm);
555 
556 				tsp = new_tsp(path);
557 				if (!tsp) {
558 					efh.fh_fpath = NULL;
559 					rv = ENOMEM;
560 				} else {
561 					next_dir = 1;
562 					pl += el;
563 					tsp->ts_fh = efh;
564 					tsp->ts_st = est;
565 					tsp->ts_ent = lp;
566 					pn.tn_fh = &tsp->ts_fh;
567 					pn.tn_st = &tsp->ts_st;
568 				}
569 				break;
570 			} else {
571 				/*
572 				 * The entry is not a directory so the
573 				 * callback function must be called.
574 				 */
575 				traverse_stats.fss_nondir_calls++;
576 
577 				en.tn_path = nm;
578 				en.tn_fh = &efh;
579 				en.tn_st = &est;
580 				rv = CALLBACK(&pn, &en);
581 				efh.fh_fpath = NULL;
582 				if (VERBOSE(ftp))
583 					NDMP_LOG(LOG_DEBUG,
584 					    "CALLBACK(%s/%s): %d",
585 					    pn.tn_path, en.tn_path, rv);
586 
587 				if (rv != 0)
588 					break;
589 			}
590 		} while (rv == 0);
591 
592 		/*
593 		 * A new directory must be processed, go to the start of
594 		 * the loop, open it and process it.
595 		 */
596 		if (next_dir)
597 			continue;
598 
599 		if (rv == SKIP_ENTRY)
600 			rv = 0; /* We should skip the current directory */
601 
602 		if (rv == 0) {
603 			/*
604 			 * Remove the ent from the end of path and send it
605 			 * as an entry of the path.
606 			 */
607 			lp = tsp->ts_ent;
608 			*lp = '\0';
609 			efh = tsp->ts_fh;
610 			est = tsp->ts_st;
611 			free(tsp);
612 			if (cstack_pop(sp, (void **)&tsp, (int *)NULL))
613 				break;
614 
615 			assert(tsp != NULL);
616 			pl = tsp->ts_end - path;
617 
618 			if (VERBOSE(ftp))
619 				NDMP_LOG(LOG_DEBUG, "poped pl %d 0x%p \"%s\"",
620 				    pl, tsp, path);
621 
622 			traverse_stats.fss_pops++;
623 			traverse_stats.fss_dir_calls++;
624 
625 			pn.tn_fh = &tsp->ts_fh;
626 			pn.tn_st = &tsp->ts_st;
627 			en.tn_path = lp + 1;
628 			en.tn_fh = &efh;
629 			en.tn_st = &est;
630 
631 			rv = CALLBACK(&pn, &en);
632 			efh.fh_fpath = NULL;
633 			if (VERBOSE(ftp))
634 				NDMP_LOG(LOG_DEBUG, "CALLBACK(%s/%s): %d",
635 				    pn.tn_path, en.tn_path, rv);
636 			/*
637 			 * Does not need to free tsp here.  It will be released
638 			 * later.
639 			 */
640 		}
641 
642 		if (rv != 0 && tsp)
643 			free(tsp);
644 
645 	} while (rv == 0);
646 
647 	/*
648 	 * For the 'ftp->ft_path' directory itself.
649 	 */
650 	if (rv == 0) {
651 		traverse_stats.fss_dir_calls++;
652 
653 		pn.tn_fh = &efh;
654 		pn.tn_st = &est;
655 		en.tn_path = NULL;
656 		en.tn_fh = NULL;
657 		en.tn_st = NULL;
658 		rv = CALLBACK(&pn, &en);
659 		if (VERBOSE(ftp))
660 			NDMP_LOG(LOG_DEBUG, "CALLBACK(%s): %d", pn.tn_path, rv);
661 	}
662 
663 	/*
664 	 * Pop and free all the remaining entries on the stack.
665 	 */
666 	while (!cstack_pop(sp, (void **)&tsp, (int *)NULL)) {
667 		traverse_stats.fss_stack_residue++;
668 
669 		free(tsp);
670 	}
671 
672 	fs_free_pathlist(plhead);
673 	free(pfh.fh_fpath);
674 	cstack_delete(sp);
675 	return (rv);
676 }
677 
678 /*
679  * In one pass, read all the directory entries of the specified
680  * directory and call the callback function for non-directory
681  * entries.
682  *
683  * On return:
684  *    0: Lets the directory to be scanned for directory entries.
685  *    < 0: Completely stops traversing.
686  *    FST_SKIP: stops further scanning of the directory.  Traversing
687  *        will continue with the next directory in the hierarchy.
688  *    SKIP_ENTRY: Failed to get the directory entries, so the caller
689  *	  should skip this entry.
690  */
691 static int
692 traverse_level_nondir(struct fs_traverse *ftp,
693     traverse_state_t *tsp, struct fst_node *pnp, dent_arg_t *darg)
694 {
695 	int pl; /* patth length */
696 	int rv;
697 	struct fst_node en; /* entry node */
698 	longlong_t cookie_verf;
699 	fs_dent_info_t *dent;
700 	struct dirent *buf;
701 	size_t len = 0;
702 	path_list_t *plhead, *plist;
703 	int fd;
704 
705 	rv = 0;
706 	pl = strlen(pnp->tn_path);
707 
708 	buf = ndmp_malloc(MAX_DENT_BUF_SIZE);
709 	if (buf == NULL)
710 		return (errno);
711 
712 	fd = open(tsp->ts_fh.fh_fpath, O_RDONLY);
713 	if (fd == -1) {
714 		free(buf);
715 		return (errno);
716 	}
717 	if ((plist = fs_init_pathlist()) == NULL) {
718 		free(buf);
719 		(void) close(fd);
720 		return (errno);
721 	}
722 	plhead = plist;
723 
724 	while (rv == 0) {
725 		long i, n_entries;
726 
727 		darg->da_end = 0;
728 		n_entries = 0;
729 		rv = fs_getdents(fd, buf, &len, pnp->tn_path, &tsp->ts_dpos,
730 		    &cookie_verf, &n_entries, darg, &plist);
731 		if (n_entries == 0)
732 			break;
733 		if (rv != 0) {
734 			traverse_stats.fss_readdir_err++;
735 
736 			NDMP_LOG(LOG_DEBUG, "Error %d on readdir(%s) pos %d",
737 			    rv, pnp->tn_path, tsp->ts_dpos);
738 			if (STOP_ONERR(ftp)) {
739 				NEGATE(rv);
740 				break;
741 			}
742 			/*
743 			 * We cannot read the directory entry, we should
744 			 * skip to the next directory.
745 			 */
746 			rv = SKIP_ENTRY;
747 			continue;
748 		}
749 
750 		/* LINTED imporper alignment */
751 		dent = (fs_dent_info_t *)darg->da_buf;
752 		/* LINTED imporper alignment */
753 		for (i = 0; i < n_entries; i++, dent = (fs_dent_info_t *)
754 		    ((char *)dent + dent->fd_len)) {
755 
756 			if (rootfs_dot_or_dotdot(dent->fd_name)) {
757 				dent->fd_fh.fh_fpath = NULL;
758 				continue;
759 			}
760 
761 			if (VERBOSE(ftp))
762 				NDMP_LOG(LOG_DEBUG, "i %u dname: \"%s\"",
763 				    dent->fd_fh.fh_fid, dent->fd_name);
764 
765 			if ((pl + strlen(dent->fd_name)) > PATH_MAX) {
766 				traverse_stats.fss_longpath_err++;
767 
768 				NDMP_LOG(LOG_ERR, "Path %s/%s is too long.",
769 				    pnp->tn_path, dent->fd_name);
770 				if (STOP_ONLONG(ftp))
771 					rv = -ENAMETOOLONG;
772 				continue;
773 			}
774 
775 			/*
776 			 * The entry is not a directory so the callback
777 			 * function must be called.
778 			 */
779 			if (!S_ISDIR(dent->fd_attr.st_mode)) {
780 				traverse_stats.fss_nondir_calls++;
781 
782 				en.tn_path = dent->fd_name;
783 				en.tn_fh = &dent->fd_fh;
784 				en.tn_st = &dent->fd_attr;
785 				rv = CALLBACK(pnp, &en);
786 				dent->fd_fh.fh_fpath = NULL;
787 				if (rv < 0)
788 					break;
789 				if (rv == FST_SKIP) {
790 					traverse_stats.fss_nondir_skipped++;
791 					break;
792 				}
793 			} else  {
794 				dent->fd_fh.fh_fpath = NULL;
795 			}
796 		}
797 	}
798 
799 	fs_free_pathlist(plhead);
800 	free(buf);
801 	(void) close(fd);
802 	return (rv);
803 }
804 
805 /*
806  * Traverse the file system in the level-order way.  The description
807  * and example is in the header file.
808  */
809 int
810 traverse_level(struct fs_traverse *ftp)
811 {
812 	char path[PATH_MAX + 1];	/* full path name of the current dir */
813 	char nm[NAME_MAX + 1];	/* directory entry name */
814 	char *lp;		/* last position on the path */
815 	int next_dir, rv;
816 	int pl, el;		/* path and directory entry length */
817 
818 	cstack_t *sp;
819 	fs_fhandle_t pfh, efh;
820 	struct stat64 pst, est;
821 	traverse_state_t *tsp;
822 	struct fst_node pn, en;  /* parent and entry nodes */
823 	dent_arg_t darg;
824 	path_list_t *plhead, *plist;
825 
826 	if (!ftp || !ftp->ft_path || !*ftp->ft_path || !ftp->ft_callbk) {
827 		NDMP_LOG(LOG_DEBUG, "Invalid argument");
828 		errno = EINVAL;
829 		return (-1);
830 	}
831 	/* set the default log function if it's not already set */
832 	if (!ftp->ft_logfp) {
833 		ftp->ft_logfp = (ft_log_t)syslog;
834 		NDMP_LOG(LOG_DEBUG, "Log to system log \"%s\"", ftp->ft_path);
835 	}
836 	if (!ftp->ft_lpath) {
837 		NDMP_LOG(LOG_DEBUG,
838 		    "report the same paths \"%s\"", ftp->ft_path);
839 		ftp->ft_lpath = ftp->ft_path;
840 	}
841 
842 	pl = strlen(ftp->ft_lpath);
843 	if (pl + 1 > PATH_MAX) { /* +1 for the '/' */
844 		NDMP_LOG(LOG_DEBUG, "lpath too long \"%s\"", ftp->ft_path);
845 		errno = ENAMETOOLONG;
846 		return (-1);
847 	}
848 	(void) strcpy(path, ftp->ft_lpath);
849 	(void) memset(&pfh, 0, sizeof (pfh));
850 	rv = fs_getstat(ftp->ft_lpath, &pfh, &pst, NULL);
851 	if (rv != 0) {
852 		NDMP_LOG(LOG_DEBUG,
853 		    "Error %d on fs_getstat(%s)", rv, ftp->ft_path);
854 		return (-1);
855 	}
856 
857 	en.tn_path = NULL;
858 	en.tn_fh = NULL;
859 	en.tn_st = NULL;
860 	if (!S_ISDIR(pst.st_mode)) {
861 		pn.tn_path = ftp->ft_lpath;
862 		pn.tn_fh = &pfh;
863 		pn.tn_st = &pst;
864 		rv = CALLBACK(&pn, &en);
865 		if (VERBOSE(ftp))
866 			NDMP_LOG(LOG_DEBUG, "CALLBACK(%s): %d", pn.tn_path, rv);
867 
868 		free(pfh.fh_fpath);
869 		return (rv);
870 	}
871 
872 	sp = cstack_new();
873 	if (!sp) {
874 		free(pfh.fh_fpath);
875 		errno = ENOMEM;
876 		return (-1);
877 	}
878 	tsp = new_tsp(path);
879 	if (!tsp) {
880 		cstack_delete(sp);
881 		free(pfh.fh_fpath);
882 		errno = ENOMEM;
883 		return (-1);
884 	}
885 
886 	darg.da_buf = ndmp_malloc(MAX_DENT_BUF_SIZE);
887 	if (!darg.da_buf) {
888 		cstack_delete(sp);
889 		free(pfh.fh_fpath);
890 		free(tsp);
891 		errno = ENOMEM;
892 		return (-1);
893 	}
894 	darg.da_size = MAX_DENT_BUF_SIZE;
895 
896 	tsp->ts_ent = tsp->ts_end;
897 	tsp->ts_fh = pfh;
898 	tsp->ts_st = pst;
899 	pn.tn_path = path;
900 	pn.tn_fh = &tsp->ts_fh;
901 	pn.tn_st = &tsp->ts_st;
902 
903 	if ((plist = fs_init_pathlist()) == NULL) {
904 		cstack_delete(sp);
905 		free(pfh.fh_fpath);
906 		free(tsp);
907 		errno = ENOMEM;
908 		return (-1);
909 	}
910 	plhead = plist;
911 
912 	/* call the callback function on the path itself */
913 	traverse_stats.fss_dir_calls++;
914 	rv = CALLBACK(&pn, &en);
915 	if (rv < 0) {
916 		free(tsp);
917 		goto end;
918 	}
919 	if (rv == FST_SKIP) {
920 		traverse_stats.fss_dir_skipped++;
921 		free(tsp);
922 		rv = 0;
923 		goto end;
924 	}
925 
926 	rv = 0;
927 	next_dir = 1;
928 	do {
929 		if (next_dir) {
930 			traverse_stats.fss_newdirs++;
931 
932 			*tsp->ts_end = '\0';
933 			if (VERBOSE(ftp))
934 				NDMP_LOG(LOG_DEBUG, "pl %d \"%s\"", pl, path);
935 
936 			rv = traverse_level_nondir(ftp, tsp, &pn, &darg);
937 			if (rv < 0) {
938 				NEGATE(rv);
939 				free(tsp);
940 				break;
941 			}
942 			/*
943 			 * If skipped by the callback function or
944 			 * error happened reading the information
945 			 */
946 			if (rv == FST_SKIP || rv == SKIP_ENTRY) {
947 				/*
948 				 * N.B. next_dir should be set to 0 as
949 				 * well. This prevents the infinite loop.
950 				 * If it's not set the same directory will
951 				 * be poped from the stack and will be
952 				 * scanned again.
953 				 */
954 				next_dir = 0;
955 				rv = 0;
956 				goto skip_dir;
957 			}
958 
959 			/* re-start reading entries of the directory */
960 			tsp->ts_dpos = 0;
961 		}
962 
963 		next_dir = 0;
964 		do {
965 			el = NAME_MAX;
966 			rv = fs_readdir(&tsp->ts_fh, pn.tn_path,
967 			    &tsp->ts_dpos, nm, &el, &efh,
968 			    &est, &plist);
969 			if (rv != 0) {
970 				traverse_stats.fss_readdir_err++;
971 
972 				NDMP_LOG(LOG_DEBUG,
973 				    "Error %d on readdir(%s) pos %d",
974 				    rv, path, tsp->ts_dpos);
975 				if (STOP_ONERR(ftp))
976 					break;
977 				rv = SKIP_ENTRY;
978 				continue;
979 			}
980 
981 			/* done with this directory */
982 			if (el == 0)
983 				break;
984 
985 			nm[el] = '\0';
986 
987 			if (rootfs_dot_or_dotdot(nm)) {
988 				efh.fh_fpath = NULL;
989 				continue;
990 			}
991 
992 			if (VERBOSE(ftp))
993 				NDMP_LOG(LOG_DEBUG, "%u dname: \"%s\"",
994 				    tsp->ts_dpos, nm);
995 
996 			if (pl + 1 + el > PATH_MAX) {
997 				/*
998 				 * The long paths were already encountered
999 				 * when processing non-dir entries in.
1000 				 * traverse_level_nondir.
1001 				 * We don't increase fss_longpath_err
1002 				 * counter for them again here.
1003 				 */
1004 				NDMP_LOG(LOG_ERR, "Path %s/%s is too long.",
1005 				    path, nm);
1006 				if (STOP_ONLONG(ftp))
1007 					rv = ENAMETOOLONG;
1008 				efh.fh_fpath = NULL;
1009 				continue;
1010 			}
1011 
1012 			if (!S_ISDIR(est.st_mode)) {
1013 				efh.fh_fpath = NULL;
1014 				continue;
1015 			}
1016 
1017 			/*
1018 			 * Call the callback function for the new
1019 			 * directory found, then push the current
1020 			 * directory on to the stack.  Then dive
1021 			 * into the entry found.
1022 			 */
1023 			traverse_stats.fss_dir_calls++;
1024 			en.tn_path = nm;
1025 			en.tn_fh = &efh;
1026 			en.tn_st = &est;
1027 			rv = CALLBACK(&pn, &en);
1028 
1029 			if (rv < 0) {
1030 				NEGATE(rv);
1031 				break;
1032 			}
1033 			if (rv == FST_SKIP) {
1034 				traverse_stats.fss_dir_skipped++;
1035 				rv = 0;
1036 				continue;
1037 			}
1038 
1039 			/*
1040 			 * Push the current directory on to the stack and
1041 			 * dive into the entry found.
1042 			 */
1043 			if (cstack_push(sp, tsp, 0))
1044 				rv = ENOMEM;
1045 			else {
1046 				traverse_stats.fss_pushes++;
1047 
1048 				lp = tsp->ts_end;
1049 				*tsp->ts_end = '/';
1050 				(void) strcpy(tsp->ts_end + 1, nm);
1051 
1052 				tsp = new_tsp(path);
1053 				if (!tsp)
1054 					rv = ENOMEM;
1055 				else {
1056 					next_dir = 1;
1057 					pl += el + 1;
1058 					tsp->ts_fh = efh;
1059 					tsp->ts_st = est;
1060 					tsp->ts_ent = lp;
1061 					pn.tn_fh = &tsp->ts_fh;
1062 					pn.tn_st = &tsp->ts_st;
1063 				}
1064 			}
1065 			break;
1066 
1067 		} while (rv == 0);
1068 
1069 		/*
1070 		 * A new directory must be processed, go to the start of
1071 		 * the loop, open it and process it.
1072 		 */
1073 		if (next_dir)
1074 			continue;
1075 skip_dir:
1076 		if (tsp)
1077 			free(tsp);
1078 
1079 		if (rv == SKIP_ENTRY)
1080 			rv = 0;
1081 
1082 		if (rv == 0) {
1083 			if (cstack_pop(sp, (void **)&tsp, (int *)NULL))
1084 				break;
1085 
1086 			traverse_stats.fss_pops++;
1087 
1088 			if (VERBOSE(ftp))
1089 				NDMP_LOG(LOG_DEBUG,
1090 				    "Poped pl %d \"%s\"", pl, path);
1091 
1092 			*tsp->ts_end = '\0';
1093 			pl = tsp->ts_end - path;
1094 			pn.tn_fh = &tsp->ts_fh;
1095 			pn.tn_st = &tsp->ts_st;
1096 		}
1097 	} while (rv == 0);
1098 
1099 	/*
1100 	 * Pop and free all the remaining entries on the stack.
1101 	 */
1102 	while (!cstack_pop(sp, (void **)&tsp, (int *)NULL)) {
1103 		traverse_stats.fss_stack_residue++;
1104 
1105 		free(tsp);
1106 	}
1107 end:
1108 	free(darg.da_buf);
1109 	free(pfh.fh_fpath);
1110 	fs_free_pathlist(plhead);
1111 	cstack_delete(sp);
1112 	return (rv);
1113 }
1114 
1115 /*
1116  * filecopy - Copy a file
1117  *
1118  * Parameters:
1119  *  char *dest  - Destination path
1120  *  char *src   - Source path
1121  *
1122  * Returns:
1123  *  0    - No errors
1124  *  #0   - Error occured
1125  *		-4   - read/write error
1126  *		-5   - source modified during copy
1127  *
1128  * Simplified version for Solaris
1129  */
1130 #define	BUFSIZE	32768
1131 int
1132 filecopy(char *dest, char *src)
1133 {
1134 	FILE *src_fh = 0;
1135 	FILE *dst_fh = 0;
1136 	struct stat64 src_attr;
1137 	struct stat64 dst_attr;
1138 	char *buf = 0;
1139 	u_longlong_t bytes_to_copy;
1140 	size_t nbytes;
1141 	int file_copied = 0;
1142 
1143 	buf = ndmp_malloc(BUFSIZE);
1144 	if (!buf)
1145 		return (-1);
1146 
1147 	src_fh = fopen(src, "r");
1148 	if (src_fh == 0) {
1149 		free(buf);
1150 		return (-2);
1151 	}
1152 
1153 	dst_fh = fopen(dest, "w");
1154 	if (dst_fh == NULL) {
1155 		free(buf);
1156 		(void) fclose(src_fh);
1157 		return (-3);
1158 	}
1159 
1160 	if (stat64(src, &src_attr) < 0) {
1161 		free(buf);
1162 		(void) fclose(src_fh);
1163 		(void) fclose(dst_fh);
1164 		return (-2);
1165 	}
1166 
1167 	bytes_to_copy = src_attr.st_size;
1168 	while (bytes_to_copy) {
1169 		if (bytes_to_copy > BUFSIZE)
1170 			nbytes = BUFSIZE;
1171 		else
1172 			nbytes = bytes_to_copy;
1173 
1174 		if ((fread(buf, nbytes, 1, src_fh) != 1) ||
1175 		    (fwrite(buf, nbytes, 1, dst_fh) != 1))
1176 			break;
1177 		bytes_to_copy -= nbytes;
1178 	}
1179 
1180 	(void) fclose(src_fh);
1181 	(void) fclose(dst_fh);
1182 
1183 	if (bytes_to_copy > 0) {
1184 		free(buf);
1185 		/* short read/write, remove the partial file */
1186 		return (-4);
1187 	}
1188 
1189 	if (stat64(src, &dst_attr) < 0) {
1190 		free(buf);
1191 		return (-2);
1192 	}
1193 
1194 	free(buf);
1195 
1196 	if (!file_copied)
1197 		return (-5);	/* source modified during copy */
1198 	else
1199 		return (0);
1200 }
1201