diff --git a/test/TEST-64-UDEV-STORAGE/test.sh b/test/TEST-64-UDEV-STORAGE/test.sh index 89de1ea692..0c6e9dfa27 100755 --- a/test/TEST-64-UDEV-STORAGE/test.sh +++ b/test/TEST-64-UDEV-STORAGE/test.sh @@ -5,9 +5,7 @@ # * iSCSI # * LVM over iSCSI (?) # * SW raid (mdadm) -# * LUKS -> MD (mdadm) -> LVM -# * BTRFS -# * MD BTRFS +# * MD (mdadm) -> DM-CRYPT -> LVM set -e TEST_DESCRIPTION="systemd-udev storage tests" @@ -26,28 +24,64 @@ if ! get_bool "$QEMU_KVM"; then exit 0 fi -test_append_files() { - ( - instmods "=block" "=md" "=nvme" "=scsi" - install_dmevent - generate_module_dependencies - image_install lsblk wc +_host_has_feature() {( + set -e - # Configure multipath - if command -v multipath && command -v multipathd; then - install_multipath - fi + case "${1:?}" in + multipath) + command -v multipath && command -v multipathd || return $? + ;; + lvm) + command -v lvm || return $? + ;; + btrfs) + modprobe -nv btrfs && command -v mkfs.btrfs && command -v btrfs || return $? + ;; + *) + echo >&2 "ERROR: Unknown feature '$1'" + # Make this a hard error to distinguish an invalid feature from + # a missing feature + exit 1 + esac +)} - # Configure LVM - if command -v lvm; then - install_lvm - fi - - for i in {0..127}; do - dd if=/dev/zero of="${TESTDIR:?}/disk$i.img" bs=1M count=1 - echo "device$i" >"${TESTDIR:?}/disk$i.img" - done +test_append_files() {( + local feature + # An associative array of requested (but optional) features and their + # respective "handlers" from test/test-functions + # + # Note: we install cryptsetup unconditionally, hence it's not explicitly + # checked for here + local -A features=( + [btrfs]=install_btrfs + [lvm]=install_lvm + [multipath]=install_multipath ) + + instmods "=block" "=md" "=nvme" "=scsi" + install_dmevent + image_install lsblk wc wipefs + + # Install the optional features if the host has the respective tooling + for feature in "${!features[@]}"; do + if _host_has_feature "$feature"; then + "${features[$feature]}" + fi + done + + generate_module_dependencies + + for i in {0..127}; do + dd if=/dev/zero of="${TESTDIR:?}/disk$i.img" bs=1M count=1 + echo "device$i" >"${TESTDIR:?}/disk$i.img" + done +)} + +_image_cleanup() { + mount_initdir + # Clean up certain "problematic" files which may be left over by failing tests + : >"${initdir:?}/etc/fstab" + : >"${initdir:?}/etc/crypttab" } test_run_one() { @@ -76,8 +110,19 @@ test_run() { # Execute each currently defined function starting with "testcase_" for testcase in "${TESTCASES[@]}"; do + _image_cleanup echo "------ $testcase: BEGIN ------" - { "$testcase" "$test_id"; ec=$?; } || : + # Note for my future frustrated self: `fun && xxx` (as wel as ||, if, while, + # until, etc.) _DISABLES_ the `set -e` behavior in _ALL_ nested function + # calls made from `fun()`, i.e. the function _CONTINUES_ even when a called + # command returned non-zero EC. That may unexpectedly hide failing commands + # if not handled properly. See: bash(1) man page, `set -e` section. + # + # So, be careful when adding clean up snippets in the testcase_*() functions - + # if the `test_run_one()` function isn't the last command, you have propagate + # the exit code correctly (e.g. `test_run_one() || return $?`, see below). + ec=0 + "$testcase" "$test_id" || ec=$? case $ec in 0) passed+=("$testcase") @@ -163,11 +208,9 @@ testcase_virtio_scsi_identically_named_partitions() { local diskpath="${TESTDIR:?}/namedpart0.img" local lodev - # Save some time (and storage life) during local testing - if [[ ! -e "$diskpath" ]]; then - dd if=/dev/zero of="$diskpath" bs=1M count=18 - lodev="$(losetup --show -f -P "$diskpath")" - sfdisk "${lodev:?}" </etc/crypttab + for ((i = 0; i < ${#devices[@]}; i++)); do + # Intentionally use weaker cipher-related settings, since we don't care + # about security here as it's a throwaway LUKS partition + cryptsetup luksFormat -q \ + --use-urandom --pbkdf pbkdf2 --pbkdf-force-iterations 1000 \ + --uuid "deadbeef-dead-dead-beef-11111111111$i" --label "encdisk$i" "${devices[$i]}" /etc/btrfs_keyfile + udevadm settle + test -e "/dev/disk/by-uuid/deadbeef-dead-dead-beef-11111111111$i" + test -e "/dev/disk/by-label/encdisk$i" + # Add the device into /etc/crypttab, reload systemd, and then activate + # the device so we can create a filesystem on it later + echo "encbtrfs$i UUID=deadbeef-dead-dead-beef-11111111111$i /etc/btrfs_keyfile luks,noearly" >>/etc/crypttab + systemctl daemon-reload + systemctl start "systemd-cryptsetup@encbtrfs$i" + done + helper_check_device_symlinks + # Check if we have all necessary DM devices + ls -l /dev/mapper/encbtrfs{0..3} + # Create a multi-device btrfs filesystem on the LUKS devices + mkfs.btrfs -M -d raid1 -m raid1 -L "$label" -U "$uuid" /dev/mapper/encbtrfs{0..3} + udevadm settle + btrfs filesystem show + test -e "/dev/disk/by-uuid/$uuid" + test -e "/dev/disk/by-label/$label" + helper_check_device_symlinks + # Mount it and write some data to it we can compare later + mount -t btrfs /dev/mapper/encbtrfs0 "$mpoint" + echo "hello there" >"$mpoint/test" + # "Deconstruct" the btrfs device and check if we're in a sane state (symlink-wise) + umount "$mpoint" + systemctl stop systemd-cryptsetup@encbtrfs{0..3} + test ! -e "/dev/disk/by-uuid/$uuid" + helper_check_device_symlinks + # Add the mount point to /etc/fstab and check if the device can be put together + # automagically. The source device is the DM name of the first LUKS device + # (from /etc/crypttab). We have to specify all LUKS devices manually, as + # registering the necessary devices is usually initrd's job (via btrfs device scan) + dev_stub="/dev/mapper/encbtrfs" + echo "/dev/mapper/encbtrfs0 $mpoint btrfs device=${dev_stub}0,device=${dev_stub}1,device=${dev_stub}2,device=${dev_stub}3 0 2" >>/etc/fstab + # Tell systemd about the new mount + systemctl daemon-reload + # Restart cryptsetup.target to trigger autounlock of partitions in /etc/crypttab + systemctl restart cryptsetup.target + # Start the corresponding mount unit and check if the btrfs device was reconstructed + # correctly + systemctl start "${mpoint##*/}.mount" + btrfs filesystem show + test -e "/dev/disk/by-uuid/$uuid" + test -e "/dev/disk/by-label/$label" + helper_check_device_symlinks + grep "hello there" "$mpoint/test" + # Cleanup + systemctl stop "${mpoint##*/}.mount" + systemctl stop systemd-cryptsetup@encbtrfs{0..3} + sed -i "/${mpoint##*/}/d" /etc/fstab + : >/etc/crypttab + rm -fr "$mpoint" + systemctl daemon-reload + udevadm settle +} + : >/failed udevadm settle