mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-22 02:27:36 +03:00
Detect and prevent mixed raw and non-raw sends
Currently, there is an issue in the raw receive code where raw receives are allowed to happen on top of previously non-raw received datasets. This is a problem because the source-side dataset doesn't know about how the blocks on the destination were encrypted. As a result, any MAC in the objset's checksum-of-MACs tree that is a parent of both blocks encrypted on the source and blocks encrypted by the destination will be incorrect. This will result in authentication errors when we decrypt the dataset. This patch fixes this issue by adding a new check to the raw receive code. The code now maintains an "IVset guid", which acts as an identifier for the set of IVs used to encrypt a given snapshot. When a snapshot is raw received, the destination snapshot will take this value from the DRR_BEGIN payload. Non-raw receives and normal "zfs snap" operations will cause ZFS to generate a new IVset guid. When a raw incremental stream is received, ZFS will check that the "from" IVset guid in the stream matches that of the "from" destination snapshot. If they do not match, the code will error out the receive, preventing the problem. This patch requires an on-disk format change to add the IVset guids to snapshots and bookmarks. As a result, this patch has errata handling and a tunable to help affected users resolve the issue with as little interruption as possible. Reviewed-by: Paul Dagnelie <pcd@delphix.com> Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Reviewed-by: Matt Ahrens <mahrens@delphix.com> Signed-off-by: Tom Caputi <tcaputi@datto.com> Closes #8308
This commit is contained in:
committed by
Brian Behlendorf
parent
579ce7c5ae
commit
f00ab3f22c
@@ -378,7 +378,7 @@ tests = ['zpool_import_001_pos', 'zpool_import_002_pos',
|
||||
'zpool_import_missing_002_pos', 'zpool_import_missing_003_pos',
|
||||
'zpool_import_rename_001_pos', 'zpool_import_all_001_pos',
|
||||
'zpool_import_encrypted', 'zpool_import_encrypted_load',
|
||||
'zpool_import_errata3',
|
||||
'zpool_import_errata3', 'zpool_import_errata4',
|
||||
'import_cachefile_device_added',
|
||||
'import_cachefile_device_removed',
|
||||
'import_cachefile_device_replaced',
|
||||
@@ -793,7 +793,8 @@ tests = ['rsend_001_pos', 'rsend_002_pos', 'rsend_003_pos', 'rsend_004_pos',
|
||||
'send-c_embedded_blocks', 'send-c_resume', 'send-cpL_varied_recsize',
|
||||
'send-c_recv_dedup', 'send_encrypted_files', 'send_encrypted_heirarchy',
|
||||
'send_encrypted_props', 'send_freeobjects', 'send_realloc_dnode_size',
|
||||
'send_holds', 'send_hole_birth', 'send-wDR_encrypted_zvol']
|
||||
'send_holds', 'send_hole_birth', 'send_mixed_raw',
|
||||
'send-wDR_encrypted_zvol']
|
||||
tags = ['functional', 'rsend']
|
||||
|
||||
[tests/functional/scrub_mirror]
|
||||
|
||||
@@ -88,5 +88,6 @@ if is_linux; then
|
||||
"feature@project_quota"
|
||||
"feature@allocation_classes"
|
||||
"feature@resilver_defer"
|
||||
"feature@bookmark_v2"
|
||||
)
|
||||
fi
|
||||
|
||||
@@ -39,7 +39,8 @@ dist_pkgdata_SCRIPTS = \
|
||||
zpool_import_rename_001_pos.ksh \
|
||||
zpool_import_encrypted.ksh \
|
||||
zpool_import_encrypted_load.ksh \
|
||||
zpool_import_errata3.ksh
|
||||
zpool_import_errata3.ksh \
|
||||
zpool_import_errata4.ksh
|
||||
|
||||
dist_pkgdata_DATA = \
|
||||
zpool_import.cfg \
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
pkgdatadir = $(datadir)/@PACKAGE@/zfs-tests/tests/functional/cli_root/zpool_import/blockfiles
|
||||
dist_pkgdata_DATA = \
|
||||
unclean_export.dat.bz2 \
|
||||
cryptv0.dat.bz2
|
||||
cryptv0.dat.bz2 \
|
||||
missing_ivset.dat.bz2
|
||||
|
||||
BIN
Binary file not shown.
@@ -62,7 +62,7 @@ log_assert "Verify that Errata 3 is properly handled"
|
||||
|
||||
uncompress_pool
|
||||
log_must zpool import -d /$TESTPOOL/ $POOL_NAME
|
||||
log_must eval "zpool status $POOL_NAME | grep -q Errata"
|
||||
log_must eval "zpool status $POOL_NAME | grep -q Errata" # also detects 'Errata #4'
|
||||
log_must eval "zpool status $POOL_NAME | grep -q ZFS-8000-ER"
|
||||
log_must eval "echo 'password' | zfs load-key $POOL_NAME/testfs"
|
||||
log_must eval "echo 'password' | zfs load-key $POOL_NAME/testvol"
|
||||
@@ -96,6 +96,5 @@ log_must zfs destroy -r $POOL_NAME/testvol
|
||||
|
||||
log_must zpool export $POOL_NAME
|
||||
log_must zpool import -d /$TESTPOOL/ $POOL_NAME
|
||||
log_mustnot eval "zpool status $POOL_NAME | grep -q Errata"
|
||||
log_mustnot eval "zpool status $POOL_NAME | grep -q ZFS-8000-ER"
|
||||
log_mustnot eval "zpool status $POOL_NAME | grep -q 'Errata #3'"
|
||||
log_pass "Errata 3 is properly handled"
|
||||
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
#!/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 (c) 2019 Datto, Inc. All rights reserved.
|
||||
#
|
||||
|
||||
. $STF_SUITE/include/libtest.shlib
|
||||
|
||||
#
|
||||
# DESCRIPTION:
|
||||
# 'zpool import' should import a pool with Errata #4. Users should be
|
||||
# able to set the zfs_disable_ivset_guid_check to continue normal
|
||||
# operation and the errata should disappear when no more effected
|
||||
# datasets remain.
|
||||
#
|
||||
# STRATEGY:
|
||||
# 1. Import a pre-packaged pool with Errata #4 and verify its state
|
||||
# 2. Prepare pool to fix existing datasets
|
||||
# 3. Use raw sends to fix datasets
|
||||
# 4. Ensure fixed datasets match their initial counterparts
|
||||
# 5. Destroy the initial datasets and verify the errata is gone
|
||||
#
|
||||
|
||||
verify_runnable "global"
|
||||
|
||||
POOL_NAME=missing_ivset
|
||||
POOL_FILE=missing_ivset.dat
|
||||
|
||||
function uncompress_pool
|
||||
{
|
||||
log_note "Creating pool from $POOL_FILE"
|
||||
log_must bzcat \
|
||||
$STF_SUITE/tests/functional/cli_root/zpool_import/blockfiles/$POOL_FILE.bz2 \
|
||||
> /$TESTPOOL/$POOL_FILE
|
||||
return 0
|
||||
}
|
||||
|
||||
function cleanup
|
||||
{
|
||||
log_must set_tunable32 zfs_disable_ivset_guid_check 0
|
||||
poolexists $POOL_NAME && log_must zpool destroy $POOL_NAME
|
||||
[[ -e /$TESTPOOL/$POOL_FILE ]] && rm /$TESTPOOL/$POOL_FILE
|
||||
return 0
|
||||
}
|
||||
log_onexit cleanup
|
||||
|
||||
log_assert "Verify that Errata 4 is properly handled"
|
||||
|
||||
function has_ivset_guid # dataset
|
||||
{
|
||||
ds="$1"
|
||||
ivset_guid=$(get_prop ivsetguid $ds)
|
||||
|
||||
if [ "$ivset_guid" == "-" ]; then
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
# 1. Import a pre-packaged pool with Errata #4 and verify its state
|
||||
uncompress_pool
|
||||
log_must zpool import -d /$TESTPOOL/ $POOL_NAME
|
||||
log_must eval "zpool status $POOL_NAME | grep -q 'Errata #4'"
|
||||
log_must eval "zpool status $POOL_NAME | grep -q ZFS-8000-ER"
|
||||
bm2_value=$(zpool get -H -o value feature@bookmark_v2 $POOL_NAME)
|
||||
if [ "$bm2_value" != "disabled" ]; then
|
||||
log_fail "initial pool's bookmark_v2 feature is not disabled"
|
||||
fi
|
||||
|
||||
log_mustnot has_ivset_guid $POOL_NAME/testfs@snap1
|
||||
log_mustnot has_ivset_guid $POOL_NAME/testfs@snap2
|
||||
log_mustnot has_ivset_guid $POOL_NAME/testfs@snap3
|
||||
log_mustnot has_ivset_guid $POOL_NAME/testvol@snap1
|
||||
log_mustnot has_ivset_guid $POOL_NAME/testvol@snap2
|
||||
log_mustnot has_ivset_guid $POOL_NAME/testvol@snap3
|
||||
|
||||
# 2. Prepare pool to fix existing datasets
|
||||
log_must zpool set feature@bookmark_v2=enabled $POOL_NAME
|
||||
log_must set_tunable32 zfs_disable_ivset_guid_check 1
|
||||
log_must zfs create $POOL_NAME/fixed
|
||||
|
||||
# 3. Use raw sends to fix datasets
|
||||
log_must eval "zfs send -w $POOL_NAME/testfs@snap1 | \
|
||||
zfs recv $POOL_NAME/fixed/testfs"
|
||||
log_must eval "zfs send -w -i @snap1 $POOL_NAME/testfs@snap2 | \
|
||||
zfs recv $POOL_NAME/fixed/testfs"
|
||||
log_must eval \
|
||||
"zfs send -w -i $POOL_NAME/testfs#snap2 $POOL_NAME/testfs@snap3 | \
|
||||
zfs recv $POOL_NAME/fixed/testfs"
|
||||
|
||||
log_must eval "zfs send -w $POOL_NAME/testvol@snap1 | \
|
||||
zfs recv $POOL_NAME/fixed/testvol"
|
||||
log_must eval "zfs send -w -i @snap1 $POOL_NAME/testvol@snap2 | \
|
||||
zfs recv $POOL_NAME/fixed/testvol"
|
||||
log_must eval \
|
||||
"zfs send -w -i $POOL_NAME/testvol#snap2 $POOL_NAME/testvol@snap3 | \
|
||||
zfs recv $POOL_NAME/fixed/testvol"
|
||||
|
||||
# 4. Ensure fixed datasets match their initial counterparts
|
||||
log_must eval "echo 'password' | zfs load-key $POOL_NAME/testfs"
|
||||
log_must eval "echo 'password' | zfs load-key $POOL_NAME/testvol"
|
||||
log_must eval "echo 'password' | zfs load-key $POOL_NAME/fixed/testfs"
|
||||
log_must eval "echo 'password' | zfs load-key $POOL_NAME/fixed/testvol"
|
||||
log_must zfs mount $POOL_NAME/testfs
|
||||
log_must zfs mount $POOL_NAME/fixed/testfs
|
||||
block_device_wait
|
||||
|
||||
old_mntpnt=$(get_prop mountpoint $POOL_NAME/testfs)
|
||||
new_mntpnt=$(get_prop mountpoint $POOL_NAME/fixed/testfs)
|
||||
log_must diff -r "$old_mntpnt" "$new_mntpnt"
|
||||
log_must diff /dev/zvol/$POOL_NAME/testvol /dev/zvol/$POOL_NAME/fixed/testvol
|
||||
|
||||
log_must has_ivset_guid $POOL_NAME/fixed/testfs@snap1
|
||||
log_must has_ivset_guid $POOL_NAME/fixed/testfs@snap2
|
||||
log_must has_ivset_guid $POOL_NAME/fixed/testfs@snap3
|
||||
log_must has_ivset_guid $POOL_NAME/fixed/testvol@snap1
|
||||
log_must has_ivset_guid $POOL_NAME/fixed/testvol@snap2
|
||||
log_must has_ivset_guid $POOL_NAME/fixed/testvol@snap3
|
||||
|
||||
# 5. Destroy the initial datasets and verify the errata is gone
|
||||
log_must zfs destroy -r $POOL_NAME/testfs
|
||||
log_must zfs destroy -r $POOL_NAME/testvol
|
||||
|
||||
log_must zpool export $POOL_NAME
|
||||
log_must zpool import -d /$TESTPOOL/ $POOL_NAME
|
||||
log_mustnot eval "zpool status $POOL_NAME | grep -q 'Errata #4'"
|
||||
log_mustnot eval "zpool status $POOL_NAME | grep -q ZFS-8000-ER"
|
||||
log_pass "Errata 4 is properly handled"
|
||||
@@ -43,6 +43,7 @@ dist_pkgdata_SCRIPTS = \
|
||||
send_realloc_dnode_size.ksh \
|
||||
send_holds.ksh \
|
||||
send_hole_birth.ksh \
|
||||
send_mixed_raw.ksh \
|
||||
send-wDR_encrypted_zvol.ksh
|
||||
|
||||
dist_pkgdata_DATA = \
|
||||
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
#!/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 (c) 2019 Datto, Inc. All rights reserved.
|
||||
#
|
||||
|
||||
. $STF_SUITE/include/libtest.shlib
|
||||
|
||||
#
|
||||
# DESCRIPTION:
|
||||
# Verify that 'zfs receive' produces an error when mixing
|
||||
# raw and non-raw sends in a way that would break IV set
|
||||
# consistency.
|
||||
#
|
||||
# STRATEGY:
|
||||
# 1. Create an initial dataset with 3 snapshots.
|
||||
# 2. Perform a raw send of the first snapshot to 2 other datasets.
|
||||
# 3. Perform a non-raw send of the second snapshot to one of
|
||||
# the other datasets. Perform a raw send from this dataset to
|
||||
# the last one.
|
||||
# 4. Attempt to raw send the final snapshot of the first dataset
|
||||
# to the other 2 datasets, which should fail.
|
||||
# 5. Repeat steps 1-4, but using bookmarks for incremental sends.
|
||||
#
|
||||
#
|
||||
# A B C notes
|
||||
# ------------------------------------------------------------------------------
|
||||
# snap1 ---raw---> snap1 --raw--> snap1 # all snaps initialized via raw send
|
||||
# snap2 -non-raw-> snap2 --raw--> snap2 # A sends non-raw to B, B sends raw to C
|
||||
# snap3 ------------raw---------> snap3 # attempt send to C (should fail)
|
||||
#
|
||||
|
||||
|
||||
verify_runnable "both"
|
||||
|
||||
function cleanup
|
||||
{
|
||||
datasetexists $TESTPOOL/$TESTFS3 && \
|
||||
log_must zfs destroy -r $TESTPOOL/$TESTFS3
|
||||
datasetexists $TESTPOOL/$TESTFS2 && \
|
||||
log_must zfs destroy -r $TESTPOOL/$TESTFS2
|
||||
datasetexists $TESTPOOL/$TESTFS1 && \
|
||||
log_must zfs destroy -r $TESTPOOL/$TESTFS1
|
||||
}
|
||||
log_onexit cleanup
|
||||
|
||||
log_assert "Mixing raw and non-raw receives should fail"
|
||||
|
||||
typeset passphrase="password"
|
||||
|
||||
log_must eval "echo $passphrase | zfs create -o encryption=on" \
|
||||
"-o keyformat=passphrase $TESTPOOL/$TESTFS1"
|
||||
|
||||
log_must zfs snapshot $TESTPOOL/$TESTFS1@1
|
||||
log_must touch /$TESTPOOL/$TESTFS1/a
|
||||
log_must zfs snapshot $TESTPOOL/$TESTFS1@2
|
||||
log_must touch /$TESTPOOL/$TESTFS1/b
|
||||
log_must zfs snapshot $TESTPOOL/$TESTFS1@3
|
||||
|
||||
# Testing with snapshots
|
||||
log_must eval "zfs send -w $TESTPOOL/$TESTFS1@1 |" \
|
||||
"zfs receive $TESTPOOL/$TESTFS2"
|
||||
log_must eval "echo $passphrase | zfs load-key $TESTPOOL/$TESTFS2"
|
||||
log_must eval "zfs send -w $TESTPOOL/$TESTFS2@1 |" \
|
||||
"zfs receive $TESTPOOL/$TESTFS3"
|
||||
log_must eval "echo $passphrase | zfs load-key $TESTPOOL/$TESTFS3"
|
||||
|
||||
log_must eval "zfs send -i $TESTPOOL/$TESTFS1@1 $TESTPOOL/$TESTFS1@2 |" \
|
||||
"zfs receive $TESTPOOL/$TESTFS2"
|
||||
log_must eval "zfs send -w -i $TESTPOOL/$TESTFS2@1 $TESTPOOL/$TESTFS2@2 |" \
|
||||
"zfs receive $TESTPOOL/$TESTFS3"
|
||||
|
||||
log_mustnot eval "zfs send -w -i $TESTPOOL/$TESTFS1@2 $TESTPOOL/$TESTFS1@3 |" \
|
||||
"zfs receive $TESTPOOL/$TESTFS2"
|
||||
log_mustnot eval "zfs send -w -i $TESTPOOL/$TESTFS2@2 $TESTPOOL/$TESTFS2@3 |" \
|
||||
"zfs receive $TESTPOOL/$TESTFS3"
|
||||
|
||||
log_must zfs destroy -r $TESTPOOL/$TESTFS3
|
||||
log_must zfs destroy -r $TESTPOOL/$TESTFS2
|
||||
|
||||
# Testing with bookmarks
|
||||
log_must zfs bookmark $TESTPOOL/$TESTFS1@1 $TESTPOOL/$TESTFS1#b1
|
||||
log_must zfs bookmark $TESTPOOL/$TESTFS1@2 $TESTPOOL/$TESTFS1#b2
|
||||
|
||||
log_must eval "zfs send -w $TESTPOOL/$TESTFS1@1 |" \
|
||||
"zfs receive $TESTPOOL/$TESTFS2"
|
||||
log_must eval "echo $passphrase | zfs load-key $TESTPOOL/$TESTFS2"
|
||||
|
||||
log_must zfs bookmark $TESTPOOL/$TESTFS2@1 $TESTPOOL/$TESTFS2#b1
|
||||
|
||||
log_must eval "zfs send -w $TESTPOOL/$TESTFS2@1 |" \
|
||||
"zfs receive $TESTPOOL/$TESTFS3"
|
||||
log_must eval "echo $passphrase | zfs load-key $TESTPOOL/$TESTFS3"
|
||||
|
||||
log_must eval "zfs send -i $TESTPOOL/$TESTFS1#b1 $TESTPOOL/$TESTFS1@2 |" \
|
||||
"zfs receive $TESTPOOL/$TESTFS2"
|
||||
log_must eval "zfs send -w -i $TESTPOOL/$TESTFS2#b1 $TESTPOOL/$TESTFS2@2 |" \
|
||||
"zfs receive $TESTPOOL/$TESTFS3"
|
||||
|
||||
log_mustnot eval "zfs send -w -i $TESTPOOL/$TESTFS1#b2" \
|
||||
"$TESTPOOL/$TESTFS1@3 | zfs receive $TESTPOOL/$TESTFS2"
|
||||
log_mustnot eval "zfs send -w -i $TESTPOOL/$TESTFS2#b2" \
|
||||
"$TESTPOOL/$TESTFS2@3 | zfs receive $TESTPOOL/$TESTFS3"
|
||||
|
||||
log_pass "Mixing raw and non-raw receives fail as expected"
|
||||
Reference in New Issue
Block a user