OpenSolaris_b135/cmd/ndmpd/ndmp/ndmpd_tar3.c

Compare this file to the similar file:
Show the results in this format:

/*
 * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * BSD 3 Clause License
 *
 * Copyright (c) 2007, The Storage Networking Industry Association.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 	- Redistributions of source code must retain the above copyright
 *	  notice, this list of conditions and the following disclaimer.
 *
 * 	- Redistributions in binary form must reproduce the above copyright
 *	  notice, this list of conditions and the following disclaimer in
 *	  the documentation and/or other materials provided with the
 *	  distribution.
 *
 *	- Neither the name of The Storage Networking Industry Association (SNIA)
 *	  nor the names of its contributors may be used to endorse or promote
 *	  products derived from this software without specific prior written
 *	  permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <ctype.h>
#include <sys/socket.h>
#include <sys/acl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <cstack.h>
#include "ndmp.h"
#include "ndmpd.h"
#include <bitmap.h>
#include <traverse.h>


/*
 * Maximum length of the string-representation of u_longlong_t type.
 */
#define	QUAD_DECIMAL_LEN	20


/* Is Y=yes or T=true */
#define	IS_YORT(c)	(strchr("YT", toupper(c)))

/* Is F=file format (vs D=node-dir format) */
#define	IS_F(c)		(toupper(c) == 'F')

/*
 * If path is defined.
 */
#define	ISDEFINED(cp)	((cp) && *(cp))
#define	SHOULD_LBRBK(bpp)	(!((bpp)->bp_opr & TLM_OP_CHOOSE_ARCHIVE))

/*
 * Component boundary means end of path or on a '/'.  At this
 * point both paths should be on component boundary.
 */
#define	COMPBNDRY(p)	(!*(p) || (*p) == '/')

typedef struct bk_param_v3 {
	ndmpd_session_t *bp_session;
	ndmp_lbr_params_t *bp_nlp;
	tlm_job_stats_t *bp_js;
	tlm_cmd_t *bp_lcmd;
	tlm_commands_t *bp_cmds;
	tlm_acls_t *bp_tlmacl;
	int bp_opr;
	char *bp_tmp;
	char *bp_chkpnm;
	char **bp_excls;
	char *bp_unchkpnm;
} bk_param_v3_t;


/*
 * Multiple destination restore mode
 */
#define	MULTIPLE_DEST_DIRS 128

int multiple_dest_restore = 0;

/*
 * Plug-in module ops
 */
ndmp_plugin_t *ndmp_pl;

/*
 * NDMP exclusion list
 */
char **ndmp_excl_list = NULL;

/*
 * split_env
 *
 * Splits the string into list of sections separated by the
 * sep character.
 *
 * Parameters:
 *   envp (input) - the environment variable that should be broken
 *   sep (input) - the separator character
 *
 * Returns:
 *   Array of character pointers: On success.  The array is allocated
 *	as well as all its entries.  They all should be freed by the
 *	caller.
 *   NULL: on error
 */
static char **
split_env(char *envp, char sep)
{
	char *bp, *cp, *ep;
	char *save;
	char **cpp;
	int n;

	if (!envp)
		return (NULL);

	while (isspace(*envp))
		envp++;

	if (!*envp)
		return (NULL);

	bp = save = strdup(envp);
	if (!bp)
		return (NULL);

	/*
	 * Since the env variable is not empty, it contains at least one
	 * component
	 */
	n = 1;
	while ((cp = strchr(bp, sep))) {
		if (cp > save && *(cp-1) != '\\')
			n++;

		bp = cp + 1;
	}

	n++; /* for the terminating NULL pointer */
	cpp = ndmp_malloc(sizeof (char *) * n);
	if (!cpp) {
		free(save);
		return (NULL);
	}

	(void) memset(cpp, 0, n * sizeof (char *));
	n = 0;
	cp = bp = ep = save;
	while (*cp)
		if (*cp == sep) {
			*ep = '\0';
			if (strlen(bp) > 0) {
				cpp[n] = strdup(bp);
				if (!cpp[n++]) {
					tlm_release_list(cpp);
					cpp = NULL;
					break;
				}
			}
			ep = bp = ++cp;
		} else if (*cp == '\\') {
			++cp;
			if (*cp == 'n') {	/* "\n" */
				*ep++ = '\n';
				cp++;
			} else if (*cp == 't') {	/* "\t" */
				*ep++ = '\t';
				cp++;
			} else
				*ep++ = *cp++;
		} else
			*ep++ = *cp++;

	*ep = '\0';
	if (cpp) {
		if (strlen(bp) > 0) {
			cpp[n] = strdup(bp);
			if (!cpp[n++]) {
				tlm_release_list(cpp);
				cpp = NULL;
			} else
				cpp[n] = NULL;
		}

		if (n == 0 && cpp != NULL) {
			tlm_release_list(cpp);
			cpp = NULL;
		}
	}

	free(save);
	return (cpp);
}


/*
 * prl
 *
 * Print the array of character pointers passed to it.  This is
 * used for debugging purpose.
 *
 * Parameters:
 *   lpp (input) - pointer to the array of strings
 *
 * Returns:
 *   void
 */
static void
prl(char **lpp)
{
	if (!lpp) {
		NDMP_LOG(LOG_DEBUG, "empty");
		return;
	}

	while (*lpp)
		NDMP_LOG(LOG_DEBUG, "\"%s\"", *lpp++);
}


/*
 * inlist
 *
 * Looks through all the strings of the array to see if the ent
 * matches any of the strings.  The strings are patterns.
 *
 * Parameters:
 *   lpp (input) - pointer to the array of strings
 *   ent (input) - the entry to be matched
 *
 * Returns:
 *   TRUE: if there is a match
 *   FALSE: invalid argument or no match
 */
static boolean_t
inlist(char **lpp, char *ent)
{
	if (!lpp || !ent) {
		NDMP_LOG(LOG_DEBUG, "empty list");
		return (FALSE);
	}

	while (*lpp) {
		/*
		 * Fixing the sync_sort NDMPV3 problem, it sends the inclusion
		 * like "./" which we should skip the "./"
		 */
		char *pattern = *lpp;
		if (strncmp(pattern, "./", 2) == 0)
			pattern += 2;

		NDMP_LOG(LOG_DEBUG, "pattern %s, ent %s", pattern, ent);

		if (match(pattern, ent)) {
			NDMP_LOG(LOG_DEBUG, "match(%s,%s)", pattern, ent);
			return (TRUE);
		}
		lpp++;
	}

	NDMP_LOG(LOG_DEBUG, "no match");
	return (FALSE);
}


/*
 * inexl
 *
 * Checks if the entry is in the list.  This is used for exclusion
 * list.  If the exclusion list is empty, FALSE should be returned
 * showing that nothing should be excluded by default.
 *
 * Parameters:
 *   lpp (input) - pointer to the array of strings
 *   ent (input) - the entry to be matched
 *
 * Returns:
 *   TRUE: if there is a match
 *   FALSE: invalid argument or no match
 *
 */
static boolean_t
inexl(char **lpp, char *ent)
{
	if (!lpp || !ent)
		return (FALSE);

	return (inlist(lpp, ent));
}


/*
 * ininc
 *
 * Checks if the entry is in the list.  This is used for inclusion
 * list.  If the inclusion list is empty, TRUE should be returned
 * showing that everything should be included by default.
 *
 * Parameters:
 *   lpp (input) - pointer to the array of strings
 *   ent (input) - the entry to be matched
 *
 * Returns:
 *   TRUE: if there is a match or the list is empty
 *   FALSE: no match
 */
static boolean_t
ininc(char **lpp, char *ent)
{
	if (!lpp || !ent || !*ent)
		return (TRUE);

	return (inlist(lpp, ent));
}


/*
 * setupsels
 *
 * Set up the selection list for Local B/R functions.  A new array of
 * "char *" is created and the pointers point to the original paths of
 * the Nlist.
 *
 * Parameters:
 *   session (input) - pointer to the session
 *   params (input) - pointer to the parameters structure
 *   nlp (input) - pointer to the nlp structure
 *   index(input) - If not zero is the DAR entry position
 *
 * Returns:
 *   list pointer: on success
 *   NULL: on error
 */
/*ARGSUSED*/
char **
setupsels(ndmpd_session_t *session, ndmpd_module_params_t *params,
    ndmp_lbr_params_t *nlp, int index)
{
	char **lpp, **save;
	int i, n;
	int len;
	int start, end;
	mem_ndmp_name_v3_t *ep;

	n = session->ns_data.dd_nlist_len;

	save = lpp = ndmp_malloc(sizeof (char *) * (n + 1));
	if (!lpp) {
		MOD_LOGV3(params, NDMP_LOG_ERROR, "Insufficient memory.\n");
		return (NULL);
	}

	if (index) { /* DAR, just one entry */
		/*
		 * We have to setup a list of strings that will not match any
		 * file. One DAR entry will be added in the right position later
		 * in this function.
		 * When the match is called from tar_getdir the
		 * location of the selection that matches the entry is
		 * important
		 */
		for (i = 0; i < n; ++i)
			*(lpp+i) = " ";
		n = 1;
		start = index-1;
		end = start+1;
		lpp += start; /* Next selection entry will be in lpp[start] */
	} else {
		start = 0;
		end = n;
	}

	for (i = start; i < end; i++) {
		ep = (mem_ndmp_name_v3_t *)MOD_GETNAME(params, i);
		if (!ep)
			continue;

		/*
		 * Check for clients that send original path as "."(like
		 * CA products). In this situation opath is something like
		 * "/v1/." and we should change it to "/v1/"
		 */
		len = strlen(ep->nm3_opath);
		if (len > 1 && ep->nm3_opath[len-2] == '/' &&
		    ep->nm3_opath[len-1] == '.') {
			ep->nm3_opath[len-1] = '\0';
			NDMP_LOG(LOG_DEBUG,
			    "nm3_opath changed from %s. to %s",
			    ep->nm3_opath, ep->nm3_opath);
		}
		*lpp++ = ep->nm3_opath;
	}

	/* list termination indicator is a null pointer */
	*lpp = NULL;

	return (save);
}


/*
 * mkrsp
 *
 * Make Restore Path.
 * It gets a path, a selection (with which the path has matched) a new
 * name and makes a new name for the path.
 * All the components of the path and the selection are skipped as long
 * as they are the same.  If either of the path or selection are not on
 * a component boundary, the match was reported falsefully and no new name
 * is generated(Except the situation in which both path and selection
 * end with trailing '/' and selection is the prefix of the path).
 * Otherwise, the remaining of the path is appended to the
 * new name.  The result is saved in the buffer passed.
 *
 * Parameters:
 *   bp (output) - pointer to the result buffer
 *   pp (input) - pointer to the path
 *   sp (input) - pointer to the selection
 *   np (input) - pointer to the new name
 *
 * Returns:
 *   pointer to the bp: on success
 *   NULL: otherwise
 */
char *
mkrsp(char *bp, char *pp, char *sp, char *np)
{
	if (!bp || !pp)
		return (NULL);


	pp += strspn(pp, "/");
	if (sp) {
		sp += strspn(sp, "/");

		/* skip as much as match */
		while (*sp && *pp && *sp == *pp) {
			sp++;
			pp++;
		}

		if (!COMPBNDRY(pp) || !COMPBNDRY(sp))
			/* An exception to the boundary rule */
			/* (!(!*sp && (*(pp - 1)) == '/')) */
			if (*sp || (*(pp - 1)) != '/')
				return (NULL);

		/* if pp shorter than sp, it should not be restored */
		if (!*pp && *sp) {
			sp += strspn(sp, "/");
			if (strlen(sp) > 0)
				return (NULL);
		}
	}

	if (np)
		np += strspn(np, "/");
	else
		np = "";

	if (!tlm_cat_path(bp, np, pp)) {
		NDMP_LOG(LOG_ERR, "Restore path too long %s/%s.", np, pp);
		return (NULL);
	}

	return (bp);
}


/*
 * mknewname
 *
 * This is used as callback for creating the restore path. This function
 * can handle both single destination and multiple restore paths.
 *
 * Make up the restore destination path for a particular file/directory, path,
 * based on nm3_opath and nm3_dpath.  path should have matched nm3_opath
 * in some way.
 */
char *
mknewname(struct rs_name_maker *rnp, char *buf, int idx, char *path)
{
	char *rv;
	ndmp_lbr_params_t *nlp;
	mem_ndmp_name_v3_t *ep;

	rv = NULL;
	if (!buf) {
		NDMP_LOG(LOG_DEBUG, "buf is NULL");
	} else if (!path) {
		NDMP_LOG(LOG_DEBUG, "path is NULL");
	} else if ((nlp = rnp->rn_nlp) == 0) {
		NDMP_LOG(LOG_DEBUG, "rnp->rn_nlp is NULL");
	} else if (!nlp->nlp_params) {
		NDMP_LOG(LOG_DEBUG, "nlp->nlp_params is NULL");
	} else
		if (!ndmp_full_restore_path) {
			if (idx < 0 || idx >= (int)nlp->nlp_nfiles) {
				NDMP_LOG(LOG_DEBUG,
				    "Invalid idx %d range (0, %d)",
				    idx, nlp->nlp_nfiles);
			} else if (!(ep = (mem_ndmp_name_v3_t *)MOD_GETNAME(
			    nlp->nlp_params, idx))) {
				NDMP_LOG(LOG_DEBUG,
				    "nlist entry %d is NULL", idx);
			} else {
				rv = mkrsp(buf, path, ep->nm3_opath,
				    ep->nm3_dpath);

				NDMP_LOG(LOG_DEBUG,
				    "idx %d org \"%s\" dst \"%s\"",
				    idx, ep->nm3_opath, ep->nm3_dpath);
				if (rv) {
					NDMP_LOG(LOG_DEBUG,
					    "path \"%s\": \"%s\"", path, rv);
				} else {
					NDMP_LOG(LOG_DEBUG,
					    "path \"%s\": NULL", path);
				}
			}
		} else {
			if (!tlm_cat_path(buf, nlp->nlp_restore_path, path)) {
				NDMP_LOG(LOG_ERR, "Path too long %s/%s.",
				    nlp->nlp_restore_path, path);
				rv = NULL;
			} else {
				rv = buf;
				NDMP_LOG(LOG_DEBUG,
				    "path \"%s\": \"%s\"", path, rv);
			}
		}

	return (rv);
}


/*
 * chopslash
 *
 * Remove the slash from the end of the given path
 */
static void
chopslash(char *cp)
{
	int ln;

	if (!cp || !*cp)
		return;

	ln = strlen(cp);
	cp += ln - 1; /* end of the string */
	while (ln > 0 && *cp == '/') {
		*cp-- = '\0';
		ln--;
	}
}


/*
 * joinpath
 *
 * Join two given paths
 */
static char *
joinpath(char *bp, char *pp, char *np)
{
	if (pp && *pp) {
		if (np && *np)
			(void) tlm_cat_path(bp, pp, np);
		else
			(void) strlcpy(bp, pp, TLM_MAX_PATH_NAME);
	} else {
		if (np && *np)
			(void) strlcpy(bp, np, TLM_MAX_PATH_NAME);
		else
			bp = NULL;
	}

	return (bp);
}


/*
 * voliswr
 *
 * Is the volume writable?
 */
static int
voliswr(char *path)
{
	int rv;

	if (!path)
		return (0);

	rv = !fs_is_rdonly(path) && !fs_is_chkpntvol(path);
	NDMP_LOG(LOG_DEBUG, "%d path \"%s\"", rv, path);
	return (rv);

}


/*
 * get_bk_path_v3
 *
 * Get the backup path from the NDMP environment variables.
 *
 * Parameters:
 *   params (input) - pointer to the parameters structure.
 *
 * Returns:
 *   The backup path: if anything is specified
 *   NULL: Otherwise
 */
char *
get_bk_path_v3(ndmpd_module_params_t *params)
{
	char *bkpath;

	bkpath = MOD_GETENV(params, "PREFIX");
	if (!bkpath)
		bkpath = MOD_GETENV(params, "FILESYSTEM");


	if (!bkpath) {
		MOD_LOGV3(params, NDMP_LOG_ERROR,
		    "Backup path not defined.\n");
	} else {
		NDMP_LOG(LOG_DEBUG, "bkpath: \"%s\"", bkpath);
	}

	return (bkpath);
}


/*
 * is_valid_backup_dir_v3
 *
 * Checks the validity of the backup path.  Backup path should
 * have the following characteristics to be valid:
 *	1) It should be an absolute path.
 *	2) It should be a directory.
 *	3) It should not be checkpoint root directory
 *	4) If the file system is read-only, the backup path
 *	    should be a checkpointed path.  Checkpoint cannot
 *	    be created on a read-only file system.
 *
 * Parameters:
 *   params (input) - pointer to the parameters structure.
 *   bkpath (input) - the backup path
 *
 * Returns:
 *   TRUE: if everything's OK
 *   FALSE: otherwise.
 */
static boolean_t
is_valid_backup_dir_v3(ndmpd_module_params_t *params, char *bkpath)
{
	char *msg;
	struct stat64 st;

	if (*bkpath != '/') {
		MOD_LOGV3(params, NDMP_LOG_ERROR,
		    "Relative backup path not allowed \"%s\".\n", bkpath);
		return (FALSE);
	}
	if (stat64(bkpath, &st) < 0) {
		msg = strerror(errno);
		MOD_LOGV3(params, NDMP_LOG_ERROR, "\"%s\" %s.\n",
		    bkpath, msg);
		return (FALSE);
	}
	if (!S_ISDIR(st.st_mode)) {
		/* only directories can be specified as the backup path */
		MOD_LOGV3(params, NDMP_LOG_ERROR,
		    "\"%s\" is not a directory.\n", bkpath);
		return (FALSE);
	}
	if (ndmp_is_chkpnt_root(bkpath)) {
		/* it is the chkpnt root directory */
		MOD_LOGV3(params, NDMP_LOG_ERROR,
		    "\"%s\" is a checkpoint root directory.\n", bkpath);
		return (FALSE);
	}
	if (fs_is_rdonly(bkpath) && !fs_is_chkpntvol(bkpath) &&
	    fs_is_chkpnt_enabled(bkpath)) {
		/* it is not a chkpnted path */
		MOD_LOGV3(params, NDMP_LOG_ERROR,
		    "\"%s\" is not a checkpointed path.\n", bkpath);
		return (FALSE);
	}

	return (TRUE);
}


/*
 * log_date_token_v3
 *
 * Log the token sequence number and also the date of the
 * last backup for token-based backup in the system log
 * and also send them as normal log to the client.
 *
 * Parameters:
 *   params (input) - pointer to the parameters structure
 *   nlp (input) - pointer to the nlp structure
 *
 * Returns:
 *   void
 */
static void
log_date_token_v3(ndmpd_module_params_t *params, ndmp_lbr_params_t *nlp)
{
	MOD_LOGV3(params, NDMP_LOG_NORMAL, "Token sequence counter: %d.\n",
	    nlp->nlp_tokseq);

	MOD_LOGV3(params, NDMP_LOG_NORMAL, "Date of the last backup: %s.\n",
	    cctime(&nlp->nlp_tokdate));

	if (nlp->nlp_dmpnm) {
		MOD_LOGV3(params, NDMP_LOG_NORMAL,
		    "Backup date log file name: \"%s\".\n", nlp->nlp_dmpnm);
	}
}


/*
 * log_lbr_bk_v3
 *
 * Log the backup level and data of the backup for LBR-type
 * backup in the system log and also send them as normal log
 * to the client.
 *
 * Parameters:
 *   params (input) - pointer to the parameters structure
 *   nlp (input) - pointer to the nlp structure
 *
 * Returns:
 *   void
 */
static void
log_lbr_bk_v3(ndmpd_module_params_t *params, ndmp_lbr_params_t *nlp)
{
	MOD_LOGV3(params, NDMP_LOG_NORMAL,
	    "Date of this level '%c': %s.\n", nlp->nlp_clevel,
	    cctime(&nlp->nlp_cdate));

	if (nlp->nlp_dmpnm) {
		MOD_LOGV3(params, NDMP_LOG_NORMAL,
		    "Backup date log file name: \"%s\".\n", nlp->nlp_dmpnm);
	}
}


/*
 * log_level_v3
 *
 * Log the backup level and date of the last and the current
 * backup for level-type backup in the system log and also
 * send them as normal log to the client.
 *
 * Parameters:
 *   params (input) - pointer to the parameters structure
 *   nlp (input) - pointer to the nlp structure
 *
 * Returns:
 *   void
 */
static void
log_level_v3(ndmpd_module_params_t *params, ndmp_lbr_params_t *nlp)
{
	MOD_LOGV3(params, NDMP_LOG_NORMAL,
	    "Date of the last level '%u': %s.\n", nlp->nlp_llevel,
	    cctime(&nlp->nlp_ldate));

	MOD_LOGV3(params, NDMP_LOG_NORMAL,
	    "Date of this level '%u': %s.\n", nlp->nlp_clevel,
	    cctime(&nlp->nlp_cdate));

	MOD_LOGV3(params, NDMP_LOG_NORMAL, "Update: %s.\n",
	    NDMP_TORF(NLP_ISSET(nlp, NLPF_UPDATE)));
}


/*
 * log_bk_params_v3
 *
 * Dispatcher function which calls the appropriate function
 * for logging the backup date and level in the system log
 * and also send them as normal log message to the client.
 *
 * Parameters:
 *   session (input) - pointer to the session
 *   params (input) - pointer to the parameters structure
 *   nlp (input) - pointer to the nlp structure
 *
 * Returns:
 *   void
 */
static void
log_bk_params_v3(ndmpd_session_t *session, ndmpd_module_params_t *params,
    ndmp_lbr_params_t *nlp)
{
	MOD_LOGV3(params, NDMP_LOG_NORMAL, "Backing up \"%s\".\n",
	    nlp->nlp_backup_path);

	if (session->ns_mover.md_data_addr.addr_type == NDMP_ADDR_LOCAL)
		MOD_LOGV3(params, NDMP_LOG_NORMAL,
		    "Tape record size: %d.\n",
		    session->ns_mover.md_record_size);

	MOD_LOGV3(params, NDMP_LOG_NORMAL, "File history: %c.\n",
	    NDMP_YORN(NLP_ISSET(nlp, NLPF_FH)));

	if (NLP_ISSET(nlp, NLPF_TOKENBK))
		log_date_token_v3(params, nlp);
	else if (NLP_ISSET(nlp, NLPF_LBRBK))
		log_lbr_bk_v3(params, nlp);
	else if (NLP_ISSET(nlp, NLPF_LEVELBK))
		log_level_v3(params, nlp);
	else {
		MOD_LOGV3(params, NDMP_LOG_ERROR,
		    "Internal error: backup level not defined for \"%s\".\n",
		    nlp->nlp_backup_path);
	}
}


/*
 * get_update_env_v3
 *
 * Is the UPDATE environment variable specified?  If it is
 * the corresponding flag is set in the flags field of the
 * nlp structure, otherwise the flag is cleared.
 *
 * Parameters:
 *   params (input) - pointer to the parameters structure
 *   nlp (input) - pointer to the nlp structure
 *
 * Returns:
 *   void
 */
static void
get_update_env_v3(ndmpd_module_params_t *params, ndmp_lbr_params_t *nlp)
{
	char *envp;

	envp = MOD_GETENV(params, "UPDATE");
	if (!envp) {
		NLP_SET(nlp, NLPF_UPDATE);
		NDMP_LOG(LOG_DEBUG,
		    "env(UPDATE) not defined, default to TRUE");
	} else {
		NDMP_LOG(LOG_DEBUG, "env(UPDATE): \"%s\"", envp);
		if (IS_YORT(*envp))
			NLP_SET(nlp, NLPF_UPDATE);
		else
			NLP_UNSET(nlp, NLPF_UPDATE);
	}
}


/*
 * get_hist_env_v3
 *
 * Is backup history requested?  If it is, the corresponding
 * flag is set in the flags field of the nlp structure, otherwise
 * the flag is cleared.
 *
 * Parameters:
 *   params (input) - pointer to the parameters structure
 *   nlp (input) - pointer to the nlp structure
 *
 * Returns:
 *   void
 */
static void
get_hist_env_v3(ndmpd_module_params_t *params, ndmp_lbr_params_t *nlp)
{
	char *envp;

	envp = MOD_GETENV(params, "HIST");
	if (!envp) {
		NDMP_LOG(LOG_DEBUG, "env(HIST) not defined");
		NLP_UNSET(nlp, NLPF_FH);
	} else {
		NDMP_LOG(LOG_DEBUG, "env(HIST): \"%s\"", envp);
		if (IS_YORT(*envp) || IS_F(*envp))
			NLP_SET(nlp, NLPF_FH);
		else
			NLP_UNSET(nlp, NLPF_FH);

		/* Force file format if specified */
		if (IS_F(*envp)) {
			params->mp_file_history_path_func =
			    ndmpd_api_file_history_file_v3;
			params->mp_file_history_dir_func = 0;
			params->mp_file_history_node_func = 0;
		}
	}
}


/*
 * get_exc_env_v3
 *
 * Gets the EXCLUDE environment variable and breaks it
 * into strings.  The separator of the EXCLUDE environment
 * variable is the ',' character.
 *
 * Parameters:
 *   params (input) - pointer to the parameters structure
 *   nlp (input) - pointer to the nlp structure
 *
 * Returns:
 *   void
 */
static void
get_exc_env_v3(ndmpd_module_params_t *params, ndmp_lbr_params_t *nlp)
{
	char *envp;

	envp = MOD_GETENV(params, "EXCLUDE");
	if (!envp) {
		NDMP_LOG(LOG_DEBUG, "env(EXCLUDE) not defined");
		nlp->nlp_exl = NULL;
	} else {
		NDMP_LOG(LOG_DEBUG, "env(EXCLUDE): \"%s\"", envp);
		nlp->nlp_exl = split_env(envp, ',');
		prl(nlp->nlp_exl);
	}
}


/*
 * get_inc_env_v3
 *
 * Gets the FILES environment variable that shows which files
 * should be backed up, and breaks it into strings.  The
 * separator of the FILES environment variable is the space
 * character.
 *
 * Parameters:
 *   params (input) - pointer to the parameters structure
 *   nlp (input) - pointer to the nlp structure
 *
 * Returns:
 *   void
 */
static void
get_inc_env_v3(ndmpd_module_params_t *params, ndmp_lbr_params_t *nlp)
{
	char *envp;

	envp = MOD_GETENV(params, "FILES");
	if (!envp) {
		NDMP_LOG(LOG_DEBUG, "env(FILES) not defined");
		nlp->nlp_inc = NULL;
	} else {
		NDMP_LOG(LOG_DEBUG, "env(FILES): \"%s\"", envp);
		nlp->nlp_inc = split_env(envp, ' ');
		prl(nlp->nlp_inc);
	}
}


/*
 * get_direct_env_v3
 *
 * Gets the DIRECT environment variable that shows if the fh_info should
 * be sent to the client or not.
 *
 * Parameters:
 *   params (input) - pointer to the parameters structure
 *   nlp (input) - pointer to the nlp structure
 *
 * Returns:
 *   void
 */
static void
get_direct_env_v3(ndmpd_module_params_t *params, ndmp_lbr_params_t *nlp)
{
	char *envp;

	/*
	 * We should send the fh_info to the DMA, unless it is specified
	 * in the request that we should not send fh_info.
	 * At the moment we do not support DAR on directories, so if the user
	 * needs to restore a directory they should disable the DAR.
	 */
	if (params->mp_operation == NDMP_DATA_OP_RECOVER && !ndmp_dar_support) {
		NDMP_LOG(LOG_DEBUG, "Direct Access Restore Disabled");
		NLP_UNSET(nlp, NLPF_DIRECT);
		MOD_LOGV3(params, NDMP_LOG_NORMAL,
		    "DAR is disabled. Running Restore without DAR");
		return;
	}

	/*
	 * Regardless of whether DIRECT is defined at backup time we send
	 * back the fh_info, for some clients do not use get_backup_attrs.
	 * If operation is restore we have to unset the DIRECT, for
	 * some clients do not set the MOVER window.
	 */
	if (params->mp_operation == NDMP_DATA_OP_BACKUP) {
		NDMP_LOG(LOG_DEBUG, "backup default env(DIRECT): YES");
		NLP_SET(nlp, NLPF_DIRECT);
	} else {

		envp = MOD_GETENV(params, "DIRECT");
		if (!envp) {
			NDMP_LOG(LOG_DEBUG, "env(DIRECT) not defined");
			NLP_UNSET(nlp, NLPF_DIRECT);
		} else {
			NDMP_LOG(LOG_DEBUG, "env(DIRECT): \"%s\"", envp);
			if (IS_YORT(*envp)) {
				NLP_SET(nlp, NLPF_DIRECT);
				NDMP_LOG(LOG_DEBUG,
				    "Direct Access Restore Enabled");
			} else {
				NLP_UNSET(nlp, NLPF_DIRECT);
				NDMP_LOG(LOG_DEBUG,
				    "Direct Access Restore Disabled");
			}
		}
	}

	if (NLP_ISSET(nlp, NLPF_DIRECT)) {
		if (params->mp_operation == NDMP_DATA_OP_BACKUP)
			MOD_LOGV3(params, NDMP_LOG_NORMAL,
			    "Direct Access Restore information is supported");
		else
			MOD_LOGV3(params, NDMP_LOG_NORMAL,
			    "Running Restore with Direct Access Restore");
	} else {
		if (params->mp_operation == NDMP_DATA_OP_BACKUP)
			MOD_LOGV3(params, NDMP_LOG_NORMAL,
			    "Direct Access Restore is not supported");
		else
			MOD_LOGV3(params, NDMP_LOG_NORMAL,
			    "Running Restore without Direct Access Restore");
	}
}


/*
 * get_date_token_v3
 *
 * Parse the token passed as the argument.  Evaluate it and
 * issue any warning or error if needed.  Save the date and
 * token sequence in the nlp structure fields.  The sequence
 * number in the token should be less than hard-limit.  If
 * it's between soft and hard limit, a warning is issued.
 * There is a configurable limit which should be less than
 * the soft-limit saved in ndmp_max_tok_seq variable.
 *
 * The NLPF_TOKENBK flag is set in the nlp flags field to
 * show that the backup type is token-based.
 *
 * Parameters:
 *   params (input) - pointer to the parameters structure
 *   nlp (input) - pointer to the nlp structure
 *   basedate (input) - the value of the BASE_DATE environment
 *	variable.
 *
 * Returns:
 *   NDMP_NO_ERR: on success
 *   != NDMP_NO_ERR: Otherwise
 *
 */
