From 57d2db22de705dec0f03d7d4d2f27171a935701d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 6 Feb 2019 17:48:21 +0100 Subject: [PATCH 01/48] env-file: (void)ify an unlink() call --- src/basic/env-file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/basic/env-file.c b/src/basic/env-file.c index 7f10f9ad39..a1f1308a54 100644 --- a/src/basic/env-file.c +++ b/src/basic/env-file.c @@ -559,6 +559,6 @@ int write_env_file(const char *fname, char **l) { r = -errno; } - unlink(p); + (void) unlink(p); return r; } From 71f514169b14e2bd67e9465f132bae502b51af98 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 23 Jan 2019 16:08:55 +0100 Subject: [PATCH 02/48] fs-util: add new helper syncfs_path() --- src/basic/fs-util.c | 15 +++++++++++++++ src/basic/fs-util.h | 2 ++ 2 files changed, 17 insertions(+) diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index 3ff8615797..0d631093b2 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -1330,6 +1330,21 @@ int fsync_path_at(int at_fd, const char *path) { return 0; } +int syncfs_path(int atfd, const char *path) { + _cleanup_close_ int fd = -1; + + assert(path); + + fd = openat(atfd, path, O_CLOEXEC|O_RDONLY|O_NONBLOCK); + if (fd < 0) + return -errno; + + if (syncfs(fd) < 0) + return -errno; + + return 0; +} + int open_parent(const char *path, int flags, mode_t mode) { _cleanup_free_ char *parent = NULL; int fd; diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h index 7ad030be5d..9c9044669d 100644 --- a/src/basic/fs-util.h +++ b/src/basic/fs-util.h @@ -108,4 +108,6 @@ int unlinkat_deallocate(int fd, const char *name, int flags); int fsync_directory_of_file(int fd); int fsync_path_at(int at_fd, const char *path); +int syncfs_path(int atfd, const char *path); + int open_parent(const char *path, int flags, mode_t mode); From feb41f1ffe3441a4249241b412351a1ffc6ddbc6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 28 Jan 2019 17:34:17 +0100 Subject: [PATCH 03/48] bootspec: update log message, to indicate the error is ignored --- src/shared/bootspec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index 7e276f1edf..410930e9cd 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -115,7 +115,7 @@ static int boot_entry_load(const char *path, BootEntry *entry) { else if (streq(field, "devicetree")) r = free_and_strdup(&tmp.device_tree, p); else { - log_notice("%s:%u: Unknown line \"%s\"", path, line, field); + log_notice("%s:%u: Unknown line \"%s\", ignoring.", path, line, field); continue; } if (r < 0) @@ -203,7 +203,7 @@ static int boot_loader_read_conf(const char *path, BootConfig *config) { else if (streq(field, "console-mode")) r = free_and_strdup(&config->console_mode, p); else { - log_notice("%s:%u: Unknown line \"%s\"", path, line, field); + log_notice("%s:%u: Unknown line \"%s\", ignoring.", path, line, field); continue; } if (r < 0) From 46bba8a51cddc966a3ec853a7e4fbae8e58c5c77 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 28 Jan 2019 17:32:04 +0100 Subject: [PATCH 04/48] bootspec: use SYNTHETIC_ERRNO() at one more place --- src/shared/bootspec.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index 410930e9cd..c60627e24c 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -50,10 +50,8 @@ static int boot_entry_load(const char *path, BootEntry *entry) { assert(entry); c = endswith_no_case(path, ".conf"); - if (!c) { - log_error("Invalid loader entry filename: %s", path); - return -EINVAL; - } + if (!c) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry filename: %s", path); b = basename(path); tmp.id = strndup(b, c - b); From 59ba6d0c1747270716a3c8e7877857b203beab38 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 23 Jan 2019 16:59:57 +0100 Subject: [PATCH 05/48] dissect: use SYNTHETIC_ERRNO() where appropriate --- src/shared/dissect-image.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index d340487025..94df2fe255 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -343,10 +343,8 @@ int dissect_image( errno = 0; r = blkid_do_safeprobe(b); - if (IN_SET(r, -2, 1)) { - log_debug("Failed to identify any partition table."); - return -ENOPKG; - } + if (IN_SET(r, -2, 1)) + return log_debug_errno(SYNTHETIC_ERRNO(ENOPKG), "Failed to identify any partition table."); if (r != 0) return -errno ?: -EIO; From 78d5d4ed77b53ae1aab49a9e5e0b6949a875db68 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 28 Jan 2019 17:32:46 +0100 Subject: [PATCH 06/48] bootctl: use SYNTHETIC_ERRNO() where appropriate --- src/boot/bootctl.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index dc2fd96628..1dbe00adb6 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -112,8 +112,7 @@ static int get_file_version(int fd, char **v) { e = memmem(s, st.st_size - (s - buf), " ####", 5); if (!e || e - s < 3) { - log_error("Malformed version string."); - r = -EINVAL; + r = log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Malformed version string."); goto finish; } @@ -386,10 +385,8 @@ static int version_check(int fd_from, const char *from, int fd_to, const char *t "Skipping \"%s\", since it's owned by another boot loader.", to); - if (compare_version(a, b) < 0) { - log_warning("Skipping \"%s\", since a newer boot loader version exists already.", to); - return -ESTALE; - } + if (compare_version(a, b) < 0) + return log_warning_errno(SYNTHETIC_ERRNO(ESTALE), "Skipping \"%s\", since a newer boot loader version exists already.", to); return 0; } From c4ba5b516d03ec0973b5c4ad58e2735d96b1bd83 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 28 Jan 2019 17:33:08 +0100 Subject: [PATCH 07/48] bootctl: safety check for regular file when reading EFI images --- src/boot/bootctl.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index 1dbe00adb6..2424aa011f 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -96,6 +96,10 @@ static int get_file_version(int fd, char **v) { if (fstat(fd, &st) < 0) return log_error_errno(errno, "Failed to stat EFI binary: %m"); + r = stat_verify_regular(&st); + if (r < 0) + return log_error_errno(errno, "EFI binary is not a regular file: %m"); + if (st.st_size < 27) { *v = NULL; return 0; From 7b5de295692926f1356ea078a9534b2b5a8ea798 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 22 Jan 2019 20:57:11 +0100 Subject: [PATCH 08/48] gpt: add definition for boot loader spec partition As listed in the boot loader spec since a long time: https://systemd.io/BOOT_LOADER_SPECIFICATION#technical-details --- src/shared/gpt.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/shared/gpt.h b/src/shared/gpt.h index fd953fabc7..31e01bd5a5 100644 --- a/src/shared/gpt.h +++ b/src/shared/gpt.h @@ -15,6 +15,7 @@ #define GPT_ROOT_ARM_64 SD_ID128_MAKE(b9,21,b0,45,1d,f0,41,c3,af,44,4c,6f,28,0d,3f,ae) #define GPT_ROOT_IA64 SD_ID128_MAKE(99,3d,8d,3d,f8,0e,42,25,85,5a,9d,af,8e,d7,ea,97) #define GPT_ESP SD_ID128_MAKE(c1,2a,73,28,f8,1f,11,d2,ba,4b,00,a0,c9,3e,c9,3b) +#define GPT_XBOOTLDR SD_ID128_MAKE(bc,13,c2,ff,59,e6,42,62,a3,52,b2,75,fd,6f,71,72) #define GPT_SWAP SD_ID128_MAKE(06,57,fd,6d,a4,ab,43,c4,84,e5,09,33,c8,4b,4f,4f) #define GPT_HOME SD_ID128_MAKE(93,3a,c7,e1,2e,b4,4f,13,b8,44,0e,14,e2,ae,f9,15) #define GPT_SRV SD_ID128_MAKE(3b,8f,84,25,20,e0,4f,3b,90,7f,1a,25,a7,6f,98,e8) From a8c47660bbbcc2ffc483beaa17d5c806f73849b3 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 23 Jan 2019 11:34:31 +0100 Subject: [PATCH 09/48] dissect: automatically detect boot loader spec $BOOT partition The boot loader spec supports two places to store boot loader configuration: the ESP and a generic replacement for it in case the ESP is not available or not suitable. Let's look for both. --- src/shared/dissect-image.c | 65 +++++++++++++++++++++++++++++++------- src/shared/dissect-image.h | 1 + 2 files changed, 54 insertions(+), 12 deletions(-) diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 94df2fe255..2dbc0bdb3d 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -495,6 +495,14 @@ int dissect_image( designator = PARTITION_ESP; fstype = "vfat"; + + } else if (sd_id128_equal(type_id, GPT_XBOOTLDR)) { + + if (pflags & GPT_FLAG_NO_AUTO) + continue; + + designator = PARTITION_XBOOTLDR; + rw = !(pflags & GPT_FLAG_READ_ONLY); } #ifdef GPT_ROOT_NATIVE else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE)) { @@ -610,21 +618,53 @@ int dissect_image( } else if (is_mbr) { - if (pflags != 0x80) /* Bootable flag */ - continue; + switch (blkid_partition_get_type(pp)) { - if (blkid_partition_get_type(pp) != 0x83) /* Linux partition */ - continue; + case 0x83: /* Linux partition */ - if (generic_node) - multiple_generic = true; - else { - generic_nr = nr; - generic_rw = true; - generic_node = strdup(node); - if (!generic_node) + if (pflags != 0x80) /* Bootable flag */ + continue; + + if (generic_node) + multiple_generic = true; + else { + generic_nr = nr; + generic_rw = true; + generic_node = strdup(node); + if (!generic_node) + return -ENOMEM; + } + + break; + + case 0xEA: { /* Boot Loader Spec extended $BOOT partition */ + _cleanup_free_ char *n = NULL; + sd_id128_t id = SD_ID128_NULL; + const char *sid; + + /* First one wins */ + if (m->partitions[PARTITION_XBOOTLDR].found) + continue; + + sid = blkid_partition_get_uuid(pp); + if (sid) + (void) sd_id128_from_string(sid, &id); + + n = strdup(node); + if (!n) return -ENOMEM; - } + + m->partitions[PARTITION_XBOOTLDR] = (DissectedPartition) { + .found = true, + .partno = nr, + .rw = true, + .architecture = _ARCHITECTURE_INVALID, + .node = TAKE_PTR(n), + .uuid = id, + }; + + break; + }} } } @@ -1497,6 +1537,7 @@ static const char *const partition_designator_table[] = { [PARTITION_HOME] = "home", [PARTITION_SRV] = "srv", [PARTITION_ESP] = "esp", + [PARTITION_XBOOTLDR] = "xbootldr", [PARTITION_SWAP] = "swap", [PARTITION_ROOT_VERITY] = "root-verity", [PARTITION_ROOT_SECONDARY_VERITY] = "root-secondary-verity", diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h index f50b40ea11..ded43f331a 100644 --- a/src/shared/dissect-image.h +++ b/src/shared/dissect-image.h @@ -29,6 +29,7 @@ enum { PARTITION_HOME, PARTITION_SRV, PARTITION_ESP, + PARTITION_XBOOTLDR, PARTITION_SWAP, PARTITION_ROOT_VERITY, /* verity data for the PARTITION_ROOT partition */ PARTITION_ROOT_SECONDARY_VERITY, /* verity data for the PARTITION_ROOT_SECONDARY partition */ From d9223c07f5540ce9fa61a07a2cbd5757e2b8f5ca Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 23 Jan 2019 11:53:28 +0100 Subject: [PATCH 10/48] dissect: when mounting an image mount the XBOOTLDR partition to /boot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, we'd mount the ESP to /efi if that existed and was empty, falling back to /boot if that existed and was empty. With this change, the XBOOTLDR partition is mounted to /boot unconditionally. And the EFI is mounted to /efi if that exists (but it doesn't have to be empty — after all the name is very indicative of what this is supposed to be), and to /boot as a fallback but only if it exists and is empty (we insist on emptiness for that, since it might be used differently than what we assume). The net effect is that $BOOT should be reliably found under /boot, and the ESP is either /efi or /boot. (Note that this commit only is relevant for nspawn and suchlike, i.e. the codepaths that mount an image without involving udev during boot.) --- src/shared/dissect-image.c | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 2dbc0bdb3d..201a515589 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -857,11 +857,15 @@ static int mount_partition( return -ENOMEM; } - return mount_verbose(LOG_DEBUG, node, p, fstype, MS_NODEV|(rw ? 0 : MS_RDONLY), options); + r = mount_verbose(LOG_DEBUG, node, p, fstype, MS_NODEV|(rw ? 0 : MS_RDONLY), options); + if (r < 0) + return r; + + return 1; } int dissected_image_mount(DissectedImage *m, const char *where, uid_t uid_shift, DissectImageFlags flags) { - int r; + int r, boot_mounted; assert(m); assert(where); @@ -894,21 +898,26 @@ int dissected_image_mount(DissectedImage *m, const char *where, uid_t uid_shift, if (r < 0) return r; + boot_mounted = mount_partition(m->partitions + PARTITION_XBOOTLDR, where, "/boot", uid_shift, flags); + if (boot_mounted < 0) + return boot_mounted; + if (m->partitions[PARTITION_ESP].found) { - const char *mp; + /* 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 /efi if it exists and is empty. If it doesn't exist, use /boot instead. */ + r = chase_symlinks("/efi", where, CHASE_PREFIX_ROOT, NULL); + if (r >= 0) { + r = mount_partition(m->partitions + PARTITION_ESP, where, "/efi", uid_shift, flags); + if (r < 0) + return r; - FOREACH_STRING(mp, "/efi", "/boot") { + } else if (boot_mounted <= 0) { _cleanup_free_ char *p = NULL; - r = chase_symlinks(mp, where, CHASE_PREFIX_ROOT, &p); - if (r < 0) - continue; - - r = dir_is_empty(p); - if (r > 0) { - r = mount_partition(m->partitions + PARTITION_ESP, where, mp, uid_shift, flags); + r = chase_symlinks("/boot", where, CHASE_PREFIX_ROOT, &p); + if (r >= 0 && dir_is_empty(p) > 0) { + r = mount_partition(m->partitions + PARTITION_ESP, where, "/boot", uid_shift, flags); if (r < 0) return r; } From 9f1cb0c1fad5c86d580803dc4999f693fb6843db Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 23 Jan 2019 13:19:41 +0100 Subject: [PATCH 11/48] gpt-auto: also load the boot loader partition during regular boots --- src/gpt-auto-generator/gpt-auto-generator.c | 86 +++++++++++++++++---- 1 file changed, 71 insertions(+), 15 deletions(-) diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c index 09c0bcba2d..2f8ccd025b 100644 --- a/src/gpt-auto-generator/gpt-auto-generator.c +++ b/src/gpt-auto-generator/gpt-auto-generator.c @@ -322,7 +322,6 @@ static int add_swap(const char *path) { return generator_add_symlink(arg_dest, SPECIAL_SWAP_TARGET, "wants", name); } -#if ENABLE_EFI static int add_automount( const char *id, const char *what, @@ -384,8 +383,43 @@ static int add_automount( return generator_add_symlink(arg_dest, SPECIAL_LOCAL_FS_TARGET, "wants", unit); } -static int add_esp(DissectedPartition *p) { - const char *esp; +static int add_xbootldr(DissectedPartition *p) { + int r; + + assert(p); + + if (in_initrd()) { + log_debug("In initrd, ignoring the XBOOTLDR partition."); + return 0; + } + + r = fstab_is_mount_point("/boot"); + if (r < 0) + return log_error_errno(r, "Failed to parse fstab: %m"); + if (r > 0) { + log_debug("/boot specified in fstab, ignoring XBOOTLDR partition."); + return 0; + } + + r = path_is_busy("/boot"); + if (r < 0) + return r; + if (r > 0) + return 0; + + return add_automount("boot", + p->node, + "/boot", + p->fstype, + true, + "umask=0077", + "Boot Loader Partition", + 120 * USEC_PER_SEC); +} + +#if ENABLE_EFI +static int add_esp(DissectedPartition *p, bool has_xbootldr) { + const char *esp_path = NULL, *id = NULL; int r; assert(p); @@ -395,21 +429,37 @@ static int add_esp(DissectedPartition *p) { return 0; } - /* If /efi exists we'll use that. Otherwise we'll use /boot, as that's usually the better choice */ - esp = access("/efi/", F_OK) >= 0 ? "/efi" : "/boot"; + /* 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 */ + if (!has_xbootldr) { + esp_path = "/boot"; + id = "boot"; + } + } + if (!esp_path) + 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); + 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); + log_debug("%s specified in fstab, ignoring.", esp_path); return 0; } - r = path_is_busy(esp); - if (r != 0) - return r < 0 ? r : 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; @@ -425,15 +475,15 @@ static int add_esp(DissectedPartition *p) { return log_error_errno(r, "Failed to read ESP partition UUID: %m"); if (!sd_id128_equal(p->uuid, loader_uuid)) { - log_debug("Partition for %s does not appear to be the partition we are booted from.", esp); + log_debug("Partition for %s does not appear to be the partition we are booted from.", p->node); return 0; } } else log_debug("Not an EFI boot, skipping ESP check."); - return add_automount("boot", + return add_automount(id, p->node, - esp, + esp_path, p->fstype, true, "umask=0077", @@ -441,7 +491,7 @@ static int add_esp(DissectedPartition *p) { 120 * USEC_PER_SEC); } #else -static int add_esp(DissectedPartition *p) { +static int add_esp(DissectedPartition *p, bool has_xbootldr) { return 0; } #endif @@ -569,8 +619,14 @@ static int enumerate_partitions(dev_t devnum) { r = k; } + if (m->partitions[PARTITION_XBOOTLDR].found) { + k = add_xbootldr(m->partitions + PARTITION_XBOOTLDR); + if (k < 0) + r = k; + } + if (m->partitions[PARTITION_ESP].found) { - k = add_esp(m->partitions + PARTITION_ESP); + k = add_esp(m->partitions + PARTITION_ESP, m->partitions[PARTITION_XBOOTLDR].found); if (k < 0) r = k; } From 15cb6c98973293d82ab6e27889a90d281b39ed19 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 23 Jan 2019 14:19:40 +0100 Subject: [PATCH 12/48] bootspec: add internal APIs to discover the XBOOTLDR partition --- src/shared/bootspec.c | 168 ++++++++++++++++++++++++++++++++++++++++++ src/shared/bootspec.h | 1 + 2 files changed, 169 insertions(+) diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index c60627e24c..5715c94d68 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -629,6 +629,174 @@ found: return 0; } +static int verify_xbootldr( + const char *p, + bool searching, + bool unprivileged_mode, + sd_id128_t *ret_uuid) { +#if HAVE_BLKID + _cleanup_(blkid_free_probep) blkid_probe b = NULL; + _cleanup_free_ char *node = NULL; + const char *v; +#endif + struct stat st, st2; + const char *t2; + sd_id128_t uuid = SD_ID128_NULL; + bool relax_checks; + int r; + + assert(p); + + relax_checks = getenv_bool("SYSTEMD_RELAX_XBOOTLDR_CHECKS") > 0; + + if (stat(p, &st) < 0) + return log_full_errno((searching && errno == ENOENT) || + (unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno, + "Failed to determine block device node of \"%s\": %m", p); + + if (major(st.st_dev) == 0) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "Block device node of \"%s\" is invalid.", p); + + t2 = strjoina(p, "/.."); + r = stat(t2, &st2); + if (r < 0) + return log_full_errno(unprivileged_mode && errno == EACCES ? LOG_DEBUG : LOG_ERR, errno, + "Failed to determine block device node of parent of \"%s\": %m", p); + + if (st.st_dev == st2.st_dev) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "Directory \"%s\" is not the root of the XBOOTLDR file system.", p); + + /* In a container we don't have access to block devices, skip this part of the verification, we trust + * the container manager set everything up correctly on its own. Also skip the following verification + * for non-root user. */ + if (detect_container() > 0 || unprivileged_mode || relax_checks) + goto finish; + +#if HAVE_BLKID + r = device_path_make_major_minor(S_IFBLK, st.st_dev, &node); + if (r < 0) + return log_error_errno(r, "Failed to format major/minor device path: %m"); + errno = 0; + b = blkid_new_probe_from_filename(node); + if (!b) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "Failed to open file system \"%s\": %m", p); + + blkid_probe_enable_partitions(b, 1); + blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS); + + errno = 0; + r = blkid_do_safeprobe(b); + if (r == -2) + return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" is ambiguous.", p); + else if (r == 1) + return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", p); + else if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", p); + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition scheme \"%s\": %m", p); + if (streq(v, "gpt")) { + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition type UUID \"%s\": %m", p); + if (!streq(v, "bc13c2ff-59e6-4262-a352-b275fd6f7172")) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV), + "File system \"%s\" has wrong type for extended boot loader partition.", p); + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition entry UUID \"%s\": %m", p); + r = sd_id128_from_string(v, &uuid); + if (r < 0) + return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", p, v); + + } else if (streq(v, "dos")) { + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition type UUID \"%s\": %m", p); + if (!streq(v, "0xea")) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV), + "File system \"%s\" has wrong type for extended boot loader partition.", p); + + } else + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV), + "File system \"%s\" is not on a GPT or DOS partition table.", p); + +#endif + +finish: + if (ret_uuid) + *ret_uuid = uuid; + + return 0; +} + +int find_xbootldr_and_warn( + const char *path, + bool unprivileged_mode, + char **ret_path, + sd_id128_t *ret_uuid) { + + int r; + + /* Similar to find_esp_and_warn(), but finds the XBOOTLDR partition. Returns the same errors. */ + + if (path) { + r = verify_xbootldr(path, false, unprivileged_mode, ret_uuid); + if (r < 0) + return r; + + goto found; + } + + path = getenv("SYSTEMD_XBOOTLDR_PATH"); + if (path) { + if (!path_is_valid(path) || !path_is_absolute(path)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "$SYSTEMD_XBOOTLDR_PATH does not refer to absolute path, refusing to use it: %s", + path); + + goto found; + } + + r = verify_xbootldr("/boot", true, unprivileged_mode, ret_uuid); + if (r >= 0) { + path = "/boot"; + goto found; + } + if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */ + return r; + + return -ENOKEY; + +found: + if (ret_path) { + char *c; + + c = strdup(path); + if (!c) + return log_oom(); + + *ret_path = c; + } + + return 0; +} + int find_default_boot_entry( const char *esp_path, char **esp_where, diff --git a/src/shared/bootspec.h b/src/shared/bootspec.h index ed576210fe..8a22fd5cdd 100644 --- a/src/shared/bootspec.h +++ b/src/shared/bootspec.h @@ -47,5 +47,6 @@ static inline const char* boot_entry_title(const BootEntry *entry) { } int find_esp_and_warn(const char *path, bool unprivileged_mode, char **ret_path, uint32_t *ret_part, uint64_t *ret_pstart, uint64_t *ret_psize, sd_id128_t *ret_uuid); +int find_xbootldr_and_warn(const char *path, bool unprivileged_mode, char **ret_path,sd_id128_t *ret_uuid); int find_default_boot_entry(const char *esp_path, char **esp_where, BootConfig *config, const BootEntry **e); From 43b736a84bbceabe7cb7588bcc61389e831e6f73 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 28 Jan 2019 17:57:41 +0100 Subject: [PATCH 13/48] bootspec: store 'root' field in each bootspec entry we load MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This 'root' field contains the root path of the partition we found the snippet in. The 'kernel', 'initrd', 'efi', … fields are relative to this path. This becomes particularly useful later when we add support for loading snippets from both the ESP and XBOOTLDR, but already simplifies the code for us a bit in systemctl. --- src/shared/bootspec.c | 28 ++++++++++++++++++++-------- src/shared/bootspec.h | 5 +++-- src/systemctl/systemctl.c | 8 ++++---- 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index 5715c94d68..c662e89068 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -27,6 +27,7 @@ static void boot_entry_free(BootEntry *entry) { free(entry->id); free(entry->path); + free(entry->root); free(entry->title); free(entry->show_title); free(entry->version); @@ -39,13 +40,18 @@ static void boot_entry_free(BootEntry *entry) { free(entry->device_tree); } -static int boot_entry_load(const char *path, BootEntry *entry) { +static int boot_entry_load( + const char *root, + const char *path, + BootEntry *entry) { + _cleanup_(boot_entry_free) BootEntry tmp = {}; _cleanup_fclose_ FILE *f = NULL; unsigned line = 1; char *b, *c; int r; + assert(root); assert(path); assert(entry); @@ -62,6 +68,10 @@ static int boot_entry_load(const char *path, BootEntry *entry) { if (!tmp.path) return log_oom(); + tmp.root = strdup(root); + if (!tmp.root) + return log_oom(); + f = fopen(path, "re"); if (!f) return log_error_errno(errno, "Failed to open \"%s\": %m", path); @@ -215,13 +225,19 @@ static int boot_entry_compare(const BootEntry *a, const BootEntry *b) { return str_verscmp(a->id, b->id); } -static int boot_entries_find(const char *dir, BootEntry **ret_entries, size_t *ret_n_entries) { +static int boot_entries_find( + const char *root, + const char *dir, + BootEntry **ret_entries, + size_t *ret_n_entries) { + _cleanup_strv_free_ char **files = NULL; char **f; int r; BootEntry *array = NULL; size_t n_allocated = 0, n = 0; + assert(root); assert(dir); assert(ret_entries); assert(ret_n_entries); @@ -234,7 +250,7 @@ static int boot_entries_find(const char *dir, BootEntry **ret_entries, size_t *r if (!GREEDY_REALLOC0(array, n_allocated, n + 1)) return log_oom(); - r = boot_entry_load(*f, array + n); + r = boot_entry_load(root, *f, array + n); if (r < 0) continue; @@ -369,7 +385,7 @@ int boot_entries_load_config(const char *esp_path, BootConfig *config) { return r; p = strjoina(esp_path, "/loader/entries"); - r = boot_entries_find(p, &config->entries, &config->n_entries); + r = boot_entries_find(esp_path, p, &config->entries, &config->n_entries); if (r < 0) return r; @@ -799,7 +815,6 @@ found: int find_default_boot_entry( const char *esp_path, - char **esp_where, BootConfig *config, const BootEntry **e) { @@ -824,8 +839,5 @@ int find_default_boot_entry( *e = &config->entries[config->default_entry]; log_debug("Found default boot entry in file \"%s\"", (*e)->path); - if (esp_where) - *esp_where = TAKE_PTR(where); - return 0; } diff --git a/src/shared/bootspec.h b/src/shared/bootspec.h index 8a22fd5cdd..0fac051398 100644 --- a/src/shared/bootspec.h +++ b/src/shared/bootspec.h @@ -10,7 +10,8 @@ typedef struct BootEntry { char *id; /* This is the file basename without extension */ - char *path; /* This is the full path to the file */ + char *path; /* This is the full path to the drop-in file */ + char *root; /* The root path in which the drop-in was found, i.e. to which 'kernel', 'efi' and 'initrd' are relative */ char *title; char *show_title; char *version; @@ -49,4 +50,4 @@ static inline const char* boot_entry_title(const BootEntry *entry) { int find_esp_and_warn(const char *path, bool unprivileged_mode, char **ret_path, uint32_t *ret_part, uint64_t *ret_pstart, uint64_t *ret_psize, sd_id128_t *ret_uuid); int find_xbootldr_and_warn(const char *path, bool unprivileged_mode, char **ret_path,sd_id128_t *ret_uuid); -int find_default_boot_entry(const char *esp_path, char **esp_where, BootConfig *config, const BootEntry **e); +int find_default_boot_entry(const char *esp_path, BootConfig *config, const BootEntry **e); diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 63dae2c872..6900f61e9e 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -3519,7 +3519,7 @@ static int prepare_firmware_setup(void) { static int load_kexec_kernel(void) { _cleanup_(boot_config_free) BootConfig config = {}; - _cleanup_free_ char *where = NULL, *kernel = NULL, *initrd = NULL, *options = NULL; + _cleanup_free_ char *kernel = NULL, *initrd = NULL, *options = NULL; const BootEntry *e; pid_t pid; int r; @@ -3532,7 +3532,7 @@ static int load_kexec_kernel(void) { if (access(KEXEC, X_OK) < 0) return log_error_errno(errno, KEXEC" is not available: %m"); - r = find_default_boot_entry(arg_esp_path, &where, &config, &e); + r = find_default_boot_entry(arg_esp_path, &config, &e); if (r == -ENOKEY) /* find_default_boot_entry() doesn't warn about this case */ return log_error_errno(r, "Cannot find the ESP partition mount point."); if (r < 0) @@ -3544,9 +3544,9 @@ static int load_kexec_kernel(void) { return -EINVAL; } - kernel = path_join(where, e->kernel); + kernel = path_join(e->root, e->kernel); if (!strv_isempty(e->initrd)) - initrd = path_join(where, *e->initrd); + initrd = path_join(e->root, *e->initrd); options = strv_join(e->options, " "); if (!options) return log_oom(); From 947fbaea8bb77085114db2e855553448c8c0d248 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 28 Jan 2019 18:47:01 +0100 Subject: [PATCH 14/48] systemctl: drop arg_esp_path variable from systemctl It's not set ever (and there's no real need to make it settable, since users can as well set $SYSTEMD_ESP_PATH to configure this. --- src/systemctl/systemctl.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 6900f61e9e..bca1b79bab 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -134,7 +134,6 @@ static const char *arg_kill_who = NULL; static int arg_signal = SIGTERM; static char *arg_root = NULL; static usec_t arg_when = 0; -static char *arg_esp_path = NULL; static char *argv_cmdline = NULL; static enum action { ACTION_SYSTEMCTL, @@ -3532,7 +3531,7 @@ static int load_kexec_kernel(void) { if (access(KEXEC, X_OK) < 0) return log_error_errno(errno, KEXEC" is not available: %m"); - r = find_default_boot_entry(arg_esp_path, &config, &e); + r = find_default_boot_entry(NULL, &config, &e); if (r == -ENOKEY) /* find_default_boot_entry() doesn't warn about this case */ return log_error_errno(r, "Cannot find the ESP partition mount point."); if (r < 0) @@ -8814,7 +8813,6 @@ finish: strv_free(arg_wall); free(arg_root); - free(arg_esp_path); /* Note that we return r here, not 0, so that we can implement the LSB-like return codes */ return r; From 8a1ef3bf8d0eaea5a34911b3a34d741ef3fb4b5c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 28 Jan 2019 19:30:30 +0100 Subject: [PATCH 15/48] systemctl: use SYNTHETIC_ERRNO() where appropriate --- src/systemctl/systemctl.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index bca1b79bab..f79121bc24 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -3538,10 +3538,9 @@ static int load_kexec_kernel(void) { /* But it logs about all these cases, hence don't log here again */ return r; - if (strv_length(e->initrd) > 1) { - log_error("Boot entry specifies multiple initrds, which is not supported currently."); - return -EINVAL; - } + if (strv_length(e->initrd) > 1) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Boot entry specifies multiple initrds, which is not supported currently."); kernel = path_join(e->root, e->kernel); if (!strv_isempty(e->initrd)) From 9fc32cd9b3a8bbce64a0a48a0f186d367b7f88ce Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 28 Jan 2019 19:30:42 +0100 Subject: [PATCH 16/48] systemctl: add missing OOM checks --- src/systemctl/systemctl.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index f79121bc24..d30b607c43 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -3543,8 +3543,15 @@ static int load_kexec_kernel(void) { "Boot entry specifies multiple initrds, which is not supported currently."); kernel = path_join(e->root, e->kernel); - if (!strv_isempty(e->initrd)) + if (!kernel) + return log_oom(); + + if (!strv_isempty(e->initrd)) { initrd = path_join(e->root, *e->initrd); + if (!initrd) + return log_oom(); + } + options = strv_join(e->options, " "); if (!options) return log_oom(); From a2f8664e9a544d7547f4fcdbfe2fa6396b8f7654 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 28 Jan 2019 18:56:53 +0100 Subject: [PATCH 17/48] bootspec: load entries from both the ESP and XBOOTLDR partitions Let's simply search in both. --- src/boot/bootctl.c | 4 +-- src/shared/bootspec.c | 74 ++++++++++++++++++++++++--------------- src/shared/bootspec.h | 4 +-- src/systemctl/systemctl.c | 2 +- 4 files changed, 50 insertions(+), 34 deletions(-) diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index 2424aa011f..ddbcac9200 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -323,7 +323,7 @@ static int status_entries(const char *esp_path, sd_id128_t partition) { _cleanup_(boot_config_free) BootConfig config = {}; int r; - r = boot_entries_load_config(esp_path, &config); + r = boot_entries_load_config(esp_path, NULL, &config); if (r < 0) return r; @@ -1075,7 +1075,7 @@ static int verb_list(int argc, char *argv[], void *userdata) { if (r < 0) return r; - r = boot_entries_load_config(arg_path, &config); + r = boot_entries_load_config(arg_path, NULL, &config); if (r < 0) return r; diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index c662e89068..6f28c74a5d 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -228,39 +228,38 @@ static int boot_entry_compare(const BootEntry *a, const BootEntry *b) { static int boot_entries_find( const char *root, const char *dir, - BootEntry **ret_entries, - size_t *ret_n_entries) { + BootEntry **entries, + size_t *n_entries) { _cleanup_strv_free_ char **files = NULL; + size_t n_allocated = *n_entries; + bool added = false; char **f; int r; - BootEntry *array = NULL; - size_t n_allocated = 0, n = 0; assert(root); assert(dir); - assert(ret_entries); - assert(ret_n_entries); + assert(entries); + assert(n_entries); r = conf_files_list(&files, ".conf", NULL, 0, dir, NULL); if (r < 0) return log_error_errno(r, "Failed to list files in \"%s\": %m", dir); STRV_FOREACH(f, files) { - if (!GREEDY_REALLOC0(array, n_allocated, n + 1)) + if (!GREEDY_REALLOC0(*entries, n_allocated, *n_entries + 1)) return log_oom(); - r = boot_entry_load(root, *f, array + n); + r = boot_entry_load(root, *f, *entries + *n_entries); if (r < 0) continue; - n++; + (*n_entries) ++; + added = true; } - typesafe_qsort(array, n, boot_entry_compare); - - *ret_entries = array; - *ret_n_entries = n; + if (added) + typesafe_qsort(*entries, *n_entries, boot_entry_compare); return 0; } @@ -372,22 +371,34 @@ static int boot_entries_select_default(const BootConfig *config) { return config->n_entries - 1; /* -1 means "no default" */ } -int boot_entries_load_config(const char *esp_path, BootConfig *config) { +int boot_entries_load_config( + const char *esp_path, + const char *xbootldr_path, + BootConfig *config) { + const char *p; int r; - assert(esp_path); assert(config); - p = strjoina(esp_path, "/loader/loader.conf"); - r = boot_loader_read_conf(p, config); - if (r < 0) - return r; + if (esp_path) { + p = strjoina(esp_path, "/loader/loader.conf"); + r = boot_loader_read_conf(p, config); + if (r < 0) + return r; - p = strjoina(esp_path, "/loader/entries"); - r = boot_entries_find(esp_path, p, &config->entries, &config->n_entries); - if (r < 0) - return r; + p = strjoina(esp_path, "/loader/entries"); + r = boot_entries_find(esp_path, p, &config->entries, &config->n_entries); + if (r < 0) + return r; + } + + if (xbootldr_path) { + p = strjoina(xbootldr_path, "/loader/entries"); + r = boot_entries_find(xbootldr_path, p, &config->entries, &config->n_entries); + if (r < 0) + return r; + } r = boot_entries_uniquify(config->entries, config->n_entries); if (r < 0) @@ -815,29 +826,34 @@ found: int find_default_boot_entry( const char *esp_path, + const char *xbootldr_path, BootConfig *config, const BootEntry **e) { - _cleanup_free_ char *where = NULL; + _cleanup_free_ char *esp_where = NULL, *xbootldr_where = NULL; int r; assert(config); assert(e); - r = find_esp_and_warn(esp_path, false, &where, NULL, NULL, NULL, NULL); + r = find_esp_and_warn(esp_path, false, &esp_where, NULL, NULL, NULL, NULL); if (r < 0) return r; - r = boot_entries_load_config(where, config); + r = find_xbootldr_and_warn(xbootldr_path, false, &xbootldr_where, NULL); + if (r < 0 && r != -ENOKEY) + return r; + + r = boot_entries_load_config(esp_where, xbootldr_where, config); if (r < 0) - return log_error_errno(r, "Failed to load bootspec config from \"%s/loader\": %m", where); + return log_error_errno(r, "Failed to load boot loader entries: %m"); if (config->default_entry < 0) return log_error_errno(SYNTHETIC_ERRNO(ENOENT), - "No entry suitable as default, refusing to guess."); + "No boot loader entry suitable as default, refusing to guess."); *e = &config->entries[config->default_entry]; - log_debug("Found default boot entry in file \"%s\"", (*e)->path); + log_debug("Found default boot loader entry in file \"%s\"", (*e)->path); return 0; } diff --git a/src/shared/bootspec.h b/src/shared/bootspec.h index 0fac051398..f184902558 100644 --- a/src/shared/bootspec.h +++ b/src/shared/bootspec.h @@ -41,7 +41,7 @@ typedef struct BootConfig { } BootConfig; void boot_config_free(BootConfig *config); -int boot_entries_load_config(const char *esp_path, BootConfig *config); +int boot_entries_load_config(const char *esp_path, const char *xbootldr_path, BootConfig *config); static inline const char* boot_entry_title(const BootEntry *entry) { return entry->show_title ?: entry->title ?: entry->id; @@ -50,4 +50,4 @@ static inline const char* boot_entry_title(const BootEntry *entry) { int find_esp_and_warn(const char *path, bool unprivileged_mode, char **ret_path, uint32_t *ret_part, uint64_t *ret_pstart, uint64_t *ret_psize, sd_id128_t *ret_uuid); int find_xbootldr_and_warn(const char *path, bool unprivileged_mode, char **ret_path,sd_id128_t *ret_uuid); -int find_default_boot_entry(const char *esp_path, BootConfig *config, const BootEntry **e); +int find_default_boot_entry(const char *esp_path, const char *xbootldr_path, BootConfig *config, const BootEntry **e); diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index d30b607c43..57e3111b8d 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -3531,7 +3531,7 @@ static int load_kexec_kernel(void) { if (access(KEXEC, X_OK) < 0) return log_error_errno(errno, KEXEC" is not available: %m"); - r = find_default_boot_entry(NULL, &config, &e); + r = find_default_boot_entry(NULL, NULL, &config, &e); if (r == -ENOKEY) /* find_default_boot_entry() doesn't warn about this case */ return log_error_errno(r, "Cannot find the ESP partition mount point."); if (r < 0) From fbf45d22dad1ca7c02c74fa01755c8e843a34b84 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 23 Jan 2019 16:18:29 +0100 Subject: [PATCH 18/48] bootctl: teach bootctl the new partition type --- src/boot/bootctl.c | 350 ++++++++++++++++++++++++++++++--------------- 1 file changed, 237 insertions(+), 113 deletions(-) diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index ddbcac9200..482189f989 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -45,12 +45,20 @@ #include "verbs.h" #include "virt.h" -static char *arg_path = NULL; -static bool arg_print_path = false; +static char *arg_esp_path = NULL; +static char *arg_xbootldr_path = NULL; +static bool arg_print_esp_path = false; +static bool arg_print_dollar_boot_path = false; static bool arg_touch_variables = true; static PagerFlags arg_pager_flags = 0; -STATIC_DESTRUCTOR_REGISTER(arg_path, freep); +STATIC_DESTRUCTOR_REGISTER(arg_esp_path, freep); +STATIC_DESTRUCTOR_REGISTER(arg_xbootldr_path, freep); + +static const char *arg_dollar_boot_path(void) { + /* $BOOT shall be the XBOOTLDR partition if it exists, and otherwise the ESP */ + return arg_xbootldr_path ?: arg_esp_path; +} static int acquire_esp( bool unprivileged_mode, @@ -62,24 +70,44 @@ static int acquire_esp( char *np; int r; - /* Find the ESP, and log about errors. Note that find_esp_and_warn() will log in all error cases on its own, - * except for ENOKEY (which is good, we want to show our own message in that case, suggesting use of --path=) - * and EACCESS (only when we request unprivileged mode; in this case we simply eat up the error here, so that - * --list and --status work too, without noise about this). */ + /* Find the ESP, and log about errors. Note that find_esp_and_warn() will log in all error cases on + * its own, except for ENOKEY (which is good, we want to show our own message in that case, + * suggesting use of --esp-path=) and EACCESS (only when we request unprivileged mode; in this case + * we simply eat up the error here, so that --list and --status work too, without noise about + * this). */ - r = find_esp_and_warn(arg_path, unprivileged_mode, &np, ret_part, ret_pstart, ret_psize, ret_uuid); + r = find_esp_and_warn(arg_esp_path, unprivileged_mode, &np, ret_part, ret_pstart, ret_psize, ret_uuid); if (r == -ENOKEY) return log_error_errno(r, "Couldn't find EFI system partition. It is recommended to mount it to /boot or /efi.\n" - "Alternatively, use --path= to specify path to mount point."); + "Alternatively, use --esp-path= to specify path to mount point."); if (r < 0) return r; - free_and_replace(arg_path, np); + free_and_replace(arg_esp_path, np); + log_debug("Using EFI System Partition at %s.", arg_esp_path); - log_debug("Using EFI System Partition at %s.", arg_path); + return 1; +} - return 0; +static int acquire_xbootldr(bool unprivileged_mode, sd_id128_t *ret_uuid) { + char *np; + int r; + + r = find_xbootldr_and_warn(arg_xbootldr_path, unprivileged_mode, &np, ret_uuid); + if (r == -ENOKEY) { + log_debug_errno(r, "Didn't find an XBOOTLDR partition, using the ESP as $BOOT."); + if (ret_uuid) + *ret_uuid = SD_ID128_NULL; + return 0; + } + if (r < 0) + return r; + + free_and_replace(arg_xbootldr_path, np); + log_debug("Using XBOOTLDR partition at %s as $BOOT.", arg_xbootldr_path); + + return 1; } /* search for "#### LoaderInfo: systemd-boot 218 ####" string inside the binary */ @@ -134,10 +162,13 @@ finish: } static int enumerate_binaries(const char *esp_path, const char *path, const char *prefix) { - char *p; _cleanup_closedir_ DIR *d = NULL; struct dirent *de; - int r = 0, c = 0; + int c = 0, r; + char *p; + + assert(esp_path); + assert(path); p = strjoina(esp_path, "/", path); d = opendir(p); @@ -149,8 +180,8 @@ static int enumerate_binaries(const char *esp_path, const char *path, const char } FOREACH_DIRENT(de, d, break) { - _cleanup_close_ int fd = -1; _cleanup_free_ char *v = NULL; + _cleanup_close_ int fd = -1; if (!endswith_no_case(de->d_name, ".efi")) continue; @@ -169,6 +200,7 @@ static int enumerate_binaries(const char *esp_path, const char *path, const char printf(" File: %s/%s/%s (%s%s%s)\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), path, de->d_name, ansi_highlight(), v, ansi_normal()); else printf(" File: %s/%s/%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), path, de->d_name); + c++; } @@ -191,16 +223,16 @@ static int status_binaries(const char *esp_path, sd_id128_t partition) { printf("\n"); r = enumerate_binaries(esp_path, "EFI/systemd", NULL); + if (r < 0) + return r; if (r == 0) log_info("systemd-boot not installed in ESP."); - else if (r < 0) - return r; r = enumerate_binaries(esp_path, "EFI/BOOT", "boot"); + if (r < 0) + return r; if (r == 0) log_info("No default/fallback boot loader installed in ESP."); - else if (r < 0) - return r; printf("\n"); @@ -319,11 +351,11 @@ static int boot_entry_show(const BootEntry *e, bool show_as_default) { return 0; } -static int status_entries(const char *esp_path, sd_id128_t partition) { +static int status_entries(const char *esp_path, const char *xbootldr_path) { _cleanup_(boot_config_free) BootConfig config = {}; int r; - r = boot_entries_load_config(esp_path, NULL, &config); + r = boot_entries_load_config(esp_path, xbootldr_path, &config); if (r < 0) return r; @@ -469,20 +501,21 @@ static int mkdir_one(const char *prefix, const char *suffix) { return 0; } -static const char *efi_subdirs[] = { +static const char *const esp_subdirs[] = { "EFI", "EFI/systemd", "EFI/BOOT", "loader", - "loader/entries", + /* Note that "/loader/entries" is not listed here, since it should be placed in $BOOT, which might + * not necessarily be the ESP */ NULL }; -static int create_dirs(const char *esp_path) { - const char **i; +static int create_esp_subdirs(const char *esp_path) { + const char *const *i; int r; - STRV_FOREACH(i, efi_subdirs) { + STRV_FOREACH(i, esp_subdirs) { r = mkdir_one(esp_path, *i); if (r < 0) return r; @@ -521,17 +554,6 @@ static int install_binaries(const char *esp_path, bool force) { _cleanup_closedir_ DIR *d = NULL; int r = 0; - if (force) { - /* Don't create any of these directories when we are - * just updating. When we update we'll drop-in our - * files (unless there are newer ones already), but we - * won't create the directories for them in the first - * place. */ - r = create_dirs(esp_path); - if (r < 0) - return r; - } - d = opendir(BOOTLIBDIR); if (!d) return log_error_errno(errno, "Failed to open \""BOOTLIBDIR"\": %m"); @@ -752,18 +774,36 @@ static int rmdir_one(const char *prefix, const char *suffix) { p = strjoina(prefix, "/", suffix); if (rmdir(p) < 0) { - if (!IN_SET(errno, ENOENT, ENOTEMPTY)) - return log_error_errno(errno, "Failed to remove \"%s\": %m", p); + bool ignore = IN_SET(errno, ENOENT, ENOTEMPTY); + + log_full_errno(ignore ? LOG_DEBUG : LOG_ERR, errno, + "Failed to remove directory \"%s\": %m", p); + if (!ignore) + return -errno; } else log_info("Removed \"%s\".", p); return 0; } +static int remove_esp_subdirs(const char *esp_path) { + size_t i; + int r = 0; + + for (i = ELEMENTSOF(esp_subdirs)-1; i > 0; i--) { + int q; + + q = rmdir_one(esp_path, esp_subdirs[i-1]); + if (q < 0 && r >= 0) + r = q; + } + + return r; +} + static int remove_binaries(const char *esp_path) { char *p; int r, q; - unsigned i; p = strjoina(esp_path, "/EFI/systemd"); r = rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL); @@ -772,15 +812,31 @@ static int remove_binaries(const char *esp_path) { if (q < 0 && r == 0) r = q; - for (i = ELEMENTSOF(efi_subdirs)-1; i > 0; i--) { - q = rmdir_one(esp_path, efi_subdirs[i-1]); - if (q < 0 && r == 0) - r = q; - } - return r; } +static int remove_loader_config(const char *esp_path) { + const char *p; + + assert(esp_path); + + p = strjoina(esp_path, "/loader/loader.conf"); + if (unlink(p) < 0) { + log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno, "Failed to unlink file \"%s\": %m", p); + if (errno != ENOENT) + return -errno; + } else + log_info("Removed \"%s\".", p); + + return 0; +} + +static int remove_entries_directory(const char *dollar_boot_path) { + assert(dollar_boot_path); + + return rmdir_one(dollar_boot_path, "/loader/entries"); +} + static int remove_variables(sd_id128_t uuid, const char *path, bool in_order) { uint16_t slot; int r; @@ -803,7 +859,6 @@ static int remove_variables(sd_id128_t uuid, const char *path, bool in_order) { } static int install_loader_config(const char *esp_path) { - char machine_string[SD_ID128_STRING_MAX]; _cleanup_(unlink_and_freep) char *t = NULL; _cleanup_fclose_ FILE *f = NULL; @@ -811,15 +866,14 @@ static int install_loader_config(const char *esp_path) { const char *p; int r, fd; + p = strjoina(esp_path, "/loader/loader.conf"); + if (access(p, F_OK) >= 0) /* Silently skip creation if the file already exists (early check) */ + return 0; + r = sd_id128_get_machine(&machine_id); if (r < 0) return log_error_errno(r, "Failed to get machine id: %m"); - p = strjoina(esp_path, "/loader/loader.conf"); - - if (access(p, F_OK) >= 0) /* Silently skip creation if the file already exists (early check) */ - return 0; - fd = open_tmpfile_linkable(p, O_WRONLY|O_CLOEXEC, &t); if (fd < 0) return log_error_errno(fd, "Failed to open \"%s\" for writing: %m", p); @@ -845,10 +899,15 @@ static int install_loader_config(const char *esp_path) { return log_error_errno(r, "Failed to move \"%s\" into place: %m", p); t = mfree(t); - return 1; } +static int install_entries_directory(const char *dollar_boot_path) { + assert(dollar_boot_path); + + return mkdir_one(dollar_boot_path, "/loader/entries"); +} + static int help(int argc, char *argv[], void *userdata) { _cleanup_free_ char *link = NULL; int r; @@ -859,21 +918,23 @@ static int help(int argc, char *argv[], void *userdata) { printf("%s [COMMAND] [OPTIONS...]\n\n" "Install, update or remove the systemd-boot EFI boot manager.\n\n" - " -h --help Show this help\n" - " --version Print version\n" - " --path=PATH Path to the EFI System Partition (ESP)\n" - " -p --print-path Print path to the EFI partition\n" - " --no-variables Don't touch EFI variables\n" - " --no-pager Do not pipe output into a pager\n" + " -h --help Show this help\n" + " --version Print version\n" + " --esp-path=PATH Path to the EFI System Partition (ESP)\n" + " --boot-path=PATH Path to the $BOOT partition\n" + " -p --print-esp-path Print path to the EFI System Partition\n" + " --print-boot-path Print path to the $BOOT partition\n" + " --no-variables Don't touch EFI variables\n" + " --no-pager Do not pipe output into a pager\n" "\nBoot Loader Commands:\n" - " status Show status of installed systemd-boot and EFI variables\n" - " install Install systemd-boot to the ESP and EFI variables\n" - " update Update systemd-boot in the ESP and EFI variables\n" - " remove Remove systemd-boot from the ESP and EFI variables\n" + " status Show status of installed systemd-boot and EFI variables\n" + " install Install systemd-boot to the ESP and EFI variables\n" + " update Update systemd-boot in the ESP and EFI variables\n" + " remove Remove systemd-boot from the ESP and EFI variables\n" "\nBoot Loader Entries Commands:\n" - " list List boot loader entries\n" - " set-default ID Set default boot loader entry\n" - " set-oneshot ID Set default boot loader entry, for next boot only\n" + " list List boot loader entries\n" + " set-default ID Set default boot loader entry\n" + " set-oneshot ID Set default boot loader entry, for next boot only\n" "\nSee the %s for details.\n" , program_invocation_short_name , link); @@ -883,19 +944,25 @@ static int help(int argc, char *argv[], void *userdata) { static int parse_argv(int argc, char *argv[]) { enum { - ARG_PATH = 0x100, + ARG_ESP_PATH = 0x100, + ARG_BOOT_PATH, + ARG_PRINT_BOOT_PATH, ARG_VERSION, ARG_NO_VARIABLES, ARG_NO_PAGER, }; static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "path", required_argument, NULL, ARG_PATH }, - { "print-path", no_argument, NULL, 'p' }, - { "no-variables", no_argument, NULL, ARG_NO_VARIABLES }, - { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "esp-path", required_argument, NULL, ARG_ESP_PATH }, + { "path", required_argument, NULL, ARG_ESP_PATH }, /* Compatibility alias */ + { "boot-path", required_argument, NULL, ARG_BOOT_PATH }, + { "print-esp-path", no_argument, NULL, 'p' }, + { "print-path", no_argument, NULL, 'p' }, /* Compatibility alias */ + { "print-boot-path", no_argument, NULL, ARG_PRINT_BOOT_PATH }, + { "no-variables", no_argument, NULL, ARG_NO_VARIABLES }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, {} }; @@ -914,14 +981,24 @@ static int parse_argv(int argc, char *argv[]) { case ARG_VERSION: return version(); - case ARG_PATH: - r = free_and_strdup(&arg_path, optarg); + case ARG_ESP_PATH: + r = free_and_strdup(&arg_esp_path, optarg); + if (r < 0) + return log_oom(); + break; + + case ARG_BOOT_PATH: + r = free_and_strdup(&arg_xbootldr_path, optarg); if (r < 0) return log_oom(); break; case 'p': - arg_print_path = true; + arg_print_esp_path = true; + break; + + case ARG_PRINT_BOOT_PATH: + arg_print_dollar_boot_path = true; break; case ARG_NO_VARIABLES: @@ -951,23 +1028,33 @@ static void read_loader_efi_var(const char *name, char **var) { } static int verb_status(int argc, char *argv[], void *userdata) { - - sd_id128_t uuid = SD_ID128_NULL; + sd_id128_t esp_uuid = SD_ID128_NULL, xbootldr_uuid = SD_ID128_NULL; int r, k; - r = acquire_esp(geteuid() != 0, NULL, NULL, NULL, &uuid); - - if (arg_print_path) { + r = acquire_esp(geteuid() != 0, NULL, NULL, NULL, &esp_uuid); + if (arg_print_esp_path) { if (r == -EACCES) /* If we couldn't acquire the ESP path, log about access errors (which is the only * error the find_esp_and_warn() won't log on its own) */ - return log_error_errno(r, "Failed to determine ESP: %m"); + return log_error_errno(r, "Failed to determine ESP location: %m"); if (r < 0) return r; - puts(arg_path); - return 0; + puts(arg_esp_path); } + r = acquire_xbootldr(geteuid() != 0, &xbootldr_uuid); + if (arg_print_dollar_boot_path) { + if (r == -EACCES) + return log_error_errno(r, "Failed to determine XBOOTLDR location: %m"); + if (r < 0) + return r; + + puts(arg_dollar_boot_path()); + } + + if (arg_print_esp_path || arg_print_dollar_boot_path) + return 0; + r = 0; /* If we couldn't determine the path, then don't consider that a problem from here on, just show what we * can show */ @@ -1038,8 +1125,8 @@ static int verb_status(int argc, char *argv[], void *userdata) { } else printf("System:\n Not booted with EFI\n\n"); - if (arg_path) { - k = status_binaries(arg_path, uuid); + if (arg_esp_path) { + k = status_binaries(arg_esp_path, esp_uuid); if (k < 0) r = k; } @@ -1050,8 +1137,8 @@ static int verb_status(int argc, char *argv[], void *userdata) { r = k; } - if (arg_path) { - k = status_entries(arg_path, uuid); + if (arg_esp_path || arg_xbootldr_path) { + k = status_entries(arg_esp_path, arg_xbootldr_path); if (k < 0) r = k; } @@ -1062,20 +1149,25 @@ static int verb_status(int argc, char *argv[], void *userdata) { static int verb_list(int argc, char *argv[], void *userdata) { _cleanup_(boot_config_free) BootConfig config = {}; _cleanup_free_ char **found_by_loader = NULL; - sd_id128_t uuid = SD_ID128_NULL; int r; /* If we lack privileges we invoke find_esp_and_warn() in "unprivileged mode" here, which does two things: turn * off logging about access errors and turn off potentially privileged device probing. Here we're interested in * the latter but not the former, hence request the mode, and log about EACCES. */ - r = acquire_esp(geteuid() != 0, NULL, NULL, NULL, &uuid); + r = acquire_esp(geteuid() != 0, NULL, NULL, NULL, NULL); if (r == -EACCES) /* We really need the ESP path for this call, hence also log about access errors */ return log_error_errno(r, "Failed to determine ESP: %m"); if (r < 0) return r; - r = boot_entries_load_config(arg_path, NULL, &config); + r = acquire_xbootldr(geteuid() != 0, NULL); + if (r == -EACCES) + return log_error_errno(r, "Failed to determine XBOOTLDR partition: %m"); + if (r < 0) + return r; + + r = boot_entries_load_config(arg_esp_path, arg_xbootldr_path, &config); if (r < 0) return r; @@ -1115,24 +1207,25 @@ static int verb_list(int argc, char *argv[], void *userdata) { return 0; } -static int sync_esp(void) { - _cleanup_close_ int fd = -1; +static int sync_everything(void) { + int ret = 0, k; - if (!arg_path) - return 0; + if (arg_esp_path) { + k = syncfs_path(AT_FDCWD, arg_esp_path); + if (k < 0) + ret = log_error_errno(k, "Failed to synchronize the ESP '%s': %m", arg_esp_path); + } - fd = open(arg_path, O_CLOEXEC|O_DIRECTORY|O_RDONLY); - if (fd < 0) - return log_error_errno(errno, "Couldn't open ESP '%s' for synchronization: %m", arg_path); + if (arg_xbootldr_path) { + k = syncfs_path(AT_FDCWD, arg_xbootldr_path); + if (k < 0) + ret = log_error_errno(k, "Failed to synchronize $BOOT '%s': %m", arg_xbootldr_path); + } - if (syncfs(fd) < 0) - return log_error_errno(errno, "Failed to synchronize the ESP '%s': %m", arg_path); - - return 1; + return ret; } static int verb_install(int argc, char *argv[], void *userdata) { - sd_id128_t uuid = SD_ID128_NULL; uint64_t pstart = 0, psize = 0; uint32_t part = 0; @@ -1143,24 +1236,41 @@ static int verb_install(int argc, char *argv[], void *userdata) { if (r < 0) return r; + r = acquire_xbootldr(false, NULL); + if (r < 0) + return r; + install = streq(argv[0], "install"); RUN_WITH_UMASK(0002) { - r = install_binaries(arg_path, install); + if (install) { + /* Don't create any of these directories when we are just updating. When we update + * we'll drop-in our files (unless there are newer ones already), but we won't create + * the directories for them in the first place. */ + r = create_esp_subdirs(arg_esp_path); + if (r < 0) + return r; + } + + r = install_binaries(arg_esp_path, install); if (r < 0) return r; if (install) { - r = install_loader_config(arg_path); + r = install_loader_config(arg_esp_path); + if (r < 0) + return r; + + r = install_entries_directory(arg_dollar_boot_path()); if (r < 0) return r; } } - (void) sync_esp(); + (void) sync_everything(); if (arg_touch_variables) - r = install_variables(arg_path, + r = install_variables(arg_esp_path, part, pstart, psize, uuid, "/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME ".efi", install); @@ -1170,21 +1280,35 @@ static int verb_install(int argc, char *argv[], void *userdata) { static int verb_remove(int argc, char *argv[], void *userdata) { sd_id128_t uuid = SD_ID128_NULL; - int r; + int r, q; r = acquire_esp(false, NULL, NULL, NULL, &uuid); if (r < 0) return r; - r = remove_binaries(arg_path); + r = acquire_xbootldr(false, NULL); + if (r < 0) + return r; - (void) sync_esp(); + r = remove_binaries(arg_esp_path); + + q = remove_loader_config(arg_esp_path); + if (q < 0 && r >= 0) + r = q; + + q = remove_entries_directory(arg_dollar_boot_path()); + if (q < 0 && r >= 0) + r = q; + + q = remove_esp_subdirs(arg_esp_path); + if (q < 0 && r >= 0) + r = q; + + (void) sync_everything(); if (arg_touch_variables) { - int q; - q = remove_variables(uuid, "/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME ".efi", true); - if (q < 0 && r == 0) + if (q < 0 && r >= 0) r = q; } From 81fed855d972c0077730304a1717a3925893b718 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 23 Jan 2019 16:59:28 +0100 Subject: [PATCH 19/48] bootctl: output where we found $BOOT --- src/boot/bootctl.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index 482189f989..5cc187f5ab 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -351,10 +351,33 @@ static int boot_entry_show(const BootEntry *e, bool show_as_default) { return 0; } -static int status_entries(const char *esp_path, const char *xbootldr_path) { +static int status_entries( + const char *esp_path, + sd_id128_t esp_partition_uuid, + const char *xbootldr_path, + sd_id128_t xbootldr_partition_uuid) { + _cleanup_(boot_config_free) BootConfig config = {}; + sd_id128_t dollar_boot_partition_uuid; + const char *dollar_boot_path; int r; + assert(esp_path || xbootldr_path); + + if (xbootldr_path) { + dollar_boot_path = xbootldr_path; + dollar_boot_partition_uuid = xbootldr_partition_uuid; + } else { + dollar_boot_path = esp_path; + dollar_boot_partition_uuid = esp_partition_uuid; + } + + printf("Boot Loader Entries:\n" + " $BOOT: %s", dollar_boot_path); + if (!sd_id128_is_null(dollar_boot_partition_uuid)) + printf(" (/dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x)", SD_ID128_FORMAT_VAL(dollar_boot_partition_uuid)); + printf("\n\n"); + r = boot_entries_load_config(esp_path, xbootldr_path, &config); if (r < 0) return r; @@ -1138,7 +1161,7 @@ static int verb_status(int argc, char *argv[], void *userdata) { } if (arg_esp_path || arg_xbootldr_path) { - k = status_entries(arg_esp_path, arg_xbootldr_path); + k = status_entries(arg_esp_path, esp_uuid, arg_xbootldr_path, xbootldr_uuid); if (k < 0) r = k; } From 2f88b2a081e7b351148af00bd32399899a64d3ac Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 23 Jan 2019 17:05:15 +0100 Subject: [PATCH 20/48] boot-bless: port over to new $BOOT discovery calls --- src/boot/bless-boot.c | 195 +++++++++++++++++++++++++----------------- 1 file changed, 118 insertions(+), 77 deletions(-) diff --git a/src/boot/bless-boot.c b/src/boot/bless-boot.c index 42b9618aa9..b5d110f422 100644 --- a/src/boot/bless-boot.c +++ b/src/boot/bless-boot.c @@ -16,9 +16,9 @@ #include "verbs.h" #include "virt.h" -static char *arg_path = NULL; +static char **arg_path = NULL; -STATIC_DESTRUCTOR_REGISTER(arg_path, freep); +STATIC_DESTRUCTOR_REGISTER(arg_path, strv_freep); static int help(int argc, char *argv[], void *userdata) { @@ -27,7 +27,7 @@ static int help(int argc, char *argv[], void *userdata) { "Mark the boot process as good or bad.\n\n" " -h --help Show this help\n" " --version Print version\n" - " --path=PATH Path to the EFI System Partition (ESP)\n" + " --path=PATH Path to the $BOOT partition (may be used multiple times)\n" "\n" "Commands:\n" " good Mark this boot as good\n" @@ -67,7 +67,7 @@ static int parse_argv(int argc, char *argv[]) { return version(); case ARG_PATH: - r = free_and_strdup(&arg_path, optarg); + r = strv_extend(&arg_path, optarg); if (r < 0) return log_oom(); break; @@ -82,20 +82,42 @@ static int parse_argv(int argc, char *argv[]) { return 1; } -static int acquire_esp(void) { - _cleanup_free_ char *np = NULL; +static int acquire_path(void) { + _cleanup_free_ char *esp_path = NULL, *xbootldr_path = NULL; + char **a; int r; - r = find_esp_and_warn(arg_path, false, &np, NULL, NULL, NULL, NULL); - if (r == -ENOKEY) /* find_esp_and_warn() doesn't warn in this one error case, but in all others */ - return log_error_errno(r, - "Couldn't find EFI system partition. It is recommended to mount it to /boot or /efi.\n" - "Alternatively, use --path= to specify path to mount point."); - if (r < 0) + if (!strv_isempty(arg_path)) + return 0; + + r = find_esp_and_warn(NULL, false, &esp_path, NULL, NULL, NULL, NULL); + if (r < 0 && r != -ENOKEY) /* ENOKEY means not found, and is the only error the function won't log about on its own */ return r; - free_and_replace(arg_path, np); - log_debug("Using EFI System Partition at %s.", arg_path); + r = find_xbootldr_and_warn(NULL, false, &xbootldr_path, NULL); + if (r < 0 && r != -ENOKEY) + return r; + + if (!esp_path && !xbootldr_path) + return log_error_errno(SYNTHETIC_ERRNO(ENOENT), + "Couldn't find $BOOT partition. It is recommended to mount it to /boot.\n" + "Alternatively, use --path= to specify path to mount point."); + + if (esp_path) + a = strv_new(esp_path, xbootldr_path); + else + a = strv_new(xbootldr_path); + if (!a) + return log_oom(); + + strv_free_and_replace(arg_path, a); + + if (DEBUG_LOGGING) { + _cleanup_free_ char *j; + + j = strv_join(arg_path, ":"); + log_debug("Using %s as boot loader drop-in search path.", j); + } return 0; } @@ -282,10 +304,9 @@ static const char *skip_slash(const char *path) { } static int verb_status(int argc, char *argv[], void *userdata) { - _cleanup_free_ char *path = NULL, *prefix = NULL, *suffix = NULL, *good = NULL, *bad = NULL; - _cleanup_close_ int fd = -1; uint64_t left, done; + char **p; int r; r = acquire_boot_count_path(&path, &prefix, &left, &done, &suffix); @@ -296,7 +317,7 @@ static int verb_status(int argc, char *argv[], void *userdata) { if (r < 0) return r; - r = acquire_esp(); + r = acquire_path(); if (r < 0) return r; @@ -308,50 +329,61 @@ static int verb_status(int argc, char *argv[], void *userdata) { if (r < 0) return log_oom(); - log_debug("Booted file: %s%s\n" - "The same modified for 'good': %s%s\n" - "The same modified for 'bad': %s%s\n", - arg_path, path, - arg_path, good, - arg_path, bad); + log_debug("Booted file: %s\n" + "The same modified for 'good': %s\n" + "The same modified for 'bad': %s\n", + path, + good, + bad); log_debug("Tries left: %" PRIu64"\n" "Tries done: %" PRIu64"\n", left, done); - fd = open(arg_path, O_DIRECTORY|O_CLOEXEC|O_RDONLY); - if (fd < 0) - return log_error_errno(errno, "Failed to open ESP '%s': %m", arg_path); + STRV_FOREACH(p, arg_path) { + _cleanup_close_ int fd = -1; - if (faccessat(fd, skip_slash(path), F_OK, 0) >= 0) { - puts("indeterminate"); - return 0; + fd = open(*p, O_DIRECTORY|O_CLOEXEC|O_RDONLY); + if (fd < 0) { + if (errno == ENOENT) + continue; + + return log_error_errno(errno, "Failed to open $BOOT partition '%s': %m", *p); + } + + if (faccessat(fd, skip_slash(path), F_OK, 0) >= 0) { + puts("indeterminate"); + return 0; + } + if (errno != ENOENT) + return log_error_errno(errno, "Failed to check if '%s' exists: %m", path); + + if (faccessat(fd, skip_slash(good), F_OK, 0) >= 0) { + puts("good"); + return 0; + } + + if (errno != ENOENT) + return log_error_errno(errno, "Failed to check if '%s' exists: %m", good); + + if (faccessat(fd, skip_slash(bad), F_OK, 0) >= 0) { + puts("bad"); + return 0; + } + if (errno != ENOENT) + return log_error_errno(errno, "Failed to check if '%s' exists: %m", bad); + + /* We didn't find any of the three? If so, let's try the next directory, before we give up. */ } - if (errno != ENOENT) - return log_error_errno(errno, "Failed to check if '%s' exists: %m", path); - if (faccessat(fd, skip_slash(good), F_OK, 0) >= 0) { - puts("good"); - return 0; - } - if (errno != ENOENT) - return log_error_errno(errno, "Failed to check if '%s' exists: %m", good); - - if (faccessat(fd, skip_slash(bad), F_OK, 0) >= 0) { - puts("bad"); - return 0; - } - if (errno != ENOENT) - return log_error_errno(errno, "Failed to check if '%s' exists: %m", bad); - - return log_error_errno(errno, "Couldn't determine boot state: %m"); + return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Couldn't determine boot state: %m"); } static int verb_set(int argc, char *argv[], void *userdata) { _cleanup_free_ char *path = NULL, *prefix = NULL, *suffix = NULL, *good = NULL, *bad = NULL, *parent = NULL; const char *target, *source1, *source2; - _cleanup_close_ int fd = -1; uint64_t done; + char **p; int r; r = acquire_boot_count_path(&path, &prefix, NULL, &done, &suffix); @@ -360,7 +392,7 @@ static int verb_set(int argc, char *argv[], void *userdata) { if (r < 0) return r; - r = acquire_esp(); + r = acquire_path(); if (r < 0) return r; @@ -372,10 +404,6 @@ static int verb_set(int argc, char *argv[], void *userdata) { if (r < 0) return log_oom(); - fd = open(arg_path, O_DIRECTORY|O_CLOEXEC|O_RDONLY); - if (fd < 0) - return log_error_errno(errno, "Failed to open ESP '%s': %m", arg_path); - /* Figure out what rename to what */ if (streq(argv[0], "good")) { target = good; @@ -392,45 +420,58 @@ static int verb_set(int argc, char *argv[], void *userdata) { source2 = bad; } - r = rename_noreplace(fd, skip_slash(source1), fd, skip_slash(target)); - if (r == -EEXIST) - goto exists; - else if (r == -ENOENT) { + STRV_FOREACH(p, arg_path) { + _cleanup_close_ int fd = -1; - r = rename_noreplace(fd, skip_slash(source2), fd, skip_slash(target)); + fd = open(*p, O_DIRECTORY|O_CLOEXEC|O_RDONLY); + if (fd < 0) + return log_error_errno(errno, "Failed to open $BOOT partition '%s': %m", *p); + + r = rename_noreplace(fd, skip_slash(source1), fd, skip_slash(target)); if (r == -EEXIST) goto exists; else if (r == -ENOENT) { - if (access(target, F_OK) >= 0) /* Hmm, if we can't find either source file, maybe the destination already exists? */ + r = rename_noreplace(fd, skip_slash(source2), fd, skip_slash(target)); + if (r == -EEXIST) goto exists; + else if (r == -ENOENT) { + + if (faccessat(fd, skip_slash(target), F_OK, 0) >= 0) /* Hmm, if we can't find either source file, maybe the destination already exists? */ + goto exists; + + if (errno != ENOENT) + return log_error_errno(errno, "Failed to determine if %s already exists: %m", target); + + /* We found none of the snippets here, try the next directory */ + continue; + } else if (r < 0) + return log_error_errno(r, "Failed to rename '%s' to '%s': %m", source2, target); + else + log_debug("Successfully renamed '%s' to '%s'.", source2, target); - return log_error_errno(r, "Can't find boot counter source file for '%s': %m", target); } else if (r < 0) - return log_error_errno(r, "Failed to rename '%s' to '%s': %m", source2, target); + return log_error_errno(r, "Failed to rename '%s' to '%s': %m", source1, target); else - log_debug("Successfully renamed '%s' to '%s'.", source2, target); + log_debug("Successfully renamed '%s' to '%s'.", source1, target); - } else if (r < 0) - return log_error_errno(r, "Failed to rename '%s' to '%s': %m", source1, target); - else - log_debug("Successfully renamed '%s' to '%s'.", source1, target); + /* First, fsync() the directory these files are located in */ + parent = dirname_malloc(target); + if (!parent) + return log_oom(); - /* First, fsync() the directory these files are located in */ - parent = dirname_malloc(path); - if (!parent) - return log_oom(); + r = fsync_path_at(fd, skip_slash(parent)); + if (r < 0) + log_debug_errno(errno, "Failed to synchronize image directory, ignoring: %m"); - r = fsync_path_at(fd, skip_slash(parent)); - if (r < 0) - log_debug_errno(errno, "Failed to synchronize image directory, ignoring: %m"); + /* Secondly, syncfs() the whole file system these files are located in */ + if (syncfs(fd) < 0) + log_debug_errno(errno, "Failed to synchronize $BOOT partition, ignoring: %m"); - /* Secondly, syncfs() the whole file system these files are located in */ - if (syncfs(fd) < 0) - log_debug_errno(errno, "Failed to synchronize ESP, ignoring: %m"); - - log_info("Marked boot as '%s'. (Boot attempt counter is at %" PRIu64".)", argv[0], done); + log_info("Marked boot as '%s'. (Boot attempt counter is at %" PRIu64".)", argv[0], done); + } + log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Can't find boot counter source file for '%s': %m", target); return 1; exists: From a7afbd60e3102d6c73bfcc27e642f1f147daf925 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 28 Jan 2019 20:08:34 +0100 Subject: [PATCH 21/48] bootspec: use SYNTHETIC_ERRNO() where appropriate --- src/shared/bootspec.c | 97 ++++++++++++++++++++----------------------- 1 file changed, 44 insertions(+), 53 deletions(-) diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index 6f28c74a5d..d0ded11610 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -450,32 +450,27 @@ static int verify_esp( * issues. Let's also, silence the error messages. */ if (!relax_checks) { - if (statfs(p, &sfs) < 0) { + if (statfs(p, &sfs) < 0) /* If we are searching for the mount point, don't generate a log message if we can't find the path */ - if (errno == ENOENT && searching) - return -ENOENT; - - return log_full_errno(unprivileged_mode && errno == EACCES ? LOG_DEBUG : LOG_ERR, errno, + return log_full_errno((searching && errno == ENOENT) || + (unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno, "Failed to check file system type of \"%s\": %m", p); - } - if (!F_TYPE_EQUAL(sfs.f_type, MSDOS_SUPER_MAGIC)) { - if (searching) - return -EADDRNOTAVAIL; - - log_error("File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p); - return -ENODEV; - } + if (!F_TYPE_EQUAL(sfs.f_type, MSDOS_SUPER_MAGIC)) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p); } if (stat(p, &st) < 0) - return log_full_errno(unprivileged_mode && errno == EACCES ? LOG_DEBUG : LOG_ERR, errno, + return log_full_errno((searching && errno == ENOENT) || + (unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno, "Failed to determine block device node of \"%s\": %m", p); - if (major(st.st_dev) == 0) { - log_error("Block device node of %p is invalid.", p); - return -ENODEV; - } + if (major(st.st_dev) == 0) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "Block device node of \"%s\" is invalid.", p); t2 = strjoina(p, "/.."); r = stat(t2, &st2); @@ -483,10 +478,10 @@ static int verify_esp( return log_full_errno(unprivileged_mode && errno == EACCES ? LOG_DEBUG : LOG_ERR, errno, "Failed to determine block device node of parent of \"%s\": %m", p); - if (st.st_dev == st2.st_dev) { - log_error("Directory \"%s\" is not the root of the EFI System Partition (ESP) file system.", p); - return -ENODEV; - } + if (st.st_dev == st2.st_dev) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "Directory \"%s\" is not the root of the EFI System Partition (ESP) file system.", p); /* In a container we don't have access to block devices, skip this part of the verification, we trust the * container manager set everything up correctly on its own. Also skip the following verification for non-root user. */ @@ -500,7 +495,7 @@ static int verify_esp( errno = 0; b = blkid_new_probe_from_filename(node); if (!b) - return log_error_errno(errno ?: ENOMEM, "Failed to open file system \"%s\": %m", p); + return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "Failed to open file system \"%s\": %m", p); blkid_probe_enable_superblocks(b, 1); blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE); @@ -509,56 +504,52 @@ static int verify_esp( errno = 0; r = blkid_do_safeprobe(b); - if (r == -2) { - log_error("File system \"%s\" is ambiguous.", p); - return -ENODEV; - } else if (r == 1) { - log_error("File system \"%s\" does not contain a label.", p); - return -ENODEV; - } else if (r != 0) - return log_error_errno(errno ?: EIO, "Failed to probe file system \"%s\": %m", p); + if (r == -2) + return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" is ambiguous.", p); + else if (r == 1) + return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", p); + else if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", p); errno = 0; r = blkid_probe_lookup_value(b, "TYPE", &v, NULL); if (r != 0) - return log_error_errno(errno ?: EIO, "Failed to probe file system type \"%s\": %m", p); - if (!streq(v, "vfat")) { - log_error("File system \"%s\" is not FAT.", p); - return -ENODEV; - } + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system type \"%s\": %m", p); + if (!streq(v, "vfat")) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "File system \"%s\" is not FAT.", p); errno = 0; r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL); if (r != 0) - return log_error_errno(errno ?: EIO, "Failed to probe partition scheme \"%s\": %m", p); - if (!streq(v, "gpt")) { - log_error("File system \"%s\" is not on a GPT partition table.", p); - return -ENODEV; - } + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition scheme \"%s\": %m", p); + if (!streq(v, "gpt")) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "File system \"%s\" is not on a GPT partition table.", p); errno = 0; r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL); if (r != 0) return log_error_errno(errno ?: EIO, "Failed to probe partition type UUID \"%s\": %m", p); - if (!streq(v, "c12a7328-f81f-11d2-ba4b-00a0c93ec93b")) { - log_error("File system \"%s\" has wrong type for an EFI System Partition (ESP).", p); - return -ENODEV; - } + if (!streq(v, "c12a7328-f81f-11d2-ba4b-00a0c93ec93b")) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "File system \"%s\" has wrong type for an EFI System Partition (ESP).", p); errno = 0; r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL); if (r != 0) - return log_error_errno(errno ?: EIO, "Failed to probe partition entry UUID \"%s\": %m", p); + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition entry UUID \"%s\": %m", p); r = sd_id128_from_string(v, &uuid); - if (r < 0) { - log_error("Partition \"%s\" has invalid UUID \"%s\".", p, v); - return -EIO; - } + if (r < 0) + return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", p, v); errno = 0; r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL); if (r != 0) - return log_error_errno(errno ?: EIO, "Failed to probe partition number \"%s\": m", p); + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition number \"%s\": m", p); r = safe_atou32(v, &part); if (r < 0) return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field."); @@ -566,7 +557,7 @@ static int verify_esp( errno = 0; r = blkid_probe_lookup_value(b, "PART_ENTRY_OFFSET", &v, NULL); if (r != 0) - return log_error_errno(errno ?: EIO, "Failed to probe partition offset \"%s\": %m", p); + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition offset \"%s\": %m", p); r = safe_atou64(v, &pstart); if (r < 0) return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field."); @@ -574,7 +565,7 @@ static int verify_esp( errno = 0; r = blkid_probe_lookup_value(b, "PART_ENTRY_SIZE", &v, NULL); if (r != 0) - return log_error_errno(errno ?: EIO, "Failed to probe partition size \"%s\": %m", p); + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition size \"%s\": %m", p); r = safe_atou64(v, &psize); if (r < 0) return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field."); From 23953a3ef85423fa91da5e5e03687583b60a82c2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 29 Jan 2019 12:01:36 +0100 Subject: [PATCH 22/48] bootspec: add comment explaining verify_esp() return codes --- src/shared/bootspec.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index d0ded11610..69bc0d5c60 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -444,6 +444,13 @@ static int verify_esp( assert(p); + /* This logs about all errors, except: + * + * -ENOENT → if 'searching' is set, and the dir doesn't exist + * -EADDRNOTAVAIL → if 'searching' is set, and the dir doesn't look like an ESP + * -EACESS → if 'unprivileged_mode' is set, and we have trouble acessing the thing + */ + relax_checks = getenv_bool("SYSTEMD_RELAX_ESP_CHECKS") > 0; /* Non-root user can only check the status, so if an error occured in the following, it does not cause any From 575d437032a4a8681d9b526ab61eea04c02a8d7c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 30 Jan 2019 18:24:59 +0100 Subject: [PATCH 23/48] bootspec: split out ESP blkid validation into function of its own This makes it easier to add an alternative implementation for this that uses sd-device instead of blkid directly. --- src/shared/bootspec.c | 220 +++++++++++++++++++++++------------------- 1 file changed, 123 insertions(+), 97 deletions(-) diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index 69bc0d5c60..4b4eed0c4c 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -420,6 +420,119 @@ int boot_entries_load_config( /********************************************************************************/ +static int verify_esp_blkid( + dev_t devid, + bool searching, + uint32_t *ret_part, + uint64_t *ret_pstart, + uint64_t *ret_psize, + sd_id128_t *ret_uuid) { + + sd_id128_t uuid = SD_ID128_NULL; + uint64_t pstart = 0, psize = 0; + uint32_t part = 0; + +#if HAVE_BLKID + _cleanup_(blkid_free_probep) blkid_probe b = NULL; + _cleanup_free_ char *node = NULL; + const char *v; + int r; + + r = device_path_make_major_minor(S_IFBLK, devid, &node); + if (r < 0) + return log_error_errno(r, "Failed to format major/minor device path: %m"); + + errno = 0; + b = blkid_new_probe_from_filename(node); + if (!b) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "Failed to open file system \"%s\": %m", node); + + blkid_probe_enable_superblocks(b, 1); + blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE); + blkid_probe_enable_partitions(b, 1); + blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS); + + errno = 0; + r = blkid_do_safeprobe(b); + if (r == -2) + return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" is ambiguous.", node); + else if (r == 1) + return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", node); + else if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", node); + + errno = 0; + r = blkid_probe_lookup_value(b, "TYPE", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system type of \"%s\": %m", node); + if (!streq(v, "vfat")) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "File system \"%s\" is not FAT.", node); + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition scheme of \"%s\": %m", node); + if (!streq(v, "gpt")) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "File system \"%s\" is not on a GPT partition table.", node); + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: EIO, "Failed to probe partition type UUID of \"%s\": %m", node); + if (!streq(v, "c12a7328-f81f-11d2-ba4b-00a0c93ec93b")) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "File system \"%s\" has wrong type for an EFI System Partition (ESP).", node); + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition entry UUID of \"%s\": %m", node); + r = sd_id128_from_string(v, &uuid); + if (r < 0) + return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v); + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition number of \"%s\": m", node); + r = safe_atou32(v, &part); + if (r < 0) + return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field."); + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_OFFSET", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition offset of \"%s\": %m", node); + r = safe_atou64(v, &pstart); + if (r < 0) + return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field."); + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_SIZE", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition size of \"%s\": %m", node); + r = safe_atou64(v, &psize); + if (r < 0) + return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field."); +#endif + + if (ret_part) + *ret_part = part; + if (ret_pstart) + *ret_pstart = pstart; + if (ret_psize) + *ret_psize = psize; + if (ret_uuid) + *ret_uuid = uuid; + + return 0; +} + static int verify_esp( const char *p, bool searching, @@ -428,18 +541,11 @@ static int verify_esp( uint64_t *ret_pstart, uint64_t *ret_psize, sd_id128_t *ret_uuid) { -#if HAVE_BLKID - _cleanup_(blkid_free_probep) blkid_probe b = NULL; - _cleanup_free_ char *node = NULL; - const char *v; -#endif - uint64_t pstart = 0, psize = 0; + struct stat st, st2; - const char *t2; struct statfs sfs; - sd_id128_t uuid = SD_ID128_NULL; - uint32_t part = 0; bool relax_checks; + const char *t2; int r; assert(p); @@ -490,103 +596,23 @@ static int verify_esp( SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), "Directory \"%s\" is not the root of the EFI System Partition (ESP) file system.", p); - /* In a container we don't have access to block devices, skip this part of the verification, we trust the - * container manager set everything up correctly on its own. Also skip the following verification for non-root user. */ + /* In a container we don't have access to block devices, skip this part of the verification, we trust + * the container manager set everything up correctly on its own. Also skip the following verification + * for non-root user. */ if (detect_container() > 0 || unprivileged_mode || relax_checks) goto finish; -#if HAVE_BLKID - r = device_path_make_major_minor(S_IFBLK, st.st_dev, &node); - if (r < 0) - return log_error_errno(r, "Failed to format major/minor device path: %m"); - errno = 0; - b = blkid_new_probe_from_filename(node); - if (!b) - return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "Failed to open file system \"%s\": %m", p); - - blkid_probe_enable_superblocks(b, 1); - blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE); - blkid_probe_enable_partitions(b, 1); - blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS); - - errno = 0; - r = blkid_do_safeprobe(b); - if (r == -2) - return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" is ambiguous.", p); - else if (r == 1) - return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", p); - else if (r != 0) - return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", p); - - errno = 0; - r = blkid_probe_lookup_value(b, "TYPE", &v, NULL); - if (r != 0) - return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system type \"%s\": %m", p); - if (!streq(v, "vfat")) - return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, - SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), - "File system \"%s\" is not FAT.", p); - - errno = 0; - r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL); - if (r != 0) - return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition scheme \"%s\": %m", p); - if (!streq(v, "gpt")) - return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, - SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), - "File system \"%s\" is not on a GPT partition table.", p); - - errno = 0; - r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL); - if (r != 0) - return log_error_errno(errno ?: EIO, "Failed to probe partition type UUID \"%s\": %m", p); - if (!streq(v, "c12a7328-f81f-11d2-ba4b-00a0c93ec93b")) - return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, - SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), - "File system \"%s\" has wrong type for an EFI System Partition (ESP).", p); - - errno = 0; - r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL); - if (r != 0) - return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition entry UUID \"%s\": %m", p); - r = sd_id128_from_string(v, &uuid); - if (r < 0) - return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", p, v); - - errno = 0; - r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL); - if (r != 0) - return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition number \"%s\": m", p); - r = safe_atou32(v, &part); - if (r < 0) - return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field."); - - errno = 0; - r = blkid_probe_lookup_value(b, "PART_ENTRY_OFFSET", &v, NULL); - if (r != 0) - return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition offset \"%s\": %m", p); - r = safe_atou64(v, &pstart); - if (r < 0) - return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field."); - - errno = 0; - r = blkid_probe_lookup_value(b, "PART_ENTRY_SIZE", &v, NULL); - if (r != 0) - return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition size \"%s\": %m", p); - r = safe_atou64(v, &psize); - if (r < 0) - return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field."); -#endif + return verify_esp_blkid(st.st_dev, searching, ret_part, ret_pstart, ret_psize, ret_uuid); finish: if (ret_part) - *ret_part = part; + *ret_part = 0; if (ret_pstart) - *ret_pstart = pstart; + *ret_pstart = 0; if (ret_psize) - *ret_psize = psize; + *ret_psize = 0; if (ret_uuid) - *ret_uuid = uuid; + *ret_uuid = SD_ID128_NULL; return 0; } From ad95aa44d62b2a0f9784df3293e45bf4bf53b788 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Feb 2019 18:17:01 +0100 Subject: [PATCH 24/48] bootspec: also split out XBOOTLDR partition blkid code into its own function --- src/shared/bootspec.c | 152 +++++++++++++++++++++++------------------- 1 file changed, 83 insertions(+), 69 deletions(-) diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index 4b4eed0c4c..cfae1cc0b3 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -680,20 +680,94 @@ found: return 0; } +static int verify_xbootldr_blkid( + dev_t devid, + bool searching, + sd_id128_t *ret_uuid) { + + sd_id128_t uuid = SD_ID128_NULL; + +#if HAVE_BLKID + _cleanup_(blkid_free_probep) blkid_probe b = NULL; + _cleanup_free_ char *node = NULL; + const char *v; + int r; + + r = device_path_make_major_minor(S_IFBLK, devid, &node); + if (r < 0) + return log_error_errno(r, "Failed to format major/minor device path: %m"); + errno = 0; + b = blkid_new_probe_from_filename(node); + if (!b) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "Failed to open file system \"%s\": %m", node); + + blkid_probe_enable_partitions(b, 1); + blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS); + + errno = 0; + r = blkid_do_safeprobe(b); + if (r == -2) + return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" is ambiguous.", node); + else if (r == 1) + return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", node); + else if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", node); + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition scheme of \"%s\": %m", node); + if (streq(v, "gpt")) { + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition type UUID of \"%s\": %m", node); + if (!streq(v, "bc13c2ff-59e6-4262-a352-b275fd6f7172")) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV), + "File system \"%s\" has wrong type for extended boot loader partition.", node); + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition entry UUID of \"%s\": %m", node); + r = sd_id128_from_string(v, &uuid); + if (r < 0) + return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v); + + } else if (streq(v, "dos")) { + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition type UUID of \"%s\": %m", node); + if (!streq(v, "0xea")) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV), + "File system \"%s\" has wrong type for extended boot loader partition.", node); + + } else + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV), + "File system \"%s\" is not on a GPT or DOS partition table.", node); +#endif + + if (ret_uuid) + *ret_uuid = uuid; + + return 0; +} + static int verify_xbootldr( const char *p, bool searching, bool unprivileged_mode, sd_id128_t *ret_uuid) { -#if HAVE_BLKID - _cleanup_(blkid_free_probep) blkid_probe b = NULL; - _cleanup_free_ char *node = NULL; - const char *v; -#endif + struct stat st, st2; - const char *t2; - sd_id128_t uuid = SD_ID128_NULL; bool relax_checks; + const char *t2; int r; assert(p); @@ -727,71 +801,11 @@ static int verify_xbootldr( if (detect_container() > 0 || unprivileged_mode || relax_checks) goto finish; -#if HAVE_BLKID - r = device_path_make_major_minor(S_IFBLK, st.st_dev, &node); - if (r < 0) - return log_error_errno(r, "Failed to format major/minor device path: %m"); - errno = 0; - b = blkid_new_probe_from_filename(node); - if (!b) - return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "Failed to open file system \"%s\": %m", p); - - blkid_probe_enable_partitions(b, 1); - blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS); - - errno = 0; - r = blkid_do_safeprobe(b); - if (r == -2) - return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" is ambiguous.", p); - else if (r == 1) - return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", p); - else if (r != 0) - return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", p); - - errno = 0; - r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL); - if (r != 0) - return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition scheme \"%s\": %m", p); - if (streq(v, "gpt")) { - - errno = 0; - r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL); - if (r != 0) - return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition type UUID \"%s\": %m", p); - if (!streq(v, "bc13c2ff-59e6-4262-a352-b275fd6f7172")) - return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, - searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV), - "File system \"%s\" has wrong type for extended boot loader partition.", p); - - errno = 0; - r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL); - if (r != 0) - return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition entry UUID \"%s\": %m", p); - r = sd_id128_from_string(v, &uuid); - if (r < 0) - return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", p, v); - - } else if (streq(v, "dos")) { - - errno = 0; - r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL); - if (r != 0) - return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition type UUID \"%s\": %m", p); - if (!streq(v, "0xea")) - return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, - searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV), - "File system \"%s\" has wrong type for extended boot loader partition.", p); - - } else - return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, - searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV), - "File system \"%s\" is not on a GPT or DOS partition table.", p); - -#endif + return verify_xbootldr_blkid(st.st_dev, searching, ret_uuid); finish: if (ret_uuid) - *ret_uuid = uuid; + *ret_uuid = SD_ID128_NULL; return 0; } From cedb9eec765ddc5ac28b3141cbb87313650c563d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Feb 2019 18:45:41 +0100 Subject: [PATCH 25/48] bootspec: if unprivileged validate partition data with udev rather than blkid directly udev metadata access works unprivileged, which the blkid stuff doesn't (as that needs raw device node access). Hence let's use udev if we lack privs, and raw device access only if root. --- src/shared/bootspec.c | 104 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 100 insertions(+), 4 deletions(-) diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index cfae1cc0b3..47da478c3e 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -3,6 +3,7 @@ #include #include +#include "sd-device.h" #include "sd-id128.h" #include "alloc-util.h" @@ -533,6 +534,94 @@ static int verify_esp_blkid( return 0; } +static int verify_esp_udev( + dev_t devid, + bool searching, + uint32_t *ret_part, + uint64_t *ret_pstart, + uint64_t *ret_psize, + sd_id128_t *ret_uuid) { + + _cleanup_(sd_device_unrefp) sd_device *d = NULL; + _cleanup_free_ char *node = NULL; + sd_id128_t uuid = SD_ID128_NULL; + uint64_t pstart = 0, psize = 0; + uint32_t part = 0; + const char *v; + int r; + + r = device_path_make_major_minor(S_IFBLK, devid, &node); + if (r < 0) + return log_error_errno(r, "Failed to format major/minor device path: %m"); + + r = sd_device_new_from_devnum(&d, 'b', devid); + if (r < 0) + return log_error_errno(r, "Failed to get device from device number: %m"); + + r = sd_device_get_property_value(d, "ID_FS_TYPE", &v); + if (r < 0) + return log_error_errno(r, "Failed to get device property: %m"); + if (!streq(v, "vfat")) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "File system \"%s\" is not FAT.", node ); + + r = sd_device_get_property_value(d, "ID_PART_ENTRY_SCHEME", &v); + if (r < 0) + return log_error_errno(r, "Failed to get device property: %m"); + if (!streq(v, "gpt")) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "File system \"%s\" is not on a GPT partition table.", node); + + r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v); + if (r < 0) + return log_error_errno(r, "Failed to get device property: %m"); + if (!streq(v, "c12a7328-f81f-11d2-ba4b-00a0c93ec93b")) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "File system \"%s\" has wrong type for an EFI System Partition (ESP).", node); + + r = sd_device_get_property_value(d, "ID_PART_ENTRY_UUID", &v); + if (r < 0) + return log_error_errno(r, "Failed to get device property: %m"); + r = sd_id128_from_string(v, &uuid); + if (r < 0) + return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v); + + r = sd_device_get_property_value(d, "ID_PART_ENTRY_NUMBER", &v); + if (r < 0) + return log_error_errno(r, "Failed to get device property: %m"); + r = safe_atou32(v, &part); + if (r < 0) + return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field."); + + r = sd_device_get_property_value(d, "ID_PART_ENTRY_OFFSET", &v); + if (r < 0) + return log_error_errno(r, "Failed to get device property: %m"); + r = safe_atou64(v, &pstart); + if (r < 0) + return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field."); + + r = sd_device_get_property_value(d, "ID_PART_ENTRY_SIZE", &v); + if (r < 0) + return log_error_errno(r, "Failed to get device property: %m"); + r = safe_atou64(v, &psize); + if (r < 0) + return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field."); + + if (ret_part) + *ret_part = part; + if (ret_pstart) + *ret_pstart = pstart; + if (ret_psize) + *ret_psize = psize; + if (ret_uuid) + *ret_uuid = uuid; + + return 0; +} + static int verify_esp( const char *p, bool searching, @@ -597,12 +686,19 @@ static int verify_esp( "Directory \"%s\" is not the root of the EFI System Partition (ESP) file system.", p); /* In a container we don't have access to block devices, skip this part of the verification, we trust - * the container manager set everything up correctly on its own. Also skip the following verification - * for non-root user. */ - if (detect_container() > 0 || unprivileged_mode || relax_checks) + * the container manager set everything up correctly on its own. */ + if (detect_container() > 0 || relax_checks) goto finish; - return verify_esp_blkid(st.st_dev, searching, ret_part, ret_pstart, ret_psize, ret_uuid); + /* If we are unprivileged we ask udev for the metadata about the partition. If we are privileged we + * use blkid instead. Why? Because this code is called from 'bootctl' which is pretty much an + * emergency recovery tool that should also work when udev isn't up (i.e. from the emergency shell), + * however blkid can't work if we have no privileges to access block devices directly, which is why + * we use udev in that case. */ + if (unprivileged_mode) + return verify_esp_udev(st.st_dev, searching, ret_part, ret_pstart, ret_psize, ret_uuid); + else + return verify_esp_blkid(st.st_dev, searching, ret_part, ret_pstart, ret_psize, ret_uuid); finish: if (ret_part) From 18ae9ef1a52d0f903ada0c5225cb8710a6c0359b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Feb 2019 18:52:24 +0100 Subject: [PATCH 26/48] bootspec: split out code that validates whether directory is top-level dir of fs Let's add a new function that checks whether some directory is the top-level directory inside an fs, splitting out the code for this from verify_esp(). While we are at it, let's slightly improve the code, so that we can correctly work if we have no priviliges but the ESP is mounted unaccessible: if we can't stat() the path "$ESP/.." then manually remove the last component of $ESP and check that instead. Which is very similar in behaviour, and hopefully good enough in the unprivileged case. --- src/shared/bootspec.c | 91 +++++++++++++++++++++++++++++++------------ 1 file changed, 67 insertions(+), 24 deletions(-) diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index 47da478c3e..4fbf576e70 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -622,6 +622,66 @@ static int verify_esp_udev( return 0; } +static int verify_fsroot_dir( + const char *path, + bool searching, + bool unprivileged_mode, + dev_t *ret_dev) { + + struct stat st, st2; + const char *t2; + int r; + + assert(path); + assert(ret_dev); + + if (stat(path, &st) < 0) + return log_full_errno((searching && errno == ENOENT) || + (unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno, + "Failed to determine block device node of \"%s\": %m", path); + + if (major(st.st_dev) == 0) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "Block device node of \"%s\" is invalid.", path); + + t2 = strjoina(path, "/.."); + if (stat(t2, &st2) < 0) { + if (errno != EACCES) + r = -errno; + else { + _cleanup_free_ char *parent = NULL; + + /* If going via ".." didn't work due to EACCESS, then let's determine the parent path + * directly instead. It's not as good, due to symlinks and such, but we can't do + * anything better here. */ + + parent = dirname_malloc(path); + if (!parent) + return log_oom(); + + if (stat(parent, &st2) < 0) + r = -errno; + else + r = 0; + } + + if (r < 0) + return log_full_errno(unprivileged_mode && r == -EACCES ? LOG_DEBUG : LOG_ERR, r, + "Failed to determine block device node of parent of \"%s\": %m", path); + } + + if (st.st_dev == st2.st_dev) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "Directory \"%s\" is not the root of the file system.", path); + + if (ret_dev) + *ret_dev = st.st_dev; + + return 0; +} + static int verify_esp( const char *p, bool searching, @@ -631,10 +691,8 @@ static int verify_esp( uint64_t *ret_psize, sd_id128_t *ret_uuid) { - struct stat st, st2; - struct statfs sfs; bool relax_checks; - const char *t2; + dev_t devid; int r; assert(p); @@ -652,6 +710,8 @@ static int verify_esp( * issues. Let's also, silence the error messages. */ if (!relax_checks) { + struct statfs sfs; + if (statfs(p, &sfs) < 0) /* If we are searching for the mount point, don't generate a log message if we can't find the path */ return log_full_errno((searching && errno == ENOENT) || @@ -664,26 +724,9 @@ static int verify_esp( "File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p); } - if (stat(p, &st) < 0) - return log_full_errno((searching && errno == ENOENT) || - (unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno, - "Failed to determine block device node of \"%s\": %m", p); - - if (major(st.st_dev) == 0) - return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, - SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), - "Block device node of \"%s\" is invalid.", p); - - t2 = strjoina(p, "/.."); - r = stat(t2, &st2); + r = verify_fsroot_dir(p, searching, unprivileged_mode, &devid); if (r < 0) - return log_full_errno(unprivileged_mode && errno == EACCES ? LOG_DEBUG : LOG_ERR, errno, - "Failed to determine block device node of parent of \"%s\": %m", p); - - if (st.st_dev == st2.st_dev) - return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, - SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), - "Directory \"%s\" is not the root of the EFI System Partition (ESP) file system.", p); + return r; /* In a container we don't have access to block devices, skip this part of the verification, we trust * the container manager set everything up correctly on its own. */ @@ -696,9 +739,9 @@ static int verify_esp( * however blkid can't work if we have no privileges to access block devices directly, which is why * we use udev in that case. */ if (unprivileged_mode) - return verify_esp_udev(st.st_dev, searching, ret_part, ret_pstart, ret_psize, ret_uuid); + return verify_esp_udev(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid); else - return verify_esp_blkid(st.st_dev, searching, ret_part, ret_pstart, ret_psize, ret_uuid); + return verify_esp_blkid(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid); finish: if (ret_part) From 85d021023fcd491ceedc004bc28fc8d5c90b0c69 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Feb 2019 19:02:03 +0100 Subject: [PATCH 27/48] bootspec: also optoinally validate XBOOTLDR partition with udev insteado of blkid --- src/shared/bootspec.c | 70 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 65 insertions(+), 5 deletions(-) diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index 4fbf576e70..0d0b09a77c 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -898,6 +898,66 @@ static int verify_xbootldr_blkid( return 0; } +static int verify_xbootldr_udev( + dev_t devid, + bool searching, + sd_id128_t *ret_uuid) { + + _cleanup_(sd_device_unrefp) sd_device *d = NULL; + _cleanup_free_ char *node = NULL; + sd_id128_t uuid = SD_ID128_NULL; + const char *v; + int r; + + r = device_path_make_major_minor(S_IFBLK, devid, &node); + if (r < 0) + return log_error_errno(r, "Failed to format major/minor device path: %m"); + + r = sd_device_new_from_devnum(&d, 'b', devid); + if (r < 0) + return log_error_errno(r, "Failed to get device from device number: %m"); + + r = sd_device_get_property_value(d, "ID_PART_ENTRY_SCHEME", &v); + if (r < 0) + return log_error_errno(r, "Failed to get device property: %m"); + + if (streq(v, "gpt")) { + + r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v); + if (r < 0) + return log_error_errno(r, "Failed to get device property: %m"); + if (!streq(v, "bc13c2ff-59e6-4262-a352-b275fd6f7172")) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV), + "File system \"%s\" has wrong type for extended boot loader partition.", node); + + r = sd_device_get_property_value(d, "ID_PART_ENTRY_UUID", &v); + if (r < 0) + return log_error_errno(r, "Failed to get device property: %m"); + r = sd_id128_from_string(v, &uuid); + if (r < 0) + return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v); + + } else if (streq(v, "dos")) { + + r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v); + if (r < 0) + return log_error_errno(r, "Failed to get device property: %m"); + if (!streq(v, "0xea")) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV), + "File system \"%s\" has wrong type for extended boot loader partition.", node); + } else + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV), + "File system \"%s\" is not on a GPT or DOS partition table.", node); + + if (ret_uuid) + *ret_uuid = uuid; + + return 0; +} + static int verify_xbootldr( const char *p, bool searching, @@ -934,13 +994,13 @@ static int verify_xbootldr( SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), "Directory \"%s\" is not the root of the XBOOTLDR file system.", p); - /* In a container we don't have access to block devices, skip this part of the verification, we trust - * the container manager set everything up correctly on its own. Also skip the following verification - * for non-root user. */ - if (detect_container() > 0 || unprivileged_mode || relax_checks) + if (detect_container() > 0 || relax_checks) goto finish; - return verify_xbootldr_blkid(st.st_dev, searching, ret_uuid); + if (unprivileged_mode) + return verify_xbootldr_udev(st.st_dev, searching, ret_uuid); + else + return verify_xbootldr_blkid(st.st_dev, searching, ret_uuid); finish: if (ret_uuid) From 4a4994b68dd1e61696b122c74111ed40e329027e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Feb 2019 19:11:31 +0100 Subject: [PATCH 28/48] bootspec: use verify_fsroot_dir() in verify_xbootldr() too Let's share some code between verify_xbootldr() and verify_esp(). --- src/shared/bootspec.c | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index 0d0b09a77c..b20af117b3 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -964,43 +964,25 @@ static int verify_xbootldr( bool unprivileged_mode, sd_id128_t *ret_uuid) { - struct stat st, st2; bool relax_checks; - const char *t2; + dev_t devid; int r; assert(p); relax_checks = getenv_bool("SYSTEMD_RELAX_XBOOTLDR_CHECKS") > 0; - if (stat(p, &st) < 0) - return log_full_errno((searching && errno == ENOENT) || - (unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno, - "Failed to determine block device node of \"%s\": %m", p); - - if (major(st.st_dev) == 0) - return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, - SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), - "Block device node of \"%s\" is invalid.", p); - - t2 = strjoina(p, "/.."); - r = stat(t2, &st2); + r = verify_fsroot_dir(p, searching, unprivileged_mode, &devid); if (r < 0) - return log_full_errno(unprivileged_mode && errno == EACCES ? LOG_DEBUG : LOG_ERR, errno, - "Failed to determine block device node of parent of \"%s\": %m", p); - - if (st.st_dev == st2.st_dev) - return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, - SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), - "Directory \"%s\" is not the root of the XBOOTLDR file system.", p); + return r; if (detect_container() > 0 || relax_checks) goto finish; if (unprivileged_mode) - return verify_xbootldr_udev(st.st_dev, searching, ret_uuid); + return verify_xbootldr_udev(devid, searching, ret_uuid); else - return verify_xbootldr_blkid(st.st_dev, searching, ret_uuid); + return verify_xbootldr_blkid(devid, searching, ret_uuid); finish: if (ret_uuid) From 882b3bd6c5cb4f629d0142f5c0d3fc542f45e0de Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Feb 2019 19:15:21 +0100 Subject: [PATCH 29/48] bootctl: let's make sure we always add empty line after EFI binary output Let's make sure we output another "\n", even if we fail this function, so that the output we started is separated properly from what is following. --- src/boot/bootctl.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index 5cc187f5ab..be8a90445c 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -224,19 +224,21 @@ static int status_binaries(const char *esp_path, sd_id128_t partition) { r = enumerate_binaries(esp_path, "EFI/systemd", NULL); if (r < 0) - return r; + goto finish; if (r == 0) log_info("systemd-boot not installed in ESP."); r = enumerate_binaries(esp_path, "EFI/BOOT", "boot"); if (r < 0) - return r; + goto finish; if (r == 0) log_info("No default/fallback boot loader installed in ESP."); - printf("\n"); + r = 0; - return 0; +finish: + printf("\n"); + return r; } static int print_efi_option(uint16_t id, bool in_order) { From 0d73a81611c62419411b8e033d0f3b3a01472918 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 6 Feb 2019 17:48:40 +0100 Subject: [PATCH 30/48] bootctl: properly handle readdir() errors --- src/boot/bootctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index be8a90445c..a529989ea0 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -583,7 +583,7 @@ static int install_binaries(const char *esp_path, bool force) { if (!d) return log_error_errno(errno, "Failed to open \""BOOTLIBDIR"\": %m"); - FOREACH_DIRENT(de, d, break) { + FOREACH_DIRENT(de, d, return log_error_errno(errno, "Failed to read \""BOOTLIBDIR"\": %m")) { int k; if (!endswith_no_case(de->d_name, ".efi")) From 5e146a75b3a5f44f2315a1643cfd4491d57d12a2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 6 Feb 2019 17:49:37 +0100 Subject: [PATCH 31/48] bootspec: also look for boot loader spec type 2 entries (i.e. unified kernel images) sd-boot reads them, and hence we should from our userspace side too --- src/shared/bootspec.c | 253 +++++++++++++++++++++++++++++++++++++++++ src/shared/meson.build | 1 + src/shared/pe-header.h | 59 ++++++++++ 3 files changed, 313 insertions(+) create mode 100644 src/shared/pe-header.h diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index b20af117b3..ee285b92e6 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -12,15 +12,19 @@ #include "conf-files.h" #include "def.h" #include "device-nodes.h" +#include "dirent-util.h" #include "efivars.h" +#include "env-file.h" #include "env-util.h" #include "fd-util.h" #include "fileio.h" #include "parse-util.h" #include "path-util.h" +#include "pe-header.h" #include "stat-util.h" #include "string-util.h" #include "strv.h" +#include "unaligned.h" #include "virt.h" static void boot_entry_free(BootEntry *entry) { @@ -265,6 +269,245 @@ static int boot_entries_find( return 0; } +static int boot_entry_load_unified( + const char *root, + const char *path, + const char *osrelease, + const char *cmdline, + BootEntry *ret) { + + _cleanup_free_ char *os_pretty_name = NULL, *os_id = NULL, *version_id = NULL, *build_id = NULL; + _cleanup_(boot_entry_free) BootEntry tmp = {}; + _cleanup_fclose_ FILE *f = NULL; + const char *k; + int r; + + assert(root); + assert(path); + assert(osrelease); + + k = path_startswith(path, root); + if (!k) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Path is not below root: %s", path); + + f = fmemopen((void*) osrelease, strlen(osrelease), "r"); + if (!f) + return log_error_errno(errno, "Failed to open os-release buffer: %m"); + + r = parse_env_file(f, "os-release", + "PRETTY_NAME", &os_pretty_name, + "ID", &os_id, + "VERSION_ID", &version_id, + "BUILD_ID", &build_id); + if (r < 0) + return log_error_errno(r, "Failed to parse os-release data from unified kernel image %s: %m", path); + + if (!os_pretty_name || !os_id || !(version_id || build_id)) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Missing fields in os-release data from unified kernel image %s, refusing.", path); + + tmp.id = strjoin(os_id, "-", version_id ?: build_id); + if (!tmp.id) + return log_oom(); + + tmp.path = strdup(path); + if (!tmp.path) + return log_oom(); + + tmp.root = strdup(root); + if (!tmp.root) + return log_oom(); + + tmp.kernel = strdup(skip_leading_chars(k, "/")); + if (!tmp.kernel) + return log_oom(); + + tmp.options = strv_new(skip_leading_chars(cmdline, WHITESPACE)); + if (!tmp.options) + return log_oom(); + + delete_trailing_chars(tmp.options[0], WHITESPACE); + + tmp.title = TAKE_PTR(os_pretty_name); + + *ret = tmp; + tmp = (BootEntry) {}; + return 0; +} + +/* Maximum PE section we are willing to load (Note that sections we are not interested in may be larger, but + * the ones we do care about and we are willing to load into memory have this size limit.) */ +#define PE_SECTION_SIZE_MAX (4U*1024U*1024U) + +static int find_sections( + int fd, + char **ret_osrelease, + char **ret_cmdline) { + + _cleanup_free_ struct PeSectionHeader *sections = NULL; + _cleanup_free_ char *osrelease = NULL, *cmdline = NULL; + size_t i, n_sections; + struct DosFileHeader dos; + struct PeHeader pe; + uint64_t start; + ssize_t n; + + n = pread(fd, &dos, sizeof(dos), 0); + if (n < 0) + return log_error_errno(errno, "Failed read DOS header: %m"); + if (n != sizeof(dos)) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read while reading DOS header, refusing."); + + if (dos.Magic[0] != 'M' || dos.Magic[1] != 'Z') + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "DOS executable magic missing, refusing."); + + start = unaligned_read_le32(&dos.ExeHeader); + n = pread(fd, &pe, sizeof(pe), start); + if (n < 0) + return log_error_errno(errno, "Failed to read PE header: %m"); + if (n != sizeof(pe)) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read while reading PE header, refusing."); + + if (pe.Magic[0] != 'P' || pe.Magic[1] != 'E' || pe.Magic[2] != 0 || pe.Magic[3] != 0) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "PE executable magic missing, refusing."); + + n_sections = unaligned_read_le16(&pe.FileHeader.NumberOfSections); + if (n_sections > 96) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "PE header has too many sections, refusing."); + + sections = new(struct PeSectionHeader, n_sections); + if (!sections) + return log_oom(); + + n = pread(fd, sections, + n_sections * sizeof(struct PeSectionHeader), + start + sizeof(pe) + unaligned_read_le16(&pe.FileHeader.SizeOfOptionalHeader)); + if (n < 0) + return log_error_errno(errno, "Failed to read section data: %m"); + if ((size_t) n != n_sections * sizeof(struct PeSectionHeader)) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read while reading sections, refusing."); + + for (i = 0; i < n_sections; i++) { + _cleanup_free_ char *k = NULL; + uint32_t offset, size; + char **b; + + if (strneq((char*) sections[i].Name, ".osrel", sizeof(sections[i].Name))) + b = &osrelease; + else if (strneq((char*) sections[i].Name, ".cmdline", sizeof(sections[i].Name))) + b = &cmdline; + else + continue; + + if (*b) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Duplicate section %s, refusing.", sections[i].Name); + + offset = unaligned_read_le32(§ions[i].PointerToRawData); + size = unaligned_read_le32(§ions[i].VirtualSize); + + if (size > PE_SECTION_SIZE_MAX) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Section %s too large, refusing.", sections[i].Name); + + k = new(char, size+1); + if (!k) + return log_oom(); + + n = pread(fd, k, size, offset); + if (n < 0) + return log_error_errno(errno, "Failed to read section payload: %m"); + if (n != size) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read while reading section payload, refusing:"); + + /* Allow one trailing NUL byte, but nothing more. */ + if (size > 0 && memchr(k, 0, size - 1)) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Section contains embedded NUL byte: %m"); + + k[size] = 0; + *b = TAKE_PTR(k); + } + + if (!osrelease) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Image lacks .osrel section, refusing."); + + if (ret_osrelease) + *ret_osrelease = TAKE_PTR(osrelease); + if (ret_cmdline) + *ret_cmdline = TAKE_PTR(cmdline); + + return 0; +} + +static int boot_entries_find_unified( + const char *root, + const char *dir, + BootEntry **entries, + size_t *n_entries) { + + _cleanup_(closedirp) DIR *d = NULL; + size_t n_allocated = *n_entries; + bool added = false; + struct dirent *de; + int r; + + assert(root); + assert(dir); + assert(entries); + assert(n_entries); + + d = opendir(dir); + if (!d) { + if (errno == ENOENT) + return 0; + + return log_error_errno(errno, "Failed to open %s: %m", dir); + } + + FOREACH_DIRENT(de, d, return log_error_errno(errno, "Failed to read %s: %m", dir)) { + _cleanup_free_ char *j = NULL, *osrelease = NULL, *cmdline = NULL; + _cleanup_close_ int fd = -1; + + if (!dirent_is_file(de)) + continue; + + if (!endswith_no_case(de->d_name, ".efi")) + continue; + + if (!GREEDY_REALLOC0(*entries, n_allocated, *n_entries + 1)) + return log_oom(); + + fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC|O_NONBLOCK); + if (fd < 0) { + log_warning_errno(errno, "Failed to open %s/%s, ignoring: %m", dir, de->d_name); + continue; + } + + r = fd_verify_regular(fd); + if (r < 0) { + log_warning_errno(errno, "File %s/%s is not regular, ignoring: %m", dir, de->d_name); + continue; + } + + r = find_sections(fd, &osrelease, &cmdline); + if (r < 0) + continue; + + j = path_join(dir, de->d_name); + if (!j) + return log_oom(); + + r = boot_entry_load_unified(root, j, osrelease, cmdline, *entries + *n_entries); + if (r < 0) + continue; + + (*n_entries) ++; + added = true; + } + + if (added) + typesafe_qsort(*entries, *n_entries, boot_entry_compare); + + return 0; +} + static bool find_nonunique(BootEntry *entries, size_t n_entries, bool *arr) { size_t i, j; bool non_unique = false; @@ -392,6 +635,11 @@ int boot_entries_load_config( r = boot_entries_find(esp_path, p, &config->entries, &config->n_entries); if (r < 0) return r; + + p = strjoina(esp_path, "/EFI/Linux/"); + r = boot_entries_find_unified(esp_path, p, &config->entries, &config->n_entries); + if (r < 0) + return r; } if (xbootldr_path) { @@ -399,6 +647,11 @@ int boot_entries_load_config( r = boot_entries_find(xbootldr_path, p, &config->entries, &config->n_entries); if (r < 0) return r; + + p = strjoina(xbootldr_path, "/EFI/Linux/"); + r = boot_entries_find_unified(xbootldr_path, p, &config->entries, &config->n_entries); + if (r < 0) + return r; } r = boot_entries_uniquify(config->entries, config->n_entries); diff --git a/src/shared/meson.build b/src/shared/meson.build index 99d6ba14f1..1a5ba8c0f4 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -117,6 +117,7 @@ shared_sources = files(''' pager.h path-lookup.c path-lookup.h + pe-header.h pretty-print.c pretty-print.h ptyfwd.c diff --git a/src/shared/pe-header.h b/src/shared/pe-header.h new file mode 100644 index 0000000000..a362917523 --- /dev/null +++ b/src/shared/pe-header.h @@ -0,0 +1,59 @@ +#pragma once + +#include + +#include "macro.h" +#include "sparse-endian.h" + +struct DosFileHeader { + uint8_t Magic[2]; + le16_t LastSize; + le16_t nBlocks; + le16_t nReloc; + le16_t HdrSize; + le16_t MinAlloc; + le16_t MaxAlloc; + le16_t ss; + le16_t sp; + le16_t Checksum; + le16_t ip; + le16_t cs; + le16_t RelocPos; + le16_t nOverlay; + le16_t reserved[4]; + le16_t OEMId; + le16_t OEMInfo; + le16_t reserved2[10]; + le32_t ExeHeader; +} _packed_; + +#define PE_HEADER_MACHINE_I386 0x014cU +#define PE_HEADER_MACHINE_X64 0x8664U + +struct PeFileHeader { + le16_t Machine; + le16_t NumberOfSections; + le32_t TimeDateStamp; + le32_t PointerToSymbolTable; + le32_t NumberOfSymbols; + le16_t SizeOfOptionalHeader; + le16_t Characteristics; +} _packed_; + +struct PeHeader { + uint8_t Magic[4]; + struct PeFileHeader FileHeader; +} _packed_; + +struct PeSectionHeader { + uint8_t Name[8]; + le32_t VirtualSize; + le32_t VirtualAddress; + le32_t SizeOfRawData; + le32_t PointerToRawData; + le32_t PointerToRelocations; + le32_t PointerToLinenumbers; + le16_t NumberOfRelocations; + le16_t NumberOfLinenumbers; + le32_t Characteristics; + } _packed_; From dd2bf34cd0a39ad7869177f4379de55ad3603e53 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 6 Feb 2019 17:53:43 +0100 Subject: [PATCH 32/48] bootspec: only sort entries list once Instead of re-sorting entries list each time we loaded enrties from a specific source, let's just sort them once at the end. --- src/shared/bootspec.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index ee285b92e6..2db6425f2f 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -238,7 +238,6 @@ static int boot_entries_find( _cleanup_strv_free_ char **files = NULL; size_t n_allocated = *n_entries; - bool added = false; char **f; int r; @@ -260,12 +259,8 @@ static int boot_entries_find( continue; (*n_entries) ++; - added = true; } - if (added) - typesafe_qsort(*entries, *n_entries, boot_entry_compare); - return 0; } @@ -444,7 +439,6 @@ static int boot_entries_find_unified( _cleanup_(closedirp) DIR *d = NULL; size_t n_allocated = *n_entries; - bool added = false; struct dirent *de; int r; @@ -499,12 +493,8 @@ static int boot_entries_find_unified( continue; (*n_entries) ++; - added = true; } - if (added) - typesafe_qsort(*entries, *n_entries, boot_entry_compare); - return 0; } @@ -654,6 +644,8 @@ int boot_entries_load_config( return r; } + typesafe_qsort(config->entries, config->n_entries, boot_entry_compare); + r = boot_entries_uniquify(config->entries, config->n_entries); if (r < 0) return log_error_errno(r, "Failed to uniquify boot entries: %m"); From c8ad5996b0c209511365f03ec82a1812c444f414 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 6 Feb 2019 18:07:50 +0100 Subject: [PATCH 33/48] sd-boot: pass device handle to config_entry_add_linux() This makes the code a bit simpler (after all the call is not interested in the loaded image, just where it is found), and more like config_load_entries() which takes the same arguments. This also makes things easier for us later on, when we add support for discovering images in $XBOOTLDR partitions. --- src/boot/efi/boot.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index 9bf6895831..909e4b0e62 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -1836,7 +1836,7 @@ static VOID config_entry_add_osx(Config *config) { static VOID config_entry_add_linux( Config *config, - EFI_LOADED_IMAGE *loaded_image, + EFI_HANDLE *device, EFI_FILE *root_dir) { EFI_FILE_HANDLE linux_dir; @@ -1926,7 +1926,7 @@ static VOID config_entry_add_linux( conf = PoolPrint(L"%s-%s", os_id, os_version ? : os_build); path = PoolPrint(L"\\EFI\\Linux\\%s", f->FileName); - entry = config_entry_add_loader(config, loaded_image->DeviceHandle, LOADER_LINUX, conf, 'l', os_name, path); + entry = config_entry_add_loader(config, device, LOADER_LINUX, conf, 'l', os_name, path); FreePool(content); content = NULL; @@ -2142,7 +2142,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { config_load_defaults(&config, root_dir); /* scan /EFI/Linux/ directory */ - config_entry_add_linux(&config, loaded_image, root_dir); + config_entry_add_linux(&config, loaded_image->DeviceHandle, root_dir); /* scan /loader/entries/\*.conf files */ config_load_entries(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path); From 17ad0f0f4b1cbe3ab9a47b61fec1cc9b0c05fb94 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 8 Feb 2019 13:03:26 +0100 Subject: [PATCH 34/48] sd-boot: don't dereference NULL ptr if loaded_image_path is NULL In a follow-up commit we'd like to invoke config_entry_add_from_file() on partitions that are not the ESP, let's prepare fpr that and allow loaded_image_path to be passed as NULL. --- src/boot/efi/boot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index 909e4b0e62..35c649fde6 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -1316,7 +1316,7 @@ static VOID config_entry_add_from_file( entry->loader = stra_to_path(value); /* do not add an entry for ourselves */ - if (StriCmp(entry->loader, loaded_image_path) == 0) { + if (loaded_image_path && StriCmp(entry->loader, loaded_image_path) == 0) { entry->type = LOADER_UNDEFINED; break; } From fa2a3bbdd8bfcc3402c2ad9e941eb3911971e09a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 8 Feb 2019 13:04:41 +0100 Subject: [PATCH 35/48] sd-boot: also look for boot loader entries in the XBOOTLDR partition The specification always said so, let's actually implement this. Unfortunately UEFI's own APIs don't allow us to search for partition type GUID, hence we have to implement a minimal GPT parser ourselves. --- src/boot/efi/boot.c | 192 +++++++++++++++++++++++++++++++++++++++ src/boot/efi/crc32.c | 142 +++++++++++++++++++++++++++++ src/boot/efi/crc32.h | 8 ++ src/boot/efi/meson.build | 2 + 4 files changed, 344 insertions(+) create mode 100644 src/boot/efi/crc32.c create mode 100644 src/boot/efi/crc32.h diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index 35c649fde6..188ce65867 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -1,9 +1,11 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include +#include #include #include "console.h" +#include "crc32.h" #include "disk.h" #include "graphics.h" #include "linux.h" @@ -1955,6 +1957,193 @@ static VOID config_entry_add_linux( uefi_call_wrapper(linux_dir->Close, 1, linux_dir); } +/* Note that this is in GUID format, i.e. the first 32bit, and the following pair of 16bit are byteswapped. */ +static const UINT8 xbootldr_guid[16] = { + 0xff, 0xc2, 0x13, 0xbc, 0xe6, 0x59, 0x62, 0x42, 0xa3, 0x52, 0xb2, 0x75, 0xfd, 0x6f, 0x71, 0x72 +}; + +EFI_DEVICE_PATH *path_parent(EFI_DEVICE_PATH *path, EFI_DEVICE_PATH *node) { + EFI_DEVICE_PATH *parent; + UINTN len; + + len = (UINT8*) NextDevicePathNode(node) - (UINT8*) path; + parent = (EFI_DEVICE_PATH*) AllocatePool(len + sizeof(EFI_DEVICE_PATH)); + CopyMem(parent, path, len); + CopyMem((UINT8*) parent + len, EndDevicePath, sizeof(EFI_DEVICE_PATH)); + + return parent; +} + +static VOID config_load_xbootldr( + Config *config, + EFI_HANDLE *device) { + + EFI_DEVICE_PATH *partition_path, *node, *disk_path, *copy; + UINT32 found_partition_number = (UINT32) -1; + UINT64 found_partition_start = (UINT64) -1; + UINT64 found_partition_size = (UINT64) -1; + UINT8 found_partition_signature[16] = {}; + EFI_HANDLE new_device; + EFI_FILE *root_dir; + EFI_STATUS r; + + partition_path = DevicePathFromHandle(device); + if (!partition_path) + return; + + for (node = partition_path; !IsDevicePathEnd(node); node = NextDevicePathNode(node)) { + EFI_HANDLE disk_handle; + EFI_BLOCK_IO *block_io; + EFI_DEVICE_PATH *p; + UINTN nr; + + /* First, Let's look for the SCSI/SATA/USB/… device path node, i.e. one above the media + * devices */ + if (DevicePathType(node) != MESSAGING_DEVICE_PATH) + continue; + + /* Determine the device path one level up */ + disk_path = path_parent(partition_path, node); + p = disk_path; + r = uefi_call_wrapper(BS->LocateDevicePath, 3, &BlockIoProtocol, &p, &disk_handle); + if (EFI_ERROR(r)) + continue; + + r = uefi_call_wrapper(BS->HandleProtocol, 3, disk_handle, &BlockIoProtocol, (VOID **)&block_io); + if (EFI_ERROR(r)) + continue; + + /* Filter out some block devices early. (We only care about block devices that aren't + * partitions themselves — we look for GPT partition tables to parse after all —, and only + * those which contain a medium and have at least 2 blocks.) */ + if (block_io->Media->LogicalPartition || + !block_io->Media->MediaPresent || + block_io->Media->LastBlock <= 1) + continue; + + /* Try both copies of the GPT header, in case one is corrupted */ + for (nr = 0; nr < 2; nr++) { + _cleanup_freepool_ EFI_PARTITION_ENTRY* entries = NULL; + union { + EFI_PARTITION_TABLE_HEADER gpt_header; + uint8_t space[((sizeof(EFI_PARTITION_TABLE_HEADER) + 511) / 512) * 512]; + } gpt_header_buffer; + UINT64 where; + UINTN i, sz; + UINT32 c; + + if (nr == 0) + where = 1; /* Read the first copy at LBA 1 */ + else + where = block_io->Media->LastBlock; /* Read the second copy at the very last LBA of this block device */ + + /* Read the GPT header */ + r = uefi_call_wrapper(block_io->ReadBlocks, 5, block_io, block_io->Media->MediaId, where, sizeof(gpt_header_buffer), &gpt_header_buffer); + if (EFI_ERROR(r)) + continue; + + /* Some superficial validation of the GPT header */ + if (CompareMem(&gpt_header_buffer.gpt_header.Header.Signature, "EFI PART", sizeof(gpt_header_buffer.gpt_header.Header.Signature)) != 0) + continue; + + if (gpt_header_buffer.gpt_header.Header.HeaderSize < 92 || gpt_header_buffer.gpt_header.Header.HeaderSize > 512) + continue; + + if (gpt_header_buffer.gpt_header.Header.Revision != 0x00010000U) + continue; + + /* Calculate CRC check */ + c = ~crc32_exclude_offset((UINT32) -1, (const UINT8*) &gpt_header_buffer, gpt_header_buffer.gpt_header.Header.HeaderSize, + OFFSETOF(EFI_PARTITION_TABLE_HEADER, Header.CRC32), sizeof(gpt_header_buffer.gpt_header.Header.CRC32)); + if (c != gpt_header_buffer.gpt_header.Header.CRC32) + continue; + + if (gpt_header_buffer.gpt_header.MyLBA != where) + continue; + + if (gpt_header_buffer.gpt_header.SizeOfPartitionEntry < sizeof(EFI_PARTITION_ENTRY)) + continue; + + if (gpt_header_buffer.gpt_header.NumberOfPartitionEntries <= 0 || gpt_header_buffer.gpt_header.NumberOfPartitionEntries > 1024) + continue; + + /* Now load the GPT entry table */ + sz = ((gpt_header_buffer.gpt_header.SizeOfPartitionEntry * gpt_header_buffer.gpt_header.NumberOfPartitionEntries + 511) / 512) * 512; + entries = AllocatePool(sz); + + r = uefi_call_wrapper(block_io->ReadBlocks, 5, block_io, block_io->Media->MediaId, gpt_header_buffer.gpt_header.PartitionEntryLBA, sz, entries); + if (EFI_ERROR(r)) + continue; + + /* Calculate CRC of entries array, too */ + c = ~crc32((UINT32) -1, entries, sz); + if (c != gpt_header_buffer.gpt_header.PartitionEntryArrayCRC32) + continue; + + for (i = 0; i < gpt_header_buffer.gpt_header.NumberOfPartitionEntries; i++) { + EFI_PARTITION_ENTRY *entry; + + entry = (EFI_PARTITION_ENTRY*) ((UINT8*) entries + gpt_header_buffer.gpt_header.SizeOfPartitionEntry * i); + + if (CompareMem(&entry->PartitionTypeGUID, xbootldr_guid, 16) == 0) { + UINT64 end; + + /* Let's use memcpy(), in case the structs are not aligned (they really should be though) */ + CopyMem(&found_partition_start, &entry->StartingLBA, sizeof(found_partition_start)); + CopyMem(&end, &entry->EndingLBA, sizeof(end)); + + if (end < found_partition_start) /* Bogus? */ + continue; + + found_partition_size = end - found_partition_start + 1; + CopyMem(found_partition_signature, &entry->UniquePartitionGUID, sizeof(found_partition_signature)); + + found_partition_number = i + 1; + goto found; + } + } + + break; /* This GPT was fully valid, but we didn't find what we are looking for. This + * means there's no reason to check the second copy of the GPT header */ + } + } + + return; /* Not found */ + +found: + copy = DuplicateDevicePath(partition_path); + + /* Patch in the data we found */ + for (node = copy; !IsDevicePathEnd(node); node = NextDevicePathNode(node)) { + HARDDRIVE_DEVICE_PATH *hd; + + if (DevicePathType(node) != MEDIA_DEVICE_PATH) + continue; + + if (DevicePathSubType(node) != MEDIA_HARDDRIVE_DP) + continue; + + hd = (HARDDRIVE_DEVICE_PATH*) node; + hd->PartitionNumber = found_partition_number; + hd->PartitionStart = found_partition_start; + hd->PartitionSize = found_partition_size; + CopyMem(hd->Signature, found_partition_signature, sizeof(hd->Signature)); + hd->MBRType = MBR_TYPE_EFI_PARTITION_TABLE_HEADER; + hd->SignatureType = SIGNATURE_TYPE_GUID; + } + + r = uefi_call_wrapper(BS->LocateDevicePath, 3, &BlockIoProtocol, ©, &new_device); + if (EFI_ERROR(r)) + return; + + root_dir = LibOpenRoot(new_device); + if (!root_dir) + return; + + config_entry_add_linux(config, new_device, root_dir); + config_load_entries(config, new_device, root_dir, NULL); +} + static EFI_STATUS image_start( EFI_HANDLE parent_image, const Config *config, @@ -2147,6 +2336,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { /* scan /loader/entries/\*.conf files */ config_load_entries(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path); + /* Similar, but on any XBOOTLDR partition */ + config_load_xbootldr(&config, loaded_image->DeviceHandle); + /* sort entries after version number */ config_sort_entries(&config); diff --git a/src/boot/efi/crc32.c b/src/boot/efi/crc32.c new file mode 100644 index 0000000000..46b9aeea90 --- /dev/null +++ b/src/boot/efi/crc32.c @@ -0,0 +1,142 @@ +/* This is copied from util-linux, which in turn copied in the version from Gary S. Brown */ + +/* + * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or + * code or tables extracted from it, as desired without restriction. + * + * First, the polynomial itself and its table of feedback terms. The + * polynomial is + * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 + * + * Note that we take it "backwards" and put the highest-order term in + * the lowest-order bit. The X^32 term is "implied"; the LSB is the + * X^31 term, etc. The X^0 term (usually shown as "+1") results in + * the MSB being 1. + * + * Note that the usual hardware shift register implementation, which + * is what we're using (we're merely optimizing it by doing eight-bit + * chunks at a time) shifts bits into the lowest-order term. In our + * implementation, that means shifting towards the right. Why do we + * do it this way? Because the calculated CRC must be transmitted in + * order from highest-order term to lowest-order term. UARTs transmit + * characters in order from LSB to MSB. By storing the CRC this way, + * we hand it to the UART in the order low-byte to high-byte; the UART + * sends each low-bit to high-bit; and the result is transmission bit + * by bit from highest- to lowest-order term without requiring any bit + * shuffling on our part. Reception works similarly. + * + * The feedback terms table consists of 256, 32-bit entries. Notes + * + * The table can be generated at runtime if desired; code to do so + * is shown later. It might not be obvious, but the feedback + * terms simply represent the results of eight shift/xor opera- + * tions for all combinations of data and CRC register values. + * + * The values must be right-shifted by eight bits by the "updcrc" + * logic; the shift must be unsigned (bring in zeroes). On some + * hardware you could probably optimize the shift in assembler by + * using byte-swap instructions. + * polynomial $edb88320 + * + */ + +#include "crc32.h" + +static const UINT32 crc32_tab[] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; + +static inline UINT32 crc32_add_char(UINT32 crc, UINT8 c) { + return crc32_tab[(crc ^ c) & 0xff] ^ (crc >> 8); +} + +/* + * This a generic crc32() function, it takes seed as an argument, + * and does __not__ xor at the end. Then individual users can do + * whatever they need. + */ +UINT32 crc32(UINT32 seed, const VOID *buf, UINTN len) { + const UINT8 *p = buf; + UINT32 crc = seed; + + while (len > 0) { + crc = crc32_add_char(crc, *p++); + len--; + } + + return crc; +} + +UINT32 crc32_exclude_offset( + UINT32 seed, + const VOID *buf, + UINTN len, + UINTN exclude_off, + UINTN exclude_len) { + + const UINT8 *p = buf; + UINT32 crc = seed; + UINTN i; + + for (i = 0; i < len; i++) { + UINT8 x = *p++; + + if (i >= exclude_off && i < exclude_off + exclude_len) + x = 0; + + crc = crc32_add_char(crc, x); + } + + return crc; +} diff --git a/src/boot/efi/crc32.h b/src/boot/efi/crc32.h new file mode 100644 index 0000000000..64150ee948 --- /dev/null +++ b/src/boot/efi/crc32.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include +#include + +UINT32 crc32(UINT32 seed, const VOID *buf, UINTN len); +UINT32 crc32_exclude_offset(UINT32 seed, const VOID *buf, UINTN len, UINTN exclude_off, UINTN exclude_len); diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build index 2140151844..67b4331a95 100644 --- a/src/boot/efi/meson.build +++ b/src/boot/efi/meson.build @@ -2,6 +2,7 @@ efi_headers = files(''' console.h + crc32.h disk.h graphics.h linux.h @@ -24,6 +25,7 @@ systemd_boot_sources = ''' boot.c console.c shim.c + crc32.c '''.split() stub_sources = ''' From 5d87589732411c05c90ffab292f31e536502bebb Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 8 Feb 2019 13:05:55 +0100 Subject: [PATCH 36/48] sd-boot: don't print error string where there's no error code known --- src/boot/efi/boot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index 188ce65867..c7ba088761 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -2310,7 +2310,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { root_dir = LibOpenRoot(loaded_image->DeviceHandle); if (!root_dir) { - Print(L"Unable to open root directory: %r ", err); + Print(L"Unable to open root directory."); uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); return EFI_LOAD_ERROR; } From b9e452423c25888e147e8aaa25ad8087176eb3a0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 8 Feb 2019 16:23:40 +0100 Subject: [PATCH 37/48] efi: beef up efivar_get_xyz() to accept NULL return values --- src/boot/efi/util.c | 11 ++++++++--- src/boot/efi/util.h | 7 +++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/boot/efi/util.c b/src/boot/efi/util.c index f1f1674c59..bc1d4ae5c4 100644 --- a/src/boot/efi/util.c +++ b/src/boot/efi/util.c @@ -117,6 +117,9 @@ EFI_STATUS efivar_get(const CHAR16 *name, CHAR16 **value) { if ((size % 2) != 0) return EFI_INVALID_PARAMETER; + if (!value) + return EFI_SUCCESS; + /* Return buffer directly if it happens to be NUL terminated already */ if (size >= 2 && buf[size-2] == 0 && buf[size-1] == 0) { *value = (CHAR16*) buf; @@ -141,7 +144,7 @@ EFI_STATUS efivar_get_int(const CHAR16 *name, UINTN *i) { EFI_STATUS err; err = efivar_get(name, &val); - if (!EFI_ERROR(err)) + if (!EFI_ERROR(err) && i) *i = Atoi(val); return err; @@ -159,8 +162,10 @@ EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, const CHAR16 *name, CHAR8 **bu err = uefi_call_wrapper(RT->GetVariable, 5, (CHAR16*) name, (EFI_GUID *)vendor, NULL, &l, buf); if (!EFI_ERROR(err)) { - *buffer = buf; - buf = NULL; + + if (buffer) + *buffer = TAKE_PTR(buf); + if (size) *size = l; } diff --git a/src/boot/efi/util.h b/src/boot/efi/util.h index 4ba7050a91..8c5e35ad25 100644 --- a/src/boot/efi/util.h +++ b/src/boot/efi/util.h @@ -55,3 +55,10 @@ const EFI_GUID loader_guid; #define UINTN_MAX (~(UINTN)0) #define INTN_MAX ((INTN)(UINTN_MAX>>1)) + +#define TAKE_PTR(ptr) \ + ({ \ + typeof(ptr) _ptr_ = (ptr); \ + (ptr) = NULL; \ + _ptr_; \ + }) From 0d957ef6cfc6b98c2e90ed81198a64437906763a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 8 Feb 2019 16:46:51 +0100 Subject: [PATCH 38/48] stub: fix GUID to check EFI vars in Our own variables are in the the "loader" GUID namespace, but our code so far checked the "global" GUID namespace (i.e. EFI's own), before setting the variables. Correct that, so that we always check the right namespace for existing variables before we write them. --- src/boot/efi/stub.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index 2a60f38bf7..e5c916f7dd 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -92,7 +92,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { efivar_set(L"LoaderDevicePartUUID", uuid, FALSE); /* if LoaderImageIdentifier is not set, assume the image with this stub was loaded directly from UEFI */ - if (efivar_get_raw(&global_guid, L"LoaderImageIdentifier", &b, &size) != EFI_SUCCESS) { + if (efivar_get_raw(&loader_guid, L"LoaderImageIdentifier", &b, &size) != EFI_SUCCESS) { _cleanup_freepool_ CHAR16 *s; s = DevicePathToStr(loaded_image->FilePath); @@ -100,7 +100,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { } /* if LoaderFirmwareInfo is not set, let's set it */ - if (efivar_get_raw(&global_guid, L"LoaderFirmwareInfo", &b, &size) != EFI_SUCCESS) { + if (efivar_get_raw(&loader_guid, L"LoaderFirmwareInfo", &b, &size) != EFI_SUCCESS) { _cleanup_freepool_ CHAR16 *s; s = PoolPrint(L"%s %d.%02d", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff); @@ -108,7 +108,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { } /* ditto for LoaderFirmwareType */ - if (efivar_get_raw(&global_guid, L"LoaderFirmwareType", &b, &size) != EFI_SUCCESS) { + if (efivar_get_raw(&loader_guid, L"LoaderFirmwareType", &b, &size) != EFI_SUCCESS) { _cleanup_freepool_ CHAR16 *s; s = PoolPrint(L"UEFI %d.%02d", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff); @@ -116,7 +116,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { } /* add StubInfo */ - if (efivar_get_raw(&global_guid, L"StubInfo", &b, &size) != EFI_SUCCESS) + if (efivar_get_raw(&loader_guid, L"StubInfo", &b, &size) != EFI_SUCCESS) efivar_set(L"StubInfo", L"systemd-stub " GIT_VERSION, FALSE); if (szs[3] > 0) From 8118fb3c83bc813976e9fb4984bc80b13798d2a1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 8 Feb 2019 16:49:09 +0100 Subject: [PATCH 39/48] stub: don't ask for variable data we actually don't care about Let's take benefit of the fact that efivar_get_xyz() take NULL pointers for the return data: let's shorten the code a bit. --- src/boot/efi/stub.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index e5c916f7dd..d0a9acf5d2 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -92,7 +92,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { efivar_set(L"LoaderDevicePartUUID", uuid, FALSE); /* if LoaderImageIdentifier is not set, assume the image with this stub was loaded directly from UEFI */ - if (efivar_get_raw(&loader_guid, L"LoaderImageIdentifier", &b, &size) != EFI_SUCCESS) { + if (efivar_get_raw(&loader_guid, L"LoaderImageIdentifier", NULL, NULL) != EFI_SUCCESS) { _cleanup_freepool_ CHAR16 *s; s = DevicePathToStr(loaded_image->FilePath); @@ -100,7 +100,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { } /* if LoaderFirmwareInfo is not set, let's set it */ - if (efivar_get_raw(&loader_guid, L"LoaderFirmwareInfo", &b, &size) != EFI_SUCCESS) { + if (efivar_get_raw(&loader_guid, L"LoaderFirmwareInfo", NULL, NULL) != EFI_SUCCESS) { _cleanup_freepool_ CHAR16 *s; s = PoolPrint(L"%s %d.%02d", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff); @@ -108,7 +108,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { } /* ditto for LoaderFirmwareType */ - if (efivar_get_raw(&loader_guid, L"LoaderFirmwareType", &b, &size) != EFI_SUCCESS) { + if (efivar_get_raw(&loader_guid, L"LoaderFirmwareType", NULL, NULL) != EFI_SUCCESS) { _cleanup_freepool_ CHAR16 *s; s = PoolPrint(L"UEFI %d.%02d", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff); @@ -116,7 +116,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { } /* add StubInfo */ - if (efivar_get_raw(&loader_guid, L"StubInfo", &b, &size) != EFI_SUCCESS) + if (efivar_get_raw(&loader_guid, L"StubInfo", NULL, NULL) != EFI_SUCCESS) efivar_set(L"StubInfo", L"systemd-stub " GIT_VERSION, FALSE); if (szs[3] > 0) From c8b32d06bd4729bc48628873e35d2769d08f3575 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 8 Feb 2019 16:51:58 +0100 Subject: [PATCH 40/48] stub: don't override LoaderDevicePartUUID EFI var We document and all our code assumes that LoaderDevicePartUUID is initialized to the ESP's UUID. Let's hence not override the variable if it is already set, in order to not confuse userspace if the kernel's EFI image is run from a different partition than the ESP. This matches behaviour for all other variables set by the EFI stub, in particular the closely related LoaderImageIdentifier variable. --- src/boot/efi/stub.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index d0a9acf5d2..26c204fb43 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -87,9 +87,10 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { #endif } - /* export the device path this image is started from */ - if (disk_get_part_uuid(loaded_image->DeviceHandle, uuid) == EFI_SUCCESS) - efivar_set(L"LoaderDevicePartUUID", uuid, FALSE); + /* Export the device path this image is started from, if it's not set yet */ + if (efivar_get_raw(&loader_guid, L"LoaderDevicePartUUID", NULL, NULL) != EFI_SUCCESS) + if (disk_get_part_uuid(loaded_image->DeviceHandle, uuid) == EFI_SUCCESS) + efivar_set(L"LoaderDevicePartUUID", uuid, FALSE); /* if LoaderImageIdentifier is not set, assume the image with this stub was loaded directly from UEFI */ if (efivar_get_raw(&loader_guid, L"LoaderImageIdentifier", NULL, NULL) != EFI_SUCCESS) { From 8312d69409b467c890ccf17e67ff306fb6fa6925 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 8 Feb 2019 17:18:48 +0100 Subject: [PATCH 41/48] bootspec: stat() on an autofs mount point doesn't trigger it, let's hence do it explicitly --- src/shared/bootspec.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index 2db6425f2f..53ab042404 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -874,12 +874,18 @@ static int verify_fsroot_dir( dev_t *ret_dev) { struct stat st, st2; - const char *t2; + const char *t2, *trigger; int r; assert(path); assert(ret_dev); + /* So, the ESP and XBOOTLDR partition are commonly located on an autofs mount. stat() on the + * directory won't trigger it, if it is not mounted yet. Let's hence explicitly trigger it here, + * before stat()ing */ + trigger = strjoina(path, "/trigger"); /* Filename doesn't matter... */ + (void) access(trigger, F_OK); + if (stat(path, &st) < 0) return log_full_errno((searching && errno == ENOENT) || (unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno, From 1b14a4b09700c411ce1fe6f9993aee4806d55fc9 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 8 Feb 2019 17:19:15 +0100 Subject: [PATCH 42/48] mkosi: let's update the boot loader also in /efi This is after all where we preferable mount the ESP today. --- mkosi.build | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mkosi.build b/mkosi.build index 114e617c85..16ac2e9d0d 100755 --- a/mkosi.build +++ b/mkosi.build @@ -95,3 +95,7 @@ EOF mkdir -p "$DESTDIR"/boot/efi/EFI/systemd "$DESTDIR"/boot/efi/EFI/BOOT cp "$DESTDIR"/usr/lib/systemd/boot/efi/systemd-bootx64.efi "$DESTDIR"/boot/efi/EFI/systemd/systemd-bootx64.efi cp "$DESTDIR"/usr/lib/systemd/boot/efi/systemd-bootx64.efi "$DESTDIR"/boot/efi/EFI/BOOT/bootx64.efi + +mkdir -p "$DESTDIR"/efi/EFI/systemd "$DESTDIR"/efi/EFI/BOOT +cp "$DESTDIR"/usr/lib/systemd/boot/efi/systemd-bootx64.efi "$DESTDIR"/efi/EFI/systemd/systemd-bootx64.efi +cp "$DESTDIR"/usr/lib/systemd/boot/efi/systemd-bootx64.efi "$DESTDIR"/efi/EFI/BOOT/bootx64.efi From 2cb45cdc4d6cb50dc8cf2048a7552173fc6f62ee Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 12 Feb 2019 14:39:34 +0100 Subject: [PATCH 43/48] meson: sort header list again --- src/boot/efi/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build index 67b4331a95..0ae3191635 100644 --- a/src/boot/efi/meson.build +++ b/src/boot/efi/meson.build @@ -8,9 +8,9 @@ efi_headers = files(''' linux.h measure.h pe.h + shim.h splash.h util.h - shim.h '''.split()) common_sources = ''' From 82dad528182ba07d1fb7ce741a097d54a3d956d7 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 12 Feb 2019 16:05:55 +0100 Subject: [PATCH 44/48] docs: enclose all uuids in `` --- docs/BOOT_LOADER_SPECIFICATION.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/BOOT_LOADER_SPECIFICATION.md b/docs/BOOT_LOADER_SPECIFICATION.md index 3612ff1d33..0d6bdabb8b 100644 --- a/docs/BOOT_LOADER_SPECIFICATION.md +++ b/docs/BOOT_LOADER_SPECIFICATION.md @@ -42,9 +42,9 @@ Everything described below is located on a placeholder file system `$BOOT`. The * If the OS is installed on a disk with MBR disk label, and a partition with the MBR type id of 0xEA already exists it should be used as `$BOOT`. * Otherwise, if the OS is installed on a disk with MBR disk label, a new partition with MBR type id of 0xEA shall be created, of a suitable size (let's say 500MB), and it should be used as `$BOOT`. * On disks with GPT disk labels - * If the OS is installed on a disk with GPT disk label, and a partition with the GPT type GUID of bc13c2ff-59e6-4262-a352-b275fd6f7172 already exists, it should be used as `$BOOT`. - * Otherwise, if the OS is installed on a disk with GPT disk label, and an ESP partition (i.e. with the GPT type UID of c12a7328-f81f-11d2-ba4b-00a0c93ec93b) already exists and is large enough (let's say 250MB) and otherwise qualifies, it should be used as `$BOOT`. - * Otherwise, if the OS is installed on a disk with GPT disk label, and if the ESP partition already exists but is too small, a new suitably sized (let's say 500MB) partition with GPT type GUID of bc13c2ff-59e6-4262-a352-b275fd6f7172 shall be created and it should be used as `$BOOT`. + * If the OS is installed on a disk with GPT disk label, and a partition with the GPT type GUID of `bc13c2ff-59e6-4262-a352-b275fd6f7172` already exists, it should be used as `$BOOT`. + * Otherwise, if the OS is installed on a disk with GPT disk label, and an ESP partition (i.e. with the GPT type UID of `c12a7328-f81f-11d2-ba4b-00a0c93ec93b`) already exists and is large enough (let's say 250MB`) and otherwise qualifies, it should be used as `$BOOT`. + * Otherwise, if the OS is installed on a disk with GPT disk label, and if the ESP partition already exists but is too small, a new suitably sized (let's say 500MB) partition with GPT type GUID of `bc13c2ff-59e6-4262-a352-b275fd6f7172` shall be created and it should be used as `$BOOT`. * Otherwise, if the OS is installed on a disk with GPT disk label, and no ESP partition exists yet, a new suitably sized (let's say 500MB) ESP should be created and should be used as `$BOOT`. This placeholder file system shall be determined during _installation time_, and an fstab entry may be created. It should be mounted to either `/boot/` or `/efi/`. Additional locations like `/boot/efi/`, with `/boot/` being a separate file system, might be supported by implementations. This is not recommended because the mounting of `$BOOT` is then dependent on and requires the mounting of the intermediate file system. From aa4c06844d7c43c79575fa7e4801d9deb1512aba Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 12 Feb 2019 16:06:21 +0100 Subject: [PATCH 45/48] man: extend systemd-gpt-auto-generator with XBOOTLDR info --- man/systemd-gpt-auto-generator.xml | 88 ++++++++++++++++++------------ 1 file changed, 53 insertions(+), 35 deletions(-) diff --git a/man/systemd-gpt-auto-generator.xml b/man/systemd-gpt-auto-generator.xml index 5ae4343705..c932ce5ce1 100644 --- a/man/systemd-gpt-auto-generator.xml +++ b/man/systemd-gpt-auto-generator.xml @@ -32,22 +32,18 @@ Description - systemd-gpt-auto-generator is a unit - generator that automatically discovers root, - /home, /srv and swap - partitions and creates mount and swap units for them, based on the - partition type GUIDs of GUID partition tables (GPT), - see UEFI Specification, chapter 5. - It implements the Discoverable - Partitions Specification. Note that this generator has no - effect on non-GPT systems, or where the directories under the - mount points are already non-empty. Also, on systems where the - units are explicitly configured (for example, listed in - fstab5), - the units this generator creates are overridden, but additional - implicit dependencies might be created. + systemd-gpt-auto-generator is a unit generator that automatically discovers + root, /home/, /srv/, 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 UEFI Specification, chapter 5. It implements the Discoverable Partitions + Specification. 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 fstab5), the + units this generator creates are overridden, but additional implicit dependencies might be + created. This generator will only look for root partitions on the same physical disk the EFI System Partition (ESP) is located on. @@ -70,11 +66,13 @@ + Partition Type GUID Name + Mount Point Explanation @@ -82,48 +80,63 @@ 44479540-f297-41b2-9af7-d131d5f0458a Root Partition (x86) + / On 32-bit x86 systems, the first x86 root partition on the disk the EFI ESP is located on is mounted to the root directory /. 4f68bce3-e8cd-4db1-96e7-fbcaf984b709 Root Partition (x86-64) + / On 64-bit x86 systems, the first x86-64 root partition on the disk the EFI ESP is located on is mounted to the root directory /. 69dad710-2ce4-4e3c-b16c-21a1d49abed3 Root Partition (32-bit ARM) + / On 32-bit ARM systems, the first ARM root partition on the disk the EFI ESP is located on is mounted to the root directory /. b921b045-1df0-41c3-af44-4c6f280d3fae Root Partition (64-bit ARM) + / On 64-bit ARM systems, the first ARM root partition on the disk the EFI ESP is located on is mounted to the root directory /. 993d8d3d-f80e-4225-855a-9daf8ed7ea97 Root Partition (Itanium/IA-64) + / On Itanium systems, the first Itanium root partition on the disk the EFI ESP is located on is mounted to the root directory /. 933ac7e1-2eb4-4f13-b844-0e14e2aef915 Home Partition + /home/ The first home partition on the disk the root partition is located on is mounted to /home. 3b8f8425-20e0-4f3b-907f-1a25a76f98e8 Server Data Partition + /srv/ The first server data partition on the disk the root partition is located on is mounted to /srv. 0657fd6d-a4ab-43c4-84e5-0933c84b4f4f Swap + n/a All swap partitions located on the disk the root partition is located on are enabled. c12a7328-f81f-11d2-ba4b-00a0c93ec93b EFI System Partition (ESP) + /efi/ or /boot/ The first ESP located on the disk the root partition is located on is mounted to /boot or /efi, see below. + + bc13c2ff-59e6-4262-a352-b275fd6f7172 + Extended Boot Loader Partition + /boot/ + The first Extended Boot Loader Partition is mounted to /boot, see below. + @@ -149,44 +162,49 @@ GPT_FLAG_READ_ONLY 0x1000000000000000 - /, /srv, /home + /, /home/, /srv/, Extended Boot Loader Partition Partition is mounted read-only GPT_FLAG_NO_AUTO 0x8000000000000000 - /, /srv, /home + /, /home/, /srv/, Extended Boot Loader Partition Partition is not mounted automatically GPT_FLAG_NO_BLOCK_IO_PROTOCOL 0x0000000000000002 - ESP + EFI System Partition (ESP) Partition is not mounted automatically - The /home and /srv - partitions may be encrypted in LUKS format. In this case, a device - mapper device is set up under the names - /dev/mapper/home and - /dev/mapper/srv. Note that this might create - conflicts if the same partition is listed in - /etc/crypttab with a different device mapper - device name. + The /home/ and /srv/ partitions may be encrypted in LUKS + format. In this case, a device mapper device is set up under the names + /dev/mapper/home and /dev/mapper/srv. Note that this might + create conflicts if the same partition is listed in /etc/crypttab with a different + device mapper device name. - Mount and automount units for the EFI System Partition (ESP) are generated on EFI systems. The ESP is mounted - to /boot, unless a mount point directory /efi 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 /boot (or /efi if it exists) is an explicitly - configured mount (for example, listed in fstab5) or where the - /boot (or /efi) mount point is non-empty, no mount units are - generated. + Mount and automount units for the EFI System Partition (ESP) are generated on EFI systems. The ESP + is mounted to /boot/ (except if an Extended Boot Loader partition exists, see + below), unless a mount point directory /efi/ 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 /boot/ (or /efi/ if it exists) is an + explicitly configured mount (for example, listed in fstab5) or where + the /boot/ (or /efi/) mount point is non-empty, no mount units + are generated. + + If the disk contains an Extended Boot Loader partition, as defined in the Boot Loader Specification, it is made + available at /boot (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 /boot/. Make sure to create both /efi/ + and /boot/ to ensure both partitions are mounted. When using this generator in conjunction with btrfs file systems, make sure to set the correct default subvolumes on them, From c5fcaed88161eb38418e5a90c018f0cf31175b53 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 12 Feb 2019 16:13:57 +0100 Subject: [PATCH 46/48] man: document XBOOTLDR search logic for sd-boot --- man/systemd-boot.xml | 62 ++++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/man/systemd-boot.xml b/man/systemd-boot.xml index 4c914e6156..0ec7a47b51 100644 --- a/man/systemd-boot.xml +++ b/man/systemd-boot.xml @@ -25,27 +25,28 @@ Description - systemd-boot (short: sd-boot) is a simple UEFI boot manager. It - provides a graphical menu to select the entry to boot and an editor for the kernel command line. systemd-boot - supports systems with UEFI firmware only. + systemd-boot (short: sd-boot) is a simple UEFI boot + manager. It provides a graphical menu to select the entry to boot and an editor for the kernel command + line. systemd-boot supports systems with UEFI firmware only. systemd-boot loads boot entry information from the EFI system partition (ESP), usually mounted at - /boot, /efi, or /boot/efi during OS - runtime. Configuration file fragments, kernels, initrds and other EFI images to boot generally need to reside on - the ESP. Linux kernels must be built with to be able to be directly executed as an - EFI image. During boot systemd-boot automatically assembles a list of boot entries from the following - sources: + /efi/, /boot/, or /boot/efi/ during OS + runtime, as well as from the Extended Boot Loader partition if it exists (usually mounted to + /boot/). Configuration file fragments, kernels, initrds and other EFI images to boot + generally need to reside on the ESP or the Extended Boot Loader partition. Linux kernels must be built + with to be able to be directly executed as an EFI image. During boot + systemd-boot automatically assembles a list of boot entries from the following sources: Boot entries defined with Boot Loader - Specification description files located in /loader/entries/ on the ESP. These - usually describe Linux kernel images with associated initrd images, but alternatively may also describe - arbitrary other EFI executables. + url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification description files + located in /loader/entries/ on the ESP and the Extended Boot Loader + Partition. These usually describe Linux kernel images with associated initrd images, but alternatively + may also describe arbitrary other EFI executables. Unified kernel images following the Boot Loader - Specification, as executable EFI binaries in /EFI/Linux/ on the ESP. + url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification, as executable EFI + binaries in /EFI/Linux/ on the ESP and the Extended Boot Loader Partition. The Microsoft Windows EFI boot manager, if installed @@ -57,10 +58,12 @@ A reboot into the UEFI firmware setup option, if supported by the firmware - kernel-install8 may be - used to copy kernel images onto the ESP and to generate description files compliant with the Boot Loader - Specification. bootctl1 may be - used from a running system to locate the ESP, list available entries, and install systemd-boot itself. + kernel-install8 + may be used to copy kernel images onto the ESP or the Extended Boot Loader Partition and to generate + description files compliant with the Boot Loader + Specification. bootctl1 + may be used from a running system to locate the ESP and the Extended Boot Loader Partition, list + available entries, and install systemd-boot itself. systemd-boot will provide information about the time spent in UEFI firmware using the Boot Loader Interface. This information can be displayed @@ -224,16 +227,19 @@ Files - The files systemd-boot reads generally reside on the UEFI ESP which is usually mounted to - /boot/, /efi/ or /boot/efi during OS - runtime. systemd-boot reads runtime configuration such as the boot timeout and default entry from - /loader/loader.conf on the ESP (in combination with data read from EFI variables). See - loader.conf5. Boot entry - description files following the Boot Loader - Specification are read from /loader/entries/ on the ESP. Unified kernel boot entries - following the Boot - Loader Specification are read from /EFI/Linux/ on the ESP. + The files systemd-boot processes generally reside on the UEFI ESP which is + usually mounted to /efi/, /boot/ or + /boot/efi/ during OS runtime. It also processes files on the Extended Boot Loader + partition which is typically mounted to /boot/, if it + exists. systemd-boot reads runtime configuration such as the boot timeout and default + entry from /loader/loader.conf on the ESP (in combination with data read from EFI + variables). See + loader.conf5. Boot + entry description files following the Boot + Loader Specification are read from /loader/entries/ on the ESP and the + Extended Boot Loader partition. Unified kernel boot entries following the Boot Loader Specification are read from + /EFI/Linux/ on the ESP and the Extended Boot Loader partition. From 3da2b703d94af3a0946162f8dd2fbc0294981903 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 12 Feb 2019 16:22:31 +0100 Subject: [PATCH 47/48] man: document XBOOTLDR partition logic in bootctl's man page --- man/bootctl.xml | 49 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/man/bootctl.xml b/man/bootctl.xml index 9cfa9cccdf..41165cd09a 100644 --- a/man/bootctl.xml +++ b/man/bootctl.xml @@ -46,18 +46,34 @@ - - Path to the EFI System Partition (ESP). If not specified, /efi, - /boot, and /boot/efi are checked in turn. It is recommended to mount - the ESP to /boot, if possible. + + Path to the EFI System Partition (ESP). If not specified, /efi/, + /boot/, and /boot/efi are checked in turn. It is recommended to mount + the ESP to /efi/, if possible. + + + + + Path to the Extended Boot Loader partition, as defined in the Boot Loader Specification. If not + specified, /boot/ are checked. It is recommended to mount the Extended Boot + Loader partition to /boot/, if possible. - - This option modifies the behaviour of status. - Just print the path to the EFI System Partition (ESP) to standard output and - exit. + + This option modifies the behaviour of status. Prints only the + path to the EFI System Partition (ESP) to standard output and exits. + + + + + This option modifies the behaviour of status. Prints only the + path to the Extended Boot Loader partition if it exists, and the path to the ESP otherwise to + standard output and exit. This command is useful to determine where to place boot loader entries, as + they are preferably placed in the Extended Boot Loader partition if it exists and in the ESP + otherwise. @@ -87,10 +103,10 @@ - Installs systemd-boot into the EFI system partition. A copy of systemd-boot - will be stored as the EFI default/fallback loader at - ESP/EFI/BOOT/BOOT*.EFI. The boot loader is then added to the - top of the firmware's boot loader list. + Installs systemd-boot into the EFI system partition. A copy of + systemd-boot will be stored as the EFI default/fallback loader at + ESP/EFI/BOOT/BOOT*.EFI. The boot loader is then added + to the top of the firmware's boot loader list. @@ -138,9 +154,12 @@ Environment - If $SYSTEMD_RELAX_ESP_CHECKS=1 is set the validation checks for the ESP are relaxed, and - the path specified with may refer to any kind of file system on any kind of - partition. + If $SYSTEMD_RELAX_ESP_CHECKS=1 is set the validation checks for the ESP are + relaxed, and the path specified with may refer to any kind of file system on + any kind of partition. + + Similarly, $SYSTEMD_RELAX_XBOOTLDR_CHECKS=1 turns off some validation checks for + the Extended Boot Loader partition. From ccc45964a47d0bf262324ce1e4f69195ff99a948 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 28 Jan 2019 19:54:09 +0100 Subject: [PATCH 48/48] update TODO --- TODO | 8 -------- 1 file changed, 8 deletions(-) diff --git a/TODO b/TODO index b4e0cbb88a..0178d431d5 100644 --- a/TODO +++ b/TODO @@ -51,8 +51,6 @@ Features: * When logind.conf contains HandleLidSwitch=suspend-then-hibernate and we can't hibernate because the swap partition isn't large enough, still suspend -* bootctl: implement Type #2 boot loader entry discovery - * bootctl,sd-boot: actually honour the "architecture" key * when a socket unit is spawned with an AF_UNIX path in /var/run, complain and @@ -90,23 +88,17 @@ Features: show state of these flags, and optionally trigger such a factory reset on next boot by setting the flag. -* sd-boot: search drop-ins in $BOOT, too - * sd-boot: add "oneshot boot timeout" variable support * sd-boot: automatically load EFI modules from some drop-in dir, so that people can add in file system drivers and such -* esp generator: also mount $BOOT if found - * sd-boot: optionally, show boot menu when previous default boot item has non-zero "tries done" count * logind: add "boot into bootmenu" API, and possibly even "boot into windows" and "boot into macos". -* bootspec.c: also enumerate EFI unified kernel images. - * maybe set a special xattr on cgroups that have delegate=yes set, to make it easy to mark cut points