aboutsummaryrefslogtreecommitdiffstats
path: root/module/zfs/zfs_ioctl.c
diff options
context:
space:
mode:
authorLOLi <[email protected]>2017-05-10 01:21:09 +0200
committerBrian Behlendorf <[email protected]>2017-05-09 16:21:09 -0700
commita3eeab2de68670a4481eab3d086982aff23b6906 (patch)
tree5e3da58bca04309596df84dd84b591e8631eae0f /module/zfs/zfs_ioctl.c
parent305bc4b370b20de81eaf10a1cf724374258b74d1 (diff)
Add property overriding (-o|-x) to 'zfs receive'
This allows users to specify "-o property=value" to override and "-x property" to exclude properties when receiving a zfs send stream. Both native and user properties can be specified. This is useful when using zfs send/receive for periodic backup/replication because it lets users change properties such as canmount, mountpoint, or compression without modifying the source. References: https://www.illumos.org/issues/2745 https://www.illumos.org/issues/3753 Reviewed by: Matthew Ahrens <[email protected]> Reviewed-by: Alek Pinchuk <[email protected]> Reviewed-by: Brian Behlendorf <[email protected]> Signed-off-by: loli10K <[email protected]> Closes #1350 Closes #5349
Diffstat (limited to 'module/zfs/zfs_ioctl.c')
-rw-r--r--module/zfs/zfs_ioctl.c196
1 files changed, 165 insertions, 31 deletions
diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c
index 109762795..f94bf3b1c 100644
--- a/module/zfs/zfs_ioctl.c
+++ b/module/zfs/zfs_ioctl.c
@@ -33,6 +33,7 @@
* Copyright (c) 2014 Integros [integros.com]
* Copyright 2016 Toomas Soome <[email protected]>
* Copyright (c) 2016 Actifio, Inc. All rights reserved.
+ * Copyright (c) 2017, loli10K <[email protected]>. All rights reserved.
*/
/*
@@ -2497,7 +2498,11 @@ retry:
}
/* Validate value type */
- if (err == 0 && prop == ZPROP_INVAL) {
+ if (err == 0 && source == ZPROP_SRC_INHERITED) {
+ /* inherited properties are expected to be booleans */
+ if (nvpair_type(propval) != DATA_TYPE_BOOLEAN)
+ err = SET_ERROR(EINVAL);
+ } else if (err == 0 && prop == ZPROP_INVAL) {
if (zfs_prop_user(propname)) {
if (nvpair_type(propval) != DATA_TYPE_STRING)
err = SET_ERROR(EINVAL);
@@ -2542,7 +2547,11 @@ retry:
err = zfs_check_settable(dsname, pair, CRED());
if (err == 0) {
- err = zfs_prop_set_special(dsname, source, pair);
+ if (source == ZPROP_SRC_INHERITED)
+ err = -1; /* does not need special handling */
+ else
+ err = zfs_prop_set_special(dsname, source,
+ pair);
if (err == -1) {
/*
* For better performance we build up a list of
@@ -2594,6 +2603,9 @@ retry:
strval = fnvpair_value_string(propval);
err = dsl_prop_set_string(dsname, propname,
source, strval);
+ } else if (nvpair_type(propval) == DATA_TYPE_BOOLEAN) {
+ err = dsl_prop_inherit(dsname, propname,
+ source);
} else {
intval = fnvpair_value_uint64(propval);
err = dsl_prop_set_int(dsname, propname, source,
@@ -4159,8 +4171,8 @@ static boolean_t zfs_ioc_recv_inject_err;
* encountered errors, if any. It's the callers responsibility to free.
*/
static int
-zfs_ioc_recv_impl(char *tofs, char *tosnap, char *origin,
- nvlist_t *props, boolean_t force, boolean_t resumable, int input_fd,
+zfs_ioc_recv_impl(char *tofs, char *tosnap, char *origin, nvlist_t *recvprops,
+ nvlist_t *localprops, boolean_t force, boolean_t resumable, int input_fd,
dmu_replay_record_t *begin_record, int cleanup_fd, uint64_t *read_bytes,
uint64_t *errflags, uint64_t *action_handle, nvlist_t **errors)
{
@@ -4170,6 +4182,7 @@ zfs_ioc_recv_impl(char *tofs, char *tosnap, char *origin,
offset_t off;
nvlist_t *delayprops = NULL; /* sent properties applied post-receive */
nvlist_t *origprops = NULL; /* existing properties */
+ nvlist_t *origrecvd = NULL; /* existing received properties */
boolean_t first_recvd_props = B_FALSE;
file_t *input_fp;
@@ -4191,7 +4204,7 @@ zfs_ioc_recv_impl(char *tofs, char *tosnap, char *origin,
* to the new data. Note that we must call dmu_recv_stream() if
* dmu_recv_begin() succeeds.
*/
- if (props != NULL && !drc.drc_newfs) {
+ if (recvprops != NULL && !drc.drc_newfs) {
if (spa_version(dsl_dataset_get_spa(drc.drc_ds)) >=
SPA_VERSION_RECVD_PROPS &&
!dsl_prop_get_hasrecvd(tofs))
@@ -4202,7 +4215,7 @@ zfs_ioc_recv_impl(char *tofs, char *tosnap, char *origin,
* completely replace the existing received properties, so stash
* away the existing ones.
*/
- if (dsl_prop_get_received(tofs, &origprops) == 0) {
+ if (dsl_prop_get_received(tofs, &origrecvd) == 0) {
nvlist_t *errlist = NULL;
/*
* Don't bother writing a property if its value won't
@@ -4213,29 +4226,76 @@ zfs_ioc_recv_impl(char *tofs, char *tosnap, char *origin,
* regardless.
*/
if (!first_recvd_props)
- props_reduce(props, origprops);
- if (zfs_check_clearable(tofs, origprops, &errlist) != 0)
+ props_reduce(recvprops, origrecvd);
+ if (zfs_check_clearable(tofs, origrecvd, &errlist) != 0)
(void) nvlist_merge(*errors, errlist, 0);
nvlist_free(errlist);
- if (clear_received_props(tofs, origprops,
- first_recvd_props ? NULL : props) != 0)
+ if (clear_received_props(tofs, origrecvd,
+ first_recvd_props ? NULL : recvprops) != 0)
+ *errflags |= ZPROP_ERR_NOCLEAR;
+ } else {
+ *errflags |= ZPROP_ERR_NOCLEAR;
+ }
+ }
+
+ /*
+ * Stash away existing properties so we can restore them on error unless
+ * we're doing the first receive after SPA_VERSION_RECVD_PROPS, in which
+ * case "origrecvd" will take care of that.
+ */
+ if (localprops != NULL && !drc.drc_newfs && !first_recvd_props) {
+ objset_t *os;
+ if (dmu_objset_hold(tofs, FTAG, &os) == 0) {
+ if (dsl_prop_get_all(os, &origprops) != 0) {
*errflags |= ZPROP_ERR_NOCLEAR;
+ }
+ dmu_objset_rele(os, FTAG);
} else {
*errflags |= ZPROP_ERR_NOCLEAR;
}
}
- if (props != NULL) {
+ if (recvprops != NULL) {
props_error = dsl_prop_set_hasrecvd(tofs);
if (props_error == 0) {
- delayprops = extract_delay_props(props);
+ delayprops = extract_delay_props(recvprops);
(void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_RECEIVED,
- props, *errors);
+ recvprops, *errors);
}
}
+ if (localprops != NULL) {
+ nvlist_t *oprops = fnvlist_alloc();
+ nvlist_t *xprops = fnvlist_alloc();
+ nvpair_t *nvp = NULL;
+
+ while ((nvp = nvlist_next_nvpair(localprops, nvp)) != NULL) {
+ if (nvpair_type(nvp) == DATA_TYPE_BOOLEAN) {
+ /* -x property */
+ const char *name = nvpair_name(nvp);
+ zfs_prop_t prop = zfs_name_to_prop(name);
+ if (prop != ZPROP_INVAL) {
+ if (!zfs_prop_inheritable(prop))
+ continue;
+ } else if (!zfs_prop_user(name))
+ continue;
+ fnvlist_add_boolean(xprops, name);
+ } else {
+ /* -o property=value */
+ fnvlist_add_nvpair(oprops, nvp);
+ }
+ }
+ (void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_LOCAL,
+ oprops, *errors);
+ (void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_INHERITED,
+ xprops, *errors);
+
+ nvlist_free(oprops);
+ nvlist_free(xprops);
+ }
+
off = input_fp->f_offset;
error = dmu_recv_stream(&drc, input_fp->f_vnode, &off, cleanup_fd,
action_handle);
@@ -4284,7 +4344,7 @@ zfs_ioc_recv_impl(char *tofs, char *tosnap, char *origin,
* Since zfs_ioc_recv_inject_err is only in DEBUG kernels,
* using ASSERT() will be just like a VERIFY.
*/
- ASSERT(nvlist_merge(props, delayprops, 0) == 0);
+ ASSERT(nvlist_merge(recvprops, delayprops, 0) == 0);
nvlist_free(delayprops);
}
@@ -4303,8 +4363,8 @@ zfs_ioc_recv_impl(char *tofs, char *tosnap, char *origin,
/*
* On error, restore the original props.
*/
- if (error != 0 && props != NULL && !drc.drc_newfs) {
- if (clear_received_props(tofs, props, NULL) != 0) {
+ if (error != 0 && recvprops != NULL && !drc.drc_newfs) {
+ if (clear_received_props(tofs, recvprops, NULL) != 0) {
/*
* We failed to clear the received properties.
* Since we may have left a $recvd value on the
@@ -4315,7 +4375,7 @@ zfs_ioc_recv_impl(char *tofs, char *tosnap, char *origin,
dsl_prop_unset_hasrecvd(tofs);
}
- if (origprops == NULL && !drc.drc_newfs) {
+ if (origrecvd == NULL && !drc.drc_newfs) {
/* We failed to stash the original properties. */
*errflags |= ZPROP_ERR_NORESTORE;
}
@@ -4326,10 +4386,10 @@ zfs_ioc_recv_impl(char *tofs, char *tosnap, char *origin,
* explicitly if we're restoring local properties cleared in the
* first new-style receive.
*/
- if (origprops != NULL &&
+ if (origrecvd != NULL &&
zfs_set_prop_nvlist(tofs, (first_recvd_props ?
ZPROP_SRC_LOCAL : ZPROP_SRC_RECEIVED),
- origprops, NULL) != 0) {
+ origrecvd, NULL) != 0) {
/*
* We stashed the original properties but failed to
* restore them.
@@ -4337,8 +4397,64 @@ zfs_ioc_recv_impl(char *tofs, char *tosnap, char *origin,
*errflags |= ZPROP_ERR_NORESTORE;
}
}
+ if (error != 0 && localprops != NULL && !drc.drc_newfs &&
+ !first_recvd_props) {
+ nvlist_t *setprops;
+ nvlist_t *inheritprops;
+ nvpair_t *nvp;
+
+ if (origprops == NULL) {
+ /* We failed to stash the original properties. */
+ *errflags |= ZPROP_ERR_NORESTORE;
+ goto out;
+ }
+
+ /* Restore original props */
+ setprops = fnvlist_alloc();
+ inheritprops = fnvlist_alloc();
+ nvp = NULL;
+ while ((nvp = nvlist_next_nvpair(localprops, nvp)) != NULL) {
+ const char *name = nvpair_name(nvp);
+ const char *source;
+ nvlist_t *attrs;
+
+ if (!nvlist_exists(origprops, name)) {
+ /*
+ * Property was not present or was explicitly
+ * inherited before the receive, restore this.
+ */
+ fnvlist_add_boolean(inheritprops, name);
+ continue;
+ }
+ attrs = fnvlist_lookup_nvlist(origprops, name);
+ source = fnvlist_lookup_string(attrs, ZPROP_SOURCE);
+
+ /* Skip received properties */
+ if (strcmp(source, ZPROP_SOURCE_VAL_RECVD) == 0)
+ continue;
+
+ if (strcmp(source, tofs) == 0) {
+ /* Property was locally set */
+ fnvlist_add_nvlist(setprops, name, attrs);
+ } else {
+ /* Property was implicitly inherited */
+ fnvlist_add_boolean(inheritprops, name);
+ }
+ }
+
+ if (zfs_set_prop_nvlist(tofs, ZPROP_SRC_LOCAL, setprops,
+ NULL) != 0)
+ *errflags |= ZPROP_ERR_NORESTORE;
+ if (zfs_set_prop_nvlist(tofs, ZPROP_SRC_INHERITED, inheritprops,
+ NULL) != 0)
+ *errflags |= ZPROP_ERR_NORESTORE;
+
+ nvlist_free(setprops);
+ nvlist_free(inheritprops);
+ }
out:
releasef(input_fd);
+ nvlist_free(origrecvd);
nvlist_free(origprops);
if (error == 0)
@@ -4351,6 +4467,8 @@ out:
* inputs:
* zc_name name of containing filesystem (unused)
* zc_nvlist_src{_size} nvlist of properties to apply
+ * zc_nvlist_conf{_size} nvlist of properties to exclude
+ * (DATA_TYPE_BOOLEAN) and override (everything else)
* zc_value name of snapshot to create
* zc_string name of clone origin (if DRR_FLAG_CLONE)
* zc_cookie file descriptor to recv from
@@ -4370,7 +4488,8 @@ zfs_ioc_recv(zfs_cmd_t *zc)
{
dmu_replay_record_t begin_record;
nvlist_t *errors = NULL;
- nvlist_t *props = NULL;
+ nvlist_t *recvdprops = NULL;
+ nvlist_t *localprops = NULL;
char *origin = NULL;
char *tosnap;
char tofs[ZFS_MAX_DATASET_NAME_LEN];
@@ -4387,7 +4506,12 @@ zfs_ioc_recv(zfs_cmd_t *zc)
if (zc->zc_nvlist_src != 0 &&
(error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
- zc->zc_iflags, &props)) != 0)
+ zc->zc_iflags, &recvdprops)) != 0)
+ return (error);
+
+ if (zc->zc_nvlist_conf != 0 &&
+ (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
+ zc->zc_iflags, &localprops)) != 0)
return (error);
if (zc->zc_string[0])
@@ -4397,10 +4521,12 @@ zfs_ioc_recv(zfs_cmd_t *zc)
begin_record.drr_payloadlen = 0;
begin_record.drr_u.drr_begin = zc->zc_begin_record;
- error = zfs_ioc_recv_impl(tofs, tosnap, origin, props, zc->zc_guid,
- B_FALSE, zc->zc_cookie, &begin_record, zc->zc_cleanup_fd,
- &zc->zc_cookie, &zc->zc_obj, &zc->zc_action_handle, &errors);
- nvlist_free(props);
+ error = zfs_ioc_recv_impl(tofs, tosnap, origin, recvdprops, localprops,
+ zc->zc_guid, B_FALSE, zc->zc_cookie, &begin_record,
+ zc->zc_cleanup_fd, &zc->zc_cookie, &zc->zc_obj,
+ &zc->zc_action_handle, &errors);
+ nvlist_free(recvdprops);
+ nvlist_free(localprops);
/*
* Now that all props, initial and delayed, are set, report the prop
@@ -4424,7 +4550,8 @@ zfs_ioc_recv(zfs_cmd_t *zc)
/*
* innvl: {
* "snapname" -> full name of the snapshot to create
- * (optional) "props" -> properties to set (nvlist)
+ * (optional) "props" -> received properties to set (nvlist)
+ * (optional) "localprops" -> override and exclude properties (nvlist)
* (optional) "origin" -> name of clone origin (DRR_FLAG_CLONE)
* "begin_record" -> non-byteswapped dmu_replay_record_t
* "input_fd" -> file descriptor to read stream from (int32)
@@ -4447,7 +4574,8 @@ zfs_ioc_recv_new(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl)
dmu_replay_record_t *begin_record;
uint_t begin_record_size;
nvlist_t *errors = NULL;
- nvlist_t *props = NULL;
+ nvlist_t *recvprops = NULL;
+ nvlist_t *localprops = NULL;
char *snapname = NULL;
char *origin = NULL;
char *tosnap;
@@ -4498,12 +4626,17 @@ zfs_ioc_recv_new(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl)
if (error && error != ENOENT)
return (error);
- error = nvlist_lookup_nvlist(innvl, "props", &props);
+ /* we still use "props" here for backwards compatibility */
+ error = nvlist_lookup_nvlist(innvl, "props", &recvprops);
+ if (error && error != ENOENT)
+ return (error);
+
+ error = nvlist_lookup_nvlist(innvl, "localprops", &localprops);
if (error && error != ENOENT)
return (error);
- error = zfs_ioc_recv_impl(tofs, tosnap, origin, props, force,
- resumable, input_fd, begin_record, cleanup_fd, &read_bytes,
+ error = zfs_ioc_recv_impl(tofs, tosnap, origin, recvprops, localprops,
+ force, resumable, input_fd, begin_record, cleanup_fd, &read_bytes,
&errflags, &action_handle, &errors);
fnvlist_add_uint64(outnvl, "read_bytes", read_bytes);
@@ -4512,7 +4645,8 @@ zfs_ioc_recv_new(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl)
fnvlist_add_nvlist(outnvl, "errors", errors);
nvlist_free(errors);
- nvlist_free(props);
+ nvlist_free(recvprops);
+ nvlist_free(localprops);
return (error);
}