>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:
_______________________________________________
[email protected] mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-bugs
To unsubscribe, send any mail to "[email protected]"

Reply via email to