Normalize property names for zfs receive
It turns out, userland is much more happy with aliased property names than the kernel is. So let's normalize those to the expected names before we pass them off. Added a test case hacked up from the other recv -o/-x test that fails on unpatched git and passes here. Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Signed-off-by: Rich Ercolani <rincebrain@gmail.com> Closes #12607 Closes #12609
This commit is contained in:
parent
adeccfea17
commit
4476ccd906
|
@ -3958,6 +3958,19 @@ zfs_setup_cmdline_props(libzfs_handle_t *hdl, zfs_type_t type,
|
||||||
const char *name = nvpair_name(nvp);
|
const char *name = nvpair_name(nvp);
|
||||||
zfs_prop_t prop = zfs_name_to_prop(name);
|
zfs_prop_t prop = zfs_name_to_prop(name);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* It turns out, if we don't normalize "aliased" names
|
||||||
|
* e.g. compress= against the "real" names (e.g. compression)
|
||||||
|
* here, then setting/excluding them does not work as
|
||||||
|
* intended.
|
||||||
|
*
|
||||||
|
* But since user-defined properties wouldn't have a valid
|
||||||
|
* mapping here, we do this conditional dance.
|
||||||
|
*/
|
||||||
|
const char *newname = name;
|
||||||
|
if (prop >= ZFS_PROP_TYPE)
|
||||||
|
newname = zfs_prop_to_name(prop);
|
||||||
|
|
||||||
/* "origin" is processed separately, don't handle it here */
|
/* "origin" is processed separately, don't handle it here */
|
||||||
if (prop == ZFS_PROP_ORIGIN)
|
if (prop == ZFS_PROP_ORIGIN)
|
||||||
continue;
|
continue;
|
||||||
|
@ -4004,11 +4017,12 @@ zfs_setup_cmdline_props(libzfs_handle_t *hdl, zfs_type_t type,
|
||||||
* locally-set, in which case its value will take
|
* locally-set, in which case its value will take
|
||||||
* priority over the received anyway.
|
* priority over the received anyway.
|
||||||
*/
|
*/
|
||||||
if (nvlist_exists(origprops, name)) {
|
if (nvlist_exists(origprops, newname)) {
|
||||||
nvlist_t *attrs;
|
nvlist_t *attrs;
|
||||||
char *source = NULL;
|
char *source = NULL;
|
||||||
|
|
||||||
attrs = fnvlist_lookup_nvlist(origprops, name);
|
attrs = fnvlist_lookup_nvlist(origprops,
|
||||||
|
newname);
|
||||||
if (nvlist_lookup_string(attrs,
|
if (nvlist_lookup_string(attrs,
|
||||||
ZPROP_SOURCE, &source) == 0 &&
|
ZPROP_SOURCE, &source) == 0 &&
|
||||||
strcmp(source, ZPROP_SOURCE_VAL_RECVD) != 0)
|
strcmp(source, ZPROP_SOURCE_VAL_RECVD) != 0)
|
||||||
|
@ -4021,10 +4035,10 @@ zfs_setup_cmdline_props(libzfs_handle_t *hdl, zfs_type_t type,
|
||||||
*/
|
*/
|
||||||
if (!zfs_prop_inheritable(prop) &&
|
if (!zfs_prop_inheritable(prop) &&
|
||||||
!zfs_prop_user(name) && /* can be inherited too */
|
!zfs_prop_user(name) && /* can be inherited too */
|
||||||
nvlist_exists(recvprops, name))
|
nvlist_exists(recvprops, newname))
|
||||||
fnvlist_remove(recvprops, name);
|
fnvlist_remove(recvprops, newname);
|
||||||
else
|
else
|
||||||
fnvlist_add_nvpair(*oxprops, nvp);
|
fnvlist_add_boolean(*oxprops, newname);
|
||||||
break;
|
break;
|
||||||
case DATA_TYPE_STRING: /* -o property=value */
|
case DATA_TYPE_STRING: /* -o property=value */
|
||||||
/*
|
/*
|
||||||
|
@ -4045,7 +4059,8 @@ zfs_setup_cmdline_props(libzfs_handle_t *hdl, zfs_type_t type,
|
||||||
ret = zfs_error(hdl, EZFS_BADPROP, errbuf);
|
ret = zfs_error(hdl, EZFS_BADPROP, errbuf);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
fnvlist_add_nvpair(oprops, nvp);
|
fnvlist_add_string(oprops, newname,
|
||||||
|
fnvpair_value_string(nvp));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||||
|
|
|
@ -232,6 +232,7 @@ tests = ['zfs_receive_001_pos', 'zfs_receive_002_pos', 'zfs_receive_003_pos',
|
||||||
'zfs_receive_010_pos', 'zfs_receive_011_pos', 'zfs_receive_012_pos',
|
'zfs_receive_010_pos', 'zfs_receive_011_pos', 'zfs_receive_012_pos',
|
||||||
'zfs_receive_013_pos', 'zfs_receive_014_pos', 'zfs_receive_015_pos',
|
'zfs_receive_013_pos', 'zfs_receive_014_pos', 'zfs_receive_015_pos',
|
||||||
'zfs_receive_016_pos', 'receive-o-x_props_override',
|
'zfs_receive_016_pos', 'receive-o-x_props_override',
|
||||||
|
'receive-o-x_props_aliases',
|
||||||
'zfs_receive_from_encrypted', 'zfs_receive_to_encrypted',
|
'zfs_receive_from_encrypted', 'zfs_receive_to_encrypted',
|
||||||
'zfs_receive_raw', 'zfs_receive_raw_incremental', 'zfs_receive_-e',
|
'zfs_receive_raw', 'zfs_receive_raw_incremental', 'zfs_receive_-e',
|
||||||
'zfs_receive_raw_-d', 'zfs_receive_from_zstd', 'zfs_receive_new_props']
|
'zfs_receive_raw_-d', 'zfs_receive_from_zstd', 'zfs_receive_new_props']
|
||||||
|
|
|
@ -19,6 +19,7 @@ dist_pkgdata_SCRIPTS = \
|
||||||
zfs_receive_015_pos.ksh \
|
zfs_receive_015_pos.ksh \
|
||||||
zfs_receive_016_pos.ksh \
|
zfs_receive_016_pos.ksh \
|
||||||
receive-o-x_props_override.ksh \
|
receive-o-x_props_override.ksh \
|
||||||
|
receive-o-x_props_aliases.ksh \
|
||||||
zfs_receive_from_encrypted.ksh \
|
zfs_receive_from_encrypted.ksh \
|
||||||
zfs_receive_from_zstd.ksh \
|
zfs_receive_from_zstd.ksh \
|
||||||
zfs_receive_new_props.ksh \
|
zfs_receive_new_props.ksh \
|
||||||
|
|
|
@ -0,0 +1,213 @@
|
||||||
|
#!/bin/ksh -p
|
||||||
|
#
|
||||||
|
# CDDL HEADER START
|
||||||
|
#
|
||||||
|
# This file and its contents are supplied under the terms of the
|
||||||
|
# Common Development and Distribution License ("CDDL"), version 1.0.
|
||||||
|
# You may only use this file in accordance with the terms of version
|
||||||
|
# 1.0 of the CDDL.
|
||||||
|
#
|
||||||
|
# A full copy of the text of the CDDL should have accompanied this
|
||||||
|
# source. A copy of the CDDL is also available via the Internet at
|
||||||
|
# http://www.illumos.org/license/CDDL.
|
||||||
|
#
|
||||||
|
# CDDL HEADER END
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright 2017, loli10K <ezomori.nozomu@gmail.com>. All rights reserved.
|
||||||
|
#
|
||||||
|
|
||||||
|
. $STF_SUITE/include/libtest.shlib
|
||||||
|
. $STF_SUITE/tests/functional/cli_root/zfs_set/zfs_set_common.kshlib
|
||||||
|
|
||||||
|
#
|
||||||
|
# DESCRIPTION:
|
||||||
|
# Verify ZFS property override (-o) and exclude (-x) options work when
|
||||||
|
# receiving a send stream, using property name aliases
|
||||||
|
#
|
||||||
|
# STRATEGY:
|
||||||
|
# 1. Create a filesystem with children.
|
||||||
|
# 2. Snapshot the filesystems.
|
||||||
|
# 3. Create various send streams (full, incremental, replication) and verify
|
||||||
|
# we can both override and exclude aliased properties.
|
||||||
|
#
|
||||||
|
|
||||||
|
verify_runnable "both"
|
||||||
|
|
||||||
|
function cleanup
|
||||||
|
{
|
||||||
|
log_must rm -f $streamfile_full
|
||||||
|
log_must rm -f $streamfile_incr
|
||||||
|
log_must rm -f $streamfile_repl
|
||||||
|
log_must rm -f $streamfile_trun
|
||||||
|
destroy_dataset "$orig" "-rf"
|
||||||
|
destroy_dataset "$dest" "-rf"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_assert "ZFS receive property alias override and exclude options work as expected."
|
||||||
|
log_onexit cleanup
|
||||||
|
|
||||||
|
orig=$TESTPOOL/$TESTFS1
|
||||||
|
origsub=$orig/sub
|
||||||
|
dest=$TESTPOOL/$TESTFS2
|
||||||
|
destsub=$dest/sub
|
||||||
|
typeset streamfile_full=$TESTDIR/streamfile_full.$$
|
||||||
|
typeset streamfile_incr=$TESTDIR/streamfile_incr.$$
|
||||||
|
typeset streamfile_repl=$TESTDIR/streamfile_repl.$$
|
||||||
|
typeset streamfile_trun=$TESTDIR/streamfile_trun.$$
|
||||||
|
|
||||||
|
#
|
||||||
|
# 3.1 Verify we can't specify the same property in multiple -o or -x options
|
||||||
|
# or an invalid value was specified.
|
||||||
|
#
|
||||||
|
# Create a full send stream
|
||||||
|
log_must zfs create $orig
|
||||||
|
log_must zfs snapshot $orig@snap1
|
||||||
|
log_must eval "zfs send $orig@snap1 > $streamfile_full"
|
||||||
|
# Verify we reject invalid options
|
||||||
|
log_mustnot eval "zfs recv $dest -o compress < $streamfile_full"
|
||||||
|
log_mustnot eval "zfs recv $dest -x compress=off < $streamfile_full"
|
||||||
|
log_mustnot eval "zfs recv $dest -o compress=off -x compress < $streamfile_full"
|
||||||
|
log_mustnot eval "zfs recv $dest -o compress=off -o compress=on < $streamfile_full"
|
||||||
|
log_mustnot eval "zfs recv $dest -x compress -x compress < $streamfile_full"
|
||||||
|
log_mustnot eval "zfs recv $dest -o version=1 < $streamfile_full"
|
||||||
|
log_mustnot eval "zfs recv $dest -x version < $streamfile_full"
|
||||||
|
log_mustnot eval "zfs recv $dest -x normalization < $streamfile_full"
|
||||||
|
# Verify we also reject invalid ZVOL options
|
||||||
|
log_must zfs create -V 32K -s $orig/zvol
|
||||||
|
log_must eval "zfs send $orig@snap1 > $streamfile_full"
|
||||||
|
log_mustnot eval "zfs recv $dest -x volblock < $streamfile_full"
|
||||||
|
log_mustnot eval "zfs recv $dest -o volblock=32K < $streamfile_full"
|
||||||
|
# Cleanup
|
||||||
|
block_device_wait
|
||||||
|
log_must_busy zfs destroy -r -f $orig
|
||||||
|
|
||||||
|
#
|
||||||
|
# 3.2 Verify -o property=value works on streams without properties.
|
||||||
|
#
|
||||||
|
# Create a full send stream
|
||||||
|
log_must zfs create $orig
|
||||||
|
log_must zfs snapshot $orig@snap1
|
||||||
|
log_must eval "zfs send $orig@snap1 > $streamfile_full"
|
||||||
|
# Receive the full stream, override some properties
|
||||||
|
log_must eval "zfs recv -o compress=on -o '$userprop:dest'='$userval' "\
|
||||||
|
"$dest < $streamfile_full"
|
||||||
|
log_must eval "check_prop_source $dest compression on local"
|
||||||
|
log_must eval "check_prop_source $dest '$userprop:dest' '$userval' local"
|
||||||
|
# Cleanup
|
||||||
|
log_must zfs destroy -r -f $orig
|
||||||
|
log_must zfs destroy -r -f $dest
|
||||||
|
|
||||||
|
#
|
||||||
|
# 3.3 Verify -o property=value and -x work
|
||||||
|
# for an incremental replication send stream.
|
||||||
|
#
|
||||||
|
# Create a dataset tree and receive it
|
||||||
|
log_must zfs create $orig
|
||||||
|
log_must zfs create $origsub
|
||||||
|
log_must zfs snapshot -r $orig@snap1
|
||||||
|
log_must eval "zfs send -R $orig@snap1 > $streamfile_repl"
|
||||||
|
log_must eval "zfs recv $dest < $streamfile_repl"
|
||||||
|
# Fill the datasets with properties and create an incremental replication stream
|
||||||
|
log_must zfs snapshot -r $orig@snap2
|
||||||
|
log_must zfs snapshot -r $orig@snap3
|
||||||
|
log_must eval "zfs set copies=2 $orig"
|
||||||
|
log_must eval "zfs set dnsize=4k $orig"
|
||||||
|
log_must eval "zfs set compression=gzip $origsub"
|
||||||
|
log_must eval "zfs send -R -I $orig@snap1 $orig@snap3 > $streamfile_incr"
|
||||||
|
# Sets various combination of override and exclude options
|
||||||
|
log_must eval "zfs recv -F -o atime=off -o quota=123456789 -o checksum=sha512" \
|
||||||
|
" -o dnsize=2k -x compress $dest < $streamfile_incr"
|
||||||
|
# Verify we can correctly override and exclude properties
|
||||||
|
log_must eval "check_prop_source $dest copies 2 received"
|
||||||
|
log_must eval "check_prop_source $dest atime off local"
|
||||||
|
log_must eval "check_prop_source $dest quota 123456789 local"
|
||||||
|
log_must eval "check_prop_source $dest checksum sha512 local"
|
||||||
|
log_must eval "check_prop_source $dest dnodesize 2k local"
|
||||||
|
log_must eval "check_prop_inherit $destsub copies $dest"
|
||||||
|
log_must eval "check_prop_inherit $destsub atime $dest"
|
||||||
|
log_must eval "check_prop_inherit $destsub checksum $dest"
|
||||||
|
log_must eval "check_prop_source $destsub quota 0 default"
|
||||||
|
log_must eval "check_prop_source $destsub compression off default"
|
||||||
|
# Cleanup
|
||||||
|
log_must zfs destroy -r -f $orig
|
||||||
|
log_must zfs destroy -r -f $dest
|
||||||
|
|
||||||
|
#
|
||||||
|
# 3.4 Verify '-x property' does not remove existing local properties and a
|
||||||
|
# modified sent property is received and updated to the new value but can
|
||||||
|
# still be excluded.
|
||||||
|
#
|
||||||
|
# Create a dataset tree
|
||||||
|
log_must zfs create $orig
|
||||||
|
log_must zfs create $origsub
|
||||||
|
log_must zfs snapshot -r $orig@snap1
|
||||||
|
log_must eval "zfs set copies=2 $orig"
|
||||||
|
log_must eval "zfs send -R $orig@snap1 > $streamfile_repl"
|
||||||
|
log_must eval "zfs receive $dest < $streamfile_repl"
|
||||||
|
log_must eval "check_prop_source $dest copies 2 received"
|
||||||
|
log_must eval "check_prop_inherit $destsub copies $dest"
|
||||||
|
# Set new custom properties on both source and destination
|
||||||
|
log_must eval "zfs set copies=3 $orig"
|
||||||
|
log_must eval "zfs set compression=on $orig"
|
||||||
|
log_must eval "zfs set compression=lzjb $origsub"
|
||||||
|
log_must eval "zfs set compression=gzip $dest"
|
||||||
|
# Receive the new stream, verify we preserve locally set properties
|
||||||
|
log_must zfs snapshot -r $orig@snap2
|
||||||
|
log_must zfs snapshot -r $orig@snap3
|
||||||
|
log_must eval "zfs send -R -I $orig@snap1 $orig@snap3 > $streamfile_incr"
|
||||||
|
log_must eval "zfs recv -F -x copies -x compress $dest < $streamfile_incr"
|
||||||
|
log_must eval "check_prop_source $dest copies 1 default"
|
||||||
|
log_must eval "check_prop_received $dest copies 3"
|
||||||
|
log_must eval "check_prop_source $destsub copies 1 default"
|
||||||
|
log_must eval "check_prop_received $destsub copies '-'"
|
||||||
|
log_must eval "check_prop_source $dest compression gzip local"
|
||||||
|
log_must eval "check_prop_inherit $destsub compression $dest"
|
||||||
|
# Cleanup
|
||||||
|
log_must zfs destroy -r -f $orig
|
||||||
|
log_must zfs destroy -r -f $dest
|
||||||
|
|
||||||
|
#
|
||||||
|
# 3.6 Verify we correctly restore existing properties on a failed receive
|
||||||
|
#
|
||||||
|
# Receive a "clean" dataset tree
|
||||||
|
log_must zfs create $orig
|
||||||
|
log_must zfs create $origsub
|
||||||
|
log_must zfs snapshot -r $orig@snap1
|
||||||
|
log_must eval "zfs send -R $orig@snap1 > $streamfile_repl"
|
||||||
|
log_must eval "zfs receive $dest < $streamfile_repl"
|
||||||
|
# Set custom properties on the destination
|
||||||
|
log_must eval "zfs set compress=on $dest"
|
||||||
|
log_must eval "zfs set compress=lzjb $destsub"
|
||||||
|
# Create a truncated incremental replication stream
|
||||||
|
mntpnt=$(get_prop mountpoint $orig)
|
||||||
|
log_must eval "dd if=/dev/urandom of=$mntpnt/file bs=1024k count=10"
|
||||||
|
log_must zfs snapshot -r $orig@snap2
|
||||||
|
log_must zfs snapshot -r $orig@snap3
|
||||||
|
log_must eval "zfs send -R -I $orig@snap1 $orig@snap3 > $streamfile_incr"
|
||||||
|
log_must eval "dd if=$streamfile_incr of=$streamfile_trun bs=1024k count=9"
|
||||||
|
# Receive the truncated stream, verify original properties are kept
|
||||||
|
log_mustnot eval "zfs recv -F -o copies=3 -o compress=gzip "\
|
||||||
|
"$dest < $streamfile_trun"
|
||||||
|
log_must eval "check_prop_source $dest copies 1 default"
|
||||||
|
log_must eval "check_prop_source $destsub copies 1 default"
|
||||||
|
log_must eval "check_prop_source $dest compression on local"
|
||||||
|
log_must eval "check_prop_source $destsub compression lzjb local"
|
||||||
|
# Cleanup
|
||||||
|
log_must zfs destroy -r -f $orig
|
||||||
|
log_must zfs destroy -r -f $dest
|
||||||
|
|
||||||
|
#
|
||||||
|
# 3.7 Verify that we can't get around checking a property is readonly
|
||||||
|
# by using the alias or receiving a parent replication stream.
|
||||||
|
log_must zfs create $orig
|
||||||
|
log_must zfs create -V 128K -s $origsub
|
||||||
|
log_must zfs snapshot -r $orig@snap1
|
||||||
|
log_must eval "zfs send -R $orig@snap1 > $streamfile_repl"
|
||||||
|
log_mustnot eval "zfs receive -o volblock=64k $dest < $streamfile_repl"
|
||||||
|
# Cleanup
|
||||||
|
block_device_wait
|
||||||
|
log_must_busy zfs destroy -r -f $orig
|
||||||
|
|
||||||
|
log_pass "ZFS receive property alias override and exclude options passed."
|
Loading…
Reference in New Issue