Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 31 Oct 2012 10:16:31 GMT
From:      Steven Hartland <steven.hartland@multiplay.co.uk>
To:        freebsd-gnats-submit@FreeBSD.org
Subject:   misc/173234: Allow filtering of properties on zfs receive (patch included)
Message-ID:  <201210311016.q9VAGVAv042185@red.freebsd.org>
Resent-Message-ID: <201210311020.q9VAK1kD011231@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         173234
>Category:       misc
>Synopsis:       Allow filtering of properties on zfs receive (patch included)
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          update
>Submitter-Id:   current-users
>Arrival-Date:   Wed Oct 31 10:20:01 UTC 2012
>Closed-Date:
>Last-Modified:
>Originator:     Steven Hartland
>Release:        8.3-RELEASE
>Organization:
Multiplay
>Environment:
FreeBSD dev 8.3-RELEASE-p4 FreeBSD 8.3-RELEASE-p4 #22: Mon Sep 17 17:18:32 UTC 2012     root@dev:/usr/obj/usr/src/sys/MULTIPLAY  amd64
>Description:
Its some times useful to allow filtering / overriding of zfs properties on receive for example if a disk has a quota set the receive can fail due to changes to the way the data is compressed resulting in exceeding the quota.

The attached patch adds both -x (exclude property) as well as -o (override property) to zfs receive as requested by the upstream ticket:-
https://www.illumos.org/issues/2745

This mirrors and extends the functionality available in Sun's ZFS receive.
>How-To-Repeat:
N/A
>Fix:
Apply the attached patch

Patch attached with submission follows:

Adds support for property overrides (-o property=value), property excludes
(-x property) and dataset limits (-l <volume|filesystem>) to zfs receive.

Both -o and -x options mirror the functionality already available in
Oracle's ZFS implementation which is also mentioned in the upstream
feature request #2745:
https://www.illumos.org/issues/2745

The -l option allows receive to be limited to specific datasets within
the stream effectively allowing partial restores from a multi dataset
stream.
--- cddl/contrib/opensolaris/cmd/zfs/zfs.8.orig	2012-09-30 15:55:57.189686148 +0000
+++ cddl/contrib/opensolaris/cmd/zfs/zfs.8	2012-09-30 23:43:39.870349406 +0000
@@ -170,10 +170,22 @@
 .Nm
 .Cm receive
 .Op Fl vnFu
+.Op Fl l Ar filesystem Ns | Ns Ar volume
+.Ar ...
+.Op Fl o Ar property Ns = Ns Ar value
+.Ar ...
+.Op Fl x Ar property
+.Ar ...
 .Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot
 .Nm
 .Cm receive
 .Op Fl vnFu
+.Op Fl l Ar filesystem Ns | Ns Ar volume
+.Ar ...
+.Op Fl o Ar property=value
+.Ar ...
+.Op Fl x Ar property
+.Ar ...
 .Op Fl d | e
 .Ar filesystem
 .Nm
@@ -2262,12 +2274,24 @@
 .Nm
 .Cm receive
 .Op Fl vnFu
+.Op Fl l Ar filesystem Ns | Ns Ar volume
+.Ar ...
+.Op Fl o Ar property Ns = Ns Ar value
+.Ar ...
+.Op Fl x Ar property
+.Ar ...
 .Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot
 .Xc
 .It Xo
 .Nm
 .Cm receive
 .Op Fl vnFu
+.Op Fl l Ar filesystem Ns | Ns Ar volume
+.Ar ...
+.Op Fl o Ar property Ns = Ns Ar value
+.Ar ...
+.Op Fl x Ar property
+.Ar ...
 .Op Fl d | e
 .Ar filesystem
 .Xc
@@ -2359,6 +2383,109 @@
 stream (for example, one generated by
 .Qq Nm Cm send Fl R Fi iI ) ,
 destroy snapshots and file systems that do not exist on the sending side.
