1
0
mirror of https://github.com/systemd/systemd.git synced 2025-03-14 04:58:28 +03:00

udev-builtin-blkid: use loopback block device 'ref' field fo determining gpt-auto whole block device

So far the gpt-auto logic only looked for the partition table of devices
that the ESP/XBOOTLDR partition used to boot was on. This works great
for local boots, but is more problematic if we boot a UKI via UEFI HTTP
boot, because there is no ESP in play in that case.

Let's introduce an alternative to communicate the intended default root
disk to cover for this situation: any loopback block device whose
backing file field (i.e. the userspace controlled freeform field we use
for /dev/disk/by-loop-ref/ naming) is set to "rootdisk" will be consider
for gpt-auto will be consider for gpt-auto.

With this in place we should have nice automatic behaviour:

1. If we are booted locally we'll get the ESP/XBOOTLDR data, and derive
   the root disk from that.

2. If we are booted via UEFI HTTP boot we expect that the caller makes
   the loopback device appear with the right loop-ref identifier, and
   then will use that.
This commit is contained in:
Lennart Poettering 2025-02-10 22:24:52 +01:00
parent 3d49850096
commit f8825c1364
3 changed files with 113 additions and 43 deletions

View File

@ -50,19 +50,49 @@
<para>If the units this generator creates are overridden, for example by units in directories with higher
precedence, drop-ins and additional dependencies created by this generator might still be used.</para>
<para>This generator will only look for the root partition on the same physical disk where the EFI System
Partition (ESP) is located. Note that support from the boot loader is required: the EFI variable
<varname>LoaderDevicePartUUID</varname> of the <constant>4a67b082-0a4c-41cf-b6c7-440b29bb8c4f</constant>
vendor UUID 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 detect the root
partition. See the <ulink url="https://systemd.io/BOOT_LOADER_INTERFACE">Boot Loader Interface</ulink>
for details.</para>
<para>When run in the initial RAM disk (initrd) this generator can automatically search for the root file
system. Specifically:
<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.
<itemizedlist>
<listitem><para>It will look for the root partition on the same physical disk where the EFI System
Partition (ESP) is located. Note that support from the boot loader is required for this to work: the
EFI variable <varname>LoaderDevicePartUUID</varname> of the
<constant>4a67b082-0a4c-41cf-b6c7-440b29bb8c4f</constant> vendor UUID is used to determine from which
partition (and hence disk) the system was booted. If the boot loader does not set this variable, this
generator will not be able to detect the root partition. See the <ulink
url="https://systemd.io/BOOT_LOADER_INTERFACE">Boot Loader Interface</ulink> for
details.</para></listitem>
<listitem><para>Alternatively, it will look for the root file system on a loopback block device whose
<literal>.lo_name</literal> field is set to one of the literal strings <literal>rootdisk</literal> or
<literal>rootdisk.raw</literal>. This field can be set via <command>losetup</command>'s
<option>--loop-ref=</option> string. For images downloaded via
<citerefentry><refentrytitle>systemd-import-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
make sure to set the <literal>blockdev</literal> option and set the local name string to
<literal>rootdisk</literal> to achieve this effect. Note that discovery of the root file system on
loopback block devices like this is only done if <literal>root=gpt-auto</literal> is specified
explicitly on the kernel command line, unlike the discovery based on the boot loader reported ESP which
is also enabled if no <literal>root=</literal> parameter is specified at all. (The latter relies on
<command>systemd-udevd.service</command>'s <filename>/dev/gpt-auto-root</filename> block device symlink
generation).</para></listitem>
</itemizedlist>
</para>
<para>When run on the host system (i.e. after successfully transitioning out of the initrd into the root
filesystem) this generator will look for all other partitions on the same physical disk as the root
partition. For this discovery, boot loader support is not required. Moreover, it is not required that the
root partition was automatically discovered by the initrd (as described above) for the discovery of the
non-root file partitions to take place. Or in other words: automatic discovery of the root file system
and of the non-root file systems are independent operations, that do not rely on each other, and are done
during two distinct phases of the boot process (one in the initrd, the other after). 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>The root partition can be configured explicitly by symlinking
<filename>/run/systemd/volatile-root</filename> to <filename>/dev/block/$major:$minor</filename>. This is
especially useful if the root mount has been replaced by some form of volatile file system
(overlayfs).</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>
@ -225,11 +255,6 @@
<citerefentry><refentrytitle>bootup</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
</para>
<para>The root partition can be specified by symlinking <filename>/run/systemd/volatile-root</filename>
to <filename>/dev/block/$major:$minor</filename>. This is especially useful if the root mount has been
replaced by some form of volatile file system (overlayfs).
</para>
<para>Mount and automount units for the EFI System Partition (ESP) and Extended Boot Loader Partition
(XBOOTLDR) are generated on EFI systems. If the disk contains an XBOOTLDR partition, as defined in the
<ulink url="https://uapi-group.org/specifications/specs/boot_loader_specification">Boot Loader
@ -297,10 +322,14 @@
<term><varname>rootflags=</varname></term>
<listitem><para>When <varname>root=</varname> is used with the special value
<literal>gpt-auto</literal> (or if the parameter is not used at all), automatic discovery of the root
<literal>gpt-auto</literal>, full automatic discovery of the root
partition based on the GPT partition type is enabled. Any other value disables this
logic.</para>
<para>If <varname>root=</varname> is not specified at all on the kernel command line automatic
discovery of the root partition via the boot loader reported ESP is also enabled, however in this
case discovery based on the loopback block device <literal>.lo_name</literal> field is not enabled.</para>
<para>The <varname>rootfstype=</varname> and <varname>rootflags=</varname> are used to select the
file system type and options when the root file system is automatically discovered.</para>

