Merge pull request #14196 from keszybz/gpt-auto-generator-debugging

gpt-auto-generator debugging
This commit is contained in:
Lennart Poettering 2019-12-16 09:24:02 +01:00 committed by GitHub
commit 3b5cd25f4d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 199 additions and 188 deletions

2
TODO
View File

@ -234,7 +234,7 @@ Features:
1. add resume_offset support to the resume code (i.e. support swap files
properly)
2. check if swap is on weird storage and refuse if so
3. add autodetection of hibernation images
3. add auto-detection of hibernation images
* cgroups: use inotify to get notified when somebody else modifies cgroups
owned by us, then log a friendly warning.

View File

@ -35,7 +35,7 @@
root, <filename>/home/</filename>, <filename>/srv/</filename>, the EFI System Partition, the Extended
Boot Loader Partition and swap partitions and creates mount and swap units for them, based on the
partition type GUIDs of GUID partition tables (GPT), see <ulink
url="https://uefi.org/specifications">UEFI Specification</ulink>, chapter 5. It implements the <ulink
url="https://uefi.org/specifications">UEFI Specification</ulink>, chapter 5. It implements the <ulink
url="https://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/">Discoverable Partitions
Specification</ulink>. Note that this generator has no effect on non-GPT systems, and on specific mount
points that are directories already containing files. Also, on systems where the units are explicitly
@ -44,18 +44,21 @@
units this generator creates are overridden, but additional implicit dependencies might be
created.</para>
<para>This generator will only look for root partitions on the
same physical disk the EFI System Partition (ESP) is located on.
It will only look for the other partitions on the same physical
disk the root file system is located on. These partitions will not
be searched for on systems where the root file system is distributed
on multiple disks, for example via btrfs RAID.</para>
<para>This generator will only look for the root partition on the same physical disk the EFI System
Partition (ESP) is located on. Note that support from the boot loader is required: EFI variable
<varname>LoaderDevicePartUUID-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f</varname> is used to determine from
which partition, and hence the disk from which the system was booted. If the boot loader does not set
this variable, this generator will not be able to autodetect the root partition.</para>
<para><filename>systemd-gpt-auto-generator</filename> is useful
for centralizing file system configuration in the partition table
and making configuration in <filename>/etc/fstab</filename> unnecessary.
<para>Similarly, this generator will only look for the other partitions on the same physical disk as the
root partition. In this case, boot loader support is not required. These partitions will not be searched
for on systems where the root file system is distributed on multiple disks, for example via btrfs RAID.
</para>
<para><filename>systemd-gpt-auto-generator</filename> is useful for centralizing file system
configuration in the partition table and making configuration in <filename>/etc/fstab</filename> or on
the kernel command line unnecessary.</para>
<para>This generator looks for the partitions based on their
partition type GUID. The following partition type GUIDs are
identified:</para>

View File