+.It Fl l
+Limits the the recieve to only the
+.Ar filesystem
+or
+.Ar volume
+specified. As multiple
+.Fl l
+options may be specified, this can be used to restore specific filesystems or
+volumes from the received stream.
+.It Fl o Ar property Ns = Ns Ar value
+Sets the specified property as if the command zfs set
+.Ar property=value
+is invoked at the same time the received dataset is created from the
+non-incremental send stream or updated from the incremental send stream.
+.Pp
+Any editable ZFS property can also be set at receive time. Set-once properties
+bound to the received data, such as normalization and casesensitivity, cannot
+be set at receive time even when the datasets are newly created by zfs receive.
+.Pp
+Multiple
+.Fl o
+options can be specified.
+.Pp
+The
+.Ar property
+option may take one of two forms
+.Bl -bullet -compact
+.It
+.Ar property
+- Global property applied to all streams.
+.It
+.Ar property Ns @ Ns Op Ar volume Ns | Ns Ar filesystem
+- Local property applied to the specified
+.Ar volume
+or 
+.Ar filesystem
+streams only.
+.El
+.Pp
+The most specific
+.Fl o
+option takes precedence so in the case where both a global
+.Ar property
+and a
+.Ar property Ns @ Ns Op Ar filesystem Ns | Ns Ar volume
+are specified for the same
+.Ar property
+the value of said
+.Ar property
+will be the one which most closely matches the domain of the
+.Ar property .
+.Pp
+If both
+.Fl o
+and 
+.Fl x
+are specified for the same
+.Ar property
+the
+.Fl x
+option takes precedence unless the
+.Fl o
+option is a better domain match than the 
+.Fl x
+option.
+.It Xo
+.Fl x Ar property
+.Xc
+Ensures that the effective value of the specified property after the receive is
+unaffected by the value of that property in the send stream (if any), as if the
+property had been excluded from the send stream.
+.Pp
+If the specified property is not present in the send stream, this option does
+nothing.
+.Pp
+If a received property needs to be overridden, the effective value can be
+set or inherited, depending on the property.
+.Pp
+In the case of an incremental update,
+.Fl x
+leaves any existing local setting or explicit inheritance unchanged (since the
+received property is already overridden).
+.Pp
+The
+.Ar property
+option may take one of two forms
+.Bl -bullet -compact
+.It
+.Ar property
+- Global property excluded from all streams.
+.It
+.Ar property Ns @ Ns Op Ar volume Ns | Ns Ar filesystem
+- Local property excluded from the specified
+.Ar volume
+or 
+.Ar filesystem
+streams only.
+.El
+.Pp
+All
+.Fl o
+restrictions apply equally to
+.Fl x.
 .El
 .It Xo
 .Nm
--- cddl/contrib/opensolaris/cmd/zfs/zfs_main.c.orig	2012-09-28 12:05:10.787091006 +0000
+++ cddl/contrib/opensolaris/cmd/zfs/zfs_main.c	2012-09-30 23:49:37.368220482 +0000
@@ -251,9 +251,9 @@
 	case HELP_PROMOTE:
 		return (gettext("\tpromote <clone-filesystem>\n"));
 	case HELP_RECEIVE:
