diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/boot/bless-boot.c | 1 | ||||
-rw-r--r-- | src/boot/bootctl.c | 1 | ||||
-rw-r--r-- | src/shared/bootspec.c | 715 | ||||
-rw-r--r-- | src/shared/bootspec.h | 6 | ||||
-rw-r--r-- | src/shared/find-esp.c | 709 | ||||
-rw-r--r-- | src/shared/find-esp.h | 12 | ||||
-rw-r--r-- | src/shared/meson.build | 2 |
7 files changed, 728 insertions, 718 deletions
diff --git a/src/boot/bless-boot.c b/src/boot/bless-boot.c index 2cbabc6c59..259085d5de 100644 --- a/src/boot/bless-boot.c +++ b/src/boot/bless-boot.c @@ -8,6 +8,7 @@ #include "efi-loader.h" #include "efivars.h" #include "fd-util.h" +#include "find-esp.h" #include "fs-util.h" #include "log.h" #include "main-func.h" diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index e2900291bf..6c011f34f0 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -24,6 +24,7 @@ #include "escape.h" #include "fd-util.h" #include "fileio.h" +#include "find-esp.h" #include "fs-util.h" #include "glyph-util.h" #include "main-func.h" diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index 524f0ccfcd..e34ac539c0 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -1,40 +1,22 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include <stdio.h> -#include <linux/magic.h> #include <unistd.h> -#include "sd-device.h" -#include "sd-id128.h" - -#include "alloc-util.h" -#include "blkid-util.h" -#include "bootspec-fundamental.h" #include "bootspec.h" +#include "bootspec-fundamental.h" #include "conf-files.h" -#include "def.h" -#include "device-nodes.h" #include "dirent-util.h" -#include "efivars.h" #include "efi-loader.h" #include "env-file.h" -#include "env-util.h" -#include "errno-util.h" #include "fd-util.h" #include "fileio.h" -#include "gpt.h" -#include "id128-util.h" -#include "parse-util.h" +#include "find-esp.h" #include "path-util.h" #include "pe-header.h" #include "sort-util.h" #include "stat-util.h" -#include "string-table.h" -#include "string-util.h" #include "strv.h" #include "unaligned.h" -#include "util.h" -#include "virt.h" static void boot_entry_free(BootEntry *entry) { assert(entry); @@ -884,696 +866,3 @@ int boot_entries_augment_from_loader( return 0; } - -/********************************************************************************/ - -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); - - r = blkid_probe_lookup_value(b, "TYPE", &v, NULL); - if (r != 0) - return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, - SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), - "No filesystem found on \"%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); - - r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL); - if (r != 0) - return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, - SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), - "File system \"%s\" is not located on a partitioned block device.", 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 (id128_equal_string(v, GPT_ESP) <= 0) - 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_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 (id128_equal_string(v, GPT_ESP) <= 0) - 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_fsroot_dir( - const char *path, - bool searching, - bool unprivileged_mode, - dev_t *ret_dev) { - - struct stat st, st2; - 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, - "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); - - if (path_equal(path, "/")) { - /* Let's assume that the root directory of the OS is always the root of its file system - * (which technically doesn't have to be the case, but it's close enough, and it's not easy - * to be fully correct for it, since we can't look further up than the root dir easily.) */ - if (ret_dev) - *ret_dev = st.st_dev; - - return 0; - } - - 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(); - - r = RET_NERRNO(stat(parent, &st2)); - } - - 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, - bool unprivileged_mode, - uint32_t *ret_part, - uint64_t *ret_pstart, - uint64_t *ret_psize, - sd_id128_t *ret_uuid, - dev_t *ret_devid) { - - bool relax_checks; - dev_t devid; - int r; - - 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 accessing the thing - */ - - relax_checks = getenv_bool("SYSTEMD_RELAX_ESP_CHECKS") > 0; - - /* Non-root user can only check the status, so if an error occurred in the following, it does not cause any - * 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) || - (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)) - 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); - } - - r = verify_fsroot_dir(p, searching, unprivileged_mode, &devid); - if (r < 0) - 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. */ - if (detect_container() > 0 || relax_checks) - goto finish; - - /* 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) - r = verify_esp_udev(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid); - else - r = verify_esp_blkid(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid); - if (r < 0) - return r; - - if (ret_devid) - *ret_devid = devid; - - return 0; - -finish: - if (ret_part) - *ret_part = 0; - if (ret_pstart) - *ret_pstart = 0; - if (ret_psize) - *ret_psize = 0; - if (ret_uuid) - *ret_uuid = SD_ID128_NULL; - if (ret_devid) - *ret_devid = 0; - - return 0; -} - -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, - dev_t *ret_devid) { - - int r; - - /* This logs about all errors except: - * - * -ENOKEY → when we can't find the partition - * -EACCESS → when unprivileged_mode is true, and we can't access something - */ - - if (path) { - r = verify_esp(path, /* searching= */ false, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid); - if (r < 0) - return r; - - goto found; - } - - path = getenv("SYSTEMD_ESP_PATH"); - if (path) { - struct stat st; - - if (!path_is_valid(path) || !path_is_absolute(path)) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "$SYSTEMD_ESP_PATH does not refer to absolute path, refusing to use it: %s", - path); - - /* Note: when the user explicitly configured things with an env var we won't validate the - * path beyond checking it refers to a directory. After all we want this to be useful for - * testing. */ - - if (stat(path, &st) < 0) - return log_error_errno(errno, "Failed to stat '%s': %m", path); - if (!S_ISDIR(st.st_mode)) - return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "ESP path '%s' is not a directory.", path); - - if (ret_part) - *ret_part = 0; - if (ret_pstart) - *ret_pstart = 0; - if (ret_psize) - *ret_psize = 0; - if (ret_uuid) - *ret_uuid = SD_ID128_NULL; - if (ret_devid) - *ret_devid = st.st_dev; - - goto found; - } - - FOREACH_STRING(path, "/efi", "/boot", "/boot/efi") { - - r = verify_esp(path, /* searching= */ true, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid); - if (r >= 0) - goto found; - if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */ - return r; - } - - /* No logging here */ - return -ENOKEY; - -found: - if (ret_path) { - char *c; - - c = strdup(path); - if (!c) - return log_oom(); - - *ret_path = c; - } - - 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 (id128_equal_string(v, GPT_XBOOTLDR) <= 0) - 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_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 (id128_equal_string(v, GPT_XBOOTLDR)) - 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, - bool unprivileged_mode, - sd_id128_t *ret_uuid, - dev_t *ret_devid) { - - bool relax_checks; - dev_t devid; - int r; - - assert(p); - - relax_checks = getenv_bool("SYSTEMD_RELAX_XBOOTLDR_CHECKS") > 0; - - r = verify_fsroot_dir(p, searching, unprivileged_mode, &devid); - if (r < 0) - return r; - - if (detect_container() > 0 || relax_checks) - goto finish; - - if (unprivileged_mode) - r = verify_xbootldr_udev(devid, searching, ret_uuid); - else - r = verify_xbootldr_blkid(devid, searching, ret_uuid); - if (r < 0) - return r; - - if (ret_devid) - *ret_devid = devid; - - return 0; - -finish: - if (ret_uuid) - *ret_uuid = SD_ID128_NULL; - if (ret_devid) - *ret_devid = 0; - - return 0; -} - -int find_xbootldr_and_warn( - const char *path, - bool unprivileged_mode, - char **ret_path, - sd_id128_t *ret_uuid, - dev_t *ret_devid) { - - int r; - - /* Similar to find_esp_and_warn(), but finds the XBOOTLDR partition. Returns the same errors. */ - - if (path) { - r = verify_xbootldr(path, /* searching= */ false, unprivileged_mode, ret_uuid, ret_devid); - if (r < 0) - return r; - - goto found; - } - - path = getenv("SYSTEMD_XBOOTLDR_PATH"); - if (path) { - struct stat st; - - 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); - - if (stat(path, &st) < 0) - return log_error_errno(errno, "Failed to stat '%s': %m", path); - if (!S_ISDIR(st.st_mode)) - return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "XBOOTLDR path '%s' is not a directory.", path); - - if (ret_uuid) - *ret_uuid = SD_ID128_NULL; - if (ret_devid) - *ret_devid = st.st_dev; - - goto found; - } - - r = verify_xbootldr("/boot", true, unprivileged_mode, ret_uuid, ret_devid); - 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; -} diff --git a/src/shared/bootspec.h b/src/shared/bootspec.h index 16353746ae..13a2029739 100644 --- a/src/shared/bootspec.h +++ b/src/shared/bootspec.h @@ -2,12 +2,11 @@ #pragma once +#include <errno.h> #include <inttypes.h> #include <stdbool.h> #include <sys/types.h> -#include "sd-id128.h" - #include "string-util.h" typedef enum BootEntryType { @@ -90,6 +89,3 @@ static inline const char* boot_entry_title(const BootEntry *entry) { return entry->show_title ?: entry->title ?: entry->id; } - -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, dev_t *ret_devid); -int find_xbootldr_and_warn(const char *path, bool unprivileged_mode, char **ret_path,sd_id128_t *ret_uuid, dev_t *ret_devid); diff --git a/src/shared/find-esp.c b/src/shared/find-esp.c new file mode 100644 index 0000000000..1569b46bdf --- /dev/null +++ b/src/shared/find-esp.c @@ -0,0 +1,709 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include <linux/magic.h> + +#include "sd-device.h" + +#include "alloc-util.h" +#include "blkid-util.h" +#include "env-util.h" +#include "errno-util.h" +#include "find-esp.h" +#include "gpt.h" +#include "id128-util.h" +#include "parse-util.h" +#include "path-util.h" +#include "stat-util.h" +#include "string-util.h" +#include "virt.h" + +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); + + r = blkid_probe_lookup_value(b, "TYPE", &v, NULL); + if (r != 0) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "No filesystem found on \"%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); + + r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL); + if (r != 0) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "File system \"%s\" is not located on a partitioned block device.", 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 (id128_equal_string(v, GPT_ESP) <= 0) + 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_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 (id128_equal_string(v, GPT_ESP) <= 0) + 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_fsroot_dir( + const char *path, + bool searching, + bool unprivileged_mode, + dev_t *ret_dev) { + + struct stat st, st2; + 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, + "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); + + if (path_equal(path, "/")) { + /* Let's assume that the root directory of the OS is always the root of its file system + * (which technically doesn't have to be the case, but it's close enough, and it's not easy + * to be fully correct for it, since we can't look further up than the root dir easily.) */ + if (ret_dev) + *ret_dev = st.st_dev; + + return 0; + } + + 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(); + + r = RET_NERRNO(stat(parent, &st2)); + } + + 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, + bool unprivileged_mode, + uint32_t *ret_part, + uint64_t *ret_pstart, + uint64_t *ret_psize, + sd_id128_t *ret_uuid, + dev_t *ret_devid) { + + bool relax_checks; + dev_t devid; + int r; + + 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 accessing the thing + */ + + relax_checks = getenv_bool("SYSTEMD_RELAX_ESP_CHECKS") > 0; + + /* Non-root user can only check the status, so if an error occurred in the following, it does not cause any + * 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) || + (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)) + 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); + } + + r = verify_fsroot_dir(p, searching, unprivileged_mode, &devid); + if (r < 0) + 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. */ + if (detect_container() > 0 || relax_checks) + goto finish; + + /* 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) + r = verify_esp_udev(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid); + else + r = verify_esp_blkid(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid); + if (r < 0) + return r; + + if (ret_devid) + *ret_devid = devid; + + return 0; + +finish: + if (ret_part) + *ret_part = 0; + if (ret_pstart) + *ret_pstart = 0; + if (ret_psize) + *ret_psize = 0; + if (ret_uuid) + *ret_uuid = SD_ID128_NULL; + if (ret_devid) + *ret_devid = 0; + + return 0; +} + +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, + dev_t *ret_devid) { + + int r; + + /* This logs about all errors except: + * + * -ENOKEY → when we can't find the partition + * -EACCESS → when unprivileged_mode is true, and we can't access something + */ + + if (path) { + r = verify_esp(path, /* searching= */ false, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid); + if (r < 0) + return r; + + goto found; + } + + path = getenv("SYSTEMD_ESP_PATH"); + if (path) { + struct stat st; + + if (!path_is_valid(path) || !path_is_absolute(path)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "$SYSTEMD_ESP_PATH does not refer to absolute path, refusing to use it: %s", + path); + + /* Note: when the user explicitly configured things with an env var we won't validate the + * path beyond checking it refers to a directory. After all we want this to be useful for + * testing. */ + + if (stat(path, &st) < 0) + return log_error_errno(errno, "Failed to stat '%s': %m", path); + if (!S_ISDIR(st.st_mode)) + return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "ESP path '%s' is not a directory.", path); + + if (ret_part) + *ret_part = 0; + if (ret_pstart) + *ret_pstart = 0; + if (ret_psize) + *ret_psize = 0; + if (ret_uuid) + *ret_uuid = SD_ID128_NULL; + if (ret_devid) + *ret_devid = st.st_dev; + + goto found; + } + + FOREACH_STRING(path, "/efi", "/boot", "/boot/efi") { + + r = verify_esp(path, /* searching= */ true, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid); + if (r >= 0) + goto found; + if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */ + return r; + } + + /* No logging here */ + return -ENOKEY; + +found: + if (ret_path) { + char *c; + + c = strdup(path); + if (!c) + return log_oom(); + + *ret_path = c; + } + + 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 (id128_equal_string(v, GPT_XBOOTLDR) <= 0) + 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_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 (id128_equal_string(v, GPT_XBOOTLDR)) + 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, + bool unprivileged_mode, + sd_id128_t *ret_uuid, + dev_t *ret_devid) { + + bool relax_checks; + dev_t devid; + int r; + + assert(p); + + relax_checks = getenv_bool("SYSTEMD_RELAX_XBOOTLDR_CHECKS") > 0; + + r = verify_fsroot_dir(p, searching, unprivileged_mode, &devid); + if (r < 0) + return r; + + if (detect_container() > 0 || relax_checks) + goto finish; + + if (unprivileged_mode) + r = verify_xbootldr_udev(devid, searching, ret_uuid); + else + r = verify_xbootldr_blkid(devid, searching, ret_uuid); + if (r < 0) + return r; + + if (ret_devid) + *ret_devid = devid; + + return 0; + +finish: + if (ret_uuid) + *ret_uuid = SD_ID128_NULL; + if (ret_devid) + *ret_devid = 0; + + return 0; +} + +int find_xbootldr_and_warn( + const char *path, + bool unprivileged_mode, + char **ret_path, + sd_id128_t *ret_uuid, + dev_t *ret_devid) { + + int r; + + /* Similar to find_esp_and_warn(), but finds the XBOOTLDR partition. Returns the same errors. */ + + if (path) { + r = verify_xbootldr(path, /* searching= */ false, unprivileged_mode, ret_uuid, ret_devid); + if (r < 0) + return r; + + goto found; + } + + path = getenv("SYSTEMD_XBOOTLDR_PATH"); + if (path) { + struct stat st; + + 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); + + if (stat(path, &st) < 0) + return log_error_errno(errno, "Failed to stat '%s': %m", path); + if (!S_ISDIR(st.st_mode)) + return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "XBOOTLDR path '%s' is not a directory.", path); + + if (ret_uuid) + *ret_uuid = SD_ID128_NULL; + if (ret_devid) + *ret_devid = st.st_dev; + + goto found; + } + + r = verify_xbootldr("/boot", true, unprivileged_mode, ret_uuid, ret_devid); + 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; +} diff --git a/src/shared/find-esp.h b/src/shared/find-esp.h new file mode 100644 index 0000000000..e4e65ac3e2 --- /dev/null +++ b/src/shared/find-esp.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#pragma once + +#include <inttypes.h> +#include <stdbool.h> +#include <sys/types.h> + +#include "sd-id128.h" + +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, dev_t *ret_devid); +int find_xbootldr_and_warn(const char *path, bool unprivileged_mode, char **ret_path, sd_id128_t *ret_uuid, dev_t *ret_devid); diff --git a/src/shared/meson.build b/src/shared/meson.build index fa636dfa2f..76cfa89fe5 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -125,6 +125,8 @@ shared_sources = files( 'fdset.h', 'fileio-label.c', 'fileio-label.h', + 'find-esp.c', + 'find-esp.h', 'firewall-util-nft.c', 'firewall-util-private.h', 'firewall-util.c', |