Allow --reduce-device-size and --device-size in encrypt action.

Fixes: #822
This commit is contained in:
Ondrej Kozina
2025-05-20 09:54:22 +02:00
parent ad30673dc5
commit 10ab6be262
6 changed files with 91 additions and 6 deletions

View File

@@ -124,6 +124,13 @@ ifdef::ACTION_REENCRYPT[]
It means that only specified area (from the start of the device It means that only specified area (from the start of the device
to the specified size) will be reencrypted. to the specified size) will be reencrypted.
+ +
*LUKS2*:
When used together with --reduce-device-size, only the initial _size_ value
(--device-size parameter) of data is shifted backwards while being encrypted.
+
*NOTE*:
The sum of --device-size and --reduce-device-size values must not exceed real device size.
+
*WARNING:* This is destructive operation. Data beyond --device-size limit may *WARNING:* This is destructive operation. Data beyond --device-size limit may
be lost after operation gets finished. be lost after operation gets finished.
endif::[] endif::[]
@@ -993,11 +1000,18 @@ unrecoverable.
+ +
*LUKS2*: *LUKS2*:
Initialize LUKS2 reencryption with data device size reduction Initialize LUKS2 reencryption with data device size reduction
(currently only encryption mode is supported). (currently only encryption mode is supported). The last _size_ sectors
on the original plaintext device is used for temporarily storing original
first data segment. The former first data segment is replaced with LUKS2
header (half the _size_ value) and plaintext data are shifted backwards (
again half the _size_ value) while being encrypted.
+ +
Recommended minimal size is twice the default LUKS2 header size Recommended minimal size is twice the default LUKS2 header size
(--reduce-device-size 32M) for encryption mode. (--reduce-device-size 32M) for encryption mode.
+ +
*NOTE*:
The sum of --device-size and --reduce-device-size values must not exceed real device size.
+
*LUKS1*: *LUKS1*:
Enlarge data offset to specified value by shrinking device size. Enlarge data offset to specified value by shrinking device size.
+ +

View File

@@ -142,6 +142,13 @@ is unused (e.g.: does not contain filesystem data):
*cryptsetup reencrypt --encrypt --type luks2 --reduce-device-size 32m /dev/plaintext_device* *cryptsetup reencrypt --encrypt --type luks2 --reduce-device-size 32m /dev/plaintext_device*
Encrypt LUKS2 device (in-place). Only the initial 1 GiB of original
_/dev/plaintext_ data is encrypted while being shifted backwards.
Make sure last 32 MiB (tail) on the data device is unused (e.g.: does
not contain any data):
*cryptsetup reencrypt --encrypt --type luks2 --device-size 1g --reduce-device-size 32m /dev/plaintext_device*
Encrypt LUKS2 device (in-place) with detached header put in a file: Encrypt LUKS2 device (in-place) with detached header put in a file:
*cryptsetup reencrypt --encrypt --type luks2 --header my_luks2_header /dev/plaintext_device* *cryptsetup reencrypt --encrypt --type luks2 --header my_luks2_header /dev/plaintext_device*

View File