static ndmp_error
get_date_token_v3(ndmpd_module_params_t *params, ndmp_lbr_params_t *nlp,
    char *basedate)
{
	char *endp;
	uint_t seq;
	ndmp_error rv;
	time_t tstamp;
	u_longlong_t tok;

	if (!params || !nlp || !basedate || !*basedate)
		return (NDMP_ILLEGAL_ARGS_ERR);

	if (MOD_GETENV(params, "LEVEL")) {
		MOD_LOGV3(params, NDMP_LOG_WARNING,
		    "Both BASE_DATE and LEVEL environment variables "
		    "defined.\n");
		MOD_LOGCONTV3(params, NDMP_LOG_WARNING,
		    "BASE_DATE is being used for this backup.\n");
	}

	tok = strtoll(basedate, &endp, 10);
	if (endp == basedate) {
		MOD_LOGV3(params, NDMP_LOG_ERROR,
		    "Invalid BASE_DATE environment variable: \"%s\".\n",
		    basedate);
		return (NDMP_ILLEGAL_ARGS_ERR);
	}

	tstamp = tok & 0xffffffff;
	seq = (tok >> 32) & 0xffffffff;
	NDMP_LOG(LOG_DEBUG, "basedate \"%s\" %lld seq %u tstamp %u",
	    basedate, tok, seq, tstamp);

	if ((int)seq > ndmp_get_max_tok_seq()) {
		rv = NDMP_ILLEGAL_ARGS_ERR;
		MOD_LOGV3(params, NDMP_LOG_ERROR,
		    "The sequence counter of the token exceeds the "
		    "maximum permitted value.\n");
		MOD_LOGCONTV3(params, NDMP_LOG_ERROR,
		    "Token sequence: %u, maxiumum value: %u.\n",
		    seq, ndmp_get_max_tok_seq());
	} else if (seq >= NDMP_TOKSEQ_HLIMIT) {
		rv = NDMP_ILLEGAL_ARGS_ERR;
		MOD_LOGV3(params, NDMP_LOG_ERROR,
		    "The sequence counter the of token exceeds the "
		    "hard-limit.\n");
		MOD_LOGCONTV3(params, NDMP_LOG_ERROR,
		    "Token sequence: %u, hard-limit: %u.\n",
		    seq, NDMP_TOKSEQ_HLIMIT);
	} else {
		rv = NDMP_NO_ERR;
		/*
		 * Issue a warning if the seq is equal to the maximum
		 * permitted seq number or equal to the soft-limit.
		 */
		if (seq == NDMP_TOKSEQ_SLIMIT) {
			MOD_LOGV3(params, NDMP_LOG_WARNING,
			    "The sequence counter of the token has reached "
			    "the soft-limit.\n");
			MOD_LOGCONTV3(params, NDMP_LOG_WARNING,
			    "Token sequence: %u, soft-limit: %u.\n",
			    seq, NDMP_TOKSEQ_SLIMIT);
		} else if ((int)seq == ndmp_get_max_tok_seq()) {
			MOD_LOGV3(params, NDMP_LOG_WARNING,
			    "The sequence counter of the token has reached "
			    "the maximum permitted value.\n");
			MOD_LOGCONTV3(params, NDMP_LOG_WARNING,
			    "Token sequence: %u, maxiumum value: %u.\n",
			    seq, ndmp_get_max_tok_seq());
		}

		/*
		 * The current seq is equal to the seq field of the
		 * token.  It will be increased after successful backup
		 * before setting the DUMP_DATE environment variable.
		 */
		nlp->nlp_dmpnm = MOD_GETENV(params, "DMP_NAME");
		NLP_SET(nlp, NLPF_TOKENBK);
		NLP_UNSET(nlp, NLPF_LEVELBK);
		NLP_UNSET(nlp, NLPF_LBRBK);
		nlp->nlp_tokseq = seq;
		nlp->nlp_tokdate = tstamp;
		/*
		 * The value of nlp_cdate will be set to the checkpoint
		 * creation time after it is created.
		 */
	}

	return (rv);
}


/*
 * get_lbr_bk_v3
 *
 * Sets the level fields of the nlp structures for
 * LBR-type backup.  The NLPF_LBRBK flag of the
 * nlp flags is also set to show the backup type.
 *
 * Parameters:
 *   params (input) - pointer to the parameters structure
 *   nlp (input) - pointer to the nlp structure
 *   type (input) - the backup level: 'F', 'A', 'I', 'D' or
 *	their lower-case values.
 *
 * Returns:
 *   NDMP_NO_ERR: on success
 *   != NDMP_NO_ERR: Otherwise
 */
static ndmp_error
get_lbr_bk_v3(ndmpd_module_params_t *params, ndmp_lbr_params_t *nlp, char *type)
{
	if (!params || !nlp || !type || !*type)
		return (NDMP_ILLEGAL_ARGS_ERR);

	NLP_SET(nlp, NLPF_LBRBK);
	NLP_UNSET(nlp, NLPF_TOKENBK);
	NLP_UNSET(nlp, NLPF_LEVELBK);
	nlp->nlp_dmpnm = MOD_GETENV(params, "DMP_NAME");
	nlp->nlp_llevel = toupper(*type);
	nlp->nlp_ldate = (time_t)0;
	nlp->nlp_clevel = nlp->nlp_llevel;
	(void) time(&nlp->nlp_cdate);

	return (NDMP_NO_ERR);
}


/*
 * get_backup_level_v3
 *
 * Gets the backup level from the environment variables.  If
 * BASE_DATE is specified, it will be used, otherwise LEVEL
 * will be used.  If neither is specified, LEVEL = '0' is
 * assumed.
 *
 * Parameters:
 *   params (input) - pointer to the parameters structure
 *   nlp (input) - pointer to the nlp structure
 *
 * Returns:
 *   NDMP_NO_ERR: on success
 *   != NDMP_NO_ERR: Otherwise
 */
static ndmp_error
get_backup_level_v3(ndmpd_module_params_t *params, ndmp_lbr_params_t *nlp)
{
	char *envp;
	ndmp_error rv;

	/*
	 * If the BASE_DATE env variable is specified use it, otherwise
	 * look to see if LEVEL is specified.  If LEVEL is not
	 * specified either, backup level '0' must be made. Level backup
	 * does not clear the archive bit.
	 *
	 * If LEVEL environment varaible is specified, values for
	 * 'F', 'D', 'I' and 'A' (for 'Full', 'Differential',
	 * 'Incremental', and 'Archive' is checked first.  Then
	 * level '0' to '9' will be checked.
	 *
	 * LEVEL environment variable can hold only one character.
	 * If its length is longer than 1, an error is returned.
	 */
	envp = MOD_GETENV(params, "BASE_DATE");
	if (envp)
		return (get_date_token_v3(params, nlp, envp));


	envp = MOD_GETENV(params, "LEVEL");
	if (!envp) {
		NDMP_LOG(LOG_DEBUG, "env(LEVEL) not defined, default to 0");
		NLP_SET(nlp, NLPF_LEVELBK);
		NLP_UNSET(nlp, NLPF_LBRBK);
		NLP_UNSET(nlp, NLPF_TOKENBK);
		nlp->nlp_llevel = 0;
		nlp->nlp_ldate = 0;
		nlp->nlp_clevel = 0;
		/*
		 * The value of nlp_cdate will be set to the checkpoint
		 * creation time after it is created.
		 */
		return (NDMP_NO_ERR);
	}

	if (*(envp+1) != '\0') {
		MOD_LOGV3(params, NDMP_LOG_ERROR,
		    "Invalid backup level \"%s\".\n", envp);
		return (NDMP_ILLEGAL_ARGS_ERR);
	}

	if (IS_LBR_BKTYPE(*envp))
		return (get_lbr_bk_v3(params, nlp, envp));

	if (!isdigit(*envp)) {
		MOD_LOGV3(params, NDMP_LOG_ERROR,
		    "Invalid backup level \"%s\".\n", envp);
		return (NDMP_ILLEGAL_ARGS_ERR);
	}

	NLP_SET(nlp, NLPF_LEVELBK);
	NLP_UNSET(nlp, NLPF_LBRBK);
	NLP_UNSET(nlp, NLPF_TOKENBK);
	nlp->nlp_llevel = *envp - '0';
	nlp->nlp_ldate = 0;
	nlp->nlp_clevel = nlp->nlp_llevel;
	/*
	 * The value of nlp_cdate will be set to the checkpoint
	 * creation time after it is created.
	 */
	if (ndmpd_get_dumptime(nlp->nlp_backup_path, &nlp->nlp_llevel,
	    &nlp->nlp_ldate) < 0) {
		MOD_LOGV3(params, NDMP_LOG_ERROR,
		    "Getting dumpdates for %s level '%c'.\n",
		    nlp->nlp_backup_path, *envp);
		return (NDMP_NO_MEM_ERR);
	} else {
		get_update_env_v3(params, nlp);
		rv = NDMP_NO_ERR;
	}

	return (rv);
}


/*
 * save_date_token_v3
 *
 * Make the value of DUMP_DATE env variable and append the values
 * of the current backup in the file specified with the DMP_NAME
 * env variable if any file is specified.  The file will be
 * relative name in the backup directory path.
 *
 * Parameters:
 *   params (input) - pointer to the parameters structure
 *   nlp (input) - pointer to the nlp structure
 *
 * Returns:
 *   void
 */
static void
save_date_token_v3(ndmpd_module_params_t *params, ndmp_lbr_params_t *nlp)
{
	char val[QUAD_DECIMAL_LEN];
	u_longlong_t tok;

	if (!params || !nlp)
		return;

	nlp->nlp_tokseq++;
	tok = ((u_longlong_t)nlp->nlp_tokseq << 32) | nlp->nlp_cdate;
	(void) snprintf(val, sizeof (val), "%llu", tok);

	NDMP_LOG(LOG_DEBUG, "tok: %lld %s", tok, val);

	if (MOD_SETENV(params, "DUMP_DATE", val) != 0) {
		MOD_LOGV3(params, NDMP_LOG_ERROR,
		    "Could not set DUMP_DATE to %s", val);
	} else if (!nlp->nlp_dmpnm) {
		NDMP_LOG(LOG_DEBUG, "No log file defined");
	} else if (ndmpd_append_dumptime(nlp->nlp_dmpnm, nlp->nlp_backup_path,
	    nlp->nlp_tokseq, nlp->nlp_tokdate) < 0) {
		MOD_LOGV3(params, NDMP_LOG_ERROR,
		    "Saving backup date for \"%s\" in \"%s\".\n",
		    nlp->nlp_backup_path, nlp->nlp_dmpnm);
	}
}


/*
 * save_lbr_bk_v3
 *
 * Append the backup type and date in the DMP_NAME file for
 * LBR-type backup if any file is specified.
 *
 * Parameters:
 *   params (input) - pointer to the parameters structure
 *   nlp (input) - pointer to the nlp structure
 *
 * Returns:
 *   void
 */
static void
save_lbr_bk_v3(ndmpd_module_params_t *params, ndmp_lbr_params_t *nlp)
{
	if (!params || !nlp)
		return;

	if (!nlp->nlp_dmpnm) {
		NDMP_LOG(LOG_DEBUG, "No log file defined");
	} else if (ndmpd_append_dumptime(nlp->nlp_dmpnm, nlp->nlp_backup_path,
	    nlp->nlp_clevel, nlp->nlp_cdate) < 0) {
		MOD_LOGV3(params, NDMP_LOG_ERROR,
		    "Saving backup date for \"%s\" in \"%s\".\n",
		    nlp->nlp_backup_path, nlp->nlp_dmpnm);
	}
}


/*
 * save_level_v3
 *
 * Save the date and level of the current backup in the dumpdates
 * file.
 *
 * Parameters:
 *   params (input) - pointer to the parameters structure
 *   nlp (input) - pointer to the nlp structure
 *
 * Returns:
 *   void
 */
static void
save_level_v3(ndmpd_module_params_t *params, ndmp_lbr_params_t *nlp)
{
	if (!params || !nlp)
		return;

	if (!NLP_SHOULD_UPDATE(nlp)) {
		NDMP_LOG(LOG_DEBUG, "update not requested");
	} else if (ndmpd_put_dumptime(nlp->nlp_backup_path, nlp->nlp_clevel,
	    nlp->nlp_cdate) < 0) {
		MOD_LOGV3(params, NDMP_LOG_ERROR, "Logging backup date.\n");
	}
}


/*
 * save_backup_date_v3
 *
 * A dispatcher function to call the corresponding save function
 * based on the backup type.
 *
 * Parameters:
 *   params (input) - pointer to the parameters structure
 *   nlp (input) - pointer to the nlp structure
 *
 * Returns:
 *   void
 */
static void
save_backup_date_v3(ndmpd_module_params_t *params, ndmp_lbr_params_t *nlp)
{
	if (!params || !nlp)
		return;

	if (NLP_ISSET(nlp, NLPF_TOKENBK))
		save_date_token_v3(params, nlp);
	else if (NLP_ISSET(nlp, NLPF_LBRBK))
		save_lbr_bk_v3(params, nlp);
	else if (NLP_ISSET(nlp, NLPF_LEVELBK))
		save_level_v3(params, nlp);
	else {
		MOD_LOGV3(params, NDMP_LOG_ERROR,
		    "Internal error: lost backup level type for \"%s\".\n",
		    nlp->nlp_backup_path);
	}
}


/*
 * backup_alloc_structs_v3
 *
 * Create the structures for V3 backup.  This includes:
 *	Job stats
 *	Reader writer IPC
 *	File history callback structure
 *
 * Parameters:
 *   session (input) - pointer to the session
 *   jname (input) - name assigned to the current backup for
 *	job stats strucure
 *
 * Returns:
 *   0: on success
 *   -1: otherwise
 */
static int
backup_alloc_structs_v3(ndmpd_session_t *session, char *jname)
{
	int n;
	long xfer_size;
	ndmp_lbr_params_t *nlp;
	tlm_commands_t *cmds;

	nlp = ndmp_get_nlp(session);
	if (!nlp) {
		NDMP_LOG(LOG_DEBUG, "nlp == NULL");
		return (-1);
	}

	nlp->nlp_jstat = tlm_new_job_stats(jname);
	if (!nlp->nlp_jstat) {
		NDMP_LOG(LOG_DEBUG, "Creating job stats");
		return (-1);
	}

	cmds = &nlp->nlp_cmds;
	(void) memset(cmds, 0, sizeof (*cmds));

	xfer_size = ndmp_buffer_get_size(session);
	if (xfer_size < 512*KILOBYTE) {
		/*
		 * Read multiple of mover_record_size near to 512K.  This
		 * will prevent the data being copied in the mover buffer
		 * when we write the data.
		 */
		n = 512 * KILOBYTE / xfer_size;
		if (n <= 0)
			n = 1;
		xfer_size *= n;
		NDMP_LOG(LOG_DEBUG, "Adjusted read size: %d",
		    xfer_size);
	}

	cmds->tcs_command = tlm_create_reader_writer_ipc(TRUE, xfer_size);
	if (!cmds->tcs_command) {
		tlm_un_ref_job_stats(jname);
		return (-1);
	}

	nlp->nlp_logcallbacks = lbrlog_callbacks_init(session,
	    ndmpd_fhpath_v3_cb, ndmpd_fhdir_v3_cb, ndmpd_fhnode_v3_cb);
	if (!nlp->nlp_logcallbacks) {
		tlm_release_reader_writer_ipc(cmds->tcs_command);
		tlm_un_ref_job_stats(jname);
		return (-1);
	}
	nlp->nlp_jstat->js_callbacks = (void *)(nlp->nlp_logcallbacks);
	nlp->nlp_restored = NULL;

	return (0);
}


/*
 * restore_alloc_structs_v3
 *
 * Create the structures for V3 Restore.  This includes:
 *	Job stats
 *	Reader writer IPC
 *	File recovery callback structure
 *
 * Parameters:
 *   session (input) - pointer to the session
 *   jname (input) - name assigned to the current backup for
 *	job stats strucure
 *
 * Returns:
 *   0: on success
 *   -1: otherwise
 */
int
restore_alloc_structs_v3(ndmpd_session_t *session, char *jname)
{
	long xfer_size;
	ndmp_lbr_params_t *nlp;
	tlm_commands_t *cmds;

	nlp = ndmp_get_nlp(session);
	if (!nlp) {
		NDMP_LOG(LOG_DEBUG, "nlp == NULL");
		return (-1);
	}

	/* this is used in ndmpd_path_restored_v3() */
	nlp->nlp_lastidx = -1;

	nlp->nlp_jstat = tlm_new_job_stats(jname);
	if (!nlp->nlp_jstat) {
		NDMP_LOG(LOG_DEBUG, "Creating job stats");
		return (-1);
	}

	cmds = &nlp->nlp_cmds;
	(void) memset(cmds, 0, sizeof (*cmds));

	xfer_size = ndmp_buffer_get_size(session);
	cmds->tcs_command = tlm_create_reader_writer_ipc(FALSE, xfer_size);
	if (!cmds->tcs_command) {
		tlm_un_ref_job_stats(jname);
		return (-1);
	}

	nlp->nlp_logcallbacks = lbrlog_callbacks_init(session,
	    ndmpd_path_restored_v3, NULL, NULL);
	if (!nlp->nlp_logcallbacks) {
		tlm_release_reader_writer_ipc(cmds->tcs_command);
		tlm_un_ref_job_stats(jname);
		return (-1);
	}
	nlp->nlp_jstat->js_callbacks = (void *)(nlp->nlp_logcallbacks);

	nlp->nlp_rsbm = bm_alloc(nlp->nlp_nfiles, 0);
	if (nlp->nlp_rsbm < 0) {
		NDMP_LOG(LOG_ERR, "Out of memory.");
		lbrlog_callbacks_done(nlp->nlp_logcallbacks);
		tlm_release_reader_writer_ipc(cmds->tcs_command);
		tlm_un_ref_job_stats(jname);
		return (-1);
	}

	return (0);
}


