]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
bootctl: accept espPath/xbootldrPath in Varlink methods
authorr-vdp <ramses@well-founded.dev>
Wed, 1 Jul 2026 12:16:09 +0000 (14:16 +0200)
committerr-vdp <ramses@well-founded.dev>
Thu, 2 Jul 2026 15:34:03 +0000 (17:34 +0200)
The CLI verbs have --esp-path/--boot-path but the Varlink methods
always auto-discover the partitions, so callers that mount the ESP
or XBOOTLDR at a non-standard location have to fall back to the
SYSTEMD_ESP_PATH/SYSTEMD_XBOOTLDR_PATH environment variables.
Allow to specify the paths when calling Install/Unlink/Link/LinkAuto
so the Varlink API is on par with the CLI.

src/bootctl/bootctl-install.c
src/bootctl/bootctl-link.c
src/bootctl/bootctl-unlink.c
src/shared/varlink-io.systemd.BootControl.c
test/units/TEST-87-AUX-UTILS-VM.bootctl.sh

index f8ba48a485bcd0b062d961c50084d435d7d4a064..5015f4c8072d1d61712908588c81c4433eec6a5e 100644 (file)
@@ -2085,12 +2085,16 @@ static JSON_DISPATCH_ENUM_DEFINE(json_dispatch_boot_entry_token_type, BootEntryT
 typedef struct InstallParameters {
         InstallContext context;
         unsigned root_fd_index;
+        char *esp_path;
+        char *xbootldr_path;
 } InstallParameters;
 
 static void install_parameters_done(InstallParameters *p) {
         assert(p);
 
         install_context_done(&p->context);
+        free(p->esp_path);
+        free(p->xbootldr_path);
 }
 
 int vl_method_install(
@@ -2113,6 +2117,8 @@ int vl_method_install(
                 { "graceful",           SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_stdbool,            voffsetof(p, context.graceful),         0                 },
                 { "rootFileDescriptor", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint,               voffsetof(p, root_fd_index),            0                 },
                 { "rootDirectory",      SD_JSON_VARIANT_STRING,        json_dispatch_path,                  voffsetof(p, context.root),             0                 },
+                { "espPath",            SD_JSON_VARIANT_STRING,        json_dispatch_path,                  voffsetof(p, esp_path),                 0                 },
+                { "xbootldrPath",       SD_JSON_VARIANT_STRING,        json_dispatch_path,                  voffsetof(p, xbootldr_path),            0                 },
                 { "bootEntryTokenType", SD_JSON_VARIANT_STRING,        json_dispatch_boot_entry_token_type, voffsetof(p, context.entry_token_type), 0                 },
                 { "touchVariables",     SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_tristate,           voffsetof(p, context.touch_variables),  0                 },
                 {},
@@ -2162,7 +2168,7 @@ int vl_method_install(
 
         r = find_esp_and_warn_at_full(
                         p.context.root_fd,
-                        /* path= */ NULL,
+                        p.esp_path,
                         /* unprivileged_mode= */ false,
                         &p.context.esp_path,
                         &p.context.esp_fd,
@@ -2178,7 +2184,7 @@ int vl_method_install(
 
         r = find_xbootldr_and_warn_at(
                         p.context.root_fd,
-                        /* path= */ NULL,
+                        p.xbootldr_path,
                         /* unprivileged_mode= */ false,
                         &p.context.xbootldr_path,
                         &p.context.xbootldr_fd);
index af0955b64abbc2074153f6ac5cc89f14338513c6..b4706670857ff0495741d9a6beae7dc58af6d67d 100644 (file)
@@ -1355,6 +1355,8 @@ typedef struct LinkParameters {
         LinkContext context;
         unsigned root_fd_index;
         unsigned kernel_fd_index;
+        char *esp_path;
+        char *xbootldr_path;
         sd_varlink *link;
 } LinkParameters;
 
@@ -1362,6 +1364,8 @@ static void link_parameters_done(LinkParameters *p) {
         assert(p);
 
         link_context_done(&p->context);
+        free(p->esp_path);
+        free(p->xbootldr_path);
 }
 
 typedef struct ExtraParameters {
@@ -1509,7 +1513,7 @@ static int vl_link_finish(sd_varlink *link, LinkParameters *p, bool with_ids) {
 
         r = find_xbootldr_and_warn_at(
                         p->context.root_fd,
-                        /* path= */ NULL,
+                        p->xbootldr_path,
                         /* unprivileged_mode= */ false,
                         &p->context.dollar_boot_path,
                         &p->context.dollar_boot_fd);
@@ -1521,7 +1525,7 @@ static int vl_link_finish(sd_varlink *link, LinkParameters *p, bool with_ids) {
 
                 r = find_esp_and_warn_at(
                                 p->context.root_fd,
-                                /* path= */ NULL,
+                                p->esp_path,
                                 /* unprivileged_mode= */ false,
                                 &p->context.dollar_boot_path,
                                 &p->context.dollar_boot_fd);
@@ -1566,6 +1570,8 @@ int vl_method_link(
         static const sd_json_dispatch_field dispatch_table[] = {
                 { "rootFileDescriptor",   _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint,                        voffsetof(p, root_fd_index),            0                 },
                 { "rootDirectory",        SD_JSON_VARIANT_STRING,        json_dispatch_path,                           voffsetof(p, context.root),             0                 },
+                { "espPath",              SD_JSON_VARIANT_STRING,        json_dispatch_path,                           voffsetof(p, esp_path),                 0                 },
+                { "xbootldrPath",         SD_JSON_VARIANT_STRING,        json_dispatch_path,                           voffsetof(p, xbootldr_path),            0                 },
                 { "bootEntryTokenType",   SD_JSON_VARIANT_STRING,        json_dispatch_boot_entry_token_type,          voffsetof(p, context.entry_token_type), 0                 },
                 { "entryTitle",           SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,                      voffsetof(p, context.entry_title),      0                 },
                 { "entryVersion",         SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,                      voffsetof(p, context.entry_version),    0                 },
@@ -1634,6 +1640,8 @@ int vl_method_link_auto(
         static const sd_json_dispatch_field dispatch_table[] = {
                 { "rootFileDescriptor", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint,               voffsetof(p, root_fd_index),            0 },
                 { "rootDirectory",      SD_JSON_VARIANT_STRING,        json_dispatch_path,                  voffsetof(p, context.root),             0 },
+                { "espPath",            SD_JSON_VARIANT_STRING,        json_dispatch_path,                  voffsetof(p, esp_path),                 0 },
+                { "xbootldrPath",       SD_JSON_VARIANT_STRING,        json_dispatch_path,                  voffsetof(p, xbootldr_path),            0 },
                 { "bootEntryTokenType", SD_JSON_VARIANT_STRING,        json_dispatch_boot_entry_token_type, voffsetof(p, context.entry_token_type), 0 },
                 { "entryTitle",         SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,             voffsetof(p, context.entry_title),      0 },
                 { "entryVersion",       SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,             voffsetof(p, context.entry_version),    0 },
index 44954d4fa331d33e4ea3956b81dbbb242db37f0b..dabe698bc7c4805aa189a71d6839a38e98cb82c9 100644 (file)
@@ -550,6 +550,8 @@ static JSON_DISPATCH_ENUM_DEFINE(json_dispatch_boot_entry_token_type, BootEntryT
 typedef struct UnlinkParameters {
         UnlinkContext context;
         unsigned root_fd_index;
+        char *esp_path;
+        char *xbootldr_path;
         const char *id;
         bool oldest;
 } UnlinkParameters;
@@ -558,6 +560,8 @@ static void unlink_parameters_done(UnlinkParameters *p) {
         assert(p);
 
         unlink_context_done(&p->context);
+        free(p->esp_path);
+        free(p->xbootldr_path);
 }
 
 int vl_method_unlink(
@@ -578,6 +582,8 @@ int vl_method_unlink(
         static const sd_json_dispatch_field dispatch_table[] = {
                 { "rootFileDescriptor",   _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint,               voffsetof(p, root_fd_index),            0 },
                 { "rootDirectory",        SD_JSON_VARIANT_STRING,        json_dispatch_path,                  voffsetof(p, context.root),             0 },
+                { "espPath",              SD_JSON_VARIANT_STRING,        json_dispatch_path,                  voffsetof(p, esp_path),                 0 },
+                { "xbootldrPath",         SD_JSON_VARIANT_STRING,        json_dispatch_path,                  voffsetof(p, xbootldr_path),            0 },
                 { "bootEntryTokenType",   SD_JSON_VARIANT_STRING,        json_dispatch_boot_entry_token_type, voffsetof(p, context.entry_token_type), 0 },
                 { "id",                   SD_JSON_VARIANT_STRING,        sd_json_dispatch_const_string,       voffsetof(p, id),                       0 },
                 { "oldest",               SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_stdbool,            voffsetof(p, oldest),                   0 },
@@ -631,7 +637,7 @@ int vl_method_unlink(
 
         r = find_esp_and_warn_at_full(
                         p.context.root_fd,
-                        /* path= */ NULL,
+                        p.esp_path,
                         /* unprivileged_mode= */ false,
                         &p.context.esp_path,
                         &p.context.esp_fd,
@@ -644,7 +650,7 @@ int vl_method_unlink(
                 return r;
         r = find_xbootldr_and_warn_at_full(
                         p.context.root_fd,
-                        /* path= */ NULL,
+                        p.xbootldr_path,
                         /* unprivileged_mode= */ false,
                         &p.context.xbootldr_path,
                         &p.context.xbootldr_fd,
index 0be93990d9aaa7a37a288f5403cceb6658d225fd..8df9e335e5bd0b5f7501c0a96ef3912a038e7fbd 100644 (file)
@@ -131,6 +131,10 @@ static SD_VARLINK_DEFINE_METHOD(
                 SD_VARLINK_DEFINE_INPUT(rootFileDescriptor, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
                 SD_VARLINK_FIELD_COMMENT("Root directory to operate relative to. If both this and rootFileDescriptor is specified, this is purely informational. If only this is specified, it is what will be used."),
                 SD_VARLINK_DEFINE_INPUT(rootDirectory, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("Path to the EFI System Partition mount point, resolved relative to rootDirectory/rootFileDescriptor if either is set. If not specified the ESP is auto-discovered."),
+                SD_VARLINK_DEFINE_INPUT(espPath, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("Path to the XBOOTLDR partition mount point, resolved relative to rootDirectory/rootFileDescriptor if either is set. If not specified the XBOOTLDR partition is auto-discovered."),
+                SD_VARLINK_DEFINE_INPUT(xbootldrPath, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
                 SD_VARLINK_FIELD_COMMENT("Selects how to identify boot entries"),
                 SD_VARLINK_DEFINE_INPUT_BY_TYPE(bootEntryTokenType, BootEntryTokenType, SD_VARLINK_NULLABLE),
                 SD_VARLINK_FIELD_COMMENT("If true the boot loader will be registered in an EFI boot entry via EFI variables, otherwise this is omitted"),
@@ -142,6 +146,10 @@ static SD_VARLINK_DEFINE_METHOD(
                 SD_VARLINK_DEFINE_INPUT(rootFileDescriptor, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
                 SD_VARLINK_FIELD_COMMENT("Root directory to operate relative to. If both this and rootFileDescriptor is specified, this is purely informational. If only this is specified, it is what will be used."),
                 SD_VARLINK_DEFINE_INPUT(rootDirectory, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("Path to the EFI System Partition mount point, resolved relative to rootDirectory/rootFileDescriptor if either is set. If not specified the ESP is auto-discovered."),
+                SD_VARLINK_DEFINE_INPUT(espPath, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("Path to the XBOOTLDR partition mount point, resolved relative to rootDirectory/rootFileDescriptor if either is set. If not specified the XBOOTLDR partition is auto-discovered."),
+                SD_VARLINK_DEFINE_INPUT(xbootldrPath, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
                 SD_VARLINK_FIELD_COMMENT("Selects how to identify boot entries"),
                 SD_VARLINK_DEFINE_INPUT_BY_TYPE(bootEntryTokenType, BootEntryTokenType, SD_VARLINK_NULLABLE),
                 SD_VARLINK_FIELD_COMMENT("The ID of the boot loader entry to remove."),
@@ -164,6 +172,10 @@ static SD_VARLINK_DEFINE_METHOD(
                 SD_VARLINK_DEFINE_INPUT(rootFileDescriptor, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
                 SD_VARLINK_FIELD_COMMENT("Root directory to operate relative to. If both this and rootFileDescriptor is specified, this is purely informational. If only this is specified, it is what will be used."),
                 SD_VARLINK_DEFINE_INPUT(rootDirectory, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("Path to the EFI System Partition mount point, resolved relative to rootDirectory/rootFileDescriptor if either is set. If not specified the ESP is auto-discovered."),
+                SD_VARLINK_DEFINE_INPUT(espPath, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("Path to the XBOOTLDR partition mount point, resolved relative to rootDirectory/rootFileDescriptor if either is set. If not specified the XBOOTLDR partition is auto-discovered."),
+                SD_VARLINK_DEFINE_INPUT(xbootldrPath, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
                 SD_VARLINK_FIELD_COMMENT("Selects how to identify boot entries"),
                 SD_VARLINK_DEFINE_INPUT_BY_TYPE(bootEntryTokenType, BootEntryTokenType, SD_VARLINK_NULLABLE),
                 SD_VARLINK_FIELD_COMMENT("The entry title for the newly created boot menu entry"),
@@ -191,6 +203,10 @@ static SD_VARLINK_DEFINE_METHOD(
                 SD_VARLINK_DEFINE_INPUT(rootFileDescriptor, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
                 SD_VARLINK_FIELD_COMMENT("Root directory to operate relative to. If both this and rootFileDescriptor is specified, this is purely informational. If only this is specified, it is what will be used."),
                 SD_VARLINK_DEFINE_INPUT(rootDirectory, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("Path to the EFI System Partition mount point, resolved relative to rootDirectory/rootFileDescriptor if either is set. If not specified the ESP is auto-discovered."),
+                SD_VARLINK_DEFINE_INPUT(espPath, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("Path to the XBOOTLDR partition mount point, resolved relative to rootDirectory/rootFileDescriptor if either is set. If not specified the XBOOTLDR partition is auto-discovered."),
+                SD_VARLINK_DEFINE_INPUT(xbootldrPath, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
                 SD_VARLINK_FIELD_COMMENT("Selects how to identify boot entries"),
                 SD_VARLINK_DEFINE_INPUT_BY_TYPE(bootEntryTokenType, BootEntryTokenType, SD_VARLINK_NULLABLE),
                 SD_VARLINK_FIELD_COMMENT("The entry title for the newly created boot menu entry"),
index 0b19acd0173cc131168d19b9b6a6111e55d6b07e..99fa39da40bd87391b14a087a0cbd837fe53151d 100755 (executable)
@@ -425,18 +425,43 @@ remove_root_dir() {
     rm -rf "$ROOTDIR"
 }
 
+cleanup_install_varlink() {
+    if [[ -n "${FAKE_ESP:-}" ]]; then
+        rm -rf "$FAKE_ESP"
+        unset FAKE_ESP
+    fi
+    if [[ -n "${FAKE_BOOT:-}" ]]; then
+        rm -rf "$FAKE_BOOT"
+        unset FAKE_BOOT
+    fi
+    restore_esp
+}
+
 testcase_install_varlink() {
 
     varlinkctl introspect "$(type -p bootctl)"
 
     if [ $# -eq 0 ]; then
         backup_esp
-        trap restore_esp RETURN ERR
+        trap cleanup_install_varlink RETURN ERR
     fi
 
     (! bootctl is-installed )
     SYSTEMD_LOG_TARGET=console varlinkctl call "$(type -p bootctl)" io.systemd.BootControl.Install "{\"operation\":\"new\",\"touchVariables\":false}"
     bootctl is-installed
+
+    # Verify that espPath/xbootldrPath override auto-discovery: install into fresh empty
+    # directories (with relaxed checks so verify_esp()/verify_xbootldr() accept a non-vfat
+    # non-mountpoint path) and check the loader files land there. If the parameters were ignored
+    # the call would auto-discover the real partitions instead and the directories would stay
+    # empty.
+    FAKE_ESP="$(mktemp --directory /tmp/test-bootctl-esp.XXXXXXXXXX)"
+    FAKE_BOOT="$(mktemp --directory /tmp/test-bootctl-boot.XXXXXXXXXX)"
+    SYSTEMD_RELAX_ESP_CHECKS=yes SYSTEMD_RELAX_XBOOTLDR_CHECKS=yes SYSTEMD_LOG_TARGET=console \
+            varlinkctl call --quiet "$(type -p bootctl)" io.systemd.BootControl.Install \
+            "{\"operation\":\"new\",\"touchVariables\":false,\"espPath\":\"$FAKE_ESP\",\"xbootldrPath\":\"$FAKE_BOOT\"}"
+    test -f "$FAKE_ESP/EFI/systemd/systemd-boot$(bootctl --print-efi-architecture).efi"
+    test -f "$FAKE_BOOT/loader/entries.srel"
 }
 
 cleanup_link() {