View File

@ -125,6 +125,17 @@
device (via <filename>systemd-loop@.service</filename>) after completion. This permits booting
from downloaded disk images. This is only supported for <literal>raw</literal> disk images.</para>
<para>Note when this option is used with the purpose of mounting a disk image conforming to the
<ulink url="https://uapi-group.org/specifications/specs/discoverable_disk_image/">Discoverable
Disk Image Specification</ulink> as root file system, and the automatic GPT partition discovery
logic as implemented by
<citerefentry><refentrytitle>systemd-gpt-auto-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
shall process it, it's essential to specify <literal>rootdisk</literal> as the local name for the
import. Moreover, <literal>root=gpt-auto</literal> must be specified on the kernel command line
explicitly. Also, prefix the <literal>systemd.pull=</literal> commmand line option with
<literal>rd.</literal> to ensure it is executed in the initial RAM disk (initrd) already, also
see below.</para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
@ -235,12 +246,14 @@
<example>
<title>Boot into disk image (raw), with URL derived from UEFI HTTP network booting</title>
<programlisting>rd.systemd.pull=raw,machine,verify=no,blockdev,bootorigin:rootdisk:image.raw.xz root=/dev/disk/by-loop-ref/rootdisk.raw-part2</programlisting>
<programlisting>rd.systemd.pull=raw,machine,verify=no,blockdev,bootorigin:rootdisk:image.raw.xz root=gpt-auto</programlisting>
<para>This is similar to the previous example, but this time the source URL is automatically derived
from the UEFI HTTP network boot URL. For example, if an UKI is booted from an URL
<literal>http://example.com/image.efi</literal> this would result in a root disk being downloaded from
<literal>http://example.com/image.raw.xz</literal>.</para>
<literal>http://example.com/image.raw.xz</literal>. Moreover this uses the
<citerefentry><refentrytitle>systemd-gpt-auto-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
logic to mount the root file system from the disk image.</para>
</example>
<example>
@ -263,6 +276,7 @@
<member><citerefentry><refentrytitle>systemd.system-credentials</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>importctl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-loop@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-gpt-auto-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
</simplelist></para>
</refsect1>
</refentry>

View File

