mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-22 02:27:36 +03:00
Allow block cloning across encrypted datasets
When two datasets share the same master encryption key, it is safe to clone encrypted blocks. Currently only snapshots and clones of a dataset share with it the same encryption key. Added a test for: - Clone from encrypted sibling to encrypted sibling with non encrypted parent - Clone from encrypted parent to inherited encrypted child - Clone from child to sibling with encrypted parent - Clone from snapshot to the original datasets - Clone from foreign snapshot to a foreign dataset - Cloning from non-encrypted to encrypted datasets - Cloning from encrypted to non-encrypted datasets Reviewed-by: Alexander Motin <mav@FreeBSD.org> Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Original-patch-by: Pawel Jakub Dawidek <pawel@dawidek.net> Signed-off-by: Kay Pedersen <mail@mkwg.de> Closes #15544
This commit is contained in:
@@ -451,6 +451,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \
|
||||
functional/block_cloning/block_cloning_ficlone.ksh \
|
||||
functional/block_cloning/block_cloning_ficlonerange.ksh \
|
||||
functional/block_cloning/block_cloning_ficlonerange_partial.ksh \
|
||||
functional/block_cloning/block_cloning_cross_enc_dataset.ksh \
|
||||
functional/bootfs/bootfs_001_pos.ksh \
|
||||
functional/bootfs/bootfs_002_neg.ksh \
|
||||
functional/bootfs/bootfs_003_pos.ksh \
|
||||
|
||||
@@ -28,8 +28,8 @@
|
||||
|
||||
function have_same_content
|
||||
{
|
||||
typeset hash1=$(cat $1 | md5sum)
|
||||
typeset hash2=$(cat $2 | md5sum)
|
||||
typeset hash1=$(md5digest $1)
|
||||
typeset hash2=$(md5digest $2)
|
||||
|
||||
log_must [ "$hash1" = "$hash2" ]
|
||||
}
|
||||
@@ -44,10 +44,14 @@ function have_same_content
|
||||
#
|
||||
function get_same_blocks
|
||||
{
|
||||
KEY=$5
|
||||
if [ ${#KEY} -gt 0 ]; then
|
||||
KEY="--key=$KEY"
|
||||
fi
|
||||
typeset zdbout=${TMPDIR:-$TEST_BASE_DIR}/zdbout.$$
|
||||
zdb -vvvvv $1 -O $2 | \
|
||||
zdb $KEY -vvvvv $1 -O $2 | \
|
||||
awk '/ L0 / { print l++ " " $3 " " $7 }' > $zdbout.a
|
||||
zdb -vvvvv $3 -O $4 | \
|
||||
zdb $KEY -vvvvv $3 -O $4 | \
|
||||
awk '/ L0 / { print l++ " " $3 " " $7 }' > $zdbout.b
|
||||
echo $(sort $zdbout.a $zdbout.b | uniq -d | cut -f1 -d' ')
|
||||
}
|
||||
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
#!/bin/ksh -p
|
||||
#
|
||||
# CDDL HEADER START
|
||||
#
|
||||
# The contents of this file are subject to the terms of the
|
||||
# Common Development and Distribution License (the "License").
|
||||
# You may not use this file except in compliance with the License.
|
||||
#
|
||||
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
# or https://opensource.org/licenses/CDDL-1.0.
|
||||
# See the License for the specific language governing permissions
|
||||
# and limitations under the License.
|
||||
#
|
||||
# When distributing Covered Code, include this CDDL HEADER in each
|
||||
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
# If applicable, add the following below this CDDL HEADER, with the
|
||||
# fields enclosed by brackets "[]" replaced with your own identifying
|
||||
# information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
#
|
||||
# CDDL HEADER END
|
||||
#
|
||||
|
||||
#
|
||||
# Copyright (c) 2023, Kay Pedersen <mail@mkwg.de>
|
||||
#
|
||||
|
||||
. $STF_SUITE/include/libtest.shlib
|
||||
. $STF_SUITE/tests/functional/block_cloning/block_cloning.kshlib
|
||||
|
||||
verify_runnable "global"
|
||||
|
||||
if [[ $(linux_version) -lt $(linux_version "5.3") ]]; then
|
||||
log_unsupported "copy_file_range can't copy cross-filesystem before Linux 5.3"
|
||||
fi
|
||||
|
||||
claim="Block cloning across encrypted datasets."
|
||||
|
||||
log_assert $claim
|
||||
|
||||
DS1="$TESTPOOL/encrypted1"
|
||||
DS2="$TESTPOOL/encrypted2"
|
||||
DS1_NC="$TESTPOOL/notcrypted1"
|
||||
PASSPHRASE="top_secret"
|
||||
|
||||
function prepare_enc
|
||||
{
|
||||
log_must zpool create -o feature@block_cloning=enabled $TESTPOOL $DISKS
|
||||
log_must eval "echo $PASSPHRASE | zfs create -o encryption=on" \
|
||||
"-o keyformat=passphrase -o keylocation=prompt $DS1"
|
||||
log_must eval "echo $PASSPHRASE | zfs create -o encryption=on" \
|
||||
"-o keyformat=passphrase -o keylocation=prompt $DS2"
|
||||
log_must zfs create $DS1/child1
|
||||
log_must zfs create $DS1/child2
|
||||
log_must zfs create $DS1_NC
|
||||
|
||||
log_note "Create test file"
|
||||
# we must wait until the src file txg is written to the disk otherwise we
|
||||
# will fallback to normal copy. See "dmu_read_l0_bps" in
|
||||
# "zfs/module/zfs/dmu.c" and "zfs_clone_range" in
|
||||
# "zfs/module/zfs/zfs_vnops.c"
|
||||
log_must dd if=/dev/urandom of=/$DS1/file bs=128K count=4
|
||||
log_must dd if=/dev/urandom of=/$DS1/child1/file bs=128K count=4
|
||||
log_must dd if=/dev/urandom of=/$DS1_NC/file bs=128K count=4
|
||||
log_must sync_pool $TESTPOOL
|
||||
}
|
||||
|
||||
function cleanup_enc
|
||||
{
|
||||
datasetexists $TESTPOOL && destroy_pool $TESTPOOL
|
||||
}
|
||||
|
||||
function clone_and_check
|
||||
{
|
||||
I_FILE="$1"
|
||||
O_FILE=$2
|
||||
I_DS=$3
|
||||
O_DS=$4
|
||||
SAME_BLOCKS=$5
|
||||
# the CLONE option provides a choice between copy_file_range
|
||||
# which should clone and a dd which is a copy no matter what
|
||||
CLONE=$6
|
||||
SNAPSHOT=$7
|
||||
if [ ${#SNAPSHOT} -gt 0 ]; then
|
||||
I_FILE=".zfs/snapshot/$SNAPSHOT/$1"
|
||||
fi
|
||||
if [ $CLONE ]; then
|
||||
log_must clonefile -f "/$I_DS/$I_FILE" "/$O_DS/$O_FILE" 0 0 524288
|
||||
else
|
||||
log_must dd if="/$I_DS/$I_FILE" of="/$O_DS/$O_FILE" bs=128K
|
||||
fi
|
||||
log_must sync_pool $TESTPOOL
|
||||
|
||||
log_must have_same_content "/$I_DS/$I_FILE" "/$O_DS/$O_FILE"
|
||||
|
||||
if [ ${#SNAPSHOT} -gt 0 ]; then
|
||||
I_DS="$I_DS@$SNAPSHOT"
|
||||
I_FILE="$1"
|
||||
fi
|
||||
typeset blocks=$(get_same_blocks \
|
||||
$I_DS $I_FILE $O_DS $O_FILE $PASSPHRASE)
|
||||
log_must [ "$blocks" = "$SAME_BLOCKS" ]
|
||||
}
|
||||
|
||||
log_onexit cleanup_enc
|
||||
|
||||
prepare_enc
|
||||
|
||||
log_note "Cloning entire file with copy_file_range across different enc" \
|
||||
"roots, should fallback"
|
||||
# we are expecting no same block map.
|
||||
clone_and_check "file" "clone" $DS1 $DS2 "" true
|
||||
log_note "check if the file is still readable and the same after" \
|
||||
"unmount and key unload, shouldn't fail"
|
||||
typeset hash1=$(md5digest "/$DS1/file")
|
||||
log_must zfs umount $DS1 && zfs unload-key $DS1
|
||||
typeset hash2=$(md5digest "/$DS2/clone")
|
||||
log_must [ "$hash1" = "$hash2" ]
|
||||
|
||||
cleanup_enc
|
||||
prepare_enc
|
||||
|
||||
log_note "Cloning entire file with copy_file_range across different child datasets"
|
||||
# clone shouldn't work because of deriving a new master key for the child
|
||||
# we are expecting no same block map.
|
||||
clone_and_check "file" "clone" $DS1 "$DS1/child1" "" true
|
||||
clone_and_check "file" "clone" "$DS1/child1" "$DS1/child2" "" true
|
||||
|
||||
cleanup_enc
|
||||
prepare_enc
|
||||
|
||||
log_note "Copying entire file with copy_file_range across same snapshot"
|
||||
log_must zfs snapshot -r $DS1@s1
|
||||
log_must sync_pool $TESTPOOL
|
||||
log_must rm -f "/$DS1/file"
|
||||
log_must sync_pool $TESTPOOL
|
||||
clone_and_check "file" "clone" "$DS1" "$DS1" "0 1 2 3" true "s1"
|
||||
|
||||
cleanup_enc
|
||||
prepare_enc
|
||||
|
||||
log_note "Copying entire file with copy_file_range across different snapshot"
|
||||
clone_and_check "file" "file" $DS1 $DS2 "" true
|
||||
log_must zfs snapshot -r $DS2@s1
|
||||
log_must sync_pool $TESTPOOL
|
||||
log_must rm -f "/$DS1/file" "/$DS2/file"
|
||||
log_must sync_pool $TESTPOOL
|
||||
clone_and_check "file" "clone" "$DS2" "$DS1" "" true "s1"
|
||||
typeset hash1=$(md5digest "/$DS1/.zfs/snapshot/s1/file")
|
||||
log_note "destroy the snapshot and check if the file is still readable and" \
|
||||
"has the same content"
|
||||
log_must zfs destroy -r $DS2@s1
|
||||
log_must sync_pool $TESTPOOL
|
||||
typeset hash2=$(md5digest "/$DS1/file")
|
||||
log_must [ "$hash1" = "$hash2" ]
|
||||
|
||||
cleanup_enc
|
||||
prepare_enc
|
||||
|
||||
log_note "Copying with copy_file_range from non encrypted to encrypted"
|
||||
clone_and_check "file" "copy" $DS1_NC $DS1 "" true
|
||||
|
||||
cleanup_enc
|
||||
prepare_enc
|
||||
|
||||
log_note "Copying with copy_file_range from encrypted to non encrypted"
|
||||
clone_and_check "file" "copy" $DS1 $DS1_NC "" true
|
||||
|
||||
log_must sync_pool $TESTPOOL
|
||||
|
||||
log_pass $claim
|
||||
Reference in New Issue
Block a user