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
);
322 static int copy_one_file(const char *esp_path
, const char *name
, bool force
) {
323 char *root
= IN_SET(arg_install_source
, ARG_INSTALL_SOURCE_AUTO
, ARG_INSTALL_SOURCE_IMAGE
) ? arg_root
: NULL
;
324 _cleanup_free_
char *source_path
= NULL
, *dest_path
= NULL
, *p
= NULL
, *q
= NULL
;
329 dest_name
= strdupa_safe(name
);
330 s
= endswith_no_case(dest_name
, ".signed");
334 p
= path_join(BOOTLIBDIR
, name
);
338 r
= chase(p
, root
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
, &source_path
, NULL
);
339 /* If we had a root directory to try, we didn't find it and we are in auto mode, retry on the host */
340 if (r
== -ENOENT
&& root
&& arg_install_source
== ARG_INSTALL_SOURCE_AUTO
)
341 r
= chase(p
, NULL
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
, &source_path
, NULL
);
343 return log_error_errno(r
,
344 "Failed to resolve path %s%s%s: %m",
346 root
? " under directory " : "",
349 q
= path_join("/EFI/systemd/", dest_name
);
353 r
= chase(q
, esp_path
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
|CHASE_NONEXISTENT
, &dest_path
, NULL
);
355 return log_error_errno(r
, "Failed to resolve path %s under directory %s: %m", q
, esp_path
);
357 /* Note that if this fails we do the second copy anyway, but return this error code,
358 * so we stash it away in a separate variable. */
359 ret
= copy_file_with_version_check(source_path
, dest_path
, force
);
361 e
= startswith(dest_name
, "systemd-boot");
363 _cleanup_free_
char *default_dest_path
= NULL
;
366 /* Create the EFI default boot loader name (specified for removable devices) */
367 v
= strjoina("/EFI/BOOT/BOOT", e
);
368 ascii_strupper(strrchr(v
, '/') + 1);
370 r
= chase(v
, esp_path
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
|CHASE_NONEXISTENT
, &default_dest_path
, NULL
);
372 return log_error_errno(r
, "Failed to resolve path %s under directory %s: %m", v
, esp_path
);
374 r
= copy_file_with_version_check(source_path
, default_dest_path
, force
);
375 if (r
< 0 && ret
== 0)
382 static int install_binaries(const char *esp_path
, const char *arch
, bool force
) {
383 char *root
= IN_SET(arg_install_source
, ARG_INSTALL_SOURCE_AUTO
, ARG_INSTALL_SOURCE_IMAGE
) ? arg_root
: NULL
;
384 _cleanup_closedir_
DIR *d
= NULL
;
385 _cleanup_free_
char *path
= NULL
;
388 r
= chase_and_opendir(BOOTLIBDIR
, root
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
, &path
, &d
);
389 /* If we had a root directory to try, we didn't find it and we are in auto mode, retry on the host */
390 if (r
== -ENOENT
&& root
&& arg_install_source
== ARG_INSTALL_SOURCE_AUTO
)
391 r
= chase_and_opendir(BOOTLIBDIR
, NULL
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
, &path
, &d
);
392 if (r
== -ENOENT
&& arg_graceful
) {
393 log_debug("Source directory does not exist, ignoring.");
397 return log_error_errno(r
, "Failed to open boot loader directory %s%s: %m", strempty(root
), BOOTLIBDIR
);
399 const char *suffix
= strjoina(arch
, ".efi");
400 const char *suffix_signed
= strjoina(arch
, ".efi.signed");
402 FOREACH_DIRENT(de
, d
, return log_error_errno(errno
, "Failed to read \"%s\": %m", path
)) {
405 if (!endswith_no_case(de
->d_name
, suffix
) && !endswith_no_case(de
->d_name
, suffix_signed
))
408 /* skip the .efi file, if there's a .signed version of it */
409 if (endswith_no_case(de
->d_name
, ".efi")) {
410 _cleanup_free_
const char *s
= strjoin(de
->d_name
, ".signed");
413 if (faccessat(dirfd(d
), s
, F_OK
, 0) >= 0)
417 k
= copy_one_file(esp_path
, de
->d_name
, force
);
418 /* Don't propagate an error code if no update necessary, installed version already equal or
419 * newer version, or other boot loader in place. */
420 if (arg_graceful
&& IN_SET(k
, -ESTALE
, -ESRCH
))
428 static int install_loader_config(const char *esp_path
) {
429 _cleanup_(unlink_and_freep
) char *t
= NULL
;
430 _cleanup_fclose_
FILE *f
= NULL
;
431 _cleanup_free_
char *p
= NULL
;
434 assert(arg_make_entry_directory
>= 0);
436 p
= path_join(esp_path
, "/loader/loader.conf");
439 if (access(p
, F_OK
) >= 0) /* Silently skip creation if the file already exists (early check) */
442 r
= fopen_tmpfile_linkable(p
, O_WRONLY
|O_CLOEXEC
, &t
, &f
);
444 return log_error_errno(r
, "Failed to open \"%s\" for writing: %m", p
);
446 fprintf(f
, "#timeout 3\n"
447 "#console-mode keep\n");
449 if (arg_make_entry_directory
) {
450 assert(arg_entry_token
);
451 fprintf(f
, "default %s-*\n", arg_entry_token
);
454 r
= flink_tmpfile(f
, t
, p
, LINK_TMPFILE_SYNC
);
456 return 0; /* Silently skip creation if the file exists now (recheck) */
458 return log_error_errno(r
, "Failed to move \"%s\" into place: %m", p
);
464 static int install_loader_specification(const char *root
) {
465 _cleanup_(unlink_and_freep
) char *t
= NULL
;
466 _cleanup_fclose_
FILE *f
= NULL
;
467 _cleanup_free_
char *p
= NULL
;
470 p
= path_join(root
, "/loader/entries.srel");
474 if (access(p
, F_OK
) >= 0) /* Silently skip creation if the file already exists (early check) */
477 r
= fopen_tmpfile_linkable(p
, O_WRONLY
|O_CLOEXEC
, &t
, &f
);
479 return log_error_errno(r
, "Failed to open \"%s\" for writing: %m", p
);
481 fprintf(f
, "type1\n");
483 r
= flink_tmpfile(f
, t
, p
, LINK_TMPFILE_SYNC
);
485 return 0; /* Silently skip creation if the file exists now (recheck) */
487 return log_error_errno(r
, "Failed to move \"%s\" into place: %m", p
);
493 static int install_entry_directory(const char *root
) {
495 assert(arg_make_entry_directory
>= 0);
497 if (!arg_make_entry_directory
)
500 assert(arg_entry_token
);
501 return mkdir_one(root
, arg_entry_token
);
504 static int install_entry_token(void) {
505 _cleanup_free_
char* p
= NULL
;
508 assert(arg_make_entry_directory
>= 0);
509 assert(arg_entry_token
);
511 /* Let's save the used entry token in /etc/kernel/entry-token if we used it to create the entry
512 * directory, or if anything else but the machine ID */
514 if (!arg_make_entry_directory
&& arg_entry_token_type
== BOOT_ENTRY_TOKEN_MACHINE_ID
)
517 p
= path_join(arg_root
, etc_kernel(), "entry-token");
521 r
= write_string_file(p
, arg_entry_token
, WRITE_STRING_FILE_CREATE
|WRITE_STRING_FILE_ATOMIC
|WRITE_STRING_FILE_MKDIR_0755
);
523 return log_error_errno(r
, "Failed to write entry token '%s' to %s: %m", arg_entry_token
, p
);
528 static bool same_entry(uint16_t id
, sd_id128_t uuid
, const char *path
) {
529 _cleanup_free_
char *opath
= NULL
;
533 r
= efi_get_boot_option(id
, NULL
, &ouuid
, &opath
, NULL
);
536 if (!sd_id128_equal(uuid
, ouuid
))
539 /* Some motherboards convert the path to uppercase under certain circumstances
540 * (e.g. after booting into the Boot Menu in the ASUS ROG STRIX B350-F GAMING),
541 * so use case-insensitive checking */
542 if (!strcaseeq_ptr(path
, opath
))
548 static int find_slot(sd_id128_t uuid
, const char *path
, uint16_t *id
) {
549 _cleanup_free_
uint16_t *options
= NULL
;
551 int n
= efi_get_boot_options(&options
);
555 /* find already existing systemd-boot entry */
556 for (int i
= 0; i
< n
; i
++)
557 if (same_entry(options
[i
], uuid
, path
)) {
562 /* find free slot in the sorted BootXXXX variable list */
563 for (int i
= 0; i
< n
; i
++)
564 if (i
!= options
[i
]) {
569 /* use the next one */
576 static int insert_into_order(uint16_t slot
, bool first
) {
577 _cleanup_free_
uint16_t *order
= NULL
;
581 n
= efi_get_boot_order(&order
);
583 /* no entry, add us */
584 return efi_set_boot_order(&slot
, 1);
586 /* are we the first and only one? */
587 if (n
== 1 && order
[0] == slot
)
590 /* are we already in the boot order? */
591 for (int i
= 0; i
< n
; i
++) {
592 if (order
[i
] != slot
)
595 /* we do not require to be the first one, all is fine */
599 /* move us to the first slot */
600 memmove(order
+ 1, order
, i
* sizeof(uint16_t));
602 return efi_set_boot_order(order
, n
);
606 t
= reallocarray(order
, n
+ 1, sizeof(uint16_t));
611 /* add us to the top or end of the list */
613 memmove(order
+ 1, order
, n
* sizeof(uint16_t));
618 return efi_set_boot_order(order
, n
+ 1);
621 static int remove_from_order(uint16_t slot
) {
622 _cleanup_free_
uint16_t *order
= NULL
;
625 n
= efi_get_boot_order(&order
);
629 for (int i
= 0; i
< n
; i
++) {
630 if (order
[i
] != slot
)
634 memmove(order
+ i
, order
+ i
+1, (n
- i
) * sizeof(uint16_t));
635 return efi_set_boot_order(order
, n
- 1);
641 static const char *pick_efi_boot_option_description(void) {
642 return arg_efi_boot_option_description
?: "Linux Boot Manager";
645 static int install_variables(
646 const char *esp_path
,
659 log_info("Acting on %s, skipping EFI variable setup.",
660 arg_image
? "image" : "root directory");
664 if (!is_efi_boot()) {
665 log_warning("Not booted with EFI, skipping EFI variable setup.");
669 r
= chase_and_access(path
, esp_path
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
, F_OK
, NULL
);
673 return log_error_errno(r
, "Cannot access \"%s/%s\": %m", esp_path
, path
);
675 r
= find_slot(uuid
, path
, &slot
);
677 int level
= graceful
? arg_quiet
? LOG_DEBUG
: LOG_INFO
: LOG_ERR
;
678 const char *skip
= graceful
? ", skipping" : "";
680 log_full_errno(level
, r
,
682 "Failed to access EFI variables%s. Is the \"efivarfs\" filesystem mounted?" :
683 "Failed to determine current boot order%s: %m", skip
);
685 return graceful
? 0 : r
;
688 if (first
|| r
== 0) {
689 r
= efi_add_boot_option(slot
, pick_efi_boot_option_description(),
693 int level
= graceful
? arg_quiet
? LOG_DEBUG
: LOG_INFO
: LOG_ERR
;
694 const char *skip
= graceful
? ", skipping" : "";
696 log_full_errno(level
, r
, "Failed to create EFI Boot variable entry%s: %m", skip
);
698 return graceful
? 0 : r
;
701 log_info("Created EFI boot entry \"%s\".", pick_efi_boot_option_description());
704 return insert_into_order(slot
, first
);
707 static int are_we_installed(const char *esp_path
) {
710 /* Tests whether systemd-boot is installed. It's not obvious what to use as check here: we could
711 * check EFI variables, we could check what binary /EFI/BOOT/BOOT*.EFI points to, or whether the
712 * loader entries directory exists. Here we opted to check whether /EFI/systemd/ is non-empty, which
713 * should be a suitable and very minimal check for a number of reasons:
715 * → The check is architecture independent (i.e. we check if any systemd-boot loader is installed,
716 * not a specific one.)
718 * → It doesn't assume we are the only boot loader (i.e doesn't check if we own the main
719 * /EFI/BOOT/BOOT*.EFI fallback binary.
721 * → It specifically checks for systemd-boot, not for other boot loaders (which a check for
722 * /boot/loader/entries would do). */
724 _cleanup_free_
char *p
= path_join(esp_path
, "/EFI/systemd/");
728 log_debug("Checking whether %s contains any files%s", p
, special_glyph(SPECIAL_GLYPH_ELLIPSIS
));
729 r
= dir_is_empty(p
, /* ignore_hidden_or_backup= */ false);
730 if (r
< 0 && r
!= -ENOENT
)
731 return log_error_errno(r
, "Failed to check whether %s contains any files: %m", p
);
736 int verb_install(int argc
, char *argv
[], void *userdata
) {
737 sd_id128_t uuid
= SD_ID128_NULL
;
738 uint64_t pstart
= 0, psize
= 0;
740 bool install
, graceful
;
743 /* Invoked for both "update" and "install" */
745 install
= streq(argv
[0], "install");
746 graceful
= !install
&& arg_graceful
; /* support graceful mode for updates */
748 r
= acquire_esp(/* unprivileged_mode= */ false, graceful
, &part
, &pstart
, &psize
, &uuid
, NULL
);
749 if (graceful
&& r
== -ENOKEY
)
750 return 0; /* If --graceful is specified and we can't find an ESP, handle this cleanly */
755 /* If we are updating, don't do anything if sd-boot wasn't actually installed. */
756 r
= are_we_installed(arg_esp_path
);
760 log_debug("Skipping update because sd-boot is not installed in the ESP.");
765 r
= acquire_xbootldr(/* unprivileged_mode= */ false, NULL
, NULL
);
769 r
= settle_make_entry_directory();
773 const char *arch
= arg_arch_all
? "" : get_efi_arch();
777 /* Don't create any of these directories when we are just updating. When we update
778 * we'll drop-in our files (unless there are newer ones already), but we won't create
779 * the directories for them in the first place. */
780 r
= create_subdirs(arg_esp_path
, esp_subdirs
);
784 r
= create_subdirs(arg_dollar_boot_path(), dollar_boot_subdirs
);
789 r
= install_binaries(arg_esp_path
, arch
, install
);
794 r
= install_loader_config(arg_esp_path
);
798 r
= install_entry_directory(arg_dollar_boot_path());
802 r
= install_entry_token();
806 r
= install_random_seed(arg_esp_path
);
811 r
= install_loader_specification(arg_dollar_boot_path());
816 (void) sync_everything();
818 if (!arg_touch_variables
)
822 log_info("Not changing EFI variables with --all-architectures.");
826 char *path
= strjoina("/EFI/systemd/systemd-boot", arch
, ".efi");
827 return install_variables(arg_esp_path
, part
, pstart
, psize
, uuid
, path
, install
, graceful
);
830 static int remove_boot_efi(const char *esp_path
) {
831 _cleanup_closedir_
DIR *d
= NULL
;
832 _cleanup_free_
char *p
= NULL
;
835 r
= chase_and_opendir("/EFI/BOOT", esp_path
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
, &p
, &d
);
839 return log_error_errno(r
, "Failed to open directory \"%s/EFI/BOOT\": %m", esp_path
);
841 FOREACH_DIRENT(de
, d
, break) {
842 _cleanup_close_
int fd
= -EBADF
;
843 _cleanup_free_
char *v
= NULL
;
845 if (!endswith_no_case(de
->d_name
, ".efi"))
848 if (!startswith_no_case(de
->d_name
, "boot"))
851 fd
= openat(dirfd(d
), de
->d_name
, O_RDONLY
|O_CLOEXEC
);
853 return log_error_errno(errno
, "Failed to open \"%s/%s\" for reading: %m", p
, de
->d_name
);
855 r
= get_file_version(fd
, &v
);
857 continue; /* No version information */
860 if (startswith(v
, "systemd-boot ")) {
861 r
= unlinkat(dirfd(d
), de
->d_name
, 0);
863 return log_error_errno(errno
, "Failed to remove \"%s/%s\": %m", p
, de
->d_name
);
865 log_info("Removed \"%s/%s\".", p
, de
->d_name
);
874 static int rmdir_one(const char *prefix
, const char *suffix
) {
877 p
= prefix_roota(prefix
, suffix
);
879 bool ignore
= IN_SET(errno
, ENOENT
, ENOTEMPTY
);
881 log_full_errno(ignore
? LOG_DEBUG
: LOG_ERR
, errno
,
882 "Failed to remove directory \"%s\": %m", p
);
886 log_info("Removed \"%s\".", p
);
891 static int remove_subdirs(const char *root
, const char *const *subdirs
) {
894 /* We use recursion here to destroy the directories in reverse order. Which should be safe given how
895 * short the array is. */
897 if (!subdirs
[0]) /* A the end of the list */
900 r
= remove_subdirs(root
, subdirs
+ 1);
901 q
= rmdir_one(root
, subdirs
[0]);
903 return r
< 0 ? r
: q
;
906 static int remove_entry_directory(const char *root
) {
908 assert(arg_make_entry_directory
>= 0);
910 if (!arg_make_entry_directory
|| !arg_entry_token
)
913 return rmdir_one(root
, arg_entry_token
);
916 static int remove_binaries(const char *esp_path
) {
920 p
= prefix_roota(esp_path
, "/EFI/systemd");
921 r
= rm_rf(p
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
923 q
= remove_boot_efi(esp_path
);
930 static int remove_file(const char *root
, const char *file
) {
936 p
= prefix_roota(root
, file
);
938 log_full_errno(errno
== ENOENT
? LOG_DEBUG
: LOG_ERR
, errno
,
939 "Failed to unlink file \"%s\": %m", p
);
941 return errno
== ENOENT
? 0 : -errno
;
944 log_info("Removed \"%s\".", p
);
948 static int remove_variables(sd_id128_t uuid
, const char *path
, bool in_order
) {
952 if (arg_root
|| !is_efi_boot())
955 r
= find_slot(uuid
, path
, &slot
);
959 r
= efi_remove_boot_option(slot
);
964 return remove_from_order(slot
);
969 static int remove_loader_variables(void) {
972 /* Remove all persistent loader variables we define */
975 EFI_LOADER_VARIABLE(LoaderConfigConsoleMode
),
976 EFI_LOADER_VARIABLE(LoaderConfigTimeout
),
977 EFI_LOADER_VARIABLE(LoaderConfigTimeoutOneShot
),
978 EFI_LOADER_VARIABLE(LoaderEntryDefault
),
979 EFI_LOADER_VARIABLE(LoaderEntryLastBooted
),
980 EFI_LOADER_VARIABLE(LoaderEntryOneShot
),
981 EFI_LOADER_VARIABLE(LoaderSystemToken
)){
985 q
= efi_set_variable(var
, NULL
, 0);
989 log_warning_errno(q
, "Failed to remove EFI variable %s: %m", var
);
993 log_info("Removed EFI variable %s.", var
);
999 int verb_remove(int argc
, char *argv
[], void *userdata
) {
1000 sd_id128_t uuid
= SD_ID128_NULL
;
1003 r
= acquire_esp(/* unprivileged_mode= */ false, /* graceful= */ false, NULL
, NULL
, NULL
, &uuid
, NULL
);
1007 r
= acquire_xbootldr(/* unprivileged_mode= */ false, NULL
, NULL
);
1011 r
= settle_make_entry_directory();
1015 r
= remove_binaries(arg_esp_path
);
1017 q
= remove_file(arg_esp_path
, "/loader/loader.conf");
1018 if (q
< 0 && r
>= 0)
1021 q
= remove_file(arg_esp_path
, "/loader/random-seed");
1022 if (q
< 0 && r
>= 0)
1025 q
= remove_file(arg_esp_path
, "/loader/entries.srel");
1026 if (q
< 0 && r
>= 0)
1029 q
= remove_subdirs(arg_esp_path
, esp_subdirs
);
1030 if (q
< 0 && r
>= 0)
1033 q
= remove_subdirs(arg_esp_path
, dollar_boot_subdirs
);
1034 if (q
< 0 && r
>= 0)
1037 q
= remove_entry_directory(arg_esp_path
);
1038 if (q
< 0 && r
>= 0)
1041 if (arg_xbootldr_path
) {
1042 /* Remove a subset of these also from the XBOOTLDR partition if it exists */
1044 q
= remove_file(arg_xbootldr_path
, "/loader/entries.srel");
1045 if (q
< 0 && r
>= 0)
1048 q
= remove_subdirs(arg_xbootldr_path
, dollar_boot_subdirs
);
1049 if (q
< 0 && r
>= 0)
1052 q
= remove_entry_directory(arg_xbootldr_path
);
1053 if (q
< 0 && r
>= 0)
1057 (void) sync_everything();
1059 if (!arg_touch_variables
)
1063 log_info("Not changing EFI variables with --all-architectures.");
1067 char *path
= strjoina("/EFI/systemd/systemd-boot", get_efi_arch(), ".efi");
1068 q
= remove_variables(uuid
, path
, true);
1069 if (q
< 0 && r
>= 0)
1072 q
= remove_loader_variables();
1073 if (q
< 0 && r
>= 0)
1079 int verb_is_installed(int argc
, char *argv
[], void *userdata
) {
1082 r
= acquire_esp(/* unprivileged_mode= */ false,
1083 /* graceful= */ arg_graceful
,
1084 NULL
, NULL
, NULL
, NULL
, NULL
);
1088 r
= are_we_installed(arg_esp_path
);
1095 return EXIT_SUCCESS
;
1099 return EXIT_FAILURE
;