-		return (gettext("\treceive [-vnFu] <filesystem|volume|"
-		"snapshot>\n"
-		"\treceive [-vnFu] [-d | -e] <filesystem>\n"));
+		return (gettext("\treceive [-vnFu] [-d | -e] [-o <property>] "
+		    "... [-x <property>] ... [-l <filesystem|volume>] ... "
+		    " <filesystem|volume|snapshot>\n"));
 	case HELP_RENAME:
 		return (gettext("\trename <filesystem|volume|snapshot> "
 		    "<filesystem|volume|snapshot>\n"
@@ -478,6 +478,21 @@
 }
 
 static int
+parsepropname(nvlist_t *props)
+{
+	char *propname = optarg;
+
+	if (nvlist_lookup_string(props, propname, NULL) == 0) {
+		(void) fprintf(stderr, gettext("property '%s' "
+		    "specified multiple times\n"), propname);
+		return (-1);
+	}
+	if (nvlist_add_boolean(props, propname))
+		nomem();
+	return (0);
+}
+
+static int
 parseprop(nvlist_t *props)
 {
 	char *propname = optarg;
@@ -3635,18 +3650,24 @@
 }
 
 /*
- * zfs receive [-vnFu] [-d | -e] <fs@snap>
+ * zfs receive [-vnFu] [-d | -e] [-l <volume|filesystem>] ...
+ * [-o property=value] ... [-x property] ... <volume|filesytem|snapshot>
  *
  * Restore a backup stream from stdin.
  */
 static int
 zfs_do_receive(int argc, char **argv)
 {
+	nvlist_t *props, *limitds;
 	int c, err;
 	recvflags_t flags = { 0 };
+	if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
+		nomem();
+	if (nvlist_alloc(&limitds, NV_UNIQUE_NAME, 0) != 0)
+		nomem();
 
 	/* check options */
-	while ((c = getopt(argc, argv, ":denuvF")) != -1) {
+	while ((c = getopt(argc, argv, ":del:no:uvx:F")) != -1) {
 		switch (c) {
 		case 'd':
 			flags.isprefix = B_TRUE;
@@ -3655,15 +3676,32 @@
 			flags.isprefix = B_TRUE;
 			flags.istail = B_TRUE;
 			break;
+		case 'l':
+			if (parsepropname(limitds)) {
+				err = 1;
+				goto recverror;
+			}
+			break;
 		case 'n':
 			flags.dryrun = B_TRUE;
 			break;
+		case 'o':
+			if (parseprop(props)) {
+				err = 1;
+				goto recverror;
+			}
+			break;
 		case 'u':
 			flags.nomount = B_TRUE;
 			break;
 		case 'v':
 			flags.verbose = B_TRUE;
 			break;
+		case 'x':
+			if (parsepropname(props)) {
+				err = 1;
+				goto recverror;
+			}
 		case 'F':
 			flags.force = B_TRUE;
 			break;
@@ -3700,7 +3738,24 @@
 		return (1);
 	}
 
-	err = zfs_receive(g_zfs, argv[0], &flags, STDIN_FILENO, NULL);
+	if (nvlist_empty(props)) {
+		nvlist_free(props);
+		props = NULL;
+	}
+
+	if (nvlist_empty(limitds)) {
+		nvlist_free(limitds);
+		limitds = NULL;
+	}
+
+	err = zfs_receive(g_zfs, argv[0], &flags, STDIN_FILENO, props, limitds, NULL);
+
+recverror:
+	if (props != NULL)
+		nvlist_free(props);
+
+	if (limitds != NULL)
+		nvlist_free(limitds);
 
 	return (err != 0);
 }
--- cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h.orig	2012-09-28 13:30:59.119107089 +0000
+++ cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h	2012-09-30 15:45:38.747577467 +0000
@@ -622,7 +622,7 @@
 } recvflags_t;
 
 extern int zfs_receive(libzfs_handle_t *, const char *, recvflags_t *,
-    int, avl_tree_t *);
+    int, nvlist_t *, nvlist_t *, avl_tree_t *);
 
 typedef enum diff_flags {
 	ZFS_DIFF_PARSEABLE = 0x1,
--- cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c.orig	2012-09-29 01:20:12.812833366 +0000
+++ cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c	2012-10-01 21:56:01.417440564 +0000
@@ -2746,7 +2752,7 @@
  * 'zoned' property, which is used to validate property settings when creating
  * new datasets.
  */
-static int
+int
 check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned,
     boolean_t accept_ancestor, int *prefixlen)
 {
--- cddl/contrib/opensolaris/lib/libzfs/common/libzfs_impl.h.orig	2012-09-28 16:41:57.768614244 +0000
+++ cddl/contrib/opensolaris/lib/libzfs/common/libzfs_impl.h	2012-09-28 16:47:22.390247435 +0000
@@ -188,6 +188,8 @@
 
 void remove_mountpoint(zfs_handle_t *);
 int create_parents(libzfs_handle_t *, char *, int);
+int check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned,
+    boolean_t accept_ancestor, int *prefixlen);
 boolean_t isa_child_of(const char *dataset, const char *parent);
 
 zfs_handle_t *make_dataset_handle(libzfs_handle_t *, const char *);
--- cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c.orig	2012-10-29 17:58:11.000000000 +0000
+++ cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c	2012-10-30 16:11:46.777082243 +0000
@@ -55,7 +55,8 @@
 #define	ENODATA	EIDRM
 
 static int zfs_receive_impl(libzfs_handle_t *, const char *, recvflags_t *,
-    int, const char *, nvlist_t *, avl_tree_t *, char **, int, uint64_t *);
+    int, nvlist_t *, nvlist_t *, const char *, nvlist_t *, avl_tree_t *,
+    char **, int, uint64_t *);
 
 static const zio_cksum_t zero_cksum = { 0 };
 
