From owner-freebsd-bugs@FreeBSD.ORG Wed Oct 31 10:20:01 2012 Return-Path: Delivered-To: freebsd-bugs@hub.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [69.147.83.52]) by hub.freebsd.org (Postfix) with ESMTP id A8350BB5 for ; Wed, 31 Oct 2012 10:20:01 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (freefall.freebsd.org [IPv6:2001:1900:2254:206c::16:87]) by mx1.freebsd.org (Postfix) with ESMTP id 7CCB78FC17 for ; Wed, 31 Oct 2012 10:20:01 +0000 (UTC) Received: from freefall.freebsd.org (localhost [127.0.0.1]) by freefall.freebsd.org (8.14.5/8.14.5) with ESMTP id q9VAK1SV011232 for ; Wed, 31 Oct 2012 10:20:01 GMT (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.14.5/8.14.5/Submit) id q9VAK1kD011231; Wed, 31 Oct 2012 10:20:01 GMT (envelope-from gnats) Resent-Date: Wed, 31 Oct 2012 10:20:01 GMT Resent-Message-Id: <201210311020.q9VAK1kD011231@freefall.freebsd.org> Resent-From: FreeBSD-gnats-submit@FreeBSD.org (GNATS Filer) Resent-To: freebsd-bugs@FreeBSD.org Resent-Reply-To: FreeBSD-gnats-submit@FreeBSD.org, Steven Hartland Received: from mx1.freebsd.org (mx1.freebsd.org [69.147.83.52]) by hub.freebsd.org (Postfix) with ESMTP id B89FBB61 for ; Wed, 31 Oct 2012 10:16:34 +0000 (UTC) (envelope-from nobody@FreeBSD.org) Received: from red.freebsd.org (red.freebsd.org [IPv6:2001:4f8:fff6::22]) by mx1.freebsd.org (Postfix) with ESMTP id 9E7ED8FC08 for ; Wed, 31 Oct 2012 10:16:34 +0000 (UTC) Received: from red.freebsd.org (localhost [127.0.0.1]) by red.freebsd.org (8.14.5/8.14.5) with ESMTP id q9VAGVfw042186 for ; Wed, 31 Oct 2012 10:16:31 GMT (envelope-from nobody@red.freebsd.org) Received: (from nobody@localhost) by red.freebsd.org (8.14.5/8.14.5/Submit) id q9VAGVAv042185; Wed, 31 Oct 2012 10:16:31 GMT (envelope-from nobody) Message-Id: <201210311016.q9VAGVAv042185@red.freebsd.org> Date: Wed, 31 Oct 2012 10:16:31 GMT From: Steven Hartland To: freebsd-gnats-submit@FreeBSD.org X-Send-Pr-Version: www-3.1 Subject: misc/173234: Allow filtering of properties on zfs receive (patch included) X-BeenThere: freebsd-bugs@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: Bug reports List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 31 Oct 2012 10:20:01 -0000 >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 ) 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 \n")); case HELP_RECEIVE: - return (gettext("\treceive [-vnFu] \n" - "\treceive [-vnFu] [-d | -e] \n")); + return (gettext("\treceive [-vnFu] [-d | -e] [-o ] " + "... [-x ] ... [-l ] ... " + " \n")); case HELP_RENAME: return (gettext("\trename " "\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] + * zfs receive [-vnFu] [-d | -e] [-l ] ... + * [-o property=value] ... [-x property] ... * * 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: