diff --git a/module/os/freebsd/zfs/zio_crypt.c b/module/os/freebsd/zfs/zio_crypt.c index fb88bc325d..fd2beee7bd 100644 --- a/module/os/freebsd/zfs/zio_crypt.c +++ b/module/os/freebsd/zfs/zio_crypt.c @@ -1070,6 +1070,16 @@ zio_crypt_do_objset_hmacs(zio_crypt_key_t *key, void *data, uint_t datalen, bcopy(raw_portable_mac, portable_mac, ZIO_OBJSET_MAC_LEN); + /* + * This is necessary here as we check next whether + * OBJSET_FLAG_USERACCOUNTING_COMPLETE or + * OBJSET_FLAG_USEROBJACCOUNTING are set in order to + * decide if the local_mac should be zeroed out. + */ + intval = osp->os_flags; + if (should_bswap) + intval = BSWAP_64(intval); + /* * The local MAC protects the user, group and project accounting. * If these objects are not present, the local MAC is zeroed out. @@ -1081,7 +1091,10 @@ zio_crypt_do_objset_hmacs(zio_crypt_key_t *key, void *data, uint_t datalen, (datalen >= OBJSET_PHYS_SIZE_V2 && osp->os_userused_dnode.dn_type == DMU_OT_NONE && osp->os_groupused_dnode.dn_type == DMU_OT_NONE) || - (datalen <= OBJSET_PHYS_SIZE_V1)) { + (datalen <= OBJSET_PHYS_SIZE_V1) || + (((intval & OBJSET_FLAG_USERACCOUNTING_COMPLETE) == 0 || + (intval & OBJSET_FLAG_USEROBJACCOUNTING_COMPLETE) == 0) && + key->zk_version > 0)) { bzero(local_mac, ZIO_OBJSET_MAC_LEN); return (0); } diff --git a/module/os/linux/zfs/zio_crypt.c b/module/os/linux/zfs/zio_crypt.c index 96dabe55a1..8106359e1c 100644 --- a/module/os/linux/zfs/zio_crypt.c +++ b/module/os/linux/zfs/zio_crypt.c @@ -1197,6 +1197,16 @@ zio_crypt_do_objset_hmacs(zio_crypt_key_t *key, void *data, uint_t datalen, bcopy(raw_portable_mac, portable_mac, ZIO_OBJSET_MAC_LEN); + /* + * This is necessary here as we check next whether + * OBJSET_FLAG_USERACCOUNTING_COMPLETE or + * OBJSET_FLAG_USEROBJACCOUNTING are set in order to + * decide if the local_mac should be zeroed out. + */ + intval = osp->os_flags; + if (should_bswap) + intval = BSWAP_64(intval); + /* * The local MAC protects the user, group and project accounting. * If these objects are not present, the local MAC is zeroed out. @@ -1208,7 +1218,10 @@ zio_crypt_do_objset_hmacs(zio_crypt_key_t *key, void *data, uint_t datalen, (datalen >= OBJSET_PHYS_SIZE_V2 && osp->os_userused_dnode.dn_type == DMU_OT_NONE && osp->os_groupused_dnode.dn_type == DMU_OT_NONE) || - (datalen <= OBJSET_PHYS_SIZE_V1)) { + (datalen <= OBJSET_PHYS_SIZE_V1) || + (((intval & OBJSET_FLAG_USERACCOUNTING_COMPLETE) == 0 || + (intval & OBJSET_FLAG_USEROBJACCOUNTING_COMPLETE) == 0) && + key->zk_version > 0)) { bzero(local_mac, ZIO_OBJSET_MAC_LEN); return (0); } diff --git a/module/zfs/dsl_crypt.c b/module/zfs/dsl_crypt.c index 26d4c2fe7e..e38ec0cae8 100644 --- a/module/zfs/dsl_crypt.c +++ b/module/zfs/dsl_crypt.c @@ -2007,14 +2007,6 @@ dsl_crypto_recv_raw_objset_check(dsl_dataset_t *ds, dsl_dataset_t *fromds, if (ret != 0) return (ret); - /* - * Useraccounting is not portable and must be done with the keys loaded. - * Therefore, whenever we do any kind of receive the useraccounting - * must not be present. - */ - ASSERT0(os->os_flags & OBJSET_FLAG_USERACCOUNTING_COMPLETE); - ASSERT0(os->os_flags & OBJSET_FLAG_USEROBJACCOUNTING_COMPLETE); - mdn = DMU_META_DNODE(os); /* @@ -2105,6 +2097,9 @@ dsl_crypto_recv_raw_objset_sync(dsl_dataset_t *ds, dmu_objset_type_t ostype, */ arc_release(os->os_phys_buf, &os->os_phys_buf); bcopy(portable_mac, os->os_phys->os_portable_mac, ZIO_OBJSET_MAC_LEN); + os->os_phys->os_flags &= ~OBJSET_FLAG_USERACCOUNTING_COMPLETE; + os->os_phys->os_flags &= ~OBJSET_FLAG_USEROBJACCOUNTING_COMPLETE; + os->os_flags = os->os_phys->os_flags; bzero(os->os_phys->os_local_mac, ZIO_OBJSET_MAC_LEN); os->os_next_write_raw[tx->tx_txg & TXG_MASK] = B_TRUE; diff --git a/tests/runfiles/common.run b/tests/runfiles/common.run index d8345f62bb..257fac2c18 100644 --- a/tests/runfiles/common.run +++ b/tests/runfiles/common.run @@ -865,7 +865,8 @@ tests = [ 'userquota_004_pos', 'userquota_005_neg', 'userquota_006_pos', 'userquota_007_pos', 'userquota_008_pos', 'userquota_009_pos', 'userquota_010_pos', 'userquota_011_pos', 'userquota_012_neg', - 'userspace_001_pos', 'userspace_002_pos', 'userspace_encrypted'] + 'userspace_001_pos', 'userspace_002_pos', 'userspace_encrypted', + 'userspace_send_encrypted'] tags = ['functional', 'userquota'] [tests/functional/vdev_zaps] diff --git a/tests/zfs-tests/tests/functional/userquota/userspace_send_encrypted.ksh b/tests/zfs-tests/tests/functional/userquota/userspace_send_encrypted.ksh new file mode 100755 index 0000000000..fbd2cc99b5 --- /dev/null +++ b/tests/zfs-tests/tests/functional/userquota/userspace_send_encrypted.ksh @@ -0,0 +1,108 @@ +#!/bin/ksh -p +# +# 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. +# + +# +# Copyright 2020, George Amanakis . All rights reserved. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/userquota/userquota_common.kshlib + +# +# DESCRIPTION: +# Sending raw encrypted datasets back to the source dataset succeeds. +# +# +# STRATEGY: +# 1. Create encrypted source dataset, set userquota and write a file +# 2. Create base and an additional snapshot (s1) +# 3. Unmount the source dataset +# 4. Raw send the base snapshot to a new target dataset +# 5. Raw send incrementally the s1 snapshot to the new target dataset +# 6. Mount both source and target datasets +# 7. Verify encrypted datasets support 'zfs userspace' and 'zfs groupspace' +# and the accounting is done correctly +# + +function cleanup +{ + destroy_pool $POOLNAME + rm -f $FILEDEV +} + +function log_must_unsupported +{ + log_must_retry "unsupported" 3 "$@" + (( $? != 0 )) && log_fail +} + +log_onexit cleanup + +FILEDEV="$TEST_BASE_DIR/userspace_encrypted" +POOLNAME="testpool$$" +ENC_SOURCE="$POOLNAME/source" +ENC_TARGET="$POOLNAME/target" + +log_assert "Sending raw encrypted datasets back to the source dataset succeeds." + +# Setup +truncate -s 200m $FILEDEV +log_must zpool create -o feature@encryption=enabled $POOLNAME \ + $FILEDEV + +# Create encrypted source dataset +log_must eval "echo 'password' | zfs create -o encryption=on" \ + "-o keyformat=passphrase -o keylocation=prompt " \ + "$ENC_SOURCE" + +# Set user quota and write file +log_must zfs set userquota@$QUSER1=50m $ENC_SOURCE +mkmount_writable $ENC_SOURCE +mntpnt=$(get_prop mountpoint $ENC_SOURCE) +log_must user_run $QUSER1 mkfile 20m /$mntpnt/file +sync + +# Snapshot, raw send to new dataset +log_must zfs snap $ENC_SOURCE@base +log_must zfs snap $ENC_SOURCE@s1 +log_must zfs umount $ENC_SOURCE +log_must eval "zfs send -w $ENC_SOURCE@base | zfs recv " \ + "$ENC_TARGET" + +log_must eval "zfs send -w -i @base $ENC_SOURCE@s1 | zfs recv " \ + "$ENC_TARGET" + +log_must zfs destroy $ENC_SOURCE@s1 +log_must eval "zfs send -w -i @base $ENC_TARGET@s1 | zfs recv " \ + "$ENC_SOURCE" + +# Mount encrypted datasets and verify they support 'zfs userspace' and +# 'zfs groupspace' and the accounting is done correctly +log_must zfs mount $ENC_SOURCE +log_must eval "echo password | zfs load-key $ENC_TARGET" +log_must zfs mount $ENC_TARGET +sync + +src_uspace=$(( $(zfs userspace -Hp $ENC_SOURCE | grep $QUSER1 | \ + awk '{print $4}')/1024/1024)) +tgt_uspace=$(( $(zfs userspace -Hp $ENC_TARGET | grep $QUSER1 | \ + awk '{print $4}')/1024/1024)) +log_must test "$src_uspace" -eq "$tgt_uspace" + +src_uquota=$(zfs userspace -Hp $ENC_SOURCE | grep $QUSER1 | awk '{print $5}') +tgt_uquota=$(zfs userspace -Hp $ENC_TARGET | grep $QUSER1 | awk '{print $5}') +log_must test "$src_uquota" -eq "$tgt_uquota" + +# Cleanup +cleanup + +log_pass "Sending raw encrypted datasets back to the source dataset succeeds."