@@ -1856,7 +1857,7 @@
 static int
 recv_incremental_replication(libzfs_handle_t *hdl, const char *tofs,
     recvflags_t *flags, nvlist_t *stream_nv, avl_tree_t *stream_avl,
-    nvlist_t *renamed)
+    nvlist_t *renamed, nvlist_t *limitds)
 {
 	nvlist_t *local_nv, *deleted = NULL;
 	avl_tree_t *local_avl;
@@ -1908,6 +1909,12 @@
 		    &parent_fromsnap_guid));
 		(void) nvlist_lookup_uint64(nvfs, "origin", &originguid);
 
+		if (limitds && !nvlist_exists(limitds, fsname)) {
+			if (flags->verbose)
+				(void) printf("skipping replication of %s\n", fsname);
+			continue;
+		}
+
 		/*
 		 * First find the stream's fs, so we can check for
 		 * a different origin (due to "zfs promote")
@@ -2163,8 +2170,9 @@
 
 static int
 zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname,
-    recvflags_t *flags, dmu_replay_record_t *drr, zio_cksum_t *zc,
-    char **top_zfs, int cleanup_fd, uint64_t *action_handlep)
+    recvflags_t *flags, nvlist_t *props, nvlist_t *limitds,
+    dmu_replay_record_t *drr, zio_cksum_t *zc, char **top_zfs, int cleanup_fd,
+    uint64_t *action_handlep)
 {
 	nvlist_t *stream_nv = NULL;
 	avl_tree_t *stream_avl = NULL;
@@ -2284,7 +2292,7 @@
 			}
 
 			softerr = recv_incremental_replication(hdl, tofs, flags,
-			    stream_nv, stream_avl, renamed);
+			    stream_nv, stream_avl, renamed, limitds);
 
 			/* Unmount renamed filesystems before receiving. */
 			while ((pair = nvlist_next_nvpair(renamed,
@@ -2330,8 +2338,8 @@
 		 * recv_skip() and return 0).
 		 */
 		error = zfs_receive_impl(hdl, destname, flags, fd,
-		    sendfs, stream_nv, stream_avl, top_zfs, cleanup_fd,
-		    action_handlep);
+		    props, limitds, sendfs, stream_nv, stream_avl, top_zfs,
+		    cleanup_fd, action_handlep);
 		if (error == ENODATA) {
 			error = 0;
 			break;
@@ -2345,7 +2353,7 @@
 		 * renames again.
 		 */
 		softerr = recv_incremental_replication(hdl, tofs, flags,
-		    stream_nv, stream_avl, NULL);
+		    stream_nv, stream_avl, NULL, limitds);
 	}
 
 out:
