From: Tom Caputi <tcaputi@datto.com>
Date: Thu, 24 Oct 2019 17:51:01 +0000 (-0400)
Subject: Fix incremental recursive encrypted receive
X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b4238327b4ec84451fd2944cee7ccff37a065d27;p=zfs

Fix incremental recursive encrypted receive

Currently, incremental recursive encrypted receives fail to work
for any snapshot after the first. The reason for this is because
the check in zfs_setup_cmdline_props() did not properly realize
that when the user attempts to use '-x encryption' in this
situation, they are not really overriding the existing encryption
property and instead are attempting to prevent it from changing.
This resulted in an error message stating: "encryption property
'encryption' cannot be set or excluded for raw or incremental
streams".

This problem is fixed by updating the logic to expect this use
case.

Reviewed-by: loli10K <ezomori.nozomu@gmail.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Igor Kozhukhov <igor@dilos.org>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #9494
---

diff --git a/lib/libzfs/libzfs_sendrecv.c b/lib/libzfs/libzfs_sendrecv.c
index 12ae8759a..20d29f48c 100644
--- a/lib/libzfs/libzfs_sendrecv.c
+++ b/lib/libzfs/libzfs_sendrecv.c
@@ -4257,11 +4257,21 @@ zfs_setup_cmdline_props(libzfs_handle_t *hdl, zfs_type_t type,
 
 		/* raw streams can't override encryption properties */
 		if ((zfs_prop_encryption_key_param(prop) ||
-		    prop == ZFS_PROP_ENCRYPTION) && (raw || !newfs)) {
+		    prop == ZFS_PROP_ENCRYPTION) && raw) {
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "encryption property '%s' cannot "
-			    "be set or excluded for raw or incremental "
-			    "streams."), name);
+			    "be set or excluded for raw streams."), name);
+			ret = zfs_error(hdl, EZFS_BADPROP, errbuf);
+			goto error;
+		}
+
+		/* incremental streams can only exclude encryption properties */
+		if ((zfs_prop_encryption_key_param(prop) ||
+		    prop == ZFS_PROP_ENCRYPTION) && !newfs &&
+		    nvpair_type(nvp) != DATA_TYPE_BOOLEAN) {
+			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+			    "encryption property '%s' cannot "
+			    "be set for incremental streams."), name);
 			ret = zfs_error(hdl, EZFS_BADPROP, errbuf);
 			goto error;
 		}
@@ -4279,10 +4289,12 @@ zfs_setup_cmdline_props(libzfs_handle_t *hdl, zfs_type_t type,
 			 */
 			if (nvlist_exists(origprops, name)) {
 				nvlist_t *attrs;
+				char *source = NULL;
 
 				attrs = fnvlist_lookup_nvlist(origprops, name);
-				if (strcmp(fnvlist_lookup_string(attrs,
-				    ZPROP_SOURCE), ZPROP_SOURCE_VAL_RECVD) != 0)
+				if (nvlist_lookup_string(attrs,
+				    ZPROP_SOURCE, &source) == 0 &&
+				    strcmp(source, ZPROP_SOURCE_VAL_RECVD) != 0)
 					continue;
 			}
 			/*
diff --git a/tests/zfs-tests/tests/functional/rsend/send_encrypted_props.ksh b/tests/zfs-tests/tests/functional/rsend/send_encrypted_props.ksh
index 4c90ba95b..8e21acd99 100755
--- a/tests/zfs-tests/tests/functional/rsend/send_encrypted_props.ksh
+++ b/tests/zfs-tests/tests/functional/rsend/send_encrypted_props.ksh
@@ -58,7 +58,8 @@ log_assert "'zfs recv' must properly handle encryption properties"
 
 typeset keyfile=/$TESTPOOL/pkey
 typeset sendfile=/$TESTPOOL/sendfile
-typeset snap=$TESTPOOL/ds@snap
+typeset snap=$TESTPOOL/ds@snap1
+typeset snap2=$TESTPOOL/ds@snap2
 typeset esnap=$TESTPOOL/crypt@snap1
 typeset esnap2=$TESTPOOL/crypt@snap2
 
@@ -78,6 +79,7 @@ log_must cp /$TESTPOOL/ds/$TESTFILE0 /$TESTPOOL/crypt/$TESTFILE0
 typeset cksum=$(md5digest /$TESTPOOL/ds/$TESTFILE0)
 
 log_must zfs snap -r $snap
+log_must zfs snap -r $snap2
 log_must zfs snap -r $esnap
 log_must zfs snap -r $esnap2
 
@@ -193,6 +195,20 @@ recv_cksum=$(md5digest /$ds/$TESTFILE0)
 log_must test "$recv_cksum" == "$cksum"
 log_must zfs destroy -r $ds
 
+# Test that we can override an unencrypted, incremental, recursive stream's
+# encryption settings, receiving all datasets as encrypted children.
+log_note "Must be able to receive recursive stream to encrypted child"
+ds=$TESTPOOL/crypt/recv
+log_must eval "zfs send -R $snap2 > $sendfile"
+log_must eval "zfs recv -x encryption $ds < $sendfile"
+log_must test "$(get_prop 'encryptionroot' $ds)" == "$TESTPOOL/crypt"
+log_must test "$(get_prop 'encryption' $ds)" == "aes-256-ccm"
+log_must test "$(get_prop 'keyformat' $ds)" == "passphrase"
+log_must test "$(get_prop 'mounted' $ds)" == "yes"
+recv_cksum=$(md5digest /$ds/$TESTFILE0)
+log_must test "$recv_cksum" == "$cksum"
+log_must zfs destroy -r $ds
+
 # Check that we haven't printed the key to the zpool history log
 log_mustnot eval "zpool history -i | grep -q 'wkeydata'"