/*
 * free_structs_v3
 *
 * Release the resources allocated by backup_alloc_structs_v3
 * function.
 *
 * Parameters:
 *   session (input) - pointer to the session
 *   jname (input) - name assigned to the current backup for
 *	job stats strucure
 *
 * Returns:
 *   void
 */
/*ARGSUSED*/
static void
free_structs_v3(ndmpd_session_t *session, char *jname)
{
	ndmp_lbr_params_t *nlp;
	tlm_commands_t *cmds;

	nlp = ndmp_get_nlp(session);
	if (!nlp) {
		NDMP_LOG(LOG_DEBUG, "nlp == NULL");
		return;
	}
	cmds = &nlp->nlp_cmds;
	if (!cmds) {
		NDMP_LOG(LOG_DEBUG, "cmds == NULL");
		return;
	}

	if (nlp->nlp_logcallbacks) {
		lbrlog_callbacks_done(nlp->nlp_logcallbacks);
		nlp->nlp_logcallbacks = NULL;
	} else
		NDMP_LOG(LOG_DEBUG, "FH CALLBACKS == NULL");

	if (cmds->tcs_command) {
		if (cmds->tcs_command->tc_buffers != NULL)
			tlm_release_reader_writer_ipc(cmds->tcs_command);
		else
			NDMP_LOG(LOG_DEBUG, "BUFFERS == NULL");
		cmds->tcs_command = NULL;
	} else
		NDMP_LOG(LOG_DEBUG, "COMMAND == NULL");

	if (nlp->nlp_bkmap >= 0) {
		(void) dbm_free(nlp->nlp_bkmap);
		nlp->nlp_bkmap = -1;
	}

	if (session->ns_data.dd_operation == NDMP_DATA_OP_RECOVER) {
		if (nlp->nlp_rsbm < 0) {
			NDMP_LOG(LOG_DEBUG, "nlp_rsbm < 0 %d", nlp->nlp_rsbm);
		} else {
			(void) bm_free(nlp->nlp_rsbm);
			nlp->nlp_rsbm = -1;
		}
	}
}


/*
 * backup_dirv3
 *
 * Backup a directory and update the bytes processed field of the
 * data server.
 *
 * Parameters:
 *   bpp (input) - pointer to the backup parameters structure
 *   pnp (input) - pointer to the path node
 *   enp (input) - pointer to the entry node
 *
 * Returns:
 *   0: on success
 *   != 0: otherwise
 */
static int
backup_dirv3(bk_param_v3_t *bpp, fst_node_t *pnp,
    fst_node_t *enp)
{
	longlong_t apos, bpos;
	acl_t *aclp = NULL;
	char *acltp;
	struct stat64 st;
	char fullpath[TLM_MAX_PATH_NAME];
	char *p;

	if (!bpp || !pnp || !enp) {
		NDMP_LOG(LOG_DEBUG, "Invalid argument");
		return (-1);
	}

	NDMP_LOG(LOG_DEBUG, "d(%s)", bpp->bp_tmp);

	if (lstat64(bpp->bp_tmp, &st) != 0)
		return (0);

	if (acl_get(bpp->bp_tmp, ACL_NO_TRIVIAL, &aclp) != 0) {
		NDMP_LOG(LOG_DEBUG, "acl_get error errno=%d", errno);
		return (-1);
	}
	if (aclp && (acltp = acl_totext(aclp,
	    ACL_APPEND_ID | ACL_SID_FMT | ACL_COMPACT_FMT)) != NULL) {
		(void) strlcpy(bpp->bp_tlmacl->acl_info.attr_info,
		    acltp, TLM_MAX_ACL_TXT);
		acl_free(aclp);
		free(acltp);
	} else {
		*bpp->bp_tlmacl->acl_info.attr_info = '\0';
	}

	bpos = tlm_get_data_offset(bpp->bp_lcmd);

	p = bpp->bp_tmp + strlen(bpp->bp_chkpnm);
	if (*p == '/')
		(void) snprintf(fullpath, TLM_MAX_PATH_NAME, "%s%s",
		    bpp->bp_unchkpnm, p);
	else
		(void) snprintf(fullpath, TLM_MAX_PATH_NAME, "%s/%s",
		    bpp->bp_unchkpnm, p);

	if (tm_tar_ops.tm_putdir != NULL)
		(void) (tm_tar_ops.tm_putdir)(fullpath, bpp->bp_tlmacl,
		    bpp->bp_lcmd, bpp->bp_js);

	apos = tlm_get_data_offset(bpp->bp_lcmd);
	bpp->bp_session->ns_data.dd_module.dm_stats.ms_bytes_processed +=
	    apos - bpos;

	return (0);
}


/*
 * backup_filev3
 *
 * Backup a file and update the bytes processed field of the
 * data server.
 *
 * Parameters:
 *   bpp (input) - pointer to the backup parameters structure
 *   pnp (input) - pointer to the path node
 *   enp (input) - pointer to the entry node
 *
 * Returns:
 *   0: on success
 *   != 0: otherwise
 */
static int
backup_filev3(bk_param_v3_t *bpp, fst_node_t *pnp,
    fst_node_t *enp)
{
	char *ent;
	longlong_t rv;
	longlong_t apos, bpos;
	acl_t *aclp = NULL;
	char *acltp;
	struct stat64 st;
	char fullpath[TLM_MAX_PATH_NAME];
	char *p;

	if (!bpp || !pnp || !enp) {
		NDMP_LOG(LOG_DEBUG, "Invalid argument");
		return (-1);
	}

	NDMP_LOG(LOG_DEBUG, "f(%s)", bpp->bp_tmp);

	if (lstat64(bpp->bp_tmp, &st) != 0)
		return (0);

	if (!S_ISLNK(bpp->bp_tlmacl->acl_attr.st_mode)) {
		if (acl_get(bpp->bp_tmp, ACL_NO_TRIVIAL, &aclp) != 0) {
			NDMP_LOG(LOG_DEBUG, "acl_get error");
			return (-1);
		}

		if (aclp &&
		    (acltp = acl_totext(aclp,
		    ACL_APPEND_ID | ACL_SID_FMT | ACL_COMPACT_FMT)) != NULL) {
			(void) strlcpy(bpp->bp_tlmacl->acl_info.attr_info,
			    acltp, TLM_MAX_ACL_TXT);
			acl_free(aclp);
			free(acltp);
		} else {
			*bpp->bp_tlmacl->acl_info.attr_info = '\0';
		}
	}

	bpos = tlm_get_data_offset(bpp->bp_lcmd);
	ent = enp->tn_path ? enp->tn_path : "";

	p = pnp->tn_path + strlen(bpp->bp_chkpnm);
	if (*p == '/')
		(void) snprintf(fullpath, TLM_MAX_PATH_NAME, "%s%s",
		    bpp->bp_unchkpnm, p);
	else
		(void) snprintf(fullpath, TLM_MAX_PATH_NAME, "%s/%s",
		    bpp->bp_unchkpnm, p);

	if (tm_tar_ops.tm_putfile != NULL)
		rv = (tm_tar_ops.tm_putfile)(fullpath, ent, pnp->tn_path,
		    bpp->bp_tlmacl, bpp->bp_cmds, bpp->bp_lcmd, bpp->bp_js,
		    bpp->bp_session->hardlink_q);

	apos = tlm_get_data_offset(bpp->bp_lcmd);
	bpp->bp_session->ns_data.dd_module.dm_stats.ms_bytes_processed +=
	    apos - bpos;

	return (rv < 0 ? rv : 0);
}


/*
 * check_bk_args
 *
 * Check the argument of the bpp.  This is shared function between
 * timebk_v3 and lbrbk_v3 functions.  The checks include:
 *	- The bpp itself.
 *	- If the session pointer of the bpp is valid.
 *	- If the session connection to the DMA is closed.
 *	- If the nlp pointer of the bpp is valid.
 *	- If the backup is aborted.
 *
 * Parameters:
 *   bpp (input) - pointer to the backup parameters structure
 *
 * Returns:
 *   0: if everything's OK
 *   != 0: otherwise
 */
static int
check_bk_args(bk_param_v3_t *bpp)
{
	int rv;

	if (!bpp) {
		rv = -1;
		NDMP_LOG(LOG_DEBUG, "Lost bpp");
	} else if (!bpp->bp_session) {
		rv = -1;
		NDMP_LOG(LOG_DEBUG, "Session is NULL");
	} else if (bpp->bp_session->ns_eof) {
		rv = -1;
		NDMP_LOG(LOG_INFO,
		    "Connection client is closed for backup \"%s\"",
		    bpp->bp_nlp->nlp_backup_path);
	} else if (!bpp->bp_nlp) {
		NDMP_LOG(LOG_DEBUG, "Lost nlp");
		return (-1);
	} else if (bpp->bp_session->ns_data.dd_abort) {
		rv = -1;
		NDMP_LOG(LOG_INFO, "Backup aborted \"%s\"",
		    bpp->bp_nlp->nlp_backup_path);
	} else
		rv = 0;

	return (rv);
}


/*
 * shouldskip
 *
 * Determines if the current entry should be skipped or it
 * should be backed up.
 *
 * Parameters:
 *   bpp (input) - pointer to the backup parameters structure
 *   pnp (input) - pointer to the path node
 *   enp (input) - pointer to the entry node
 *   errp (output) - pointer to the error value that should be
 *	returned by the caller
 *
 * Returns:
 *   TRUE: if the entry should not be backed up
 *   FALSE: otherwise
 */
static boolean_t
shouldskip(bk_param_v3_t *bpp, fst_node_t *pnp,
    fst_node_t *enp, int *errp)
{
	char *ent;
	boolean_t rv;
	struct stat64 *estp;

	if (!bpp || !pnp || !enp || !errp) {
		NDMP_LOG(LOG_DEBUG, "Invalid argument");
		return (TRUE);
	}

	if (!enp->tn_path) {
		ent = "";
		estp = pnp->tn_st;
	} else {
		ent = enp->tn_path;
		estp = enp->tn_st;
	}

	/*
	 * When excluding or skipping entries, FST_SKIP should be
	 * returned, otherwise, 0 should be returned to
	 * get other entries in the directory of this entry.
	 */
	if (!dbm_getone(bpp->bp_nlp->nlp_bkmap, (u_longlong_t)estp->st_ino)) {
		rv = TRUE;
		*errp = S_ISDIR(estp->st_mode) ? FST_SKIP : 0;
		NDMP_LOG(LOG_DEBUG, "Skipping %d %s/%s",
		    *errp, pnp->tn_path, ent);
	} else if (tlm_is_excluded(pnp->tn_path, ent, bpp->bp_excls)) {
		rv = TRUE;
		*errp = S_ISDIR(estp->st_mode) ? FST_SKIP : 0;
		NDMP_LOG(LOG_DEBUG, "excl %d \"%s/%s\"",
		    *errp, pnp->tn_path, ent);
	} else if (inexl(bpp->bp_nlp->nlp_exl, ent)) {
		rv = TRUE;
		*errp = S_ISDIR(estp->st_mode) ? FST_SKIP : 0;
		NDMP_LOG(LOG_DEBUG, "out %d \"%s/%s\"",
		    *errp, pnp->tn_path, ent);
	} else if (!S_ISDIR(estp->st_mode) &&
	    !ininc(bpp->bp_nlp->nlp_inc, ent)) {
		rv = TRUE;
		*errp = 0;
		NDMP_LOG(LOG_DEBUG, "!in \"%s/%s\"", pnp->tn_path, ent);
	} else
		rv = FALSE;

	return (rv);
}


/*
 * ischngd
 *
 * Check if the object specified should be backed up or not.
 * If stp belongs to a directory and if it is marked in the
 * bitmap vector, it shows that either the directory itself is
 * modified or there is something below it that will be backed
 * up.
 *
 * By setting ndmp_force_bk_dirs global variable to a non-zero
 * value, directories are backed up anyways.
 *
 * Backing up the directories unconditionally helps
 * restoring the metadata of directories as well, when one
 * of the objects below them are being restored.
 *
 * For non-directory objects, if the modification or change
 * time of the object is after the date specified by the
 * bk_selector_t, the the object must be backed up.
 */
static boolean_t
ischngd(struct stat64 *stp, time_t t, ndmp_lbr_params_t *nlp)
{
	boolean_t rv;

	if (!stp) {
		rv = FALSE;
		NDMP_LOG(LOG_DEBUG, "stp is NULL");
	} else if (!nlp) {
		rv = FALSE;
		NDMP_LOG(LOG_DEBUG, "nlp is NULL");
	} else if (t == 0) {
		/*
		 * if we are doing base backup then we do not need to
		 * check the time, for we should backup everything.
		 */
		rv = TRUE;
		NDMP_LOG(LOG_DEBUG, "Base Backup");
	} else if (S_ISDIR(stp->st_mode) && ndmp_force_bk_dirs) {
		rv = TRUE;
		NDMP_LOG(LOG_DEBUG, "d(%lu)", (uint_t)stp->st_ino);
	} else if (S_ISDIR(stp->st_mode) &&
	    dbm_getone(nlp->nlp_bkmap, (u_longlong_t)stp->st_ino) &&
	    ((NLP_ISDUMP(nlp) && ndmp_dump_path_node) ||
	    (NLP_ISTAR(nlp) && ndmp_tar_path_node))) {
		/*
		 * If the object is a directory and it leads to a modified
		 * object (that should be backed up) and for that type of
		 * backup the path nodes should be backed up, then return
		 * TRUE.
		 *
		 * This is required by some DMAs like Backup Express, which
		 * needs to receive ADD_NODE (for dump) or ADD_PATH (for tar)
		 * for the intermediate directories of a modified object.
		 * Other DMAs, like net_backup and net_worker, do not have such
		 * requirement.  This requirement makes sense for dump format
		 * but for 'tar' format, it does not.  In provision to the
		 * NDMP-v4 spec, for 'tar' format the intermediate directories
		 * need not to be reported.
		 */
		rv = TRUE;
		NDMP_LOG(LOG_DEBUG, "p(%lu)", (u_longlong_t)stp->st_ino);
	} else if (stp->st_mtime > t) {
		rv = TRUE;
		NDMP_LOG(LOG_DEBUG, "m(%lu): %lu > %lu",
		    (uint_t)stp->st_ino, (uint_t)stp->st_mtime, (uint_t)t);
	} else if (stp->st_ctime > t) {
		if (NLP_IGNCTIME(nlp)) {
			rv = FALSE;
			NDMP_LOG(LOG_DEBUG, "ign c(%lu): %lu > %lu",
			    (uint_t)stp->st_ino, (uint_t)stp->st_ctime,
			    (uint_t)t);
		} else {
			rv = TRUE;
			NDMP_LOG(LOG_DEBUG, "c(%lu): %lu > %lu",
			    (uint_t)stp->st_ino, (uint_t)stp->st_ctime,
			    (uint_t)t);
		}
	} else {
		rv = FALSE;
		NDMP_LOG(LOG_DEBUG, "mc(%lu): (%lu,%lu) < %lu",
		    (uint_t)stp->st_ino, (uint_t)stp->st_mtime,
		    (uint_t)stp->st_ctime, (uint_t)t);
	}

	return (rv);
}


/*
 * iscreated
 *
 * This function is used to check last mtime (currently inside the ACL
 * structure) instead of ctime for checking if the file is to be backed up
 * or not. See option "inc.lmtime" for more details
 */
/*ARGSUSED*/
int iscreated(ndmp_lbr_params_t *nlp, char *name, tlm_acls_t *tacl,
    time_t t)
{
	int ret;
	acl_t *aclp = NULL;
	char *acltp;

	NDMP_LOG(LOG_DEBUG, "flags %x", nlp->nlp_flags);
	if (NLP_INCLMTIME(nlp) == FALSE)
		return (0);

	ret = acl_get(name, ACL_NO_TRIVIAL, &aclp);
	if (ret != 0) {
		NDMP_LOG(LOG_DEBUG,
		    "Error getting the acl information: err %d", ret);
		return (0);
	}
	if (aclp && (acltp = acl_totext(aclp,
	    ACL_APPEND_ID | ACL_SID_FMT | ACL_COMPACT_FMT)) != NULL) {
		(void) strlcpy(tacl->acl_info.attr_info, acltp,
		    TLM_MAX_ACL_TXT);
		acl_free(aclp);
		free(acltp);
	}

	/* Need to add support for last mtime */

	return (0);
}

