xref: /onnv-gate/usr/src/cmd/ndmpd/tlm/tlm_traverse.c (revision 9012:ee7366019c63)
17917SReza.Sabdar@Sun.COM /*
2*9012SReza.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  * Initialize a list for path names
2027917SReza.Sabdar@Sun.COM  */
2037917SReza.Sabdar@Sun.COM path_list_t *
2047917SReza.Sabdar@Sun.COM fs_init_pathlist()
2057917SReza.Sabdar@Sun.COM {
2067917SReza.Sabdar@Sun.COM 	path_list_t *pl_head;
2077917SReza.Sabdar@Sun.COM 
2087917SReza.Sabdar@Sun.COM 	pl_head = ndmp_malloc(sizeof (path_list_t));
2097917SReza.Sabdar@Sun.COM 	return (pl_head);
2107917SReza.Sabdar@Sun.COM }
2117917SReza.Sabdar@Sun.COM 
2127917SReza.Sabdar@Sun.COM /*
2137917SReza.Sabdar@Sun.COM  * Free the list of path names
2147917SReza.Sabdar@Sun.COM  */
2157917SReza.Sabdar@Sun.COM void
2167917SReza.Sabdar@Sun.COM fs_free_pathlist(path_list_t *pl_head)
2177917SReza.Sabdar@Sun.COM {
2187917SReza.Sabdar@Sun.COM 	path_list_t *p = pl_head;
2197917SReza.Sabdar@Sun.COM 
2207917SReza.Sabdar@Sun.COM 	while (p) {
2217917SReza.Sabdar@Sun.COM 		p = pl_head->pl_next;
2227917SReza.Sabdar@Sun.COM 		free(pl_head->pl_path);
2237917SReza.Sabdar@Sun.COM 		free(pl_head);
2247917SReza.Sabdar@Sun.COM 		pl_head = p;
2257917SReza.Sabdar@Sun.COM 	}
2267917SReza.Sabdar@Sun.COM }
2277917SReza.Sabdar@Sun.COM 
2287917SReza.Sabdar@Sun.COM /*
2297917SReza.Sabdar@Sun.COM  * Add a path in the list of path names
2307917SReza.Sabdar@Sun.COM  */
2317917SReza.Sabdar@Sun.COM char *
2327917SReza.Sabdar@Sun.COM fs_add_pathlist(char *path, path_list_t **pp)
2337917SReza.Sabdar@Sun.COM {
2347917SReza.Sabdar@Sun.COM 	char *tpath;
2357917SReza.Sabdar@Sun.COM 
2367917SReza.Sabdar@Sun.COM 	if (*pp) {
2377917SReza.Sabdar@Sun.COM 		(*pp)->pl_path = strdup(path);
2387917SReza.Sabdar@Sun.COM 		if ((*pp)->pl_path == NULL)
2397917SReza.Sabdar@Sun.COM 			return (NULL);
2407917SReza.Sabdar@Sun.COM 		tpath = (*pp)->pl_path;
2417917SReza.Sabdar@Sun.COM 		(*pp)->pl_next = ndmp_malloc(sizeof (path_list_t));
2427917SReza.Sabdar@Sun.COM 		if ((*pp)->pl_next == NULL)
2437917SReza.Sabdar@Sun.COM 			return (NULL);
2447917SReza.Sabdar@Sun.COM 		*pp = (*pp)->pl_next;
2457917SReza.Sabdar@Sun.COM 		(*pp)->pl_path = NULL;
2467917SReza.Sabdar@Sun.COM 		(*pp)->pl_next = NULL;
2477917SReza.Sabdar@Sun.COM 		return (tpath);
2487917SReza.Sabdar@Sun.COM 	}
2497917SReza.Sabdar@Sun.COM 	return (NULL);
2507917SReza.Sabdar@Sun.COM }
2517917SReza.Sabdar@Sun.COM 
2527917SReza.Sabdar@Sun.COM /*
2537917SReza.Sabdar@Sun.COM  * Create a file handle and get stats for the given path
2547917SReza.Sabdar@Sun.COM  */
2557917SReza.Sabdar@Sun.COM int
2567917SReza.Sabdar@Sun.COM fs_getstat(char *path, fs_fhandle_t *fh, struct stat64 *st, path_list_t **pl)
2577917SReza.Sabdar@Sun.COM {
2587917SReza.Sabdar@Sun.COM 	if (lstat64(path, st) == -1)
2597917SReza.Sabdar@Sun.COM 		return (errno);
2607917SReza.Sabdar@Sun.COM 
2617917SReza.Sabdar@Sun.COM 	fh->fh_fid = st->st_ino;
262*9012SReza.Sabdar@Sun.COM 
263*9012SReza.Sabdar@Sun.COM 	if (!S_ISDIR(st->st_mode)) {
264*9012SReza.Sabdar@Sun.COM 		fh->fh_fpath = NULL;
265*9012SReza.Sabdar@Sun.COM 		return (0);
266*9012SReza.Sabdar@Sun.COM 	}
267*9012SReza.Sabdar@Sun.COM 
2687917SReza.Sabdar@Sun.COM 	if (pl)
2697917SReza.Sabdar@Sun.COM 		fh->fh_fpath = fs_add_pathlist(path, pl);
2707917SReza.Sabdar@Sun.COM 	else
2717917SReza.Sabdar@Sun.COM 		fh->fh_fpath = strdup(path);
2727917SReza.Sabdar@Sun.COM 	return (0);
2737917SReza.Sabdar@Sun.COM }
2747917SReza.Sabdar@Sun.COM 
2757917SReza.Sabdar@Sun.COM /*
2767917SReza.Sabdar@Sun.COM  * Get directory entries info and return in the buffer. Cookie
2777917SReza.Sabdar@Sun.COM  * will keep the state of each call
2787917SReza.Sabdar@Sun.COM  */
2797917SReza.Sabdar@Sun.COM static int
2807917SReza.Sabdar@Sun.COM fs_getdents(int fildes, struct dirent *buf, size_t *nbyte,
2817917SReza.Sabdar@Sun.COM     char *pn_path, long *dpos, longlong_t *cookie,
2827917SReza.Sabdar@Sun.COM     long *n_entries, dent_arg_t *darg, path_list_t **pl)
2837917SReza.Sabdar@Sun.COM {
2847917SReza.Sabdar@Sun.COM 	struct dirent *ptr;
2857917SReza.Sabdar@Sun.COM 	char file_path[PATH_MAX + 1];
2867917SReza.Sabdar@Sun.COM 	fs_fhandle_t fh;
2877917SReza.Sabdar@Sun.COM 	struct stat64 st;
2887917SReza.Sabdar@Sun.COM 	char *p;
2897917SReza.Sabdar@Sun.COM 	int len;
2907917SReza.Sabdar@Sun.COM 	int rv;
2917917SReza.Sabdar@Sun.COM 
2927917SReza.Sabdar@Sun.COM 	if (*nbyte == 0) {
2937917SReza.Sabdar@Sun.COM 		(void) memset((char *)buf, 0, MAX_DENT_BUF_SIZE);
2947917SReza.Sabdar@Sun.COM 		*nbyte = rv = getdents(fildes, buf, darg->da_size);
2957917SReza.Sabdar@Sun.COM 		*cookie = 0LL;
2967917SReza.Sabdar@Sun.COM 
2977917SReza.Sabdar@Sun.COM 		if (rv <= 0)
2987917SReza.Sabdar@Sun.COM 			return (rv);
2997917SReza.Sabdar@Sun.COM 	}
3007917SReza.Sabdar@Sun.COM 
3017917SReza.Sabdar@Sun.COM 	p = (char *)buf + *cookie;
3027917SReza.Sabdar@Sun.COM 	len = *nbyte;
3037917SReza.Sabdar@Sun.COM 	do {
3047917SReza.Sabdar@Sun.COM 		/* LINTED improper alignment */
3057917SReza.Sabdar@Sun.COM 		ptr = (struct dirent *)p;
3067917SReza.Sabdar@Sun.COM 		*dpos =  ptr->d_off;
3077917SReza.Sabdar@Sun.COM 		(void) snprintf(file_path, PATH_MAX, "%s/", pn_path);
3087917SReza.Sabdar@Sun.COM 		(void) strlcat(file_path, ptr->d_name, PATH_MAX);
3097917SReza.Sabdar@Sun.COM 		(void) memset(&fh, 0, sizeof (fs_fhandle_t));
3107917SReza.Sabdar@Sun.COM 
3117917SReza.Sabdar@Sun.COM 		rv = fs_getstat(file_path, &fh, &st, pl);
3127917SReza.Sabdar@Sun.COM 		if (rv != 0)
3137917SReza.Sabdar@Sun.COM 			break;
3147917SReza.Sabdar@Sun.COM 
3157917SReza.Sabdar@Sun.COM 		rv = fs_populate_dents(darg, strlen(ptr->d_name),
3167917SReza.Sabdar@Sun.COM 		    (char *)ptr->d_name, n_entries, &st, &fh);
3177917SReza.Sabdar@Sun.COM 
3187917SReza.Sabdar@Sun.COM 		if (rv != 0) {
3197917SReza.Sabdar@Sun.COM 			rv = 0;
3207917SReza.Sabdar@Sun.COM 			break;
3217917SReza.Sabdar@Sun.COM 		}
3227917SReza.Sabdar@Sun.COM 
3237917SReza.Sabdar@Sun.COM 		p = p + ptr->d_reclen;
3247917SReza.Sabdar@Sun.COM 		len -= ptr->d_reclen;
3257917SReza.Sabdar@Sun.COM 	} while (len);
3267917SReza.Sabdar@Sun.COM 
3277917SReza.Sabdar@Sun.COM 	*cookie = (longlong_t)(p - (char *)buf);
3287917SReza.Sabdar@Sun.COM 	*nbyte = len;
3297917SReza.Sabdar@Sun.COM 	return (rv);
3307917SReza.Sabdar@Sun.COM }
3317917SReza.Sabdar@Sun.COM 
3327917SReza.Sabdar@Sun.COM /*
3337917SReza.Sabdar@Sun.COM  * Read the directory entries and return the information about
3347917SReza.Sabdar@Sun.COM  * each entry
3357917SReza.Sabdar@Sun.COM  */
3367917SReza.Sabdar@Sun.COM int
3377917SReza.Sabdar@Sun.COM fs_readdir(fs_fhandle_t *ts_fh, char *path, long *dpos,
3387917SReza.Sabdar@Sun.COM     char *nm, int *el, fs_fhandle_t *efh, struct stat64 *est,
3397917SReza.Sabdar@Sun.COM     path_list_t **pl)
3407917SReza.Sabdar@Sun.COM {
3417917SReza.Sabdar@Sun.COM 	struct dirent *dp;
3427917SReza.Sabdar@Sun.COM 	char  file_path[PATH_MAX + 1];
3437917SReza.Sabdar@Sun.COM 	DIR *dirp;
3447917SReza.Sabdar@Sun.COM 	int rv;
3457917SReza.Sabdar@Sun.COM 
3467917SReza.Sabdar@Sun.COM 	if ((dirp = opendir(ts_fh->fh_fpath)) == NULL)
3477917SReza.Sabdar@Sun.COM 		return (errno);
3487917SReza.Sabdar@Sun.COM 
3497917SReza.Sabdar@Sun.COM 	seekdir(dirp, *dpos);
3507917SReza.Sabdar@Sun.COM 	if ((dp = readdir(dirp)) == NULL) {
3517917SReza.Sabdar@Sun.COM 		rv = 0;  /* skip this dir */
3527917SReza.Sabdar@Sun.COM 		*el = 0;
3537917SReza.Sabdar@Sun.COM 	} else {
3547917SReza.Sabdar@Sun.COM 		(void) snprintf(file_path, PATH_MAX, "%s/", path);
3557917SReza.Sabdar@Sun.COM 		(void) strlcat(file_path, dp->d_name, PATH_MAX);
3567917SReza.Sabdar@Sun.COM 
3577917SReza.Sabdar@Sun.COM 		rv = fs_getstat(file_path, efh, est, pl);
3587917SReza.Sabdar@Sun.COM 		if (rv == 0) {
3597917SReza.Sabdar@Sun.COM 			*dpos = telldir(dirp);
3607917SReza.Sabdar@Sun.COM 			(void) strlcpy(nm, dp->d_name, NAME_MAX);
3617917SReza.Sabdar@Sun.COM 			*el = strlen(dp->d_name);
3627917SReza.Sabdar@Sun.COM 		} else {
3637917SReza.Sabdar@Sun.COM 			*el = 0;
3647917SReza.Sabdar@Sun.COM 		}
3657917SReza.Sabdar@Sun.COM 	}
3667917SReza.Sabdar@Sun.COM 	(void) closedir(dirp);
3677917SReza.Sabdar@Sun.COM 	return (rv);
3687917SReza.Sabdar@Sun.COM }
3697917SReza.Sabdar@Sun.COM 
3707917SReza.Sabdar@Sun.COM /*
3717917SReza.Sabdar@Sun.COM  * Traverse the file system in the post-order way.  The description
3727917SReza.Sabdar@Sun.COM  * and example is in the header file.
3737917SReza.Sabdar@Sun.COM  *
3747917SReza.Sabdar@Sun.COM  * The callback function should return 0, on success and non-zero on
3757917SReza.Sabdar@Sun.COM  * failure.  If the callback function returns non-zero return value,
3767917SReza.Sabdar@Sun.COM  * the traversing stops.
3777917SReza.Sabdar@Sun.COM  */
3787917SReza.Sabdar@Sun.COM int
3797917SReza.Sabdar@Sun.COM traverse_post(struct fs_traverse *ftp)
3807917SReza.Sabdar@Sun.COM {
3817917SReza.Sabdar@Sun.COM 	char path[PATH_MAX + 1]; /* full path name of the current dir */
3827917SReza.Sabdar@Sun.COM 	char nm[NAME_MAX + 1]; /* directory entry name */
3837917SReza.Sabdar@Sun.COM 	char *lp; /* last position on the path */
3847917SReza.Sabdar@Sun.COM 	int next_dir, rv;
3857917SReza.Sabdar@Sun.COM 	int pl, el; /* path and directory entry length */
3867917SReza.Sabdar@Sun.COM 	cstack_t *sp;
3877917SReza.Sabdar@Sun.COM 	fs_fhandle_t pfh, efh;
3887917SReza.Sabdar@Sun.COM 	struct stat64 pst, est;
3897917SReza.Sabdar@Sun.COM 	traverse_state_t *tsp;
3907917SReza.Sabdar@Sun.COM 	struct fst_node pn, en; /* parent and entry nodes */
3917917SReza.Sabdar@Sun.COM 	path_list_t *plhead, *plist;
3927917SReza.Sabdar@Sun.COM 
3937917SReza.Sabdar@Sun.COM 	if (!ftp || !ftp->ft_path || !*ftp->ft_path || !ftp->ft_callbk) {
3947917SReza.Sabdar@Sun.COM 		NDMP_LOG(LOG_DEBUG, "Invalid argument");
3957917SReza.Sabdar@Sun.COM 		errno = EINVAL;
3967917SReza.Sabdar@Sun.COM 		return (-1);
3977917SReza.Sabdar@Sun.COM 	}
3987917SReza.Sabdar@Sun.COM 
3997917SReza.Sabdar@Sun.COM 	/* set the default log function if it's not already set */
4007917SReza.Sabdar@Sun.COM 	if (!ftp->ft_logfp) {
4017917SReza.Sabdar@Sun.COM 		ftp->ft_logfp = (ft_log_t)syslog;
4027917SReza.Sabdar@Sun.COM 		NDMP_LOG(LOG_DEBUG, "Log to system log \"%s\"", ftp->ft_path);
4037917SReza.Sabdar@Sun.COM 	}
4047917SReza.Sabdar@Sun.COM 
4057917SReza.Sabdar@Sun.COM 	/* set the logical path to physical path if it's not already set */
4067917SReza.Sabdar@Sun.COM 	if (!ftp->ft_lpath) {
4077917SReza.Sabdar@Sun.COM 		NDMP_LOG(LOG_DEBUG,
4087917SReza.Sabdar@Sun.COM 		    "report the same paths: \"%s\"", ftp->ft_path);
4097917SReza.Sabdar@Sun.COM 		ftp->ft_lpath = ftp->ft_path;
4107917SReza.Sabdar@Sun.COM 	}
4117917SReza.Sabdar@Sun.COM 
4127917SReza.Sabdar@Sun.COM 	pl = strlen(ftp->ft_lpath);
4137917SReza.Sabdar@Sun.COM 	if (pl + 1 > PATH_MAX) { /* +1 for the '/' */
4147917SReza.Sabdar@Sun.COM 		NDMP_LOG(LOG_DEBUG, "lpath too long \"%s\"", ftp->ft_path);
4157917SReza.Sabdar@Sun.COM 		errno = ENAMETOOLONG;
4167917SReza.Sabdar@Sun.COM 		return (-1);
4177917SReza.Sabdar@Sun.COM 	}
4187917SReza.Sabdar@Sun.COM 	(void) strcpy(path, ftp->ft_lpath);
4197917SReza.Sabdar@Sun.COM 	(void) memset(&pfh, 0, sizeof (pfh));
4207917SReza.Sabdar@Sun.COM 	rv = fs_getstat(ftp->ft_lpath, &pfh, &pst, NULL);
4217917SReza.Sabdar@Sun.COM 
4227917SReza.Sabdar@Sun.COM 	if (rv != 0) {
4237917SReza.Sabdar@Sun.COM 		NDMP_LOG(LOG_DEBUG,
4247917SReza.Sabdar@Sun.COM 		    "Error %d on fs_getstat(%s)", rv, ftp->ft_path);
4257917SReza.Sabdar@Sun.COM 		return (rv);
4267917SReza.Sabdar@Sun.COM 	}
4277917SReza.Sabdar@Sun.COM 
4287917SReza.Sabdar@Sun.COM 	if (!S_ISDIR(pst.st_mode)) {
4297917SReza.Sabdar@Sun.COM 		pn.tn_path = ftp->ft_lpath;
4307917SReza.Sabdar@Sun.COM 		pn.tn_fh = &pfh;
4317917SReza.Sabdar@Sun.COM 		pn.tn_st = &pst;
4327917SReza.Sabdar@Sun.COM 		en.tn_path = NULL;
4337917SReza.Sabdar@Sun.COM 		en.tn_fh = NULL;
4347917SReza.Sabdar@Sun.COM 		en.tn_st = NULL;
4357917SReza.Sabdar@Sun.COM 		rv = CALLBACK(&pn, &en);
4367917SReza.Sabdar@Sun.COM 		if (VERBOSE(ftp))
4377917SReza.Sabdar@Sun.COM 			NDMP_LOG(LOG_DEBUG, "CALLBACK(%s): %d", pn.tn_path, rv);
4387917SReza.Sabdar@Sun.COM 		free(pfh.fh_fpath);
4397917SReza.Sabdar@Sun.COM 		return (rv);
4407917SReza.Sabdar@Sun.COM 	}
4417917SReza.Sabdar@Sun.COM 
4427917SReza.Sabdar@Sun.COM 	sp = cstack_new();
4437917SReza.Sabdar@Sun.COM 	if (!sp) {
4447917SReza.Sabdar@Sun.COM 		errno = ENOMEM;
4457917SReza.Sabdar@Sun.COM 		free(pfh.fh_fpath);
4467917SReza.Sabdar@Sun.COM 		return (-1);
4477917SReza.Sabdar@Sun.COM 	}
4487917SReza.Sabdar@Sun.COM 	tsp = new_tsp(path);
4497917SReza.Sabdar@Sun.COM 	if (!tsp) {
4507917SReza.Sabdar@Sun.COM 		cstack_delete(sp);
4517917SReza.Sabdar@Sun.COM 		errno = ENOMEM;
4527917SReza.Sabdar@Sun.COM 		free(pfh.fh_fpath);
4537917SReza.Sabdar@Sun.COM 		return (-1);
4547917SReza.Sabdar@Sun.COM 	}
4557917SReza.Sabdar@Sun.COM 	tsp->ts_ent = tsp->ts_end;
4567917SReza.Sabdar@Sun.COM 	tsp->ts_fh = pfh;
4577917SReza.Sabdar@Sun.COM 	tsp->ts_st = pst;
4587917SReza.Sabdar@Sun.COM 	pn.tn_path = path;
4597917SReza.Sabdar@Sun.COM 	pn.tn_fh = &tsp->ts_fh;
4607917SReza.Sabdar@Sun.COM 	pn.tn_st = &tsp->ts_st;
4617917SReza.Sabdar@Sun.COM 
4627917SReza.Sabdar@Sun.COM 	if ((plist = fs_init_pathlist()) == NULL) {
4637917SReza.Sabdar@Sun.COM 		errno = ENOMEM;
4647917SReza.Sabdar@Sun.COM 		free(pfh.fh_fpath);
4657917SReza.Sabdar@Sun.COM 		return (-1);
4667917SReza.Sabdar@Sun.COM 	}
4677917SReza.Sabdar@Sun.COM 	plhead = plist;
4687917SReza.Sabdar@Sun.COM 
4697917SReza.Sabdar@Sun.COM 	rv = 0;
4707917SReza.Sabdar@Sun.COM 	next_dir = 1;
4717917SReza.Sabdar@Sun.COM 	do {
4727917SReza.Sabdar@Sun.COM 		if (next_dir) {
4737917SReza.Sabdar@Sun.COM 			traverse_stats.fss_newdirs++;
4747917SReza.Sabdar@Sun.COM 
4757917SReza.Sabdar@Sun.COM 			*tsp->ts_end = '\0';
4767917SReza.Sabdar@Sun.COM 			if (VERBOSE(ftp))
4777917SReza.Sabdar@Sun.COM 				NDMP_LOG(LOG_DEBUG, "pl %d \"%s\"", pl, path);
4787917SReza.Sabdar@Sun.COM 		}
4797917SReza.Sabdar@Sun.COM 
4807917SReza.Sabdar@Sun.COM 		next_dir = 0;
4817917SReza.Sabdar@Sun.COM 		do {
4827917SReza.Sabdar@Sun.COM 			el = NAME_MAX;
4837917SReza.Sabdar@Sun.COM 			rv = fs_readdir(&tsp->ts_fh, pn.tn_path,
4847917SReza.Sabdar@Sun.COM 			    &tsp->ts_dpos, nm, &el,
4857917SReza.Sabdar@Sun.COM 			    &efh, &est, &plist);
4867917SReza.Sabdar@Sun.COM 
4877917SReza.Sabdar@Sun.COM 			if (rv != 0) {
4887917SReza.Sabdar@Sun.COM 				efh.fh_fpath = NULL;
4897917SReza.Sabdar@Sun.COM 				traverse_stats.fss_readdir_err++;
4907917SReza.Sabdar@Sun.COM 
4917917SReza.Sabdar@Sun.COM 				NDMP_LOG(LOG_DEBUG,
4927917SReza.Sabdar@Sun.COM 				    "Error %d on readdir(%s) pos %d",
4937917SReza.Sabdar@Sun.COM 				    rv, path, tsp->ts_dpos);
4947917SReza.Sabdar@Sun.COM 				if (STOP_ONERR(ftp))
4957917SReza.Sabdar@Sun.COM 					break;
4967917SReza.Sabdar@Sun.COM 				rv = SKIP_ENTRY;
4977917SReza.Sabdar@Sun.COM 
4987917SReza.Sabdar@Sun.COM 				continue;
4997917SReza.Sabdar@Sun.COM 			}
5007917SReza.Sabdar@Sun.COM 
5017917SReza.Sabdar@Sun.COM 			/* done with this directory */
5027917SReza.Sabdar@Sun.COM 			if (el == 0) {
5037917SReza.Sabdar@Sun.COM 				if (VERBOSE(ftp))
5047917SReza.Sabdar@Sun.COM 					NDMP_LOG(LOG_DEBUG,
5057917SReza.Sabdar@Sun.COM 					    "Done(%s)", pn.tn_path);
5067917SReza.Sabdar@Sun.COM 				break;
5077917SReza.Sabdar@Sun.COM 			}
5087917SReza.Sabdar@Sun.COM 			nm[el] = '\0';
5097917SReza.Sabdar@Sun.COM 
5107917SReza.Sabdar@Sun.COM 			if (rootfs_dot_or_dotdot(nm)) {
5117917SReza.Sabdar@Sun.COM 				efh.fh_fpath = NULL;
5127917SReza.Sabdar@Sun.COM 				continue;
5137917SReza.Sabdar@Sun.COM 			}
5147917SReza.Sabdar@Sun.COM 
5157917SReza.Sabdar@Sun.COM 			if (VERBOSE(ftp))
5167917SReza.Sabdar@Sun.COM 				NDMP_LOG(LOG_DEBUG, "%u dname: \"%s\"",
5177917SReza.Sabdar@Sun.COM 				    tsp->ts_dpos, nm);
5187917SReza.Sabdar@Sun.COM 
5197917SReza.Sabdar@Sun.COM 			if (pl + 1 + el > PATH_MAX) {
5207917SReza.Sabdar@Sun.COM 				traverse_stats.fss_longpath_err++;
5217917SReza.Sabdar@Sun.COM 
5227917SReza.Sabdar@Sun.COM 				NDMP_LOG(LOG_ERR, "Path %s/%s is too long.",
5237917SReza.Sabdar@Sun.COM 				    path, nm);
5247917SReza.Sabdar@Sun.COM 				if (STOP_ONLONG(ftp))
5257917SReza.Sabdar@Sun.COM 					rv = ENAMETOOLONG;
5267917SReza.Sabdar@Sun.COM 				efh.fh_fpath = NULL;
5277917SReza.Sabdar@Sun.COM 				continue;
5287917SReza.Sabdar@Sun.COM 			}
5297917SReza.Sabdar@Sun.COM 
5307917SReza.Sabdar@Sun.COM 			/*
5317917SReza.Sabdar@Sun.COM 			 * Push the current directory on to the stack and
5327917SReza.Sabdar@Sun.COM 			 * dive into the entry found.
5337917SReza.Sabdar@Sun.COM 			 */
5347917SReza.Sabdar@Sun.COM 			if (S_ISDIR(est.st_mode)) {
5357917SReza.Sabdar@Sun.COM 
5367917SReza.Sabdar@Sun.COM 				assert(tsp != NULL);
5377917SReza.Sabdar@Sun.COM 				if (cstack_push(sp, tsp, 0)) {
5387917SReza.Sabdar@Sun.COM 					rv = ENOMEM;
5397917SReza.Sabdar@Sun.COM 					efh.fh_fpath = NULL;
5407917SReza.Sabdar@Sun.COM 					break;
5417917SReza.Sabdar@Sun.COM 				}
5427917SReza.Sabdar@Sun.COM 				traverse_stats.fss_pushes++;
5437917SReza.Sabdar@Sun.COM 
5447917SReza.Sabdar@Sun.COM 				/*
5457917SReza.Sabdar@Sun.COM 				 * Concatenate the current entry with the
5467917SReza.Sabdar@Sun.COM 				 * current path.  This will be the path of
5477917SReza.Sabdar@Sun.COM 				 * the new directory to be scanned.
5487917SReza.Sabdar@Sun.COM 				 *
5497917SReza.Sabdar@Sun.COM 				 * Note:
5507917SReza.Sabdar@Sun.COM 				 * sprintf(tsp->ts_end, "/%s", de->d_name);
5517917SReza.Sabdar@Sun.COM 				 * could be used here, but concatenating
5527917SReza.Sabdar@Sun.COM 				 * strings like this might be faster.
5537917SReza.Sabdar@Sun.COM 				 * The length of the new path has been
5547917SReza.Sabdar@Sun.COM 				 * checked above.  So strcpy() can be
5557917SReza.Sabdar@Sun.COM 				 * safe and should not lead to a buffer
5567917SReza.Sabdar@Sun.COM 				 * over-run.
5577917SReza.Sabdar@Sun.COM 				 */
5587917SReza.Sabdar@Sun.COM 				lp = tsp->ts_end;
5597917SReza.Sabdar@Sun.COM 				*tsp->ts_end = '/';
5607917SReza.Sabdar@Sun.COM 				(void) strcpy(tsp->ts_end + 1, nm);
5617917SReza.Sabdar@Sun.COM 
5627917SReza.Sabdar@Sun.COM 				tsp = new_tsp(path);
5637917SReza.Sabdar@Sun.COM 				if (!tsp) {
5647917SReza.Sabdar@Sun.COM 					efh.fh_fpath = NULL;
5657917SReza.Sabdar@Sun.COM 					rv = ENOMEM;
5667917SReza.Sabdar@Sun.COM 				} else {
5677917SReza.Sabdar@Sun.COM 					next_dir = 1;
5687917SReza.Sabdar@Sun.COM 					pl += el;
5697917SReza.Sabdar@Sun.COM 					tsp->ts_fh = efh;
5707917SReza.Sabdar@Sun.COM 					tsp->ts_st = est;
5717917SReza.Sabdar@Sun.COM 					tsp->ts_ent = lp;
5727917SReza.Sabdar@Sun.COM 					pn.tn_fh = &tsp->ts_fh;
5737917SReza.Sabdar@Sun.COM 					pn.tn_st = &tsp->ts_st;
5747917SReza.Sabdar@Sun.COM 				}
5757917SReza.Sabdar@Sun.COM 				break;
5767917SReza.Sabdar@Sun.COM 			} else {
5777917SReza.Sabdar@Sun.COM 				/*
5787917SReza.Sabdar@Sun.COM 				 * The entry is not a directory so the
5797917SReza.Sabdar@Sun.COM 				 * callback function must be called.
5807917SReza.Sabdar@Sun.COM 				 */
5817917SReza.Sabdar@Sun.COM 				traverse_stats.fss_nondir_calls++;
5827917SReza.Sabdar@Sun.COM 
5837917SReza.Sabdar@Sun.COM 				en.tn_path = nm;
5847917SReza.Sabdar@Sun.COM 				en.tn_fh = &efh;
5857917SReza.Sabdar@Sun.COM 				en.tn_st = &est;
5867917SReza.Sabdar@Sun.COM 				rv = CALLBACK(&pn, &en);
5877917SReza.Sabdar@Sun.COM 				efh.fh_fpath = NULL;
5887917SReza.Sabdar@Sun.COM 				if (VERBOSE(ftp))
5897917SReza.Sabdar@Sun.COM 					NDMP_LOG(LOG_DEBUG,
5907917SReza.Sabdar@Sun.COM 					    "CALLBACK(%s/%s): %d",
5917917SReza.Sabdar@Sun.COM 					    pn.tn_path, en.tn_path, rv);
5927917SReza.Sabdar@Sun.COM 
5937917SReza.Sabdar@Sun.COM 				if (rv != 0)
5947917SReza.Sabdar@Sun.COM 					break;
5957917SReza.Sabdar@Sun.COM 			}
5967917SReza.Sabdar@Sun.COM 		} while (rv == 0);
5977917SReza.Sabdar@Sun.COM 
5987917SReza.Sabdar@Sun.COM 		/*
5997917SReza.Sabdar@Sun.COM 		 * A new directory must be processed, go to the start of
6007917SReza.Sabdar@Sun.COM 		 * the loop, open it and process it.
6017917SReza.Sabdar@Sun.COM 		 */
6027917SReza.Sabdar@Sun.COM 		if (next_dir)
6037917SReza.Sabdar@Sun.COM 			continue;
6047917SReza.Sabdar@Sun.COM 
6057917SReza.Sabdar@Sun.COM 		if (rv == SKIP_ENTRY)
6067917SReza.Sabdar@Sun.COM 			rv = 0; /* We should skip the current directory */
6077917SReza.Sabdar@Sun.COM 
6087917SReza.Sabdar@Sun.COM 		if (rv == 0) {
6097917SReza.Sabdar@Sun.COM 			/*
6107917SReza.Sabdar@Sun.COM 			 * Remove the ent from the end of path and send it
6117917SReza.Sabdar@Sun.COM 			 * as an entry of the path.
6127917SReza.Sabdar@Sun.COM 			 */
6137917SReza.Sabdar@Sun.COM 			lp = tsp->ts_ent;
6147917SReza.Sabdar@Sun.COM 			*lp = '\0';
6157917SReza.Sabdar@Sun.COM 			efh = tsp->ts_fh;
6167917SReza.Sabdar@Sun.COM 			est = tsp->ts_st;
6177917SReza.Sabdar@Sun.COM 			free(tsp);
6187917SReza.Sabdar@Sun.COM 			if (cstack_pop(sp, (void **)&tsp, (int *)NULL))
6197917SReza.Sabdar@Sun.COM 				break;
6207917SReza.Sabdar@Sun.COM 
6217917SReza.Sabdar@Sun.COM 			assert(tsp != NULL);
6227917SReza.Sabdar@Sun.COM 			pl = tsp->ts_end - path;
6237917SReza.Sabdar@Sun.COM 
6247917SReza.Sabdar@Sun.COM 			if (VERBOSE(ftp))
6257917SReza.Sabdar@Sun.COM 				NDMP_LOG(LOG_DEBUG, "poped pl %d 0x%p \"%s\"",
6267917SReza.Sabdar@Sun.COM 				    pl, tsp, path);
6277917SReza.Sabdar@Sun.COM 
6287917SReza.Sabdar@Sun.COM 			traverse_stats.fss_pops++;
6297917SReza.Sabdar@Sun.COM 			traverse_stats.fss_dir_calls++;
6307917SReza.Sabdar@Sun.COM 
6317917SReza.Sabdar@Sun.COM 			pn.tn_fh = &tsp->ts_fh;
6327917SReza.Sabdar@Sun.COM 			pn.tn_st = &tsp->ts_st;
6337917SReza.Sabdar@Sun.COM 			en.tn_path = lp + 1;
6347917SReza.Sabdar@Sun.COM 			en.tn_fh = &efh;
6357917SReza.Sabdar@Sun.COM 			en.tn_st = &est;
6367917SReza.Sabdar@Sun.COM 
6377917SReza.Sabdar@Sun.COM 			rv = CALLBACK(&pn, &en);
6387917SReza.Sabdar@Sun.COM 			efh.fh_fpath = NULL;
6397917SReza.Sabdar@Sun.COM 			if (VERBOSE(ftp))
6407917SReza.Sabdar@Sun.COM 				NDMP_LOG(LOG_DEBUG, "CALLBACK(%s/%s): %d",
6417917SReza.Sabdar@Sun.COM 				    pn.tn_path, en.tn_path, rv);
6427917SReza.Sabdar@Sun.COM 			/*
6437917SReza.Sabdar@Sun.COM 			 * Does not need to free tsp here.  It will be released
6447917SReza.Sabdar@Sun.COM 			 * later.
6457917SReza.Sabdar@Sun.COM 			 */
6467917SReza.Sabdar@Sun.COM 		}
6477917SReza.Sabdar@Sun.COM 
6487917SReza.Sabdar@Sun.COM 		if (rv != 0 && tsp)
6497917SReza.Sabdar@Sun.COM 			free(tsp);
6507917SReza.Sabdar@Sun.COM 
6517917SReza.Sabdar@Sun.COM 	} while (rv == 0);
6527917SReza.Sabdar@Sun.COM 
6537917SReza.Sabdar@Sun.COM 	/*
6547917SReza.Sabdar@Sun.COM 	 * For the 'ftp->ft_path' directory itself.
6557917SReza.Sabdar@Sun.COM 	 */
6567917SReza.Sabdar@Sun.COM 	if (rv == 0) {
6577917SReza.Sabdar@Sun.COM 		traverse_stats.fss_dir_calls++;
6587917SReza.Sabdar@Sun.COM 
6597917SReza.Sabdar@Sun.COM 		pn.tn_fh = &efh;
6607917SReza.Sabdar@Sun.COM 		pn.tn_st = &est;
6617917SReza.Sabdar@Sun.COM 		en.tn_path = NULL;
6627917SReza.Sabdar@Sun.COM 		en.tn_fh = NULL;
6637917SReza.Sabdar@Sun.COM 		en.tn_st = NULL;
6647917SReza.Sabdar@Sun.COM 		rv = CALLBACK(&pn, &en);
6657917SReza.Sabdar@Sun.COM 		if (VERBOSE(ftp))
6667917SReza.Sabdar@Sun.COM 			NDMP_LOG(LOG_DEBUG, "CALLBACK(%s): %d", pn.tn_path, rv);
6677917SReza.Sabdar@Sun.COM 	}
6687917SReza.Sabdar@Sun.COM 
6697917SReza.Sabdar@Sun.COM 	/*
6707917SReza.Sabdar@Sun.COM 	 * Pop and free all the remaining entries on the stack.
6717917SReza.Sabdar@Sun.COM 	 */
6727917SReza.Sabdar@Sun.COM 	while (!cstack_pop(sp, (void **)&tsp, (int *)NULL)) {
6737917SReza.Sabdar@Sun.COM 		traverse_stats.fss_stack_residue++;
6747917SReza.Sabdar@Sun.COM 
6757917SReza.Sabdar@Sun.COM 		free(tsp);
6767917SReza.Sabdar@Sun.COM 	}
6777917SReza.Sabdar@Sun.COM 
6787917SReza.Sabdar@Sun.COM 	fs_free_pathlist(plhead);
6797917SReza.Sabdar@Sun.COM 	free(pfh.fh_fpath);
6807917SReza.Sabdar@Sun.COM 	cstack_delete(sp);
6817917SReza.Sabdar@Sun.COM 	return (rv);
6827917SReza.Sabdar@Sun.COM }
6837917SReza.Sabdar@Sun.COM 
6847917SReza.Sabdar@Sun.COM /*
6857917SReza.Sabdar@Sun.COM  * In one pass, read all the directory entries of the specified
6867917SReza.Sabdar@Sun.COM  * directory and call the callback function for non-directory
6877917SReza.Sabdar@Sun.COM  * entries.
6887917SReza.Sabdar@Sun.COM  *
6897917SReza.Sabdar@Sun.COM  * On return:
6907917SReza.Sabdar@Sun.COM  *    0: Lets the directory to be scanned for directory entries.
6917917SReza.Sabdar@Sun.COM  *    < 0: Completely stops traversing.
6927917SReza.Sabdar@Sun.COM  *    FST_SKIP: stops further scanning of the directory.  Traversing
6937917SReza.Sabdar@Sun.COM  *        will continue with the next directory in the hierarchy.
6947917SReza.Sabdar@Sun.COM  *    SKIP_ENTRY: Failed to get the directory entries, so the caller
6957917SReza.Sabdar@Sun.COM  *	  should skip this entry.
6967917SReza.Sabdar@Sun.COM  */
6977917SReza.Sabdar@Sun.COM static int
6987917SReza.Sabdar@Sun.COM traverse_level_nondir(struct fs_traverse *ftp,
6997917SReza.Sabdar@Sun.COM     traverse_state_t *tsp, struct fst_node *pnp, dent_arg_t *darg)
7007917SReza.Sabdar@Sun.COM {
7017917SReza.Sabdar@Sun.COM 	int pl; /* patth length */
7027917SReza.Sabdar@Sun.COM 	int rv;
7037917SReza.Sabdar@Sun.COM 	struct fst_node en; /* entry node */
7047917SReza.Sabdar@Sun.COM 	longlong_t cookie_verf;
7057917SReza.Sabdar@Sun.COM 	fs_dent_info_t *dent;
7067917SReza.Sabdar@Sun.COM 	struct dirent *buf;
7077917SReza.Sabdar@Sun.COM 	size_t len = 0;
7087917SReza.Sabdar@Sun.COM 	path_list_t *plhead, *plist;
7097917SReza.Sabdar@Sun.COM 	int fd;
7107917SReza.Sabdar@Sun.COM 
7117917SReza.Sabdar@Sun.COM 	rv = 0;
7127917SReza.Sabdar@Sun.COM 	pl = strlen(pnp->tn_path);
7137917SReza.Sabdar@Sun.COM 
7147917SReza.Sabdar@Sun.COM 	buf = ndmp_malloc(MAX_DENT_BUF_SIZE);
7157917SReza.Sabdar@Sun.COM 	if (buf == NULL)
7167917SReza.Sabdar@Sun.COM 		return (errno);
7177917SReza.Sabdar@Sun.COM 
7187917SReza.Sabdar@Sun.COM 	fd = open(tsp->ts_fh.fh_fpath, O_RDONLY);
7197917SReza.Sabdar@Sun.COM 	if (fd == -1) {
7207917SReza.Sabdar@Sun.COM 		free(buf);
7217917SReza.Sabdar@Sun.COM 		return (errno);
7227917SReza.Sabdar@Sun.COM 	}
7237917SReza.Sabdar@Sun.COM 	if ((plist = fs_init_pathlist()) == NULL) {
7247917SReza.Sabdar@Sun.COM 		free(buf);
7257917SReza.Sabdar@Sun.COM 		(void) close(fd);
7267917SReza.Sabdar@Sun.COM 		return (errno);
7277917SReza.Sabdar@Sun.COM 	}
7287917SReza.Sabdar@Sun.COM 	plhead = plist;
7297917SReza.Sabdar@Sun.COM 
7307917SReza.Sabdar@Sun.COM 	while (rv == 0) {
7317917SReza.Sabdar@Sun.COM 		long i, n_entries;
7327917SReza.Sabdar@Sun.COM 
7337917SReza.Sabdar@Sun.COM 		darg->da_end = 0;
7347917SReza.Sabdar@Sun.COM 		n_entries = 0;
7357917SReza.Sabdar@Sun.COM 		rv = fs_getdents(fd, buf, &len, pnp->tn_path, &tsp->ts_dpos,
7367917SReza.Sabdar@Sun.COM 		    &cookie_verf, &n_entries, darg, &plist);
7377917SReza.Sabdar@Sun.COM 		if (n_entries == 0)
7387917SReza.Sabdar@Sun.COM 			break;
7397917SReza.Sabdar@Sun.COM 		if (rv != 0) {
7407917SReza.Sabdar@Sun.COM 			traverse_stats.fss_readdir_err++;
7417917SReza.Sabdar@Sun.COM 
7427917SReza.Sabdar@Sun.COM 			NDMP_LOG(LOG_DEBUG, "Error %d on readdir(%s) pos %d",
7437917SReza.Sabdar@Sun.COM 			    rv, pnp->tn_path, tsp->ts_dpos);
7447917SReza.Sabdar@Sun.COM 			if (STOP_ONERR(ftp)) {
7457917SReza.Sabdar@Sun.COM 				NEGATE(rv);
7467917SReza.Sabdar@Sun.COM 				break;
7477917SReza.Sabdar@Sun.COM 			}
7487917SReza.Sabdar@Sun.COM 			/*
7497917SReza.Sabdar@Sun.COM 			 * We cannot read the directory entry, we should
7507917SReza.Sabdar@Sun.COM 			 * skip to the next directory.
7517917SReza.Sabdar@Sun.COM 			 */
7527917SReza.Sabdar@Sun.COM 			rv = SKIP_ENTRY;
7537917SReza.Sabdar@Sun.COM 			continue;
7547917SReza.Sabdar@Sun.COM 		}
7557917SReza.Sabdar@Sun.COM 
7567917SReza.Sabdar@Sun.COM 		/* LINTED imporper alignment */
7577917SReza.Sabdar@Sun.COM 		dent = (fs_dent_info_t *)darg->da_buf;
7587917SReza.Sabdar@Sun.COM 		/* LINTED imporper alignment */
7597917SReza.Sabdar@Sun.COM 		for (i = 0; i < n_entries; i++, dent = (fs_dent_info_t *)
7607917SReza.Sabdar@Sun.COM 		    ((char *)dent + dent->fd_len)) {
7617917SReza.Sabdar@Sun.COM 
7627917SReza.Sabdar@Sun.COM 			if (rootfs_dot_or_dotdot(dent->fd_name)) {
7637917SReza.Sabdar@Sun.COM 				dent->fd_fh.fh_fpath = NULL;
7647917SReza.Sabdar@Sun.COM 				continue;
7657917SReza.Sabdar@Sun.COM 			}
7667917SReza.Sabdar@Sun.COM 
7677917SReza.Sabdar@Sun.COM 			if (VERBOSE(ftp))
7687917SReza.Sabdar@Sun.COM 				NDMP_LOG(LOG_DEBUG, "i %u dname: \"%s\"",
7697917SReza.Sabdar@Sun.COM 				    dent->fd_fh.fh_fid, dent->fd_name);
7707917SReza.Sabdar@Sun.COM 
7717917SReza.Sabdar@Sun.COM 			if ((pl + strlen(dent->fd_name)) > PATH_MAX) {
7727917SReza.Sabdar@Sun.COM 				traverse_stats.fss_longpath_err++;
7737917SReza.Sabdar@Sun.COM 
7747917SReza.Sabdar@Sun.COM 				NDMP_LOG(LOG_ERR, "Path %s/%s is too long.",
7757917SReza.Sabdar@Sun.COM 				    pnp->tn_path, dent->fd_name);
7767917SReza.Sabdar@Sun.COM 				if (STOP_ONLONG(ftp))
7777917SReza.Sabdar@Sun.COM 					rv = -ENAMETOOLONG;
7787917SReza.Sabdar@Sun.COM 				continue;
7797917SReza.Sabdar@Sun.COM 			}
7807917SReza.Sabdar@Sun.COM 
7817917SReza.Sabdar@Sun.COM 			/*
7827917SReza.Sabdar@Sun.COM 			 * The entry is not a directory so the callback
7837917SReza.Sabdar@Sun.COM 			 * function must be called.
7847917SReza.Sabdar@Sun.COM 			 */
7857917SReza.Sabdar@Sun.COM 			if (!S_ISDIR(dent->fd_attr.st_mode)) {
7867917SReza.Sabdar@Sun.COM 				traverse_stats.fss_nondir_calls++;
7877917SReza.Sabdar@Sun.COM 
7887917SReza.Sabdar@Sun.COM 				en.tn_path = dent->fd_name;
7897917SReza.Sabdar@Sun.COM 				en.tn_fh = &dent->fd_fh;
7907917SReza.Sabdar@Sun.COM 				en.tn_st = &dent->fd_attr;
7917917SReza.Sabdar@Sun.COM 				rv = CALLBACK(pnp, &en);
7927917SReza.Sabdar@Sun.COM 				dent->fd_fh.fh_fpath = NULL;
7937917SReza.Sabdar@Sun.COM 				if (rv < 0)
7947917SReza.Sabdar@Sun.COM 					break;
7957917SReza.Sabdar@Sun.COM 				if (rv == FST_SKIP) {
7967917SReza.Sabdar@Sun.COM 					traverse_stats.fss_nondir_skipped++;
7977917SReza.Sabdar@Sun.COM 					break;
7987917SReza.Sabdar@Sun.COM 				}
7997917SReza.Sabdar@Sun.COM 			} else  {
8007917SReza.Sabdar@Sun.COM 				dent->fd_fh.fh_fpath = NULL;
8017917SReza.Sabdar@Sun.COM 			}
8027917SReza.Sabdar@Sun.COM 		}
8037917SReza.Sabdar@Sun.COM 	}
8047917SReza.Sabdar@Sun.COM 
8057917SReza.Sabdar@Sun.COM 	fs_free_pathlist(plhead);
8067917SReza.Sabdar@Sun.COM 	free(buf);
8077917SReza.Sabdar@Sun.COM 	(void) close(fd);
8087917SReza.Sabdar@Sun.COM 	return (rv);
8097917SReza.Sabdar@Sun.COM }
8107917SReza.Sabdar@Sun.COM 
8117917SReza.Sabdar@Sun.COM /*
8127917SReza.Sabdar@Sun.COM  * Traverse the file system in the level-order way.  The description
8137917SReza.Sabdar@Sun.COM  * and example is in the header file.
8147917SReza.Sabdar@Sun.COM  */
8157917SReza.Sabdar@Sun.COM int
8167917SReza.Sabdar@Sun.COM traverse_level(struct fs_traverse *ftp)
8177917SReza.Sabdar@Sun.COM {
8187917SReza.Sabdar@Sun.COM 	char path[PATH_MAX + 1];	/* full path name of the current dir */
8197917SReza.Sabdar@Sun.COM 	char nm[NAME_MAX + 1];	/* directory entry name */
8207917SReza.Sabdar@Sun.COM 	char *lp;		/* last position on the path */
8217917SReza.Sabdar@Sun.COM 	int next_dir, rv;
8227917SReza.Sabdar@Sun.COM 	int pl, el;		/* path and directory entry length */
8237917SReza.Sabdar@Sun.COM 
8247917SReza.Sabdar@Sun.COM 	cstack_t *sp;
8257917SReza.Sabdar@Sun.COM 	fs_fhandle_t pfh, efh;
8267917SReza.Sabdar@Sun.COM 	struct stat64 pst, est;
8277917SReza.Sabdar@Sun.COM 	traverse_state_t *tsp;
8287917SReza.Sabdar@Sun.COM 	struct fst_node pn, en;  /* parent and entry nodes */
8297917SReza.Sabdar@Sun.COM 	dent_arg_t darg;
8307917SReza.Sabdar@Sun.COM 	path_list_t *plhead, *plist;
8317917SReza.Sabdar@Sun.COM 
8327917SReza.Sabdar@Sun.COM 	if (!ftp || !ftp->ft_path || !*ftp->ft_path || !ftp->ft_callbk) {
8337917SReza.Sabdar@Sun.COM 		NDMP_LOG(LOG_DEBUG, "Invalid argument");
8347917SReza.Sabdar@Sun.COM 		errno = EINVAL;
8357917SReza.Sabdar@Sun.COM 		return (-1);
8367917SReza.Sabdar@Sun.COM 	}
8377917SReza.Sabdar@Sun.COM 	/* set the default log function if it's not already set */
8387917SReza.Sabdar@Sun.COM 	if (!ftp->ft_logfp) {
8397917SReza.Sabdar@Sun.COM 		ftp->ft_logfp = (ft_log_t)syslog;
8407917SReza.Sabdar@Sun.COM 		NDMP_LOG(LOG_DEBUG, "Log to system log \"%s\"", ftp->ft_path);
8417917SReza.Sabdar@Sun.COM 	}
8427917SReza.Sabdar@Sun.COM 	if (!ftp->ft_lpath) {
8437917SReza.Sabdar@Sun.COM 		NDMP_LOG(LOG_DEBUG,
8447917SReza.Sabdar@Sun.COM 		    "report the same paths \"%s\"", ftp->ft_path);
8457917SReza.Sabdar@Sun.COM 		ftp->ft_lpath = ftp->ft_path;
8467917SReza.Sabdar@Sun.COM 	}
8477917SReza.Sabdar@Sun.COM 
8487917SReza.Sabdar@Sun.COM 	pl = strlen(ftp->ft_lpath);
8497917SReza.Sabdar@Sun.COM 	if (pl + 1 > PATH_MAX) { /* +1 for the '/' */
8507917SReza.Sabdar@Sun.COM 		NDMP_LOG(LOG_DEBUG, "lpath too long \"%s\"", ftp->ft_path);
8517917SReza.Sabdar@Sun.COM 		errno = ENAMETOOLONG;
8527917SReza.Sabdar@Sun.COM 		return (-1);
8537917SReza.Sabdar@Sun.COM 	}
8547917SReza.Sabdar@Sun.COM 	(void) strcpy(path, ftp->ft_lpath);
8557917SReza.Sabdar@Sun.COM 	(void) memset(&pfh, 0, sizeof (pfh));
8567917SReza.Sabdar@Sun.COM 	rv = fs_getstat(ftp->ft_lpath, &pfh, &pst, NULL);
8577917SReza.Sabdar@Sun.COM 	if (rv != 0) {
8587917SReza.Sabdar@Sun.COM 		NDMP_LOG(LOG_DEBUG,
8597917SReza.Sabdar@Sun.COM 		    "Error %d on fs_getstat(%s)", rv, ftp->ft_path);
8607917SReza.Sabdar@Sun.COM 		return (-1);
8617917SReza.Sabdar@Sun.COM 	}
8627917SReza.Sabdar@Sun.COM 
8637917SReza.Sabdar@Sun.COM 	en.tn_path = NULL;
8647917SReza.Sabdar@Sun.COM 	en.tn_fh = NULL;
8657917SReza.Sabdar@Sun.COM 	en.tn_st = NULL;
8667917SReza.Sabdar@Sun.COM 	if (!S_ISDIR(pst.st_mode)) {
8677917SReza.Sabdar@Sun.COM 		pn.tn_path = ftp->ft_lpath;
8687917SReza.Sabdar@Sun.COM 		pn.tn_fh = &pfh;
8697917SReza.Sabdar@Sun.COM 		pn.tn_st = &pst;
8707917SReza.Sabdar@Sun.COM 		rv = CALLBACK(&pn, &en);
8717917SReza.Sabdar@Sun.COM 		if (VERBOSE(ftp))
8727917SReza.Sabdar@Sun.COM 			NDMP_LOG(LOG_DEBUG, "CALLBACK(%s): %d", pn.tn_path, rv);
8737917SReza.Sabdar@Sun.COM 
8747917SReza.Sabdar@Sun.COM 		free(pfh.fh_fpath);
8757917SReza.Sabdar@Sun.COM 		return (rv);
8767917SReza.Sabdar@Sun.COM 	}
8777917SReza.Sabdar@Sun.COM 
8787917SReza.Sabdar@Sun.COM 	sp = cstack_new();
8797917SReza.Sabdar@Sun.COM 	if (!sp) {
8807917SReza.Sabdar@Sun.COM 		free(pfh.fh_fpath);
8817917SReza.Sabdar@Sun.COM 		errno = ENOMEM;
8827917SReza.Sabdar@Sun.COM 		return (-1);
8837917SReza.Sabdar@Sun.COM 	}
8847917SReza.Sabdar@Sun.COM 	tsp = new_tsp(path);
8857917SReza.Sabdar@Sun.COM 	if (!tsp) {
8867917SReza.Sabdar@Sun.COM 		cstack_delete(sp);
8877917SReza.Sabdar@Sun.COM 		free(pfh.fh_fpath);
8887917SReza.Sabdar@Sun.COM 		errno = ENOMEM;
8897917SReza.Sabdar@Sun.COM 		return (-1);
8907917SReza.Sabdar@Sun.COM 	}
8917917SReza.Sabdar@Sun.COM 
8927917SReza.Sabdar@Sun.COM 	darg.da_buf = ndmp_malloc(MAX_DENT_BUF_SIZE);
8937917SReza.Sabdar@Sun.COM 	if (!darg.da_buf) {
8947917SReza.Sabdar@Sun.COM 		cstack_delete(sp);
8957917SReza.Sabdar@Sun.COM 		free(pfh.fh_fpath);
8967917SReza.Sabdar@Sun.COM 		free(tsp);
8977917SReza.Sabdar@Sun.COM 		errno = ENOMEM;
8987917SReza.Sabdar@Sun.COM 		return (-1);
8997917SReza.Sabdar@Sun.COM 	}
9007917SReza.Sabdar@Sun.COM 	darg.da_size = MAX_DENT_BUF_SIZE;
9017917SReza.Sabdar@Sun.COM 
9027917SReza.Sabdar@Sun.COM 	tsp->ts_ent = tsp->ts_end;
9037917SReza.Sabdar@Sun.COM 	tsp->ts_fh = pfh;
9047917SReza.Sabdar@Sun.COM 	tsp->ts_st = pst;
9057917SReza.Sabdar@Sun.COM 	pn.tn_path = path;
9067917SReza.Sabdar@Sun.COM 	pn.tn_fh = &tsp->ts_fh;
9077917SReza.Sabdar@Sun.COM 	pn.tn_st = &tsp->ts_st;
9087917SReza.Sabdar@Sun.COM 
9097917SReza.Sabdar@Sun.COM 	if ((plist = fs_init_pathlist()) == NULL) {
9107917SReza.Sabdar@Sun.COM 		cstack_delete(sp);
9117917SReza.Sabdar@Sun.COM 		free(pfh.fh_fpath);
9127917SReza.Sabdar@Sun.COM 		free(tsp);
9137917SReza.Sabdar@Sun.COM 		errno = ENOMEM;
9147917SReza.Sabdar@Sun.COM 		return (-1);
9157917SReza.Sabdar@Sun.COM 	}
9167917SReza.Sabdar@Sun.COM 	plhead = plist;
9177917SReza.Sabdar@Sun.COM 
9187917SReza.Sabdar@Sun.COM 	/* call the callback function on the path itself */
9197917SReza.Sabdar@Sun.COM 	traverse_stats.fss_dir_calls++;
9207917SReza.Sabdar@Sun.COM 	rv = CALLBACK(&pn, &en);
9217917SReza.Sabdar@Sun.COM 	if (rv < 0) {
9227917SReza.Sabdar@Sun.COM 		free(tsp);
9237917SReza.Sabdar@Sun.COM 		goto end;
9247917SReza.Sabdar@Sun.COM 	}
9257917SReza.Sabdar@Sun.COM 	if (rv == FST_SKIP) {
9267917SReza.Sabdar@Sun.COM 		traverse_stats.fss_dir_skipped++;
9277917SReza.Sabdar@Sun.COM 		free(tsp);
9287917SReza.Sabdar@Sun.COM 		rv = 0;
9297917SReza.Sabdar@Sun.COM 		goto end;
9307917SReza.Sabdar@Sun.COM 	}
9317917SReza.Sabdar@Sun.COM 
9327917SReza.Sabdar@Sun.COM 	rv = 0;
9337917SReza.Sabdar@Sun.COM 	next_dir = 1;
9347917SReza.Sabdar@Sun.COM 	do {
9357917SReza.Sabdar@Sun.COM 		if (next_dir) {
9367917SReza.Sabdar@Sun.COM 			traverse_stats.fss_newdirs++;
9377917SReza.Sabdar@Sun.COM 
9387917SReza.Sabdar@Sun.COM 			*tsp->ts_end = '\0';
9397917SReza.Sabdar@Sun.COM 			if (VERBOSE(ftp))
9407917SReza.Sabdar@Sun.COM 				NDMP_LOG(LOG_DEBUG, "pl %d \"%s\"", pl, path);
9417917SReza.Sabdar@Sun.COM 
9427917SReza.Sabdar@Sun.COM 			rv = traverse_level_nondir(ftp, tsp, &pn, &darg);
9437917SReza.Sabdar@Sun.COM 			if (rv < 0) {
9447917SReza.Sabdar@Sun.COM 				NEGATE(rv);
9457917SReza.Sabdar@Sun.COM 				free(tsp);
9467917SReza.Sabdar@Sun.COM 				break;
9477917SReza.Sabdar@Sun.COM 			}
9487917SReza.Sabdar@Sun.COM 			/*
9497917SReza.Sabdar@Sun.COM 			 * If skipped by the callback function or
9507917SReza.Sabdar@Sun.COM 			 * error happened reading the information
9517917SReza.Sabdar@Sun.COM 			 */
9527917SReza.Sabdar@Sun.COM 			if (rv == FST_SKIP || rv == SKIP_ENTRY) {
9537917SReza.Sabdar@Sun.COM 				/*
9547917SReza.Sabdar@Sun.COM 				 * N.B. next_dir should be set to 0 as
9557917SReza.Sabdar@Sun.COM 				 * well. This prevents the infinite loop.
9567917SReza.Sabdar@Sun.COM 				 * If it's not set the same directory will
9577917SReza.Sabdar@Sun.COM 				 * be poped from the stack and will be
9587917SReza.Sabdar@Sun.COM 				 * scanned again.
9597917SReza.Sabdar@Sun.COM 				 */
9607917SReza.Sabdar@Sun.COM 				next_dir = 0;
9617917SReza.Sabdar@Sun.COM 				rv = 0;
9627917SReza.Sabdar@Sun.COM 				goto skip_dir;
9637917SReza.Sabdar@Sun.COM 			}
9647917SReza.Sabdar@Sun.COM 
9657917SReza.Sabdar@Sun.COM 			/* re-start reading entries of the directory */
9667917SReza.Sabdar@Sun.COM 			tsp->ts_dpos = 0;
9677917SReza.Sabdar@Sun.COM 		}
9687917SReza.Sabdar@Sun.COM 
9697917SReza.Sabdar@Sun.COM 		next_dir = 0;
9707917SReza.Sabdar@Sun.COM 		do {
9717917SReza.Sabdar@Sun.COM 			el = NAME_MAX;
9727917SReza.Sabdar@Sun.COM 			rv = fs_readdir(&tsp->ts_fh, pn.tn_path,
9737917SReza.Sabdar@Sun.COM 			    &tsp->ts_dpos, nm, &el, &efh,
9747917SReza.Sabdar@Sun.COM 			    &est, &plist);
9757917SReza.Sabdar@Sun.COM 			if (rv != 0) {
9767917SReza.Sabdar@Sun.COM 				traverse_stats.fss_readdir_err++;
9777917SReza.Sabdar@Sun.COM 
9787917SReza.Sabdar@Sun.COM 				NDMP_LOG(LOG_DEBUG,
9797917SReza.Sabdar@Sun.COM 				    "Error %d on readdir(%s) pos %d",
9807917SReza.Sabdar@Sun.COM 				    rv, path, tsp->ts_dpos);
9817917SReza.Sabdar@Sun.COM 				if (STOP_ONERR(ftp))
9827917SReza.Sabdar@Sun.COM 					break;
9837917SReza.Sabdar@Sun.COM 				rv = SKIP_ENTRY;
9847917SReza.Sabdar@Sun.COM 				continue;
9857917SReza.Sabdar@Sun.COM 			}
9867917SReza.Sabdar@Sun.COM 
9877917SReza.Sabdar@Sun.COM 			/* done with this directory */
9887917SReza.Sabdar@Sun.COM 			if (el == 0)
9897917SReza.Sabdar@Sun.COM 				break;
9907917SReza.Sabdar@Sun.COM 
9917917SReza.Sabdar@Sun.COM 			nm[el] = '\0';
9927917SReza.Sabdar@Sun.COM 
9937917SReza.Sabdar@Sun.COM 			if (rootfs_dot_or_dotdot(nm)) {
9947917SReza.Sabdar@Sun.COM 				efh.fh_fpath = NULL;
9957917SReza.Sabdar@Sun.COM 				continue;
9967917SReza.Sabdar@Sun.COM 			}
9977917SReza.Sabdar@Sun.COM 
9987917SReza.Sabdar@Sun.COM 			if (VERBOSE(ftp))
9997917SReza.Sabdar@Sun.COM 				NDMP_LOG(LOG_DEBUG, "%u dname: \"%s\"",
10007917SReza.Sabdar@Sun.COM 				    tsp->ts_dpos, nm);
10017917SReza.Sabdar@Sun.COM 
10027917SReza.Sabdar@Sun.COM 			if (pl + 1 + el > PATH_MAX) {
10037917SReza.Sabdar@Sun.COM 				/*
10047917SReza.Sabdar@Sun.COM 				 * The long paths were already encountered
10057917SReza.Sabdar@Sun.COM 				 * when processing non-dir entries in.
10067917SReza.Sabdar@Sun.COM 				 * traverse_level_nondir.
10077917SReza.Sabdar@Sun.COM 				 * We don't increase fss_longpath_err
10087917SReza.Sabdar@Sun.COM 				 * counter for them again here.
10097917SReza.Sabdar@Sun.COM 				 */
10107917SReza.Sabdar@Sun.COM 				NDMP_LOG(LOG_ERR, "Path %s/%s is too long.",
10117917SReza.Sabdar@Sun.COM 				    path, nm);
10127917SReza.Sabdar@Sun.COM 				if (STOP_ONLONG(ftp))
10137917SReza.Sabdar@Sun.COM 					rv = ENAMETOOLONG;
10147917SReza.Sabdar@Sun.COM 				efh.fh_fpath = NULL;
10157917SReza.Sabdar@Sun.COM 				continue;
10167917SReza.Sabdar@Sun.COM 			}
10177917SReza.Sabdar@Sun.COM 
10187917SReza.Sabdar@Sun.COM 			if (!S_ISDIR(est.st_mode)) {
10197917SReza.Sabdar@Sun.COM 				efh.fh_fpath = NULL;
10207917SReza.Sabdar@Sun.COM 				continue;
10217917SReza.Sabdar@Sun.COM 			}
10227917SReza.Sabdar@Sun.COM 
10237917SReza.Sabdar@Sun.COM 			/*
10247917SReza.Sabdar@Sun.COM 			 * Call the callback function for the new
10257917SReza.Sabdar@Sun.COM 			 * directory found, then push the current
10267917SReza.Sabdar@Sun.COM 			 * directory on to the stack.  Then dive
10277917SReza.Sabdar@Sun.COM 			 * into the entry found.
10287917SReza.Sabdar@Sun.COM 			 */
10297917SReza.Sabdar@Sun.COM 			traverse_stats.fss_dir_calls++;
10307917SReza.Sabdar@Sun.COM 			en.tn_path = nm;
10317917SReza.Sabdar@Sun.COM 			en.tn_fh = &efh;
10327917SReza.Sabdar@Sun.COM 			en.tn_st = &est;
10337917SReza.Sabdar@Sun.COM 			rv = CALLBACK(&pn, &en);
10347917SReza.Sabdar@Sun.COM 
10357917SReza.Sabdar@Sun.COM 			if (rv < 0) {
10367917SReza.Sabdar@Sun.COM 				NEGATE(rv);
10377917SReza.Sabdar@Sun.COM 				break;
10387917SReza.Sabdar@Sun.COM 			}
10397917SReza.Sabdar@Sun.COM 			if (rv == FST_SKIP) {
10407917SReza.Sabdar@Sun.COM 				traverse_stats.fss_dir_skipped++;
10417917SReza.Sabdar@Sun.COM 				rv = 0;
10427917SReza.Sabdar@Sun.COM 				continue;
10437917SReza.Sabdar@Sun.COM 			}
10447917SReza.Sabdar@Sun.COM 
10457917SReza.Sabdar@Sun.COM 			/*
10467917SReza.Sabdar@Sun.COM 			 * Push the current directory on to the stack and
10477917SReza.Sabdar@Sun.COM 			 * dive into the entry found.
10487917SReza.Sabdar@Sun.COM 			 */
10497917SReza.Sabdar@Sun.COM 			if (cstack_push(sp, tsp, 0))
10507917SReza.Sabdar@Sun.COM 				rv = ENOMEM;
10517917SReza.Sabdar@Sun.COM 			else {
10527917SReza.Sabdar@Sun.COM 				traverse_stats.fss_pushes++;
10537917SReza.Sabdar@Sun.COM 
10547917SReza.Sabdar@Sun.COM 				lp = tsp->ts_end;
10557917SReza.Sabdar@Sun.COM 				*tsp->ts_end = '/';
10567917SReza.Sabdar@Sun.COM 				(void) strcpy(tsp->ts_end + 1, nm);
10577917SReza.Sabdar@Sun.COM 
10587917SReza.Sabdar@Sun.COM 				tsp = new_tsp(path);
10597917SReza.Sabdar@Sun.COM 				if (!tsp)
10607917SReza.Sabdar@Sun.COM 					rv = ENOMEM;
10617917SReza.Sabdar@Sun.COM 				else {
10627917SReza.Sabdar@Sun.COM 					next_dir = 1;
10637917SReza.Sabdar@Sun.COM 					pl += el + 1;
10647917SReza.Sabdar@Sun.COM 					tsp->ts_fh = efh;
10657917SReza.Sabdar@Sun.COM 					tsp->ts_st = est;
10667917SReza.Sabdar@Sun.COM 					tsp->ts_ent = lp;
10677917SReza.Sabdar@Sun.COM 					pn.tn_fh = &tsp->ts_fh;
10687917SReza.Sabdar@Sun.COM 					pn.tn_st = &tsp->ts_st;
10697917SReza.Sabdar@Sun.COM 				}
10707917SReza.Sabdar@Sun.COM 			}
10717917SReza.Sabdar@Sun.COM 			break;
10727917SReza.Sabdar@Sun.COM 
10737917SReza.Sabdar@Sun.COM 		} while (rv == 0);
10747917SReza.Sabdar@Sun.COM 
10757917SReza.Sabdar@Sun.COM 		/*
10767917SReza.Sabdar@Sun.COM 		 * A new directory must be processed, go to the start of
10777917SReza.Sabdar@Sun.COM 		 * the loop, open it and process it.
10787917SReza.Sabdar@Sun.COM 		 */
10797917SReza.Sabdar@Sun.COM 		if (next_dir)
10807917SReza.Sabdar@Sun.COM 			continue;
10817917SReza.Sabdar@Sun.COM skip_dir:
10827917SReza.Sabdar@Sun.COM 		if (tsp)
10837917SReza.Sabdar@Sun.COM 			free(tsp);
10847917SReza.Sabdar@Sun.COM 
10857917SReza.Sabdar@Sun.COM 		if (rv == SKIP_ENTRY)
10867917SReza.Sabdar@Sun.COM 			rv = 0;
10877917SReza.Sabdar@Sun.COM 
10887917SReza.Sabdar@Sun.COM 		if (rv == 0) {
10897917SReza.Sabdar@Sun.COM 			if (cstack_pop(sp, (void **)&tsp, (int *)NULL))
10907917SReza.Sabdar@Sun.COM 				break;
10917917SReza.Sabdar@Sun.COM 
10927917SReza.Sabdar@Sun.COM 			traverse_stats.fss_pops++;
10937917SReza.Sabdar@Sun.COM 
10947917SReza.Sabdar@Sun.COM 			if (VERBOSE(ftp))
10957917SReza.Sabdar@Sun.COM 				NDMP_LOG(LOG_DEBUG,
10967917SReza.Sabdar@Sun.COM 				    "Poped pl %d \"%s\"", pl, path);
10977917SReza.Sabdar@Sun.COM 
10987917SReza.Sabdar@Sun.COM 			*tsp->ts_end = '\0';
10997917SReza.Sabdar@Sun.COM 			pl = tsp->ts_end - path;
11007917SReza.Sabdar@Sun.COM 			pn.tn_fh = &tsp->ts_fh;
11017917SReza.Sabdar@Sun.COM 			pn.tn_st = &tsp->ts_st;
11027917SReza.Sabdar@Sun.COM 		}
11037917SReza.Sabdar@Sun.COM 	} while (rv == 0);
11047917SReza.Sabdar@Sun.COM 
11057917SReza.Sabdar@Sun.COM 	/*
11067917SReza.Sabdar@Sun.COM 	 * Pop and free all the remaining entries on the stack.
11077917SReza.Sabdar@Sun.COM 	 */
11087917SReza.Sabdar@Sun.COM 	while (!cstack_pop(sp, (void **)&tsp, (int *)NULL)) {
11097917SReza.Sabdar@Sun.COM 		traverse_stats.fss_stack_residue++;
11107917SReza.Sabdar@Sun.COM 
11117917SReza.Sabdar@Sun.COM 		free(tsp);
11127917SReza.Sabdar@Sun.COM 	}
11137917SReza.Sabdar@Sun.COM end:
11147917SReza.Sabdar@Sun.COM 	free(darg.da_buf);
11157917SReza.Sabdar@Sun.COM 	free(pfh.fh_fpath);
11167917SReza.Sabdar@Sun.COM 	fs_free_pathlist(plhead);
11177917SReza.Sabdar@Sun.COM 	cstack_delete(sp);
11187917SReza.Sabdar@Sun.COM 	return (rv);
11197917SReza.Sabdar@Sun.COM }
11207917SReza.Sabdar@Sun.COM 
11217917SReza.Sabdar@Sun.COM /*
11227917SReza.Sabdar@Sun.COM  * filecopy - Copy a file
11237917SReza.Sabdar@Sun.COM  *
11247917SReza.Sabdar@Sun.COM  * Parameters:
11257917SReza.Sabdar@Sun.COM  *  char *dest  - Destination path
11267917SReza.Sabdar@Sun.COM  *  char *src   - Source path
11277917SReza.Sabdar@Sun.COM  *
11287917SReza.Sabdar@Sun.COM  * Returns:
11297917SReza.Sabdar@Sun.COM  *  0    - No errors
11307917SReza.Sabdar@Sun.COM  *  #0   - Error occured
11317917SReza.Sabdar@Sun.COM  *		-4   - read/write error
11327917SReza.Sabdar@Sun.COM  *		-5   - source modified during copy
11337917SReza.Sabdar@Sun.COM  *
11347917SReza.Sabdar@Sun.COM  * Simplified version for Solaris
11357917SReza.Sabdar@Sun.COM  */
11367917SReza.Sabdar@Sun.COM #define	BUFSIZE	32768
11377917SReza.Sabdar@Sun.COM int
11387917SReza.Sabdar@Sun.COM filecopy(char *dest, char *src)
11397917SReza.Sabdar@Sun.COM {
11407917SReza.Sabdar@Sun.COM 	FILE *src_fh = 0;
11417917SReza.Sabdar@Sun.COM 	FILE *dst_fh = 0;
11427917SReza.Sabdar@Sun.COM 	struct stat64 src_attr;
11437917SReza.Sabdar@Sun.COM 	struct stat64 dst_attr;
11447917SReza.Sabdar@Sun.COM 	char *buf = 0;
11457917SReza.Sabdar@Sun.COM 	u_longlong_t bytes_to_copy;
11467917SReza.Sabdar@Sun.COM 	size_t nbytes;
11477917SReza.Sabdar@Sun.COM 	int file_copied = 0;
11487917SReza.Sabdar@Sun.COM 
11497917SReza.Sabdar@Sun.COM 	buf = ndmp_malloc(BUFSIZE);
11507917SReza.Sabdar@Sun.COM 	if (!buf)
11517917SReza.Sabdar@Sun.COM 		return (-1);
11527917SReza.Sabdar@Sun.COM 
11537917SReza.Sabdar@Sun.COM 	src_fh = fopen(src, "r");
11547917SReza.Sabdar@Sun.COM 	if (src_fh == 0) {
11557917SReza.Sabdar@Sun.COM 		free(buf);
11567917SReza.Sabdar@Sun.COM 		return (-2);
11577917SReza.Sabdar@Sun.COM 	}
11587917SReza.Sabdar@Sun.COM 
11597917SReza.Sabdar@Sun.COM 	dst_fh = fopen(dest, "w");
11607917SReza.Sabdar@Sun.COM 	if (dst_fh == NULL) {
11617917SReza.Sabdar@Sun.COM 		free(buf);
11627917SReza.Sabdar@Sun.COM 		(void) fclose(src_fh);
11637917SReza.Sabdar@Sun.COM 		return (-3);
11647917SReza.Sabdar@Sun.COM 	}
11657917SReza.Sabdar@Sun.COM 
11667917SReza.Sabdar@Sun.COM 	if (stat64(src, &src_attr) < 0) {
11677917SReza.Sabdar@Sun.COM 		free(buf);
11687917SReza.Sabdar@Sun.COM 		(void) fclose(src_fh);
11697917SReza.Sabdar@Sun.COM 		(void) fclose(dst_fh);
11707917SReza.Sabdar@Sun.COM 		return (-2);
11717917SReza.Sabdar@Sun.COM 	}
11727917SReza.Sabdar@Sun.COM 
11737917SReza.Sabdar@Sun.COM 	bytes_to_copy = src_attr.st_size;
11747917SReza.Sabdar@Sun.COM 	while (bytes_to_copy) {
11757917SReza.Sabdar@Sun.COM 		if (bytes_to_copy > BUFSIZE)
11767917SReza.Sabdar@Sun.COM 			nbytes = BUFSIZE;
11777917SReza.Sabdar@Sun.COM 		else
11787917SReza.Sabdar@Sun.COM 			nbytes = bytes_to_copy;
11797917SReza.Sabdar@Sun.COM 
11807917SReza.Sabdar@Sun.COM 		if ((fread(buf, nbytes, 1, src_fh) != 1) ||
11817917SReza.Sabdar@Sun.COM 		    (fwrite(buf, nbytes, 1, dst_fh) != 1))
11827917SReza.Sabdar@Sun.COM 			break;
11837917SReza.Sabdar@Sun.COM 		bytes_to_copy -= nbytes;
11847917SReza.Sabdar@Sun.COM 	}
11857917SReza.Sabdar@Sun.COM 
11867917SReza.Sabdar@Sun.COM 	(void) fclose(src_fh);
11877917SReza.Sabdar@Sun.COM 	(void) fclose(dst_fh);
11887917SReza.Sabdar@Sun.COM 
11897917SReza.Sabdar@Sun.COM 	if (bytes_to_copy > 0) {
11907917SReza.Sabdar@Sun.COM 		free(buf);
11917917SReza.Sabdar@Sun.COM 		/* short read/write, remove the partial file */
11927917SReza.Sabdar@Sun.COM 		return (-4);
11937917SReza.Sabdar@Sun.COM 	}
11947917SReza.Sabdar@Sun.COM 
11957917SReza.Sabdar@Sun.COM 	if (stat64(src, &dst_attr) < 0) {
11967917SReza.Sabdar@Sun.COM 		free(buf);
11977917SReza.Sabdar@Sun.COM 		return (-2);
11987917SReza.Sabdar@Sun.COM 	}
11997917SReza.Sabdar@Sun.COM 
12007917SReza.Sabdar@Sun.COM 	free(buf);
12017917SReza.Sabdar@Sun.COM 
12027917SReza.Sabdar@Sun.COM 	if (!file_copied)
12037917SReza.Sabdar@Sun.COM 		return (-5);	/* source modified during copy */
12047917SReza.Sabdar@Sun.COM 	else
12057917SReza.Sabdar@Sun.COM 		return (0);
12067917SReza.Sabdar@Sun.COM }
1207