@ -1163,6 +1163,15 @@ static void read_loader_efi_var(const char *name, char **var) {
log_warning_errno(r, "Failed to read EFI variable %s: %m", name);
}
static void print_yes_no_line(bool first, bool good, const char *name) {
printf("%s%s%s%s %s\n",
first ? " Features: " : " ",
good ? ansi_highlight_green() : ansi_highlight_red(),
good ? special_glyph(SPECIAL_GLYPH_CHECK_MARK) : special_glyph(SPECIAL_GLYPH_CROSS_MARK),
ansi_normal(),
name);
}
static int verb_status(int argc, char *argv[], void *userdata) {
sd_id128_t esp_uuid = SD_ID128_NULL, xbootldr_uuid = SD_ID128_NULL;
int r, k;
@ -1242,18 +1251,15 @@ static int verb_status(int argc, char *argv[], void *userdata) {
printf("Current Boot Loader:\n");
printf(" Product: %s%s%s\n", ansi_highlight(), strna(loader), ansi_normal());
for (i = 0; i < ELEMENTSOF(flags); i++) {
for (i = 0; i < ELEMENTSOF(flags); i++)
print_yes_no_line(i == 0, FLAGS_SET(loader_features, flags[i].flag), flags[i].name);
if (i == 0)
printf(" Features: ");
else
printf(" ");
sd_id128_t bootloader_esp_uuid;
bool have_bootloader_esp_uuid = efi_loader_get_device_part_uuid(&bootloader_esp_uuid) >= 0;
if (FLAGS_SET(loader_features, flags[i].flag))
printf("%s%s%s %s\n", ansi_highlight_green(), special_glyph(SPECIAL_GLYPH_CHECK_MARK), ansi_normal(), flags[i].name);
else
printf("%s%s%s %s\n", ansi_highlight_red(), special_glyph(SPECIAL_GLYPH_CROSS_MARK), ansi_normal(), flags[i].name);
}
print_yes_no_line(false, have_bootloader_esp_uuid, "Boot loader sets ESP partition information");
if (have_bootloader_esp_uuid && !sd_id128_equal(esp_uuid, bootloader_esp_uuid))
printf("WARNING: The boot loader reports different ESP UUID then detected!\n");
if (stub)
printf(" Stub: %s\n", stub);

View File

@ -13,6 +13,7 @@
#include "device-util.h"
#include "dirent-util.h"
#include "dissect-image.h"
#include "dropin.h"
#include "efi-loader.h"
#include "fd-util.h"
#include "fileio.h"
@ -39,6 +40,70 @@ static bool arg_enabled = true;
static bool arg_root_enabled = true;
static int arg_root_rw = -1;
static int open_parent_block_device(dev_t devnum, int *ret_fd) {
_cleanup_(sd_device_unrefp) sd_device *d = NULL;
const char *name, *devtype, *node;
sd_device *parent;
dev_t pn;
int fd, r;
assert(ret_fd);
r = sd_device_new_from_devnum(&d, 'b', devnum);
if (r < 0)
return log_debug_errno(r, "Failed to open device: %m");
if (sd_device_get_devname(d, &name) < 0) {
r = sd_device_get_syspath(d, &name);
if (r < 0) {
log_device_debug_errno(d, r, "Device %u:%u does not have a name, ignoring: %m",
major(devnum), minor(devnum));
return 0;
}
}
r = sd_device_get_parent(d, &parent);
if (r < 0) {
log_device_debug_errno(d, r, "Not a partitioned device, ignoring: %m");
return 0;
}
/* Does it have a devtype? */
r = sd_device_get_devtype(parent, &devtype);
if (r < 0) {
log_device_debug_errno(parent, r, "Parent doesn't have a device type, ignoring: %m");
return 0;
}
/* Is this a disk or a partition? We only care for disks... */
if (!streq(devtype, "disk")) {
log_device_debug(parent, "Parent isn't a raw disk, ignoring.");
return 0;
}
/* Does it have a device node? */
r = sd_device_get_devname(parent, &node);
if (r < 0) {
log_device_debug_errno(parent, r, "Parent device does not have device node, ignoring: %m");
return 0;
}
log_device_debug(d, "Root device %s.", node);
r = sd_device_get_devnum(parent, &pn);
if (r < 0) {
log_device_debug_errno(parent, r, "Parent device is not a proper block device, ignoring: %m");
return 0;
}
fd = open(node, O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0)
return log_error_errno(errno, "Failed to open %s: %m", node);
*ret_fd = fd;
return 1;
}
static int add_cryptsetup(const char *id, const char *what, bool rw, bool require, char **device) {
_cleanup_free_ char *e = NULL, *n = NULL, *d = NULL, *id_escaped = NULL, *what_escaped = NULL;
_cleanup_fclose_ FILE *f = NULL;
@ -103,28 +168,25 @@ static int add_cryptsetup(const char *id, const char *what, bool rw, bool requir
if (r < 0)
return r;
if (require) {
const char *dmname;
const char *dmname;
dmname = strjoina("dev-mapper-", e, ".device");
if (require) {
r = generator_add_symlink(arg_dest, "cryptsetup.target", "requires", n);
if (r < 0)
return r;
dmname = strjoina("dev-mapper-", e, ".device");
r = generator_add_symlink(arg_dest, dmname, "requires", n);
if (r < 0)
return r;
}
p = strjoina(arg_dest, "/dev-mapper-", e, ".device.d/50-job-timeout-sec-0.conf");
mkdir_parents_label(p, 0755);
r = write_string_file(p,
"# Automatically generated by systemd-gpt-auto-generator\n\n"
"[Unit]\n"
"JobTimeoutSec=0\n",
WRITE_STRING_FILE_CREATE); /* the binary handles timeouts anyway */
r = write_drop_in_format(arg_dest, dmname, 50, "job-timeout",
"# Automatically generated by systemd-gpt-auto-generator\n\n"
"[Unit]\n"
"JobTimeoutSec=0"); /* the binary handles timeouts anyway */
if (r < 0)
return log_error_errno(r, "Failed to write device drop-in: %m");
log_warning_errno(r, "Failed to write device timeout drop-in, ignoring: %m");
if (device) {
char *ret;
@ -162,7 +224,7 @@ static int add_mount(
assert(where);
assert(description);
log_debug("Adding %s: %s %s", where, what, strna(fstype));
log_debug("Adding %s: %s fstype=%s", where, what, fstype ?: "(any)");
if (streq_ptr(fstype, "crypto_LUKS")) {
@ -530,67 +592,62 @@ static int add_root_rw(DissectedPartition *p) {
return 0;
}
static int open_parent_devno(dev_t devnum, int *ret) {
_cleanup_(sd_device_unrefp) sd_device *d = NULL;
const char *name, *devtype, *node;
sd_device *parent;
dev_t pn;
int fd, r;
#if ENABLE_EFI
static int add_root_cryptsetup(void) {
assert(ret);
/* If a device /dev/gpt-auto-root-luks appears, then make it pull in systemd-cryptsetup-root.service, which
* sets it up, and causes /dev/gpt-auto-root to appear which is all we are looking for. */
r = sd_device_new_from_devnum(&d, 'b', devnum);
if (r < 0)
return log_debug_errno(r, "Failed to open device: %m");
return add_cryptsetup("root", "/dev/gpt-auto-root-luks", true, false, NULL);
}
#endif
if (sd_device_get_devname(d, &name) < 0) {
r = sd_device_get_syspath(d, &name);
if (r < 0) {
log_device_debug_errno(d, r, "Device %u:%u does not have a name, ignoring: %m", major(devnum), minor(devnum));
static int add_root_mount(void) {
#if ENABLE_EFI
int r;
if (!is_efi_boot()) {
log_debug("Not a EFI boot, not creating root mount.");
return 0;
}
r = efi_loader_get_device_part_uuid(NULL);
if (r == -ENOENT) {
log_notice("EFI loader partition unknown, exiting.\n"
"(The boot loader did not set EFI variable LoaderDevicePartUUID.)");
return 0;
} else if (r < 0)
return log_error_errno(r, "Failed to read ESP partition UUID: %m");
/* OK, we have an ESP partition, this is fantastic, so let's
* wait for a root device to show up. A udev rule will create
* the link for us under the right name. */
if (in_initrd()) {
r = generator_write_initrd_root_device_deps(arg_dest, "/dev/gpt-auto-root");
if (r < 0)
return 0;
}
r = add_root_cryptsetup();
if (r < 0)
return r;
}
r = sd_device_get_parent(d, &parent);
if (r < 0) {
log_device_debug_errno(d, r, "Not a partitioned device, ignoring: %m");
return 0;
}
/* Note that we do not need to enable systemd-remount-fs.service here. If
* /etc/fstab exists, systemd-fstab-generator will pull it in for us. */
/* Does it have a devtype? */
r = sd_device_get_devtype(parent, &devtype);
if (r < 0) {
log_device_debug_errno(parent, r, "Parent doesn't have a device type, ignoring: %m");
return 0;
}
/* Is this a disk or a partition? We only care for disks... */
if (!streq(devtype, "disk")) {
log_device_debug(parent, "Parent isn't a raw disk, ignoring.");
return 0;
}
/* Does it have a device node? */
r = sd_device_get_devname(parent, &node);
if (r < 0) {
log_device_debug_errno(parent, r, "Parent device does not have device node, ignoring: %m");
return 0;
}
log_device_debug(d, "Root device %s.", node);
r = sd_device_get_devnum(parent, &pn);
if (r < 0) {
log_device_debug_errno(parent, r, "Parent device is not a proper block device, ignoring: %m");
return 0;
}
fd = open(node, O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0)
return log_error_errno(errno, "Failed to open %s: %m", node);
*ret = fd;
return 1;
return add_mount(
"root",
"/dev/gpt-auto-root",
in_initrd() ? "/sysroot" : "/",
NULL,
arg_root_rw > 0,
NULL,
"Root Partition",
in_initrd() ? SPECIAL_INITRD_ROOT_FS_TARGET : SPECIAL_LOCAL_FS_TARGET);
#else
return 0;
#endif
}
static int enumerate_partitions(dev_t devnum) {
@ -598,7 +655,7 @@ static int enumerate_partitions(dev_t devnum) {
_cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
int r, k;
r = open_parent_devno(devnum, &fd);
r = open_parent_block_device(devnum, &fd);
if (r <= 0)
return r;
@ -649,105 +706,6 @@ static int enumerate_partitions(dev_t devnum) {
return r;
}
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
int r;
assert(key);
if (proc_cmdline_key_streq(key, "systemd.gpt_auto") ||
proc_cmdline_key_streq(key, "rd.systemd.gpt_auto")) {
r = value ? parse_boolean(value) : 1;
if (r < 0)
log_warning_errno(r, "Failed to parse gpt-auto switch \"%s\", ignoring: %m", value);
else
arg_enabled = r;
} else if (proc_cmdline_key_streq(key, "root")) {
if (proc_cmdline_value_missing(key, value))
return 0;
/* Disable root disk logic if there's a root= value
* specified (unless it happens to be "gpt-auto") */
arg_root_enabled = streq(value, "gpt-auto");
} else if (proc_cmdline_key_streq(key, "roothash")) {
if (proc_cmdline_value_missing(key, value))
return 0;
/* Disable root disk logic if there's roothash= defined (i.e. verity enabled) */
arg_root_enabled = false;
} else if (proc_cmdline_key_streq(key, "rw") && !value)
arg_root_rw = true;
else if (proc_cmdline_key_streq(key, "ro") && !value)
arg_root_rw = false;
return 0;
}
#if ENABLE_EFI
static int add_root_cryptsetup(void) {
/* If a device /dev/gpt-auto-root-luks appears, then make it pull in systemd-cryptsetup-root.service, which
* sets it up, and causes /dev/gpt-auto-root to appear which is all we are looking for. */
return add_cryptsetup("root", "/dev/gpt-auto-root-luks", true, false, NULL);
}
#endif
static int add_root_mount(void) {
#if ENABLE_EFI
int r;
if (!is_efi_boot()) {
log_debug("Not a EFI boot, not creating root mount.");
return 0;
}
r = efi_loader_get_device_part_uuid(NULL);
if (r == -ENOENT) {
log_debug("EFI loader partition unknown, exiting.");
return 0;
} else if (r < 0)
return log_error_errno(r, "Failed to read ESP partition UUID: %m");
/* OK, we have an ESP partition, this is fantastic, so let's
* wait for a root device to show up. A udev rule will create
* the link for us under the right name. */
if (in_initrd()) {
r = generator_write_initrd_root_device_deps(arg_dest, "/dev/gpt-auto-root");
if (r < 0)
return 0;
r = add_root_cryptsetup();
if (r < 0)
return r;
}
/* Note that we do not need to enable systemd-remount-fs.service here. If
* /etc/fstab exists, systemd-fstab-generator will pull it in for us. */
return add_mount(
"root",
"/dev/gpt-auto-root",
in_initrd() ? "/sysroot" : "/",
NULL,
arg_root_rw > 0,
NULL,
"Root Partition",
in_initrd() ? SPECIAL_INITRD_ROOT_FS_TARGET : SPECIAL_LOCAL_FS_TARGET);
#else
return 0;
#endif
}
static int add_mounts(void) {
dev_t devno;
int r;
@ -785,6 +743,50 @@ static int add_mounts(void) {
return enumerate_partitions(devno);
}
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
int r;
assert(key);
if (proc_cmdline_key_streq(key, "systemd.gpt_auto") ||
proc_cmdline_key_streq(key, "rd.systemd.gpt_auto")) {
r = value ? parse_boolean(value) : 1;
if (r < 0)
log_warning_errno(r, "Failed to parse gpt-auto switch \"%s\", ignoring: %m", value);
else
arg_enabled = r;
} else if (proc_cmdline_key_streq(key, "root")) {
if (proc_cmdline_value_missing(key, value))
return 0;
/* Disable root disk logic if there's a root= value
* specified (unless it happens to be "gpt-auto") */
if (!streq(value, "gpt-auto")) {
arg_root_enabled = false;
log_debug("Disabling root partition auto-detection, root= is defined.");
}
} else if (proc_cmdline_key_streq(key, "roothash")) {
if (proc_cmdline_value_missing(key, value))
return 0;
/* Disable root disk logic if there's roothash= defined (i.e. verity enabled) */
arg_root_enabled = false;
} else if (proc_cmdline_key_streq(key, "rw") && !value)
arg_root_rw = true;
else if (proc_cmdline_key_streq(key, "ro") && !value)
arg_root_rw = false;
return 0;
}
static int run(const char *dest, const char *dest_early, const char *dest_late) {
int r, k;

View File

@ -51,7 +51,7 @@ TEST_NO_NSPAWN=1
Don't run tests under systemd-nspawn
TEST_NO_KVM=1
Disable QEMU KVM autodetection (may be necessary when you're trying to run the
Disable QEMU KVM auto-detection (may be necessary when you're trying to run the
*vanilla* QEMU and have both qemu and qemu-kvm installed)
TEST_NESTED_KVM=1