]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
bootctl: add --root and --image
authorLuca Boccassi <bluca@debian.org>
Sun, 12 Jun 2022 23:01:02 +0000 (00:01 +0100)
committerLuca Boccassi <bluca@debian.org>
Fri, 8 Jul 2022 15:58:15 +0000 (16:58 +0100)
Operate on image/directory, and also take files to install from it

man/bootctl.xml
shell-completion/bash/bootctl
shell-completion/zsh/_bootctl
src/boot/bless-boot.c
src/boot/bootctl.c
src/fuzz/fuzz-bootspec.c
src/shared/bootspec.c
src/shared/bootspec.h
src/shared/find-esp.c
src/shared/find-esp.h
src/systemctl/systemctl-start-special.c

index 8c8823838347c168c7590633eda413a3f8c4e747..839fa79efd85a7a5f2063390c333fe610a9a9ff9 100644 (file)
         Loader partition to <filename>/boot/</filename>, if possible.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--root=<replaceable>root</replaceable></option></term>
+        <listitem><para>Takes a directory path as an argument. All
+        paths will be prefixed with the given alternate
+        <replaceable>root</replaceable> path, including config search
+        paths. </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--image=<replaceable>image</replaceable></option></term>
+
+        <listitem><para>Takes a path to a disk image file or block device node. If specified all operations
+        are applied to file system in the indicated disk image. This is similar to <option>--root=</option>
+        but operates on file systems stored in disk images or block devices. The disk image should either
+        contain just a file system or a set of file systems within a GPT partition table, following the
+        <ulink url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions
+        Specification</ulink>. For further information on supported disk images, see
+        <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
+        switch of the same name.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>-p</option></term>
         <term><option>--print-esp-path</option></term>
index 9214af5350642572f1a3e3b87a99ccf1b5d5955b..fd71cffe3f05e50591581323ff7edda4f6197644 100644 (file)
@@ -32,7 +32,7 @@ _bootctl() {
     local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
     local -A OPTS=(
         [STANDALONE]='-h --help -p --print-esp-path -x --print-boot-path --version --no-variables --no-pager --graceful'
-        [ARG]='--esp-path --boot-path --make-machine-id-directory'
+        [ARG]='--esp-path --boot-path --make-machine-id-directory --root --image'
     )
 
     if __contains_word "$prev" ${OPTS[ARG]}; then
@@ -48,6 +48,10 @@ _bootctl() {
             --make-machine-id-directory)
                 comps="yes no auto"
                 ;;
+            --image|--root)
+                compopt -o nospace
+                comps=$( compgen -A file -- "$cur" )
+                ;;
         esac
         COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
         return 0
index eef48d71d9be720935cb26ac464870f4473eacd7..fbd62bc0fd91fa97e8b23d281b3f8fdc2c6def4c 100644 (file)
@@ -73,4 +73,6 @@ _arguments \
     '--no-variables[Do not touch EFI variables]' \
     '--no-pager[Do not pipe output into a pager]' \
     '--graceful[Do not fail when locating ESP or writing fails]' \
+    '--root=[Operate under the specified directory]:PATH' \
+    '--image=[Operate on the specified image]:PATH' \
     '*::bootctl command:_bootctl_commands'