/*
 * size_cb
 *
 * The callback function for calculating the size of
 * the backup path. This is used to get an estimate
 * of the progress of backup during NDMP backup
 */
static int
size_cb(void *arg, fst_node_t *pnp, fst_node_t *enp)
{
	struct stat64 *stp;

	stp = enp->tn_path ? enp->tn_st : pnp->tn_st;
	*((u_longlong_t *)arg) += stp->st_size;

	return (0);
}

/*
 * timebk_v3
 *
 * The callback function for backing up objects based on
 * their time stamp.  This is shared between token-based
 * and level-based backup, which look at the time stamps
 * of the objects to determine if they should be backed
 * up.
 *
 * Parameters:
 *   arg (input) - pointer to the backup parameters structure
 *   pnp (input) - pointer to the path node
 *   enp (input) - pointer to the entry node
 *
 * Returns:
 *   0: if backup should continue
 *   -1: if the backup should be stopped
 *   FST_SKIP: if backing up the current directory is enough
 */
static int
timebk_v3(void *arg, fst_node_t *pnp, fst_node_t *enp)
{
	char *ent;
	int rv;
	time_t t;
	bk_param_v3_t *bpp;
	struct stat64 *stp;
	fs_fhandle_t *fhp;

	bpp = (bk_param_v3_t *)arg;

	rv = check_bk_args(bpp);
	if (rv != 0)
		return (rv);

	stp = enp->tn_path ? enp->tn_st : pnp->tn_st;
	if (shouldskip(bpp, pnp, enp, &rv))
		return (rv);

	if (enp->tn_path) {
		ent = enp->tn_path;
		stp = enp->tn_st;
		fhp = enp->tn_fh;
	} else {
		ent = "";
		stp = pnp->tn_st;
		fhp = pnp->tn_fh;
	}


	if (!tlm_cat_path(bpp->bp_tmp, pnp->tn_path, ent)) {
		NDMP_LOG(LOG_ERR, "Path too long %s/%s.", pnp->tn_path, ent);
		return (FST_SKIP);
	}
	if (NLP_ISSET(bpp->bp_nlp, NLPF_TOKENBK))
		t = bpp->bp_nlp->nlp_tokdate;
	else if (NLP_ISSET(bpp->bp_nlp, NLPF_LEVELBK)) {
		t = bpp->bp_nlp->nlp_ldate;
	} else {
		NDMP_LOG(LOG_DEBUG, "Unknown backup type on \"%s/%s\"",
		    pnp->tn_path, ent);
		return (-1);
	}

	if (S_ISDIR(stp->st_mode)) {
		bpp->bp_tlmacl->acl_dir_fh = *fhp;
		(void) ndmpd_fhdir_v3_cb(bpp->bp_nlp->nlp_logcallbacks,
		    bpp->bp_tmp, stp);

		if (ischngd(stp, t, bpp->bp_nlp)) {
			(void) memcpy(&bpp->bp_tlmacl->acl_attr, stp,
			    sizeof (struct stat64));
			rv = backup_dirv3(bpp, pnp, enp);
		}
	} else {
		if (ischngd(stp, t, bpp->bp_nlp) ||
		    iscreated(bpp->bp_nlp, bpp->bp_tmp, bpp->bp_tlmacl, t)) {
			rv = 0;
			(void) memcpy(&bpp->bp_tlmacl->acl_attr, stp,
			    sizeof (struct stat64));
			bpp->bp_tlmacl->acl_fil_fh = *fhp;
			(void) backup_filev3(bpp, pnp, enp);
		}
	}

	return (rv);
}


/*
 * lbrbk_v3
 *
 * The callback function for backing up objects based on
 * their archive directory bit.  This is used in LBR-type
 * backup.  In which the objects are backed up if their
 * archive bit is set.
 *
 * Parameters:
 *   arg (input) - pointer to the backup parameters structure
 *   pnp (input) - pointer to the path node
 *   enp (input) - pointer to the entry node
 *
 * Returns:
 *   0: if backup should continue
 *   -1: if the backup should be stopped
 *   FST_SKIP: if backing up the current directory is enough
 */
static int
lbrbk_v3(void *arg, fst_node_t *pnp, fst_node_t *enp)
{
	char *ent;
	int rv;
	bk_param_v3_t *bpp;
	struct stat64 *stp;
	fs_fhandle_t *fhp;

	bpp = (bk_param_v3_t *)arg;
	rv = check_bk_args(bpp);
	if (rv != 0)
		return (rv);

	stp = enp->tn_path ? enp->tn_st : pnp->tn_st;
	if (shouldskip(bpp, pnp, enp, &rv))
		return (rv);

	if (enp->tn_path) {
		ent = enp->tn_path;
		stp = enp->tn_st;
		fhp = enp->tn_fh;
	} else {
		ent = "";
		stp = pnp->tn_st;
		fhp = pnp->tn_fh;
	}

	if (!tlm_cat_path(bpp->bp_tmp, pnp->tn_path, ent)) {
		NDMP_LOG(LOG_ERR, "Path too long %s/%s.", pnp->tn_path, ent);
		return (FST_SKIP);
	}
	if (!NLP_ISSET(bpp->bp_nlp, NLPF_LBRBK)) {
		NDMP_LOG(LOG_DEBUG, "!NLPF_LBRBK");
		return (-1);
	}

	if (S_ISDIR(stp->st_mode)) {
		bpp->bp_tlmacl->acl_dir_fh = *fhp;
		(void) ndmpd_fhdir_v3_cb(bpp->bp_nlp->nlp_logcallbacks,
		    bpp->bp_tmp, stp);

		if (SHOULD_LBRBK(bpp)) {
			bpp->bp_tlmacl->acl_attr = *stp;
			rv = backup_dirv3(bpp, pnp, enp);
		}
	} else if (SHOULD_LBRBK(bpp)) {
		rv = 0;
		bpp->bp_tlmacl->acl_attr = *stp;
		bpp->bp_tlmacl->acl_fil_fh = *fhp;
		(void) backup_filev3(bpp, pnp, enp);
	}

	return (rv);
}


/*
 * backup_reader_v3
 *
 * The reader thread for the backup.  It sets up the callback
 * parameters and traverses the backup hierarchy in level-order
 * way.
 *
 * Parameters:
 *   jname (input) - name assigned to the current backup for
 *	job stats strucure
 *   nlp (input) - pointer to the nlp structure
 *   cmds (input) - pointer to the tlm_commands_t structure
 *
 * Returns:
 *   0: on success
 *   != 0: otherwise
 */
static int
backup_reader_v3(backup_reader_arg_t *argp)
{
	int rv;
	tlm_cmd_t *lcmd;
	tlm_acls_t tlm_acls;
	longlong_t bpos, n;
	bk_param_v3_t bp;
	fs_traverse_t ft;
	char *jname;
	ndmp_lbr_params_t *nlp;
	tlm_commands_t *cmds;

	if (!argp)
		return (-1);

	jname = argp->br_jname;
	nlp = argp->br_nlp;
	cmds = argp->br_cmds;

	rv = 0;
	lcmd = cmds->tcs_command;
	lcmd->tc_ref++;
	cmds->tcs_reader_count++;

	(void) memset(&tlm_acls, 0, sizeof (tlm_acls));

	/* NDMP parameters */
	bp.bp_session = nlp->nlp_session;
	bp.bp_nlp = nlp;

	/* LBR-related parameters  */
	bp.bp_js = tlm_ref_job_stats(jname);
	bp.bp_cmds = cmds;
	bp.bp_lcmd = lcmd;
	bp.bp_tlmacl = &tlm_acls;
	bp.bp_opr = 0;

	/* release the parent thread, after referencing the job stats */
	(void) pthread_barrier_wait(&argp->br_barrier);

	bp.bp_tmp = ndmp_malloc(sizeof (char) * TLM_MAX_PATH_NAME);
	if (!bp.bp_tmp)
		return (-1);

	/*
	 * Make the checkpointed paths for traversing the
	 * backup hierarchy, if we make the checkpoint.
	 */
	bp.bp_unchkpnm = nlp->nlp_backup_path;
	if (!NLP_ISCHKPNTED(nlp)) {
		tlm_acls.acl_checkpointed = TRUE;
		bp.bp_chkpnm = ndmp_malloc(sizeof (char) * TLM_MAX_PATH_NAME);
		if (!bp.bp_chkpnm) {
			NDMP_FREE(bp.bp_tmp);
			return (-1);
		}
		(void) tlm_build_snapshot_name(nlp->nlp_backup_path,
		    bp.bp_chkpnm, nlp->nlp_jstat->js_job_name);
	} else {
		tlm_acls.acl_checkpointed = FALSE;
		bp.bp_chkpnm = nlp->nlp_backup_path;
	}
	bp.bp_excls = ndmpd_make_exc_list();

	/* set traversing arguments */
	ft.ft_path = nlp->nlp_backup_path;
	ft.ft_lpath = bp.bp_chkpnm;

	NDMP_LOG(LOG_DEBUG, "path %s lpath %s", ft.ft_path, ft.ft_lpath);
	if (NLP_ISSET(nlp, NLPF_TOKENBK) || NLP_ISSET(nlp, NLPF_LEVELBK)) {
		ft.ft_callbk = timebk_v3;
		tlm_acls.acl_clear_archive = FALSE;
	} else if (NLP_ISSET(nlp, NLPF_LBRBK)) {
		ft.ft_callbk = lbrbk_v3;
		tlm_acls.acl_clear_archive = FALSE;

		NDMP_LOG(LOG_DEBUG, "bp_opr %x clr_arc %c",
		    bp.bp_opr, NDMP_YORN(tlm_acls.acl_clear_archive));
	} else {
		rv = -1;
		MOD_LOGV3(nlp->nlp_params, NDMP_LOG_ERROR,
		    "Unknow backup type.\n");
	}
	ft.ft_arg = &bp;
	ft.ft_logfp = (ft_log_t)ndmp_log;
	ft.ft_flags = FST_VERBOSE;	/* Solaris */

	/* take into account the header written to the stream so far */
	n = tlm_get_data_offset(lcmd);
	nlp->nlp_session->ns_data.dd_module.dm_stats.ms_bytes_processed = n;

	if (rv == 0) {
		/* start traversing the hierarchy and actual backup */
		rv = traverse_level(&ft);
		if (rv == 0) {
			/* write the trailer and update the bytes processed */
			bpos = tlm_get_data_offset(lcmd);
			(void) write_tar_eof(lcmd);
			n = tlm_get_data_offset(lcmd) - bpos;
			nlp->nlp_session->
			    ns_data.dd_module.dm_stats.ms_bytes_processed += n;
		}
	}

	if (!NLP_ISCHKPNTED(nlp))
		NDMP_FREE(bp.bp_chkpnm);
	NDMP_FREE(bp.bp_tmp);
	NDMP_FREE(bp.bp_excls);

	cmds->tcs_reader_count--;
	lcmd->tc_writer = TLM_STOP;
	tlm_release_reader_writer_ipc(lcmd);
	tlm_un_ref_job_stats(jname);
	return (rv);

}


/*
 * tar_backup_v3
 *
 * Traverse the backup hierarchy if needed and make the bitmap.
 * Then launch reader and writer threads to do the actual backup.
 *
 * Parameters:
 *   session (input) - pointer to the session
 *   params (input) - pointer to the parameters structure
 *   nlp (input) - pointer to the nlp structure
 *   jname (input) - job name
 *
 * Returns:
 *   0: on success
 *   != 0: otherwise
 */
static int
tar_backup_v3(ndmpd_session_t *session, ndmpd_module_params_t *params,
    ndmp_lbr_params_t *nlp, char *jname)
{
	tlm_commands_t *cmds;
	backup_reader_arg_t arg;
	pthread_t rdtp;
	char info[256];
	int result;
	ndmp_context_t nctx;
	int err;

	if (ndmp_get_bk_dir_ino(nlp))
		return (-1);

	result = err = 0;

	/* exit as if there was an internal error */
	if (session->ns_eof)
		return (-1);

	if (!session->ns_data.dd_abort) {
		if (backup_alloc_structs_v3(session, jname) < 0) {
			nlp->nlp_bkmap = -1;
			return (-1);
		}

		if (ndmpd_mark_inodes_v3(session, nlp) != 0) {
			if (nlp->nlp_bkmap != -1) {
				(void) dbm_free(nlp->nlp_bkmap);
				nlp->nlp_bkmap = -1;
			}
			free_structs_v3(session, jname);
			return (-1);
		}

		nlp->nlp_jstat->js_start_ltime = time(NULL);
		nlp->nlp_jstat->js_start_time = nlp->nlp_jstat->js_start_ltime;
		nlp->nlp_jstat->js_chkpnt_time = nlp->nlp_cdate;

		cmds = &nlp->nlp_cmds;
		cmds->tcs_reader = cmds->tcs_writer = TLM_BACKUP_RUN;
		cmds->tcs_command->tc_reader = TLM_BACKUP_RUN;
		cmds->tcs_command->tc_writer = TLM_BACKUP_RUN;

		if (ndmp_write_utf8magic(cmds->tcs_command) < 0) {
			free_structs_v3(session, jname);
			return (-1);
		}

		NDMP_LOG(LOG_DEBUG,
		    "Backing up \"%s\" started.", nlp->nlp_backup_path);

		/* Plug-in module */
		if (ndmp_pl != NULL &&
		    ndmp_pl->np_pre_backup != NULL) {
			(void) memset(&nctx, 0, sizeof (ndmp_context_t));
			nctx.nc_plversion = ndmp_pl->np_plversion;
			nctx.nc_plname = ndmpd_get_prop(NDMP_PLUGIN_PATH);
			nctx.nc_cmds = cmds;
			nctx.nc_params = params;
			if ((err = ndmp_pl->np_pre_backup(ndmp_pl, &nctx,
			    nlp->nlp_backup_path)) != 0) {
				NDMP_LOG(LOG_ERR, "Pre-backup plug-in: %m");
				goto backup_out;
			}
		}

		(void) memset(&arg, 0, sizeof (backup_reader_arg_t));
		arg.br_jname = jname;
		arg.br_nlp = nlp;
		arg.br_cmds = cmds;

		(void) pthread_barrier_init(&arg.br_barrier, 0, 2);

		err = pthread_create(&rdtp, NULL, (funct_t)backup_reader_v3,
		    (void *)&arg);
		if (err == 0) {
			(void) pthread_barrier_wait(&arg.br_barrier);
			(void) pthread_barrier_destroy(&arg.br_barrier);
		} else {
			(void) pthread_barrier_destroy(&arg.br_barrier);
			free_structs_v3(session, jname);
			NDMP_LOG(LOG_DEBUG, "Launch backup_reader_v3: %m");
			return (-1);
		}

		if ((err = ndmp_tar_writer(session, params, cmds)) != 0)
			result = EIO;

		nlp->nlp_jstat->js_stop_time = time(NULL);

		(void) snprintf(info, sizeof (info),
		    "Runtime [%s] %llu bytes (%llu): %d seconds\n",
		    nlp->nlp_backup_path,
		    session->ns_data.dd_module.dm_stats.ms_bytes_processed,
		    session->ns_data.dd_module.dm_stats.ms_bytes_processed,
		    nlp->nlp_jstat->js_stop_time -
		    nlp->nlp_jstat->js_start_ltime);
		MOD_LOGV3(params, NDMP_LOG_NORMAL, info);

		ndmp_wait_for_reader(cmds);
		(void) pthread_join(rdtp, NULL);

		/* exit as if there was an internal error */
		if (session->ns_eof) {
			result = EPIPE;
			err = -1;
		}
		if (!session->ns_data.dd_abort) {
			ndmpd_audit_backup(session->ns_connection,
			    nlp->nlp_backup_path,
			    session->ns_data.dd_data_addr.addr_type,
			    session->ns_tape.td_adapter_name, result);
			NDMP_LOG(LOG_DEBUG, "Backing up \"%s\" Finished.",
			    nlp->nlp_backup_path);
		}
	}

	if (session->ns_data.dd_abort) {
		ndmpd_audit_backup(session->ns_connection,
		    nlp->nlp_backup_path,
		    session->ns_data.dd_data_addr.addr_type,
		    session->ns_tape.td_adapter_name, EINTR);
		NDMP_LOG(LOG_DEBUG,
		    "Backing up \"%s\" aborted.", nlp->nlp_backup_path);
		err = -1;
	} else {

backup_out:
		/* Plug-in module */
		if (ndmp_pl != NULL &&
		    ndmp_pl->np_post_backup != NULL &&
		    ndmp_pl->np_post_backup(ndmp_pl, &nctx, err) == -1) {
			NDMP_LOG(LOG_DEBUG, "Post-backup plug-in: %m");
			return (-1);
		}
	}

	free_structs_v3(session, jname);
	return (err);
}