@ -125,12 +125,13 @@ static void print_property(UdevEvent *event, const char *name, const char *value
}
}
static int find_gpt_root(UdevEvent *event, blkid_probe pr) {
static int find_gpt_root(UdevEvent *event, blkid_probe pr, const char *loop_backing_fname) {
#if defined(SD_GPT_ROOT_NATIVE) && ENABLE_EFI
sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
sd_id128_t esp_or_xbootldr = SD_ID128_NULL;
_cleanup_free_ char *root_label = NULL;
bool found_esp_or_xbootldr = false;
bool found_esp_or_xbootldr = false, need_esp_or_xbootldr;
sd_id128_t root_id = SD_ID128_NULL;
int r;
@ -138,13 +139,42 @@ static int find_gpt_root(UdevEvent *event, blkid_probe pr) {
assert(pr);
/* Iterate through the partitions on this disk, and see if the UEFI ESP or XBOOTLDR partition we
* booted from is on it. If so, find the first root disk, and add a property indicating its partition
* UUID. */
* booted from is on it. If so, find the newest root partition, and add a property indicating its
* partition UUID. We also do this if we are dealing with a loopback block device whose "backing
* filename" field is set to the string "root". In the latter case we do not search for ESP or
* XBOOTLDR. */
if (!device_is_devtype(dev, "disk")) {
log_device_debug(dev, "Skipping GPT root logic on partition block device.");
return 0;
}
r = efi_loader_get_device_part_uuid(&esp_or_xbootldr);
if (r < 0) {
if (r != -ENOENT && !ERRNO_IS_NOT_SUPPORTED(r))
return log_debug_errno(r, "Unable to determine loader partition UUID: %m");
log_device_debug(dev, "No loader partition UUID EFI variable set, not using partition data for search for default root block device.");
/* NB: if an ESP/xbootldr field is set, we always use that. We do this in order to guarantee
* systematic behaviour. */
if (!STRPTR_IN_SET(loop_backing_fname, "rootdisk", "rootdisk.raw")) {
log_device_debug(dev, "Device is not a loopback block device with reference string 'root', not considering block device as default root block device.");
return 0;
}
/* OK, we have now sufficiently identified this device as the right root "whole" device,
* hence no need to bother with searching for ESP/XBOOTLDR */
need_esp_or_xbootldr = false;
} else
/* We now know the the ESP/xbootldr UUID, but we cannot be sure yet it's on this block
* device, hence look for it among partitions now */
need_esp_or_xbootldr = true;
errno = 0;
blkid_partlist pl = blkid_probe_get_partitions(pr);
if (!pl)
return errno_or_else(ENOMEM);
return log_device_debug_errno(dev, errno_or_else(ENOMEM), "Failed to probe partitions: %m");
int nvals = blkid_partlist_numof_partitions(pl);
for (int i = 0; i < nvals; i++) {
@ -158,27 +188,21 @@ static int find_gpt_root(UdevEvent *event, blkid_probe pr) {
r = blkid_partition_get_uuid_id128(pp, &id);
if (r < 0) {
log_debug_errno(r, "Failed to get partition UUID, ignoring: %m");
log_device_debug_errno(dev, r, "Failed to get partition UUID, ignoring: %m");
continue;
}
r = blkid_partition_get_type_id128(pp, &type);
if (r < 0) {
log_debug_errno(r, "Failed to get partition type UUID, ignoring: %m");
log_device_debug_errno(dev, r, "Failed to get partition type UUID, ignoring: %m");
continue;
}
label = blkid_partition_get_name(pp); /* returns NULL if empty */
if (sd_id128_in_set(type, SD_GPT_ESP, SD_GPT_XBOOTLDR)) {
sd_id128_t esp_or_xbootldr;
if (need_esp_or_xbootldr && sd_id128_in_set(type, SD_GPT_ESP, SD_GPT_XBOOTLDR)) {
/* We found an ESP or XBOOTLDR, let's see if it matches the ESP/XBOOTLDR we booted from. */
r = efi_loader_get_device_part_uuid(&esp_or_xbootldr);
if (r < 0)
return r;
if (sd_id128_equal(id, esp_or_xbootldr))
found_esp_or_xbootldr = true;
@ -195,17 +219,20 @@ static int find_gpt_root(UdevEvent *event, blkid_probe pr) {
if (sd_id128_is_null(root_id) || strverscmp_improved(label, root_label) > 0) {
root_id = id;
r = free_and_strdup(&root_label, label);
if (r < 0)
return r;
if (free_and_strdup(&root_label, label) < 0)
return log_oom_debug();
}
}
}
/* We found the ESP/XBOOTLDR on this disk, and also found a root partition, nice! Let's export its
* UUID */
if (found_esp_or_xbootldr && !sd_id128_is_null(root_id))
udev_builtin_add_property(event, "ID_PART_GPT_AUTO_ROOT_UUID", SD_ID128_TO_UUID_STRING(root_id));
if (!need_esp_or_xbootldr || found_esp_or_xbootldr) {
/* We found the ESP/XBOOTLDR on this disk (or we didn't need it) */
udev_builtin_add_property(event, "ID_PART_GPT_AUTO_ROOT_DISK", "1");
/* We found a root partition, nice! Let's export its UUID. */
if (!sd_id128_is_null(root_id))
udev_builtin_add_property(event, "ID_PART_GPT_AUTO_ROOT_UUID", SD_ID128_TO_UUID_STRING(root_id));
}
#endif
return 0;
@ -437,9 +464,6 @@ static int builtin_blkid(UdevEvent *event, int argc, char *argv[]) {
udev_builtin_add_property(event, "ID_PART_GPT_AUTO_ROOT", "1");
}
if (is_gpt)
find_gpt_root(event, pr);
r = read_loopback_backing_inode(
dev,
fd,
@ -466,6 +490,9 @@ static int builtin_blkid(UdevEvent *event, int argc, char *argv[]) {
}
}
if (is_gpt)
find_gpt_root(event, pr, backing_fname);
return 0;
}