Merge pull request #27687 from keszybz/boot-efi-choices
gpt-auto-generator: rework/simplify logic for picking /efi or /boot
This commit is contained in:
@@ -30,25 +30,28 @@
|
||||
<title>Description</title>
|
||||
|
||||
<para><filename>systemd-gpt-auto-generator</filename> is a unit generator that automatically discovers
|
||||
root, <filename>/home/</filename>, <filename>/srv/</filename>, <filename>/var/</filename>,
|
||||
<filename>/var/tmp/</filename>, the EFI System Partition, the Extended Boot Loader Partition and swap
|
||||
the root partition, <filename>/home/</filename>, <filename>/srv/</filename>, <filename>/var/</filename>,
|
||||
<filename>/var/tmp/</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://uapi-group.org/specifications/specs/discoverable_partitions_specification">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
|
||||
configured (for example, listed in <citerefentry
|
||||
project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>), the
|
||||
units this generator creates are overridden, but additional implicit dependencies might be
|
||||
created.</para>
|
||||
tables (GPT). See <ulink url="https://uefi.org/specifications">UEFI Specification</ulink>, chapter 5 for
|
||||
more details. It implements the <ulink
|
||||
url="https://uapi-group.org/specifications/specs/discoverable_partitions_specification">Discoverable
|
||||
Partitions Specification</ulink>.</para>
|
||||
|
||||
<para>Note that this generator has no effect on non-GPT systems. It will also not create mount point
|
||||
configuration for directories which already contain files or if the mount point is explicitly configured
|
||||
in <citerefentry
|
||||
project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>. 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 autodetect the
|
||||
root partition. See the <ulink url="https://systemd.io/BOOT_LOADER_INTERFACE">Boot Loader
|
||||
Interface</ulink> for details.</para>
|
||||
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>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
|
||||
@@ -190,9 +193,10 @@
|
||||
listed in <filename>/etc/crypttab</filename> with a different device mapper device name.</para>
|
||||
|
||||
<para>When systemd is running in the initrd the <filename>/</filename> partition may be encrypted in LUKS
|
||||
format as well. In this case, a device mapper device is set up under the name <filename>/dev/mapper/root</filename>,
|
||||
and a <filename>sysroot.mount</filename> is set up that mounts the device under <filename>/sysroot</filename>.
|
||||
For more information, see <citerefentry><refentrytitle>bootup</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
|
||||
format as well. In this case, a device mapper device is set up under the name
|
||||
<filename>/dev/mapper/root</filename>, and a <filename>sysroot.mount</filename> is set up that mounts the
|
||||
device under <filename>/sysroot</filename>. For more information, see
|
||||
<citerefentry><refentrytitle>bootup</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
|
||||
</para>
|
||||
|
||||
<para>The root partition can be specified by symlinking <filename>/run/systemd/volatile-root</filename>
|
||||
@@ -200,22 +204,20 @@
|
||||
replaced by some form of volatile file system (overlayfs).
|
||||
</para>
|
||||
|
||||
<para>Mount and automount units for the EFI System Partition (ESP) are generated on EFI systems. The ESP
|
||||
is mounted to <filename>/boot/</filename> (except if an Extended Boot Loader partition exists, see
|
||||
below), unless a mount point directory <filename>/efi/</filename> exists, in which case it is mounted
|
||||
there. Since this generator creates an automount unit, the mount will only be activated on-demand, when
|
||||
accessed. On systems where <filename>/boot/</filename> (or <filename>/efi/</filename> if it exists) is an
|
||||
explicitly configured mount (for example, listed in <citerefentry
|
||||
project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>) or where
|
||||
the <filename>/boot/</filename> (or <filename>/efi/</filename>) mount point is non-empty, no mount units
|
||||
are generated.</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
|
||||
Specification</ulink>, it is made available at <filename>/boot/</filename>. This generator creates an
|
||||
automount unit; the mount will only be activated on-demand when accessed. The mount point will be created
|
||||
if necessary.</para>
|
||||
|
||||
<para>If the disk contains an Extended Boot Loader partition, as defined in the <ulink
|
||||
url="https://uapi-group.org/specifications/specs/boot_loader_specification">Boot Loader Specification</ulink>, it is made
|
||||
available at <filename>/boot/</filename> (by means of an automount point, similar to the ESP, see
|
||||
above). If both an EFI System Partition and an Extended Boot Loader partition exist the latter is
|
||||
preferably mounted to <filename>/boot/</filename>. Make sure to create both <filename>/efi/</filename>
|
||||
and <filename>/boot/</filename> to ensure both partitions are mounted.</para>
|
||||
<para>The ESP is mounted to <filename>/boot/</filename> if that directory exists and is not used for
|
||||
XBOOTLDR, and otherwise to <filename>/efi/</filename>. Same as for <filename>/boot/</filename>, an
|
||||
automount unit is used. The mount point will be created if necessary.</para>
|
||||
|
||||
<para>No configuration is created for mount points that are configured in <citerefentry
|
||||
project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry> or when
|
||||
the target directory contains files.</para>
|
||||
|
||||
<para>When using this generator in conjunction with btrfs file
|
||||
systems, make sure to set the correct default subvolumes on them,
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include "gpt.h"
|
||||
#include "image-policy.h"
|
||||
#include "initrd-util.h"
|
||||
#include "mkdir.h"
|
||||
#include "mountpoint-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
@@ -109,7 +108,7 @@ static int add_cryptsetup(
|
||||
|
||||
r = efi_stub_measured(LOG_WARNING);
|
||||
if (r == 0)
|
||||
log_debug("Will not measure volume key of volume '%s', because not booted via systemd-stub with measurements enabled.", id);
|
||||
log_debug("Will not measure volume key of volume '%s', not booted via systemd-stub with measurements enabled.", id);
|
||||
else if (r > 0) {
|
||||
if (!strextend_with_separator(&options, ",", "tpm2-measure-pcr=yes"))
|
||||
return log_oom();
|
||||
@@ -128,8 +127,7 @@ static int add_cryptsetup(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
const char *dmname;
|
||||
dmname = strjoina("dev-mapper-", e, ".device");
|
||||
const char *dmname = strjoina("dev-mapper-", e, ".device");
|
||||
|
||||
if (require) {
|
||||
r = generator_add_symlink(arg_dest, "cryptsetup.target", "requires", n);
|
||||
@@ -160,7 +158,8 @@ static int add_cryptsetup(
|
||||
|
||||
return 0;
|
||||
#else
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Partition is encrypted, but the project was compiled without libcryptsetup support");
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"Partition is encrypted, but systemd-gpt-auto-generator was compiled without libcryptsetup support");
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -280,12 +279,13 @@ static int add_mount(
|
||||
static int path_is_busy(const char *where) {
|
||||
int r;
|
||||
|
||||
assert(where);
|
||||
|
||||
/* already a mountpoint; generators run during reload */
|
||||
r = path_is_mount_point(where, NULL, AT_SYMLINK_FOLLOW);
|
||||
if (r > 0)
|
||||
return false;
|
||||
|
||||
/* the directory might not exist on a stateless system */
|
||||
/* The directory will be created by the mount or automount unit when it is started. */
|
||||
if (r == -ENOENT)
|
||||
return false;
|
||||
|
||||
@@ -294,13 +294,17 @@ static int path_is_busy(const char *where) {
|
||||
|
||||
/* not a mountpoint but it contains files */
|
||||
r = dir_is_empty(where, /* ignore_hidden_or_backup= */ false);
|
||||
if (r < 0)
|
||||
if (r == -ENOTDIR) {
|
||||
log_debug("\"%s\" is not a directory, ignoring.", where);
|
||||
return true;
|
||||
} else if (r < 0)
|
||||
return log_warning_errno(r, "Cannot check if \"%s\" is empty: %m", where);
|
||||
if (r > 0)
|
||||
return false;
|
||||
else if (r == 0) {
|
||||
log_debug("\"%s\" already populated, ignoring.", where);
|
||||
return true;
|
||||
}
|
||||
|
||||
log_debug("\"%s\" already populated, ignoring.", where);
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static int add_partition_mount(
|
||||
@@ -471,6 +475,45 @@ static int add_automount(
|
||||
return generator_add_symlink(arg_dest, SPECIAL_LOCAL_FS_TARGET, "wants", unit);
|
||||
}
|
||||
|
||||
static int slash_boot_in_fstab(void) {
|
||||
static int cache = -1;
|
||||
|
||||
if (cache >= 0)
|
||||
return cache;
|
||||
|
||||
cache = fstab_is_mount_point("/boot");
|
||||
if (cache < 0)
|
||||
return log_error_errno(cache, "Failed to parse fstab: %m");
|
||||
return cache;
|
||||
}
|
||||
|
||||
static int slash_efi_in_fstab(void) {
|
||||
static int cache = -1;
|
||||
|
||||
if (cache >= 0)
|
||||
return cache;
|
||||
|
||||
cache = fstab_is_mount_point("/efi");
|
||||
if (cache < 0)
|
||||
return log_error_errno(cache, "Failed to parse fstab: %m");
|
||||
return cache;
|
||||
}
|
||||
|
||||
static bool slash_boot_exists(void) {
|
||||
static int cache = -1;
|
||||
|
||||
if (cache >= 0)
|
||||
return cache;
|
||||
|
||||
if (access("/boot", F_OK) >= 0)
|
||||
return (cache = true);
|
||||
if (errno != ENOENT)
|
||||
log_error_errno(errno, "Failed to determine whether /boot/ exists, assuming no: %m");
|
||||
else
|
||||
log_debug_errno(errno, "/boot/: %m");
|
||||
return (cache = false);
|
||||
}
|
||||
|
||||
static int add_partition_xbootldr(DissectedPartition *p) {
|
||||
_cleanup_free_ char *options = NULL;
|
||||
int r;
|
||||
@@ -482,11 +525,11 @@ static int add_partition_xbootldr(DissectedPartition *p) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = fstab_is_mount_point("/boot");
|
||||
r = slash_boot_in_fstab();
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse fstab: %m");
|
||||
return r;
|
||||
if (r > 0) {
|
||||
log_debug("/boot specified in fstab, ignoring XBOOTLDR partition.");
|
||||
log_debug("/boot/ specified in fstab, ignoring XBOOTLDR partition.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -504,17 +547,18 @@ static int add_partition_xbootldr(DissectedPartition *p) {
|
||||
&options,
|
||||
/* ret_ms_flags= */ NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine default mount options for Boot Loader Partition: %m");
|
||||
return log_error_errno(r, "Failed to determine default mount options for /boot/: %m");
|
||||
|
||||
return add_automount("boot",
|
||||
p->node,
|
||||
"/boot",
|
||||
p->fstype,
|
||||
/* rw= */ true,
|
||||
/* growfs= */ false,
|
||||
options,
|
||||
"Boot Loader Partition",
|
||||
120 * USEC_PER_SEC);
|
||||
return add_automount(
|
||||
"boot",
|
||||
p->node,
|
||||
"/boot",
|
||||
p->fstype,
|
||||
/* rw= */ true,
|
||||
/* growfs= */ false,
|
||||
options,
|
||||
"Boot Loader Partition",
|
||||
120 * USEC_PER_SEC);
|
||||
}
|
||||
|
||||
#if ENABLE_EFI
|
||||
@@ -530,43 +574,42 @@ static int add_partition_esp(DissectedPartition *p, bool has_xbootldr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If /efi exists we'll use that. Otherwise we'll use /boot, as that's usually the better choice, but
|
||||
* only if there's no explicit XBOOTLDR partition around. */
|
||||
if (access("/efi", F_OK) < 0) {
|
||||
if (errno != ENOENT)
|
||||
return log_error_errno(errno, "Failed to determine whether /efi exists: %m");
|
||||
|
||||
/* Use /boot as fallback, but only if there's no XBOOTLDR partition and /boot exists */
|
||||
if (!has_xbootldr) {
|
||||
if (access("/boot", F_OK) < 0) {
|
||||
if (errno != ENOENT)
|
||||
return log_error_errno(errno, "Failed to determine whether /boot exists: %m");
|
||||
} else {
|
||||
/* If /boot/ is present, unused, and empty, we'll take that.
|
||||
* Otherwise, if /efi/ is unused and empty (or missing), we'll take that.
|
||||
* Otherwise, we do nothing.
|
||||
*/
|
||||
if (!has_xbootldr && slash_boot_exists()) {
|
||||
r = slash_boot_in_fstab();
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
r = path_is_busy("/boot");
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
esp_path = "/boot";
|
||||
id = "boot";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!esp_path)
|
||||
|
||||
if (!esp_path) {
|
||||
r = slash_efi_in_fstab();
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
return 0;
|
||||
|
||||
r = path_is_busy("/efi");
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
return 0;
|
||||
|
||||
esp_path = "/efi";
|
||||
if (!id)
|
||||
id = "efi";
|
||||
|
||||
/* We create an .automount which is not overridden by the .mount from the fstab generator. */
|
||||
r = fstab_is_mount_point(esp_path);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse fstab: %m");
|
||||
if (r > 0) {
|
||||
log_debug("%s specified in fstab, ignoring.", esp_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = path_is_busy(esp_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
return 0;
|
||||
|
||||
if (is_efi_boot()) {
|
||||
sd_id128_t loader_uuid;
|
||||
|
||||
@@ -595,17 +638,18 @@ static int add_partition_esp(DissectedPartition *p, bool has_xbootldr) {
|
||||
&options,
|
||||
/* ret_ms_flags= */ NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine default mount options for EFI System Partition: %m");
|
||||
return log_error_errno(r, "Failed to determine default mount options for %s: %m", esp_path);
|
||||
|
||||
return add_automount(id,
|
||||
p->node,
|
||||
esp_path,
|
||||
p->fstype,
|
||||
/* rw= */ true,
|
||||
/* growfs= */ false,
|
||||
options,
|
||||
"EFI System Partition Automount",
|
||||
120 * USEC_PER_SEC);
|
||||
return add_automount(
|
||||
id,
|
||||
p->node,
|
||||
esp_path,
|
||||
p->fstype,
|
||||
/* rw= */ true,
|
||||
/* growfs= */ false,
|
||||
options,
|
||||
"EFI System Partition Automount",
|
||||
120 * USEC_PER_SEC);
|
||||
}
|
||||
#else
|
||||
static int add_partition_esp(DissectedPartition *p, bool has_xbootldr) {
|
||||
|
||||
+50
-32
@@ -2019,6 +2019,27 @@ static int mount_root_tmpfs(const char *where, uid_t uid_shift, DissectImageFlag
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int mount_point_is_available(const char *where, const char *path, bool missing_ok) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
int r;
|
||||
|
||||
/* Check whether <path> is suitable as a mountpoint, i.e. is an empty directory
|
||||
* or does not exist at all (when missing_ok). */
|
||||
|
||||
r = chase(path, where, CHASE_PREFIX_ROOT, &p, NULL);
|
||||
if (r == -ENOENT)
|
||||
return missing_ok;
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to chase \"%s\": %m", path);
|
||||
|
||||
r = dir_is_empty(p, /* ignore_hidden_or_backup= */ false);
|
||||
if (r == -ENOTDIR)
|
||||
return false;
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to check directory \"%s\": %m", p);
|
||||
return true;
|
||||
}
|
||||
|
||||
int dissected_image_mount(
|
||||
DissectedImage *m,
|
||||
const char *where,
|
||||
@@ -2026,7 +2047,7 @@ int dissected_image_mount(
|
||||
uid_t uid_range,
|
||||
DissectImageFlags flags) {
|
||||
|
||||
int r, xbootldr_mounted;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(where);
|
||||
@@ -2110,45 +2131,42 @@ int dissected_image_mount(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
xbootldr_mounted = mount_partition(PARTITION_XBOOTLDR, m->partitions + PARTITION_XBOOTLDR, where, "/boot", uid_shift, uid_range, flags);
|
||||
if (xbootldr_mounted < 0)
|
||||
return xbootldr_mounted;
|
||||
int slash_boot_is_available;
|
||||
r = slash_boot_is_available = mount_point_is_available(where, "/boot", /* missing_ok = */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0) {
|
||||
r = mount_partition(PARTITION_XBOOTLDR, m->partitions + PARTITION_XBOOTLDR, where, "/boot", uid_shift, uid_range, flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
slash_boot_is_available = !r;
|
||||
}
|
||||
|
||||
if (m->partitions[PARTITION_ESP].found) {
|
||||
int esp_done = false;
|
||||
const char *esp_path = NULL;
|
||||
|
||||
/* Mount the ESP to /efi if it exists. If it doesn't exist, use /boot instead, but only if it
|
||||
* exists and is empty, and we didn't already mount the XBOOTLDR partition into it. */
|
||||
/* Mount the ESP to /boot/ if it exists and is empty and we didn't already mount the XBOOTLDR
|
||||
* partition into it. Otherwise, use /efi instead, but only if it exists and is empty. */
|
||||
|
||||
r = chase("/efi", where, CHASE_PREFIX_ROOT, NULL, NULL);
|
||||
if (r < 0) {
|
||||
if (r != -ENOENT)
|
||||
if (slash_boot_is_available) {
|
||||
r = mount_point_is_available(where, "/boot", /* missing_ok = */ false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* /efi doesn't exist. Let's see if /boot is suitable then */
|
||||
|
||||
if (!xbootldr_mounted) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
|
||||
r = chase("/boot", where, CHASE_PREFIX_ROOT, &p, NULL);
|
||||
if (r < 0) {
|
||||
if (r != -ENOENT)
|
||||
return r;
|
||||
} else if (dir_is_empty(p, /* ignore_hidden_or_backup= */ false) > 0) {
|
||||
/* It exists and is an empty directory. Let's mount the ESP there. */
|
||||
r = mount_partition(PARTITION_ESP, m->partitions + PARTITION_ESP, where, "/boot", uid_shift, uid_range, flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
esp_done = true;
|
||||
}
|
||||
}
|
||||
if (r > 0)
|
||||
esp_path = "/boot";
|
||||
}
|
||||
|
||||
if (!esp_done) {
|
||||
/* OK, let's mount the ESP now to /efi (possibly creating the dir if missing) */
|
||||
if (!esp_path) {
|
||||
r = mount_point_is_available(where, "/efi", /* missing_ok = */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
esp_path = "/efi";
|
||||
}
|
||||
|
||||
r = mount_partition(PARTITION_ESP, m->partitions + PARTITION_ESP, where, "/efi", uid_shift, uid_range, flags);
|
||||
if (esp_path) {
|
||||
/* OK, let's mount the ESP now (possibly creating the dir if missing) */
|
||||
r = mount_partition(PARTITION_ESP, m->partitions + PARTITION_ESP, where, esp_path, uid_shift, uid_range, flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user