/*
 * get_backup_size
 *
 * Find the estimate of backup size. This is used to get an estimate
 * of the progress of backup during NDMP backup.
 */
void
get_backup_size(ndmp_bkup_size_arg_t *sarg)
{
	fs_traverse_t ft;
	u_longlong_t bk_size;
	char spath[PATH_MAX];
	int rv;

	bk_size = 0;
	if (fs_is_chkpntvol(sarg->bs_path)) {
		ft.ft_path = sarg->bs_path;
	} else {
		(void) tlm_build_snapshot_name(sarg->bs_path,
		    spath, sarg->bs_jname);
		ft.ft_path = spath;
	}

	ft.ft_lpath = ft.ft_path;
	ft.ft_callbk = size_cb;
	ft.ft_arg = &bk_size;
	ft.ft_logfp = (ft_log_t)ndmp_log;
	ft.ft_flags = FST_VERBOSE;

	if ((rv = traverse_level(&ft)) != 0) {
		NDMP_LOG(LOG_DEBUG, "bksize err=%d", rv);
		bk_size = 0;
	} else {
		NDMP_LOG(LOG_DEBUG, "bksize %lld, %lldKB, %lldMB\n",
		    bk_size, bk_size / 1024, bk_size /(1024 * 1024));
	}
	sarg->bs_session->ns_data.dd_data_size = bk_size;
}

/*
 * get_rs_path_v3
 *
 * Find the restore path
 */
ndmp_error
get_rs_path_v3(ndmpd_module_params_t *params, ndmp_lbr_params_t *nlp)
{
	char *dp;
	ndmp_error rv;
	mem_ndmp_name_v3_t *ep;
	int i, nm_cnt;
	char *nm_dpath_list[MULTIPLE_DEST_DIRS];
	static char mdest_buf[256];

	*mdest_buf = 0;
	*nm_dpath_list = "";
	for (i = 0, nm_cnt = 0; i < (int)nlp->nlp_nfiles; i++) {
		ep = (mem_ndmp_name_v3_t *)MOD_GETNAME(params, i);
		if (!ep) {
			NDMP_LOG(LOG_DEBUG, "Can't get Nlist[%d]", i);
			return (NDMP_ILLEGAL_ARGS_ERR);
		}
		if (strcmp(nm_dpath_list[nm_cnt], ep->nm3_dpath) != 0 &&
		    nm_cnt < MULTIPLE_DEST_DIRS - 1)
			nm_dpath_list[++nm_cnt] = ep->nm3_dpath;
	}

	multiple_dest_restore = (nm_cnt > 1);
	nlp->nlp_restore_path = mdest_buf;

	for (i = 1; i < nm_cnt + 1; i++) {
		if (ISDEFINED(nm_dpath_list[i]))
			dp = nm_dpath_list[i];
		else
			/* the default destination path is backup directory */
			dp = nlp->nlp_backup_path;

		/* check the destination directory exists and is writable */
		if (!fs_volexist(dp)) {
			rv = NDMP_ILLEGAL_ARGS_ERR;
			MOD_LOGV3(params, NDMP_LOG_ERROR,
			    "Invalid destination path volume \"%s\".\n", dp);
		} else if (!voliswr(dp)) {
			rv = NDMP_ILLEGAL_ARGS_ERR;
			MOD_LOGV3(params, NDMP_LOG_ERROR,
			    "The destination path volume"
			    " is not writable \"%s\".\n", dp);
		} else {
			rv = NDMP_NO_ERR;
			(void) strlcat(nlp->nlp_restore_path, dp,
			    sizeof (mdest_buf));
			NDMP_LOG(LOG_DEBUG, "rspath: \"%s\"", dp);
		}

		/*
		 * Exit if there is an error or it is not a multiple
		 * destination restore mode
		 */
		if (rv != NDMP_NO_ERR || !multiple_dest_restore)
			break;

		if (i < nm_cnt)
			(void) strlcat(nlp->nlp_restore_path, ", ",
			    sizeof (mdest_buf));
	}

	return (rv);
}


/*
 * fix_nlist_v3
 *
 * Check if the recovery list is valid and fix it if there are some
 * unspecified entries in it. It checks for original, destination
 * and new path for all NDMP names provided inside the list.
 *
 * V3: dpath is the destination directory.  If newnm is not NULL, the
 * destination path is dpath/newnm.  Otherwise the destination path is
 * dpath/opath_last_node, where opath_last_node is the last node in opath.
 *
 * V4: If newnm is not NULL, dpath is the destination directory, and
 * dpath/newnm is the destination path.  If newnm is NULL, dpath is
 * the destination path (opath is not involved in forming destination path).
 */
ndmp_error
fix_nlist_v3(ndmpd_session_t *session, ndmpd_module_params_t *params,
    ndmp_lbr_params_t *nlp)
{
	char *cp, *buf, *bp;
	int i, n;
	int iswrbk;
	int bvexists;
	ndmp_error rv;
	mem_ndmp_name_v3_t *ep;
	char *dp;
	char *nm;
	int existsvol;
	int isrwdst;

	buf = ndmp_malloc(TLM_MAX_PATH_NAME);
	if (!buf) {
		MOD_LOGV3(params, NDMP_LOG_ERROR, "Insufficient memory.\n");
		return (NDMP_NO_MEM_ERR);
	}

	bvexists = fs_volexist(nlp->nlp_backup_path);
	iswrbk = voliswr(nlp->nlp_backup_path);

	rv = NDMP_NO_ERR;
	n = session->ns_data.dd_nlist_len;
	for (i = 0; i < n; i++) {
		ep = (mem_ndmp_name_v3_t *)MOD_GETNAME(params, i);
		if (!ep)
			continue;

		/* chop off the trailing slashes */
		chopslash(ep->nm3_opath);

		chopslash(ep->nm3_dpath);
		chopslash(ep->nm3_newnm);

		/* existing and non-empty destination path */
		if (ISDEFINED(ep->nm3_dpath)) {
			dp = ep->nm3_dpath;
			existsvol = fs_volexist(dp);
			isrwdst = voliswr(dp);
		} else {
			/* the default destination path is backup directory */
			dp = nlp->nlp_backup_path;
			existsvol = bvexists;
			isrwdst = iswrbk;
		}

		/* check the destination directory exists and is writable */
		if (!existsvol) {
			rv = NDMP_ILLEGAL_ARGS_ERR;
			MOD_LOGV3(params, NDMP_LOG_ERROR,
			    "Invalid destination path volume "
			    "\"%s\".\n", dp);
			break;
		}
		if (!isrwdst) {
			rv = NDMP_ILLEGAL_ARGS_ERR;
			MOD_LOGV3(params, NDMP_LOG_ERROR,
			    "The destination path volume is not "
			    "writable \"%s\".\n", dp);
			break;
		}

		/*
		 * If new name is not specified, the default new name is
		 * the last component of the original path, if any
		 * (except in V4).
		 */
		if (session->ns_protocol_version == NDMPV4) {
			nm = ep->nm3_newnm;
		} else {
			if (ISDEFINED(ep->nm3_newnm)) {
				nm = ep->nm3_newnm;
			} else {
				/*
				 * Find the last component of nm3_opath.
				 * nm3_opath has no trailing '/'.
				 */
				char *p = strrchr(ep->nm3_opath, '/');
				nm = p? p : ep->nm3_opath;
			}
		}

		bp = joinpath(buf, dp, nm);
		if (!bp) {
			/*
			 * Note: What should be done with this entry?
			 * We leave it untouched for now, hence no path in
			 * the backup image matches with this entry and will
			 * be reported as not found.
			 */
			MOD_LOGV3(params, NDMP_LOG_ERROR,
			    "Destination path too long(%s/%s)", dp, nm);
			continue;
		}
		cp = strdup(bp);
		if (!cp) {
			MOD_LOGV3(params, NDMP_LOG_ERROR,
			    "Insufficient memory.\n");
			rv = NDMP_NO_MEM_ERR;
			break;
		}
		free(ep->nm3_dpath);
		ep->nm3_dpath = cp;
		NDMP_FREE(ep->nm3_newnm);

		bp = joinpath(buf, nlp->nlp_backup_path, ep->nm3_opath);
		if (!bp) {
			/*
			 * Note: The same problem of above with long path.
			 */
			MOD_LOGV3(params, NDMP_LOG_ERROR,
			    "Path too long(%s/%s)",
			    nlp->nlp_backup_path, ep->nm3_opath);
			continue;
		}
		cp = strdup(bp);
		if (!cp) {
			MOD_LOGV3(params, NDMP_LOG_ERROR,
			    "Insufficient memory.\n");
			rv = NDMP_NO_MEM_ERR;
			break;
		}
		NDMP_FREE(ep->nm3_opath);
		ep->nm3_opath = cp;

		NDMP_LOG(LOG_DEBUG, "orig[%d]: \"%s\"", i, ep->nm3_opath);
		if (ep->nm3_dpath) {
			NDMP_LOG(LOG_DEBUG,
			    "dest[%d]: \"%s\"", i, ep->nm3_dpath);
		} else {
			NDMP_LOG(LOG_DEBUG, "dest[%d]: \"%s\"", i, "NULL");
		}
	}

	free(buf);

	return (rv);
}


/*
 * allvalidfh
 *
 * Run a sanity check on the file history info. The file history
 * info is the offset of the record starting the entry on the tape
 * and is used in DAR (direct access restore mode).
 */
static boolean_t
allvalidfh(ndmpd_session_t *session, ndmpd_module_params_t *params)
{
	int i, n;
	boolean_t rv;
	mem_ndmp_name_v3_t *ep;

	rv = TRUE;
	n = session->ns_data.dd_nlist_len;
	for (i = 0; i < n; i++) {
		ep = (mem_ndmp_name_v3_t *)MOD_GETNAME(params, i);
		if (!ep)
			continue;
		/*
		 * The fh_info's sent from the client are multiples
		 * of RECORDSIZE which is 512 bytes.
		 *
		 * All our fh_info's are at the RECORDSIZE boundary.  If there
		 * is any fh_info that is less than RECORDSIZE (this covers 0
		 * and -1 values too), then the result is that DAR cannot be
		 * done.
		 */
		if (ep->nm3_fh_info < RECORDSIZE ||
		    ep->nm3_fh_info % RECORDSIZE != 0) {
			rv = FALSE;
			break;
		}
	}

	return (rv);
}


/*
 * log_rs_params_v3
 *
 * Log a copy of all values of the restore parameters
 */
void
log_rs_params_v3(ndmpd_session_t *session, ndmpd_module_params_t *params,
    ndmp_lbr_params_t *nlp)
{
	MOD_LOGV3(params, NDMP_LOG_NORMAL, "Restoring to \"%s\".\n",
	    (nlp->nlp_restore_path) ? nlp->nlp_restore_path : "NULL");

	if (session->ns_data.dd_data_addr.addr_type == NDMP_ADDR_LOCAL) {
		MOD_LOGV3(params, NDMP_LOG_NORMAL, "Tape server: local.\n");
		MOD_LOGV3(params, NDMP_LOG_NORMAL,
		    "Tape record size: %d.\n",
		    session->ns_mover.md_record_size);
	} else if (session->ns_data.dd_data_addr.addr_type == NDMP_ADDR_TCP)
		MOD_LOGV3(params, NDMP_LOG_NORMAL,
		    "Tape server: remote at %s:%d.\n",
		    inet_ntoa(IN_ADDR(session->ns_data.dd_data_addr.tcp_ip_v3)),
		    session->ns_data.dd_data_addr.tcp_port_v3);
	else
		MOD_LOGV3(params, NDMP_LOG_ERROR,
		    "Unknown tape server address type.\n");

	if (NLP_ISSET(nlp, NLPF_DIRECT))
		MOD_LOGV3(params, NDMP_LOG_NORMAL,
		    "Direct Access Restore.\n");
}


/*
 * send_unrecovered_list_v3
 *
 * Create the list of files that were in restore list but
 * not recovered due to some errors.
 */
int
send_unrecovered_list_v3(ndmpd_module_params_t *params, ndmp_lbr_params_t *nlp)
{
	int i, rv;
	int err;

	if (!params) {
		NDMP_LOG(LOG_DEBUG, "params == NULL");
		return (-1);
	}
	if (!nlp) {
		NDMP_LOG(LOG_DEBUG, "nlp == NULL");
		return (-1);
	}

	if (nlp->nlp_lastidx != -1) {
		if (!bm_getone(nlp->nlp_rsbm, (u_longlong_t)nlp->nlp_lastidx))
			err = ENOENT;
		else
			err = 0;
		(void) ndmp_send_recovery_stat_v3(params, nlp,
		    nlp->nlp_lastidx, err);
		nlp->nlp_lastidx = -1;
	}

	rv = 0;
	for (i = 0; i < (int)nlp->nlp_nfiles; i++) {
		if (!bm_getone(nlp->nlp_rsbm, (u_longlong_t)i)) {
			rv = ndmp_send_recovery_stat_v3(params, nlp, i, ENOENT);
			if (rv < 0)
				break;
		}
	}

	return (rv);
}



/*
 * restore_dar_alloc_structs_v3
 *
 * Allocates the necessary structures for running DAR restore.
 * It just creates the reader writer IPC.
 * This function is called for each entry in the restore entry list.
 *
 * Parameters:
 *   session (input) - pointer to the session
 *   jname (input) - Job name
 *
 * Returns:
 *    0: on success
 *   -1: on error
 */
int
restore_dar_alloc_structs_v3(ndmpd_session_t *session, char *jname)
{
	long xfer_size;
	ndmp_lbr_params_t *nlp;
	tlm_commands_t *cmds;

	nlp = ndmp_get_nlp(session);
	if (!nlp) {
		NDMP_LOG(LOG_DEBUG, "nlp == NULL");
		return (-1);
	}

	cmds = &nlp->nlp_cmds;
	(void) memset(cmds, 0, sizeof (*cmds));

	xfer_size = ndmp_buffer_get_size(session);
	cmds->tcs_command = tlm_create_reader_writer_ipc(FALSE, xfer_size);
	if (!cmds->tcs_command) {
		tlm_un_ref_job_stats(jname);
		return (-1);
	}

	return (0);
}


/*
 * free_dar_structs_v3
 *
 * To free the structures were created by restore_dar_alloc_structs_v3.
 * This funnction is called for each entry in restore entry list.
 *
 * Parameters:
 *   session (input) - pointer to the session
 *   jname (input) - job name
 *
 * Returns:
 *	NONE
 */
/*ARGSUSED*/
static void
free_dar_structs_v3(ndmpd_session_t *session, char *jname)
{
	ndmp_lbr_params_t *nlp;
	tlm_commands_t *cmds;

	nlp = ndmp_get_nlp(session);
	if (!nlp) {
		NDMP_LOG(LOG_DEBUG, "nlp == NULL");
		return;
	}
	cmds = &nlp->nlp_cmds;
	if (!cmds) {
		NDMP_LOG(LOG_DEBUG, "cmds == NULL");
		return;
	}

	if (cmds->tcs_command) {
		if (cmds->tcs_command->tc_buffers != NULL)
			tlm_release_reader_writer_ipc(cmds->tcs_command);
		else
			NDMP_LOG(LOG_DEBUG, "BUFFERS == NULL");
		cmds->tcs_command = NULL;
	} else
		NDMP_LOG(LOG_DEBUG, "COMMAND == NULL");
}


/*
 * ndmp_dar_tar_init_v3
 *
 * Constructor for the DAR restore. Creates job name, allocates structures
 * needed for keeping the statistics, and reports the start of restore action.
 * It is called once for each DAR restore request.
 *
 * Parameters:
 *   session (input) - pointer to the session
 *   nlp (input) - pointer to the nlp structure
 *
 * Returns:
 *   char pointer: on success
 *   NULL: on error
 */
