hibernate-resume: automatically decrypt dissected swap (#37335)
Closes https://github.com/systemd/systemd/issues/27247 (https://github.com/systemd/systemd/pull/35328, https://github.com/systemd/systemd/issues/37330)
This commit is contained in:
+2
-1
@@ -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);
|
||||
|
||||
@@ -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 },
|
||||
{},
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
+37
-1
@@ -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)),
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user