12654012fSReza Sabdar /*
2*65451a03SReza Sabdar  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
32654012fSReza Sabdar  */
42654012fSReza Sabdar 
52654012fSReza Sabdar /*
62654012fSReza Sabdar  * BSD 3 Clause License
72654012fSReza Sabdar  *
82654012fSReza Sabdar  * Copyright (c) 2007, The Storage Networking Industry Association.
92654012fSReza Sabdar  *
102654012fSReza Sabdar  * Redistribution and use in source and binary forms, with or without
112654012fSReza Sabdar  * modification, are permitted provided that the following conditions
122654012fSReza Sabdar  * are met:
132654012fSReza Sabdar  * 	- Redistributions of source code must retain the above copyright
142654012fSReza Sabdar  *	  notice, this list of conditions and the following disclaimer.
152654012fSReza Sabdar  *
162654012fSReza Sabdar  * 	- Redistributions in binary form must reproduce the above copyright
172654012fSReza Sabdar  *	  notice, this list of conditions and the following disclaimer in
182654012fSReza Sabdar  *	  the documentation and/or other materials provided with the
192654012fSReza Sabdar  *	  distribution.
202654012fSReza Sabdar  *
212654012fSReza Sabdar  *	- Neither the name of The Storage Networking Industry Association (SNIA)
222654012fSReza Sabdar  *	  nor the names of its contributors may be used to endorse or promote
232654012fSReza Sabdar  *	  products derived from this software without specific prior written
242654012fSReza Sabdar  *	  permission.
252654012fSReza Sabdar  *
262654012fSReza Sabdar  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
272654012fSReza Sabdar  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
282654012fSReza Sabdar  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
292654012fSReza Sabdar  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
302654012fSReza Sabdar  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
312654012fSReza Sabdar  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
322654012fSReza Sabdar  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
332654012fSReza Sabdar  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
342654012fSReza Sabdar  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
352654012fSReza Sabdar  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
362654012fSReza Sabdar  * POSSIBILITY OF SUCH DAMAGE.
372654012fSReza Sabdar  */
382654012fSReza Sabdar /*
392654012fSReza Sabdar  * This file implemets the post-order, pre-order and level-order
402654012fSReza Sabdar  * traversing of the file system.  The related macros and constants
412654012fSReza Sabdar  * are defined in traverse.h.
422654012fSReza Sabdar  */
432654012fSReza Sabdar 
442654012fSReza Sabdar #include <sys/stat.h>
452654012fSReza Sabdar #include <sys/types.h>
462654012fSReza Sabdar #include <sys/param.h>
472654012fSReza Sabdar #include <assert.h>
482654012fSReza Sabdar #include <cstack.h>
492654012fSReza Sabdar #include <dirent.h>
502654012fSReza Sabdar #include <errno.h>
512654012fSReza Sabdar #include <traverse.h>
522654012fSReza Sabdar #include <limits.h>
532654012fSReza Sabdar #include <stdarg.h>
542654012fSReza Sabdar #include <stdio.h>
552654012fSReza Sabdar #include <stdlib.h>
562654012fSReza Sabdar #include <string.h>
572654012fSReza Sabdar #include <syslog.h>
582654012fSReza Sabdar #include <fcntl.h>
592654012fSReza Sabdar #include <unistd.h>
602654012fSReza Sabdar #include <tlm.h>
612654012fSReza Sabdar #include "tlm_proto.h"
622654012fSReza Sabdar 
632654012fSReza Sabdar /*
642654012fSReza Sabdar  * Check if it's "." or ".."
652654012fSReza Sabdar  */
662654012fSReza Sabdar boolean_t
rootfs_dot_or_dotdot(char * name)672654012fSReza Sabdar rootfs_dot_or_dotdot(char *name)
682654012fSReza Sabdar {
692654012fSReza Sabdar 	if (*name != '.')
702654012fSReza Sabdar 		return (FALSE);
712654012fSReza Sabdar 
722654012fSReza Sabdar 	if ((name[1] == 0) || (name[1] == '.' && name[2] == 0))
732654012fSReza Sabdar 		return (TRUE);
742654012fSReza Sabdar 
752654012fSReza Sabdar 	return (FALSE);
762654012fSReza Sabdar }
772654012fSReza Sabdar 
782654012fSReza Sabdar /*
792654012fSReza Sabdar  * Macros on fs_traverse flags.
802654012fSReza Sabdar  */
812654012fSReza Sabdar #define	STOP_ONERR(f)	((f)->ft_flags & FST_STOP_ONERR)
822654012fSReza Sabdar #define	STOP_ONLONG(f)	((f)->ft_flags & FST_STOP_ONLONG)
832654012fSReza Sabdar #define	VERBOSE(f)	((f)->ft_flags & FST_VERBOSE)
842654012fSReza Sabdar 
852654012fSReza Sabdar #define	CALLBACK(pp, ep)	\
862654012fSReza Sabdar 	(*(ftp)->ft_callbk)((ftp)->ft_arg, pp, ep)
872654012fSReza Sabdar 
882654012fSReza Sabdar #define	NEGATE(rv)	((rv) =	-(rv))
892654012fSReza Sabdar 
902654012fSReza Sabdar /*
912654012fSReza Sabdar  * The traversing state that is pushed onto the stack.
922654012fSReza Sabdar  * This include:
932654012fSReza Sabdar  * 	- The end of the path of the current directory.
942654012fSReza Sabdar  *	- The position of the last component on it.
952654012fSReza Sabdar  *	- The read position in the directory.
962654012fSReza Sabdar  *	- The file handle of the directory.
972654012fSReza Sabdar  *	- The stat of the directory.
982654012fSReza Sabdar  */
992654012fSReza Sabdar typedef struct traverse_state {
1002654012fSReza Sabdar 	char *ts_end;
1012654012fSReza Sabdar 	char *ts_ent;
1022654012fSReza Sabdar 	long ts_dpos; /* position in the directory when reading its entries */
1032654012fSReza Sabdar 	fs_fhandle_t ts_fh;
1042654012fSReza Sabdar 	struct stat64 ts_st;
1052654012fSReza Sabdar } traverse_state_t;
1062654012fSReza Sabdar 
1072654012fSReza Sabdar /*
1082654012fSReza Sabdar  * Statistics gathering structure.
1092654012fSReza Sabdar  */
1102654012fSReza Sabdar typedef struct traverse_statistics {
1112654012fSReza Sabdar 	ulong_t fss_newdirs;
1122654012fSReza Sabdar 	ulong_t fss_readdir_err;
1132654012fSReza Sabdar 	ulong_t fss_longpath_err;
1142654012fSReza Sabdar 	ulong_t fss_lookup_err;
1152654012fSReza Sabdar 	ulong_t fss_nondir_calls;
1162654012fSReza Sabdar 	ulong_t fss_dir_calls;
1172654012fSReza Sabdar 	ulong_t fss_nondir_skipped;
1182654012fSReza Sabdar 	ulong_t fss_dir_skipped;
1192654012fSReza Sabdar 	ulong_t fss_pushes;
1202654012fSReza Sabdar 	ulong_t fss_pops;
1212654012fSReza Sabdar 	ulong_t fss_stack_residue;
1222654012fSReza Sabdar } traverse_statistics_t;
1232654012fSReza Sabdar 
1242654012fSReza Sabdar /*
1252654012fSReza Sabdar  * Global instance of statistics variable.
1262654012fSReza Sabdar  */
1272654012fSReza Sabdar traverse_statistics_t traverse_stats;
1282654012fSReza Sabdar 
1292654012fSReza Sabdar #define	MAX_DENT_BUF_SIZE	(8 * 1024)
1302654012fSReza Sabdar 
1312654012fSReza Sabdar typedef struct {
1322654012fSReza Sabdar 	struct stat64 fd_attr;
1332654012fSReza Sabdar 	fs_fhandle_t fd_fh;
1342654012fSReza Sabdar 	short fd_len;
1352654012fSReza Sabdar 	char fd_name[1];
1362654012fSReza Sabdar } fs_dent_info_t;
1372654012fSReza Sabdar 
1382654012fSReza Sabdar typedef struct dent_arg {
1392654012fSReza Sabdar 	char *da_buf;
1402654012fSReza Sabdar 	int da_end;
1412654012fSReza Sabdar 	int da_size;
1422654012fSReza Sabdar } dent_arg_t;
1432654012fSReza Sabdar 
1442654012fSReza Sabdar static int traverse_level_nondir(struct fs_traverse *ftp,
1452654012fSReza Sabdar     traverse_state_t *tsp, struct fst_node *pnp,
1462654012fSReza Sabdar     dent_arg_t *darg);
1472654012fSReza Sabdar 
1482654012fSReza Sabdar /*
1492654012fSReza Sabdar  * Gather some directory entry information and return them
1502654012fSReza Sabdar  */
1512654012fSReza Sabdar static int
fs_populate_dents(void * arg,int namelen,char * name,long * countp,struct stat64 * attr,fs_fhandle_t * fh)1522654012fSReza Sabdar fs_populate_dents(void *arg, int namelen,
1532654012fSReza Sabdar     char *name, long *countp, struct stat64 *attr,
1542654012fSReza Sabdar     fs_fhandle_t *fh)
1552654012fSReza Sabdar {
1562654012fSReza Sabdar 	dent_arg_t *darg = (dent_arg_t *)arg;
1572654012fSReza Sabdar 	int reclen = sizeof (fs_dent_info_t) + namelen;
1582654012fSReza Sabdar 	fs_dent_info_t *dent;
1592654012fSReza Sabdar 
1602654012fSReza Sabdar 	if ((darg->da_end + reclen) > darg->da_size)
1612654012fSReza Sabdar 		return (-1);
1622654012fSReza Sabdar 
1632654012fSReza Sabdar 	/* LINTED improper alignment */
1642654012fSReza Sabdar 	dent = (fs_dent_info_t *)(darg->da_buf + darg->da_end);
1652654012fSReza Sabdar 
1662654012fSReza Sabdar 	dent->fd_attr = *attr;
1672654012fSReza Sabdar 	dent->fd_fh = *fh;
1682654012fSReza Sabdar 	(void) strcpy(dent->fd_name, name);
1692654012fSReza Sabdar 
1702654012fSReza Sabdar 	dent->fd_len = reclen;
1712654012fSReza Sabdar 	darg->da_end += reclen;
1722654012fSReza Sabdar 
1732654012fSReza Sabdar 	if (countp)
1742654012fSReza Sabdar 		(*countp)++;
1752654012fSReza Sabdar 
1762654012fSReza Sabdar 	return (0);
1772654012fSReza Sabdar }
1782654012fSReza Sabdar 
1792654012fSReza Sabdar /*
1802654012fSReza Sabdar  * Creates a new traversing state based on the path passed to it.
1812654012fSReza Sabdar  */
1822654012fSReza Sabdar static traverse_state_t *
new_tsp(char * path)1832654012fSReza Sabdar new_tsp(char *path)
1842654012fSReza Sabdar {
1852654012fSReza Sabdar 	traverse_state_t *tsp;
1862654012fSReza Sabdar 	tsp = ndmp_malloc(sizeof (traverse_state_t));
1872654012fSReza Sabdar 	if (!tsp)
1882654012fSReza Sabdar 		return (NULL);
1892654012fSReza Sabdar 
1902654012fSReza Sabdar 	tsp->ts_end = strchr(path, '\0');
1912654012fSReza Sabdar 	if (*(tsp->ts_end-1) == '/')
1922654012fSReza Sabdar 		*--tsp->ts_end = '\0';
1932654012fSReza Sabdar 	tsp->ts_ent = NULL;
1942654012fSReza Sabdar 	tsp->ts_dpos = 0;
1952654012fSReza Sabdar 
1962654012fSReza Sabdar 	return (tsp);
1972654012fSReza Sabdar }
1982654012fSReza Sabdar 
1992654012fSReza Sabdar /*
2002654012fSReza Sabdar  * Create a file handle and get stats for the given path
2012654012fSReza Sabdar  */
2022654012fSReza Sabdar int
fs_getstat(char * path,fs_fhandle_t * fh,struct stat64 * st)20384bf06e9SReza Sabdar fs_getstat(char *path, fs_fhandle_t *fh, struct stat64 *st)
2042654012fSReza Sabdar {
2052654012fSReza Sabdar 	if (lstat64(path, st) == -1)
2062654012fSReza Sabdar 		return (errno);
2072654012fSReza Sabdar 
2082654012fSReza Sabdar 	fh->fh_fid = st->st_ino;
209c211fc47SReza Sabdar 
21084bf06e9SReza Sabdar 	if (!S_ISDIR(st->st_mode))
211c211fc47SReza Sabdar 		fh->fh_fpath = NULL;
2122654012fSReza Sabdar 	else
2132654012fSReza Sabdar 		fh->fh_fpath = strdup(path);
2142654012fSReza Sabdar 	return (0);
2152654012fSReza Sabdar }
2162654012fSReza Sabdar 
2172654012fSReza Sabdar /*
2182654012fSReza Sabdar  * Get directory entries info and return in the buffer. Cookie
2192654012fSReza Sabdar  * will keep the state of each call
2202654012fSReza Sabdar  */
2212654012fSReza Sabdar static int
fs_getdents(int fildes,struct dirent * buf,size_t * nbyte,char * pn_path,long * dpos,longlong_t * cookie,long * n_entries,dent_arg_t * darg)2222654012fSReza Sabdar fs_getdents(int fildes, struct dirent *buf, size_t *nbyte,
2232654012fSReza Sabdar     char *pn_path, long *dpos, longlong_t *cookie,
22484bf06e9SReza Sabdar     long *n_entries, dent_arg_t *darg)
2252654012fSReza Sabdar {
2262654012fSReza Sabdar 	struct dirent *ptr;
2272654012fSReza Sabdar 	char file_path[PATH_MAX + 1];
2282654012fSReza Sabdar 	fs_fhandle_t fh;
2292654012fSReza Sabdar 	struct stat64 st;
2302654012fSReza Sabdar 	char *p;
2312654012fSReza Sabdar 	int len;
2322654012fSReza Sabdar 	int rv;
2332654012fSReza Sabdar 
2342654012fSReza Sabdar 	if (*nbyte == 0) {
2352654012fSReza Sabdar 		(void) memset((char *)buf, 0, MAX_DENT_BUF_SIZE);
2362654012fSReza Sabdar 		*nbyte = rv = getdents(fildes, buf, darg->da_size);
2372654012fSReza Sabdar 		*cookie = 0LL;
2382654012fSReza Sabdar 
2392654012fSReza Sabdar 		if (rv <= 0)
2402654012fSReza Sabdar 			return (rv);
2412654012fSReza Sabdar 	}
2422654012fSReza Sabdar 
2432654012fSReza Sabdar 	p = (char *)buf + *cookie;
2442654012fSReza Sabdar 	len = *nbyte;
2452654012fSReza Sabdar 	do {
2462654012fSReza Sabdar 		/* LINTED improper alignment */
2472654012fSReza Sabdar 		ptr = (struct dirent *)p;
2482654012fSReza Sabdar 		*dpos =  ptr->d_off;
24984bf06e9SReza Sabdar 
25084bf06e9SReza Sabdar 		if (rootfs_dot_or_dotdot(ptr->d_name))
25184bf06e9SReza Sabdar 			goto skip_entry;
25284bf06e9SReza Sabdar 
2532654012fSReza Sabdar 		(void) snprintf(file_path, PATH_MAX, "%s/", pn_path);
254*65451a03SReza Sabdar 		(void) strlcat(file_path, ptr->d_name, PATH_MAX + 1);
2552654012fSReza Sabdar 		(void) memset(&fh, 0, sizeof (fs_fhandle_t));
2562654012fSReza Sabdar 
2575a3c8170SReza Sabdar 		if (lstat64(file_path, &st) != 0) {
2585a3c8170SReza Sabdar 			rv = -1;
2592654012fSReza Sabdar 			break;
2605a3c8170SReza Sabdar 		}
2612654012fSReza Sabdar 
26284bf06e9SReza Sabdar 		fh.fh_fid = st.st_ino;
26384bf06e9SReza Sabdar 
26484bf06e9SReza Sabdar 		if (S_ISDIR(st.st_mode))
26584bf06e9SReza Sabdar 			goto skip_entry;
26684bf06e9SReza Sabdar 
2675a3c8170SReza Sabdar 		if (fs_populate_dents(darg, strlen(ptr->d_name),
2685a3c8170SReza Sabdar 		    (char *)ptr->d_name, n_entries, &st, &fh) != 0)
2692654012fSReza Sabdar 			break;
2702654012fSReza Sabdar 
27184bf06e9SReza Sabdar skip_entry:
2722654012fSReza Sabdar 		p = p + ptr->d_reclen;
2732654012fSReza Sabdar 		len -= ptr->d_reclen;
2742654012fSReza Sabdar 	} while (len);
2752654012fSReza Sabdar 
2762654012fSReza Sabdar 	*cookie = (longlong_t)(p - (char *)buf);
2772654012fSReza Sabdar 	*nbyte = len;
2782654012fSReza Sabdar 	return (rv);
2792654012fSReza Sabdar }
2802654012fSReza Sabdar 
2812654012fSReza Sabdar /*
2822654012fSReza Sabdar  * Read the directory entries and return the information about
2832654012fSReza Sabdar  * each entry
2842654012fSReza Sabdar  */
2852654012fSReza Sabdar int
fs_readdir(fs_fhandle_t * ts_fh,char * path,long * dpos,char * nm,int * el,fs_fhandle_t * efh,struct stat64 * est)2862654012fSReza Sabdar fs_readdir(fs_fhandle_t *ts_fh, char *path, long *dpos,
28784bf06e9SReza Sabdar     char *nm, int *el, fs_fhandle_t *efh, struct stat64 *est)
2882654012fSReza Sabdar {
2892654012fSReza Sabdar 	struct dirent *dp;
2902654012fSReza Sabdar 	char  file_path[PATH_MAX + 1];
2912654012fSReza Sabdar 	DIR *dirp;
2922654012fSReza Sabdar 	int rv;
2932654012fSReza Sabdar 
2942654012fSReza Sabdar 	if ((dirp = opendir(ts_fh->fh_fpath)) == NULL)
2952654012fSReza Sabdar 		return (errno);
2962654012fSReza Sabdar 
2972654012fSReza Sabdar 	seekdir(dirp, *dpos);
2982654012fSReza Sabdar 	if ((dp = readdir(dirp)) == NULL) {
2992654012fSReza Sabdar 		rv = 0;  /* skip this dir */
3002654012fSReza Sabdar 		*el = 0;
3012654012fSReza Sabdar 	} else {
3022654012fSReza Sabdar 		(void) snprintf(file_path, PATH_MAX, "%s/", path);
303*65451a03SReza Sabdar 		(void) strlcat(file_path, dp->d_name, PATH_MAX + 1);
3042654012fSReza Sabdar 
30584bf06e9SReza Sabdar 		rv = fs_getstat(file_path, efh, est);
3062654012fSReza Sabdar 		if (rv == 0) {
3072654012fSReza Sabdar 			*dpos = telldir(dirp);
308*65451a03SReza Sabdar 			(void) strlcpy(nm, dp->d_name, NAME_MAX + 1);
3092654012fSReza Sabdar 			*el = strlen(dp->d_name);
3102654012fSReza Sabdar 		} else {
3112654012fSReza Sabdar 			*el = 0;
3122654012fSReza Sabdar 		}
3132654012fSReza Sabdar 	}
3142654012fSReza Sabdar 	(void) closedir(dirp);
3152654012fSReza Sabdar 	return (rv);
3162654012fSReza Sabdar }
3172654012fSReza Sabdar 
3182654012fSReza Sabdar /*
3192654012fSReza Sabdar  * Traverse the file system in the post-order way.  The description
3202654012fSReza Sabdar  * and example is in the header file.
3212654012fSReza Sabdar  *
3222654012fSReza Sabdar  * The callback function should return 0, on success and non-zero on
3232654012fSReza Sabdar  * failure.  If the callback function returns non-zero return value,
3242654012fSReza Sabdar  * the traversing stops.
3252654012fSReza Sabdar  */
3262654012fSReza Sabdar int
traverse_post(struct fs_traverse * ftp)3272654012fSReza Sabdar traverse_post(struct fs_traverse *ftp)
3282654012fSReza Sabdar {
3292654012fSReza Sabdar 	char path[PATH_MAX + 1]; /* full path name of the current dir */
3302654012fSReza Sabdar 	char nm[NAME_MAX + 1]; /* directory entry name */
3312654012fSReza Sabdar 	char *lp; /* last position on the path */
3322654012fSReza Sabdar 	int next_dir, rv;
3332654012fSReza Sabdar 	int pl, el; /* path and directory entry length */
3342654012fSReza Sabdar 	cstack_t *sp;
3352654012fSReza Sabdar 	fs_fhandle_t pfh, efh;
3362654012fSReza Sabdar 	struct stat64 pst, est;
3372654012fSReza Sabdar 	traverse_state_t *tsp;
3382654012fSReza Sabdar 	struct fst_node pn, en; /* parent and entry nodes */
3392654012fSReza Sabdar 
3402654012fSReza Sabdar 	if (!ftp || !ftp->ft_path || !*ftp->ft_path || !ftp->ft_callbk) {
3412654012fSReza Sabdar 		NDMP_LOG(LOG_DEBUG, "Invalid argument");
3422654012fSReza Sabdar 		errno = EINVAL;
3432654012fSReza Sabdar 		return (-1);
3442654012fSReza Sabdar 	}
3452654012fSReza Sabdar 
3462654012fSReza Sabdar 	/* set the default log function if it's not already set */
3472654012fSReza Sabdar 	if (!ftp->ft_logfp) {
3482654012fSReza Sabdar 		ftp->ft_logfp = (ft_log_t)syslog;
3492654012fSReza Sabdar 		NDMP_LOG(LOG_DEBUG, "Log to system log \"%s\"", ftp->ft_path);
3502654012fSReza Sabdar 	}
3512654012fSReza Sabdar 
3522654012fSReza Sabdar 	/* set the logical path to physical path if it's not already set */
3532654012fSReza Sabdar 	if (!ftp->ft_lpath) {
3542654012fSReza Sabdar 		NDMP_LOG(LOG_DEBUG,
3552654012fSReza Sabdar 		    "report the same paths: \"%s\"", ftp->ft_path);
3562654012fSReza Sabdar 		ftp->ft_lpath = ftp->ft_path;
3572654012fSReza Sabdar 	}
3582654012fSReza Sabdar 
3592654012fSReza Sabdar 	pl = strlen(ftp->ft_lpath);
3602654012fSReza Sabdar 	if (pl + 1 > PATH_MAX) { /* +1 for the '/' */
3612654012fSReza Sabdar 		NDMP_LOG(LOG_DEBUG, "lpath too long \"%s\"", ftp->ft_path);
3622654012fSReza Sabdar 		errno = ENAMETOOLONG;
3632654012fSReza Sabdar 		return (-1);
3642654012fSReza Sabdar 	}
3652654012fSReza Sabdar 	(void) strcpy(path, ftp->ft_lpath);
3662654012fSReza Sabdar 	(void) memset(&pfh, 0, sizeof (pfh));
36784bf06e9SReza Sabdar 	rv = fs_getstat(ftp->ft_lpath, &pfh, &pst);
3682654012fSReza Sabdar 
3692654012fSReza Sabdar 	if (rv != 0) {
3702654012fSReza Sabdar 		NDMP_LOG(LOG_DEBUG,
3712654012fSReza Sabdar 		    "Error %d on fs_getstat(%s)", rv, ftp->ft_path);
3722654012fSReza Sabdar 		return (rv);
3732654012fSReza Sabdar 	}
3742654012fSReza Sabdar 
3752654012fSReza Sabdar 	if (!S_ISDIR(pst.st_mode)) {
3762654012fSReza Sabdar 		pn.tn_path = ftp->ft_lpath;
3772654012fSReza Sabdar 		pn.tn_fh = &pfh;
3782654012fSReza Sabdar 		pn.tn_st = &pst;
3792654012fSReza Sabdar 		en.tn_path = NULL;
3802654012fSReza Sabdar 		en.tn_fh = NULL;
3812654012fSReza Sabdar 		en.tn_st = NULL;
3822654012fSReza Sabdar 		rv = CALLBACK(&pn, &en);
3832654012fSReza Sabdar 		if (VERBOSE(ftp))
3842654012fSReza Sabdar 			NDMP_LOG(LOG_DEBUG, "CALLBACK(%s): %d", pn.tn_path, rv);
3852654012fSReza Sabdar 		free(pfh.fh_fpath);
3862654012fSReza Sabdar 		return (rv);
3872654012fSReza Sabdar 	}
3882654012fSReza Sabdar 
3892654012fSReza Sabdar 	sp = cstack_new();
3902654012fSReza Sabdar 	if (!sp) {
3912654012fSReza Sabdar 		errno = ENOMEM;
3922654012fSReza Sabdar 		free(pfh.fh_fpath);
3932654012fSReza Sabdar 		return (-1);
3942654012fSReza Sabdar 	}
3952654012fSReza Sabdar 	tsp = new_tsp(path);
3962654012fSReza Sabdar 	if (!tsp) {
3972654012fSReza Sabdar 		cstack_delete(sp);
3982654012fSReza Sabdar 		errno = ENOMEM;
3992654012fSReza Sabdar 		free(pfh.fh_fpath);
4002654012fSReza Sabdar 		return (-1);
4012654012fSReza Sabdar 	}
4022654012fSReza Sabdar 	tsp->ts_ent = tsp->ts_end;
4032654012fSReza Sabdar 	tsp->ts_fh = pfh;
4042654012fSReza Sabdar 	tsp->ts_st = pst;
4052654012fSReza Sabdar 	pn.tn_path = path;
4062654012fSReza Sabdar 	pn.tn_fh = &tsp->ts_fh;
4072654012fSReza Sabdar 	pn.tn_st = &tsp->ts_st;
4082654012fSReza Sabdar 
4092654012fSReza Sabdar 	rv = 0;
4102654012fSReza Sabdar 	next_dir = 1;
4112654012fSReza Sabdar 	do {
4122654012fSReza Sabdar 		if (next_dir) {
4132654012fSReza Sabdar 			traverse_stats.fss_newdirs++;
4142654012fSReza Sabdar 
4152654012fSReza Sabdar 			*tsp->ts_end = '\0';
4162654012fSReza Sabdar 			if (VERBOSE(ftp))
4172654012fSReza Sabdar 				NDMP_LOG(LOG_DEBUG, "pl %d \"%s\"", pl, path);
4182654012fSReza Sabdar 		}
4192654012fSReza Sabdar 
4202654012fSReza Sabdar 		next_dir = 0;
4212654012fSReza Sabdar 		do {
4222654012fSReza Sabdar 			el = NAME_MAX;
4232654012fSReza Sabdar 			rv = fs_readdir(&tsp->ts_fh, pn.tn_path,
4242654012fSReza Sabdar 			    &tsp->ts_dpos, nm, &el,
42584bf06e9SReza Sabdar 			    &efh, &est);
4262654012fSReza Sabdar 
4272654012fSReza Sabdar 			if (rv != 0) {
42884bf06e9SReza Sabdar 				free(efh.fh_fpath);
4292654012fSReza Sabdar 				traverse_stats.fss_readdir_err++;
4302654012fSReza Sabdar 
4312654012fSReza Sabdar 				NDMP_LOG(LOG_DEBUG,
4322654012fSReza Sabdar 				    "Error %d on readdir(%s) pos %d",
4332654012fSReza Sabdar 				    rv, path, tsp->ts_dpos);
4342654012fSReza Sabdar 				if (STOP_ONERR(ftp))
4352654012fSReza Sabdar 					break;
4362654012fSReza Sabdar 				rv = SKIP_ENTRY;
4372654012fSReza Sabdar 
4382654012fSReza Sabdar 				continue;
4392654012fSReza Sabdar 			}
4402654012fSReza Sabdar 
4412654012fSReza Sabdar 			/* done with this directory */
4422654012fSReza Sabdar 			if (el == 0) {
4432654012fSReza Sabdar 				if (VERBOSE(ftp))
4442654012fSReza Sabdar 					NDMP_LOG(LOG_DEBUG,
4452654012fSReza Sabdar 					    "Done(%s)", pn.tn_path);
4462654012fSReza Sabdar 				break;
4472654012fSReza Sabdar 			}
4482654012fSReza Sabdar 			nm[el] = '\0';
4492654012fSReza Sabdar 
4502654012fSReza Sabdar 			if (rootfs_dot_or_dotdot(nm)) {
45184bf06e9SReza Sabdar 				free(efh.fh_fpath);
4522654012fSReza Sabdar 				continue;
4532654012fSReza Sabdar 			}
4542654012fSReza Sabdar 
4552654012fSReza Sabdar 			if (VERBOSE(ftp))
4562654012fSReza Sabdar 				NDMP_LOG(LOG_DEBUG, "%u dname: \"%s\"",
4572654012fSReza Sabdar 				    tsp->ts_dpos, nm);
4582654012fSReza Sabdar 
4592654012fSReza Sabdar 			if (pl + 1 + el > PATH_MAX) {
4602654012fSReza Sabdar 				traverse_stats.fss_longpath_err++;
4612654012fSReza Sabdar 
4622654012fSReza Sabdar 				NDMP_LOG(LOG_ERR, "Path %s/%s is too long.",
4632654012fSReza Sabdar 				    path, nm);
4642654012fSReza Sabdar 				if (STOP_ONLONG(ftp))
4652654012fSReza Sabdar 					rv = ENAMETOOLONG;
46684bf06e9SReza Sabdar 				free(efh.fh_fpath);
4672654012fSReza Sabdar 				continue;
4682654012fSReza Sabdar 			}
4692654012fSReza Sabdar 
4702654012fSReza Sabdar 			/*
4712654012fSReza Sabdar 			 * Push the current directory on to the stack and
4722654012fSReza Sabdar 			 * dive into the entry found.
4732654012fSReza Sabdar 			 */
4742654012fSReza Sabdar 			if (S_ISDIR(est.st_mode)) {
4752654012fSReza Sabdar 
4762654012fSReza Sabdar 				assert(tsp != NULL);
4772654012fSReza Sabdar 				if (cstack_push(sp, tsp, 0)) {
4782654012fSReza Sabdar 					rv = ENOMEM;
47984bf06e9SReza Sabdar 					free(efh.fh_fpath);
4802654012fSReza Sabdar 					break;
4812654012fSReza Sabdar 				}
4822654012fSReza Sabdar 				traverse_stats.fss_pushes++;
4832654012fSReza Sabdar 
4842654012fSReza Sabdar 				/*
4852654012fSReza Sabdar 				 * Concatenate the current entry with the
4862654012fSReza Sabdar 				 * current path.  This will be the path of
4872654012fSReza Sabdar 				 * the new directory to be scanned.
4882654012fSReza Sabdar 				 *
4892654012fSReza Sabdar 				 * Note:
4902654012fSReza Sabdar 				 * sprintf(tsp->ts_end, "/%s", de->d_name);
4912654012fSReza Sabdar 				 * could be used here, but concatenating
4922654012fSReza Sabdar 				 * strings like this might be faster.
4932654012fSReza Sabdar 				 * The length of the new path has been
4942654012fSReza Sabdar 				 * checked above.  So strcpy() can be
4952654012fSReza Sabdar 				 * safe and should not lead to a buffer
4962654012fSReza Sabdar 				 * over-run.
4972654012fSReza Sabdar 				 */
4982654012fSReza Sabdar 				lp = tsp->ts_end;
4992654012fSReza Sabdar 				*tsp->ts_end = '/';
5002654012fSReza Sabdar 				(void) strcpy(tsp->ts_end + 1, nm);
5012654012fSReza Sabdar 
5022654012fSReza Sabdar 				tsp = new_tsp(path);
5032654012fSReza Sabdar 				if (!tsp) {
50484bf06e9SReza Sabdar 					free(efh.fh_fpath);
5052654012fSReza Sabdar 					rv = ENOMEM;
5062654012fSReza Sabdar 				} else {
5072654012fSReza Sabdar 					next_dir = 1;
5082654012fSReza Sabdar 					pl += el;
5092654012fSReza Sabdar 					tsp->ts_fh = efh;
5102654012fSReza Sabdar 					tsp->ts_st = est;
5112654012fSReza Sabdar 					tsp->ts_ent = lp;
5122654012fSReza Sabdar 					pn.tn_fh = &tsp->ts_fh;
5132654012fSReza Sabdar 					pn.tn_st = &tsp->ts_st;
5142654012fSReza Sabdar 				}
5152654012fSReza Sabdar 				break;
5162654012fSReza Sabdar 			} else {
5172654012fSReza Sabdar 				/*
5182654012fSReza Sabdar 				 * The entry is not a directory so the
5192654012fSReza Sabdar 				 * callback function must be called.
5202654012fSReza Sabdar 				 */
5212654012fSReza Sabdar 				traverse_stats.fss_nondir_calls++;
5222654012fSReza Sabdar 
5232654012fSReza Sabdar 				en.tn_path = nm;
5242654012fSReza Sabdar 				en.tn_fh = &efh;
5252654012fSReza Sabdar 				en.tn_st = &est;
5262654012fSReza Sabdar 				rv = CALLBACK(&pn, &en);
52784bf06e9SReza Sabdar 				free(efh.fh_fpath);
5282654012fSReza Sabdar 				if (VERBOSE(ftp))
5292654012fSReza Sabdar 					NDMP_LOG(LOG_DEBUG,
5302654012fSReza Sabdar 					    "CALLBACK(%s/%s): %d",
5312654012fSReza Sabdar 					    pn.tn_path, en.tn_path, rv);
5322654012fSReza Sabdar 
5332654012fSReza Sabdar 				if (rv != 0)
5342654012fSReza Sabdar 					break;
5352654012fSReza Sabdar 			}
5362654012fSReza Sabdar 		} while (rv == 0);
5372654012fSReza Sabdar 
5382654012fSReza Sabdar 		/*
5392654012fSReza Sabdar 		 * A new directory must be processed, go to the start of
5402654012fSReza Sabdar 		 * the loop, open it and process it.
5412654012fSReza Sabdar 		 */
5422654012fSReza Sabdar 		if (next_dir)
5432654012fSReza Sabdar 			continue;
5442654012fSReza Sabdar 
5452654012fSReza Sabdar 		if (rv == SKIP_ENTRY)
5462654012fSReza Sabdar 			rv = 0; /* We should skip the current directory */
5472654012fSReza Sabdar 
5482654012fSReza Sabdar 		if (rv == 0) {
5492654012fSReza Sabdar 			/*
5502654012fSReza Sabdar 			 * Remove the ent from the end of path and send it
5512654012fSReza Sabdar 			 * as an entry of the path.
5522654012fSReza Sabdar 			 */
5532654012fSReza Sabdar 			lp = tsp->ts_ent;
5542654012fSReza Sabdar 			*lp = '\0';
5552654012fSReza Sabdar 			efh = tsp->ts_fh;
5562654012fSReza Sabdar 			est = tsp->ts_st;
5572654012fSReza Sabdar 			free(tsp);
5582654012fSReza Sabdar 			if (cstack_pop(sp, (void **)&tsp, (int *)NULL))
5592654012fSReza Sabdar 				break;
5602654012fSReza Sabdar 
5612654012fSReza Sabdar 			assert(tsp != NULL);
5622654012fSReza Sabdar 			pl = tsp->ts_end - path;
5632654012fSReza Sabdar 
5642654012fSReza Sabdar 			if (VERBOSE(ftp))
5652654012fSReza Sabdar 				NDMP_LOG(LOG_DEBUG, "poped pl %d 0x%p \"%s\"",
5662654012fSReza Sabdar 				    pl, tsp, path);
5672654012fSReza Sabdar 
5682654012fSReza Sabdar 			traverse_stats.fss_pops++;
5692654012fSReza Sabdar 			traverse_stats.fss_dir_calls++;
5702654012fSReza Sabdar 
5712654012fSReza Sabdar 			pn.tn_fh = &tsp->ts_fh;
5722654012fSReza Sabdar 			pn.tn_st = &tsp->ts_st;
5732654012fSReza Sabdar 			en.tn_path = lp + 1;
5742654012fSReza Sabdar 			en.tn_fh = &efh;
5752654012fSReza Sabdar 			en.tn_st = &est;
5762654012fSReza Sabdar 
5772654012fSReza Sabdar 			rv = CALLBACK(&pn, &en);
57884bf06e9SReza Sabdar 			free(efh.fh_fpath);
5792654012fSReza Sabdar 			if (VERBOSE(ftp))
5802654012fSReza Sabdar 				NDMP_LOG(LOG_DEBUG, "CALLBACK(%s/%s): %d",
5812654012fSReza Sabdar 				    pn.tn_path, en.tn_path, rv);
5822654012fSReza Sabdar 			/*
5832654012fSReza Sabdar 			 * Does not need to free tsp here.  It will be released
5842654012fSReza Sabdar 			 * later.
5852654012fSReza Sabdar 			 */
5862654012fSReza Sabdar 		}
5872654012fSReza Sabdar 
58884bf06e9SReza Sabdar 		if (rv != 0 && tsp) {
58984bf06e9SReza Sabdar 			free(tsp->ts_fh.fh_fpath);
5902654012fSReza Sabdar 			free(tsp);
59184bf06e9SReza Sabdar 		}
5922654012fSReza Sabdar 
5932654012fSReza Sabdar 	} while (rv == 0);
5942654012fSReza Sabdar 
5952654012fSReza Sabdar 	/*
5962654012fSReza Sabdar 	 * For the 'ftp->ft_path' directory itself.
5972654012fSReza Sabdar 	 */
5982654012fSReza Sabdar 	if (rv == 0) {
5992654012fSReza Sabdar 		traverse_stats.fss_dir_calls++;
6002654012fSReza Sabdar 
6012654012fSReza Sabdar 		pn.tn_fh = &efh;
6022654012fSReza Sabdar 		pn.tn_st = &est;
6032654012fSReza Sabdar 		en.tn_path = NULL;
6042654012fSReza Sabdar 		en.tn_fh = NULL;
6052654012fSReza Sabdar 		en.tn_st = NULL;
6062654012fSReza Sabdar 		rv = CALLBACK(&pn, &en);
6072654012fSReza Sabdar 		if (VERBOSE(ftp))
6082654012fSReza Sabdar 			NDMP_LOG(LOG_DEBUG, "CALLBACK(%s): %d", pn.tn_path, rv);
6092654012fSReza Sabdar 	}
6102654012fSReza Sabdar 
6112654012fSReza Sabdar 	/*
6122654012fSReza Sabdar 	 * Pop and free all the remaining entries on the stack.
6132654012fSReza Sabdar 	 */
6142654012fSReza Sabdar 	while (!cstack_pop(sp, (void **)&tsp, (int *)NULL)) {
6152654012fSReza Sabdar 		traverse_stats.fss_stack_residue++;
6162654012fSReza Sabdar 
61784bf06e9SReza Sabdar 		free(tsp->ts_fh.fh_fpath);
6182654012fSReza Sabdar 		free(tsp);
6192654012fSReza Sabdar 	}
6202654012fSReza Sabdar 
6212654012fSReza Sabdar 	cstack_delete(sp);
6222654012fSReza Sabdar 	return (rv);
6232654012fSReza Sabdar }
6242654012fSReza Sabdar 
6252654012fSReza Sabdar /*
6262654012fSReza Sabdar  * In one pass, read all the directory entries of the specified
6272654012fSReza Sabdar  * directory and call the callback function for non-directory
6282654012fSReza Sabdar  * entries.
6292654012fSReza Sabdar  *
6302654012fSReza Sabdar  * On return:
6312654012fSReza Sabdar  *    0: Lets the directory to be scanned for directory entries.
6322654012fSReza Sabdar  *    < 0: Completely stops traversing.
6332654012fSReza Sabdar  *    FST_SKIP: stops further scanning of the directory.  Traversing
6342654012fSReza Sabdar  *        will continue with the next directory in the hierarchy.
6352654012fSReza Sabdar  *    SKIP_ENTRY: Failed to get the directory entries, so the caller
6362654012fSReza Sabdar  *	  should skip this entry.
6372654012fSReza Sabdar  */
6382654012fSReza Sabdar static int
traverse_level_nondir(struct fs_traverse * ftp,traverse_state_t * tsp,struct fst_node * pnp,dent_arg_t * darg)6392654012fSReza Sabdar traverse_level_nondir(struct fs_traverse *ftp,
6402654012fSReza Sabdar     traverse_state_t *tsp, struct fst_node *pnp, dent_arg_t *darg)
6412654012fSReza Sabdar {
64284bf06e9SReza Sabdar 	int pl; /* path length */
6432654012fSReza Sabdar 	int rv;
6442654012fSReza Sabdar 	struct fst_node en; /* entry node */
6452654012fSReza Sabdar 	longlong_t cookie_verf;
6462654012fSReza Sabdar 	fs_dent_info_t *dent;
6472654012fSReza Sabdar 	struct dirent *buf;
6482654012fSReza Sabdar 	size_t len = 0;
6492654012fSReza Sabdar 	int fd;
6502654012fSReza Sabdar 
6512654012fSReza Sabdar 	rv = 0;
6522654012fSReza Sabdar 	pl = strlen(pnp->tn_path);
6532654012fSReza Sabdar 
6542654012fSReza Sabdar 	buf = ndmp_malloc(MAX_DENT_BUF_SIZE);
6552654012fSReza Sabdar 	if (buf == NULL)
6562654012fSReza Sabdar 		return (errno);
6572654012fSReza Sabdar 
6582654012fSReza Sabdar 	fd = open(tsp->ts_fh.fh_fpath, O_RDONLY);
6592654012fSReza Sabdar 	if (fd == -1) {
6602654012fSReza Sabdar 		free(buf);
6612654012fSReza Sabdar 		return (errno);
6622654012fSReza Sabdar 	}
6632654012fSReza Sabdar 
6642654012fSReza Sabdar 	while (rv == 0) {
6652654012fSReza Sabdar 		long i, n_entries;
6662654012fSReza Sabdar 
6672654012fSReza Sabdar 		darg->da_end = 0;
6682654012fSReza Sabdar 		n_entries = 0;
6692654012fSReza Sabdar 		rv = fs_getdents(fd, buf, &len, pnp->tn_path, &tsp->ts_dpos,
67084bf06e9SReza Sabdar 		    &cookie_verf, &n_entries, darg);
6715a3c8170SReza Sabdar 		if (rv < 0) {
6722654012fSReza Sabdar 			traverse_stats.fss_readdir_err++;
6732654012fSReza Sabdar 
6742654012fSReza Sabdar 			NDMP_LOG(LOG_DEBUG, "Error %d on readdir(%s) pos %d",
6752654012fSReza Sabdar 			    rv, pnp->tn_path, tsp->ts_dpos);
676*65451a03SReza Sabdar 			if (STOP_ONERR(ftp))
6772654012fSReza Sabdar 				break;
6782654012fSReza Sabdar 			/*
6792654012fSReza Sabdar 			 * We cannot read the directory entry, we should
6802654012fSReza Sabdar 			 * skip to the next directory.
6812654012fSReza Sabdar 			 */
6822654012fSReza Sabdar 			rv = SKIP_ENTRY;
6832654012fSReza Sabdar 			continue;
6845a3c8170SReza Sabdar 		} else {
6855a3c8170SReza Sabdar 			/* Break at the end of directory */
6865a3c8170SReza Sabdar 			if (rv > 0)
6875a3c8170SReza Sabdar 				rv = 0;
6885a3c8170SReza Sabdar 			else
6895a3c8170SReza Sabdar 				break;
6902654012fSReza Sabdar 		}
6912654012fSReza Sabdar 
6922654012fSReza Sabdar 		/* LINTED imporper alignment */
6932654012fSReza Sabdar 		dent = (fs_dent_info_t *)darg->da_buf;
6942654012fSReza Sabdar 		/* LINTED imporper alignment */
6952654012fSReza Sabdar 		for (i = 0; i < n_entries; i++, dent = (fs_dent_info_t *)
6962654012fSReza Sabdar 		    ((char *)dent + dent->fd_len)) {
6972654012fSReza Sabdar 
6982654012fSReza Sabdar 			if (VERBOSE(ftp))
6992654012fSReza Sabdar 				NDMP_LOG(LOG_DEBUG, "i %u dname: \"%s\"",
7002654012fSReza Sabdar 				    dent->fd_fh.fh_fid, dent->fd_name);
7012654012fSReza Sabdar 
7022654012fSReza Sabdar 			if ((pl + strlen(dent->fd_name)) > PATH_MAX) {
7032654012fSReza Sabdar 				traverse_stats.fss_longpath_err++;
7042654012fSReza Sabdar 
7052654012fSReza Sabdar 				NDMP_LOG(LOG_ERR, "Path %s/%s is too long.",
7062654012fSReza Sabdar 				    pnp->tn_path, dent->fd_name);
7072654012fSReza Sabdar 				if (STOP_ONLONG(ftp))
7082654012fSReza Sabdar 					rv = -ENAMETOOLONG;
70984bf06e9SReza Sabdar 				free(dent->fd_fh.fh_fpath);
7102654012fSReza Sabdar 				continue;
7112654012fSReza Sabdar 			}
7122654012fSReza Sabdar 
7132654012fSReza Sabdar 			/*
7142654012fSReza Sabdar 			 * The entry is not a directory so the callback
7152654012fSReza Sabdar 			 * function must be called.
7162654012fSReza Sabdar 			 */
7172654012fSReza Sabdar 			if (!S_ISDIR(dent->fd_attr.st_mode)) {
7182654012fSReza Sabdar 				traverse_stats.fss_nondir_calls++;
7192654012fSReza Sabdar 
7202654012fSReza Sabdar 				en.tn_path = dent->fd_name;
7212654012fSReza Sabdar 				en.tn_fh = &dent->fd_fh;
7222654012fSReza Sabdar 				en.tn_st = &dent->fd_attr;
7232654012fSReza Sabdar 				rv = CALLBACK(pnp, &en);
7242654012fSReza Sabdar 				dent->fd_fh.fh_fpath = NULL;
7252654012fSReza Sabdar 				if (rv < 0)
7262654012fSReza Sabdar 					break;
7272654012fSReza Sabdar 				if (rv == FST_SKIP) {
7282654012fSReza Sabdar 					traverse_stats.fss_nondir_skipped++;
7292654012fSReza Sabdar 					break;
7302654012fSReza Sabdar 				}
7312654012fSReza Sabdar 			}
7322654012fSReza Sabdar 		}
7332654012fSReza Sabdar 	}
7342654012fSReza Sabdar 
7352654012fSReza Sabdar 	free(buf);
7362654012fSReza Sabdar 	(void) close(fd);
7372654012fSReza Sabdar 	return (rv);
7382654012fSReza Sabdar }
7392654012fSReza Sabdar 
7402654012fSReza Sabdar /*
7412654012fSReza Sabdar  * Traverse the file system in the level-order way.  The description
7422654012fSReza Sabdar  * and example is in the header file.
7432654012fSReza Sabdar  */
7442654012fSReza Sabdar int
traverse_level(struct fs_traverse * ftp)7452654012fSReza Sabdar traverse_level(struct fs_traverse *ftp)
7462654012fSReza Sabdar {
7472654012fSReza Sabdar 	char path[PATH_MAX + 1];	/* full path name of the current dir */
7482654012fSReza Sabdar 	char nm[NAME_MAX + 1];	/* directory entry name */
7492654012fSReza Sabdar 	char *lp;		/* last position on the path */
7502654012fSReza Sabdar 	int next_dir, rv;
7512654012fSReza Sabdar 	int pl, el;		/* path and directory entry length */
7522654012fSReza Sabdar 
7532654012fSReza Sabdar 	cstack_t *sp;
7542654012fSReza Sabdar 	fs_fhandle_t pfh, efh;
7552654012fSReza Sabdar 	struct stat64 pst, est;
7562654012fSReza Sabdar 	traverse_state_t *tsp;
7572654012fSReza Sabdar 	struct fst_node pn, en;  /* parent and entry nodes */
7582654012fSReza Sabdar 	dent_arg_t darg;
7592654012fSReza Sabdar 
7602654012fSReza Sabdar 	if (!ftp || !ftp->ft_path || !*ftp->ft_path || !ftp->ft_callbk) {
7612654012fSReza Sabdar 		NDMP_LOG(LOG_DEBUG, "Invalid argument");
7622654012fSReza Sabdar 		errno = EINVAL;
7632654012fSReza Sabdar 		return (-1);
7642654012fSReza Sabdar 	}
7652654012fSReza Sabdar 	/* set the default log function if it's not already set */
7662654012fSReza Sabdar 	if (!ftp->ft_logfp) {
7672654012fSReza Sabdar 		ftp->ft_logfp = (ft_log_t)syslog;
7682654012fSReza Sabdar 		NDMP_LOG(LOG_DEBUG, "Log to system log \"%s\"", ftp->ft_path);
7692654012fSReza Sabdar 	}
7702654012fSReza Sabdar 	if (!ftp->ft_lpath) {
7712654012fSReza Sabdar 		NDMP_LOG(LOG_DEBUG,
7722654012fSReza Sabdar 		    "report the same paths \"%s\"", ftp->ft_path);
7732654012fSReza Sabdar 		ftp->ft_lpath = ftp->ft_path;
7742654012fSReza Sabdar 	}
7752654012fSReza Sabdar 
7762654012fSReza Sabdar 	pl = strlen(ftp->ft_lpath);
7772654012fSReza Sabdar 	if (pl + 1 > PATH_MAX) { /* +1 for the '/' */
7782654012fSReza Sabdar 		NDMP_LOG(LOG_DEBUG, "lpath too long \"%s\"", ftp->ft_path);
7792654012fSReza Sabdar 		errno = ENAMETOOLONG;
7802654012fSReza Sabdar 		return (-1);
7812654012fSReza Sabdar 	}
7822654012fSReza Sabdar 	(void) strcpy(path, ftp->ft_lpath);
7832654012fSReza Sabdar 	(void) memset(&pfh, 0, sizeof (pfh));
78484bf06e9SReza Sabdar 	rv = fs_getstat(ftp->ft_lpath, &pfh, &pst);
7852654012fSReza Sabdar 	if (rv != 0) {
7862654012fSReza Sabdar 		NDMP_LOG(LOG_DEBUG,
7872654012fSReza Sabdar 		    "Error %d on fs_getstat(%s)", rv, ftp->ft_path);
7882654012fSReza Sabdar 		return (-1);
7892654012fSReza Sabdar 	}
7902654012fSReza Sabdar 
7912654012fSReza Sabdar 	en.tn_path = NULL;
7922654012fSReza Sabdar 	en.tn_fh = NULL;
7932654012fSReza Sabdar 	en.tn_st = NULL;
7942654012fSReza Sabdar 	if (!S_ISDIR(pst.st_mode)) {
7952654012fSReza Sabdar 		pn.tn_path = ftp->ft_lpath;
7962654012fSReza Sabdar 		pn.tn_fh = &pfh;
7972654012fSReza Sabdar 		pn.tn_st = &pst;
7982654012fSReza Sabdar 		rv = CALLBACK(&pn, &en);
7992654012fSReza Sabdar 		if (VERBOSE(ftp))
8002654012fSReza Sabdar 			NDMP_LOG(LOG_DEBUG, "CALLBACK(%s): %d", pn.tn_path, rv);
8012654012fSReza Sabdar 
8022654012fSReza Sabdar 		free(pfh.fh_fpath);
8032654012fSReza Sabdar 		return (rv);
8042654012fSReza Sabdar 	}
8052654012fSReza Sabdar 
8062654012fSReza Sabdar 	sp = cstack_new();
8072654012fSReza Sabdar 	if (!sp) {
8082654012fSReza Sabdar 		free(pfh.fh_fpath);
8092654012fSReza Sabdar 		errno = ENOMEM;
8102654012fSReza Sabdar 		return (-1);
8112654012fSReza Sabdar 	}
8122654012fSReza Sabdar 	tsp = new_tsp(path);
8132654012fSReza Sabdar 	if (!tsp) {
8142654012fSReza Sabdar 		cstack_delete(sp);
8152654012fSReza Sabdar 		free(pfh.fh_fpath);
8162654012fSReza Sabdar 		errno = ENOMEM;
8172654012fSReza Sabdar 		return (-1);
8182654012fSReza Sabdar 	}
8192654012fSReza Sabdar 
8202654012fSReza Sabdar 	darg.da_buf = ndmp_malloc(MAX_DENT_BUF_SIZE);
8212654012fSReza Sabdar 	if (!darg.da_buf) {
8222654012fSReza Sabdar 		cstack_delete(sp);
8232654012fSReza Sabdar 		free(pfh.fh_fpath);
8242654012fSReza Sabdar 		free(tsp);
8252654012fSReza Sabdar 		errno = ENOMEM;
8262654012fSReza Sabdar 		return (-1);
8272654012fSReza Sabdar 	}
8282654012fSReza Sabdar 	darg.da_size = MAX_DENT_BUF_SIZE;
8292654012fSReza Sabdar 
8302654012fSReza Sabdar 	tsp->ts_ent = tsp->ts_end;
8312654012fSReza Sabdar 	tsp->ts_fh = pfh;
8322654012fSReza Sabdar 	tsp->ts_st = pst;
8332654012fSReza Sabdar 	pn.tn_path = path;
8342654012fSReza Sabdar 	pn.tn_fh = &tsp->ts_fh;
8352654012fSReza Sabdar 	pn.tn_st = &tsp->ts_st;
8362654012fSReza Sabdar 
8372654012fSReza Sabdar 	/* call the callback function on the path itself */
8382654012fSReza Sabdar 	traverse_stats.fss_dir_calls++;
8392654012fSReza Sabdar 	rv = CALLBACK(&pn, &en);
8402654012fSReza Sabdar 	if (rv < 0) {
8412654012fSReza Sabdar 		free(tsp);
8422654012fSReza Sabdar 		goto end;
8432654012fSReza Sabdar 	}
8442654012fSReza Sabdar 	if (rv == FST_SKIP) {
8452654012fSReza Sabdar 		traverse_stats.fss_dir_skipped++;
8462654012fSReza Sabdar 		free(tsp);
8472654012fSReza Sabdar 		rv = 0;
8482654012fSReza Sabdar 		goto end;
8492654012fSReza Sabdar 	}
8502654012fSReza Sabdar 
8512654012fSReza Sabdar 	rv = 0;
8522654012fSReza Sabdar 	next_dir = 1;
8532654012fSReza Sabdar 	do {
8542654012fSReza Sabdar 		if (next_dir) {
8552654012fSReza Sabdar 			traverse_stats.fss_newdirs++;
8562654012fSReza Sabdar 
8572654012fSReza Sabdar 			*tsp->ts_end = '\0';
8582654012fSReza Sabdar 			if (VERBOSE(ftp))
8592654012fSReza Sabdar 				NDMP_LOG(LOG_DEBUG, "pl %d \"%s\"", pl, path);
8602654012fSReza Sabdar 
8612654012fSReza Sabdar 			rv = traverse_level_nondir(ftp, tsp, &pn, &darg);
8622654012fSReza Sabdar 			if (rv < 0) {
8632654012fSReza Sabdar 				NEGATE(rv);
86484bf06e9SReza Sabdar 				free(tsp->ts_fh.fh_fpath);
8652654012fSReza Sabdar 				free(tsp);
8662654012fSReza Sabdar 				break;
8672654012fSReza Sabdar 			}
8682654012fSReza Sabdar 			/*
8692654012fSReza Sabdar 			 * If skipped by the callback function or
8702654012fSReza Sabdar 			 * error happened reading the information
8712654012fSReza Sabdar 			 */
8722654012fSReza Sabdar 			if (rv == FST_SKIP || rv == SKIP_ENTRY) {
8732654012fSReza Sabdar 				/*
8742654012fSReza Sabdar 				 * N.B. next_dir should be set to 0 as
8752654012fSReza Sabdar 				 * well. This prevents the infinite loop.
8762654012fSReza Sabdar 				 * If it's not set the same directory will
8772654012fSReza Sabdar 				 * be poped from the stack and will be
8782654012fSReza Sabdar 				 * scanned again.
8792654012fSReza Sabdar 				 */
8802654012fSReza Sabdar 				next_dir = 0;
8812654012fSReza Sabdar 				rv = 0;
8822654012fSReza Sabdar 				goto skip_dir;
8832654012fSReza Sabdar 			}
8842654012fSReza Sabdar 
8852654012fSReza Sabdar 			/* re-start reading entries of the directory */
8862654012fSReza Sabdar 			tsp->ts_dpos = 0;
8872654012fSReza Sabdar 		}
8882654012fSReza Sabdar 
8892654012fSReza Sabdar 		next_dir = 0;
8902654012fSReza Sabdar 		do {
8912654012fSReza Sabdar 			el = NAME_MAX;
8922654012fSReza Sabdar 			rv = fs_readdir(&tsp->ts_fh, pn.tn_path,
8932654012fSReza Sabdar 			    &tsp->ts_dpos, nm, &el, &efh,
89484bf06e9SReza Sabdar 			    &est);
8952654012fSReza Sabdar 			if (rv != 0) {
8962654012fSReza Sabdar 				traverse_stats.fss_readdir_err++;
8972654012fSReza Sabdar 
8982654012fSReza Sabdar 				NDMP_LOG(LOG_DEBUG,
8992654012fSReza Sabdar 				    "Error %d on readdir(%s) pos %d",
9002654012fSReza Sabdar 				    rv, path, tsp->ts_dpos);
9012654012fSReza Sabdar 				if (STOP_ONERR(ftp))
9022654012fSReza Sabdar 					break;
9032654012fSReza Sabdar 				rv = SKIP_ENTRY;
9042654012fSReza Sabdar 				continue;
9052654012fSReza Sabdar 			}
9062654012fSReza Sabdar 
9072654012fSReza Sabdar 			/* done with this directory */
9082654012fSReza Sabdar 			if (el == 0)
9092654012fSReza Sabdar 				break;
9102654012fSReza Sabdar 
9112654012fSReza Sabdar 			nm[el] = '\0';
9122654012fSReza Sabdar 
9132654012fSReza Sabdar 			if (rootfs_dot_or_dotdot(nm)) {
91484bf06e9SReza Sabdar 				free(efh.fh_fpath);
9152654012fSReza Sabdar 				continue;
9162654012fSReza Sabdar 			}
9172654012fSReza Sabdar 
9182654012fSReza Sabdar 			if (VERBOSE(ftp))
9192654012fSReza Sabdar 				NDMP_LOG(LOG_DEBUG, "%u dname: \"%s\"",
9202654012fSReza Sabdar 				    tsp->ts_dpos, nm);
9212654012fSReza Sabdar 
9222654012fSReza Sabdar 			if (pl + 1 + el > PATH_MAX) {
9232654012fSReza Sabdar 				/*
9242654012fSReza Sabdar 				 * The long paths were already encountered
9252654012fSReza Sabdar 				 * when processing non-dir entries in.
9262654012fSReza Sabdar 				 * traverse_level_nondir.
9272654012fSReza Sabdar 				 * We don't increase fss_longpath_err
9282654012fSReza Sabdar 				 * counter for them again here.
9292654012fSReza Sabdar 				 */
9302654012fSReza Sabdar 				NDMP_LOG(LOG_ERR, "Path %s/%s is too long.",
9312654012fSReza Sabdar 				    path, nm);
9322654012fSReza Sabdar 				if (STOP_ONLONG(ftp))
9332654012fSReza Sabdar 					rv = ENAMETOOLONG;
93484bf06e9SReza Sabdar 				free(efh.fh_fpath);
9352654012fSReza Sabdar 				continue;
9362654012fSReza Sabdar 			}
9372654012fSReza Sabdar 
93884bf06e9SReza Sabdar 			if (!S_ISDIR(est.st_mode))
9392654012fSReza Sabdar 				continue;
9402654012fSReza Sabdar 
9412654012fSReza Sabdar 			/*
9422654012fSReza Sabdar 			 * Call the callback function for the new
9432654012fSReza Sabdar 			 * directory found, then push the current
9442654012fSReza Sabdar 			 * directory on to the stack.  Then dive
9452654012fSReza Sabdar 			 * into the entry found.
9462654012fSReza Sabdar 			 */
9472654012fSReza Sabdar 			traverse_stats.fss_dir_calls++;
9482654012fSReza Sabdar 			en.tn_path = nm;
9492654012fSReza Sabdar 			en.tn_fh = &efh;
9502654012fSReza Sabdar 			en.tn_st = &est;
9512654012fSReza Sabdar 			rv = CALLBACK(&pn, &en);
9522654012fSReza Sabdar 
9532654012fSReza Sabdar 			if (rv < 0) {
9542654012fSReza Sabdar 				NEGATE(rv);
95584bf06e9SReza Sabdar 				free(efh.fh_fpath);
9562654012fSReza Sabdar 				break;
9572654012fSReza Sabdar 			}
9582654012fSReza Sabdar 			if (rv == FST_SKIP) {
9592654012fSReza Sabdar 				traverse_stats.fss_dir_skipped++;
96084bf06e9SReza Sabdar 				free(efh.fh_fpath);
9612654012fSReza Sabdar 				rv = 0;
9622654012fSReza Sabdar 				continue;
9632654012fSReza Sabdar 			}
9642654012fSReza Sabdar 
9652654012fSReza Sabdar 			/*
9662654012fSReza Sabdar 			 * Push the current directory on to the stack and
9672654012fSReza Sabdar 			 * dive into the entry found.
9682654012fSReza Sabdar 			 */
96984bf06e9SReza Sabdar 			if (cstack_push(sp, tsp, 0)) {
9702654012fSReza Sabdar 				rv = ENOMEM;
97184bf06e9SReza Sabdar 			} else {
9722654012fSReza Sabdar 				traverse_stats.fss_pushes++;
9732654012fSReza Sabdar 
9742654012fSReza Sabdar 				lp = tsp->ts_end;
9752654012fSReza Sabdar 				*tsp->ts_end = '/';
9762654012fSReza Sabdar 				(void) strcpy(tsp->ts_end + 1, nm);
9772654012fSReza Sabdar 
9782654012fSReza Sabdar 				tsp = new_tsp(path);
9792654012fSReza Sabdar 				if (!tsp)
9802654012fSReza Sabdar 					rv = ENOMEM;
9812654012fSReza Sabdar 				else {
9822654012fSReza Sabdar 					next_dir = 1;
9832654012fSReza Sabdar 					pl += el + 1;
9842654012fSReza Sabdar 					tsp->ts_fh = efh;
9852654012fSReza Sabdar 					tsp->ts_st = est;
9862654012fSReza Sabdar 					tsp->ts_ent = lp;
9872654012fSReza Sabdar 					pn.tn_fh = &tsp->ts_fh;
9882654012fSReza Sabdar 					pn.tn_st = &tsp->ts_st;
9892654012fSReza Sabdar 				}
9902654012fSReza Sabdar 			}
9912654012fSReza Sabdar 			break;
9922654012fSReza Sabdar 
9932654012fSReza Sabdar 		} while (rv == 0);
9942654012fSReza Sabdar 
9952654012fSReza Sabdar 		/*
9962654012fSReza Sabdar 		 * A new directory must be processed, go to the start of
9972654012fSReza Sabdar 		 * the loop, open it and process it.
9982654012fSReza Sabdar 		 */
9992654012fSReza Sabdar 		if (next_dir)
10002654012fSReza Sabdar 			continue;
10012654012fSReza Sabdar skip_dir:
100284bf06e9SReza Sabdar 		if (tsp) {
100384bf06e9SReza Sabdar 			free(tsp->ts_fh.fh_fpath);
10042654012fSReza Sabdar 			free(tsp);
100584bf06e9SReza Sabdar 		}
10062654012fSReza Sabdar 
10072654012fSReza Sabdar 		if (rv == SKIP_ENTRY)
10082654012fSReza Sabdar 			rv = 0;
10092654012fSReza Sabdar 
10102654012fSReza Sabdar 		if (rv == 0) {
10112654012fSReza Sabdar 			if (cstack_pop(sp, (void **)&tsp, (int *)NULL))
10122654012fSReza Sabdar 				break;
10132654012fSReza Sabdar 
10142654012fSReza Sabdar 			traverse_stats.fss_pops++;
10152654012fSReza Sabdar 
10162654012fSReza Sabdar 			if (VERBOSE(ftp))
10172654012fSReza Sabdar 				NDMP_LOG(LOG_DEBUG,
10182654012fSReza Sabdar 				    "Poped pl %d \"%s\"", pl, path);
10192654012fSReza Sabdar 
10202654012fSReza Sabdar 			*tsp->ts_end = '\0';
10212654012fSReza Sabdar 			pl = tsp->ts_end - path;
10222654012fSReza Sabdar 			pn.tn_fh = &tsp->ts_fh;
10232654012fSReza Sabdar 			pn.tn_st = &tsp->ts_st;
10242654012fSReza Sabdar 		}
10252654012fSReza Sabdar 	} while (rv == 0);
10262654012fSReza Sabdar 
10272654012fSReza Sabdar 	/*
10282654012fSReza Sabdar 	 * Pop and free all the remaining entries on the stack.
10292654012fSReza Sabdar 	 */
10302654012fSReza Sabdar 	while (!cstack_pop(sp, (void **)&tsp, (int *)NULL)) {
10312654012fSReza Sabdar 		traverse_stats.fss_stack_residue++;
10322654012fSReza Sabdar 
103384bf06e9SReza Sabdar 		free(tsp->ts_fh.fh_fpath);
10342654012fSReza Sabdar 		free(tsp);
10352654012fSReza Sabdar 	}
10362654012fSReza Sabdar end:
10372654012fSReza Sabdar 	free(darg.da_buf);
10382654012fSReza Sabdar 	cstack_delete(sp);
10392654012fSReza Sabdar 	return (rv);
10402654012fSReza Sabdar }
10412654012fSReza Sabdar 
10422654012fSReza Sabdar /*
10432654012fSReza Sabdar  * filecopy - Copy a file
10442654012fSReza Sabdar  *
10452654012fSReza Sabdar  * Parameters:
10462654012fSReza Sabdar  *  char *dest  - Destination path
10472654012fSReza Sabdar  *  char *src   - Source path
10482654012fSReza Sabdar  *
10492654012fSReza Sabdar  * Returns:
10502654012fSReza Sabdar  *  0    - No errors
10512654012fSReza Sabdar  *  #0   - Error occured
10522654012fSReza Sabdar  *		-4   - read/write error
10532654012fSReza Sabdar  *		-5   - source modified during copy
10542654012fSReza Sabdar  *
10552654012fSReza Sabdar  * Simplified version for Solaris
10562654012fSReza Sabdar  */
10572654012fSReza Sabdar #define	BUFSIZE	32768
10582654012fSReza Sabdar int
filecopy(char * dest,char * src)10592654012fSReza Sabdar filecopy(char *dest, char *src)
10602654012fSReza Sabdar {
10612654012fSReza Sabdar 	FILE *src_fh = 0;
10622654012fSReza Sabdar 	FILE *dst_fh = 0;
10632654012fSReza Sabdar 	struct stat64 src_attr;
10642654012fSReza Sabdar 	struct stat64 dst_attr;
10652654012fSReza Sabdar 	char *buf = 0;
10662654012fSReza Sabdar 	u_longlong_t bytes_to_copy;
10672654012fSReza Sabdar 	size_t nbytes;
10682654012fSReza Sabdar 	int file_copied = 0;
10692654012fSReza Sabdar 
10702654012fSReza Sabdar 	buf = ndmp_malloc(BUFSIZE);
10712654012fSReza Sabdar 	if (!buf)
10722654012fSReza Sabdar 		return (-1);
10732654012fSReza Sabdar 
10742654012fSReza Sabdar 	src_fh = fopen(src, "r");
10752654012fSReza Sabdar 	if (src_fh == 0) {
10762654012fSReza Sabdar 		free(buf);
10772654012fSReza Sabdar 		return (-2);
10782654012fSReza Sabdar 	}
10792654012fSReza Sabdar 
10802654012fSReza Sabdar 	dst_fh = fopen(dest, "w");
10812654012fSReza Sabdar 	if (dst_fh == NULL) {
10822654012fSReza Sabdar 		free(buf);
10832654012fSReza Sabdar 		(void) fclose(src_fh);
10842654012fSReza Sabdar 		return (-3);
10852654012fSReza Sabdar 	}
10862654012fSReza Sabdar 
10872654012fSReza Sabdar 	if (stat64(src, &src_attr) < 0) {
10882654012fSReza Sabdar 		free(buf);
10892654012fSReza Sabdar 		(void) fclose(src_fh);
10902654012fSReza Sabdar 		(void) fclose(dst_fh);
10912654012fSReza Sabdar 		return (-2);
10922654012fSReza Sabdar 	}
10932654012fSReza Sabdar 
10942654012fSReza Sabdar 	bytes_to_copy = src_attr.st_size;
10952654012fSReza Sabdar 	while (bytes_to_copy) {
10962654012fSReza Sabdar 		if (bytes_to_copy > BUFSIZE)
10972654012fSReza Sabdar 			nbytes = BUFSIZE;
10982654012fSReza Sabdar 		else
10992654012fSReza Sabdar 			nbytes = bytes_to_copy;
11002654012fSReza Sabdar 
11012654012fSReza Sabdar 		if ((fread(buf, nbytes, 1, src_fh) != 1) ||
11022654012fSReza Sabdar 		    (fwrite(buf, nbytes, 1, dst_fh) != 1))
11032654012fSReza Sabdar 			break;
11042654012fSReza Sabdar 		bytes_to_copy -= nbytes;
11052654012fSReza Sabdar 	}
11062654012fSReza Sabdar 
11072654012fSReza Sabdar 	(void) fclose(src_fh);
11082654012fSReza Sabdar 	(void) fclose(dst_fh);
11092654012fSReza Sabdar 
11102654012fSReza Sabdar 	if (bytes_to_copy > 0) {
11112654012fSReza Sabdar 		free(buf);
11122654012fSReza Sabdar 		/* short read/write, remove the partial file */
11132654012fSReza Sabdar 		return (-4);
11142654012fSReza Sabdar 	}
11152654012fSReza Sabdar 
11162654012fSReza Sabdar 	if (stat64(src, &dst_attr) < 0) {
11172654012fSReza Sabdar 		free(buf);
11182654012fSReza Sabdar 		return (-2);
11192654012fSReza Sabdar 	}
11202654012fSReza Sabdar 
11212654012fSReza Sabdar 	free(buf);
11222654012fSReza Sabdar 
11232654012fSReza Sabdar 	if (!file_copied)
11242654012fSReza Sabdar 		return (-5);	/* source modified during copy */
11252654012fSReza Sabdar 	else
11262654012f