static char *ndmpd_dar_tar_init_v3(ndmpd_session_t *session,
    ndmp_lbr_params_t *nlp)
{
	char *jname;

	jname = ndmp_malloc(TLM_MAX_BACKUP_JOB_NAME);

	if (!jname)
		return (NULL);

	(void) ndmp_new_job_name(jname);

	if (!nlp) {
		free(jname);
		NDMP_LOG(LOG_DEBUG, "nlp == NULL");
		return (NULL);
	}

	nlp->nlp_jstat = tlm_new_job_stats(jname);
	if (!nlp->nlp_jstat) {
		free(jname);
		NDMP_LOG(LOG_DEBUG, "Creating job stats");
		return (NULL);
	}

	nlp->nlp_jstat->js_start_ltime = time(NULL);
	nlp->nlp_jstat->js_start_time = nlp->nlp_jstat->js_start_ltime;

	nlp->nlp_logcallbacks = lbrlog_callbacks_init(session,
	    ndmpd_path_restored_v3, NULL, NULL);
	if (!nlp->nlp_logcallbacks) {
		tlm_un_ref_job_stats(jname);
		free(jname);
		return (NULL);
	}
	nlp->nlp_jstat->js_callbacks = (void *)(nlp->nlp_logcallbacks);

	nlp->nlp_rsbm = bm_alloc(nlp->nlp_nfiles, 0);
	if (nlp->nlp_rsbm < 0) {
		NDMP_LOG(LOG_ERR, "Out of memory.");
		lbrlog_callbacks_done(nlp->nlp_logcallbacks);
		tlm_un_ref_job_stats(jname);
		free(jname);
		return (NULL);
	}

	/* this is used in ndmpd_path_restored_v3() */
	nlp->nlp_lastidx = -1;

	NDMP_LOG(LOG_DEBUG, "Restoring from %s tape(s).",
	    ndmp_data_get_mover_mode(session));

	return (jname);
}

/*
 * ndmpd_dar_tar_end_v3
 *
 * Deconstructor for the DAR restore. This function is called once per
 * DAR request. It deallocates memories allocated by ndmpd_dar_tar_init_v3.
 *
 * Parameters:
 *   session (input) - pointer to the session
 *   params (input) - pointer to the parameters structure
 *   nlp (input) - pointer to the nlp structure
 *   jname(input) - job name
 *
 * Returns:
 *   0: on success
 *   -1: on error
 */
static int ndmpd_dar_tar_end_v3(ndmpd_session_t *session,
    ndmpd_module_params_t *params, ndmp_lbr_params_t *nlp, char *jname)
{
	int err = 0;


	NDMP_LOG(LOG_DEBUG, "lastidx %d", nlp->nlp_lastidx);

	/* nothing restored. */
	(void) send_unrecovered_list_v3(params, nlp);

	if (nlp->nlp_jstat) {
		nlp->nlp_bytes_total =
		    (u_longlong_t)nlp->nlp_jstat->js_bytes_total;
		tlm_un_ref_job_stats(jname);
		nlp->nlp_jstat = NULL;
	} else {
		NDMP_LOG(LOG_DEBUG, "JSTAT == NULL");
	}

	if (nlp->nlp_logcallbacks) {
		lbrlog_callbacks_done(nlp->nlp_logcallbacks);
		nlp->nlp_logcallbacks = NULL;
	} else {
		NDMP_LOG(LOG_DEBUG, "FH CALLBACKS == NULL");
	}

	if (session->ns_data.dd_abort) {
		NDMP_LOG(LOG_DEBUG, "Restoring to \"%s\" aborted.",
		    (nlp->nlp_restore_path) ? nlp->nlp_restore_path : "NULL");
		err = EINTR;
	} else {
		NDMP_LOG(LOG_DEBUG, "Restoring to \"%s\" finished. (%d)",
		    (nlp->nlp_restore_path) ? nlp->nlp_restore_path :
		    "NULL", err);
	}

	if (session->ns_data.dd_operation == NDMP_DATA_OP_RECOVER) {
		if (nlp->nlp_rsbm < 0) {
			NDMP_LOG(LOG_DEBUG, "nlp_rsbm < 0 %d", nlp->nlp_rsbm);
		} else {
			(void) bm_free(nlp->nlp_rsbm);
			nlp->nlp_rsbm = -1;
		}
	}

	free(jname);

	return (err);
}


/*
 * ndmpd_dar_tar_v3
 *
 * This function is called for each entry in DAR entry list. The window
 * is already located and we should be in the right position to read
 * the data from the tape.
 * For each entry we setup selection list; so that, if the file name from
 * tape is not as the name client asked for, error be returned.
 *
 * Parameters:
 *   session (input) - pointer to the session
 *   params (input) - pointer to the parameters structure
 *   nlp (input) - pointer to the nlp structure
 *   jname (input) - job name
 *   dar_index(input) - Index of this entry in the restore list
 *
 * Returns:
 *   0: on success
 *   -1: on error
 */
static int
ndmpd_dar_tar_v3(ndmpd_session_t *session, ndmpd_module_params_t *params,
    ndmp_lbr_params_t *nlp, char *jname, int dar_index)
{
	char *excl;
	char **sels;
	int flags;
	int err;
	tlm_commands_t *cmds;
	struct rs_name_maker rn;
	int data_addr_type = session->ns_data.dd_data_addr.addr_type;
	ndmp_tar_reader_arg_t arg;
	pthread_t rdtp;
	ndmp_context_t nctx;
	mem_ndmp_name_v3_t *ep;

	err = 0;

	/*
	 * We have to allocate and deallocate buffers every time we
	 * run the restore, for we need to flush the buffers.
	 */
	if (restore_dar_alloc_structs_v3(session, jname) < 0)
		return (-1);

	sels = setupsels(session, params, nlp, dar_index);
	if (!sels) {
		free_dar_structs_v3(session, jname);
		return (-1);
	}
	excl = NULL;
	flags = RSFLG_OVR_ALWAYS;
	rn.rn_nlp = nlp;
	rn.rn_fp = mknewname;

	if (!session->ns_data.dd_abort) {
		cmds = &nlp->nlp_cmds;
		cmds->tcs_reader = cmds->tcs_writer = TLM_RESTORE_RUN;
		cmds->tcs_command->tc_reader = TLM_RESTORE_RUN;
		cmds->tcs_command->tc_writer = TLM_RESTORE_RUN;

		arg.tr_session = session;
		arg.tr_mod_params = params;
		arg.tr_cmds = cmds;

		err = pthread_create(&rdtp, NULL, (funct_t)ndmp_tar_reader,
		    (void *)&arg);
		if (err == 0) {
			tlm_cmd_wait(cmds->tcs_command, TLM_TAR_READER);
		} else {
			NDMP_LOG(LOG_DEBUG, "launch ndmp_tar_reader: %m");
			return (-1);
		}

		cmds->tcs_command->tc_ref++;
		cmds->tcs_writer_count++;

		/* Plug-in module */
		if (ndmp_pl != NULL &&
		    ndmp_pl->np_pre_restore != NULL) {
			nctx.nc_cmds = cmds;
			nctx.nc_params = params;
			ep = (mem_ndmp_name_v3_t *)MOD_GETNAME(params,
			    dar_index - 1);

			if ((err = ndmp_pl->np_pre_restore(ndmp_pl, &nctx,
			    ep->nm3_opath, ep->nm3_dpath))
			    != 0) {
				NDMP_LOG(LOG_ERR, "Pre-restore plug-in: %m");
				cmds->tcs_command->tc_reader = TLM_STOP;
				ndmp_stop_local_reader(session, cmds);
				ndmp_wait_for_reader(cmds);
				(void) pthread_join(rdtp, NULL);
				ndmp_stop_remote_reader(session);
				goto restore_out;
			}
		}

		if (tm_tar_ops.tm_getdir != NULL) {
			err = (tm_tar_ops.tm_getdir)(cmds, cmds->tcs_command,
			    nlp->nlp_jstat, &rn, 1, 1, sels, &excl, flags,
			    dar_index, nlp->nlp_backup_path,
			    session->hardlink_q);
		}

		cmds->tcs_writer_count--;
		cmds->tcs_command->tc_ref--;
		cmds->tcs_command->tc_reader = TLM_STOP;


		/*
		 * If it is a two-way restore then we stop the reader.
		 */
		NDMP_LOG(LOG_DEBUG, "stop local reader.");
		ndmp_stop_local_reader(session, cmds);

		ndmp_wait_for_reader(cmds);
		(void) pthread_join(rdtp, NULL);

		/*
		 * If this is the last DAR entry and it is a three-way
		 * restore then we should close the connection.
		 */
		if ((data_addr_type == NDMP_ADDR_TCP) &&
		    (dar_index == (int)session->ns_data.dd_nlist_len)) {
			NDMP_LOG(LOG_DEBUG, "stop remote reader.");
			ndmp_stop_remote_reader(session);
		}

		/* exit as if there was an internal error */
		if (session->ns_eof)
			err = -1;
restore_out:
		/* Plug-in module */
		if (ndmp_pl != NULL &&
		    ndmp_pl->np_post_restore != NULL &&
		    ndmp_pl->np_post_restore(ndmp_pl, &nctx, err) == -1) {
			NDMP_LOG(LOG_DEBUG, "Post-restore plug-in: %m");
			err = -1;
		}
	}

	NDMP_FREE(sels);

	free_dar_structs_v3(session, jname);

	return (err);
}

/*
 * ndmpd_dar_locate_windwos_v3
 *
 * Locating the right window in which the requested file is backed up.
 * We should go through windows to find the exact location, for the
 * file can be located in for example 10th window after the current window.
 *
 * Parameters:
 *   session (input) - pointer to the session
 *   params (input) - pointer to the parameters structure
 *   fh_info (input) - index from the beginning of the backup stream
 *   len (input) - Length of the mover window
 *
 * Returns:
 *   0: on success
 *   -1: on error
 */
static int
ndmpd_dar_locate_window_v3(ndmpd_session_t *session,
    ndmpd_module_params_t *params, u_longlong_t fh_info, u_longlong_t len)
{
	int ret = 0;


	for (; ; ) {
		ret = (*params->mp_seek_func)(session, fh_info, len);

		NDMP_LOG(LOG_DEBUG, "ret %d", ret);
		if (ret == 0) /* Seek was done successfully */
			break;
		else if (ret < 0) {
			NDMP_LOG(LOG_DEBUG, "Seek error");
			break;
		}

		/*
		 * DMA moved to a new window.
		 * If we are reading the remainig of the file from
		 * new window, seek is handled by ndmpd_local_read_v3.
		 * Here we should continue the seek inside the new
		 * window.
		 */
		continue;
	}
	return (ret);
}

/*
 * ndmpd_rs_dar_tar_v3
 *
 * Main DAR function. It calls the constructor, then for each entry it
 * calls the locate_window_v3 to find the exact position of the file. Then
 * it restores the file.
 * When all restore requests are done it calls the deconstructor to clean
 * everything up.
 *
 * Parameters:
 *   session (input) - pointer to the session
 *   params (input) - pointer to the parameters structure
 *   nlp (input) - pointer to the nlp structure
 *
 * Returns:
 *   0: on success
 *   -1: on error
 */
static int
ndmpd_rs_dar_tar_v3(ndmpd_session_t *session, ndmpd_module_params_t *params,
    ndmp_lbr_params_t *nlp)
{
	mem_ndmp_name_v3_t *ep;
	u_longlong_t len;
	char *jname;
	int n = session->ns_data.dd_nlist_len;
	int i, ret = 0;
	int result = 0;

	jname = ndmpd_dar_tar_init_v3(session, nlp);

	if (!jname)
		return (-1);

	/*
	 * We set the length = sizeof (tlm_tar_hdr_t)
	 * This is important for three-way DAR restore, for we should
	 * read the header first (If we ask for more data then we have
	 * to read and discard the remaining data in the socket)
	 */
	len = tlm_tarhdr_size();

	for (i = 0; i < n; ++i) {
		ep = (mem_ndmp_name_v3_t *)MOD_GETNAME(params, i);
		if (!ep) {
			NDMP_LOG(LOG_DEBUG, "ep NULL, i %d", i);
			continue;
		}
		NDMP_LOG(LOG_DEBUG,
		    "restoring opath %s, dpath %s, fh_info %lld",
		    ep->nm3_opath ? ep->nm3_opath : "NULL",
		    ep->nm3_dpath ? ep->nm3_dpath : "NULL",
		    ep->nm3_fh_info);

		/*
		 * We should seek till finding the window in which file
		 * is located.
		 */
		ret = ndmpd_dar_locate_window_v3(session, params,
		    ep->nm3_fh_info, len);

		if (ret < 0) /* If seek fails, restore should be aborted */
			break;
		/*
		 * We are inside the target window.
		 * for each restore we will use one entry as selection list
		 */
		if ((ret = ndmpd_dar_tar_v3(session, params, nlp, jname, i+1))
		    != 0)
			result = EIO;
		ndmpd_audit_restore(session->ns_connection,
		    ep->nm3_opath ? ep->nm3_opath : "NULL",
		    session->ns_data.dd_data_addr.addr_type,
		    session->ns_tape.td_adapter_name, result);
	}

	NDMP_LOG(LOG_DEBUG, "End of restore list");

	(void) ndmpd_dar_tar_end_v3(session, params, nlp, jname);

	return (ret);
}

/*
 * ndmp_plugin_pre_restore
 *
 * Wrapper for pre-restore callback with multiple path
 */
static int
ndmp_plugin_pre_restore(ndmp_context_t *ctxp, ndmpd_module_params_t *params,
    int ncount)
{
	mem_ndmp_name_v3_t *ep;
	int err;
	int i;

	for (i = 0; i < ncount; i++) {
		if (!(ep = (mem_ndmp_name_v3_t *)MOD_GETNAME(params, i)))
			continue;
		if ((err = ndmp_pl->np_pre_restore(ndmp_pl, ctxp,
		    ep->nm3_opath, ep->nm3_dpath)) != 0)
			return (err);
	}

	return (0);
}

/*
 * get_absolute_path
 *
 * Get resolved path name which does not involve ".", ".." or extra
 * "/" or symbolic links.
 *
 * e.g.
 *
 * /backup/path/ -> /backup/path
 * /backup/path/. -> /backup/path
 * /backup/path/../path/ -> /backup/path
 * /link-to-backup-path -> /backup/path
 *
 * Returns:
 * 	Pointer to the new path (allocated)
 * 	NULL if the path doesnt exist
 */
static char *
get_absolute_path(const char *bkpath)
{
	char *pbuf;
	char *rv;

	if (!(pbuf = ndmp_malloc(TLM_MAX_PATH_NAME)))
		return (NULL);

	if ((rv = realpath(bkpath, pbuf)) == NULL) {
		NDMP_LOG(LOG_DEBUG, "Invalid path [%s] err=%d",
		    bkpath, errno);
	}
	return (rv);
}


/*
 * Expands the format string and logs the resulting message to the
 * remote DMA
 */
void
ndmp_log_dma(ndmp_context_t *nctx, ndmp_log_dma_type_t lt, const char *fmt, ...)
{
	va_list ap;
	char buf[256];
	ndmpd_module_params_t *params;

	if (nctx == NULL ||
	    (params = (ndmpd_module_params_t *)nctx->nc_params) == NULL)
		return;

	va_start(ap, fmt);
	(void) vsnprintf(buf, sizeof (buf), fmt, ap);
	va_end(ap);

	MOD_LOGV3(params, (ndmp_log_type)lt, "%s", buf);
}


/*
 * ndmpd_rs_sar_tar_v3
 *
 * Main non-DAR restore function. It will try to restore all the entries
 * that have been backed up.
 *
 * Parameters:
 *   session (input) - pointer to the session
 *   params (input) - pointer to the parameters structure
 *   nlp (input) - pointer to the nlp structure
 *
 * Returns:
 *   0: on success
 *   -1: on error
 */