index d9c901d73bb2cc8eae76c949a948415725f85108..315a1a37edb529a1ef88b6500b7e28e24963b63e 100644 (file)
@@ -108,11 +108,11 @@ static int acquire_path(void) {
         if (!strv_isempty(arg_path))
                 return 0;
 
-        r = find_esp_and_warn(NULL, /* unprivileged_mode= */ false, &esp_path, NULL, NULL, NULL, NULL, &esp_devid);
+        r = find_esp_and_warn(NULL, NULL, /* unprivileged_mode= */ false, &esp_path, NULL, NULL, NULL, NULL, &esp_devid);
         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;
 
-        r = find_xbootldr_and_warn(NULL, /* unprivileged_mode= */ false, &xbootldr_path, NULL, &xbootldr_devid);
+        r = find_xbootldr_and_warn(NULL, NULL, /* unprivileged_mode= */ false, &xbootldr_path, NULL, &xbootldr_devid);
         if (r < 0 && r != -ENOKEY)
                 return r;
 
index e50f96cb2fa90d3664bc5796d7bfa8167ba1cbd7..6cb0f4253935135256827dd1ff0aaf7c9b867bd9 100644 (file)
 #include "alloc-util.h"
 #include "blkid-util.h"
 #include "bootspec.h"
+#include "chase-symlinks.h"
 #include "copy.h"
 #include "devnum-util.h"
 #include "dirent-util.h"
+#include "dissect-image.h"
 #include "efi-api.h"
 #include "efi-loader.h"
 #include "efivars.h"
@@ -31,6 +33,7 @@
 #include "glyph-util.h"
 #include "main-func.h"
 #include "mkdir.h"
+#include "mount-util.h"
 #include "os-util.h"
 #include "pager.h"
 #include "parse-argument.h"
@@ -75,11 +78,15 @@ static enum {
 static char *arg_entry_token = NULL;
 static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
 static bool arg_arch_all = false;
+static char *arg_root = NULL;
+static char *arg_image = NULL;
 
 STATIC_DESTRUCTOR_REGISTER(arg_esp_path, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_xbootldr_path, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_install_layout, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_entry_token, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
 
 static const char *arg_dollar_boot_path(void) {
         /* $BOOT shall be the XBOOTLDR partition if it exists, and otherwise the ESP */
@@ -104,7 +111,7 @@ static int acquire_esp(
          * we simply eat up the error here, so that --list and --status work too, without noise about
          * this). */
 
-        r = find_esp_and_warn(arg_esp_path, unprivileged_mode, &np, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid);
+        r = find_esp_and_warn(arg_root, arg_esp_path, unprivileged_mode, &np, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid);
         if (r == -ENOKEY) {
                 if (graceful)
                         return log_full_errno(arg_quiet ? LOG_DEBUG : LOG_INFO, r,
@@ -131,7 +138,7 @@ static int acquire_xbootldr(
         char *np;
         int r;
 
-        r = find_xbootldr_and_warn(arg_xbootldr_path, unprivileged_mode, &np, ret_uuid, ret_devid);
+        r = find_xbootldr_and_warn(arg_root, arg_xbootldr_path, unprivileged_mode, &np, ret_uuid, ret_devid);
         if (r == -ENOKEY) {
                 log_debug_errno(r, "Didn't find an XBOOTLDR partition, using the ESP as $BOOT.");
                 arg_xbootldr_path = mfree(arg_xbootldr_path);
@@ -596,7 +603,6 @@ static int boot_config_load_and_select(
                 const char *xbootldr_path,
                 dev_t xbootldr_devid) {
 
-        _cleanup_strv_free_ char **efi_entries = NULL;
         int r;
 
         /* If XBOOTLDR and ESP actually refer to the same block device, suppress XBOOTLDR, since it would
@@ -607,15 +613,19 @@ static int boot_config_load_and_select(
         if (r < 0)
                 return r;
 
-        r = efi_loader_get_entries(&efi_entries);
-        if (r == -ENOENT || ERRNO_IS_NOT_SUPPORTED(r))
-                log_debug_errno(r, "Boot loader reported no entries.");
-        else if (r < 0)
-                log_warning_errno(r, "Failed to determine entries reported by boot loader, ignoring: %m");
-        else
-                (void) boot_config_augment_from_loader(config, efi_entries, /* only_auto= */ false);
+        if (!arg_root) {
+                _cleanup_strv_free_ char **efi_entries = NULL;
 
-        return boot_config_select_special_entries(config);
+                r = efi_loader_get_entries(&efi_entries);
+                if (r == -ENOENT || ERRNO_IS_NOT_SUPPORTED(r))
+                        log_debug_errno(r, "Boot loader reported no entries.");
+                else if (r < 0)
+                        log_warning_errno(r, "Failed to determine entries reported by boot loader, ignoring: %m");
+                else
+                        (void) boot_config_augment_from_loader(config, efi_entries, /* only_auto= */ false);
+        }
+
+        return boot_config_select_special_entries(config, /* skip_efivars= */ !!arg_root);
 }
 
 static int status_entries(
@@ -833,48 +843,74 @@ static int create_subdirs(const char *root, const char * const *subdirs) {
 }
 
 static int copy_one_file(const char *esp_path, const char *name, bool force) {
+        _cleanup_free_ char *source_path = NULL, *dest_path = NULL, *p = NULL, *q = NULL;
         const char *e;
-        char *p, *q, *dest_name, *s;
-        int r;
+        char *dest_name, *s;
+        int r, ret;
 
         dest_name = strdupa_safe(name);
         s = endswith_no_case(dest_name, ".signed");
         if (s)
                 *s = 0;
 
-        p = strjoina(BOOTLIBDIR "/", name);
-        q = strjoina(esp_path, "/EFI/systemd/", dest_name);
-        r = copy_file_with_version_check(p, q, force);
+        p = path_join(BOOTLIBDIR, name);
+        if (!p)
+                return log_oom();
+
+        r = chase_symlinks(p, arg_root, CHASE_PREFIX_ROOT, &source_path, NULL);
+        if (r < 0)
+                return log_error_errno(r,
+                                       "Failed to resolve path %s%s%s: %m",
+                                       p,
+                                       arg_root ? " under directory " : "",
+                                       arg_root ? arg_root : "");
+
+        q = path_join("/EFI/systemd/", dest_name);
+        if (!q)
+                return log_oom();
+
+        r = chase_symlinks(q, esp_path, CHASE_PREFIX_ROOT | CHASE_NONEXISTENT, &dest_path, NULL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to resolve path %s under directory %s: %m", q, esp_path);
+
+        /* Note that if this fails we do the second copy anyway, but return this error code,
+         * so we stash it away in a separate variable. */
+        ret = copy_file_with_version_check(source_path, dest_path, force);
 
         e = startswith(dest_name, "systemd-boot");
         if (e) {
-                int k;
+                _cleanup_free_ char *default_dest_path = NULL;
                 char *v;
 
                 /* Create the EFI default boot loader name (specified for removable devices) */
-                v = strjoina(esp_path, "/EFI/BOOT/BOOT", e);
+                v = strjoina("/EFI/BOOT/BOOT", e);
                 ascii_strupper(strrchr(v, '/') + 1);
 
-                k = copy_file_with_version_check(p, v, force);
-                if (k < 0 && r == 0)
-                        r = k;
+                r = chase_symlinks(v, esp_path, CHASE_PREFIX_ROOT | CHASE_NONEXISTENT, &default_dest_path, NULL);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to resolve path %s under directory %s: %m", v, esp_path);
+
+                r = copy_file_with_version_check(source_path, default_dest_path, force);
+                if (r < 0 && ret == 0)
+                        ret = r;
         }
 
-        return r;
+        return ret;
 }
 
 static int install_binaries(const char *esp_path, const char *arch, bool force) {
         _cleanup_closedir_ DIR *d = NULL;
-        int r = 0;
+        _cleanup_free_ char *path = NULL;
+        int r;
 
-        d = opendir(BOOTLIBDIR);
-        if (!d)
-                return log_error_errno(errno, "Failed to open \""BOOTLIBDIR"\": %m");
+        r = chase_symlinks_and_opendir(BOOTLIBDIR, arg_root, CHASE_PREFIX_ROOT, &path, &d);
+        if (r < 0)
+                return log_error_errno(r, "Failed to open boot loader directory %s: %m", BOOTLIBDIR);
 
         const char *suffix = strjoina(arch, ".efi");
         const char *suffix_signed = strjoina(arch, ".efi.signed");
 
-        FOREACH_DIRENT(de, d, return log_error_errno(errno, "Failed to read \""BOOTLIBDIR"\": %m")) {
+        FOREACH_DIRENT(de, d, return log_error_errno(errno, "Failed to read \"%s\": %m", path)) {
                 int k;
 
                 if (!endswith_no_case(de->d_name, suffix) && !endswith_no_case(de->d_name, suffix_signed))
@@ -1022,6 +1058,12 @@ static int install_variables(const char *esp_path,
         uint16_t slot;
         int r;
 
+        if (arg_root) {
+                log_info("Acting on %s, skipping EFI variable setup.",
+                         arg_image ? "image" : "root directory");
+                return 0;
+        }
+
         if (!is_efi_boot()) {
                 log_warning("Not booted with EFI, skipping EFI variable setup.");
                 return 0;
@@ -1178,7 +1220,7 @@ static int remove_variables(sd_id128_t uuid, const char *path, bool in_order) {
         uint16_t slot;
         int r;
 
-        if (!is_efi_boot())
+        if (arg_root || !is_efi_boot())
                 return 0;
 
         r = find_slot(uuid, path, &slot);
@@ -1350,6 +1392,8 @@ static int help(int argc, char *argv[], void *userdata) {
                "     --version         Print version\n"
                "     --esp-path=PATH   Path to the EFI System Partition (ESP)\n"
                "     --boot-path=PATH  Path to the $BOOT partition\n"
+               "     --root=PATH       Operate on an alternate filesystem root\n"
+               "     --image=PATH      Operate on disk image as filesystem root\n"
                "  -p --print-esp-path  Print path to the EFI System Partition\n"
                "  -x --print-boot-path Print path to the $BOOT partition\n"
                "     --no-variables    Don't touch EFI variables\n"
@@ -1380,6 +1424,8 @@ static int parse_argv(int argc, char *argv[]) {
         enum {
                 ARG_ESP_PATH = 0x100,
                 ARG_BOOT_PATH,
+                ARG_ROOT,
+                ARG_IMAGE,
                 ARG_VERSION,
                 ARG_NO_VARIABLES,
                 ARG_NO_PAGER,
@@ -1396,6 +1442,8 @@ static int parse_argv(int argc, char *argv[]) {
                 { "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                 },
+                { "root",                      required_argument, NULL, ARG_ROOT                      },
+                { "image",                     required_argument, NULL, ARG_IMAGE                     },
                 { "print-esp-path",            no_argument,       NULL, 'p'                           },
                 { "print-path",                no_argument,       NULL, 'p'                           }, /* Compatibility alias */
                 { "print-boot-path",           no_argument,       NULL, 'x'                           },
@@ -1439,6 +1487,18 @@ static int parse_argv(int argc, char *argv[]) {
                                 return log_oom();
                         break;
 
+                case ARG_ROOT:
+                        r = parse_path_argument(optarg, /* suppress_root= */ true, &arg_root);
+                        if (r < 0)
+                                return r;
+                        break;
+
+                case ARG_IMAGE:
+                        r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_image);
+                        if (r < 0)
+                                return r;
+                        break;
+
                 case 'p':
                         if (arg_print_dollar_boot_path)
                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
@@ -1523,6 +1583,15 @@ static int parse_argv(int argc, char *argv[]) {
                         assert_not_reached();
                 }
 
+        if ((arg_root || arg_image) && argv[optind] && !STR_IN_SET(argv[optind], "status", "list",
+                        "install", "update", "remove", "is-installed", "random-seed"))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "Options --root= and --image= are not supported with verb %s.",
+                                       argv[optind]);
+
+        if (arg_root && arg_image)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root= or --image=, the combination of both is not supported.");
+
         return 1;
 }
 
@@ -1608,7 +1677,7 @@ static int verb_status(int argc, char *argv[], void *userdata) {
 
         pager_open(arg_pager_flags);
 
-        if (is_efi_boot()) {
+        if (!arg_root && is_efi_boot()) {
                 static const struct {
                         uint64_t flag;
                         const char *name;
@@ -1722,7 +1791,7 @@ static int verb_status(int argc, char *argv[], void *userdata) {
                         r = k;
         }
 
-        if (is_efi_boot()) {
+        if (!arg_root && is_efi_boot()) {
                 k = status_variables();
                 if (k < 0)
                         r = k;
@@ -1846,6 +1915,12 @@ static int install_random_seed(const char *esp) {
                 return 0;
         }
 
+        if (arg_root) {
+                log_warning("Acting on %s, skipping EFI variable setup.",
+                             arg_image ? "image" : "root directory");
+                return 0;
+        }
+
         r = getenv_bool("SYSTEMD_WRITE_SYSTEM_TOKEN");
         if (r < 0) {
                 if (r != -ENXIO)
@@ -2204,6 +2279,11 @@ static int parse_loader_entry_target_arg(const char *arg1, char16_t **ret_target
 static int verb_set_efivar(int argc, char *argv[], void *userdata) {
         int r;
 
+        if (arg_root)
+                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+                                       "Acting on %s, skipping EFI variable setup.",
+                                       arg_image ? "image" : "root directory");
+
         if (!is_efi_boot())
                 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
                                        "Not booted with UEFI.");
@@ -2267,7 +2347,7 @@ static int verb_set_efivar(int argc, char *argv[], void *userdata) {
 static int verb_random_seed(int argc, char *argv[], void *userdata) {
         int r;
 
-        r = find_esp_and_warn(arg_esp_path, false, &arg_esp_path, NULL, NULL, NULL, NULL, NULL);
+        r = find_esp_and_warn(arg_root, arg_esp_path, false, &arg_esp_path, NULL, NULL, NULL, NULL, NULL);
         if (r == -ENOKEY) {
                 /* find_esp_and_warn() doesn't warn about ENOKEY, so let's do that on our own */
                 if (!arg_graceful)
@@ -2375,6 +2455,9 @@ static int bootctl_main(int argc, char *argv[]) {
 }
 
 static int run(int argc, char *argv[]) {
+        _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
+        _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
+        _cleanup_(umount_and_rmdir_and_freep) char *unlink_dir = NULL;
         int r;
 
         log_parse_environment();
@@ -2388,6 +2471,25 @@ static int run(int argc, char *argv[]) {
         if (r <= 0)
                 return r;
 
+        /* Open up and mount the image */
+        if (arg_image) {
+                assert(!arg_root);
+
+                r = mount_image_privately_interactively(
+                                arg_image,
+                                DISSECT_IMAGE_GENERIC_ROOT |
+                                DISSECT_IMAGE_RELAX_VAR_CHECK,
+                                &unlink_dir,
+                                &loop_device,
+                                &decrypted_image);
+                if (r < 0)
+                        return r;
+
+                arg_root = strdup(unlink_dir);
+                if (!arg_root)
+                        return log_oom();
+        }
+
         return bootctl_main(argc, argv);
 }
 
index c26cc94db93237362653bf3ebb17e73a5969d76a..c08f76c14a3e2e8a611d78c23310a8fee1423624 100644 (file)
@@ -104,7 +104,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
 
         assert_se(boot_config_finalize(&config) >= 0);
 
-        (void) boot_config_select_special_entries(&config);
+        (void) boot_config_select_special_entries(&config, /* skip_efivars= */ false);
 
         _cleanup_close_ int orig_stdout_fd = -1;
         if (getenv_bool("SYSTEMD_FUZZ_OUTPUT") <= 0) {
index 2c0ec6c272c1b838238d46c95342d309d2568e9c..1a47fff167c357026658681ecb4914200941694e 100644 (file)
@@ -840,12 +840,12 @@ static int boot_entries_select_selected(const BootConfig *config) {
         return boot_config_find(config, config->entry_selected);
 }
 
-static int boot_load_efi_entry_pointers(BootConfig *config) {
+static int boot_load_efi_entry_pointers(BootConfig *config, bool skip_efivars) {
         int r;
 
         assert(config);
 
-        if (!is_efi_boot())
+        if (skip_efivars || !is_efi_boot())
                 return 0;
 
         /* Loads the three "pointers" to boot loader entries from their EFI variables */
@@ -871,12 +871,12 @@ static int boot_load_efi_entry_pointers(BootConfig *config) {
         return 1;
 }
 
-int boot_config_select_special_entries(BootConfig *config) {
+int boot_config_select_special_entries(BootConfig *config, bool skip_efivars) {
         int r;
 
         assert(config);
 
-        r = boot_load_efi_entry_pointers(config);
+        r = boot_load_efi_entry_pointers(config, skip_efivars);
         if (r < 0)
                 return r;
 
@@ -967,11 +967,11 @@ int boot_config_load_auto(
                                                "Failed to determine whether /run/boot-loader-entries/ exists: %m");
         }
 
-        r = find_esp_and_warn(override_esp_path, /* unprivileged_mode= */ false, &esp_where, NULL, NULL, NULL, NULL, &esp_devid);
+        r = find_esp_and_warn(NULL, override_esp_path, /* unprivileged_mode= */ false, &esp_where, NULL, NULL, NULL, NULL, &esp_devid);
         if (r < 0) /* we don't log about ENOKEY here, but propagate it, leaving it to the caller to log */
                 return r;
 
-        r = find_xbootldr_and_warn(override_xbootldr_path, /* unprivileged_mode= */ false, &xbootldr_where, NULL, &xbootldr_devid);
+        r = find_xbootldr_and_warn(NULL, override_xbootldr_path, /* unprivileged_mode= */ false, &xbootldr_where, NULL, &xbootldr_devid);
         if (r < 0 && r != -ENOKEY)
                 return r; /* It's fine if the XBOOTLDR partition doesn't exist, hence we ignore ENOKEY here */
 
index ff54cc2e84801d682d7bca497b95fd5dfcda154e..36a3489e87860eea534e662aa5655732dce53ac5 100644 (file)
@@ -100,7 +100,7 @@ int boot_config_load(BootConfig *config, const char *esp_path, const char *xboot
 int boot_config_load_auto(BootConfig *config, const char *override_esp_path, const char *override_xbootldr_path);
 int boot_config_augment_from_loader(BootConfig *config, char **list, bool only_auto);
 
-int boot_config_select_special_entries(BootConfig *config);
+int boot_config_select_special_entries(BootConfig *config, bool skip_efivars);
 
 static inline const char* boot_entry_title(const BootEntry *entry) {
         assert(entry);
index ea27d7911a7880da40152e8714ba3d12e71abc7c..87ac9d167efa52e9df982fec49c10bbb8beba63f 100644 (file)
@@ -8,6 +8,7 @@
 
 #include "alloc-util.h"
 #include "blkid-util.h"
+#include "chase-symlinks.h"
 #include "device-util.h"
 #include "devnum-util.h"
 #include "env-util.h"
 #include "string-util.h"
 #include "virt.h"
 
+typedef enum VerifyESPFlags {
+        VERIFY_ESP_SEARCHING         = 1 << 0, /* Downgrade various "not found" logs to debug level */
+        VERIFY_ESP_UNPRIVILEGED_MODE = 1 << 1, /* Call into udev rather than blkid */
+        VERIFY_ESP_RELAX_CHECKS      = 1 << 2, /* Do not validate ESP partition */
+} VerifyESPFlags;
+
 static int verify_esp_blkid(
                 dev_t devid,
                 bool searching,
@@ -298,15 +305,15 @@ static int verify_fsroot_dir(
 
 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) {
+                dev_t *ret_devid,
+                VerifyESPFlags flags) {
 
-        bool relax_checks;
+        bool relax_checks, searching = FLAGS_SET(flags, VERIFY_ESP_SEARCHING),
+             unprivileged_mode = FLAGS_SET(flags, VERIFY_ESP_UNPRIVILEGED_MODE);
         dev_t devid;
         int r;
 
@@ -319,7 +326,7 @@ static int verify_esp(
          *  -EACESS        → if 'unprivileged_mode' is set, and we have trouble accessing the thing
          */
 
-        relax_checks = getenv_bool("SYSTEMD_RELAX_ESP_CHECKS") > 0;
+        relax_checks = getenv_bool("SYSTEMD_RELAX_ESP_CHECKS") > 0 || FLAGS_SET(flags, VERIFY_ESP_RELAX_CHECKS);
 
         /* 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. */
@@ -381,6 +388,7 @@ finish:
 }
 
 int find_esp_and_warn(
+                const char *root,
                 const char *path,
                 bool unprivileged_mode,
                 char **ret_path,
@@ -390,6 +398,9 @@ int find_esp_and_warn(
                 sd_id128_t *ret_uuid,
                 dev_t *ret_devid) {
 
+        VerifyESPFlags flags = (unprivileged_mode ? VERIFY_ESP_UNPRIVILEGED_MODE : 0) |
+                               (root ? VERIFY_ESP_RELAX_CHECKS : 0);
+        _cleanup_free_ char *p = NULL;
         int r;
 
         /* This logs about all errors except:
@@ -399,7 +410,15 @@ int find_esp_and_warn(
          */
 
         if (path) {
-                r = verify_esp(path, /* searching= */ false, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid);
+                r = chase_symlinks(path, root, CHASE_PREFIX_ROOT, &p, NULL);
+                if (r < 0)
+                        return log_error_errno(r,
+                                               "Failed to resolve path %s%s%s: %m",
+                                               path,
+                                               root ? " under directory " : "",
+                                               root ?: "");
+
+                r = verify_esp(p, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid, flags);
                 if (r < 0)
                         return r;
 
@@ -410,19 +429,27 @@ int find_esp_and_warn(
         if (path) {
                 struct stat st;
 
-                if (!path_is_valid(path) || !path_is_absolute(path))
+                r = chase_symlinks(path, root, CHASE_PREFIX_ROOT, &p, NULL);
+                if (r < 0)
+                        return log_error_errno(r,
+                                               "Failed to resolve path %s%s%s: %m",
+                                               path,
+                                               root ? " under directory " : "",
+                                               root ?: "");
+
+                if (!path_is_valid(p) || !path_is_absolute(p))
                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                                "$SYSTEMD_ESP_PATH does not refer to absolute path, refusing to use it: %s",
-                                               path);
+                                               p);
 
                 /* 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 (stat(p, &st) < 0)
+                        return log_error_errno(errno, "Failed to stat '%s': %m", p);
                 if (!S_ISDIR(st.st_mode))
-                        return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "ESP path '%s' is not a directory.", path);
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "ESP path '%s' is not a directory.", p);
 
                 if (ret_part)
                         *ret_part = 0;
@@ -438,29 +465,33 @@ int find_esp_and_warn(
                 goto found;
         }
 
-        FOREACH_STRING(_path, "/efi", "/boot", "/boot/efi") {
-                path = _path;
-
-                r = verify_esp(path, /* searching= */ true, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid);
+        FOREACH_STRING(dir, "/efi", "/boot", "/boot/efi") {
+                r = chase_symlinks(dir, root, CHASE_PREFIX_ROOT, &p, NULL);
+                if (r == -ENOENT)
+                        continue;
+                if (r < 0)
+                        return log_error_errno(r,
+                                               "Failed to resolve path %s%s%s: %m",
+                                               dir,
+                                               root ? " under directory " : "",
+                                               root ?: "");
+
+                r = verify_esp(p, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid,
+                               flags | VERIFY_ESP_SEARCHING);
                 if (r >= 0)
                         goto found;
                 if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */
                         return r;
+
+                p = mfree(p);
         }
 
         /* No logging here */
         return -ENOKEY;
 
 found:
-        if (ret_path) {
-                char *c;
-
-                c = strdup(path);
-                if (!c)
-                        return log_oom();
-
-                *ret_path = c;
-        }
+        if (ret_path)
+                *ret_path = TAKE_PTR(p);
 
         return 0;
 }
@@ -662,18 +693,28 @@ finish:
 }
 
 int find_xbootldr_and_warn(
+                const char *root,
                 const char *path,
                 bool unprivileged_mode,
                 char **ret_path,
                 sd_id128_t *ret_uuid,
                 dev_t *ret_devid) {
 
+        _cleanup_free_ char *p = NULL;
         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);
+                r = chase_symlinks(path, root, CHASE_PREFIX_ROOT, &p, NULL);
+                if (r < 0)
+                        return log_error_errno(r,
+                                               "Failed to resolve path %s%s%s: %m",
+                                               path,
+                                               root ? " under directory " : "",
+                                               root ?: "");
+
+                r = verify_xbootldr(p, /* searching= */ false, unprivileged_mode, ret_uuid, ret_devid);
                 if (r < 0)
                         return r;
 
@@ -684,15 +725,23 @@ int find_xbootldr_and_warn(
         if (path) {
                 struct stat st;
 
-                if (!path_is_valid(path) || !path_is_absolute(path))
+                r = chase_symlinks(path, root, CHASE_PREFIX_ROOT, &p, NULL);
+                if (r < 0)
+                        return log_error_errno(r,
+                                               "Failed to resolve path %s%s%s: %m",
+                                               path,
+                                               root ? " under directory " : "",
+                                               root ?: "");
+
+                if (!path_is_valid(p) || !path_is_absolute(p))
                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                                "$SYSTEMD_XBOOTLDR_PATH does not refer to absolute path, refusing to use it: %s",
-                                               path);
+                                               p);
 
-                if (stat(path, &st) < 0)
-                        return log_error_errno(errno, "Failed to stat '%s': %m", path);
+                if (stat(p, &st) < 0)
+                        return log_error_errno(errno, "Failed to stat '%s': %m", p);
                 if (!S_ISDIR(st.st_mode))
-                        return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "XBOOTLDR path '%s' is not a directory.", path);
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "XBOOTLDR path '%s' is not a directory.", p);
 
                 if (ret_uuid)
                         *ret_uuid = SD_ID128_NULL;
@@ -702,26 +751,26 @@ int find_xbootldr_and_warn(
                 goto found;
         }
 
-        r = verify_xbootldr("/boot", true, unprivileged_mode, ret_uuid, ret_devid);
-        if (r >= 0) {
-                path = "/boot";
+        r = chase_symlinks("/boot", root, CHASE_PREFIX_ROOT, &p, NULL);
+        if (r == -ENOENT)
+                return -ENOKEY;
+        if (r < 0)
+                return log_error_errno(r,
+                                       "Failed to resolve path /boot%s%s: %m",
+                                       root ? " under directory " : "",
+                                       root ?: "");
+
+        r = verify_xbootldr(p, true, unprivileged_mode, ret_uuid, ret_devid);
+        if (r >= 0)
                 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;
-        }
+        if (ret_path)
+                *ret_path = TAKE_PTR(p);
 
         return 0;
 }
index e4e65ac3e2c29aa001b066c03289974f462c7770..78d7f4551ed80127764df4db46e082319ee61532 100644 (file)
@@ -8,5 +8,5 @@
 
 #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);
+int find_esp_and_warn(const char *root, 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 *root, const char *path, bool unprivileged_mode, char **ret_path, sd_id128_t *ret_uuid, dev_t *ret_devid);
index cfd0078964aa93501755717d09bf70d5c0e0e0ac..9363764cd70e6e7d256beddbfe9e5b01667a5f5e 100644 (file)
@@ -43,7 +43,7 @@ static int load_kexec_kernel(void) {
         if (r < 0)
                 return r;
 
-        r = boot_config_select_special_entries(&config);
+        r = boot_config_select_special_entries(&config, /* skip_efivars= */ false);
         if (r < 0)
                 return r;