diff options
author | Lennart Poettering <lennart@poettering.net> | 2021-11-16 16:52:57 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-11-16 16:52:57 +0100 |
commit | 31f5c84dfc473b3679ecfa01ef7741ad3129bd00 (patch) | |
tree | 55fe6813e249024dcd317523e12347737e1008a5 | |
parent | d6c3a32056fbcf1178c45204139176e1e414dee1 (diff) | |
parent | 30548633456484db8c4b0392226332c8efe93a9f (diff) | |
download | systemd-31f5c84dfc473b3679ecfa01ef7741ad3129bd00.tar.gz |
Merge pull request #21380 from poettering/homed-test-qemu
homed: make sure homed tests actually run in qemu, too
-rw-r--r-- | src/home/homework-luks.c | 128 | ||||
-rw-r--r-- | src/home/homework-luks.h | 2 | ||||
-rw-r--r-- | src/home/homework.c | 4 | ||||
-rw-r--r-- | src/home/homework.h | 4 | ||||
-rwxr-xr-x | test/TEST-46-HOMED/test.sh | 16 | ||||
-rwxr-xr-x | test/units/testsuite-46.sh | 4 |
6 files changed, 154 insertions, 4 deletions
diff --git a/src/home/homework-luks.c b/src/home/homework-luks.c index 79d46f7306..8ad6499d8f 100644 --- a/src/home/homework-luks.c +++ b/src/home/homework-luks.c @@ -13,6 +13,8 @@ #endif #include "sd-daemon.h" +#include "sd-device.h" +#include "sd-event.h" #include "blkid-util.h" #include "blockdev-util.h" @@ -45,6 +47,7 @@ #include "strv.h" #include "sync-util.h" #include "tmpfile-util.h" +#include "udev-util.h" #include "user-util.h" /* Round down to the nearest 4K size. Given that newer hardware generally prefers 4K sectors, let's align our @@ -1506,6 +1509,7 @@ int home_deactivate_luks(UserRecord *h, HomeSetup *setup) { } } + (void) wait_for_block_device_gone(setup, USEC_PER_SEC * 30); setup->undo_dm = false; if (user_record_luks_offline_discard(h)) @@ -3196,3 +3200,127 @@ int home_unlock_luks(UserRecord *h, HomeSetup *setup, const PasswordCache *cache log_info("LUKS device resumed."); return 0; } + +static int device_is_gone(HomeSetup *setup) { + _cleanup_(sd_device_unrefp) sd_device *d = NULL; + struct stat st; + int r; + + assert(setup); + + if (!setup->dm_node) + return true; + + if (stat(setup->dm_node, &st) < 0) { + if (errno != ENOENT) + return log_error_errno(errno, "Failed to stat block device node %s: %m", setup->dm_node); + + return true; + } + + r = sd_device_new_from_stat_rdev(&d, &st); + if (r < 0) { + if (r != -ENODEV) + return log_error_errno(errno, "Failed to allocate device object from block device node %s: %m", setup->dm_node); + + return true; + } + + return false; +} + +static int device_monitor_handler(sd_device_monitor *monitor, sd_device *device, void *userdata) { + HomeSetup *setup = userdata; + int r; + + assert(setup); + + if (!device_for_action(device, SD_DEVICE_REMOVE)) + return 0; + + /* We don't really care for the device object passed to us, we just check if the device node still + * exists */ + + r = device_is_gone(setup); + if (r < 0) + return r; + if (r > 0) /* Yay! we are done! */ + (void) sd_event_exit(sd_device_monitor_get_event(monitor), 0); + + return 0; +} + +int wait_for_block_device_gone(HomeSetup *setup, usec_t timeout_usec) { + _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *m = NULL; + _cleanup_(sd_event_unrefp) sd_event *event = NULL; + int r; + + assert(setup); + + /* So here's the thing: we enable "deferred deactivation" on our dm-crypt volumes. This means they + * are automatically torn down once not used anymore (i.e. once unmounted). Which is great. It also + * means that when we deactivate a home directory and try to tear down the volume that backs it, it + * possibly is aleady torn down or in the process of being torn down, since we race against the + * automatic tearing down. Which is fine, we handle errors from that. However, we lose the ability to + * naturally wait for the tear down operation to complete: if we are not the ones who tear down the + * device we are also not the ones who naturally block on that operation. Hence let's add some code + * to actively wait for the device to go away, via sd-device. We'll call this whenever tearing down a + * LUKS device, to ensure the device is really really gone before we proceed. Net effect: "homectl + * deactivate foo && homectl activate foo" will work reliably, i.e. deactivation immediately followed + * by activation will work. Also, by the time deactivation completes we can guarantee that all data + * is sync'ed down to the lowest block layer as all higher levels are fully and entirely + * destructed. */ + + if (!setup->dm_name) + return 0; + + assert(setup->dm_node); + log_debug("Waiting until %s disappears.", setup->dm_node); + + r = sd_event_new(&event); + if (r < 0) + return log_error_errno(r, "Failed to allocate event loop: %m"); + + r = sd_device_monitor_new(&m); + if (r < 0) + return log_error_errno(r, "Failed to allocate device monitor: %m"); + + r = sd_device_monitor_filter_add_match_subsystem_devtype(m, "block", "disk"); + if (r < 0) + return log_error_errno(r, "Failed to configure device monitor match: %m"); + + r = sd_device_monitor_attach_event(m, event); + if (r < 0) + return log_error_errno(r, "Failed to attach device monitor to event loop: %m"); + + r = sd_device_monitor_start(m, device_monitor_handler, setup); + if (r < 0) + return log_error_errno(r, "Failed to start device monitor: %m"); + + r = device_is_gone(setup); + if (r < 0) + return r; + if (r > 0) { + log_debug("%s has already disappeared before entering wait loop.", setup->dm_node); + return 0; /* gone already */ + } + + if (timeout_usec != USEC_INFINITY) { + r = sd_event_add_time_relative(event, NULL, CLOCK_MONOTONIC, timeout_usec, 0, NULL, NULL); + if (r < 0) + return log_error_errno(r, "Failed to add timer event: %m"); + } + + r = sd_event_loop(event); + if (r < 0) + return log_error_errno(r, "Failed to run event loop: %m"); + + r = device_is_gone(setup); + if (r < 0) + return r; + if (r == 0) + return log_error_errno(r, "Device %s still around.", setup->dm_node); + + log_debug("Successfully waited until device %s disappeared.", setup->dm_node); + return 0; +} diff --git a/src/home/homework-luks.h b/src/home/homework-luks.h index f6ec11c2e6..796a883831 100644 --- a/src/home/homework-luks.h +++ b/src/home/homework-luks.h @@ -43,3 +43,5 @@ int run_fallocate(int backing_fd, const struct stat *st); int run_fallocate_by_path(const char *backing_path); int run_mark_dirty(int fd, bool b); int run_mark_dirty_by_path(const char *path, bool b); + +int wait_for_block_device_gone(HomeSetup *setup, usec_t timeout_usec); diff --git a/src/home/homework.c b/src/home/homework.c index 5bb759316f..5907015e2b 100644 --- a/src/home/homework.c +++ b/src/home/homework.c @@ -326,6 +326,10 @@ int home_setup_undo_dm(HomeSetup *setup, int level) { if (r < 0) return log_full_errno(level, r, "Failed to deactivate LUKS device: %m"); + /* In case the device was already remove asynchronously by an early unmount via the deferred + * remove logic, let's wait for it */ + (void) wait_for_block_device_gone(setup, USEC_PER_SEC * 30); + setup->undo_dm = false; ret = 1; } else diff --git a/src/home/homework.h b/src/home/homework.h index 053def6360..551f0d0153 100644 --- a/src/home/homework.h +++ b/src/home/homework.h @@ -12,8 +12,8 @@ #include "user-record-util.h" typedef struct HomeSetup { - char *dm_name; - char *dm_node; + char *dm_name; /* "home-<username>" */ + char *dm_node; /* "/dev/mapper/home-<username>" */ LoopDevice *loop; struct crypt_device *crypt_device; diff --git a/test/TEST-46-HOMED/test.sh b/test/TEST-46-HOMED/test.sh index 7725995195..216f3d2fb2 100755 --- a/test/TEST-46-HOMED/test.sh +++ b/test/TEST-46-HOMED/test.sh @@ -3,9 +3,23 @@ set -e TEST_DESCRIPTION="testing homed" -TEST_NO_QEMU=1 + +# Skip the qemu version of the test, unless we have btrfs +(modprobe -nv btrfs && command -v mkfs.btrfs) || TEST_NO_QEMU=1 # shellcheck source=test/test-functions . "${TEST_BASE_DIR:?}/test-functions" +# Need loop devices for mounting images +test_append_files() { + ( + if [ "$TEST_NO_QEMU" != "1" ] ; then + instmods loop =block + install_dmevent + install_btrfs + generate_module_dependencies + fi + ) +} + do_test "$@" diff --git a/test/units/testsuite-46.sh b/test/units/testsuite-46.sh index 72d1fd6f85..fc4fc50297 100755 --- a/test/units/testsuite-46.sh +++ b/test/units/testsuite-46.sh @@ -26,7 +26,9 @@ inspect() { systemd-analyze log-level debug systemd-analyze log-target console -NEWPASSWORD=xEhErW0ndafV4s homectl create test-user --disk-size=20M +# we enable --luks-discard= since we run our tests in a tight VM, hence don't +# needlessly pressure for storage +NEWPASSWORD=xEhErW0ndafV4s homectl create test-user --disk-size=256M --luks-discard=yes inspect test-user PASSWORD=xEhErW0ndafV4s homectl authenticate test-user |