static int
ndmpd_rs_sar_tar_v3(ndmpd_session_t *session, ndmpd_module_params_t *params,
    ndmp_lbr_params_t *nlp)
{
	char jname[TLM_MAX_BACKUP_JOB_NAME];
	char *excl;
	char **sels;
	int flags;
	int err;
	tlm_commands_t *cmds;
	struct rs_name_maker rn;
	ndmp_tar_reader_arg_t arg;
	pthread_t rdtp;
	int result;
	ndmp_context_t nctx;

	result = err = 0;
	(void) ndmp_new_job_name(jname);
	if (restore_alloc_structs_v3(session, jname) < 0)
		return (-1);

	sels = setupsels(session, params, nlp, 0);
	if (!sels) {
		free_structs_v3(session, jname);
		return (-1);
	}
	excl = NULL;
	flags = RSFLG_OVR_ALWAYS;
	rn.rn_nlp = nlp;
	rn.rn_fp = mknewname;

	nlp->nlp_jstat->js_start_ltime = time(NULL);
	nlp->nlp_jstat->js_start_time = nlp->nlp_jstat->js_start_ltime;

	if (!session->ns_data.dd_abort && !session->ns_data.dd_abort) {
		cmds = &nlp->nlp_cmds;
		cmds->tcs_reader = cmds->tcs_writer = TLM_RESTORE_RUN;
		cmds->tcs_command->tc_reader = TLM_RESTORE_RUN;
		cmds->tcs_command->tc_writer = TLM_RESTORE_RUN;

		NDMP_LOG(LOG_DEBUG, "Restoring to \"%s\" started.",
		    (nlp->nlp_restore_path) ? nlp->nlp_restore_path : "NULL");

		arg.tr_session = session;
		arg.tr_mod_params = params;
		arg.tr_cmds = cmds;
		err = pthread_create(&rdtp, NULL, (funct_t)ndmp_tar_reader,
		    (void *)&arg);
		if (err == 0) {
			tlm_cmd_wait(cmds->tcs_command, TLM_TAR_READER);
		} else {
			NDMP_LOG(LOG_DEBUG, "Launch ndmp_tar_reader: %m");
			free_structs_v3(session, jname);
			return (-1);
		}

		if (!ndmp_check_utf8magic(cmds->tcs_command)) {
			NDMP_LOG(LOG_DEBUG, "UTF8Magic not found!");
		} else {
			NDMP_LOG(LOG_DEBUG, "UTF8Magic found");
		}

		/* Plug-in module */
		if (ndmp_pl != NULL &&
		    ndmp_pl->np_pre_restore != NULL) {
			nctx.nc_cmds = cmds;
			nctx.nc_params = params;
			if ((err = ndmp_plugin_pre_restore(&nctx, params,
			    nlp->nlp_nfiles))
			    != 0) {
				NDMP_LOG(LOG_ERR, "Pre-restore plug-in: %m");
				cmds->tcs_command->tc_reader = TLM_STOP;
				ndmp_stop_local_reader(session, cmds);
				ndmp_wait_for_reader(cmds);
				(void) pthread_join(rdtp, NULL);
				ndmp_stop_remote_reader(session);
				goto restore_out;
			}
		}

		cmds->tcs_command->tc_ref++;
		cmds->tcs_writer_count++;

		if (tm_tar_ops.tm_getdir != NULL)
			err = (tm_tar_ops.tm_getdir)(cmds, cmds->tcs_command,
			    nlp->nlp_jstat, &rn, 1, 1, sels, &excl, flags, 0,
			    nlp->nlp_backup_path, session->hardlink_q);

		cmds->tcs_writer_count--;
		cmds->tcs_command->tc_ref--;
		cmds->tcs_command->tc_reader = TLM_STOP;
		nlp->nlp_jstat->js_stop_time = time(NULL);

		/* Send the list of un-recovered files/dirs to the client.  */
		(void) send_unrecovered_list_v3(params, nlp);

		ndmp_stop_local_reader(session, cmds);
		ndmp_wait_for_reader(cmds);
		(void) pthread_join(rdtp, NULL);

		ndmp_stop_remote_reader(session);

		/* exit as if there was an internal error */
		if (session->ns_eof)
			err = -1;
		if (err == -1)
			result = EIO;
	}

	(void) send_unrecovered_list_v3(params, nlp); /* nothing restored. */
	if (session->ns_data.dd_abort) {
		NDMP_LOG(LOG_DEBUG, "Restoring to \"%s\" aborted.",
		    (nlp->nlp_restore_path) ? nlp->nlp_restore_path : "NULL");
		result = EINTR;
		ndmpd_audit_restore(session->ns_connection,
		    nlp->nlp_restore_path,
		    session->ns_data.dd_data_addr.addr_type,
		    session->ns_tape.td_adapter_name, result);
		err = -1;
	} else {
		NDMP_LOG(LOG_DEBUG, "Restoring to \"%s\" finished. (%d)",
		    (nlp->nlp_restore_path) ? nlp->nlp_restore_path : "NULL",
		    err);
		ndmpd_audit_restore(session->ns_connection,
		    nlp->nlp_restore_path,
		    session->ns_data.dd_data_addr.addr_type,
		    session->ns_tape.td_adapter_name, result);

restore_out:
		/* Plug-in module */
		if (ndmp_pl != NULL &&
		    ndmp_pl->np_post_restore != NULL &&
		    ndmp_pl->np_post_restore(ndmp_pl, &nctx, err) == -1) {
			NDMP_LOG(LOG_DEBUG, "Post-restore plug-in: %m");
			err = -1;
		}
	}

	NDMP_FREE(sels);
	free_structs_v3(session, jname);

	return (err);
}


/*
 * ndmp_backup_get_params_v3
 *
 * Get the backup parameters from the NDMP env variables
 * and log them in the system log and as normal messages
 * to the DMA.
 *
 * Parameters:
 *   session (input) - pointer to the session
 *   params (input) - pointer to the parameters structure
 *
 * Returns:
 *   NDMP_NO_ERR: on success
 *   != NDMP_NO_ERR: otherwise
 */
ndmp_error
ndmp_backup_get_params_v3(ndmpd_session_t *session,
    ndmpd_module_params_t *params)
{
	ndmp_error rv;
	ndmp_lbr_params_t *nlp;

	if (!session || !params)
		return (NDMP_ILLEGAL_ARGS_ERR);

	rv = NDMP_NO_ERR;
	nlp = ndmp_get_nlp(session);
	if (!nlp) {
		MOD_LOGV3(params, NDMP_LOG_ERROR,
		    "Internal error: NULL nlp.\n");
		rv = NDMP_ILLEGAL_ARGS_ERR;
	} else {
		if (!(nlp->nlp_backup_path = get_bk_path_v3(params)))
			rv = NDMP_FILE_NOT_FOUND_ERR;
		else if (!is_valid_backup_dir_v3(params, nlp->nlp_backup_path))
			rv = NDMP_ILLEGAL_ARGS_ERR;
	}

	nlp->nlp_backup_path = get_absolute_path(nlp->nlp_backup_path);
	if (!nlp->nlp_backup_path)
		rv = NDMP_FILE_NOT_FOUND_ERR;

	if (rv != NDMP_NO_ERR)
		return (rv);

	if (fs_is_chkpntvol(nlp->nlp_backup_path) ||
	    fs_is_rdonly(nlp->nlp_backup_path) ||
	    !fs_is_chkpnt_enabled(nlp->nlp_backup_path))
		NLP_SET(nlp, NLPF_CHKPNTED_PATH);
	else
		NLP_UNSET(nlp, NLPF_CHKPNTED_PATH);

	/* Should the st_ctime be ignored when backing up? */
	if (ndmp_ignore_ctime) {
		NDMP_LOG(LOG_DEBUG, "ignoring st_ctime");
		NLP_SET(nlp, NLPF_IGNCTIME);
	} else {
		NLP_UNSET(nlp, NLPF_IGNCTIME);
	}

	if (ndmp_include_lmtime == TRUE) {
		NDMP_LOG(LOG_DEBUG, "including st_lmtime");
		NLP_SET(nlp, NLPF_INCLMTIME);
	} else {
		NLP_UNSET(nlp, NLPF_INCLMTIME);
	}

	NDMP_LOG(LOG_DEBUG, "flags %x", nlp->nlp_flags);

	get_hist_env_v3(params, nlp);
	get_exc_env_v3(params, nlp);
	get_inc_env_v3(params, nlp);
	get_direct_env_v3(params, nlp);
	rv = get_backup_level_v3(params, nlp);

	return (rv);
}


/*
 * ndmpd_tar_backup_starter_v3
 *
 * Create the checkpoint for the backup and do the backup,
 * then remove the backup checkpoint if we created it.
 * Save the backup time information based on the backup
 * type and stop the data server.
 *
 * Parameters:
 *   params (input) - pointer to the parameters structure
 *
 * Returns:
 *   0: on success
 *   != 0: otherwise
 */
int
ndmpd_tar_backup_starter_v3(ndmpd_module_params_t *params)
{
	int err;
	ndmpd_session_t *session;
	ndmp_lbr_params_t *nlp;
	char jname[TLM_MAX_BACKUP_JOB_NAME];
	ndmp_bkup_size_arg_t sarg;
	pthread_t tid;

	session = (ndmpd_session_t *)(params->mp_daemon_cookie);
	*(params->mp_module_cookie) = nlp = ndmp_get_nlp(session);
	ndmp_session_ref(session);
	(void) ndmp_new_job_name(jname);

	err = 0;
	if (!NLP_ISCHKPNTED(nlp) &&
	    ndmp_start_check_point(nlp->nlp_backup_path, jname) < 0) {
		MOD_LOGV3(params, NDMP_LOG_ERROR,
		    "Creating checkpoint on \"%s\".\n",
		    nlp->nlp_backup_path);
		err = -1;
	}

	NDMP_LOG(LOG_DEBUG, "err %d, chkpnted %c",
	    err, NDMP_YORN(NLP_ISCHKPNTED(nlp)));

	if (err == 0) {
		sarg.bs_session = session;
		sarg.bs_jname = jname;
		sarg.bs_path = nlp->nlp_backup_path;

		/* Get an estimate of the data size */
		if (pthread_create(&tid, NULL, (funct_t)get_backup_size,
		    (void *)&sarg) == 0)
			(void) pthread_detach(tid);

		err = ndmp_get_cur_bk_time(nlp, &nlp->nlp_cdate, jname);
		if (err != 0) {
			NDMP_LOG(LOG_DEBUG, "err %d", err);
		} else {
			log_bk_params_v3(session, params, nlp);
			err = tar_backup_v3(session, params, nlp, jname);
		}
	}

	if (!NLP_ISCHKPNTED(nlp))
		(void) ndmp_release_check_point(nlp->nlp_backup_path, jname);

	NDMP_LOG(LOG_DEBUG, "err %d, update %c",
	    err, NDMP_YORN(NLP_SHOULD_UPDATE(nlp)));

	if (err == 0)
		save_backup_date_v3(params, nlp);

	MOD_DONE(params, err);

	/* nlp_params is allocated in start_backup_v3() */
	NDMP_FREE(nlp->nlp_params);
	NDMP_FREE(nlp->nlp_backup_path);

	NS_DEC(nbk);
	ndmp_session_unref(session);
	return (err);

}


/*
 * ndmpd_tar_backup_abort_v3
 *
 * Abort the backup operation and stop the reader thread.
 *
 * Parameters:
 *   module_cookie (input) - pointer to the nlp structure
 *
 * Returns:
 *   0: always
 */
int
ndmpd_tar_backup_abort_v3(void *module_cookie)
{
	ndmp_lbr_params_t *nlp;

	nlp = (ndmp_lbr_params_t *)module_cookie;
	if (nlp && nlp->nlp_session) {
		if (nlp->nlp_session->ns_data.dd_data_addr.addr_type ==
		    NDMP_ADDR_TCP &&
		    nlp->nlp_session->ns_data.dd_sock != -1) {
			(void) close(nlp->nlp_session->ns_data.dd_sock);
			nlp->nlp_session->ns_data.dd_sock = -1;
		}
		ndmp_stop_reader_thread(nlp->nlp_session);
	}

	return (0);
}


/*
 * ndmp_restore_get_params_v3
 *
 * Get the parameters specified for recovery such as restore path, type
 * of restore (DAR, non-DAR) etc
 *
 * Parameters:
 *   session (input) - pointer to the session
 *   params (input) - pointer to the parameters structure
 *
 * Returns:
 *   NDMP_NO_ERR: on success
 *   != NDMP_NO_ERR: otherwise
 */
ndmp_error
ndmp_restore_get_params_v3(ndmpd_session_t *session,
    ndmpd_module_params_t *params)
{
	ndmp_error rv;
	ndmp_lbr_params_t *nlp;

	if (!(nlp = ndmp_get_nlp(session))) {
		NDMP_LOG(LOG_DEBUG, "nlp is NULL");
		rv = NDMP_ILLEGAL_ARGS_ERR;
	} else if (!(nlp->nlp_backup_path = get_bk_path_v3(params)))
		rv = NDMP_ILLEGAL_ARGS_ERR;
	else if ((nlp->nlp_nfiles = session->ns_data.dd_nlist_len) == 0) {
		NDMP_LOG(LOG_DEBUG, "nfiles: %d", nlp->nlp_nfiles);
		rv = NDMP_ILLEGAL_ARGS_ERR;
	} else if (get_rs_path_v3(params, nlp) != NDMP_NO_ERR) {
		rv = NDMP_ILLEGAL_ARGS_ERR;
	} else if ((rv = fix_nlist_v3(session, params, nlp)) != NDMP_NO_ERR) {
		NDMP_LOG(LOG_DEBUG, "fix_nlist_v3: %d", rv);
	} else {
		rv = NDMP_NO_ERR;
		get_direct_env_v3(params, nlp);
		if (NLP_ISSET(nlp, NLPF_DIRECT)) {
			if (NLP_ISSET(nlp, NLPF_RECURSIVE)) {
				/* Currently we dont support DAR on directory */
				NDMP_LOG(LOG_DEBUG,
				    "Can't have RECURSIVE and DIRECT together");
				rv = NDMP_ILLEGAL_ARGS_ERR;
				return (rv);
			}

			/*
			 * DAR can be done if all the fh_info's are valid.
			 */
			if (allvalidfh(session, params)) {
				ndmp_sort_nlist_v3(session);
			} else {
				MOD_LOGV3(params, NDMP_LOG_WARNING,
				    "Cannot do direct access recovery. "
				    "Some 'fh_info'es are not valid.\n");
				NLP_UNSET(nlp, NLPF_DIRECT);
			}
		}

		log_rs_params_v3(session, params, nlp);
	}

	return (rv);
}


/*
 * ndmpd_tar_restore_starter_v3
 *
 * The main restore starter function. It will start a DAR or
 * non-DAR recovery based on the parameters. (V3 and V4 only)
 *
 * Parameters:
 *   params (input) - pointer to the parameters structure
 *
 * Returns:
 *   NDMP_NO_ERR: on success
 *   != NDMP_NO_ERR: otherwise
 */
int
ndmpd_tar_restore_starter_v3(ndmpd_module_params_t *params)
{
	int err;
	ndmpd_session_t *session;
	ndmp_lbr_params_t *nlp;


	session = (ndmpd_session_t *)(params->mp_daemon_cookie);
	*(params->mp_module_cookie) = nlp = ndmp_get_nlp(session);
	ndmp_session_ref(session);

	if (NLP_ISSET(nlp, NLPF_DIRECT))
		err = ndmpd_rs_dar_tar_v3(session, params, nlp);
	else
		err = ndmpd_rs_sar_tar_v3(session, params, nlp);

	MOD_DONE(params, err);

	NS_DEC(nrs);
	/* nlp_params is allocated in start_recover() */
	NDMP_FREE(nlp->nlp_params);
	ndmp_session_unref(session);
	return (err);

}


/*
 * ndmp_tar_restore_abort_v3
 *
 * Restore abort function (V3 and V4 only)
 *
 * Parameters:
 *   module_cookie (input) - pointer to nlp
 *
 * Returns:
 *   0
 */
int
ndmpd_tar_restore_abort_v3(void *module_cookie)
{
	ndmp_lbr_params_t *nlp;

	nlp = (ndmp_lbr_params_t *)module_cookie;
	if (nlp != NULL && nlp->nlp_session != NULL) {
		if (nlp->nlp_session->ns_data.dd_mover.addr_type ==
		    NDMP_ADDR_TCP &&
		    nlp->nlp_session->ns_data.dd_sock != -1) {
			(void) close(nlp->nlp_session->ns_data.dd_sock);
			nlp->nlp_session->ns_data.dd_sock = -1;
		}
		nlp_event_nw(nlp->nlp_session);
		ndmp_stop_writer_thread(nlp->nlp_session);
	}


	return (0);

}