1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
4 #include "bootctl-install.h"
5 #include "bootctl-random-seed.h"
6 #include "bootctl-util.h"
9 #include "dirent-util.h"
15 #include "glyph-util.h"
16 #include "id128-util.h"
18 #include "path-util.h"
20 #include "stat-util.h"
21 #include "sync-util.h"
22 #include "tmpfile-util.h"
23 #include "umask-util.h"
26 static int load_etc_machine_id(void) {
29 r
= sd_id128_get_machine(&arg_machine_id
);
30 if (ERRNO_IS_NEG_MACHINE_ID_UNSET(r
)) /* Not set or empty */
33 return log_error_errno(r
, "Failed to get machine-id: %m");
35 log_debug("Loaded machine ID %s from /etc/machine-id.", SD_ID128_TO_STRING(arg_machine_id
));
39 static int load_etc_machine_info(void) {
40 /* systemd v250 added support to store the kernel-install layout setting and the machine ID to use
41 * for setting up the ESP in /etc/machine-info. The newer /etc/kernel/entry-token file, as well as
42 * the $layout field in /etc/kernel/install.conf are better replacements for this though, hence this
43 * has been deprecated and is only returned for compatibility. */
44 _cleanup_free_
char *p
= NULL
, *s
= NULL
, *layout
= NULL
;
47 p
= path_join(arg_root
, "etc/machine-info");
51 r
= parse_env_file(NULL
, p
,
52 "KERNEL_INSTALL_LAYOUT", &layout
,
53 "KERNEL_INSTALL_MACHINE_ID", &s
);
57 return log_error_errno(r
, "Failed to parse /etc/machine-info: %m");
61 log_notice("Read $KERNEL_INSTALL_MACHINE_ID from /etc/machine-info. "
62 "Please move it to /etc/kernel/entry-token.");
64 r
= sd_id128_from_string(s
, &arg_machine_id
);
66 return log_error_errno(r
, "Failed to parse KERNEL_INSTALL_MACHINE_ID=%s in /etc/machine-info: %m", s
);
68 log_debug("Loaded KERNEL_INSTALL_MACHINE_ID=%s from /etc/machine-info.",
69 SD_ID128_TO_STRING(arg_machine_id
));
72 if (!isempty(layout
)) {
74 log_notice("Read $KERNEL_INSTALL_LAYOUT from /etc/machine-info. "
75 "Please move it to the layout= setting of /etc/kernel/install.conf.");
77 log_debug("KERNEL_INSTALL_LAYOUT=%s is specified in /etc/machine-info.", layout
);
78 free_and_replace(arg_install_layout
, layout
);
84 static int load_etc_kernel_install_conf(void) {
85 _cleanup_free_
char *layout
= NULL
, *p
= NULL
;
88 p
= path_join(arg_root
, etc_kernel(), "install.conf");
92 r
= parse_env_file(NULL
, p
, "layout", &layout
);
96 return log_error_errno(r
, "Failed to parse %s: %m", p
);
98 if (!isempty(layout
)) {
99 log_debug("layout=%s is specified in %s.", layout
, p
);
100 free_and_replace(arg_install_layout
, layout
);
106 static bool use_boot_loader_spec_type1(void) {
107 /* If the layout is not specified, or if it is set explicitly to "bls" we assume Boot Loader
108 * Specification Type #1 is the chosen format for our boot loader entries */
109 return !arg_install_layout
|| streq(arg_install_layout
, "bls");
112 static int settle_make_entry_directory(void) {
115 r
= load_etc_machine_id();
119 r
= load_etc_machine_info();
123 r
= load_etc_kernel_install_conf();
127 r
= settle_entry_token();
131 bool layout_type1
= use_boot_loader_spec_type1();
132 if (arg_make_entry_directory
< 0) { /* Automatic mode */
134 if (arg_entry_token_type
== BOOT_ENTRY_TOKEN_MACHINE_ID
) {
135 r
= path_is_temporary_fs("/etc/machine-id");
137 return log_debug_errno(r
, "Couldn't determine whether /etc/machine-id is on a temporary file system: %m");
139 arg_make_entry_directory
= r
== 0;
141 arg_make_entry_directory
= true;
143 arg_make_entry_directory
= false;
146 if (arg_make_entry_directory
> 0 && !layout_type1
)
147 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
148 "KERNEL_INSTALL_LAYOUT=%s is configured, but Boot Loader Specification Type #1 entry directory creation was requested.",
154 static int compare_product(const char *a
, const char *b
) {
163 return x
< y
? -1 : x
> y
? 1 : 0;
165 return strncmp(a
, b
, x
);
168 static int compare_version(const char *a
, const char *b
) {
172 a
+= strcspn(a
, " ");
174 b
+= strcspn(b
, " ");
177 return strverscmp_improved(a
, b
);
180 static int version_check(int fd_from
, const char *from
, int fd_to
, const char *to
) {
181 _cleanup_free_
char *a
= NULL
, *b
= NULL
;
184 assert(fd_from
>= 0);
189 r
= get_file_version(fd_from
, &a
);
191 return log_notice_errno(r
, "Source file \"%s\" does not carry version information!", from
);
195 r
= get_file_version(fd_to
, &b
);
197 return log_notice_errno(r
, "Skipping \"%s\", it's owned by another boot loader (no version info found).",
201 if (compare_product(a
, b
) != 0)
202 return log_notice_errno(SYNTHETIC_ERRNO(ESRCH
),
203 "Skipping \"%s\", it's owned by another boot loader.", to
);
205 r
= compare_version(a
, b
);
206 log_debug("Comparing versions: \"%s\" %s \"%s", a
, comparison_operator(r
), b
);
208 return log_warning_errno(SYNTHETIC_ERRNO(ESTALE
),
209 "Skipping \"%s\", newer boot loader version in place already.", to
);
211 return log_info_errno(SYNTHETIC_ERRNO(ESTALE
),
212 "Skipping \"%s\", same boot loader version in place already.", to
);
217 static int copy_file_with_version_check(const char *from
, const char *to
, bool force
) {
218 _cleanup_close_
int fd_from
= -EBADF
, fd_to
= -EBADF
;
219 _cleanup_free_
char *t
= NULL
;
222 fd_from
= open(from
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
224 return log_error_errno(errno
, "Failed to open \"%s\" for reading: %m", from
);
227 fd_to
= open(to
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
230 return log_error_errno(errno
, "Failed to open \"%s\" for reading: %m", to
);
232 r
= version_check(fd_from
, from
, fd_to
, to
);
236 if (lseek(fd_from
, 0, SEEK_SET
) < 0)
237 return log_error_errno(errno
, "Failed to seek in \"%s\": %m", from
);
239 fd_to
= safe_close(fd_to
);
243 r
= tempfn_random(to
, NULL
, &t
);
248 fd_to
= open(t
, O_WRONLY
|O_CREAT
|O_CLOEXEC
|O_EXCL
|O_NOFOLLOW
, 0644);
250 return log_error_errno(errno
, "Failed to open \"%s\" for writing: %m", t
);
253 r
= copy_bytes(fd_from
, fd_to
, UINT64_MAX
, COPY_REFLINK
);
256 return log_error_errno(r
, "Failed to copy data from \"%s\" to \"%s\": %m", from
, t
);
259 (void) copy_times(fd_from
, fd_to
, 0);
261 r
= fsync_full(fd_to
);
264 return log_error_errno(r
, "Failed to copy data from \"%s\" to \"%s\": %m", from
, t
);
267 r
= RET_NERRNO(renameat(AT_FDCWD
, t
, AT_FDCWD
, to
));
270 return log_error_errno(r
, "Failed to rename \"%s\" to \"%s\": %m", t
, to
);
273 log_info("Copied \"%s\" to \"%s\".", from
, to
);
278 static int mkdir_one(const char *prefix
, const char *suffix
) {
279 _cleanup_free_
char *p
= NULL
;
281 p
= path_join(prefix
, suffix
);
282 if (mkdir(p
, 0700) < 0) {
284 return log_error_errno(errno
, "Failed to create \"%s\": %m", p
);
286 log_info("Created \"%s\".", p
);
291 static const char *const esp_subdirs
[] = {
292 /* The directories to place in the ESP */
300 static const char *const dollar_boot_subdirs
[] = {
301 /* The directories to place in the XBOOTLDR partition or the ESP, depending what exists */
303 "loader/entries", /* Type #1 entries */
305 "EFI/Linux", /* Type #2 entries */
309 static int create_subdirs(const char *root
, const char * const *subdirs
) {
312 STRV_FOREACH(i
, subdirs
) {
313 r
= mkdir_one(root
, *i
);
321 static int update_efi_boot_binaries(const char *esp_path
, const char *source_path
) {
322 _cleanup_closedir_
DIR *d
= NULL
;
323 _cleanup_free_
char *p
= NULL
;
326 r
= chase_and_opendir("/EFI/BOOT", esp_path
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
, &p
, &d
);
330 return log_error_errno(r
, "Failed to open directory \"%s/EFI/BOOT\": %m", esp_path
);
332 FOREACH_DIRENT(de
, d
, break) {
333 _cleanup_close_
int fd
= -EBADF
;
334 _cleanup_free_
char *v
= NULL
;
336 if (!endswith_no_case(de
->d_name
, ".efi"))
339 fd
= openat(dirfd(d
), de
->d_name
, O_RDONLY
|O_CLOEXEC
);
341 return log_error_errno(errno
, "Failed to open \"%s/%s\" for reading: %m", p
, de
->d_name
);
343 r
= get_file_version(fd
, &v
);
345 continue; /* No version information */
348 if (startswith(v
, "systemd-boot ")) {
349 _cleanup_free_
char *dest_path
= NULL
;
351 dest_path
= path_join(p
, de
->d_name
);
355 RET_GATHER(ret
, copy_file_with_version_check(source_path
, dest_path
, /* force = */ false));
362 static int copy_one_file(const char *esp_path
, const char *name
, bool force
) {
363 char *root
= IN_SET(arg_install_source
, ARG_INSTALL_SOURCE_AUTO
, ARG_INSTALL_SOURCE_IMAGE
) ? arg_root
: NULL
;
364 _cleanup_free_
char *source_path
= NULL
, *dest_path
= NULL
, *p
= NULL
, *q
= NULL
;
369 dest_name
= strdupa_safe(name
);
370 s
= endswith_no_case(dest_name
, ".signed");
374 p
= path_join(BOOTLIBDIR
, name
);
378 r
= chase(p
, root
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
, &source_path
, NULL
);
379 /* If we had a root directory to try, we didn't find it and we are in auto mode, retry on the host */
380 if (r
== -ENOENT
&& root
&& arg_install_source
== ARG_INSTALL_SOURCE_AUTO
)
381 r
= chase(p
, NULL
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
, &source_path
, NULL
);
383 return log_error_errno(r
,
384 "Failed to resolve path %s%s%s: %m",
386 root
? " under directory " : "",
389 q
= path_join("/EFI/systemd/", dest_name
);
393 r
= chase(q
, esp_path
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
|CHASE_NONEXISTENT
, &dest_path
, NULL
);
395 return log_error_errno(r
, "Failed to resolve path %s under directory %s: %m", q
, esp_path
);
397 /* Note that if this fails we do the second copy anyway, but return this error code,
398 * so we stash it away in a separate variable. */
399 ret
= copy_file_with_version_check(source_path
, dest_path
, force
);
401 e
= startswith(dest_name
, "systemd-boot");
403 _cleanup_free_
char *default_dest_path
= NULL
;
406 /* Create the EFI default boot loader name (specified for removable devices) */
407 v
= strjoina("/EFI/BOOT/BOOT", e
);
408 ascii_strupper(strrchr(v
, '/') + 1);
410 r
= chase(v
, esp_path
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
|CHASE_NONEXISTENT
, &default_dest_path
, NULL
);
412 return log_error_errno(r
, "Failed to resolve path %s under directory %s: %m", v
, esp_path
);
414 RET_GATHER(ret
, copy_file_with_version_check(source_path
, default_dest_path
, force
));
416 /* If we were installed under any other name in /EFI/BOOT, make sure we update those binaries
419 RET_GATHER(ret
, update_efi_boot_binaries(esp_path
, source_path
));
425 static int install_binaries(const char *esp_path
, const char *arch
, bool force
) {
426 char *root
= IN_SET(arg_install_source
, ARG_INSTALL_SOURCE_AUTO
, ARG_INSTALL_SOURCE_IMAGE
) ? arg_root
: NULL
;
427 _cleanup_closedir_
DIR *d
= NULL
;
428 _cleanup_free_
char *path
= NULL
;
431 r
= chase_and_opendir(BOOTLIBDIR
, root
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
, &path
, &d
);
432 /* If we had a root directory to try, we didn't find it and we are in auto mode, retry on the host */
433 if (r
== -ENOENT
&& root
&& arg_install_source
== ARG_INSTALL_SOURCE_AUTO
)
434 r
= chase_and_opendir(BOOTLIBDIR
, NULL
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
, &path
, &d
);
435 if (r
== -ENOENT
&& arg_graceful
) {
436 log_debug("Source directory does not exist, ignoring.");
440 return log_error_errno(r
, "Failed to open boot loader directory %s%s: %m", strempty(root
), BOOTLIBDIR
);
442 const char *suffix
= strjoina(arch
, ".efi");
443 const char *suffix_signed
= strjoina(arch
, ".efi.signed");
445 FOREACH_DIRENT(de
, d
, return log_error_errno(errno
, "Failed to read \"%s\": %m", path
)) {
448 if (!endswith_no_case(de
->d_name
, suffix
) && !endswith_no_case(de
->d_name
, suffix_signed
))
451 /* skip the .efi file, if there's a .signed version of it */
452 if (endswith_no_case(de
->d_name
, ".efi")) {
453 _cleanup_free_
const char *s
= strjoin(de
->d_name
, ".signed");
456 if (faccessat(dirfd(d
), s
, F_OK
, 0) >= 0)
460 k
= copy_one_file(esp_path
, de
->d_name
, force
);
461 /* Don't propagate an error code if no update necessary, installed version already equal or
462 * newer version, or other boot loader in place. */
463 if (arg_graceful
&& IN_SET(k
, -ESTALE
, -ESRCH
))
471 static int install_loader_config(const char *esp_path
) {
472 _cleanup_(unlink_and_freep
) char *t
= NULL
;
473 _cleanup_fclose_
FILE *f
= NULL
;
474 _cleanup_free_
char *p
= NULL
;
477 assert(arg_make_entry_directory
>= 0);
479 p
= path_join(esp_path
, "/loader/loader.conf");
482 if (access(p
, F_OK
) >= 0) /* Silently skip creation if the file already exists (early check) */
485 r
= fopen_tmpfile_linkable(p
, O_WRONLY
|O_CLOEXEC
, &t
, &f
);
487 return log_error_errno(r
, "Failed to open \"%s\" for writing: %m", p
);
489 fprintf(f
, "#timeout 3\n"
490 "#console-mode keep\n");
492 if (arg_make_entry_directory
) {
493 assert(arg_entry_token
);
494 fprintf(f
, "default %s-*\n", arg_entry_token
);
497 r
= flink_tmpfile(f
, t
, p
, LINK_TMPFILE_SYNC
);
499 return 0; /* Silently skip creation if the file exists now (recheck) */
501 return log_error_errno(r
, "Failed to move \"%s\" into place: %m", p
);
507 static int install_loader_specification(const char *root
) {
508 _cleanup_(unlink_and_freep
) char *t
= NULL
;
509 _cleanup_fclose_
FILE *f
= NULL
;
510 _cleanup_free_
char *p
= NULL
;
513 p
= path_join(root
, "/loader/entries.srel");
517 if (access(p
, F_OK
) >= 0) /* Silently skip creation if the file already exists (early check) */
520 r
= fopen_tmpfile_linkable(p
, O_WRONLY
|O_CLOEXEC
, &t
, &f
);
522 return log_error_errno(r
, "Failed to open \"%s\" for writing: %m", p
);
524 fprintf(f
, "type1\n");
526 r
= flink_tmpfile(f
, t
, p
, LINK_TMPFILE_SYNC
);
528 return 0; /* Silently skip creation if the file exists now (recheck) */
530 return log_error_errno(r
, "Failed to move \"%s\" into place: %m", p
);
536 static int install_entry_directory(const char *root
) {
538 assert(arg_make_entry_directory
>= 0);
540 if (!arg_make_entry_directory
)
543 assert(arg_entry_token
);
544 return mkdir_one(root
, arg_entry_token
);
547 static int install_entry_token(void) {
548 _cleanup_free_
char* p
= NULL
;
551 assert(arg_make_entry_directory
>= 0);
552 assert(arg_entry_token
);
554 /* Let's save the used entry token in /etc/kernel/entry-token if we used it to create the entry
555 * directory, or if anything else but the machine ID */
557 if (!arg_make_entry_directory
&& arg_entry_token_type
== BOOT_ENTRY_TOKEN_MACHINE_ID
)
560 p
= path_join(arg_root
, etc_kernel(), "entry-token");
564 r
= write_string_file(p
, arg_entry_token
, WRITE_STRING_FILE_CREATE
|WRITE_STRING_FILE_ATOMIC
|WRITE_STRING_FILE_MKDIR_0755
);
566 return log_error_errno(r
, "Failed to write entry token '%s' to %s: %m", arg_entry_token
, p
);
571 static bool same_entry(uint16_t id
, sd_id128_t uuid
, const char *path
) {
572 _cleanup_free_
char *opath
= NULL
;
576 r
= efi_get_boot_option(id
, NULL
, &ouuid
, &opath
, NULL
);
579 if (!sd_id128_equal(uuid
, ouuid
))
582 /* Some motherboards convert the path to uppercase under certain circumstances
583 * (e.g. after booting into the Boot Menu in the ASUS ROG STRIX B350-F GAMING),
584 * so use case-insensitive checking */
585 if (!strcaseeq_ptr(path
, opath
))
591 static int find_slot(sd_id128_t uuid
, const char *path
, uint16_t *id
) {
592 _cleanup_free_
uint16_t *options
= NULL
;
594 int n
= efi_get_boot_options(&options
);
598 /* find already existing systemd-boot entry */
599 for (int i
= 0; i
< n
; i
++)
600 if (same_entry(options
[i
], uuid
, path
)) {
605 /* find free slot in the sorted BootXXXX variable list */
606 for (int i
= 0; i
< n
; i
++)
607 if (i
!= options
[i
]) {
612 /* use the next one */
619 static int insert_into_order(uint16_t slot
, bool first
) {
620 _cleanup_free_
uint16_t *order
= NULL
;
624 n
= efi_get_boot_order(&order
);
626 /* no entry, add us */
627 return efi_set_boot_order(&slot
, 1);
629 /* are we the first and only one? */
630 if (n
== 1 && order
[0] == slot
)
633 /* are we already in the boot order? */
634 for (int i
= 0; i
< n
; i
++) {
635 if (order
[i
] != slot
)
638 /* we do not require to be the first one, all is fine */
642 /* move us to the first slot */
643 memmove(order
+ 1, order
, i
* sizeof(uint16_t));
645 return efi_set_boot_order(order
, n
);
649 t
= reallocarray(order
, n
+ 1, sizeof(uint16_t));
654 /* add us to the top or end of the list */
656 memmove(order
+ 1, order
, n
* sizeof(uint16_t));
661 return efi_set_boot_order(order
, n
+ 1);
664 static int remove_from_order(uint16_t slot
) {
665 _cleanup_free_
uint16_t *order
= NULL
;
668 n
= efi_get_boot_order(&order
);
672 for (int i
= 0; i
< n
; i
++) {
673 if (order
[i
] != slot
)
677 memmove(order
+ i
, order
+ i
+1, (n
- i
) * sizeof(uint16_t));
678 return efi_set_boot_order(order
, n
- 1);
684 static const char *pick_efi_boot_option_description(void) {
685 return arg_efi_boot_option_description
?: "Linux Boot Manager";
688 static int install_variables(
689 const char *esp_path
,
702 log_info("Acting on %s, skipping EFI variable setup.",
703 arg_image
? "image" : "root directory");
707 if (!is_efi_boot()) {
708 log_warning("Not booted with EFI, skipping EFI variable setup.");
712 r
= chase_and_access(path
, esp_path
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
, F_OK
, NULL
);
716 return log_error_errno(r
, "Cannot access \"%s/%s\": %m", esp_path
, path
);
718 r
= find_slot(uuid
, path
, &slot
);
720 int level
= graceful
? arg_quiet
? LOG_DEBUG
: LOG_INFO
: LOG_ERR
;
721 const char *skip
= graceful
? ", skipping" : "";
723 log_full_errno(level
, r
,
725 "Failed to access EFI variables%s. Is the \"efivarfs\" filesystem mounted?" :
726 "Failed to determine current boot order%s: %m", skip
);
728 return graceful
? 0 : r
;
731 if (first
|| r
== 0) {
732 r
= efi_add_boot_option(slot
, pick_efi_boot_option_description(),
736 int level
= graceful
? arg_quiet
? LOG_DEBUG
: LOG_INFO
: LOG_ERR
;
737 const char *skip
= graceful
? ", skipping" : "";
739 log_full_errno(level
, r
, "Failed to create EFI Boot variable entry%s: %m", skip
);
741 return graceful
? 0 : r
;
744 log_info("Created EFI boot entry \"%s\".", pick_efi_boot_option_description());
747 return insert_into_order(slot
, first
);
750 static int are_we_installed(const char *esp_path
) {
753 /* Tests whether systemd-boot is installed. It's not obvious what to use as check here: we could
754 * check EFI variables, we could check what binary /EFI/BOOT/BOOT*.EFI points to, or whether the
755 * loader entries directory exists. Here we opted to check whether /EFI/systemd/ is non-empty, which
756 * should be a suitable and very minimal check for a number of reasons:
758 * → The check is architecture independent (i.e. we check if any systemd-boot loader is installed,
759 * not a specific one.)
761 * → It doesn't assume we are the only boot loader (i.e doesn't check if we own the main
762 * /EFI/BOOT/BOOT*.EFI fallback binary.
764 * → It specifically checks for systemd-boot, not for other boot loaders (which a check for
765 * /boot/loader/entries would do). */
767 _cleanup_free_
char *p
= path_join(esp_path
, "/EFI/systemd/");
771 log_debug("Checking whether %s contains any files%s", p
, special_glyph(SPECIAL_GLYPH_ELLIPSIS
));
772 r
= dir_is_empty(p
, /* ignore_hidden_or_backup= */ false);
773 if (r
< 0 && r
!= -ENOENT
)
774 return log_error_errno(r
, "Failed to check whether %s contains any files: %m", p
);
779 int verb_install(int argc
, char *argv
[], void *userdata
) {
780 sd_id128_t uuid
= SD_ID128_NULL
;
781 uint64_t pstart
= 0, psize
= 0;
783 bool install
, graceful
;
786 /* Invoked for both "update" and "install" */
788 install
= streq(argv
[0], "install");
789 graceful
= !install
&& arg_graceful
; /* support graceful mode for updates */
791 r
= acquire_esp(/* unprivileged_mode= */ false, graceful
, &part
, &pstart
, &psize
, &uuid
, NULL
);
792 if (graceful
&& r
== -ENOKEY
)
793 return 0; /* If --graceful is specified and we can't find an ESP, handle this cleanly */
798 /* If we are updating, don't do anything if sd-boot wasn't actually installed. */
799 r
= are_we_installed(arg_esp_path
);
803 log_debug("Skipping update because sd-boot is not installed in the ESP.");
808 r
= acquire_xbootldr(/* unprivileged_mode= */ false, NULL
, NULL
);
812 r
= settle_make_entry_directory();
816 const char *arch
= arg_arch_all
? "" : get_efi_arch();
820 /* Don't create any of these directories when we are just updating. When we update
821 * we'll drop-in our files (unless there are newer ones already), but we won't create
822 * the directories for them in the first place. */
823 r
= create_subdirs(arg_esp_path
, esp_subdirs
);
827 r
= create_subdirs(arg_dollar_boot_path(), dollar_boot_subdirs
);
832 r
= install_binaries(arg_esp_path
, arch
, install
);
837 r
= install_loader_config(arg_esp_path
);
841 r
= install_entry_directory(arg_dollar_boot_path());
845 r
= install_entry_token();
849 r
= install_random_seed(arg_esp_path
);
854 r
= install_loader_specification(arg_dollar_boot_path());
859 (void) sync_everything();
861 if (!arg_touch_variables
)
865 log_info("Not changing EFI variables with --all-architectures.");
869 char *path
= strjoina("/EFI/systemd/systemd-boot", arch
, ".efi");
870 return install_variables(arg_esp_path
, part
, pstart
, psize
, uuid
, path
, install
, graceful
);
873 static int remove_boot_efi(const char *esp_path
) {
874 _cleanup_closedir_
DIR *d
= NULL
;
875 _cleanup_free_
char *p
= NULL
;
878 r
= chase_and_opendir("/EFI/BOOT", esp_path
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
, &p
, &d
);
882 return log_error_errno(r
, "Failed to open directory \"%s/EFI/BOOT\": %m", esp_path
);
884 FOREACH_DIRENT(de
, d
, break) {
885 _cleanup_close_
int fd
= -EBADF
;
886 _cleanup_free_
char *v
= NULL
;
888 if (!endswith_no_case(de
->d_name
, ".efi"))
891 fd
= openat(dirfd(d
), de
->d_name
, O_RDONLY
|O_CLOEXEC
);
893 return log_error_errno(errno
, "Failed to open \"%s/%s\" for reading: %m", p
, de
->d_name
);
895 r
= get_file_version(fd
, &v
);
897 continue; /* No version information */
900 if (startswith(v
, "systemd-boot ")) {
901 r
= unlinkat(dirfd(d
), de
->d_name
, 0);
903 return log_error_errno(errno
, "Failed to remove \"%s/%s\": %m", p
, de
->d_name
);
905 log_info("Removed \"%s/%s\".", p
, de
->d_name
);
914 static int rmdir_one(const char *prefix
, const char *suffix
) {
917 p
= prefix_roota(prefix
, suffix
);
919 bool ignore
= IN_SET(errno
, ENOENT
, ENOTEMPTY
);
921 log_full_errno(ignore
? LOG_DEBUG
: LOG_ERR
, errno
,
922 "Failed to remove directory \"%s\": %m", p
);
926 log_info("Removed \"%s\".", p
);
931 static int remove_subdirs(const char *root
, const char *const *subdirs
) {
934 /* We use recursion here to destroy the directories in reverse order. Which should be safe given how
935 * short the array is. */
937 if (!subdirs
[0]) /* A the end of the list */
940 r
= remove_subdirs(root
, subdirs
+ 1);
941 q
= rmdir_one(root
, subdirs
[0]);
943 return r
< 0 ? r
: q
;
946 static int remove_entry_directory(const char *root
) {
948 assert(arg_make_entry_directory
>= 0);
950 if (!arg_make_entry_directory
|| !arg_entry_token
)
953 return rmdir_one(root
, arg_entry_token
);
956 static int remove_binaries(const char *esp_path
) {
960 p
= prefix_roota(esp_path
, "/EFI/systemd");
961 r
= rm_rf(p
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
963 q
= remove_boot_efi(esp_path
);
970 static int remove_file(const char *root
, const char *file
) {
976 p
= prefix_roota(root
, file
);
978 log_full_errno(errno
== ENOENT
? LOG_DEBUG
: LOG_ERR
, errno
,
979 "Failed to unlink file \"%s\": %m", p
);
981 return errno
== ENOENT
? 0 : -errno
;
984 log_info("Removed \"%s\".", p
);
988 static int remove_variables(sd_id128_t uuid
, const char *path
, bool in_order
) {
992 if (arg_root
|| !is_efi_boot())
995 r
= find_slot(uuid
, path
, &slot
);
999 r
= efi_remove_boot_option(slot
);
1004 return remove_from_order(slot
);
1009 static int remove_loader_variables(void) {
1012 /* Remove all persistent loader variables we define */
1015 EFI_LOADER_VARIABLE(LoaderConfigConsoleMode
),
1016 EFI_LOADER_VARIABLE(LoaderConfigTimeout
),
1017 EFI_LOADER_VARIABLE(LoaderConfigTimeoutOneShot
),
1018 EFI_LOADER_VARIABLE(LoaderEntryDefault
),
1019 EFI_LOADER_VARIABLE(LoaderEntryLastBooted
),
1020 EFI_LOADER_VARIABLE(LoaderEntryOneShot
),
1021 EFI_LOADER_VARIABLE(LoaderSystemToken
)){
1025 q
= efi_set_variable(var
, NULL
, 0);
1029 log_warning_errno(q
, "Failed to remove EFI variable %s: %m", var
);
1033 log_info("Removed EFI variable %s.", var
);
1039 int verb_remove(int argc
, char *argv
[], void *userdata
) {
1040 sd_id128_t uuid
= SD_ID128_NULL
;
1043 r
= acquire_esp(/* unprivileged_mode= */ false, /* graceful= */ false, NULL
, NULL
, NULL
, &uuid
, NULL
);
1047 r
= acquire_xbootldr(/* unprivileged_mode= */ false, NULL
, NULL
);
1051 r
= settle_make_entry_directory();
1055 r
= remove_binaries(arg_esp_path
);
1057 q
= remove_file(arg_esp_path
, "/loader/loader.conf");
1058 if (q
< 0 && r
>= 0)
1061 q
= remove_file(arg_esp_path
, "/loader/random-seed");
1062 if (q
< 0 && r
>= 0)
1065 q
= remove_file(arg_esp_path
, "/loader/entries.srel");
1066 if (q
< 0 && r
>= 0)
1069 q
= remove_subdirs(arg_esp_path
, esp_subdirs
);
1070 if (q
< 0 && r
>= 0)
1073 q
= remove_subdirs(arg_esp_path
, dollar_boot_subdirs
);
1074 if (q
< 0 && r
>= 0)
1077 q
= remove_entry_directory(arg_esp_path
);
1078 if (q
< 0 && r
>= 0)
1081 if (arg_xbootldr_path
) {
1082 /* Remove a subset of these also from the XBOOTLDR partition if it exists */
1084 q
= remove_file(arg_xbootldr_path
, "/loader/entries.srel");
1085 if (q
< 0 && r
>= 0)
1088 q
= remove_subdirs(arg_xbootldr_path
, dollar_boot_subdirs
);
1089 if (q
< 0 && r
>= 0)
1092 q
= remove_entry_directory(arg_xbootldr_path
);
1093 if (q
< 0 && r
>= 0)
1097 (void) sync_everything();
1099 if (!arg_touch_variables
)
1103 log_info("Not changing EFI variables with --all-architectures.");
1107 char *path
= strjoina("/EFI/systemd/systemd-boot", get_efi_arch(), ".efi");
1108 q
= remove_variables(uuid
, path
, true);
1109 if (q
< 0 && r
>= 0)
1112 q
= remove_loader_variables();
1113 if (q
< 0 && r
>= 0)
1119 int verb_is_installed(int argc
, char *argv
[], void *userdata
) {
1122 r
= acquire_esp(/* unprivileged_mode= */ false,
1123 /* graceful= */ arg_graceful
,
1124 NULL
, NULL
, NULL
, NULL
, NULL
);
1128 r
= are_we_installed(arg_esp_path
);
1135 return EXIT_SUCCESS
;
1139 return EXIT_FAILURE
;