xref: /onnv-gate/usr/src/cmd/ndmpd/tlm/tlm_traverse.c (revision 9714:c2e4024f4670)
17917SReza.Sabdar@Sun.COM /*
29012SReza.Sabdar@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
37917SReza.Sabdar@Sun.COM  * Use is subject to license terms.
47917SReza.Sabdar@Sun.COM  */
57917SReza.Sabdar@Sun.COM 
67917SReza.Sabdar@Sun.COM /*
77917SReza.Sabdar@Sun.COM  * BSD 3 Clause License
87917SReza.Sabdar@Sun.COM  *
97917SReza.Sabdar@Sun.COM  * Copyright (c) 2007, The Storage Networking Industry Association.
107917SReza.Sabdar@Sun.COM  *
117917SReza.Sabdar@Sun.COM  * Redistribution and use in source and binary forms, with or without
127917SReza.Sabdar@Sun.COM  * modification, are permitted provided that the following conditions
137917SReza.Sabdar@Sun.COM  * are met:
147917SReza.Sabdar@Sun.COM  * 	- Redistributions of source code must retain the above copyright
157917SReza.Sabdar@Sun.COM  *	  notice, this list of conditions and the following disclaimer.
167917SReza.Sabdar@Sun.COM  *
177917SReza.Sabdar@Sun.COM  * 	- Redistributions in binary form must reproduce the above copyright
187917SReza.Sabdar@Sun.COM  *	  notice, this list of conditions and the following disclaimer in
197917SReza.Sabdar@Sun.COM  *	  the documentation and/or other materials provided with the
207917SReza.Sabdar@Sun.COM  *	  distribution.
217917SReza.Sabdar@Sun.COM  *
227917SReza.Sabdar@Sun.COM  *	- Neither the name of The Storage Networking Industry Association (SNIA)
237917SReza.Sabdar@Sun.COM  *	  nor the names of its contributors may be used to endorse or promote
247917SReza.Sabdar@Sun.COM  *	  products derived from this software without specific prior written
257917SReza.Sabdar@Sun.COM  *	  permission.
267917SReza.Sabdar@Sun.COM  *
277917SReza.Sabdar@Sun.COM  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
287917SReza.Sabdar@Sun.COM  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
297917SReza.Sabdar@Sun.COM  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
307917SReza.Sabdar@Sun.COM  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
317917SReza.Sabdar@Sun.COM  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
327917SReza.Sabdar@Sun.COM  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
337917SReza.Sabdar@Sun.COM  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
347917SReza.Sabdar@Sun.COM  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
357917SReza.Sabdar@Sun.COM  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
367917SReza.Sabdar@Sun.COM  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
377917SReza.Sabdar@Sun.COM  * POSSIBILITY OF SUCH DAMAGE.
387917SReza.Sabdar@Sun.COM  */
397917SReza.Sabdar@Sun.COM /*
407917SReza.Sabdar@Sun.COM  * This file implemets the post-order, pre-order and level-order
417917SReza.Sabdar@Sun.COM  * traversing of the file system.  The related macros and constants
427917SReza.Sabdar@Sun.COM  * are defined in traverse.h.
437917SReza.Sabdar@Sun.COM  */
447917SReza.Sabdar@Sun.COM 
457917SReza.Sabdar@Sun.COM #include <sys/stat.h>
467917SReza.Sabdar@Sun.COM #include <sys/types.h>
477917SReza.Sabdar@Sun.COM #include <sys/param.h>
487917SReza.Sabdar@Sun.COM #include <assert.h>
497917SReza.Sabdar@Sun.COM #include <cstack.h>
507917SReza.Sabdar@Sun.COM #include <dirent.h>
517917SReza.Sabdar@Sun.COM #include <errno.h>
527917SReza.Sabdar@Sun.COM #include <traverse.h>
537917SReza.Sabdar@Sun.COM #include <limits.h>
547917SReza.Sabdar@Sun.COM #include <stdarg.h>
557917SReza.Sabdar@Sun.COM #include <stdio.h>
567917SReza.Sabdar@Sun.COM #include <stdlib.h>
577917SReza.Sabdar@Sun.COM #include <string.h>
587917SReza.Sabdar@Sun.COM #include <syslog.h>
597917SReza.Sabdar@Sun.COM #include <fcntl.h>
607917SReza.Sabdar@Sun.COM #include <unistd.h>
617917SReza.Sabdar@Sun.COM #include <tlm.h>
627917SReza.Sabdar@Sun.COM #include "tlm_proto.h"
637917SReza.Sabdar@Sun.COM 
647917SReza.Sabdar@Sun.COM /*
657917SReza.Sabdar@Sun.COM  * Check if it's "." or ".."
667917SReza.Sabdar@Sun.COM  */
677917SReza.Sabdar@Sun.COM boolean_t
687917SReza.Sabdar@Sun.COM rootfs_dot_or_dotdot(char *name)
697917SReza.Sabdar@Sun.COM {
707917SReza.Sabdar@Sun.COM 	if (*name != '.')
717917SReza.Sabdar@Sun.COM 		return (FALSE);
727917SReza.Sabdar@Sun.COM 
737917SReza.Sabdar@Sun.COM 	if ((name[1] == 0) || (name[1] == '.' && name[2] == 0))
747917SReza.Sabdar@Sun.COM 		return (TRUE);
757917SReza.Sabdar@Sun.COM 
767917SReza.Sabdar@Sun.COM 	return (FALSE);
777917SReza.Sabdar@Sun.COM }
787917SReza.Sabdar@Sun.COM 
797917SReza.Sabdar@Sun.COM /*
807917SReza.Sabdar@Sun.COM  * Macros on fs_traverse flags.
817917SReza.Sabdar@Sun.COM  */
827917SReza.Sabdar@Sun.COM #define	STOP_ONERR(f)	((f)->ft_flags & FST_STOP_ONERR)
837917SReza.Sabdar@Sun.COM #define	STOP_ONLONG(f)	((f)->ft_flags & FST_STOP_ONLONG)
847917SReza.Sabdar@Sun.COM #define	VERBOSE(f)	((f)->ft_flags & FST_VERBOSE)
857917SReza.Sabdar@Sun.COM 
867917SReza.Sabdar@Sun.COM #define	CALLBACK(pp, ep)	\
877917SReza.Sabdar@Sun.COM 	(*(ftp)->ft_callbk)((ftp)->ft_arg, pp, ep)
887917SReza.Sabdar@Sun.COM 
897917SReza.Sabdar@Sun.COM #define	NEGATE(rv)	((rv) =	-(rv))
907917SReza.Sabdar@Sun.COM 
917917SReza.Sabdar@Sun.COM /*
927917SReza.Sabdar@Sun.COM  * The traversing state that is pushed onto the stack.
937917SReza.Sabdar@Sun.COM  * This include:
947917SReza.Sabdar@Sun.COM  * 	- The end of the path of the current directory.
957917SReza.Sabdar@Sun.COM  *	- The position of the last component on it.
967917SReza.Sabdar@Sun.COM  *	- The read position in the directory.
977917SReza.Sabdar@Sun.COM  *	- The file handle of the directory.
987917SReza.Sabdar@Sun.COM  *	- The stat of the directory.
997917SReza.Sabdar@Sun.COM  */
1007917SReza.Sabdar@Sun.COM typedef struct traverse_state {
1017917SReza.Sabdar@Sun.COM 	char *ts_end;
1027917SReza.Sabdar@Sun.COM 	char *ts_ent;
1037917SReza.Sabdar@Sun.COM 	long ts_dpos; /* position in the directory when reading its entries */
1047917SReza.Sabdar@Sun.COM 	fs_fhandle_t ts_fh;
1057917SReza.Sabdar@Sun.COM 	struct stat64 ts_st;
1067917SReza.Sabdar@Sun.COM } traverse_state_t;
1077917SReza.Sabdar@Sun.COM 
1087917SReza.Sabdar@Sun.COM /*
1097917SReza.Sabdar@Sun.COM  * Statistics gathering structure.
1107917SReza.Sabdar@Sun.COM  */
1117917SReza.Sabdar@Sun.COM typedef struct traverse_statistics {
1127917SReza.Sabdar@Sun.COM 	ulong_t fss_newdirs;
1137917SReza.Sabdar@Sun.COM 	ulong_t fss_readdir_err;
1147917SReza.Sabdar@Sun.COM 	ulong_t fss_longpath_err;
1157917SReza.Sabdar@Sun.COM 	ulong_t fss_lookup_err;
1167917SReza.Sabdar@Sun.COM 	ulong_t fss_nondir_calls;
1177917SReza.Sabdar@Sun.COM 	ulong_t fss_dir_calls;
1187917SReza.Sabdar@Sun.COM 	ulong_t fss_nondir_skipped;
1197917SReza.Sabdar@Sun.COM 	ulong_t fss_dir_skipped;
1207917SReza.Sabdar@Sun.COM 	ulong_t fss_pushes;
1217917SReza.Sabdar@Sun.COM 	ulong_t fss_pops;
1227917SReza.Sabdar@Sun.COM 	ulong_t fss_stack_residue;
1237917SReza.Sabdar@Sun.COM } traverse_statistics_t;
1247917SReza.Sabdar@Sun.COM 
1257917SReza.Sabdar@Sun.COM /*
1267917SReza.Sabdar@Sun.COM  * Global instance of statistics variable.
1277917SReza.Sabdar@Sun.COM  */
1287917SReza.Sabdar@Sun.COM traverse_statistics_t traverse_stats;
1297917SReza.Sabdar@Sun.COM 
1307917SReza.Sabdar@Sun.COM #define	MAX_DENT_BUF_SIZE	(8 * 1024)
1317917SReza.Sabdar@Sun.COM 
1327917SReza.Sabdar@Sun.COM typedef struct {
1337917SReza.Sabdar@Sun.COM 	struct stat64 fd_attr;
1347917SReza.Sabdar@Sun.COM 	fs_fhandle_t fd_fh;
1357917SReza.Sabdar@Sun.COM 	short fd_len;
1367917SReza.Sabdar@Sun.COM 	char fd_name[1];
1377917SReza.Sabdar@Sun.COM } fs_dent_info_t;
1387917SReza.Sabdar@Sun.COM 
1397917SReza.Sabdar@Sun.COM typedef struct dent_arg {
1407917SReza.Sabdar@Sun.COM 	char *da_buf;
1417917SReza.Sabdar@Sun.COM 	int da_end;
1427917SReza.Sabdar@Sun.COM 	int da_size;
1437917SReza.Sabdar@Sun.COM } dent_arg_t;
1447917SReza.Sabdar@Sun.COM 
1457917SReza.Sabdar@Sun.COM static int traverse_level_nondir(struct fs_traverse *ftp,
1467917SReza.Sabdar@Sun.COM     traverse_state_t *tsp, struct fst_node *pnp,
1477917SReza.Sabdar@Sun.COM     dent_arg_t *darg);
1487917SReza.Sabdar@Sun.COM 
1497917SReza.Sabdar@Sun.COM /*
1507917SReza.Sabdar@Sun.COM  * Gather some directory entry information and return them
1517917SReza.Sabdar@Sun.COM  */
1527917SReza.Sabdar@Sun.COM static int
1537917SReza.Sabdar@Sun.COM fs_populate_dents(void *arg, int namelen,
1547917SReza.Sabdar@Sun.COM     char *name, long *countp, struct stat64 *attr,
1557917SReza.Sabdar@Sun.COM     fs_fhandle_t *fh)
1567917SReza.Sabdar@Sun.COM {
1577917SReza.Sabdar@Sun.COM 	dent_arg_t *darg = (dent_arg_t *)arg;
1587917SReza.Sabdar@Sun.COM 	int reclen = sizeof (fs_dent_info_t) + namelen;
1597917SReza.Sabdar@Sun.COM 	fs_dent_info_t *dent;
1607917SReza.Sabdar@Sun.COM 
1617917SReza.Sabdar@Sun.COM 	if ((darg->da_end + reclen) > darg->da_size)
1627917SReza.Sabdar@Sun.COM 		return (-1);
1637917SReza.Sabdar@Sun.COM 
1647917SReza.Sabdar@Sun.COM 	/* LINTED improper alignment */
1657917SReza.Sabdar@Sun.COM 	dent = (fs_dent_info_t *)(darg->da_buf + darg->da_end);
1667917SReza.Sabdar@Sun.COM 
1677917SReza.Sabdar@Sun.COM 	dent->fd_attr = *attr;
1687917SReza.Sabdar@Sun.COM 	dent->fd_fh = *fh;
1697917SReza.Sabdar@Sun.COM 	(void) strcpy(dent->fd_name, name);
1707917SReza.Sabdar@Sun.COM 
1717917SReza.Sabdar@Sun.COM 	dent->fd_len = reclen;
1727917SReza.Sabdar@Sun.COM 	darg->da_end += reclen;
1737917SReza.Sabdar@Sun.COM 
1747917SReza.Sabdar@Sun.COM 	if (countp)
1757917SReza.Sabdar@Sun.COM 		(*countp)++;
1767917SReza.Sabdar@Sun.COM 
1777917SReza.Sabdar@Sun.COM 	return (0);
1787917SReza.Sabdar@Sun.COM }
1797917SReza.Sabdar@Sun.COM 
1807917SReza.Sabdar@Sun.COM /*
1817917SReza.Sabdar@Sun.COM  * Creates a new traversing state based on the path passed to it.
1827917SReza.Sabdar@Sun.COM  */
1837917SReza.Sabdar@Sun.COM static traverse_state_t *
1847917SReza.Sabdar@Sun.COM new_tsp(char *path)
1857917SReza.Sabdar@Sun.COM {
1867917SReza.Sabdar@Sun.COM 	traverse_state_t *tsp;
1877917SReza.Sabdar@Sun.COM 	tsp = ndmp_malloc(sizeof (traverse_state_t));
1887917SReza.Sabdar@Sun.COM 	if (!tsp)
1897917SReza.Sabdar@Sun.COM 		return (NULL);
1907917SReza.Sabdar@Sun.COM 
1917917SReza.Sabdar@Sun.COM 	tsp->ts_end = strchr(path, '\0');
1927917SReza.Sabdar@Sun.COM 	if (*(tsp->ts_end-1) == '/')
1937917SReza.Sabdar@Sun.COM 		*--tsp->ts_end = '\0';
1947917SReza.Sabdar@Sun.COM 	tsp->ts_ent = NULL;
1957917SReza.Sabdar@Sun.COM 	tsp->ts_dpos = 0;
1967917SReza.Sabdar@Sun.COM 
1977917SReza.Sabdar@Sun.COM 	return (tsp);
1987917SReza.Sabdar@Sun.COM }
1997917SReza.Sabdar@Sun.COM 
2007917SReza.Sabdar@Sun.COM /*
2017917SReza.Sabdar@Sun.COM  * Create a file handle and get stats for the given path
2027917SReza.Sabdar@Sun.COM  */
2037917SReza.Sabdar@Sun.COM int
204*9714SReza.Sabdar@Sun.COM fs_getstat(char *path, fs_fhandle_t *fh, struct stat64 *st)
2057917SReza.Sabdar@Sun.COM {
2067917SReza.Sabdar@Sun.COM 	if (lstat64(path, st) == -1)
2077917SReza.Sabdar@Sun.COM 		return (errno);
2087917SReza.Sabdar@Sun.COM 
2097917SReza.Sabdar@Sun.COM 	fh->fh_fid = st->st_ino;
2109012SReza.Sabdar@Sun.COM 
211*9714SReza.Sabdar@Sun.COM 	if (!S_ISDIR(st->st_mode))
2129012SReza.Sabdar@Sun.COM 		fh->fh_fpath = NULL;
2137917SReza.Sabdar@Sun.COM 	else
2147917SReza.Sabdar@Sun.COM 		fh->fh_fpath = strdup(path);
2157917SReza.Sabdar@Sun.COM 	return (0);
2167917SReza.Sabdar@Sun.COM }
2177917SReza.Sabdar@Sun.COM 
2187917SReza.Sabdar@Sun.COM /*
2197917SReza.Sabdar@Sun.COM  * Get directory entries info and return in the buffer. Cookie
2207917SReza.Sabdar@Sun.COM  * will keep the state of each call
2217917SReza.Sabdar@Sun.COM  */
2227917SReza.Sabdar@Sun.COM static int
2237917SReza.Sabdar@Sun.COM fs_getdents(int fildes, struct dirent *buf, size_t *nbyte,
2247917SReza.Sabdar@Sun.COM     char *pn_path, long *dpos, longlong_t *cookie,
225*9714SReza.Sabdar@Sun.COM     long *n_entries, dent_arg_t *darg)
2267917SReza.Sabdar@Sun.COM {
2277917SReza.Sabdar@Sun.COM 	struct dirent *ptr;
2287917SReza.Sabdar@Sun.COM 	char file_path[PATH_MAX + 1];
2297917SReza.Sabdar@Sun.COM 	fs_fhandle_t fh;
2307917SReza.Sabdar@Sun.COM 	struct stat64 st;
2317917SReza.Sabdar@Sun.COM 	char *p;
2327917SReza.Sabdar@Sun.COM 	int len;
2337917SReza.Sabdar@Sun.COM 	int rv;
2347917SReza.Sabdar@Sun.COM 
2357917SReza.Sabdar@Sun.COM 	if (*nbyte == 0) {
2367917SReza.Sabdar@Sun.COM 		(void) memset((char *)buf, 0, MAX_DENT_BUF_SIZE);
2377917SReza.Sabdar@Sun.COM 		*nbyte = rv = getdents(fildes, buf, darg->da_size);
2387917SReza.Sabdar@Sun.COM 		*cookie = 0LL;
2397917SReza.Sabdar@Sun.COM 
2407917SReza.Sabdar@Sun.COM 		if (rv <= 0)
2417917SReza.Sabdar@Sun.COM 			return (rv);
2427917SReza.Sabdar@Sun.COM 	}
2437917SReza.Sabdar@Sun.COM 
2447917SReza.Sabdar@Sun.COM 	p = (char *)buf + *cookie;
2457917SReza.Sabdar@Sun.COM 	len = *nbyte;
2467917SReza.Sabdar@Sun.COM 	do {
2477917SReza.Sabdar@Sun.COM 		/* LINTED improper alignment */
2487917SReza.Sabdar@Sun.COM 		ptr = (struct dirent *)p;
2497917SReza.Sabdar@Sun.COM 		*dpos =  ptr->d_off;
250*9714SReza.Sabdar@Sun.COM 
251*9714SReza.Sabdar@Sun.COM 		if (rootfs_dot_or_dotdot(ptr->d_name))
252*9714SReza.Sabdar@Sun.COM 			goto skip_entry;
253*9714SReza.Sabdar@Sun.COM 
2547917SReza.Sabdar@Sun.COM 		(void) snprintf(file_path, PATH_MAX, "%s/", pn_path);
2557917SReza.Sabdar@Sun.COM 		(void) strlcat(file_path, ptr->d_name, PATH_MAX);
2567917SReza.Sabdar@Sun.COM 		(void) memset(&fh, 0, sizeof (fs_fhandle_t));
2577917SReza.Sabdar@Sun.COM 
258*9714SReza.Sabdar@Sun.COM 		rv = lstat64(file_path, &st);
2597917SReza.Sabdar@Sun.COM 		if (rv != 0)
2607917SReza.Sabdar@Sun.COM 			break;
2617917SReza.Sabdar@Sun.COM 
262*9714SReza.Sabdar@Sun.COM 		fh.fh_fid = st.st_ino;
263*9714SReza.Sabdar@Sun.COM 
264*9714SReza.Sabdar@Sun.COM 		if (S_ISDIR(st.st_mode))
265*9714SReza.Sabdar@Sun.COM 			goto skip_entry;
266*9714SReza.Sabdar@Sun.COM 
2677917SReza.Sabdar@Sun.COM 		rv = fs_populate_dents(darg, strlen(ptr->d_name),
2687917SReza.Sabdar@Sun.COM 		    (char *)ptr->d_name, n_entries, &st, &fh);
2697917SReza.Sabdar@Sun.COM 
2707917SReza.Sabdar@Sun.COM 		if (rv != 0) {
2717917SReza.Sabdar@Sun.COM 			rv = 0;
2727917SReza.Sabdar@Sun.COM 			break;
2737917SReza.Sabdar@Sun.COM 		}
2747917SReza.Sabdar@Sun.COM 
275*9714SReza.Sabdar@Sun.COM skip_entry:
2767917SReza.Sabdar@Sun.COM 		p = p + ptr->d_reclen;
2777917SReza.Sabdar@Sun.COM 		len -= ptr->d_reclen;
2787917SReza.Sabdar@Sun.COM 	} while (len);
2797917SReza.Sabdar@Sun.COM 
2807917SReza.Sabdar@Sun.COM 	*cookie = (longlong_t)(p - (char *)buf);
2817917SReza.Sabdar@Sun.COM 	*nbyte = len;
2827917SReza.Sabdar@Sun.COM 	return (rv);
2837917SReza.Sabdar@Sun.COM }
2847917SReza.Sabdar@Sun.COM 
2857917SReza.Sabdar@Sun.COM /*
2867917SReza.Sabdar@Sun.COM  * Read the directory entries and return the information about
2877917SReza.Sabdar@Sun.COM  * each entry
2887917SReza.Sabdar@Sun.COM  */
2897917SReza.Sabdar@Sun.COM int
2907917SReza.Sabdar@Sun.COM fs_readdir(fs_fhandle_t *ts_fh, char *path, long *dpos,
291*9714SReza.Sabdar@Sun.COM     char *nm, int *el, fs_fhandle_t *efh, struct stat64 *est)
2927917SReza.Sabdar@Sun.COM {
2937917SReza.Sabdar@Sun.COM 	struct dirent *dp;
2947917SReza.Sabdar@Sun.COM 	char  file_path[PATH_MAX + 1];
2957917SReza.Sabdar@Sun.COM 	DIR *dirp;
2967917SReza.Sabdar@Sun.COM 	int rv;
2977917SReza.Sabdar@Sun.COM 
2987917SReza.Sabdar@Sun.COM 	if ((dirp = opendir(ts_fh->fh_fpath)) == NULL)
2997917SReza.Sabdar@Sun.COM 		return (errno);
3007917SReza.Sabdar@Sun.COM 
3017917SReza.Sabdar@Sun.COM 	seekdir(dirp, *dpos);
3027917SReza.Sabdar@Sun.COM 	if ((dp = readdir(dirp)) == NULL) {
3037917SReza.Sabdar@Sun.COM 		rv = 0;  /* skip this dir */
3047917SReza.Sabdar@Sun.COM 		*el = 0;
3057917SReza.Sabdar@Sun.COM 	} else {
3067917SReza.Sabdar@Sun.COM 		(void) snprintf(file_path, PATH_MAX, "%s/", path);
3077917SReza.Sabdar@Sun.COM 		(void) strlcat(file_path, dp->d_name, PATH_MAX);
3087917SReza.Sabdar@Sun.COM 
309*9714SReza.Sabdar@Sun.COM 		rv = fs_getstat(file_path, efh, est);
3107917SReza.Sabdar@Sun.COM 		if (rv == 0) {
3117917SReza.Sabdar@Sun.COM 			*dpos = telldir(dirp);
3127917SReza.Sabdar@Sun.COM 			(void) strlcpy(nm, dp->d_name, NAME_MAX);
3137917SReza.Sabdar@Sun.COM 			*el = strlen(dp->d_name);
3147917SReza.Sabdar@Sun.COM 		} else {
3157917SReza.Sabdar@Sun.COM 			*el = 0;
3167917SReza.Sabdar@Sun.COM 		}
3177917SReza.Sabdar@Sun.COM 	}
3187917SReza.Sabdar@Sun.COM 	(void) closedir(dirp);
3197917SReza.Sabdar@Sun.COM 	return (rv);
3207917SReza.Sabdar@Sun.COM }
3217917SReza.Sabdar@Sun.COM 
3227917SReza.Sabdar@Sun.COM /*
3237917SReza.Sabdar@Sun.COM  * Traverse the file system in the post-order way.  The description
3247917SReza.Sabdar@Sun.COM  * and example is in the header file.
3257917SReza.Sabdar@Sun.COM  *
3267917SReza.Sabdar@Sun.COM  * The callback function should return 0, on success and non-zero on
3277917SReza.Sabdar@Sun.COM  * failure.  If the callback function returns non-zero return value,
3287917SReza.Sabdar@Sun.COM  * the traversing stops.
3297917SReza.Sabdar@Sun.COM  */
3307917SReza.Sabdar@Sun.COM int
3317917SReza.Sabdar@Sun.COM traverse_post(struct fs_traverse *ftp)
3327917SReza.Sabdar@Sun.COM {
3337917SReza.Sabdar@Sun.COM 	char path[PATH_MAX + 1]; /* full path name of the current dir */
3347917SReza.Sabdar@Sun.COM 	char nm[NAME_MAX + 1]; /* directory entry name */
3357917SReza.Sabdar@Sun.COM 	char *lp; /* last position on the path */
3367917SReza.Sabdar@Sun.COM 	int next_dir, rv;
3377917SReza.Sabdar@Sun.COM 	int pl, el; /* path and directory entry length */
3387917SReza.Sabdar@Sun.COM 	cstack_t *sp;
3397917SReza.Sabdar@Sun.COM 	fs_fhandle_t pfh, efh;
3407917SReza.Sabdar@Sun.COM 	struct stat64 pst, est;
3417917SReza.Sabdar@Sun.COM 	traverse_state_t *tsp;
3427917SReza.Sabdar@Sun.COM 	struct fst_node pn, en; /* parent and entry nodes */
3437917SReza.Sabdar@Sun.COM 
3447917SReza.Sabdar@Sun.COM 	if (!ftp || !ftp->ft_path || !*ftp->ft_path || !ftp->ft_callbk) {
3457917SReza.Sabdar@Sun.COM 		NDMP_LOG(LOG_DEBUG, "Invalid argument");
3467917SReza.Sabdar@Sun.COM 		errno = EINVAL;
3477917SReza.Sabdar@Sun.COM 		return (-1);
3487917SReza.Sabdar@Sun.COM 	}
3497917SReza.Sabdar@Sun.COM 
3507917SReza.Sabdar@Sun.COM 	/* set the default log function if it's not already set */
3517917SReza.Sabdar@Sun.COM 	if (!ftp->ft_logfp) {
3527917SReza.Sabdar@Sun.COM 		ftp->ft_logfp = (ft_log_t)syslog;
3537917SReza.Sabdar@Sun.COM 		NDMP_LOG(LOG_DEBUG, "Log to system log \"%s\"", ftp->ft_path);
3547917SReza.Sabdar@Sun.COM 	}
3557917SReza.Sabdar@Sun.COM 
3567917SReza.Sabdar@Sun.COM 	/* set the logical path to physical path if it's not already set */
3577917SReza.Sabdar@Sun.COM 	if (!ftp->ft_lpath) {
3587917SReza.Sabdar@Sun.COM 		NDMP_LOG(LOG_DEBUG,
3597917SReza.Sabdar@Sun.COM 		    "report the same paths: \"%s\"", ftp->ft_path);
3607917SReza.Sabdar@Sun.COM 		ftp->ft_lpath = ftp->ft_path;
3617917SReza.Sabdar@Sun.COM 	}
3627917SReza.Sabdar@Sun.COM 
3637917SReza.Sabdar@Sun.COM 	pl = strlen(ftp->ft_lpath);
3647917SReza.Sabdar@Sun.COM 	if (pl + 1 > PATH_MAX) { /* +1 for the '/' */
3657917SReza.Sabdar@Sun.COM 		NDMP_LOG(LOG_DEBUG, "lpath too long \"%s\"", ftp->ft_path);
3667917SReza.Sabdar@Sun.COM 		errno = ENAMETOOLONG;
3677917SReza.Sabdar@Sun.COM 		return (-1);
3687917SReza.Sabdar@Sun.COM 	}
3697917SReza.Sabdar@Sun.COM 	(void) strcpy(path, ftp->ft_lpath);
3707917SReza.Sabdar@Sun.COM 	(void) memset(&pfh, 0, sizeof (pfh));
371*9714SReza.Sabdar@Sun.COM 	rv = fs_getstat(ftp->ft_lpath, &pfh, &pst);
3727917SReza.Sabdar@Sun.COM 
3737917SReza.Sabdar@Sun.COM 	if (rv != 0) {
3747917SReza.Sabdar@Sun.COM 		NDMP_LOG(LOG_DEBUG,
3757917SReza.Sabdar@Sun.COM 		    "Error %d on fs_getstat(%s)", rv, ftp->ft_path);
3767917SReza.Sabdar@Sun.COM 		return (rv);
3777917SReza.Sabdar@Sun.COM 	}
3787917SReza.Sabdar@Sun.COM 
3797917SReza.Sabdar@Sun.COM 	if (!S_ISDIR(pst.st_mode)) {
3807917SReza.Sabdar@Sun.COM 		pn.tn_path = ftp->ft_lpath;
3817917SReza.Sabdar@Sun.COM 		pn.tn_fh = &pfh;
3827917SReza.Sabdar@Sun.COM 		pn.tn_st = &pst;
3837917SReza.Sabdar@Sun.COM 		en.tn_path = NULL;
3847917SReza.Sabdar@Sun.COM 		en.tn_fh = NULL;
3857917SReza.Sabdar@Sun.COM 		en.tn_st = NULL;
3867917SReza.Sabdar@Sun.COM 		rv = CALLBACK(&pn, &en);
3877917SReza.Sabdar@Sun.COM 		if (VERBOSE(ftp))
3887917SReza.Sabdar@Sun.COM 			NDMP_LOG(LOG_DEBUG, "CALLBACK(%s): %d", pn.tn_path, rv);
3897917SReza.Sabdar@Sun.COM 		free(pfh.fh_fpath);
3907917SReza.Sabdar@Sun.COM 		return (rv);
3917917SReza.Sabdar@Sun.COM 	}
3927917SReza.Sabdar@Sun.COM 
3937917SReza.Sabdar@Sun.COM 	sp = cstack_new();
3947917SReza.Sabdar@Sun.COM 	if (!sp) {
3957917SReza.Sabdar@Sun.COM 		errno = ENOMEM;
3967917SReza.Sabdar@Sun.COM 		free(pfh.fh_fpath);
3977917SReza.Sabdar@Sun.COM 		return (-1);
3987917SReza.Sabdar@Sun.COM 	}
3997917SReza.Sabdar@Sun.COM 	tsp = new_tsp(path);
4007917SReza.Sabdar@Sun.COM 	if (!tsp) {
4017917SReza.Sabdar@Sun.COM 		cstack_delete(sp);
4027917SReza.Sabdar@Sun.COM 		errno = ENOMEM;
4037917SReza.Sabdar@Sun.COM 		free(pfh.fh_fpath);
4047917SReza.Sabdar@Sun.COM 		return (-1);
4057917SReza.Sabdar@Sun.COM 	}
4067917SReza.Sabdar@Sun.COM 	tsp->ts_ent = tsp->ts_end;
4077917SReza.Sabdar@Sun.COM 	tsp->ts_fh = pfh;
4087917SReza.Sabdar@Sun.COM 	tsp->ts_st = pst;
4097917SReza.Sabdar@Sun.COM 	pn.tn_path = path;
4107917SReza.Sabdar@Sun.COM 	pn.tn_fh = &tsp->ts_fh;
4117917SReza.Sabdar@Sun.COM 	pn.tn_st = &tsp->ts_st;
4127917SReza.Sabdar@Sun.COM 
4137917SReza.Sabdar@Sun.COM 	rv = 0;
4147917SReza.Sabdar@Sun.COM 	next_dir = 1;
4157917SReza.Sabdar@Sun.COM 	do {
4167917SReza.Sabdar@Sun.COM 		if (next_dir) {
4177917SReza.Sabdar@Sun.COM 			traverse_stats.fss_newdirs++;
4187917SReza.Sabdar@Sun.COM 
4197917SReza.Sabdar@Sun.COM 			*tsp->ts_end = '\0';
4207917SReza.Sabdar@Sun.COM 			if (VERBOSE(ftp))
4217917SReza.Sabdar@Sun.COM 				NDMP_LOG(LOG_DEBUG, "pl %d \"%s\"", pl, path);
4227917SReza.Sabdar@Sun.COM 		}
4237917SReza.Sabdar@Sun.COM 
4247917SReza.Sabdar@Sun.COM 		next_dir = 0;
4257917SReza.Sabdar@Sun.COM 		do {
4267917SReza.Sabdar@Sun.COM 			el = NAME_MAX;
4277917SReza.Sabdar@Sun.COM 			rv = fs_readdir(&tsp->ts_fh, pn.tn_path,
4287917SReza.Sabdar@Sun.COM 			    &tsp->ts_dpos, nm, &el,
429*9714SReza.Sabdar@Sun.COM 			    &efh, &est);
4307917SReza.Sabdar@Sun.COM 
4317917SReza.Sabdar@Sun.COM 			if (rv != 0) {
432*9714SReza.Sabdar@Sun.COM 				free(efh.fh_fpath);
4337917SReza.Sabdar@Sun.COM 				traverse_stats.fss_readdir_err++;
4347917SReza.Sabdar@Sun.COM 
4357917SReza.Sabdar@Sun.COM 				NDMP_LOG(LOG_DEBUG,
4367917SReza.Sabdar@Sun.COM 				    "Error %d on readdir(%s) pos %d",
4377917SReza.Sabdar@Sun.COM 				    rv, path, tsp->ts_dpos);
4387917SReza.Sabdar@Sun.COM 				if (STOP_ONERR(ftp))
4397917SReza.Sabdar@Sun.COM 					break;
4407917SReza.Sabdar@Sun.COM 				rv = SKIP_ENTRY;
4417917SReza.Sabdar@Sun.COM 
4427917SReza.Sabdar@Sun.COM 				continue;
4437917SReza.Sabdar@Sun.COM 			}
4447917SReza.Sabdar@Sun.COM 
4457917SReza.Sabdar@Sun.COM 			/* done with this directory */
4467917SReza.Sabdar@Sun.COM 			if (el == 0) {
4477917SReza.Sabdar@Sun.COM 				if (VERBOSE(ftp))
4487917SReza.Sabdar@Sun.COM 					NDMP_LOG(LOG_DEBUG,
4497917SReza.Sabdar@Sun.COM 					    "Done(%s)", pn.tn_path);
4507917SReza.Sabdar@Sun.COM 				break;
4517917SReza.Sabdar@Sun.COM 			}
4527917SReza.Sabdar@Sun.COM 			nm[el] = '\0';
4537917SReza.Sabdar@Sun.COM 
4547917SReza.Sabdar@Sun.COM 			if (rootfs_dot_or_dotdot(nm)) {
455*9714SReza.Sabdar@Sun.COM 				free(efh.fh_fpath);
4567917SReza.Sabdar@Sun.COM 				continue;
4577917SReza.Sabdar@Sun.COM 			}
4587917SReza.Sabdar@Sun.COM 
4597917SReza.Sabdar@Sun.COM 			if (VERBOSE(ftp))
4607917SReza.Sabdar@Sun.COM 				NDMP_LOG(LOG_DEBUG, "%u dname: \"%s\"",
4617917SReza.Sabdar@Sun.COM 				    tsp->ts_dpos, nm);
4627917SReza.Sabdar@Sun.COM 
4637917SReza.Sabdar@Sun.COM 			if (pl + 1 + el > PATH_MAX) {
4647917SReza.Sabdar@Sun.COM 				traverse_stats.fss_longpath_err++;
4657917SReza.Sabdar@Sun.COM 
4667917SReza.Sabdar@Sun.COM 				NDMP_LOG(LOG_ERR, "Path %s/%s is too long.",
4677917SReza.Sabdar@Sun.COM 				    path, nm);
4687917SReza.Sabdar@Sun.COM 				if (STOP_ONLONG(ftp))
4697917SReza.Sabdar@Sun.COM 					rv = ENAMETOOLONG;
470*9714SReza.Sabdar@Sun.COM 				free(efh.fh_fpath);
4717917SReza.Sabdar@Sun.COM 				continue;
4727917SReza.Sabdar@Sun.COM 			}
4737917SReza.Sabdar@Sun.COM 
4747917SReza.Sabdar@Sun.COM 			/*
4757917SReza.Sabdar@Sun.COM 			 * Push the current directory on to the stack and
4767917SReza.Sabdar@Sun.COM 			 * dive into the entry found.
4777917SReza.Sabdar@Sun.COM 			 */
4787917SReza.Sabdar@Sun.COM 			if (S_ISDIR(est.st_mode)) {
4797917SReza.Sabdar@Sun.COM 
4807917SReza.Sabdar@Sun.COM 				assert(tsp != NULL);
4817917SReza.Sabdar@Sun.COM 				if (cstack_push(sp, tsp, 0)) {
4827917SReza.Sabdar@Sun.COM 					rv = ENOMEM;
483*9714SReza.Sabdar@Sun.COM 					free(efh.fh_fpath);
4847917SReza.Sabdar@Sun.COM 					break;
4857917SReza.Sabdar@Sun.COM 				}
4867917SReza.Sabdar@Sun.COM 				traverse_stats.fss_pushes++;
4877917SReza.Sabdar@Sun.COM 
4887917SReza.Sabdar@Sun.COM 				/*
4897917SReza.Sabdar@Sun.COM 				 * Concatenate the current entry with the
4907917SReza.Sabdar@Sun.COM 				 * current path.  This will be the path of
4917917SReza.Sabdar@Sun.COM 				 * the new directory to be scanned.
4927917SReza.Sabdar@Sun.COM 				 *
4937917SReza.Sabdar@Sun.COM 				 * Note:
4947917SReza.Sabdar@Sun.COM 				 * sprintf(tsp->ts_end, "/%s", de->d_name);
4957917SReza.Sabdar@Sun.COM 				 * could be used here, but concatenating
4967917SReza.Sabdar@Sun.COM 				 * strings like this might be faster.
4977917SReza.Sabdar@Sun.COM 				 * The length of the new path has been
4987917SReza.Sabdar@Sun.COM 				 * checked above.  So strcpy() can be
4997917SReza.Sabdar@Sun.COM 				 * safe and should not lead to a buffer
5007917SReza.Sabdar@Sun.COM 				 * over-run.
5017917SReza.Sabdar@Sun.COM 				 */
5027917SReza.Sabdar@Sun.COM 				lp = tsp->ts_end;
5037917SReza.Sabdar@Sun.COM 				*tsp->ts_end = '/';
5047917SReza.Sabdar@Sun.COM 				(void) strcpy(tsp->ts_end + 1, nm);
5057917SReza.Sabdar@Sun.COM 
5067917SReza.Sabdar@Sun.COM 				tsp = new_tsp(path);
5077917SReza.Sabdar@Sun.COM 				if (!tsp) {
508*9714SReza.Sabdar@Sun.COM 					free(efh.fh_fpath);
5097917SReza.Sabdar@Sun.COM 					rv = ENOMEM;
5107917SReza.Sabdar@Sun.COM 				} else {
5117917SReza.Sabdar@Sun.COM 					next_dir = 1;
5127917SReza.Sabdar@Sun.COM 					pl += el;
5137917SReza.Sabdar@Sun.COM 					tsp->ts_fh = efh;
5147917SReza.Sabdar@Sun.COM 					tsp->ts_st = est;
5157917SReza.Sabdar@Sun.COM 					tsp->ts_ent = lp;
5167917SReza.Sabdar@Sun.COM 					pn.tn_fh = &tsp->ts_fh;
5177917SReza.Sabdar@Sun.COM 					pn.tn_st = &tsp->ts_st;
5187917SReza.Sabdar@Sun.COM 				}
5197917SReza.Sabdar@Sun.COM 				break;
5207917SReza.Sabdar@Sun.COM 			} else {
5217917SReza.Sabdar@Sun.COM 				/*
5227917SReza.Sabdar@Sun.COM 				 * The entry is not a directory so the
5237917SReza.Sabdar@Sun.COM 				 * callback function must be called.
5247917SReza.Sabdar@Sun.COM 				 */
5257917SReza.Sabdar@Sun.COM 				traverse_stats.fss_nondir_calls++;
5267917SReza.Sabdar@Sun.COM 
5277917SReza.Sabdar@Sun.COM 				en.tn_path = nm;
5287917SReza.Sabdar@Sun.COM 				en.tn_fh = &efh;
5297917SReza.Sabdar@Sun.COM 				en.tn_st = &est;
5307917SReza.Sabdar@Sun.COM 				rv = CALLBACK(&pn, &en);
531*9714SReza.Sabdar@Sun.COM 				free(efh.fh_fpath);
5327917SReza.Sabdar@Sun.COM 				if (VERBOSE(ftp))
5337917SReza.Sabdar@Sun.COM 					NDMP_LOG(LOG_DEBUG,
5347917SReza.Sabdar@Sun.COM 					    "CALLBACK(%s/%s): %d",
5357917SReza.Sabdar@Sun.COM 					    pn.tn_path, en.tn_path, rv);
5367917SReza.Sabdar@Sun.COM 
5377917SReza.Sabdar@Sun.COM 				if (rv != 0)
5387917SReza.Sabdar@Sun.COM 					break;
5397917SReza.Sabdar@Sun.COM 			}
5407917SReza.Sabdar@Sun.COM 		} while (rv == 0);
5417917SReza.Sabdar@Sun.COM 
5427917SReza.Sabdar@Sun.COM 		/*
5437917SReza.Sabdar@Sun.COM 		 * A new directory must be processed, go to the start of
5447917SReza.Sabdar@Sun.COM 		 * the loop, open it and process it.
5457917SReza.Sabdar@Sun.COM 		 */
5467917SReza.Sabdar@Sun.COM 		if (next_dir)
5477917SReza.Sabdar@Sun.COM 			continue;
5487917SReza.Sabdar@Sun.COM 
5497917SReza.Sabdar@Sun.COM 		if (rv == SKIP_ENTRY)
5507917SReza.Sabdar@Sun.COM 			rv = 0; /* We should skip the current directory */
5517917SReza.Sabdar@Sun.COM 
5527917SReza.Sabdar@Sun.COM 		if (rv == 0) {
5537917SReza.Sabdar@Sun.COM 			/*
5547917SReza.Sabdar@Sun.COM 			 * Remove the ent from the end of path and send it
5557917SReza.Sabdar@Sun.COM 			 * as an entry of the path.
5567917SReza.Sabdar@Sun.COM 			 */
5577917SReza.Sabdar@Sun.COM 			lp = tsp->ts_ent;
5587917SReza.Sabdar@Sun.COM 			*lp = '\0';
5597917SReza.Sabdar@Sun.COM 			efh = tsp->ts_fh;
5607917SReza.Sabdar@Sun.COM 			est = tsp->ts_st;
5617917SReza.Sabdar@Sun.COM 			free(tsp);
5627917SReza.Sabdar@Sun.COM 			if (cstack_pop(sp, (void **)&tsp, (int *)NULL))
5637917SReza.Sabdar@Sun.COM 				break;
5647917SReza.Sabdar@Sun.COM 
5657917SReza.Sabdar@Sun.COM 			assert(tsp != NULL);
5667917SReza.Sabdar@Sun.COM 			pl = tsp->ts_end - path;
5677917SReza.Sabdar@Sun.COM 
5687917SReza.Sabdar@Sun.COM 			if (VERBOSE(ftp))
5697917SReza.Sabdar@Sun.COM 				NDMP_LOG(LOG_DEBUG, "poped pl %d 0x%p \"%s\"",
5707917SReza.Sabdar@Sun.COM 				    pl, tsp, path);
5717917SReza.Sabdar@Sun.COM 
5727917SReza.Sabdar@Sun.COM 			traverse_stats.fss_pops++;
5737917SReza.Sabdar@Sun.COM 			traverse_stats.fss_dir_calls++;
5747917SReza.Sabdar@Sun.COM 
5757917SReza.Sabdar@Sun.COM 			pn.tn_fh = &tsp->ts_fh;
5767917SReza.Sabdar@Sun.COM 			pn.tn_st = &tsp->ts_st;
5777917SReza.Sabdar@Sun.COM 			en.tn_path = lp + 1;
5787917SReza.Sabdar@Sun.COM 			en.tn_fh = &efh;
5797917SReza.Sabdar@Sun.COM 			en.tn_st = &est;
5807917SReza.Sabdar@Sun.COM 
5817917SReza.Sabdar@Sun.COM 			rv = CALLBACK(&pn, &en);
582*9714SReza.Sabdar@Sun.COM 			free(efh.fh_fpath);
5837917SReza.Sabdar@Sun.COM 			if (VERBOSE(ftp))
5847917SReza.Sabdar@Sun.COM 				NDMP_LOG(LOG_DEBUG, "CALLBACK(%s/%s): %d",
5857917SReza.Sabdar@Sun.COM 				    pn.tn_path, en.tn_path, rv);
5867917SReza.Sabdar@Sun.COM 			/*
5877917SReza.Sabdar@Sun.COM 			 * Does not need to free tsp here.  It will be released
5887917SReza.Sabdar@Sun.COM 			 * later.
5897917SReza.Sabdar@Sun.COM 			 */
5907917SReza.Sabdar@Sun.COM 		}
5917917SReza.Sabdar@Sun.COM 
592*9714SReza.Sabdar@Sun.COM 		if (rv != 0 && tsp) {
593*9714SReza.Sabdar@Sun.COM 			free(tsp->ts_fh.fh_fpath);
5947917SReza.Sabdar@Sun.COM 			free(tsp);
595*9714SReza.Sabdar@Sun.COM 		}
5967917SReza.Sabdar@Sun.COM 
5977917SReza.Sabdar@Sun.COM 	} while (rv == 0);
5987917SReza.Sabdar@Sun.COM 
5997917SReza.Sabdar@Sun.COM 	/*
6007917SReza.Sabdar@Sun.COM 	 * For the 'ftp->ft_path' directory itself.
6017917SReza.Sabdar@Sun.COM 	 */
6027917SReza.Sabdar@Sun.COM 	if (rv == 0) {
6037917SReza.Sabdar@Sun.COM 		traverse_stats.fss_dir_calls++;
6047917SReza.Sabdar@Sun.COM 
6057917SReza.Sabdar@Sun.COM 		pn.tn_fh = &efh;
6067917SReza.Sabdar@Sun.COM 		pn.tn_st = &est;
6077917SReza.Sabdar@Sun.COM 		en.tn_path = NULL;
6087917SReza.Sabdar@Sun.COM 		en.tn_fh = NULL;
6097917SReza.Sabdar@Sun.COM 		en.tn_st = NULL;
6107917SReza.Sabdar@Sun.COM 		rv = CALLBACK(&pn, &en);
6117917SReza.Sabdar@Sun.COM 		if (VERBOSE(ftp))
6127917SReza.Sabdar@Sun.COM 			NDMP_LOG(LOG_DEBUG, "CALLBACK(%s): %d", pn.tn_path, rv);
6137917SReza.Sabdar@Sun.COM 	}
6147917SReza.Sabdar@Sun.COM 
6157917SReza.Sabdar@Sun.COM 	/*
6167917SReza.Sabdar@Sun.COM 	 * Pop and free all the remaining entries on the stack.
6177917SReza.Sabdar@Sun.COM 	 */
6187917SReza.Sabdar@Sun.COM 	while (!cstack_pop(sp, (void **)&tsp, (int *)NULL)) {
6197917SReza.Sabdar@Sun.COM 		traverse_stats.fss_stack_residue++;
6207917SReza.Sabdar@Sun.COM 
621*9714SReza.Sabdar@Sun.COM 		free(tsp->ts_fh.fh_fpath);
6227917SReza.Sabdar@Sun.COM 		free(tsp);
6237917SReza.Sabdar@Sun.COM 	}
6247917SReza.Sabdar@Sun.COM 
6257917SReza.Sabdar@Sun.COM 	cstack_delete(sp);
6267917SReza.Sabdar@Sun.COM 	return (rv);
6277917SReza.Sabdar@Sun.COM }
6287917SReza.Sabdar@Sun.COM 
6297917SReza.Sabdar@Sun.COM /*
6307917SReza.Sabdar@Sun.COM  * In one pass, read all the directory entries of the specified
6317917SReza.Sabdar@Sun.COM  * directory and call the callback function for non-directory
6327917SReza.Sabdar@Sun.COM  * entries.
6337917SReza.Sabdar@Sun.COM  *
6347917SReza.Sabdar@Sun.COM  * On return:
6357917SReza.Sabdar@Sun.COM  *    0: Lets the directory to be scanned for directory entries.
6367917SReza.Sabdar@Sun.COM  *    < 0: Completely stops traversing.
6377917SReza.Sabdar@Sun.COM  *    FST_SKIP: stops further scanning of the directory.  Traversing
6387917SReza.Sabdar@Sun.COM  *        will continue with the next directory in the hierarchy.
6397917SReza.Sabdar@Sun.COM  *    SKIP_ENTRY: Failed to get the directory entries, so the caller
6407917SReza.Sabdar@Sun.COM  *	  should skip this entry.
6417917SReza.Sabdar@Sun.COM  */
6427917SReza.Sabdar@Sun.COM static int
6437917SReza.Sabdar@Sun.COM traverse_level_nondir(struct fs_traverse *ftp,
6447917SReza.Sabdar@Sun.COM     traverse_state_t *tsp, struct fst_node *pnp, dent_arg_t *darg)
6457917SReza.Sabdar@Sun.COM {
646*9714SReza.Sabdar@Sun.COM 	int pl; /* path length */
6477917SReza.Sabdar@Sun.COM 	int rv;
6487917SReza.Sabdar@Sun.COM 	struct fst_node en; /* entry node */
6497917SReza.Sabdar@Sun.COM 	longlong_t cookie_verf;
6507917SReza.Sabdar@Sun.COM 	fs_dent_info_t *dent;
6517917SReza.Sabdar@Sun.COM 	struct dirent *buf;
6527917SReza.Sabdar@Sun.COM 	size_t len = 0;
6537917SReza.Sabdar@Sun.COM 	int fd;
6547917SReza.Sabdar@Sun.COM 
6557917SReza.Sabdar@Sun.COM 	rv = 0;
6567917SReza.Sabdar@Sun.COM 	pl = strlen(pnp->tn_path);
6577917SReza.Sabdar@Sun.COM 
6587917SReza.Sabdar@Sun.COM 	buf = ndmp_malloc(MAX_DENT_BUF_SIZE);
6597917SReza.Sabdar@Sun.COM 	if (buf == NULL)
6607917SReza.Sabdar@Sun.COM 		return (errno);
6617917SReza.Sabdar@Sun.COM 
6627917SReza.Sabdar@Sun.COM 	fd = open(tsp->ts_fh.fh_fpath, O_RDONLY);
6637917SReza.Sabdar@Sun.COM 	if (fd == -1) {
6647917SReza.Sabdar@Sun.COM 		free(buf);
6657917SReza.Sabdar@Sun.COM 		return (errno);
6667917SReza.Sabdar@Sun.COM 	}
6677917SReza.Sabdar@Sun.COM 
6687917SReza.Sabdar@Sun.COM 	while (rv == 0) {
6697917SReza.Sabdar@Sun.COM 		long i, n_entries;
6707917SReza.Sabdar@Sun.COM 
6717917SReza.Sabdar@Sun.COM 		darg->da_end = 0;
6727917SReza.Sabdar@Sun.COM 		n_entries = 0;
6737917SReza.Sabdar@Sun.COM 		rv = fs_getdents(fd, buf, &len, pnp->tn_path, &tsp->ts_dpos,
674*9714SReza.Sabdar@Sun.COM 		    &cookie_verf, &n_entries, darg);
6757917SReza.Sabdar@Sun.COM 		if (n_entries == 0)
6767917SReza.Sabdar@Sun.COM 			break;
6777917SReza.Sabdar@Sun.COM 		if (rv != 0) {
6787917SReza.Sabdar@Sun.COM 			traverse_stats.fss_readdir_err++;
6797917SReza.Sabdar@Sun.COM 
6807917SReza.Sabdar@Sun.COM 			NDMP_LOG(LOG_DEBUG, "Error %d on readdir(%s) pos %d",
6817917SReza.Sabdar@Sun.COM 			    rv, pnp->tn_path, tsp->ts_dpos);
6827917SReza.Sabdar@Sun.COM 			if (STOP_ONERR(ftp)) {
6837917SReza.Sabdar@Sun.COM 				NEGATE(rv);
6847917SReza.Sabdar@Sun.COM 				break;
6857917SReza.Sabdar@Sun.COM 			}
6867917SReza.Sabdar@Sun.COM 			/*
6877917SReza.Sabdar@Sun.COM 			 * We cannot read the directory entry, we should
6887917SReza.Sabdar@Sun.COM 			 * skip to the next directory.
6897917SReza.Sabdar@Sun.COM 			 */
6907917SReza.Sabdar@Sun.COM 			rv = SKIP_ENTRY;
6917917SReza.Sabdar@Sun.COM 			continue;
6927917SReza.Sabdar@Sun.COM 		}
6937917SReza.Sabdar@Sun.COM 
6947917SReza.Sabdar@Sun.COM 		/* LINTED imporper alignment */
6957917SReza.Sabdar@Sun.COM 		dent = (fs_dent_info_t *)darg->da_buf;
6967917SReza.Sabdar@Sun.COM 		/* LINTED imporper alignment */
6977917SReza.Sabdar@Sun.COM 		for (i = 0; i < n_entries; i++, dent = (fs_dent_info_t *)
6987917SReza.Sabdar@Sun.COM 		    ((char *)dent + dent->fd_len)) {
6997917SReza.Sabdar@Sun.COM 
7007917SReza.Sabdar@Sun.COM 			if (VERBOSE(ftp))
7017917SReza.Sabdar@Sun.COM 				NDMP_LOG(LOG_DEBUG, "i %u dname: \"%s\"",
7027917SReza.Sabdar@Sun.COM 				    dent->fd_fh.fh_fid, dent->fd_name);
7037917SReza.Sabdar@Sun.COM 
7047917SReza.Sabdar@Sun.COM 			if ((pl + strlen(dent->fd_name)) > PATH_MAX) {
7057917SReza.Sabdar@Sun.COM 				traverse_stats.fss_longpath_err++;
7067917SReza.Sabdar@Sun.COM 
7077917SReza.Sabdar@Sun.COM 				NDMP_LOG(LOG_ERR, "Path %s/%s is too long.",
7087917SReza.Sabdar@Sun.COM 				    pnp->tn_path, dent->fd_name);
7097917SReza.Sabdar@Sun.COM 				if (STOP_ONLONG(ftp))
7107917SReza.Sabdar@Sun.COM 					rv = -ENAMETOOLONG;
711*9714SReza.Sabdar@Sun.COM 				free(dent->fd_fh.fh_fpath);
7127917SReza.Sabdar@Sun.COM 				continue;
7137917SReza.Sabdar@Sun.COM 			}
7147917SReza.Sabdar@Sun.COM 
7157917SReza.Sabdar@Sun.COM 			/*
7167917SReza.Sabdar@Sun.COM 			 * The entry is not a directory so the callback
7177917SReza.Sabdar@Sun.COM 			 * function must be called.
7187917SReza.Sabdar@Sun.COM 			 */
7197917SReza.Sabdar@Sun.COM 			if (!S_ISDIR(dent->fd_attr.st_mode)) {
7207917SReza.Sabdar@Sun.COM 				traverse_stats.fss_nondir_calls++;
7217917SReza.Sabdar@Sun.COM 
7227917SReza.Sabdar@Sun.COM 				en.tn_path = dent->fd_name;
7237917SReza.Sabdar@Sun.COM 				en.tn_fh = &dent->fd_fh;
7247917SReza.Sabdar@Sun.COM 				en.tn_st = &dent->fd_attr;
7257917SReza.Sabdar@Sun.COM 				rv = CALLBACK(pnp, &en);
7267917SReza.Sabdar@Sun.COM 				dent->fd_fh.fh_fpath = NULL;
7277917SReza.Sabdar@Sun.COM 				if (rv < 0)
7287917SReza.Sabdar@Sun.COM 					break;
7297917SReza.Sabdar@Sun.COM 				if (rv == FST_SKIP) {
7307917SReza.Sabdar@Sun.COM 					traverse_stats.fss_nondir_skipped++;
7317917SReza.Sabdar@Sun.COM 					break;
7327917SReza.Sabdar@Sun.COM 				}
7337917SReza.Sabdar@Sun.COM 			}
7347917SReza.Sabdar@Sun.COM 		}
7357917SReza.Sabdar@Sun.COM 	}
7367917SReza.Sabdar@Sun.COM 
7377917SReza.Sabdar@Sun.COM 	free(buf);
7387917SReza.Sabdar@Sun.COM 	(void) close(fd);
7397917SReza.Sabdar@Sun.COM 	return (rv);
7407917SReza.Sabdar@Sun.COM }
7417917SReza.Sabdar@Sun.COM 
7427917SReza.Sabdar@Sun.COM /*
7437917SReza.Sabdar@Sun.COM  * Traverse the file system in the level-order way.  The description
7447917SReza.Sabdar@Sun.COM  * and example is in the header file.
7457917SReza.Sabdar@Sun.COM  */
7467917SReza.Sabdar@Sun.COM int
7477917SReza.Sabdar@Sun.COM traverse_level(struct fs_traverse *ftp)
7487917SReza.Sabdar@Sun.COM {
7497917SReza.Sabdar@Sun.COM 	char path[PATH_MAX + 1];	/* full path name of the current dir */
7507917SReza.Sabdar@Sun.COM 	char nm[NAME_MAX + 1];	/* directory entry name */
7517917SReza.Sabdar@Sun.COM 	char *lp;		/* last position on the path */
7527917SReza.Sabdar@Sun.COM 	int next_dir, rv;
7537917SReza.Sabdar@Sun.COM 	int pl, el;		/* path and directory entry length */
7547917SReza.Sabdar@Sun.COM 
7557917SReza.Sabdar@Sun.COM 	cstack_t *sp;
7567917SReza.Sabdar@Sun.COM 	fs_fhandle_t pfh, efh;
7577917SReza.Sabdar@Sun.COM 	struct stat64 pst, est;
7587917SReza.Sabdar@Sun.COM 	traverse_state_t *tsp;
7597917SReza.Sabdar@Sun.COM 	struct fst_node pn, en;  /* parent and entry nodes */
7607917SReza.Sabdar@Sun.COM 	dent_arg_t darg;
7617917SReza.Sabdar@Sun.COM 
7627917SReza.Sabdar@Sun.COM 	if (!ftp || !ftp->ft_path || !*ftp->ft_path || !ftp->ft_callbk) {
7637917SReza.Sabdar@Sun.COM 		NDMP_LOG(LOG_DEBUG, "Invalid argument");
7647917SReza.Sabdar@Sun.COM 		errno = EINVAL;
7657917SReza.Sabdar@Sun.COM 		return (-1);
7667917SReza.Sabdar@Sun.COM 	}
7677917SReza.Sabdar@Sun.COM 	/* set the default log function if it's not already set */
7687917SReza.Sabdar@Sun.COM 	if (!ftp->ft_logfp) {
7697917SReza.Sabdar@Sun.COM 		ftp->ft_logfp = (ft_log_t)syslog;
7707917SReza.Sabdar@Sun.COM 		NDMP_LOG(LOG_DEBUG, "Log to system log \"%s\"", ftp->ft_path);
7717917SReza.Sabdar@Sun.COM 	}
7727917SReza.Sabdar@Sun.COM 	if (!ftp->ft_lpath) {
7737917SReza.Sabdar@Sun.COM 		NDMP_LOG(LOG_DEBUG,
7747917SReza.Sabdar@Sun.COM 		    "report the same paths \"%s\"", ftp->ft_path);
7757917SReza.Sabdar@Sun.COM 		ftp->ft_lpath = ftp->ft_path;
7767917SReza.Sabdar@Sun.COM 	}
7777917SReza.Sabdar@Sun.COM 
7787917SReza.Sabdar@Sun.COM 	pl = strlen(ftp->ft_lpath);
7797917SReza.Sabdar@Sun.COM 	if (pl + 1 > PATH_MAX) { /* +1 for the '/' */
7807917SReza.Sabdar@Sun.COM 		NDMP_LOG(LOG_DEBUG, "lpath too long \"%s\"", ftp->ft_path);
7817917SReza.Sabdar@Sun.COM 		errno = ENAMETOOLONG;
7827917SReza.Sabdar@Sun.COM 		return (-1);
7837917SReza.Sabdar@Sun.COM 	}
7847917SReza.Sabdar@Sun.COM 	(void) strcpy(path, ftp->ft_lpath);
7857917SReza.Sabdar@Sun.COM 	(void) memset(&pfh, 0, sizeof (pfh));
786*9714SReza.Sabdar@Sun.COM 	rv = fs_getstat(ftp->ft_lpath, &pfh, &pst);
7877917SReza.Sabdar@Sun.COM 	if (rv != 0) {
7887917SReza.Sabdar@Sun.COM 		NDMP_LOG(LOG_DEBUG,
7897917SReza.Sabdar@Sun.COM 		    "Error %d on fs_getstat(%s)", rv, ftp->ft_path);
7907917SReza.Sabdar@Sun.COM 		return (-1);
7917917SReza.Sabdar@Sun.COM 	}
7927917SReza.Sabdar@Sun.COM 
7937917SReza.Sabdar@Sun.COM 	en.tn_path = NULL;
7947917SReza.Sabdar@Sun.COM 	en.tn_fh = NULL;
7957917SReza.Sabdar@Sun.COM 	en.tn_st = NULL;
7967917SReza.Sabdar@Sun.COM 	if (!S_ISDIR(pst.st_mode)) {
7977917SReza.Sabdar@Sun.COM 		pn.tn_path = ftp->ft_lpath;
7987917SReza.Sabdar@Sun.COM 		pn.tn_fh = &pfh;
7997917SReza.Sabdar@Sun.COM 		pn.tn_st = &pst;
8007917SReza.Sabdar@Sun.COM 		rv = CALLBACK(&pn, &en);
8017917SReza.Sabdar@Sun.COM 		if (VERBOSE(ftp))
8027917SReza.Sabdar@Sun.COM 			NDMP_LOG(LOG_DEBUG, "CALLBACK(%s): %d", pn.tn_path, rv);
8037917SReza.Sabdar@Sun.COM 
8047917SReza.Sabdar@Sun.COM 		free(pfh.fh_fpath);
8057917SReza.Sabdar@Sun.COM 		return (rv);
8067917SReza.Sabdar@Sun.COM 	}
8077917SReza.Sabdar@Sun.COM 
8087917SReza.Sabdar@Sun.COM 	sp = cstack_new();
8097917SReza.Sabdar@Sun.COM 	if (!sp) {
8107917SReza.Sabdar@Sun.COM 		free(pfh.fh_fpath);
8117917SReza.Sabdar@Sun.COM 		errno = ENOMEM;
8127917SReza.Sabdar@Sun.COM 		return (-1);
8137917SReza.Sabdar@Sun.COM 	}
8147917SReza.Sabdar@Sun.COM 	tsp = new_tsp(path);
8157917SReza.Sabdar@Sun.COM 	if (!tsp) {
8167917SReza.Sabdar@Sun.COM 		cstack_delete(sp);
8177917SReza.Sabdar@Sun.COM 		free(pfh.fh_fpath);
8187917SReza.Sabdar@Sun.COM 		errno = ENOMEM;
8197917SReza.Sabdar@Sun.COM 		return (-1);
8207917SReza.Sabdar@Sun.COM 	}
8217917SReza.Sabdar@Sun.COM 
8227917SReza.Sabdar@Sun.COM 	darg.da_buf = ndmp_malloc(MAX_DENT_BUF_SIZE);
8237917SReza.Sabdar@Sun.COM 	if (!darg.da_buf) {
8247917SReza.Sabdar@Sun.COM 		cstack_delete(sp);
8257917SReza.Sabdar@Sun.COM 		free(pfh.fh_fpath);
8267917SReza.Sabdar@Sun.COM 		free(tsp);
8277917SReza.Sabdar@Sun.COM 		errno = ENOMEM;
8287917SReza.Sabdar@Sun.COM 		return (-1);
8297917SReza.Sabdar@Sun.COM 	}
8307917SReza.Sabdar@Sun.COM 	darg.da_size = MAX_DENT_BUF_SIZE;
8317917SReza.Sabdar@Sun.COM 
8327917SReza.Sabdar@Sun.COM 	tsp->ts_ent = tsp->ts_end;
8337917SReza.Sabdar@Sun.COM 	tsp->ts_fh = pfh;
8347917SReza.Sabdar@Sun.COM 	tsp->ts_st = pst;
8357917SReza.Sabdar@Sun.COM 	pn.tn_path = path;
8367917SReza.Sabdar@Sun.COM 	pn.tn_fh = &tsp->ts_fh;
8377917SReza.Sabdar@Sun.COM 	pn.tn_st = &tsp->ts_st;
8387917SReza.Sabdar@Sun.COM 
8397917SReza.Sabdar@Sun.COM 	/* call the callback function on the path itself */
8407917SReza.Sabdar@Sun.COM 	traverse_stats.fss_dir_calls++;
8417917SReza.Sabdar@Sun.COM 	rv = CALLBACK(&pn, &en);
8427917SReza.Sabdar@Sun.COM 	if (rv < 0) {
8437917SReza.Sabdar@Sun.COM 		free(tsp);
8447917SReza.Sabdar@Sun.COM 		goto end;
8457917SReza.Sabdar@Sun.COM 	}
8467917SReza.Sabdar@Sun.COM 	if (rv == FST_SKIP) {
8477917SReza.Sabdar@Sun.COM 		traverse_stats.fss_dir_skipped++;
8487917SReza.Sabdar@Sun.COM 		free(tsp);
8497917SReza.Sabdar@Sun.COM 		rv = 0;
8507917SReza.Sabdar@Sun.COM 		goto end;
8517917SReza.Sabdar@Sun.COM 	}
8527917SReza.Sabdar@Sun.COM 
8537917SReza.Sabdar@Sun.COM 	rv = 0;
8547917SReza.Sabdar@Sun.COM 	next_dir = 1;
8557917SReza.Sabdar@Sun.COM 	do {
8567917SReza.Sabdar@Sun.COM 		if (next_dir) {
8577917SReza.Sabdar@Sun.COM 			traverse_stats.fss_newdirs++;
8587917SReza.Sabdar@Sun.COM 
8597917SReza.Sabdar@Sun.COM 			*tsp->ts_end = '\0';
8607917SReza.Sabdar@Sun.COM 			if (VERBOSE(ftp))
8617917SReza.Sabdar@Sun.COM 				NDMP_LOG(LOG_DEBUG, "pl %d \"%s\"", pl, path);
8627917SReza.Sabdar@Sun.COM 
8637917SReza.Sabdar@Sun.COM 			rv = traverse_level_nondir(ftp, tsp, &pn, &darg);
8647917SReza.Sabdar@Sun.COM 			if (rv < 0) {
8657917SReza.Sabdar@Sun.COM 				NEGATE(rv);
866*9714SReza.Sabdar@Sun.COM 				free(tsp->ts_fh.fh_fpath);
8677917SReza.Sabdar@Sun.COM 				free(tsp);
8687917SReza.Sabdar@Sun.COM 				break;
8697917SReza.Sabdar@Sun.COM 			}
8707917SReza.Sabdar@Sun.COM 			/*
8717917SReza.Sabdar@Sun.COM 			 * If skipped by the callback function or
8727917SReza.Sabdar@Sun.COM 			 * error happened reading the information
8737917SReza.Sabdar@Sun.COM 			 */
8747917SReza.Sabdar@Sun.COM 			if (rv == FST_SKIP || rv == SKIP_ENTRY) {
8757917SReza.Sabdar@Sun.COM 				/*
8767917SReza.Sabdar@Sun.COM 				 * N.B. next_dir should be set to 0 as
8777917SReza.Sabdar@Sun.COM 				 * well. This prevents the infinite loop.
8787917SReza.Sabdar@Sun.COM 				 * If it's not set the same directory will
8797917SReza.Sabdar@Sun.COM 				 * be poped from the stack and will be
8807917SReza.Sabdar@Sun.COM 				 * scanned again.
8817917SReza.Sabdar@Sun.COM 				 */
8827917SReza.Sabdar@Sun.COM 				next_dir = 0;
8837917SReza.Sabdar@Sun.COM 				rv = 0;
8847917SReza.Sabdar@Sun.COM 				goto skip_dir;
8857917SReza.Sabdar@Sun.COM 			}
8867917SReza.Sabdar@Sun.COM 
8877917SReza.Sabdar@Sun.COM 			/* re-start reading entries of the directory */
8887917SReza.Sabdar@Sun.COM 			tsp->ts_dpos = 0;
8897917SReza.Sabdar@Sun.COM 		}
8907917SReza.Sabdar@Sun.COM 
8917917SReza.Sabdar@Sun.COM 		next_dir = 0;
8927917SReza.Sabdar@Sun.COM 		do {
8937917SReza.Sabdar@Sun.COM 			el = NAME_MAX;
8947917SReza.Sabdar@Sun.COM 			rv = fs_readdir(&tsp->ts_fh, pn.tn_path,
8957917SReza.Sabdar@Sun.COM 			    &tsp->ts_dpos, nm, &el, &efh,
896*9714SReza.Sabdar@Sun.COM 			    &est);
8977917SReza.Sabdar@Sun.COM 			if (rv != 0) {
8987917SReza.Sabdar@Sun.COM 				traverse_stats.fss_readdir_err++;
8997917SReza.Sabdar@Sun.COM 
9007917SReza.Sabdar@Sun.COM 				NDMP_LOG(LOG_DEBUG,
9017917SReza.Sabdar@Sun.COM 				    "Error %d on readdir(%s) pos %d",
9027917SReza.Sabdar@Sun.COM 				    rv, path, tsp->ts_dpos);
9037917SReza.Sabdar@Sun.COM 				if (STOP_ONERR(ftp))
9047917SReza.Sabdar@Sun.COM 					break;
9057917SReza.Sabdar@Sun.COM 				rv = SKIP_ENTRY;
9067917SReza.Sabdar@Sun.COM 				continue;
9077917SReza.Sabdar@Sun.COM 			}
9087917SReza.Sabdar@Sun.COM 
9097917SReza.Sabdar@Sun.COM 			/* done with this directory */
9107917SReza.Sabdar@Sun.COM 			if (el == 0)
9117917SReza.Sabdar@Sun.COM 				break;
9127917SReza.Sabdar@Sun.COM 
9137917SReza.Sabdar@Sun.COM 			nm[el] = '\0';
9147917SReza.Sabdar@Sun.COM 
9157917SReza.Sabdar@Sun.COM 			if (rootfs_dot_or_dotdot(nm)) {
916*9714SReza.Sabdar@Sun.COM 				free(efh.fh_fpath);
9177917SReza.Sabdar@Sun.COM 				continue;
9187917SReza.Sabdar@Sun.COM 			}
9197917SReza.Sabdar@Sun.COM 
9207917SReza.Sabdar@Sun.COM 			if (VERBOSE(ftp))
9217917SReza.Sabdar@Sun.COM 				NDMP_LOG(LOG_DEBUG, "%u dname: \"%s\"",
9227917SReza.Sabdar@Sun.COM 				    tsp->ts_dpos, nm);
9237917SReza.Sabdar@Sun.COM 
9247917SReza.Sabdar@Sun.COM 			if (pl + 1 + el > PATH_MAX) {
9257917SReza.Sabdar@Sun.COM 				/*
9267917SReza.Sabdar@Sun.COM 				 * The long paths were already encountered
9277917SReza.Sabdar@Sun.COM 				 * when processing non-dir entries in.
9287917SReza.Sabdar@Sun.COM 				 * traverse_level_nondir.
9297917SReza.Sabdar@Sun.COM 				 * We don't increase fss_longpath_err
9307917SReza.Sabdar@Sun.COM 				 * counter for them again here.
9317917SReza.Sabdar@Sun.COM 				 */
9327917SReza.Sabdar@Sun.COM 				NDMP_LOG(LOG_ERR, "Path %s/%s is too long.",
9337917SReza.Sabdar@Sun.COM 				    path, nm);
9347917SReza.Sabdar@Sun.COM 				if (STOP_ONLONG(ftp))
9357917SReza.Sabdar@Sun.COM 					rv = ENAMETOOLONG;
936*9714SReza.Sabdar@Sun.COM 				free(efh.fh_fpath);
9377917SReza.Sabdar@Sun.COM 				continue;
9387917SReza.Sabdar@Sun.COM 			}
9397917SReza.Sabdar@Sun.COM 
940*9714SReza.Sabdar@Sun.COM 			if (!S_ISDIR(est.st_mode))
9417917SReza.Sabdar@Sun.COM 				continue;
9427917SReza.Sabdar@Sun.COM 
9437917SReza.Sabdar@Sun.COM 			/*
9447917SReza.Sabdar@Sun.COM 			 * Call the callback function for the new
9457917SReza.Sabdar@Sun.COM 			 * directory found, then push the current
9467917SReza.Sabdar@Sun.COM 			 * directory on to the stack.  Then dive
9477917SReza.Sabdar@Sun.COM 			 * into the entry found.
9487917SReza.Sabdar@Sun.COM 			 */
9497917SReza.Sabdar@Sun.COM 			traverse_stats.fss_dir_calls++;
9507917SReza.Sabdar@Sun.COM 			en.tn_path = nm;
9517917SReza.Sabdar@Sun.COM 			en.tn_fh = &efh;
9527917SReza.Sabdar@Sun.COM 			en.tn_st = &est;
9537917SReza.Sabdar@Sun.COM 			rv = CALLBACK(&pn, &en);
9547917SReza.Sabdar@Sun.COM 
9557917SReza.Sabdar@Sun.COM 			if (rv < 0) {
9567917SReza.Sabdar@Sun.COM 				NEGATE(rv);
957*9714SReza.Sabdar@Sun.COM 				free(efh.fh_fpath);
9587917SReza.Sabdar@Sun.COM 				break;
9597917SReza.Sabdar@Sun.COM 			}
9607917SReza.Sabdar@Sun.COM 			if (rv == FST_SKIP) {
9617917SReza.Sabdar@Sun.COM 				traverse_stats.fss_dir_skipped++;
962*9714SReza.Sabdar@Sun.COM 				free(efh.fh_fpath);
9637917SReza.Sabdar@Sun.COM 				rv = 0;
9647917SReza.Sabdar@Sun.COM 				continue;
9657917SReza.Sabdar@Sun.COM 			}
9667917SReza.Sabdar@Sun.COM 
9677917SReza.Sabdar@Sun.COM 			/*
9687917SReza.Sabdar@Sun.COM 			 * Push the current directory on to the stack and
9697917SReza.Sabdar@Sun.COM 			 * dive into the entry found.
9707917SReza.Sabdar@Sun.COM 			 */
971*9714SReza.Sabdar@Sun.COM 			if (cstack_push(sp, tsp, 0)) {
9727917SReza.Sabdar@Sun.COM 				rv = ENOMEM;
973*9714SReza.Sabdar@Sun.COM 			} else {
9747917SReza.Sabdar@Sun.COM 				traverse_stats.fss_pushes++;
9757917SReza.Sabdar@Sun.COM 
9767917SReza.Sabdar@Sun.COM 				lp = tsp->ts_end;
9777917SReza.Sabdar@Sun.COM 				*tsp->ts_end = '/';
9787917SReza.Sabdar@Sun.COM 				(void) strcpy(tsp->ts_end + 1, nm);
9797917SReza.Sabdar@Sun.COM 
9807917SReza.Sabdar@Sun.COM 				tsp = new_tsp(path);
9817917SReza.Sabdar@Sun.COM 				if (!tsp)
9827917SReza.Sabdar@Sun.COM 					rv = ENOMEM;
9837917SReza.Sabdar@Sun.COM 				else {
9847917SReza.Sabdar@Sun.COM 					next_dir = 1;
9857917SReza.Sabdar@Sun.COM 					pl += el + 1;
9867917SReza.Sabdar@Sun.COM 					tsp->ts_fh = efh;
9877917SReza.Sabdar@Sun.COM 					tsp->ts_st = est;
9887917SReza.Sabdar@Sun.COM 					tsp->ts_ent = lp;
9897917SReza.Sabdar@Sun.COM 					pn.tn_fh = &tsp->ts_fh;
9907917SReza.Sabdar@Sun.COM 					pn.tn_st = &tsp->ts_st;
9917917SReza.Sabdar@Sun.COM 				}
9927917SReza.Sabdar@Sun.COM 			}
9937917SReza.Sabdar@Sun.COM 			break;
9947917SReza.Sabdar@Sun.COM 
9957917SReza.Sabdar@Sun.COM 		} while (rv == 0);
9967917SReza.Sabdar@Sun.COM 
9977917SReza.Sabdar@Sun.COM 		/*
9987917SReza.Sabdar@Sun.COM 		 * A new directory must be processed, go to the start of
9997917SReza.Sabdar@Sun.COM 		 * the loop, open it and process it.
10007917SReza.Sabdar@Sun.COM 		 */
10017917SReza.Sabdar@Sun.COM 		if (next_dir)
10027917SReza.Sabdar@Sun.COM 			continue;
10037917SReza.Sabdar@Sun.COM skip_dir:
1004*9714SReza.Sabdar@Sun.COM 		if (tsp) {
1005*9714SReza.Sabdar@Sun.COM 			free(tsp->ts_fh.fh_fpath);
10067917SReza.Sabdar@Sun.COM 			free(tsp);
1007*9714SReza.Sabdar@Sun.COM 		}
10087917SReza.Sabdar@Sun.COM 
10097917SReza.Sabdar@Sun.COM 		if (rv == SKIP_ENTRY)
10107917SReza.Sabdar@Sun.COM 			rv = 0;
10117917SReza.Sabdar@Sun.COM 
10127917SReza.Sabdar@Sun.COM 		if (rv == 0) {
10137917SReza.Sabdar@Sun.COM 			if (cstack_pop(sp, (void **)&tsp, (int *)NULL))
10147917SReza.Sabdar@Sun.COM 				break;
10157917SReza.Sabdar@Sun.COM 
10167917SReza.Sabdar@Sun.COM 			traverse_stats.fss_pops++;
10177917SReza.Sabdar@Sun.COM 
10187917SReza.Sabdar@Sun.COM 			if (VERBOSE(ftp))
10197917SReza.Sabdar@Sun.COM 				NDMP_LOG(LOG_DEBUG,
10207917SReza.Sabdar@Sun.COM 				    "Poped pl %d \"%s\"", pl, path);
10217917SReza.Sabdar@Sun.COM 
10227917SReza.Sabdar@Sun.COM 			*tsp->ts_end = '\0';
10237917SReza.Sabdar@Sun.COM 			pl = tsp->ts_end - path;
10247917SReza.Sabdar@Sun.COM 			pn.tn_fh = &tsp->ts_fh;
10257917SReza.Sabdar@Sun.COM 			pn.tn_st = &tsp->ts_st;
10267917SReza.Sabdar@Sun.COM 		}
10277917SReza.Sabdar@Sun.COM 	} while (rv == 0);
10287917SReza.Sabdar@Sun.COM 
10297917SReza.Sabdar@Sun.COM 	/*
10307917SReza.Sabdar@Sun.COM 	 * Pop and free all the remaining entries on the stack.
10317917SReza.Sabdar@Sun.COM 	 */
10327917SReza.Sabdar@Sun.COM 	while (!cstack_pop(sp, (void **)&tsp, (int *)NULL)) {
10337917SReza.Sabdar@Sun.COM 		traverse_stats.fss_stack_residue++;
10347917SReza.Sabdar@Sun.COM 
1035*9714SReza.Sabdar@Sun.COM 		free(tsp->ts_fh.fh_fpath);
10367917SReza.Sabdar@Sun.COM 		free(tsp);
10377917SReza.Sabdar@Sun.COM 	}
10387917SReza.Sabdar@Sun.COM end:
10397917SReza.Sabdar@Sun.COM 	free(darg.da_buf);
10407917SReza.Sabdar@Sun.COM 	cstack_delete(sp);
10417917SReza.Sabdar@Sun.COM 	return (rv);
10427917SReza.Sabdar@Sun.COM }
10437917SReza.Sabdar@Sun.COM 
10447917SReza.Sabdar@Sun.COM /*
10457917SReza.Sabdar@Sun.COM  * filecopy - Copy a file
10467917SReza.Sabdar@Sun.COM  *
10477917SReza.Sabdar@Sun.COM  * Parameters:
10487917SReza.Sabdar@Sun.COM  *  char *dest  - Destination path
10497917SReza.Sabdar@Sun.COM  *  char *src   - Source path
10507917SReza.Sabdar@Sun.COM  *
10517917SReza.Sabdar@Sun.COM  * Returns:
10527917SReza.Sabdar@Sun.COM  *  0    - No errors
10537917SReza.Sabdar@Sun.COM  *  #0   - Error occured
10547917SReza.Sabdar@Sun.COM  *		-4   - read/write error
10557917SReza.Sabdar@Sun.COM  *		-5   - source modified during copy
10567917SReza.Sabdar@Sun.COM  *
10577917SReza.Sabdar@Sun.COM  * Simplified version for Solaris
10587917SReza.Sabdar@Sun.COM  */
10597917SReza.Sabdar@Sun.COM #define	BUFSIZE	32768
10607917SReza.Sabdar@Sun.COM int
10617917SReza.Sabdar@Sun.COM filecopy(char *dest, char *src)
10627917SReza.Sabdar@Sun.COM {
10637917SReza.Sabdar@Sun.COM 	FILE *src_fh = 0;
10647917SReza.Sabdar@Sun.COM 	FILE *dst_fh = 0;
10657917SReza.Sabdar@Sun.COM 	struct stat64 src_attr;
10667917SReza.Sabdar@Sun.COM 	struct stat64 dst_attr;
10677917SReza.Sabdar@Sun.COM 	char *buf = 0;
10687917SReza.Sabdar@Sun.COM 	u_longlong_t bytes_to_copy;
10697917SReza.Sabdar@Sun.COM 	size_t nbytes;
10707917SReza.Sabdar@Sun.COM 	int file_copied = 0;
10717917SReza.Sabdar@Sun.COM 
10727917SReza.Sabdar@Sun.COM 	buf = ndmp_malloc(BUFSIZE);
10737917SReza.Sabdar@Sun.COM 	if (!buf)
10747917SReza.Sabdar@Sun.COM 		return (-1);
10757917SReza.Sabdar@Sun.COM 
10767917SReza.Sabdar@Sun.COM 	src_fh = fopen(src, "r");
10777917SReza.Sabdar@Sun.COM 	if (src_fh == 0) {
10787917SReza.Sabdar@Sun.COM 		free(buf);
10797917SReza.Sabdar@Sun.COM 		return (-2);
10807917SReza.Sabdar@Sun.COM 	}
10817917SReza.Sabdar@Sun.COM 
10827917SReza.Sabdar@Sun.COM 	dst_fh = fopen(dest, "w");
10837917SReza.Sabdar@Sun.COM 	if (dst_fh == NULL) {
10847917SReza.Sabdar@Sun.COM 		free(buf);
10857917SReza.Sabdar@Sun.COM 		(void) fclose(src_fh);
10867917SReza.Sabdar@Sun.COM 		return (-3);
10877917SReza.Sabdar@Sun.COM 	}
10887917SReza.Sabdar@Sun.COM 
10897917SReza.Sabdar@Sun.COM 	if (stat64(src, &src_attr) < 0) {
10907917SReza.Sabdar@Sun.COM 		free(buf);
10917917SReza.Sabdar@Sun.COM 		(void) fclose(src_fh);
10927917SReza.Sabdar@Sun.COM 		(void) fclose(dst_fh);
10937917SReza.Sabdar@Sun.COM 		return (-2);
10947917SReza.Sabdar@Sun.COM 	}
10957917SReza.Sabdar@Sun.COM 
10967917SReza.Sabdar@Sun.COM 	bytes_to_copy = src_attr.st_size;
10977917SReza.Sabdar@Sun.COM 	while (bytes_to_copy) {
10987917SReza.Sabdar@Sun.COM 		if (bytes_to_copy > BUFSIZE)
10997917SReza.Sabdar@Sun.COM 			nbytes = BUFSIZE;
11007917SReza.Sabdar@Sun.COM 		else
11017917SReza.Sabdar@Sun.COM 			nbytes = bytes_to_copy;
11027917SReza.Sabdar@Sun.COM 
11037917SReza.Sabdar@Sun.COM 		if ((fread(buf, nbytes, 1, src_fh) != 1) ||
11047917SReza.Sabdar@Sun.COM 		    (fwrite(buf, nbytes, 1, dst_fh) != 1))
11057917SReza.Sabdar@Sun.COM 			break;
11067917SReza.Sabdar@Sun.COM 		bytes_to_copy -= nbytes;
11077917SReza.Sabdar@Sun.COM 	}
11087917SReza.Sabdar@Sun.COM 
11097917SReza.Sabdar@Sun.COM 	(void) fclose(src_fh);
11107917SReza.Sabdar@Sun.COM 	(void) fclose(dst_fh);
11117917SReza.Sabdar@Sun.COM 
11127917SReza.Sabdar@Sun.COM 	if (bytes_to_copy > 0) {
11137917SReza.Sabdar@Sun.COM 		free(buf);
11147917SReza.Sabdar@Sun.COM 		/* short read/write, remove the partial file */
11157917SReza.Sabdar@Sun.COM 		return (-4);
11167917SReza.Sabdar@Sun.COM 	}
11177917SReza.Sabdar@Sun.COM 
11187917SReza.Sabdar@Sun.COM 	if (stat64(src, &dst_attr) < 0) {
11197917SReza.Sabdar@Sun.COM 		free(buf);
11207917SReza.Sabdar@Sun.COM 		return (-2);
11217917SReza.Sabdar@Sun.COM 	}
11227917SReza.Sabdar@Sun.COM 
11237917SReza.Sabdar@Sun.COM 	free(buf);
11247917SReza.Sabdar@Sun.COM 
11257917SReza.Sabdar@Sun.COM 	if (!file_copied)
11267917SReza.Sabdar@Sun.COM 		return (-5);	/* source modified during copy */
11277917SReza.Sabdar@Sun.COM 	else
11287917SReza.Sabdar@Sun.COM 		return (0);
11297917SReza.Sabdar@Sun.COM }
1130