From: Mike Yuan Date: Sat, 3 May 2025 16:41:00 +0000 (+0200) Subject: hibernate-resume: automatically decrypt dissected swap if told so via autoSwap X-Git-Tag: v258-rc1~691^2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F37335%2Fhead;p=thirdparty%2Fsystemd.git 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) --- diff --git a/src/hibernate-resume/hibernate-resume-config.c b/src/hibernate-resume/hibernate-resume-config.c index ed064ea4153..3289fcd5216 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 24cc8dd2a47..4bf4af63863 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 666cd52e477..8d268a48051 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()) {