From ab2f5407a0659bc439189d241e1960f0aaf12b25 Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Sat, 3 May 2025 21:00:15 +0200 Subject: [PATCH 1/4] core/cgroup: block_get_originating() doesn't return > 0 Follow-up for 612fc70fc0f5445817f3f5c033dd3d5b5fd058ea --- src/core/cgroup.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/cgroup.c b/src/core/cgroup.c index 74c9d786a1..e37bf46e87 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -1019,7 +1019,8 @@ static int lookup_block_device(const char *p, dev_t *ret) { } /* If this is a LUKS/DM device, recursively try to get the originating block device */ - while (block_get_originating(*ret, ret) > 0); + while (block_get_originating(*ret, ret) >= 0) + ; /* If this is a partition, try to get the originating block device */ (void) block_get_whole_disk(*ret, ret); From 60997a87ef91d53fd9f4594eb982d6a7e4fbd1ec Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Sat, 3 May 2025 21:01:54 +0200 Subject: [PATCH 2/4] blockdev-util: don't use mixed style of retval in block_device_get_originating We have two typical styles of 'ret' param assignment + retval: 1) < 0 on actual error, 0 on nothing (ret == NULL), > 0 on something 2) recognizable errno on nothing, < 0 on other errors, >= 0 on something but never use both at the same time. --- src/shared/blockdev-util.c | 8 ++++---- src/storagetm/storagetm.c | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/shared/blockdev-util.c b/src/shared/blockdev-util.c index 383df8bd8f..968ff6adc3 100644 --- a/src/shared/blockdev-util.c +++ b/src/shared/blockdev-util.c @@ -138,7 +138,7 @@ int block_device_get_originating(sd_device *dev, sd_device **ret) { return -ENOENT; *ret = TAKE_PTR(first_found); - return 1; /* found */ + return 0; } int block_device_new_from_fd(int fd, BlockDeviceLookupFlag flags, sd_device **ret) { @@ -166,10 +166,10 @@ int block_device_new_from_fd(int fd, BlockDeviceLookupFlag flags, sd_device **re return r; r = block_device_get_originating(dev_whole_disk, &dev_origin); - if (r < 0 && r != -ENOENT) - return r; - if (r > 0) + if (r >= 0) device_unref_and_replace(dev, dev_origin); + else if (r != -ENOENT) + return r; } if (FLAGS_SET(flags, BLOCK_DEVICE_LOOKUP_WHOLE_DISK)) { diff --git a/src/storagetm/storagetm.c b/src/storagetm/storagetm.c index 1f1ebe2e8f..4bc6917887 100644 --- a/src/storagetm/storagetm.c +++ b/src/storagetm/storagetm.c @@ -842,7 +842,7 @@ static void device_track_back(sd_device *d, sd_device **ret) { _cleanup_(sd_device_unrefp) sd_device *d_originating = NULL; r = block_device_get_originating(d, &d_originating); - if (r < 0) + if (r < 0 && r != -ENOENT) log_device_debug_errno(d, r, "Failed to get originating device for '%s', ignoring: %m", strna(devname)); sd_device *d_whole = NULL; From a88ee2c8df4dd26acfb8bf66650610167ffc8df2 Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Sat, 3 May 2025 18:02:54 +0200 Subject: [PATCH 3/4] sleep: record whether the hibernation device is auto swap (with "swap" designator) --- src/sleep/sleep.c | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c index 7eb3130c4e..2149a49299 100644 --- a/src/sleep/sleep.c +++ b/src/sleep/sleep.c @@ -21,6 +21,7 @@ #include "battery-capacity.h" #include "battery-util.h" +#include "blockdev-util.h" #include "build.h" #include "bus-error.h" #include "bus-locator.h" @@ -53,6 +54,35 @@ static SleepOperation arg_operation = _SLEEP_OPERATION_INVALID; +#if ENABLE_EFI +static int determine_auto_swap(sd_device *device) { + _cleanup_(sd_device_unrefp) sd_device *origin = NULL; + const char *part_designator; + int r; + + /* Check if the selected hibernation device is the "auto" one. Specifically, trace to the partition + * block device and check if ID_DISSECT_PART_DESIGNATOR property is set to "swap" by udev + * dissect_image builtin. Later hibernate-resume-generator will generate cryptsetup unit based on + * this info. */ + + assert(device); + + r = block_device_get_originating(device, &origin); + if (r < 0 && r != -ENOENT) + return r; + if (r >= 0) + device = origin; + + r = sd_device_get_property_value(device, "ID_DISSECT_PART_DESIGNATOR", &part_designator); + if (r == -ENOENT) + return false; + if (r < 0) + return r; + + return streq(part_designator, "swap"); +} +#endif + static int write_efi_hibernate_location(const HibernationDevice *hibernation_device, bool required) { int log_level = required ? LOG_ERR : LOG_DEBUG; @@ -64,7 +94,7 @@ static int write_efi_hibernate_location(const HibernationDevice *hibernation_dev const char *uuid_str; sd_id128_t uuid; struct utsname uts = {}; - int r, log_level_ignore = required ? LOG_WARNING : LOG_DEBUG; + int r, auto_swap, log_level_ignore = required ? LOG_WARNING : LOG_DEBUG; assert(hibernation_device); @@ -87,6 +117,11 @@ static int write_efi_hibernate_location(const HibernationDevice *hibernation_dev return log_full_errno(log_level, r, "Failed to parse ID_FS_UUID '%s' for device '%s': %m", uuid_str, hibernation_device->path); + auto_swap = determine_auto_swap(device); + if (auto_swap < 0) + log_full_errno(log_level_ignore, auto_swap, + "Failed to get partition designator of '%s', ignoring: %m", hibernation_device->path); + if (uname(&uts) < 0) log_full_errno(log_level_ignore, errno, "Failed to get kernel info, ignoring: %m"); @@ -102,6 +137,7 @@ static int write_efi_hibernate_location(const HibernationDevice *hibernation_dev &v, SD_JSON_BUILD_PAIR_UUID("uuid", uuid), SD_JSON_BUILD_PAIR_UNSIGNED("offset", hibernation_device->offset), + SD_JSON_BUILD_PAIR_CONDITION(auto_swap >= 0, "autoSwap", SD_JSON_BUILD_BOOLEAN(auto_swap)), SD_JSON_BUILD_PAIR_CONDITION(!isempty(uts.release), "kernelVersion", SD_JSON_BUILD_STRING(uts.release)), SD_JSON_BUILD_PAIR_CONDITION(!!id, "osReleaseId", SD_JSON_BUILD_STRING(id)), SD_JSON_BUILD_PAIR_CONDITION(!!image_id, "osReleaseImageId", SD_JSON_BUILD_STRING(image_id)), From 656a7bb1fb324aec8aeab4b07bf27d71e9e2e107 Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Sat, 3 May 2025 18:41:00 +0200 Subject: [PATCH 4/4] hibernate-resume: automatically decrypt dissected swap if told so via autoSwap With the addition of /dev/disk/by-designator/ along with ID_DISSECT_PART_DESIGNATOR attr, it is now trivial to tell whether the swap device we hibernated into is the "auto" one. Hence use that bit of info and generate cryptsetup unit in hibernate-resume-generator if that's the case. Ideally, gpt-auto should really just handle swap already in initrd, which would save us a myriad of trouble and the system behaves more consistently. But I don't see that happening anytime soon. This is the second best option we have I reckon. Closes #27247 (#35328, #37330) --- .../hibernate-resume-config.c | 15 ++--- .../hibernate-resume-config.h | 4 ++ .../hibernate-resume-generator.c | 63 +++++++++++++++++++ 3 files changed, 75 insertions(+), 7 deletions(-) diff --git a/src/hibernate-resume/hibernate-resume-config.c b/src/hibernate-resume/hibernate-resume-config.c index ed064ea415..3289fcd521 100644 --- a/src/hibernate-resume/hibernate-resume-config.c +++ b/src/hibernate-resume/hibernate-resume-config.c @@ -143,13 +143,14 @@ static bool validate_efi_hibernate_location(EFIHibernateLocation *e) { int get_efi_hibernate_location(EFIHibernateLocation **ret) { #if ENABLE_EFI static const sd_json_dispatch_field dispatch_table[] = { - { "uuid", SD_JSON_VARIANT_STRING, sd_json_dispatch_id128, offsetof(EFIHibernateLocation, uuid), SD_JSON_MANDATORY }, - { "offset", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(EFIHibernateLocation, offset), SD_JSON_MANDATORY }, - { "kernelVersion", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(EFIHibernateLocation, kernel_version), SD_JSON_PERMISSIVE }, - { "osReleaseId", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(EFIHibernateLocation, id), SD_JSON_PERMISSIVE }, - { "osReleaseImageId", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(EFIHibernateLocation, image_id), SD_JSON_PERMISSIVE }, - { "osReleaseVersionId", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(EFIHibernateLocation, version_id), SD_JSON_PERMISSIVE|SD_JSON_DEBUG }, - { "osReleaseImageVersion", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(EFIHibernateLocation, image_version), SD_JSON_PERMISSIVE|SD_JSON_DEBUG }, + { "uuid", SD_JSON_VARIANT_STRING, sd_json_dispatch_id128, offsetof(EFIHibernateLocation, uuid), SD_JSON_MANDATORY }, + { "offset", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(EFIHibernateLocation, offset), SD_JSON_MANDATORY }, + { "autoSwap", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(EFIHibernateLocation, auto_swap), 0 }, + { "kernelVersion", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(EFIHibernateLocation, kernel_version), SD_JSON_PERMISSIVE }, + { "osReleaseId", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(EFIHibernateLocation, id), SD_JSON_PERMISSIVE }, + { "osReleaseImageId", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(EFIHibernateLocation, image_id), SD_JSON_PERMISSIVE }, + { "osReleaseVersionId", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(EFIHibernateLocation, version_id), SD_JSON_PERMISSIVE|SD_JSON_DEBUG }, + { "osReleaseImageVersion", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(EFIHibernateLocation, image_version), SD_JSON_PERMISSIVE|SD_JSON_DEBUG }, {}, }; diff --git a/src/hibernate-resume/hibernate-resume-config.h b/src/hibernate-resume/hibernate-resume-config.h index 24cc8dd2a4..4bf4af6386 100644 --- a/src/hibernate-resume/hibernate-resume-config.h +++ b/src/hibernate-resume/hibernate-resume-config.h @@ -16,6 +16,10 @@ typedef struct EFIHibernateLocation { sd_id128_t uuid; uint64_t offset; + /* Whether the device we hibernated into is the "auto" one, i.e. will show up as + * /dev/disk/by-designator/swap(-luks). */ + bool auto_swap; + char *kernel_version; char *id; char *image_id; diff --git a/src/hibernate-resume/hibernate-resume-generator.c b/src/hibernate-resume/hibernate-resume-generator.c index 666cd52e47..8d268a4805 100644 --- a/src/hibernate-resume/hibernate-resume-generator.c +++ b/src/hibernate-resume/hibernate-resume-generator.c @@ -2,6 +2,9 @@ #include "alloc-util.h" #include "dropin.h" +#include "efi-loader.h" +#include "fd-util.h" +#include "fileio.h" #include "generator.h" #include "hibernate-resume-config.h" #include "initrd-util.h" @@ -15,6 +18,7 @@ #include "unit-name.h" static const char *arg_dest = NULL; +static const char *arg_dest_late = NULL; static char *arg_resume_options = NULL; static char *arg_root_options = NULL; static bool arg_noresume = false; @@ -54,6 +58,58 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat return 0; } +#define DISSECTED_SWAP_LUKS_DEVICE "/dev/disk/by-designator/swap-luks" +#define DISSECTED_SWAP_LUKS_DEVICE_UNIT "dev-disk-by\\x2ddesignator-swap\\x2dluks.device" + +static int add_dissected_swap_cryptsetup(void) { + +#if HAVE_LIBCRYPTSETUP + _cleanup_fclose_ FILE *f = NULL; + int r; + + /* Write out cryptsetup unit for the "auto" swap device (/dev/disk/by-designator/swap-luks), so that + * resume from hibernation can be automatically initiated there. This mostly follows what gpt-auto does, + * but operates in initrd. */ + + r = generator_open_unit_file(arg_dest_late, /* source = */ NULL, "systemd-cryptsetup@swap.service", &f); + if (r < 0) + return r; + + r = generator_write_cryptsetup_unit_section(f, /* source = */ NULL); + if (r < 0) + return r; + + fputs("Before=umount.target cryptsetup.target\n" + "Conflicts=umount.target\n" + "BindsTo="DISSECTED_SWAP_LUKS_DEVICE_UNIT"\n" + "After="DISSECTED_SWAP_LUKS_DEVICE_UNIT"\n", + f); + + r = generator_write_cryptsetup_service_section( + f, "swap", DISSECTED_SWAP_LUKS_DEVICE, + /* key_file = */ NULL, + efi_measured_uki(LOG_DEBUG) > 0 ? "tpm2-device=auto" : NULL); + if (r < 0) + return r; + + r = fflush_and_check(f); + if (r < 0) + return log_error_errno(r, "Failed to write cryptsetup unit for " DISSECTED_SWAP_LUKS_DEVICE ": %m"); + + r = generator_write_device_timeout(arg_dest_late, + DISSECTED_SWAP_LUKS_DEVICE, + arg_resume_options ?: arg_root_options, /* filtered = */ NULL); + if (r < 0) + return r; + + return generator_add_symlink(arg_dest_late, DISSECTED_SWAP_LUKS_DEVICE_UNIT, "wants", "systemd-cryptsetup@swap.service"); +#else + return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "systemd-hibernate-resume-generator was compiled without libcryptsetup support, " + "not generating cryptsetup unit for " DISSECTED_SWAP_LUKS_DEVICE "."); +#endif +} + static int process_resume(const HibernateInfo *info) { _cleanup_free_ char *device_unit = NULL; int r; @@ -89,6 +145,12 @@ static int process_resume(const HibernateInfo *info) { if (r < 0) return log_error_errno(r, "Failed to write device dependency drop-in: %m"); + /* Generate cryptsetup unit for /dev/disk/by-designator/swap-luks if we hibernated into it, but only + * if resume= is not specified, on the assumption that the user would have everything configured + * manually otherwise. */ + if (!info->cmdline && info->efi->auto_swap) + (void) add_dissected_swap_cryptsetup(); + return generator_add_symlink(arg_dest, SPECIAL_SYSINIT_TARGET, "wants", SPECIAL_HIBERNATE_RESUME_SERVICE); } @@ -97,6 +159,7 @@ static int run(const char *dest, const char *dest_early, const char *dest_late) int r; arg_dest = ASSERT_PTR(dest); + arg_dest_late = ASSERT_PTR(dest_late); /* Don't even consider resuming outside of initrd. */ if (!in_initrd()) {