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