@@ -3377,9 +3377,6 @@ static const char *verify_resize(void)
static const char *verify_reencrypt(void) static const char *verify_reencrypt(void)
{ {
if (ARG_SET(OPT_REDUCE_DEVICE_SIZE_ID) && ARG_SET(OPT_DEVICE_SIZE_ID))
return _("Options --reduce-device-size and --device-size cannot be combined.");
if (isLUKS1(luksType(device_type)) && ARG_SET(OPT_ACTIVE_NAME_ID)) if (isLUKS1(luksType(device_type)) && ARG_SET(OPT_ACTIVE_NAME_ID))
return _("Option --active-name can be set only for LUKS2 device."); return _("Option --active-name can be set only for LUKS2 device.");

View File

@@ -680,13 +680,15 @@ static int encrypt_luks2_init(struct crypt_device **cd, const char *data_device,
} }
} }
/* The --reduce-device-size has to be at least twice the size of first moved segment (LUKS2
* data offset) */
if (!ARG_SET(OPT_HEADER_ID) && ARG_UINT64(OPT_OFFSET_ID) && if (!ARG_SET(OPT_HEADER_ID) && ARG_UINT64(OPT_OFFSET_ID) &&
data_shift && (ARG_UINT64(OPT_OFFSET_ID) > (uint64_t)(imaxabs(data_shift) / (2 * SECTOR_SIZE)))) { data_shift && (ARG_UINT64(OPT_OFFSET_ID) > (uint64_t)(imaxabs(data_shift) / (2 * SECTOR_SIZE)))) {
log_err(_("Requested data offset must be less than or equal to half of --reduce-device-size parameter.")); log_err(_("Requested data offset must be less than or equal to half of --reduce-device-size parameter."));
return -EINVAL; return -EINVAL;
} }
/* TODO: ask user to confirm. It's useless to do data device reduction and than use smaller value */ /* It's useless to do data device reduction and than use smaller value */
if (!ARG_SET(OPT_HEADER_ID) && ARG_UINT64(OPT_OFFSET_ID) && if (!ARG_SET(OPT_HEADER_ID) && ARG_UINT64(OPT_OFFSET_ID) &&
data_shift && (ARG_UINT64(OPT_OFFSET_ID) < (uint64_t)(imaxabs(data_shift) / (2 * SECTOR_SIZE)))) { data_shift && (ARG_UINT64(OPT_OFFSET_ID) < (uint64_t)(imaxabs(data_shift) / (2 * SECTOR_SIZE)))) {
data_shift = -(ARG_UINT64(OPT_OFFSET_ID) * 2 * SECTOR_SIZE); data_shift = -(ARG_UINT64(OPT_OFFSET_ID) * 2 * SECTOR_SIZE);

View File

@@ -285,7 +285,6 @@ exp_fail reencrypt DEV --reduce-device-size 2G # max 1g
exp_fail reencrypt DEV --reduce-device-size $((64*1024*1024+1)) exp_fail reencrypt DEV --reduce-device-size $((64*1024*1024+1))
exp_fail reencrypt DEV --reduce-device-size -64m exp_fail reencrypt DEV --reduce-device-size -64m
exp_pass reencrypt DEV --reduce-device-size 64m exp_pass reencrypt DEV --reduce-device-size 64m
exp_fail reencrypt DEV --reduce-device-size 64m --device-size 100g
# bugs # bugs
# exp_fail open DEV --decrypt --header H # exp_fail open DEV --decrypt --header H
# exp_fail open DEV --encrypt # exp_fail open DEV --encrypt

View File

@@ -297,6 +297,28 @@ resize_file() # $1 dev, $2 shrink bytes
losetup -c $LOOPDEV losetup -c $LOOPDEV
} }
error_io() { # $1 dmdev, $2 data dev, $3 offset, $4 size
local _dev_size=$(blockdev --getsz /dev/mapper/$1)
local _offset=$(($3+$4))
local _size=$((_dev_size-_offset))
local _table=
dmsetup create $1-err --table "0 $_dev_size error" || fail
if [ $3 -ne 0 ]; then
_table="0 $3 linear $2 0\n"
fi
_table=$_table"$3 $4 error"
if [ $_size -ne 0 ]; then
_table="$_table\n$_offset $_size linear $2 $_offset"
fi
echo -e "$_table" | dmsetup load $1 || fail
dmsetup resume $1 || fail
blockdev --setra 0 /dev/mapper/$1
}
error_writes() { # $1 dmdev, $2 data dev, $3 offset, $4 size error_writes() { # $1 dmdev, $2 data dev, $3 offset, $4 size
local _dev_size=$(blockdev --getsz /dev/mapper/$1) local _dev_size=$(blockdev --getsz /dev/mapper/$1)
local _offset=$(($3+$4)) local _offset=$(($3+$4))
@@ -1206,6 +1228,50 @@ echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt --reduce-device-size 64M --ini
check_hash_dev_head /dev/mapper/$DEV_NAME 2048 $HASH2 check_hash_dev_head /dev/mapper/$DEV_NAME 2048 $HASH2
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q || fail echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q || fail
check_hash_dev_head /dev/mapper/$DEV_NAME 2048 $HASH2 check_hash_dev_head /dev/mapper/$DEV_NAME 2048 $HASH2
$CRYPTSETUP close $DEV_NAME || fail
# encryption with both data shift and reduced data size
prepare_linear_dev 65
# --reduce-device-size + --device-size (1MiB+512B) is larger than real device size
echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt --init-only --device-size 2049s --reduce-device-size 64M -q $FAST_PBKDF_ARGON >/dev/null 2>&1 && fail
$CRYPTSETUP isLuks --type luks2 $DEV && fail
# no changes in data device
check_hash_dev_head $DEV 2048 $HASH2
# --reduce-device-size (1MiB+8KiB) + --device-size (64MiB - (8KiB-512B)) is larger than real device size
echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt --init-only --device-size 131057s --reduce-device-size 2064s -q $FAST_PBKDF_ARGON >/dev/null 2>&1 && fail
$CRYPTSETUP isLuks --type luks2 $DEV && fail
# no changes in data device
check_hash_dev_head $DEV 2048 $HASH2
echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt --device-size 1M --reduce-device-size 32M -q $FAST_PBKDF_ARGON || fail
check_hash_head $PWD1 2048 $HASH2
# test limit values (--device-size + --reduce-device-size = real device size)
wipe_dev_head $DEV 43
echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt --device-size 43M --reduce-device-size 22M -q $FAST_PBKDF_ARGON || fail
check_hash_head $PWD1 88064 $HASH6
wipe_dev_head $DEV 1
# check reencryption code does not touch data in-between --device-size and --reduce-device-size
# data device: [ reduce-device-size / 2 ] [ device-size ] [ error minefield ] [ reduce-device-size / 2]
ERROFFSET=34816
ERRLENGTH=65536
error_io $OVRDEV $OLD_DEV $ERROFFSET $ERRLENGTH
echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt --device-size 1M --reduce-device-size 32M -q $FAST_PBKDF_ARGON || fail
fix_writes $OVRDEV $OLD_DEV
check_hash_head $PWD1 2048 $HASH2
wipe_dev_head $DEV 43
ERROFFSET=104448
ERRLENGTH=12288
# data device: [ reduce-device-size / 2 ] [ device-size ] [ error minefield ] [ reduce-device-size / 2]
error_io $OVRDEV $OLD_DEV $ERROFFSET $ERRLENGTH
echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt --device-size 43M --reduce-device-size 16M -q $FAST_PBKDF_ARGON || fail
fix_writes $OVRDEV $OLD_DEV
check_hash_head $PWD1 88064 $HASH6
echo "[3] Encryption with detached header" echo "[3] Encryption with detached header"
preparebig 256 preparebig 256