@@ -2448,20 +2456,183 @@
 }
 
 /*
+ * Calculate a list of properties for the current dataset taking into account
+ * its current properties (props) and the external (exprops)
+ */
+static int
+props_override(char *dsname, nvlist_t *props, nvlist_t *exprops,
+    nvlist_t **npropsp, recvflags_t *flags, libzfs_handle_t *hdl,
+    zfs_type_t type, uint64_t zoned, zfs_handle_t *zhp,
+    const char *errbuf)
+{
+	nvlist_t *doprops, *goprops, *dxprops, *gxprops, *nprops, *vprops;
+	nvpair_t *pair;
+	char *strval;
+	int ret = 0;
+
+	if (nvlist_empty(props) || nvlist_empty(exprops))
+		return (0); /* No properties */
+
+	if (nvlist_dup(props, &nprops, 0) != 0)
+		return (-1);
+
+	VERIFY(nvlist_alloc(&doprops, NV_UNIQUE_NAME, 0) == 0);
+	VERIFY(nvlist_alloc(&goprops, NV_UNIQUE_NAME, 0) == 0);
+	VERIFY(nvlist_alloc(&dxprops, NV_UNIQUE_NAME, 0) == 0);
+	VERIFY(nvlist_alloc(&gxprops, NV_UNIQUE_NAME, 0) == 0);
+
+	/* build lists to process in order */
+	pair = nvlist_next_nvpair(exprops, NULL);
+	while (pair != NULL) {
+		const char *propname = nvpair_name(pair);
+		char *tods = strchr(propname, '@');
+		if (tods == NULL) {
+			switch(nvpair_type(pair))
+			{
+			case DATA_TYPE_BOOLEAN:
+				(void) nvlist_add_nvpair(gxprops, pair);
+				break;
+			case DATA_TYPE_STRING:
+				(void) nvlist_add_nvpair(goprops, pair);
+				break;
+			default:
+				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+				    "'%s' must be a string or boolean"),
+				    propname);
+                                (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+				ret = -1;
+				goto error;
+			}
+		} else if (strcmp(dsname, tods+1) == 0) {
+			/* dataset specific property */
+			*tods = '\0';
+			switch(nvpair_type(pair))
+			{
+			case DATA_TYPE_BOOLEAN:
+				(void) nvlist_add_boolean(dxprops, propname);
+				break;
+			case DATA_TYPE_STRING:
+				nvpair_value_string(pair, &strval);
+				(void) nvlist_add_string(doprops, propname,
+				    strval);
+				break;
+			default:
+				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+				    "'%s' must be a string or boolean"),
+				    propname);
+                                (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+				ret = -1;
+				goto error;
+			}
+			*tods = '@';
+		}
+		pair = nvlist_next_nvpair(exprops, pair);
+	}
+
+	/* convert override properties e.g. strings to native */
+	if ((vprops = zfs_valid_proplist(hdl, type, goprops, zoned, zhp,
+	    errbuf)) == NULL)
+		goto error;
+
+	nvlist_free(goprops);
+	goprops = vprops;
+
+	if ((vprops = zfs_valid_proplist(hdl, type, doprops, zoned, zhp,
+	    errbuf)) == NULL)
+		goto error;
+	
+	nvlist_free(doprops);
+	doprops = vprops;
+
+	/* global - override / set properties */
+	pair = nvlist_next_nvpair(goprops, NULL);
+	while (pair != NULL) {
+		const char *pname = nvpair_name(pair);
+		if (!nvlist_exists(gxprops, pname) &&
+		    !nvlist_exists(dxprops, pname)) {
+			if (flags->verbose)
+				(void) printf("%s %s property from %s\n",
+				    nvlist_exists(nprops, pname) ?
+				    "overriding" : "setting", pname, dsname);
+			(void) nvlist_add_nvpair(nprops, pair);
+		}
+		
+		pair = nvlist_next_nvpair(goprops, pair);
+	}
+
+	/* global - exclude properties */
+	pair = nvlist_next_nvpair(gxprops, NULL);
+	while (pair != NULL) {
+		const char *pname = nvpair_name(pair);
+		if (!nvlist_exists(doprops, pname)) {
+			if (flags->verbose && nvlist_exists(nprops, pname))
+				(void) printf("excluding %s property from %s\n",
+				    pname, dsname);
+
+			(void) nvlist_remove_all(nprops, pname);
+		}
+
+		pair = nvlist_next_nvpair(gxprops, pair);
+	}
+
+	/* dataset - override / set properties */
+	pair = nvlist_next_nvpair(doprops, NULL);
+	while (pair != NULL) {
+		const char *pname = nvpair_name(pair);
+		if (!nvlist_exists(dxprops, pname)) {
+			if (flags->verbose)
+				(void) printf("%s %s property from %s\n",
+				    nvlist_exists(nprops, pname) ?
+				    "overriding" : "setting", pname, dsname);
+			(void) nvlist_add_nvpair(nprops, pair);
+		}
+		
+		pair = nvlist_next_nvpair(doprops, pair);
+	}
+
+	/* dataset - exclude properties */
+	pair = nvlist_next_nvpair(dxprops, NULL);
+	while (pair != NULL) {
+		const char *pname = nvpair_name(pair);
+		if (nvlist_exists(nprops, pname)) {
+			if (flags->verbose)
+				(void) printf("excluding %s property from %s\n",
+				    pname, dsname);
+
+			(void) nvlist_remove_all(nprops, pname);
+		}
+
+		pair = nvlist_next_nvpair(dxprops, pair);
+	}
+
+	*npropsp = nprops;
+
+error:
+	if (0 != ret)
+		nvlist_free(nprops);
+	nvlist_free(goprops);
+	nvlist_free(gxprops);
+	nvlist_free(doprops);
+	nvlist_free(dxprops);
+	return (ret);
+}
+
+/*
  * Restores a backup of tosnap from the file descriptor specified by infd.
  */
 static int
 zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
