mirror of
https://github.com/freebsd/freebsd-src.git
synced 2026-10-28 11:25:21 +01:00
libzfs: don't abort receiving a raw encrypted send that carries holds
A raw send that gathers holds but not properties (zfs send -w -h, without -p or -R) produces a compound stream whose per-filesystem nvlist has no "props" entry. recv_fix_encryption_hierarchy() looked that entry up with fnvlist_lookup_nvlist(), which aborts the process when the key is missing, so receiving such a stream of an encrypted dataset crashed with a failed VERIFY0() after the data had already been received. Release builds abort the same way, since the VERIFY family is always compiled in. Look "props" up tolerantly and skip the keylocation fixup when it is absent: a holds-only raw send sends no properties, and the raw receive has already established a usable keylocation. The FORCE_NEW_KEY and FORCE_INHERIT fixups, which do not need properties, still run. A new rsend test sends an encrypted dataset with -w -h, both a full and an incremental stream, and confirms the receive succeeds with the holds recreated. Reviewed-by: Brian Behlendorf <[email protected]> Reviewed-by: Alexander Motin <[email protected]> Signed-off-by: MorganaFuture <[email protected]> Closes #10787 Closes #18793
This commit is contained in:
1 parent
30426217ba
commit
9a02e53ad8
4 files changed
+129
-18
No files matched your search
@@ -3462,7 +3462,15 @@ recv_fix_encryption_hierarchy(libzfs_handle_t *hdl, const char *top_zfs,
|
||||
if (stream_nvfs == NULL)
|
||||
continue;
|
||||
|
||||
stream_props = fnvlist_lookup_nvlist(stream_nvfs, "props");
|
||||
/*
|
||||
* A "props" nvlist is only present when the send gathered
|
||||
* properties (-R, -p or -b). A raw send carrying only holds
|
||||
* (zfs send -w -h) has none, so tolerate its absence rather
|
||||
* than asserting.
|
||||
*/
|
||||
stream_props = NULL;
|
||||
(void) nvlist_lookup_nvlist(stream_nvfs, "props",
|
||||
&stream_props);
|
||||
stream_encroot = nvlist_exists(stream_nvfs, "is_encroot");
|
||||
|
||||
/*
|
||||
@@ -3480,30 +3488,41 @@ recv_fix_encryption_hierarchy(libzfs_handle_t *hdl, const char *top_zfs,
|
||||
}
|
||||
}
|
||||
|
||||
stream_keylocation = fnvlist_lookup_string(stream_props,
|
||||
zfs_prop_to_name(ZFS_PROP_KEYLOCATION));
|
||||
|
||||
/*
|
||||
* Refresh the properties in case the call to
|
||||
* lzc_change_key() changed the value.
|
||||
* Restore the keylocation only if the stream carried
|
||||
* properties. A holds-only raw send has none, and the
|
||||
* raw receive has already established a usable
|
||||
* keylocation, so there is nothing to fix up.
|
||||
*/
|
||||
zfs_refresh_properties(zhp);
|
||||
err = zfs_prop_get(zhp, ZFS_PROP_KEYLOCATION,
|
||||
keylocation, sizeof (keylocation), NULL, NULL,
|
||||
0, B_TRUE);
|
||||
if (err != 0) {
|
||||
zfs_close(zhp);
|
||||
goto error;
|
||||
}
|
||||
if (stream_props != NULL) {
|
||||
stream_keylocation = fnvlist_lookup_string(
|
||||
stream_props,
|
||||
zfs_prop_to_name(ZFS_PROP_KEYLOCATION));
|
||||
|
||||
if (strcmp(keylocation, stream_keylocation) != 0) {
|
||||
err = zfs_prop_set(zhp,
|
||||
zfs_prop_to_name(ZFS_PROP_KEYLOCATION),
|
||||
stream_keylocation);
|
||||
/*
|
||||
* Refresh the properties in case the call to
|
||||
* lzc_change_key() changed the value.
|
||||
*/
|
||||
zfs_refresh_properties(zhp);
|
||||
err = zfs_prop_get(zhp, ZFS_PROP_KEYLOCATION,
|
||||
keylocation, sizeof (keylocation), NULL,
|
||||
NULL, 0, B_TRUE);
|
||||
if (err != 0) {
|
||||
zfs_close(zhp);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (strcmp(keylocation, stream_keylocation)
|
||||
!= 0) {
|
||||
err = zfs_prop_set(zhp,
|
||||
zfs_prop_to_name(
|
||||
ZFS_PROP_KEYLOCATION),
|
||||
stream_keylocation);
|
||||
if (err != 0) {
|
||||
zfs_close(zhp);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1009,6 +1009,7 @@ tests = ['recv_dedup', 'recv_dedup_encrypted_zvol', 'rsend_001_pos',
|
||||
'send-c_embedded_blocks', 'send-c_resume', 'send-cpL_varied_recsize',
|
||||
'send-c_recv_dedup', 'send-L_toggle', 'send_encrypted_incremental',
|
||||
'send_encrypted_freeobjects', 'send_encrypted_hierarchy',
|
||||
'send_encrypted_holds',
|
||||
'send_encrypted_props', 'send_encrypted_truncated_files',
|
||||
'send_freeobjects', 'send_realloc_files', 'send_realloc_encrypted_files',
|
||||
'send_realloc_dnode_nblkptr',
|
||||
|
||||
@@ -2156,6 +2156,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \
|
||||
functional/rsend/send_encrypted_files.ksh \
|
||||
functional/rsend/send_encrypted_freeobjects.ksh \
|
||||
functional/rsend/send_encrypted_hierarchy.ksh \
|
||||
functional/rsend/send_encrypted_holds.ksh \
|
||||
functional/rsend/send_encrypted_props.ksh \
|
||||
functional/rsend/send_encrypted_truncated_files.ksh \
|
||||
functional/rsend/send_freeobjects.ksh \
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
#!/bin/ksh -p
|
||||
# SPDX-License-Identifier: CDDL-1.0
|
||||
#
|
||||
# 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) 2026 by Andrew Mochalskyi. All rights reserved.
|
||||
#
|
||||
|
||||
. $STF_SUITE/tests/functional/rsend/rsend.kshlib
|
||||
|
||||
#
|
||||
# DESCRIPTION:
|
||||
# A raw send of an encrypted dataset that carries snapshot holds but no
|
||||
# properties (zfs send -w -h, without -p/-R) must be received without
|
||||
# aborting. Such a stream has no "props" nvlist, and the receive-side
|
||||
# encryption-root fixup used to dereference it unconditionally and abort
|
||||
# (https://github.com/openzfs/zfs/issues/10787).
|
||||
#
|
||||
# STRATEGY:
|
||||
# 1. Create an encrypted dataset with some data and a held snapshot.
|
||||
# 2. Raw send it with holds, receive it, and confirm the receive
|
||||
# succeeds and the hold is recreated.
|
||||
# 3. Repeat for an incremental raw send with holds.
|
||||
#
|
||||
|
||||
verify_runnable "both"
|
||||
|
||||
function cleanup
|
||||
{
|
||||
eval "zfs holds -H $snap 2>/dev/null | grep -q hold1" &&
|
||||
log_must zfs release hold1 $snap
|
||||
eval "zfs holds -H $snap2 2>/dev/null | grep -q hold2" &&
|
||||
log_must zfs release hold2 $snap2
|
||||
eval "zfs holds -H $recvsnap 2>/dev/null | grep -q hold1" &&
|
||||
log_must zfs release hold1 $recvsnap
|
||||
eval "zfs holds -H $recvsnap2 2>/dev/null | grep -q hold2" &&
|
||||
log_must zfs release hold2 $recvsnap2
|
||||
datasetexists $TESTPOOL/recv && destroy_dataset $TESTPOOL/recv "-r"
|
||||
datasetexists $TESTPOOL/crypt && destroy_dataset $TESTPOOL/crypt "-r"
|
||||
[[ -f $keyfile ]] && log_must rm -f $keyfile
|
||||
[[ -f $sendfile ]] && log_must rm -f $sendfile
|
||||
}
|
||||
log_onexit cleanup
|
||||
|
||||
log_assert "Raw send of an encrypted dataset with holds is received cleanly"
|
||||
|
||||
typeset keyfile=/$TESTPOOL/pkey.holds
|
||||
typeset sendfile=$TEST_BASE_DIR/send_encrypted_holds.$$
|
||||
typeset snap=$TESTPOOL/crypt@snap1
|
||||
typeset snap2=$TESTPOOL/crypt@snap2
|
||||
typeset recvsnap=$TESTPOOL/recv@snap1
|
||||
typeset recvsnap2=$TESTPOOL/recv@snap2
|
||||
|
||||
log_must eval "echo 'password' > $keyfile"
|
||||
|
||||
log_must zfs create -o encryption=on -o keyformat=passphrase \
|
||||
-o keylocation=file://$keyfile $TESTPOOL/crypt
|
||||
log_must mkfile 1m /$TESTPOOL/crypt/file1
|
||||
log_must zfs snapshot $snap
|
||||
log_must zfs hold hold1 $snap
|
||||
|
||||
log_note "Full raw send with holds is received without aborting"
|
||||
log_must eval "zfs send -w -h $snap > $sendfile"
|
||||
log_must eval "zfs receive $TESTPOOL/recv < $sendfile"
|
||||
log_must datasetexists $recvsnap
|
||||
log_must eval "zfs holds -H $recvsnap | grep -q hold1"
|
||||
|
||||
log_note "Incremental raw send with holds is received without aborting"
|
||||
log_must mkfile 1m /$TESTPOOL/crypt/file2
|
||||
log_must zfs snapshot $snap2
|
||||
log_must zfs hold hold2 $snap2
|
||||
log_must eval "zfs send -w -h -i $snap $snap2 > $sendfile"
|
||||
log_must eval "zfs receive $TESTPOOL/recv < $sendfile"
|
||||
log_must datasetexists $recvsnap2
|
||||
log_must eval "zfs holds -H $recvsnap2 | grep -q hold2"
|
||||
|
||||
log_pass "Raw send of an encrypted dataset with holds is received cleanly"
|
||||
Reference in new issue
Block a user