svn commit: r192175 - user/kmacy/ZFS_MFC/cddl/contrib/opensolaris/lib/libzfs/common

Kip Macy kmacy at FreeBSD.org
Sat May 16 00:56:55 UTC 2009


Author: kmacy
Date: Sat May 16 00:56:54 2009
New Revision: 192175
URL: http://svn.freebsd.org/changeset/base/192175

Log:
  add missed file

Added:
  user/kmacy/ZFS_MFC/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c   (contents, props changed)

Added: user/kmacy/ZFS_MFC/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ user/kmacy/ZFS_MFC/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c	Sat May 16 00:56:54 2009	(r192175)
@@ -0,0 +1,2104 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <libintl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/mntent.h>
+#include <sys/mnttab.h>
+#include <sys/avl.h>
+#include <stddef.h>
+
+#include <libzfs.h>
+
+#include "zfs_namecheck.h"
+#include "zfs_prop.h"
+#include "libzfs_impl.h"
+
+#include <fletcher.c> /* XXX */
+
+/* We need to use something for ENODATA. */
+#define	ENODATA	EIDRM
+
+static int zfs_receive_impl(libzfs_handle_t *, const char *, recvflags_t,
+    int, avl_tree_t *, char **);
+
+/*
+ * Routines for dealing with the AVL tree of fs-nvlists
+ */
+typedef struct fsavl_node {
+	avl_node_t fn_node;
+	nvlist_t *fn_nvfs;
+	char *fn_snapname;
+	uint64_t fn_guid;
+} fsavl_node_t;
+
+static int
+fsavl_compare(const void *arg1, const void *arg2)
+{
+	const fsavl_node_t *fn1 = arg1;
+	const fsavl_node_t *fn2 = arg2;
+
+	if (fn1->fn_guid > fn2->fn_guid)
+		return (+1);
+	else if (fn1->fn_guid < fn2->fn_guid)
+		return (-1);
+	else
+		return (0);
+}
+
+/*
+ * Given the GUID of a snapshot, find its containing filesystem and
+ * (optionally) name.
+ */
+static nvlist_t *
+fsavl_find(avl_tree_t *avl, uint64_t snapguid, char **snapname)
+{
+	fsavl_node_t fn_find;
+	fsavl_node_t *fn;
+
+	fn_find.fn_guid = snapguid;
+
+	fn = avl_find(avl, &fn_find, NULL);
+	if (fn) {
+		if (snapname)
+			*snapname = fn->fn_snapname;
+		return (fn->fn_nvfs);
+	}
+	return (NULL);
+}
+
+static void
+fsavl_destroy(avl_tree_t *avl)
+{
+	fsavl_node_t *fn;
+	void *cookie;
+
+	if (avl == NULL)
+		return;
+
+	cookie = NULL;
+	while ((fn = avl_destroy_nodes(avl, &cookie)) != NULL)
+		free(fn);
+	avl_destroy(avl);
+	free(avl);
+}
+
+static avl_tree_t *
+fsavl_create(nvlist_t *fss)
+{
+	avl_tree_t *fsavl;
+	nvpair_t *fselem = NULL;
+
+	if ((fsavl = malloc(sizeof (avl_tree_t))) == NULL)
+		return (NULL);
+
+	avl_create(fsavl, fsavl_compare, sizeof (fsavl_node_t),
+	    offsetof(fsavl_node_t, fn_node));
+
+	while ((fselem = nvlist_next_nvpair(fss, fselem)) != NULL) {
+		nvlist_t *nvfs, *snaps;
+		nvpair_t *snapelem = NULL;
+
+		VERIFY(0 == nvpair_value_nvlist(fselem, &nvfs));
+		VERIFY(0 == nvlist_lookup_nvlist(nvfs, "snaps", &snaps));
+
+		while ((snapelem =
+		    nvlist_next_nvpair(snaps, snapelem)) != NULL) {
+			fsavl_node_t *fn;
+			uint64_t guid;
+
+			VERIFY(0 == nvpair_value_uint64(snapelem, &guid));
+			if ((fn = malloc(sizeof (fsavl_node_t))) == NULL) {
+				fsavl_destroy(fsavl);
+				return (NULL);
+			}
+			fn->fn_nvfs = nvfs;
+			fn->fn_snapname = nvpair_name(snapelem);
+			fn->fn_guid = guid;
+
+			/*
+			 * Note: if there are multiple snaps with the
+			 * same GUID, we ignore all but one.
+			 */
+			if (avl_find(fsavl, fn, NULL) == NULL)
+				avl_add(fsavl, fn);
+			else
+				free(fn);
+		}
+	}
+
+	return (fsavl);
+}
+
+/*
+ * Routines for dealing with the giant nvlist of fs-nvlists, etc.
+ */
+typedef struct send_data {
+	uint64_t parent_fromsnap_guid;
+	nvlist_t *parent_snaps;
+	nvlist_t *fss;
+	nvlist_t *snapprops;
+	const char *fromsnap;
+	const char *tosnap;
+
+	/*
+	 * The header nvlist is of the following format:
+	 * {
+	 *   "tosnap" -> string
+	 *   "fromsnap" -> string (if incremental)
+	 *   "fss" -> {
+	 *	id -> {
+	 *
+	 *	 "name" -> string (full name; for debugging)
+	 *	 "parentfromsnap" -> number (guid of fromsnap in parent)
+	 *
+	 *	 "props" -> { name -> value (only if set here) }
+	 *	 "snaps" -> { name (lastname) -> number (guid) }
+	 *	 "snapprops" -> { name (lastname) -> { name -> value } }
+	 *
+	 *	 "origin" -> number (guid) (if clone)
+	 *	 "sent" -> boolean (not on-disk)
+	 *	}
+	 *   }
+	 * }
+	 *
+	 */
+} send_data_t;
+
+static void send_iterate_prop(zfs_handle_t *zhp, nvlist_t *nv);
+
+static int
+send_iterate_snap(zfs_handle_t *zhp, void *arg)
+{
+	send_data_t *sd = arg;
+	uint64_t guid = zhp->zfs_dmustats.dds_guid;
+	char *snapname;
+	nvlist_t *nv;
+
+	snapname = strrchr(zhp->zfs_name, '@')+1;
+
+	VERIFY(0 == nvlist_add_uint64(sd->parent_snaps, snapname, guid));
+	/*
+	 * NB: if there is no fromsnap here (it's a newly created fs in
+	 * an incremental replication), we will substitute the tosnap.
+	 */
+	if ((sd->fromsnap && strcmp(snapname, sd->fromsnap) == 0) ||
+	    (sd->parent_fromsnap_guid == 0 && sd->tosnap &&
+	    strcmp(snapname, sd->tosnap) == 0)) {
+		sd->parent_fromsnap_guid = guid;
+	}
+
+	VERIFY(0 == nvlist_alloc(&nv, NV_UNIQUE_NAME, 0));
+	send_iterate_prop(zhp, nv);
+	VERIFY(0 == nvlist_add_nvlist(sd->snapprops, snapname, nv));
+	nvlist_free(nv);
+
+	zfs_close(zhp);
+	return (0);
+}
+
+static void
+send_iterate_prop(zfs_handle_t *zhp, nvlist_t *nv)
+{
+	nvpair_t *elem = NULL;
+
+	while ((elem = nvlist_next_nvpair(zhp->zfs_props, elem)) != NULL) {
+		char *propname = nvpair_name(elem);
+		zfs_prop_t prop = zfs_name_to_prop(propname);
+		nvlist_t *propnv;
+
+		if (!zfs_prop_user(propname) && zfs_prop_readonly(prop))
+			continue;
+
+		verify(nvpair_value_nvlist(elem, &propnv) == 0);
+		if (prop == ZFS_PROP_QUOTA || prop == ZFS_PROP_RESERVATION) {
+			/* these guys are modifyable, but have no source */
+			uint64_t value;
+			verify(nvlist_lookup_uint64(propnv,
+			    ZPROP_VALUE, &value) == 0);
+			if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT)
+				continue;
+		} else {
+			char *source;
+			if (nvlist_lookup_string(propnv,
+			    ZPROP_SOURCE, &source) != 0)
+				continue;
+			if (strcmp(source, zhp->zfs_name) != 0)
+				continue;
+		}
+
+		if (zfs_prop_user(propname) ||
+		    zfs_prop_get_type(prop) == PROP_TYPE_STRING) {
+			char *value;
+			verify(nvlist_lookup_string(propnv,
+			    ZPROP_VALUE, &value) == 0);
+			VERIFY(0 == nvlist_add_string(nv, propname, value));
+		} else {
+			uint64_t value;
+			verify(nvlist_lookup_uint64(propnv,
+			    ZPROP_VALUE, &value) == 0);
+			VERIFY(0 == nvlist_add_uint64(nv, propname, value));
+		}
+	}
+}
+
+static int
+send_iterate_fs(zfs_handle_t *zhp, void *arg)
+{
+	send_data_t *sd = arg;
+	nvlist_t *nvfs, *nv;
+	int rv;
+	uint64_t parent_fromsnap_guid_save = sd->parent_fromsnap_guid;
+	uint64_t guid = zhp->zfs_dmustats.dds_guid;
+	char guidstring[64];
+
+	VERIFY(0 == nvlist_alloc(&nvfs, NV_UNIQUE_NAME, 0));
+	VERIFY(0 == nvlist_add_string(nvfs, "name", zhp->zfs_name));
+	VERIFY(0 == nvlist_add_uint64(nvfs, "parentfromsnap",
+	    sd->parent_fromsnap_guid));
+
+	if (zhp->zfs_dmustats.dds_origin[0]) {
+		zfs_handle_t *origin = zfs_open(zhp->zfs_hdl,
+		    zhp->zfs_dmustats.dds_origin, ZFS_TYPE_SNAPSHOT);
+		if (origin == NULL)
+			return (-1);
+		VERIFY(0 == nvlist_add_uint64(nvfs, "origin",
+		    origin->zfs_dmustats.dds_guid));
+	}
+
+	/* iterate over props */
+	VERIFY(0 == nvlist_alloc(&nv, NV_UNIQUE_NAME, 0));
+	send_iterate_prop(zhp, nv);
+	VERIFY(0 == nvlist_add_nvlist(nvfs, "props", nv));
+	nvlist_free(nv);
+
+	/* iterate over snaps, and set sd->parent_fromsnap_guid */
+	sd->parent_fromsnap_guid = 0;
+	VERIFY(0 == nvlist_alloc(&sd->parent_snaps, NV_UNIQUE_NAME, 0));
+	VERIFY(0 == nvlist_alloc(&sd->snapprops, NV_UNIQUE_NAME, 0));
+	(void) zfs_iter_snapshots(zhp, send_iterate_snap, sd);
+	VERIFY(0 == nvlist_add_nvlist(nvfs, "snaps", sd->parent_snaps));
+	VERIFY(0 == nvlist_add_nvlist(nvfs, "snapprops", sd->snapprops));
+	nvlist_free(sd->parent_snaps);
+	nvlist_free(sd->snapprops);
+
+	/* add this fs to nvlist */
+	(void) snprintf(guidstring, sizeof (guidstring),
+	    "0x%llx", (longlong_t)guid);
+	VERIFY(0 == nvlist_add_nvlist(sd->fss, guidstring, nvfs));
+	nvlist_free(nvfs);
+
+	/* iterate over children */
+	rv = zfs_iter_filesystems(zhp, send_iterate_fs, sd);
+
+	sd->parent_fromsnap_guid = parent_fromsnap_guid_save;
+
+	zfs_close(zhp);
+	return (rv);
+}
+
+static int
+gather_nvlist(libzfs_handle_t *hdl, const char *fsname, const char *fromsnap,
+    const char *tosnap, nvlist_t **nvlp, avl_tree_t **avlp)
+{
+	zfs_handle_t *zhp;
+	send_data_t sd = { 0 };
+	int error;
+
+	zhp = zfs_open(hdl, fsname, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
+	if (zhp == NULL)
+		return (EZFS_BADTYPE);
+
+	VERIFY(0 == nvlist_alloc(&sd.fss, NV_UNIQUE_NAME, 0));
+	sd.fromsnap = fromsnap;
+	sd.tosnap = tosnap;
+
+	if ((error = send_iterate_fs(zhp, &sd)) != 0) {
+		nvlist_free(sd.fss);
+		if (avlp != NULL)
+			*avlp = NULL;
+		*nvlp = NULL;
+		return (error);
+	}
+
+	if (avlp != NULL && (*avlp = fsavl_create(sd.fss)) == NULL) {
+		nvlist_free(sd.fss);
+		*nvlp = NULL;
+		return (EZFS_NOMEM);
+	}
+
+	*nvlp = sd.fss;
+	return (0);
+}
+
+/*
+ * Routines for dealing with the sorted snapshot functionality
+ */
+typedef struct zfs_node {
+	zfs_handle_t	*zn_handle;
+	avl_node_t	zn_avlnode;
+} zfs_node_t;
+
+static int
+zfs_sort_snaps(zfs_handle_t *zhp, void *data)
+{
+	avl_tree_t *avl = data;
+	zfs_node_t *node = zfs_alloc(zhp->zfs_hdl, sizeof (zfs_node_t));
+
+	node->zn_handle = zhp;
+	avl_add(avl, node);
+	return (0);
+}
+
+/* ARGSUSED */
+static int
+zfs_snapshot_compare(const void *larg, const void *rarg)
+{
+	zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle;
+	zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle;
+	uint64_t lcreate, rcreate;
+
+	/*
+	 * Sort them according to creation time.  We use the hidden
+	 * CREATETXG property to get an absolute ordering of snapshots.
+	 */
+	lcreate = zfs_prop_get_int(l, ZFS_PROP_CREATETXG);
+	rcreate = zfs_prop_get_int(r, ZFS_PROP_CREATETXG);
+
+	if (lcreate < rcreate)
+		return (-1);
+	else if (lcreate > rcreate)
+		return (+1);
+	else
+		return (0);
+}
+
+static int
+zfs_iter_snapshots_sorted(zfs_handle_t *zhp, zfs_iter_f callback, void *data)
+{
+	int ret = 0;
+	zfs_node_t *node;
+	avl_tree_t avl;
+	void *cookie = NULL;
+
+	avl_create(&avl, zfs_snapshot_compare,
+	    sizeof (zfs_node_t), offsetof(zfs_node_t, zn_avlnode));
+
+	ret = zfs_iter_snapshots(zhp, zfs_sort_snaps, &avl);
+
+	for (node = avl_first(&avl); node != NULL; node = AVL_NEXT(&avl, node))
+		ret |= callback(node->zn_handle, data);
+
+	while ((node = avl_destroy_nodes(&avl, &cookie)) != NULL)
+		free(node);
+
+	avl_destroy(&avl);
+
+	return (ret);
+}
+
+/*
+ * Routines specific to "zfs send"
+ */
+typedef struct send_dump_data {
+	/* these are all just the short snapname (the part after the @) */
+	const char *fromsnap;
+	const char *tosnap;
+	char lastsnap[ZFS_MAXNAMELEN];
+	boolean_t seenfrom, seento, replicate, doall, fromorigin;
+	boolean_t verbose;
+	int outfd;
+	boolean_t err;
+	nvlist_t *fss;
+	avl_tree_t *fsavl;
+} send_dump_data_t;
+
+/*
+ * Dumps a backup of the given snapshot (incremental from fromsnap if it's not
+ * NULL) to the file descriptor specified by outfd.
+ */
+static int
+dump_ioctl(zfs_handle_t *zhp, const char *fromsnap, boolean_t fromorigin,
+    int outfd)
+{
+	zfs_cmd_t zc = { 0 };
+	libzfs_handle_t *hdl = zhp->zfs_hdl;
+
+	assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
+	assert(fromsnap == NULL || fromsnap[0] == '\0' || !fromorigin);
+
+	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+	if (fromsnap)
+		(void) strlcpy(zc.zc_value, fromsnap, sizeof (zc.zc_value));
+	zc.zc_cookie = outfd;
+	zc.zc_obj = fromorigin;
+
+	if (ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SEND, &zc) != 0) {
+		char errbuf[1024];
+		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
+		    "warning: cannot send '%s'"), zhp->zfs_name);
+
+		switch (errno) {
+
+		case EXDEV:
+			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+			    "not an earlier snapshot from the same fs"));
+			return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
+
+		case ENOENT:
+			if (zfs_dataset_exists(hdl, zc.zc_name,
+			    ZFS_TYPE_SNAPSHOT)) {
+				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+				    "incremental source (@%s) does not exist"),
+				    zc.zc_value);
+			}
+			return (zfs_error(hdl, EZFS_NOENT, errbuf));
+
+		case EDQUOT:
+		case EFBIG:
+		case EIO:
+		case ENOLINK:
+		case ENOSPC:
+		case ENXIO:
+		case EPIPE:
+		case ERANGE:
+		case EFAULT:
+		case EROFS:
+			zfs_error_aux(hdl, strerror(errno));
+			return (zfs_error(hdl, EZFS_BADBACKUP, errbuf));
+
+		default:
+			return (zfs_standard_error(hdl, errno, errbuf));
+		}
+	}
+
+	return (0);
+}
+
+static int
+dump_snapshot(zfs_handle_t *zhp, void *arg)
+{
+	send_dump_data_t *sdd = arg;
+	const char *thissnap;
+	int err;
+
+	thissnap = strchr(zhp->zfs_name, '@') + 1;
+
+	if (sdd->fromsnap && !sdd->seenfrom &&
+	    strcmp(sdd->fromsnap, thissnap) == 0) {
+		sdd->seenfrom = B_TRUE;
+		(void) strcpy(sdd->lastsnap, thissnap);
+		zfs_close(zhp);
+		return (0);
+	}
+
+	if (sdd->seento || !sdd->seenfrom) {
+		zfs_close(zhp);
+		return (0);
+	}
+
+	/* send it */
+	if (sdd->verbose) {
+		(void) fprintf(stderr, "sending from @%s to %s\n",
+		    sdd->lastsnap, zhp->zfs_name);
+	}
+
+	err = dump_ioctl(zhp, sdd->lastsnap,
+	    sdd->lastsnap[0] == '\0' && (sdd->fromorigin || sdd->replicate),
+	    sdd->outfd);
+
+	if (!sdd->seento && strcmp(sdd->tosnap, thissnap) == 0)
+		sdd->seento = B_TRUE;
+
+	(void) strcpy(sdd->lastsnap, thissnap);
+	zfs_close(zhp);
+	return (err);
+}
+
+static int
+dump_filesystem(zfs_handle_t *zhp, void *arg)
+{
+	int rv = 0;
+	send_dump_data_t *sdd = arg;
+	boolean_t missingfrom = B_FALSE;
+	zfs_cmd_t zc = { 0 };
+
+	(void) snprintf(zc.zc_name, sizeof (zc.zc_name), "%s@%s",
+	    zhp->zfs_name, sdd->tosnap);
+	if (ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0) {
+		(void) fprintf(stderr, "WARNING: "
+		    "could not send %s@%s: does not exist\n",
+		    zhp->zfs_name, sdd->tosnap);
+		sdd->err = B_TRUE;
+		return (0);
+	}
+
+	if (sdd->replicate && sdd->fromsnap) {
+		/*
+		 * If this fs does not have fromsnap, and we're doing
+		 * recursive, we need to send a full stream from the
+		 * beginning (or an incremental from the origin if this
+		 * is a clone).  If we're doing non-recursive, then let
+		 * them get the error.
+		 */
+		(void) snprintf(zc.zc_name, sizeof (zc.zc_name), "%s@%s",
+		    zhp->zfs_name, sdd->fromsnap);
+		if (ioctl(zhp->zfs_hdl->libzfs_fd,
+		    ZFS_IOC_OBJSET_STATS, &zc) != 0) {
+			missingfrom = B_TRUE;
+		}
+	}
+
+	if (sdd->doall) {
+		sdd->seenfrom = sdd->seento = sdd->lastsnap[0] = 0;
+		if (sdd->fromsnap == NULL || missingfrom)
+			sdd->seenfrom = B_TRUE;
+
+		rv = zfs_iter_snapshots_sorted(zhp, dump_snapshot, arg);
+		if (!sdd->seenfrom) {
+			(void) fprintf(stderr,
+			    "WARNING: could not send %s@%s:\n"
+			    "incremental source (%s@%s) does not exist\n",
+			    zhp->zfs_name, sdd->tosnap,
+			    zhp->zfs_name, sdd->fromsnap);
+			sdd->err = B_TRUE;
+		} else if (!sdd->seento) {
+			(void) fprintf(stderr,
+			    "WARNING: could not send %s@%s:\n"
+			    "incremental source (%s@%s) "
+			    "is not earlier than it\n",
+			    zhp->zfs_name, sdd->tosnap,
+			    zhp->zfs_name, sdd->fromsnap);
+			sdd->err = B_TRUE;
+		}
+	} else {
+		zfs_handle_t *snapzhp;
+		char snapname[ZFS_MAXNAMELEN];
+
+		(void) snprintf(snapname, sizeof (snapname), "%s@%s",
+		    zfs_get_name(zhp), sdd->tosnap);
+		snapzhp = zfs_open(zhp->zfs_hdl, snapname, ZFS_TYPE_SNAPSHOT);
+		if (snapzhp == NULL) {
+			rv = -1;
+		} else {
+			rv = dump_ioctl(snapzhp,
+			    missingfrom ? NULL : sdd->fromsnap,
+			    sdd->fromorigin || missingfrom,
+			    sdd->outfd);
+			sdd->seento = B_TRUE;
+			zfs_close(snapzhp);
+		}
+	}
+
+	return (rv);
+}
+
+static int
+dump_filesystems(zfs_handle_t *rzhp, void *arg)
+{
+	send_dump_data_t *sdd = arg;
+	nvpair_t *fspair;
+	boolean_t needagain, progress;
+
+	if (!sdd->replicate)
+		return (dump_filesystem(rzhp, sdd));
+
+again:
+	needagain = progress = B_FALSE;
+	for (fspair = nvlist_next_nvpair(sdd->fss, NULL); fspair;
+	    fspair = nvlist_next_nvpair(sdd->fss, fspair)) {
+		nvlist_t *fslist;
+		char *fsname;
+		zfs_handle_t *zhp;
+		int err;
+		uint64_t origin_guid = 0;
+		nvlist_t *origin_nv;
+
+		VERIFY(nvpair_value_nvlist(fspair, &fslist) == 0);
+		if (nvlist_lookup_boolean(fslist, "sent") == 0)
+			continue;
+
+		VERIFY(nvlist_lookup_string(fslist, "name", &fsname) == 0);
+		(void) nvlist_lookup_uint64(fslist, "origin", &origin_guid);
+
+		origin_nv = fsavl_find(sdd->fsavl, origin_guid, NULL);
+		if (origin_nv &&
+		    nvlist_lookup_boolean(origin_nv, "sent") == ENOENT) {
+			/*
+			 * origin has not been sent yet;
+			 * skip this clone.
+			 */
+			needagain = B_TRUE;
+			continue;
+		}
+
+		zhp = zfs_open(rzhp->zfs_hdl, fsname, ZFS_TYPE_DATASET);
+		if (zhp == NULL)
+			return (-1);
+		err = dump_filesystem(zhp, sdd);
+		VERIFY(nvlist_add_boolean(fslist, "sent") == 0);
+		progress = B_TRUE;
+		zfs_close(zhp);
+		if (err)
+			return (err);
+	}
+	if (needagain) {
+		assert(progress);
+		goto again;
+	}
+	return (0);
+}
+
+/*
+ * Dumps a backup of tosnap, incremental from fromsnap if it isn't NULL.
+ * If 'doall', dump all intermediate snaps.
+ * If 'replicate', dump special header and do recursively.
+ */
+int
+zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
+    boolean_t replicate, boolean_t doall, boolean_t fromorigin,
+    boolean_t verbose, int outfd)
+{
+	char errbuf[1024];
+	send_dump_data_t sdd = { 0 };
+	int err;
+	nvlist_t *fss = NULL;
+	avl_tree_t *fsavl = NULL;
+
+	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
+	    "cannot send '%s'"), zhp->zfs_name);
+
+	if (fromsnap && fromsnap[0] == '\0') {
+		zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
+		    "zero-length incremental source"));
+		return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf));
+	}
+
+	if (replicate || doall) {
+		dmu_replay_record_t drr = { 0 };
+		char *packbuf = NULL;
+		size_t buflen = 0;
+		zio_cksum_t zc = { 0 };
+
+		assert(fromsnap || doall);
+
+		if (replicate) {
+			nvlist_t *hdrnv;
+
+			VERIFY(0 == nvlist_alloc(&hdrnv, NV_UNIQUE_NAME, 0));
+			if (fromsnap) {
+				VERIFY(0 == nvlist_add_string(hdrnv,
+				    "fromsnap", fromsnap));
+			}
+			VERIFY(0 == nvlist_add_string(hdrnv, "tosnap", tosnap));
+
+			err = gather_nvlist(zhp->zfs_hdl, zhp->zfs_name,
+			    fromsnap, tosnap, &fss, &fsavl);
+			if (err)
+				return (err);
+			VERIFY(0 == nvlist_add_nvlist(hdrnv, "fss", fss));
+			err = nvlist_pack(hdrnv, &packbuf, &buflen,
+			    NV_ENCODE_XDR, 0);
+			nvlist_free(hdrnv);
+			if (err) {
+				fsavl_destroy(fsavl);
+				nvlist_free(fss);
+				return (zfs_standard_error(zhp->zfs_hdl,
+				    err, errbuf));
+			}
+		}
+
+		/* write first begin record */
+		drr.drr_type = DRR_BEGIN;
+		drr.drr_u.drr_begin.drr_magic = DMU_BACKUP_MAGIC;
+		drr.drr_u.drr_begin.drr_version = DMU_BACKUP_HEADER_VERSION;
+		(void) snprintf(drr.drr_u.drr_begin.drr_toname,
+		    sizeof (drr.drr_u.drr_begin.drr_toname),
+		    "%s@%s", zhp->zfs_name, tosnap);
+		drr.drr_payloadlen = buflen;
+		fletcher_4_incremental_native(&drr, sizeof (drr), &zc);
+		err = write(outfd, &drr, sizeof (drr));
+
+		/* write header nvlist */
+		if (err != -1) {
+			fletcher_4_incremental_native(packbuf, buflen, &zc);
+			err = write(outfd, packbuf, buflen);
+		}
+		free(packbuf);
+		if (err == -1) {
+			fsavl_destroy(fsavl);
+			nvlist_free(fss);
+			return (zfs_standard_error(zhp->zfs_hdl,
+			    errno, errbuf));
+		}
+
+		/* write end record */
+		if (err != -1) {
+			bzero(&drr, sizeof (drr));
+			drr.drr_type = DRR_END;
+			drr.drr_u.drr_end.drr_checksum = zc;
+			err = write(outfd, &drr, sizeof (drr));
+			if (err == -1) {
+				fsavl_destroy(fsavl);
+				nvlist_free(fss);
+				return (zfs_standard_error(zhp->zfs_hdl,
+				    errno, errbuf));
+			}
+		}
+	}
+
+	/* dump each stream */
+	sdd.fromsnap = fromsnap;
+	sdd.tosnap = tosnap;
+	sdd.outfd = outfd;
+	sdd.replicate = replicate;
+	sdd.doall = doall;
+	sdd.fromorigin = fromorigin;
+	sdd.fss = fss;
+	sdd.fsavl = fsavl;
+	sdd.verbose = verbose;
+	err = dump_filesystems(zhp, &sdd);
+	fsavl_destroy(fsavl);
+	nvlist_free(fss);
+
+	if (replicate || doall) {
+		/*
+		 * write final end record.  NB: want to do this even if
+		 * there was some error, because it might not be totally
+		 * failed.
+		 */
+		dmu_replay_record_t drr = { 0 };
+		drr.drr_type = DRR_END;
+		if (write(outfd, &drr, sizeof (drr)) == -1) {
+			return (zfs_standard_error(zhp->zfs_hdl,
+			    errno, errbuf));
+		}
+	}
+
+	return (err || sdd.err);
+}
+
+/*
+ * Routines specific to "zfs recv"
+ */
+
+static int
+recv_read(libzfs_handle_t *hdl, int fd, void *buf, int ilen,
+    boolean_t byteswap, zio_cksum_t *zc)
+{
+	char *cp = buf;
+	int rv;
+	int len = ilen;
+
+	do {
+		rv = read(fd, cp, len);
+		cp += rv;
+		len -= rv;
+	} while (rv > 0);
+
+	if (rv < 0 || len != 0) {
+		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+		    "failed to read from stream"));
+		return (zfs_error(hdl, EZFS_BADSTREAM, dgettext(TEXT_DOMAIN,
+		    "cannot receive")));
+	}
+
+	if (zc) {
+		if (byteswap)
+			fletcher_4_incremental_byteswap(buf, ilen, zc);
+		else
+			fletcher_4_incremental_native(buf, ilen, zc);
+	}
+	return (0);
+}
+
+static int
+recv_read_nvlist(libzfs_handle_t *hdl, int fd, int len, nvlist_t **nvp,
+    boolean_t byteswap, zio_cksum_t *zc)
+{
+	char *buf;
+	int err;
+
+	buf = zfs_alloc(hdl, len);
+	if (buf == NULL)
+		return (ENOMEM);
+
+	err = recv_read(hdl, fd, buf, len, byteswap, zc);
+	if (err != 0) {
+		free(buf);
+		return (err);
+	}
+
+	err = nvlist_unpack(buf, len, nvp, 0);
+	free(buf);
+	if (err != 0) {
+		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
+		    "stream (malformed nvlist)"));
+		return (EINVAL);
+	}
+	return (0);
+}
+
+static int
+recv_rename(libzfs_handle_t *hdl, const char *name, const char *tryname,
+    int baselen, char *newname, recvflags_t flags)
+{
+	static int seq;
+	zfs_cmd_t zc = { 0 };
+	int err;
+	prop_changelist_t *clp;
+	zfs_handle_t *zhp;
+
+	zhp = zfs_open(hdl, name, ZFS_TYPE_DATASET);
+	if (zhp == NULL)
+		return (-1);
+	clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,
+	    flags.force ? MS_FORCE : 0);
+	zfs_close(zhp);
+	if (clp == NULL)
+		return (-1);
+	err = changelist_prefix(clp);
+	if (err)
+		return (err);
+
+	if (tryname) {
+		(void) strcpy(newname, tryname);
+
+		zc.zc_objset_type = DMU_OST_ZFS;
+		(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
+		(void) strlcpy(zc.zc_value, tryname, sizeof (zc.zc_value));
+
+		if (flags.verbose) {
+			(void) printf("attempting rename %s to %s\n",
+			    zc.zc_name, zc.zc_value);
+		}
+		err = ioctl(hdl->libzfs_fd, ZFS_IOC_RENAME, &zc);
+		if (err == 0)
+			changelist_rename(clp, name, tryname);
+	} else {
+		err = ENOENT;
+	}
+
+	if (err != 0 && strncmp(name+baselen, "recv-", 5) != 0) {
+		seq++;
+
+		(void) strncpy(newname, name, baselen);
+		(void) snprintf(newname+baselen, ZFS_MAXNAMELEN-baselen,
+		    "recv-%u-%u", getpid(), seq);
+		(void) strlcpy(zc.zc_value, newname, sizeof (zc.zc_value));
+
+		if (flags.verbose) {
+			(void) printf("failed - trying rename %s to %s\n",
+			    zc.zc_name, zc.zc_value);
+		}
+		err = ioctl(hdl->libzfs_fd, ZFS_IOC_RENAME, &zc);
+		if (err == 0)
+			changelist_rename(clp, name, newname);
+		if (err && flags.verbose) {
+			(void) printf("failed (%u) - "
+			    "will try again on next pass\n", errno);
+		}
+		err = EAGAIN;
+	} else if (flags.verbose) {
+		if (err == 0)
+			(void) printf("success\n");
+		else
+			(void) printf("failed (%u)\n", errno);
+	}
+
+	(void) changelist_postfix(clp);
+	changelist_free(clp);
+
+	return (err);
+}
+
+static int
+recv_destroy(libzfs_handle_t *hdl, const char *name, int baselen,
+    char *newname, recvflags_t flags)
+{
+	zfs_cmd_t zc = { 0 };
+	int err = 0;
+	prop_changelist_t *clp;
+	zfs_handle_t *zhp;
+
+	zhp = zfs_open(hdl, name, ZFS_TYPE_DATASET);
+	if (zhp == NULL)
+		return (-1);
+	clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,
+	    flags.force ? MS_FORCE : 0);
+	zfs_close(zhp);
+	if (clp == NULL)
+		return (-1);
+	err = changelist_prefix(clp);
+	if (err)
+		return (err);
+
+	zc.zc_objset_type = DMU_OST_ZFS;
+	(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
+
+	if (flags.verbose)
+		(void) printf("attempting destroy %s\n", zc.zc_name);
+	err = ioctl(hdl->libzfs_fd, ZFS_IOC_DESTROY, &zc);
+
+	if (err == 0) {
+		if (flags.verbose)
+			(void) printf("success\n");
+		changelist_remove(clp, zc.zc_name);
+	}
+
+	(void) changelist_postfix(clp);
+	changelist_free(clp);
+
+	if (err != 0)
+		err = recv_rename(hdl, name, NULL, baselen, newname, flags);
+
+	return (err);
+}
+
+typedef struct guid_to_name_data {
+	uint64_t guid;
+	char *name;
+} guid_to_name_data_t;
+
+static int
+guid_to_name_cb(zfs_handle_t *zhp, void *arg)

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-user mailing list