-    recvflags_t *flags, dmu_replay_record_t *drr,
-    dmu_replay_record_t *drr_noswap, const char *sendfs,
-    nvlist_t *stream_nv, avl_tree_t *stream_avl, char **top_zfs, int cleanup_fd,
-    uint64_t *action_handlep)
+    recvflags_t *flags, nvlist_t *exprops, nvlist_t *limitds,
+    dmu_replay_record_t *drr, dmu_replay_record_t *drr_noswap,
+    const char *sendfs, nvlist_t *stream_nv, avl_tree_t *stream_avl,
+    char **top_zfs, int cleanup_fd, uint64_t *action_handlep)
 {
 	zfs_cmd_t zc = { 0 };
 	time_t begin_time;
-	int ioctl_err, ioctl_errno, err;
+	int ioctl_err, ioctl_errno, err, skip;
 	char *cp;
 	struct drr_begin *drrb = &drr->drr_u.drr_begin;
+	char dsname[ZFS_MAXNAMELEN];
 	char errbuf[1024];
 	char prop_errbuf[1024];
 	const char *chopprefix;
@@ -2470,8 +2641,10 @@
 	uint64_t parent_snapguid = 0;
 	prop_changelist_t *clp = NULL;
 	nvlist_t *snapprops_nvlist = NULL;
+	nvlist_t *props = NULL;
+	nvlist_t *nprops = NULL;
 	zprop_errflags_t prop_errflags;
-	boolean_t recursive;
+	boolean_t recursive, has_exprops;
 
 	begin_time = time(NULL);
 
@@ -2483,10 +2656,9 @@
 
 	if (stream_avl != NULL) {
 		char *snapname;
+		nvlist_t *snapprops;
 		nvlist_t *fs = fsavl_find(stream_avl, drrb->drr_toguid,
 		    &snapname);
-		nvlist_t *props;
-		int ret;
 
 		(void) nvlist_lookup_uint64(fs, "parentfromsnap",
 		    &parent_snapguid);
@@ -2498,17 +2670,16 @@
 			VERIFY(0 == nvlist_add_uint64(props,
 			    zfs_prop_to_name(ZFS_PROP_CANMOUNT), 0));
 		}
-		ret = zcmd_write_src_nvlist(hdl, &zc, props);
-		if (err)
+
+		if (err) {
 			nvlist_free(props);
+			props = NULL;
+		}
 
-		if (0 == nvlist_lookup_nvlist(fs, "snapprops", &props)) {
-			VERIFY(0 == nvlist_lookup_nvlist(props,
+		if (0 == nvlist_lookup_nvlist(fs, "snapprops", &snapprops)) {
+			VERIFY(0 == nvlist_lookup_nvlist(snapprops,
 			    snapname, &snapprops_nvlist));
 		}
-
-		if (ret != 0)
-			return (-1);
 	}
 
 	cp = NULL;
@@ -2670,6 +2841,11 @@
 	(void) strcpy(zc.zc_name, zc.zc_value);
 	*strchr(zc.zc_name, '@') = '\0';
 
+	(void) strcpy(dsname, drrb->drr_toname);
+	*strchr(dsname, '@') = '\0';
+
+	has_exprops = !nvlist_empty(exprops);
+
 	if (zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) {
 		zfs_handle_t *zhp;
 
@@ -2733,6 +2909,16 @@
 				return (-1);
 			}
 		}
+
+		/* convert override properties e.g. strings to native */
+		if (has_exprops && props_override(dsname, props, exprops,
+		    &nprops, flags, hdl, zhp->zfs_type,
+		    zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, errbuf) != 0) {
+			zfs_close(zhp);
+			zcmd_free_nvlists(&zc);
+			return (-1);
+		}
+
 		zfs_close(zhp);
 	} else {
 		/*
@@ -2763,20 +2949,39 @@
 		}
 
 		newfs = B_TRUE;
+		if (has_exprops) {
+			/* Create an override set of properties if needed */
+			uint64_t zoned = 0;
+			if (flags->isprefix && !flags->istail && !flags->dryrun) {
+				/* Check if we're zoned or not */
+				if (check_parents(hdl, zc.zc_value, &zoned, B_FALSE, NULL) != 0) {
+					zcmd_free_nvlists(&zc);
+					return (-1);
+				}
+			}
+
+			if (props_override(dsname, props, exprops, &nprops, flags,
+			    hdl, ZFS_TYPE_DATASET, zoned, NULL, errbuf) != 0) {
+				zcmd_free_nvlists(&zc);
+				return (-1);
+			}
+		}
 	}
 
 	zc.zc_begin_record = drr_noswap->drr_u.drr_begin;
 	zc.zc_cookie = infd;
 	zc.zc_guid = flags->force;
