misc/173234: Allow filtering of properties on zfs receive (patch included)
Steven Hartland
steven.hartland at multiplay.co.uk
Wed Oct 31 10:20:01 UTC 2012
>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 at 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 at 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:
More information about the freebsd-bugs
mailing list