--- old/usr/src/cmd/ndmpd/ndmp/ndmpd_chkpnt.c	Mon Aug  5 17:15:15 2019
+++ new/usr/src/cmd/ndmpd/ndmp/ndmpd_chkpnt.c	Mon Aug  5 17:15:15 2019
@@ -3,6 +3,7 @@
  * Copyright (c) 2013, 2015 by Delphix. All rights reserved.
  * Copyright (c) 2013 Steven Hartland. All rights reserved.
  * Copyright (c) 2016 Martin Matuska. All rights reserved.
+ * Copyright 2017 Nexenta Systems, Inc. All rights reserved.
  */
 
 /*
@@ -39,174 +40,33 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <syslog.h>
 #include <stdio.h>
 #include <string.h>
+#include <sys/mount.h>
 #include "ndmpd.h"
 #include <libzfs.h>
 
-typedef struct snap_param {
-	char *snp_name;
-	boolean_t snp_found;
-} snap_param_t;
-
-static int cleanup_fd = -1;
-
 /*
- * ndmp_has_backup
- *
- * Call backup function which looks for backup snapshot.
- * This is a callback function used with zfs_iter_snapshots.
- *
- * Parameters:
- *   zhp (input) - ZFS handle pointer
- *   data (output) - 0 - no backup snapshot
- *		     1 - has backup snapshot
- *
- * Returns:
- *   0: on success
- *  -1: otherwise
- */
-static int
-ndmp_has_backup(zfs_handle_t *zhp, void *data)
-{
-	const char *name;
-	snap_param_t *chp = (snap_param_t *)data;
-
-	name = zfs_get_name(zhp);
-	if (name == NULL ||
-	    strstr(name, chp->snp_name) == NULL) {
-		zfs_close(zhp);
-		return (-1);
-	}
-
-	chp->snp_found = 1;
-	zfs_close(zhp);
-
-	return (0);
-}
-
-/*
- * ndmp_has_backup_snapshot
- *
- * Returns TRUE if the volume has an active backup snapshot, otherwise,
- * returns FALSE.
- *
- * Parameters:
- *   volname (input) - name of the volume
- *
- * Returns:
- *   0: on success
- *  -1: otherwise
- */
-static int
-ndmp_has_backup_snapshot(char *volname, char *jobname)
-{
-	zfs_handle_t *zhp;
-	snap_param_t snp;
-	char chname[ZFS_MAX_DATASET_NAME_LEN];
-
-	(void) mutex_lock(&zlib_mtx);
-	if ((zhp = zfs_open(zlibh, volname, ZFS_TYPE_DATASET)) == 0) {
-		NDMP_LOG(LOG_ERR, "Cannot open snapshot %s.", volname);
-		(void) mutex_unlock(&zlib_mtx);
-		return (-1);
-	}
-
-	snp.snp_found = 0;
-	(void) snprintf(chname, ZFS_MAX_DATASET_NAME_LEN, "@%s", jobname);
-	snp.snp_name = chname;
-
-	(void) zfs_iter_snapshots(zhp, B_FALSE, ndmp_has_backup, &snp);
-	zfs_close(zhp);
-	(void) mutex_unlock(&zlib_mtx);
-
-	return (snp.snp_found);
-}
-
-/*
- * ndmp_create_snapshot
- *
- * This function will parse the path to get the real volume name.
- * It will then create a snapshot based on volume and job name.
- * This function should be called before the NDMP backup is started.
- *
- * Parameters:
- *   vol_name (input) - name of the volume
- *
- * Returns:
- *   0: on success
- *   -1: otherwise
- */
-int
-ndmp_create_snapshot(char *vol_name, char *jname)
-{
-	char vol[ZFS_MAX_DATASET_NAME_LEN];
-
-	if (vol_name == 0 ||
-	    get_zfsvolname(vol, sizeof (vol), vol_name) == -1)
-		return (0);
-
-	/*
-	 * If there is an old snapshot left from the previous
-	 * backup it could be stale one and it must be
-	 * removed before using it.
-	 */
-	if (ndmp_has_backup_snapshot(vol, jname))
-		(void) snapshot_destroy(vol, jname, B_FALSE, B_TRUE, NULL);
-
-	return (snapshot_create(vol, jname, B_FALSE, B_TRUE));
-}
-
-/*
- * ndmp_remove_snapshot
- *
- * This function will parse the path to get the real volume name.
- * It will then remove the snapshot for that volume and job name.
- * This function should be called after NDMP backup is finished.
- *
- * Parameters:
- *   vol_name (input) - name of the volume
- *
- * Returns:
- *   0: on success
- *   -1: otherwise
- */
-int
-ndmp_remove_snapshot(char *vol_name, char *jname)
-{
-	char vol[ZFS_MAX_DATASET_NAME_LEN];
-
-	if (vol_name == 0 ||
-	    get_zfsvolname(vol, sizeof (vol), vol_name) == -1)
-		return (0);
-
-	return (snapshot_destroy(vol, jname, B_FALSE, B_TRUE, NULL));
-}
-
-/*
  * Put a hold on snapshot
  */
 int
-snapshot_hold(char *volname, char *snapname, char *jname, boolean_t recursive)
+snapshot_hold(char *volname, char *snapname, char *jname)
 {
 	zfs_handle_t *zhp;
 	char *p;
 
 	if ((zhp = zfs_open(zlibh, volname, ZFS_TYPE_DATASET)) == 0) {
-		NDMP_LOG(LOG_ERR, "Cannot open volume %s.", volname);
+		syslog(LOG_ERR, "Cannot open volume %s.", volname);
 		return (-1);
 	}
-
-	if (cleanup_fd == -1 && (cleanup_fd = open(ZFS_DEV,
-	    O_RDWR|O_EXCL)) < 0) {
-		NDMP_LOG(LOG_ERR, "Cannot open dev %d", errno);
-		zfs_close(zhp);
-		return (-1);
-	}
-
 	p = strchr(snapname, '@') + 1;
-	if (zfs_hold(zhp, p, jname, recursive, cleanup_fd) != 0) {
-		NDMP_LOG(LOG_ERR, "Cannot hold snapshot %s", p);
+	/*
+	 * The -1 tells the lower levels there are no snapshots
+	 * to clean up.
+	 */
+	if (zfs_hold(zhp, p, jname, B_FALSE, -1) != 0) {
+		syslog(LOG_ERR, "Cannot hold snapshot %s", p);
 		zfs_close(zhp);
 		return (-1);
 	}
@@ -215,8 +75,7 @@
 }
 
 int
-snapshot_release(char *volname, char *snapname, char *jname,
-    boolean_t recursive)
+snapshot_release(char *volname, char *snapname, char *jname)
 {
 	zfs_handle_t *zhp;
 	char *p;
@@ -223,130 +82,250 @@
 	int rv = 0;
 
 	if ((zhp = zfs_open(zlibh, volname, ZFS_TYPE_DATASET)) == 0) {
-		NDMP_LOG(LOG_ERR, "Cannot open volume %s", volname);
+		syslog(LOG_ERR, "Cannot open volume %s", volname);
 		return (-1);
 	}
 
 	p = strchr(snapname, '@') + 1;
-	if (zfs_release(zhp, p, jname, recursive) != 0) {
-		NDMP_LOG(LOG_DEBUG, "Cannot release snapshot %s", p);
+	if (zfs_release(zhp, p, jname, B_FALSE) != 0) {
+		syslog(LOG_DEBUG, "Cannot release snapshot %s", p);
 		rv = -1;
 	}
-	if (cleanup_fd != -1) {
-		(void) close(cleanup_fd);
-		cleanup_fd = -1;
-	}
 	zfs_close(zhp);
 	return (rv);
 }
 
 /*
- * Create a snapshot on the volume
+ * Create a snapshot, put a hold on it, clone it, and mount it in a
+ * well known location for so the backup process can traverse its
+ * directory tree structure.
  */
 int
-snapshot_create(char *volname, char *jname, boolean_t recursive,
-    boolean_t hold)
+backup_dataset_create(ndmp_lbr_params_t *nlp)
 {
-	char snapname[ZFS_MAX_DATASET_NAME_LEN];
+	char zpoolname[ZFS_MAX_DATASET_NAME_LEN];
+	char *slash;
 	int rv;
 
-	if (!volname || !*volname)
+	if (nlp == NULL) {
 		return (-1);
+	}
 
-	(void) snprintf(snapname, ZFS_MAX_DATASET_NAME_LEN,
-	    "%s@%s", volname, jname);
+	(void) strlcpy(zpoolname, nlp->nlp_vol, sizeof (zpoolname));
+	/*
+	 * Pull out the pool name component from the volname
+	 * to use it to build snapshot and clone names.
+	 */
+	slash = strchr(zpoolname, '/');
+	if (slash != NULL) {
+		*slash = '\0';
+	}
 
+	(void) snprintf(nlp->nlp_clonename, sizeof (nlp->nlp_clonename),
+	    "%s/%s", zpoolname, nlp->nlp_job_name);
+
 	(void) mutex_lock(&zlib_mtx);
-	if ((rv = zfs_snapshot(zlibh, snapname, recursive, NULL))
-	    == -1) {
-		if (errno == EEXIST) {
+
+	/*
+	 * If "checkpoint" is not enabled, create the normal
+	 * snapshot and continue normal backup.  If it is
+	 * enabled, the "checkpoint" name has been already set
+	 * so we just have to clone it.
+	 */
+	if (!NLP_ISCHKPNTED(nlp)) {
+		(void) snprintf(nlp->nlp_snapname, sizeof (nlp->nlp_snapname),
+		    "%s@%s", nlp->nlp_vol, nlp->nlp_job_name);
+
+		if ((rv = zfs_snapshot(zlibh, nlp->nlp_snapname,
+		    B_FALSE, NULL)) != 0) {
+			if (errno == EEXIST) {
+				(void) mutex_unlock(&zlib_mtx);
+				return (0);
+			}
+			syslog(LOG_ERR,
+			    "backup_dataset_create: %s failed (err=%d): %s",
+			    nlp->nlp_snapname, errno,
+			    libzfs_error_description(zlibh));
 			(void) mutex_unlock(&zlib_mtx);
-			return (0);
+			return (rv);
 		}
-		NDMP_LOG(LOG_DEBUG,
-		    "snapshot_create: %s failed (err=%d): %s",
-		    snapname, errno, libzfs_error_description(zlibh));
-		(void) mutex_unlock(&zlib_mtx);
-		return (rv);
+		if (snapshot_hold(nlp->nlp_vol,
+		    nlp->nlp_snapname, NDMP_RCF_BASENAME) != 0) {
+			syslog(LOG_DEBUG,
+			    "backup_dataset_create: %s "
+			    "hold failed (err=%d): %s",
+			    nlp->nlp_snapname,
+			    errno, libzfs_error_description(zlibh));
+			(void) mutex_unlock(&zlib_mtx);
+			return (-1);
+		}
+		syslog(LOG_DEBUG,
+		    "Using %s NdmpBackup snapshot for backup",
+		    nlp->nlp_snapname);
+
 	}
-	if (hold && snapshot_hold(volname, snapname, jname, recursive) != 0) {
-		NDMP_LOG(LOG_DEBUG,
-		    "snapshot_create: %s hold failed (err=%d): %s",
-		    snapname, errno, libzfs_error_description(zlibh));
+
+	if (ndmp_clone_snapshot(nlp) != 0) {
+		syslog(LOG_ERR,
+		    "backup_dataset_create: %s clone failed (err=%d): %s",
+		    nlp->nlp_snapname, errno, libzfs_error_description(zlibh));
 		(void) mutex_unlock(&zlib_mtx);
 		return (-1);
 	}
-
 	(void) mutex_unlock(&zlib_mtx);
 	return (0);
 }
 
 /*
- * Remove and release the backup snapshot
+ * Unmount, release, and destroy the snapshot created for backup.
  */
 int
-snapshot_destroy(char *volname, char *jname, boolean_t recursive,
-    boolean_t hold, int *zfs_err)
+backup_dataset_destroy(ndmp_lbr_params_t *nlp)
 {
-	char snapname[ZFS_MAX_DATASET_NAME_LEN];
-	zfs_handle_t *zhp;
-	zfs_type_t ztype;
-	char *namep;
+	char zpoolname[ZFS_MAX_DATASET_NAME_LEN];
+	char *slash;
+	zfs_handle_t *vol_zhp;
+	zfs_handle_t *cln_zhp;
 	int err;
+	int rv = 0;
 
-	if (zfs_err)
-		*zfs_err = 0;
-
-	if (!volname || !*volname)
+	if (nlp == NULL) {
+		syslog(LOG_DEBUG,
+		    "nlp NULL in backup_dataset_destroy");
 		return (-1);
+	}
 
-	if (recursive) {
-		ztype = ZFS_TYPE_VOLUME | ZFS_TYPE_FILESYSTEM;
-		namep = volname;
-	} else {
-		(void) snprintf(snapname, ZFS_MAX_DATASET_NAME_LEN,
-		    "%s@%s", volname, jname);
-		namep = snapname;
-		ztype = ZFS_TYPE_SNAPSHOT;
+	(void) strlcpy(zpoolname, nlp->nlp_vol, sizeof (zpoolname));
+	slash = strchr(zpoolname, '/');
+	if (slash != NULL) {
+		*slash = '\0';
 	}
 
+	if (!NLP_ISCHKPNTED(nlp)) {
+		(void) snprintf(nlp->nlp_snapname, sizeof (nlp->nlp_snapname),
+		    "%s@%s", nlp->nlp_vol, nlp->nlp_job_name);
+	}
+
+
+	syslog(LOG_DEBUG, "Snapname in backup_dataset_destroy is [%s]",
+	    nlp->nlp_snapname);
+
+	/*
+	 * Destroy using this sequence
+	 * zfs release <volume>@<jname>
+	 * zfs destroy <pool>/<jname>
+	 * zfs destroy <pool>/<volume>@<jname>
+	 */
 	(void) mutex_lock(&zlib_mtx);
-	if (hold &&
-	    snapshot_release(volname, namep, jname, recursive) != 0) {
-		NDMP_LOG(LOG_DEBUG,
-		    "snapshot_destroy: %s release failed (err=%d): %s",
-		    namep, errno, libzfs_error_description(zlibh));
+
+	/*
+	 * Release the normal snapshot but don't try to
+	 * release if it's a "checkpoint" because the hold
+	 * wasn't put on it to begin with.
+	 */
+	if (!NLP_ISCHKPNTED(nlp)) {
+		if (snapshot_release(nlp->nlp_vol,
+		    nlp->nlp_snapname, NDMP_RCF_BASENAME) != 0) {
+			syslog(LOG_DEBUG,
+			    "backup_dataset_destroy: %s "
+			    "release failed (err=%d): %s",
+			    nlp->nlp_clonename, errno,
+			    libzfs_error_description(zlibh));
+			(void) mutex_unlock(&zlib_mtx);
+			return (-1);
+		}
+	} else {
+		syslog(LOG_DEBUG, "Checkpointed dataset not held "
+		    "will not release [%s]", nlp->nlp_snapname);
+	}
+
+	/*
+	 * Open the clone to get descriptor
+	 */
+	if ((cln_zhp = zfs_open(zlibh, nlp->nlp_clonename,
+	    ZFS_TYPE_VOLUME | ZFS_TYPE_FILESYSTEM)) == NULL) {
+		syslog(LOG_ERR,
+		    "backup_dataset_destroy: open %s failed",
+		    nlp->nlp_clonename);
 		(void) mutex_unlock(&zlib_mtx);
 		return (-1);
 	}
 
-	if ((zhp = zfs_open(zlibh, namep, ztype)) == NULL) {
-		NDMP_LOG(LOG_DEBUG, "snapshot_destroy: open %s failed",
-		    namep);
+	/*
+	 * Open the mounted clone to get descriptor for unmount
+	 */
+	if ((vol_zhp = zfs_open(zlibh, nlp->nlp_vol,
+	    ZFS_TYPE_VOLUME | ZFS_TYPE_FILESYSTEM)) == NULL) {
+		syslog(LOG_ERR,
+		    "backup_dataset_destroy: open %s failed [while trying "
+		    "to destroy]", nlp->nlp_vol);
+		zfs_close(cln_zhp);
 		(void) mutex_unlock(&zlib_mtx);
 		return (-1);
 	}
 
-	if (recursive) {
-		err = zfs_destroy_snaps(zhp, jname, B_TRUE);
-	} else {
-		err = zfs_destroy(zhp, B_TRUE);
+	/*
+	 * This unmounts the clone which was just traversed for backup
+	 */
+	if ((err = zfs_unmount(cln_zhp, NULL, 0)) != 0) {
+		syslog(LOG_INFO, "failed to unmount [%s]", nlp->nlp_clonename);
+		rv = -1;
+		goto _out;
 	}
 
+	/*
+	 * This destroys the clone
+	 */
+	err = zfs_destroy(cln_zhp, B_TRUE);
 	if (err) {
-		NDMP_LOG(LOG_ERR, "%s (recursive destroy: %d): %d; %s; %s",
-		    namep,
-		    recursive,
+		syslog(LOG_ERR, "%s destroy: %d; %s; %s",
+		    nlp->nlp_clonename,
 		    libzfs_errno(zlibh),
 		    libzfs_error_action(zlibh),
 		    libzfs_error_description(zlibh));
+		rv = -1;
+		goto _out;
+	}
 
-		if (zfs_err)
-			*zfs_err = err;
+	/*
+	 * This destroys the snapshot of the current backup - but,
+	 * don't destroy it if it is an "checkpoint" from AutoSync
+	 * or HPR.
+	 */
+	if (!NLP_ISCHKPNTED(nlp)) {
+		if ((err = zfs_destroy_snaps(vol_zhp,
+		    nlp->nlp_job_name, B_TRUE))) {
+			syslog(LOG_ERR, "%s destroy: %d; %s; %s",
+			    nlp->nlp_job_name,
+			    libzfs_errno(zlibh),
+			    libzfs_error_action(zlibh),
+			    libzfs_error_description(zlibh));
+			rv = -1;
+			syslog(LOG_DEBUG, "Destroy [%s]", nlp->nlp_snapname);
+			goto _out;
+		}
+	} else {
+		syslog(LOG_DEBUG, "Checkpointed checkpoint will not destroy [%s]",
+		    nlp->nlp_snapname);
 	}
 
-	zfs_close(zhp);
+_out:
+	zfs_close(vol_zhp);
+	zfs_close(cln_zhp);
 	(void) mutex_unlock(&zlib_mtx);
 
-	return (0);
+	/*
+	 * The zfs_clone() call will have mounted the snapshot
+	 * in the file system at this point - so clean it up.
+	 */
+	if (rv == 0) {
+		if (rmdir(nlp->nlp_mountpoint) != 0) {
+			syslog(LOG_ERR,
+			    "Failed to remove mount point [%s]",
+			    nlp->nlp_mountpoint);
+			return (-1);
+		}
+	}
+
+	return (rv);
 }