+	skip = limitds && !nvlist_exists(limitds, dsname);
 	if (flags->verbose) {
 		(void) printf("%s %s stream of %s into %s\n",
-		    flags->dryrun ? "would receive" : "receiving",
+		    skip ? (flags->dryrun ? "would skip" : "skipping") :
+		    (flags->dryrun ? "would receive" : "receiving"),
 		    drrb->drr_fromguid ? "incremental" : "full",
 		    drrb->drr_toname, zc.zc_value);
 		(void) fflush(stdout);
 	}
 
-	if (flags->dryrun) {
+	if (flags->dryrun || skip) {
 		zcmd_free_nvlists(&zc);
 		return (recv_skip(hdl, infd, flags->byteswap));
 	}
@@ -2786,6 +2991,15 @@
 	zc.zc_cleanup_fd = cleanup_fd;
 	zc.zc_action_handle = *action_handlep;
 
+	if (nprops) {
+		if (zcmd_write_src_nvlist(hdl, &zc, nprops) != 0) {
+			nvlist_free(nprops);
+			return (-1);
+		}
+		nvlist_free(nprops);
+	} else if (props && zcmd_write_src_nvlist(hdl, &zc, props) != 0)
+		return (-1);
+
 	err = ioctl_err = zfs_ioctl(hdl, ZFS_IOC_RECV, &zc);
 	ioctl_errno = errno;
 	prop_errflags = (zprop_errflags_t)zc.zc_obj;
@@ -2993,8 +3207,9 @@
 
 static int
 zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap, recvflags_t *flags,
-    int infd, const char *sendfs, nvlist_t *stream_nv, avl_tree_t *stream_avl,
-    char **top_zfs, int cleanup_fd, uint64_t *action_handlep)
+    int infd, nvlist_t *props, nvlist_t *limitds, const char *sendfs,
+    nvlist_t *stream_nv, avl_tree_t *stream_avl, char **top_zfs, int cleanup_fd,
+    uint64_t *action_handlep)
 {
 	int err;
 	dmu_replay_record_t drr, drr_noswap;
@@ -3086,13 +3301,14 @@
 			sendfs = nonpackage_sendfs;
 		}
 		return (zfs_receive_one(hdl, infd, tosnap, flags,
-		    &drr, &drr_noswap, sendfs, stream_nv, stream_avl,
-		    top_zfs, cleanup_fd, action_handlep));
+		    props, limitds, &drr, &drr_noswap, sendfs, stream_nv,
+		    stream_avl, top_zfs, cleanup_fd, action_handlep));
 	} else {
 		assert(DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo) ==
 		    DMU_COMPOUNDSTREAM);
 		return (zfs_receive_package(hdl, infd, tosnap, flags,
-		    &drr, &zcksum, top_zfs, cleanup_fd, action_handlep));
+		    props, limitds, &drr, &zcksum, top_zfs, cleanup_fd,
+		    action_handlep));
 	}
 }
 
@@ -3104,7 +3320,7 @@
  */
 int
 zfs_receive(libzfs_handle_t *hdl, const char *tosnap, recvflags_t *flags,
-    int infd, avl_tree_t *stream_avl)
+    int infd, nvlist_t *props, nvlist_t *limitds, avl_tree_t *stream_avl)
 {
 	char *top_zfs = NULL;
 	int err;
@@ -3114,8 +3330,8 @@
 	cleanup_fd = open(ZFS_DEV, O_RDWR|O_EXCL);
 	VERIFY(cleanup_fd >= 0);
 
-	err = zfs_receive_impl(hdl, tosnap, flags, infd, NULL, NULL,
-	    stream_avl, &top_zfs, cleanup_fd, &action_handle);
+	err = zfs_receive_impl(hdl, tosnap, flags, infd, props, limitds, NULL,
+	    NULL, stream_avl, &top_zfs, cleanup_fd, &action_handle);
 
 	VERIFY(0 == close(cleanup_fd));
 


>Release-Note:
>Audit-Trail:
>Unformatted:



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201210311016.q9VAGVAv042185>