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
);
31 if (ERRNO_IS_MACHINE_ID_UNSET(r
)) /* Not set or empty */
34 return log_error_errno(r
, "Failed to get machine-id: %m");
37 log_debug("Loaded machine ID %s from /etc/machine-id.", SD_ID128_TO_STRING(arg_machine_id
));
41 static int load_etc_machine_info(void) {
42 /* systemd v250 added support to store the kernel-install layout setting and the machine ID to use
43 * for setting up the ESP in /etc/machine-info. The newer /etc/kernel/entry-token file, as well as
44 * the $layout field in /etc/kernel/install.conf are better replacements for this though, hence this
45 * has been deprecated and is only returned for compatibility. */
46 _cleanup_free_
char *p
= NULL
, *s
= NULL
, *layout
= NULL
;
49 p
= path_join(arg_root
, "etc/machine-info");
53 r
= parse_env_file(NULL
, p
,
54 "KERNEL_INSTALL_LAYOUT", &layout
,
55 "KERNEL_INSTALL_MACHINE_ID", &s
);
59 return log_error_errno(r
, "Failed to parse /etc/machine-info: %m");
63 log_notice("Read $KERNEL_INSTALL_MACHINE_ID from /etc/machine-info. "
64 "Please move it to /etc/kernel/entry-token.");
66 r
= sd_id128_from_string(s
, &arg_machine_id
);
68 return log_error_errno(r
, "Failed to parse KERNEL_INSTALL_MACHINE_ID=%s in /etc/machine-info: %m", s
);
70 log_debug("Loaded KERNEL_INSTALL_MACHINE_ID=%s from /etc/machine-info.",
71 SD_ID128_TO_STRING(arg_machine_id
));
74 if (!isempty(layout
)) {
76 log_notice("Read $KERNEL_INSTALL_LAYOUT from /etc/machine-info. "
77 "Please move it to the layout= setting of /etc/kernel/install.conf.");
79 log_debug("KERNEL_INSTALL_LAYOUT=%s is specified in /etc/machine-info.", layout
);
80 free_and_replace(arg_install_layout
, layout
);
86 static int load_etc_kernel_install_conf(void) {
87 _cleanup_free_
char *layout
= NULL
, *p
= NULL
;
90 p
= path_join(arg_root
, etc_kernel(), "install.conf");
94 r
= parse_env_file(NULL
, p
, "layout", &layout
);
98 return log_error_errno(r
, "Failed to parse %s: %m", p
);
100 if (!isempty(layout
)) {
101 log_debug("layout=%s is specified in %s.", layout
, p
);
102 free_and_replace(arg_install_layout
, layout
);
108 static bool use_boot_loader_spec_type1(void) {
109 /* If the layout is not specified, or if it is set explicitly to "bls" we assume Boot Loader
110 * Specification Type #1 is the chosen format for our boot loader entries */
111 return !arg_install_layout
|| streq(arg_install_layout
, "bls");
114 static int settle_make_entry_directory(void) {
117 r
= load_etc_machine_id();
121 r
= load_etc_machine_info();
125 r
= load_etc_kernel_install_conf();
129 r
= settle_entry_token();
133 bool layout_type1
= use_boot_loader_spec_type1();
134 if (arg_make_entry_directory
< 0) { /* Automatic mode */
136 if (arg_entry_token_type
== BOOT_ENTRY_TOKEN_MACHINE_ID
) {
137 r
= path_is_temporary_fs("/etc/machine-id");
139 return log_debug_errno(r
, "Couldn't determine whether /etc/machine-id is on a temporary file system: %m");
141 arg_make_entry_directory
= r
== 0;
143 arg_make_entry_directory
= true;
145 arg_make_entry_directory
= false;
148 if (arg_make_entry_directory
> 0 && !layout_type1
)
149 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
150 "KERNEL_INSTALL_LAYOUT=%s is configured, but Boot Loader Specification Type #1 entry directory creation was requested.",
156 static int compare_product(const char *a
, const char *b
) {
165 return x
< y
? -1 : x
> y
? 1 : 0;
167 return strncmp(a
, b
, x
);
170 static int compare_version(const char *a
, const char *b
) {
174 a
+= strcspn(a
, " ");
176 b
+= strcspn(b
, " ");
179 return strverscmp_improved(a
, b
);
182 static int version_check(int fd_from
, const char *from
, int fd_to
, const char *to
) {
183 _cleanup_free_
char *a
= NULL
, *b
= NULL
;
186 assert(fd_from
>= 0);
191 r
= get_file_version(fd_from
, &a
);
193 return log_notice_errno(r
, "Source file \"%s\" does not carry version information!", from
);
197 r
= get_file_version(fd_to
, &b
);
199 return log_notice_errno(r
, "Skipping \"%s\", it's owned by another boot loader (no version info found).",
203 if (compare_product(a
, b
) != 0)
204 return log_notice_errno(SYNTHETIC_ERRNO(ESRCH
),
205 "Skipping \"%s\", it's owned by another boot loader.", to
);
207 r
= compare_version(a
, b
);
208 log_debug("Comparing versions: \"%s\" %s \"%s", a
, comparison_operator(r
), b
);
210 return log_warning_errno(SYNTHETIC_ERRNO(ESTALE
),
211 "Skipping \"%s\", newer boot loader version in place already.", to
);
213 return log_info_errno(SYNTHETIC_ERRNO(ESTALE
),
214 "Skipping \"%s\", same boot loader version in place already.", to
);
219 static int copy_file_with_version_check(const char *from
, const char *to
, bool force
) {
220 _cleanup_close_
int fd_from
= -EBADF
, fd_to
= -EBADF
;
221 _cleanup_free_
char *t
= NULL
;
224 fd_from
= open(from
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
226 return log_error_errno(errno
, "Failed to open \"%s\" for reading: %m", from
);
229 fd_to
= open(to
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
232 return log_error_errno(errno
, "Failed to open \"%s\" for reading: %m", to
);
234 r
= version_check(fd_from
, from
, fd_to
, to
);
238 if (lseek(fd_from
, 0, SEEK_SET
) == (off_t
) -1)
239 return log_error_errno(errno
, "Failed to seek in \"%s\": %m", from
);
241 fd_to
= safe_close(fd_to
);
245 r
= tempfn_random(to
, NULL
, &t
);
250 fd_to
= open(t
, O_WRONLY
|O_CREAT
|O_CLOEXEC
|O_EXCL
|O_NOFOLLOW
, 0644);
252 return log_error_errno(errno
, "Failed to open \"%s\" for writing: %m", t
);
255 r
= copy_bytes(fd_from
, fd_to
, UINT64_MAX
, COPY_REFLINK
);
258 return log_error_errno(r
, "Failed to copy data from \"%s\" to \"%s\": %m", from
, t
);
261 (void) copy_times(fd_from
, fd_to
, 0);
263 r
= fsync_full(fd_to
);
266 return log_error_errno(r
, "Failed to copy data from \"%s\" to \"%s\": %m", from
, t
);
269 r
= RET_NERRNO(renameat(AT_FDCWD
, t
, AT_FDCWD
, to
));
272 return log_error_errno(r
, "Failed to rename \"%s\" to \"%s\": %m", t
, to
);
275 log_info("Copied \"%s\" to \"%s\".", from
, to
);
280 static int mkdir_one(const char *prefix
, const char *suffix
) {
281 _cleanup_free_
char *p
= NULL
;
283 p
= path_join(prefix
, suffix
);
284 if (mkdir(p
, 0700) < 0) {
286 return log_error_errno(errno
, "Failed to create \"%s\": %m", p
);
288 log_info("Created \"%s\".", p
);
293 static const char *const esp_subdirs
[] = {
294 /* The directories to place in the ESP */
302 static const char *const dollar_boot_subdirs
[] = {
303 /* The directories to place in the XBOOTLDR partition or the ESP, depending what exists */
305 "loader/entries", /* Type #1 entries */
307 "EFI/Linux", /* Type #2 entries */
311 static int create_subdirs(const char *root
, const char * const *subdirs
) {
314 STRV_FOREACH(i
, subdirs
) {
315 r
= mkdir_one(root
, *i
);
324 static int copy_one_file(const char *esp_path
, const char *name
, bool force
) {
325 char *root
= IN_SET(arg_install_source
, ARG_INSTALL_SOURCE_AUTO
, ARG_INSTALL_SOURCE_IMAGE
) ? arg_root
: NULL
;
326 _cleanup_free_
char *source_path
= NULL
, *dest_path
= NULL
, *p
= NULL
, *q
= NULL
;
331 dest_name
= strdupa_safe(name
);
332 s
= endswith_no_case(dest_name
, ".signed");
336 p
= path_join(BOOTLIBDIR
, name
);
340 r
= chase(p
, root
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
, &source_path
, NULL
);
341 /* If we had a root directory to try, we didn't find it and we are in auto mode, retry on the host */
342 if (r
== -ENOENT
&& root
&& arg_install_source
== ARG_INSTALL_SOURCE_AUTO
)
343 r
= chase(p
, NULL
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
, &source_path
, NULL
);
345 return log_error_errno(r
,
346 "Failed to resolve path %s%s%s: %m",
348 root
? " under directory " : "",
351 q
= path_join("/EFI/systemd/", dest_name
);
355 r
= chase(q
, esp_path
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
|CHASE_NONEXISTENT
, &dest_path
, NULL
);
357 return log_error_errno(r
, "Failed to resolve path %s under directory %s: %m", q
, esp_path
);
359 /* Note that if this fails we do the second copy anyway, but return this error code,
360 * so we stash it away in a separate variable. */
361 ret
= copy_file_with_version_check(source_path
, dest_path
, force
);
363 e
= startswith(dest_name
, "systemd-boot");
365 _cleanup_free_
char *default_dest_path
= NULL
;
368 /* Create the EFI default boot loader name (specified for removable devices) */
369 v
= strjoina("/EFI/BOOT/BOOT", e
);
370 ascii_strupper(strrchr(v
, '/') + 1);
372 r
= chase(v
, esp_path
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
|CHASE_NONEXISTENT
, &default_dest_path
, NULL
);
374 return log_error_errno(r
, "Failed to resolve path %s under directory %s: %m", v
, esp_path
);
376 r
= copy_file_with_version_check(source_path
, default_dest_path
, force
);
377 if (r
< 0 && ret
== 0)
384 static int install_binaries(const char *esp_path
, const char *arch
, bool force
) {
385 char *root
= IN_SET(arg_install_source
, ARG_INSTALL_SOURCE_AUTO
, ARG_INSTALL_SOURCE_IMAGE
) ? arg_root
: NULL
;
386 _cleanup_closedir_
DIR *d
= NULL
;
387 _cleanup_free_
char *path
= NULL
;
390 r
= chase_and_opendir(BOOTLIBDIR
, root
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
, &path
, &d
);
391 /* If we had a root directory to try, we didn't find it and we are in auto mode, retry on the host */
392 if (r
== -ENOENT
&& root
&& arg_install_source
== ARG_INSTALL_SOURCE_AUTO
)
393 r
= chase_and_opendir(BOOTLIBDIR
, NULL
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
, &path
, &d
);
395 return log_error_errno(r
, "Failed to open boot loader directory %s%s: %m", strempty(root
), BOOTLIBDIR
);
397 const char *suffix
= strjoina(arch
, ".efi");
398 const char *suffix_signed
= strjoina(arch
, ".efi.signed");
400 FOREACH_DIRENT(de
, d
, return log_error_errno(errno
, "Failed to read \"%s\": %m", path
)) {
403 if (!endswith_no_case(de
->d_name
, suffix
) && !endswith_no_case(de
->d_name
, suffix_signed
))
406 /* skip the .efi file, if there's a .signed version of it */
407 if (endswith_no_case(de
->d_name
, ".efi")) {
408 _cleanup_free_
const char *s
= strjoin(de
->d_name
, ".signed");
411 if (faccessat(dirfd(d
), s
, F_OK
, 0) >= 0)
415 k
= copy_one_file(esp_path
, de
->d_name
, force
);
416 /* Don't propagate an error code if no update necessary, installed version already equal or
417 * newer version, or other boot loader in place. */
418 if (arg_graceful
&& IN_SET(k
, -ESTALE
, -ESRCH
))
427 static int install_loader_config(const char *esp_path
) {
428 _cleanup_(unlink_and_freep
) char *t
= NULL
;
429 _cleanup_fclose_
FILE *f
= NULL
;
430 _cleanup_free_
char *p
= NULL
;
433 assert(arg_make_entry_directory
>= 0);
435 p
= path_join(esp_path
, "/loader/loader.conf");
438 if (access(p
, F_OK
) >= 0) /* Silently skip creation if the file already exists (early check) */
441 r
= fopen_tmpfile_linkable(p
, O_WRONLY
|O_CLOEXEC
, &t
, &f
);
443 return log_error_errno(r
, "Failed to open \"%s\" for writing: %m", p
);
445 fprintf(f
, "#timeout 3\n"
446 "#console-mode keep\n");
448 if (arg_make_entry_directory
) {
449 assert(arg_entry_token
);
450 fprintf(f
, "default %s-*\n", arg_entry_token
);
453 r
= flink_tmpfile(f
, t
, p
, /* replace= */ false);
455 return 0; /* Silently skip creation if the file exists now (recheck) */
457 return log_error_errno(r
, "Failed to move \"%s\" into place: %m", p
);
463 static int install_loader_specification(const char *root
) {
464 _cleanup_(unlink_and_freep
) char *t
= NULL
;
465 _cleanup_fclose_
FILE *f
= NULL
;
466 _cleanup_free_
char *p
= NULL
;
469 p
= path_join(root
, "/loader/entries.srel");
473 if (access(p
, F_OK
) >= 0) /* Silently skip creation if the file already exists (early check) */
476 r
= fopen_tmpfile_linkable(p
, O_WRONLY
|O_CLOEXEC
, &t
, &f
);
478 return log_error_errno(r
, "Failed to open \"%s\" for writing: %m", p
);
480 fprintf(f
, "type1\n");
482 r
= flink_tmpfile(f
, t
, p
, /* replace= */ false);
484 return 0; /* Silently skip creation if the file exists now (recheck) */
486 return log_error_errno(r
, "Failed to move \"%s\" into place: %m", p
);
492 static int install_entry_directory(const char *root
) {
494 assert(arg_make_entry_directory
>= 0);
496 if (!arg_make_entry_directory
)
499 assert(arg_entry_token
);
500 return mkdir_one(root
, arg_entry_token
);
503 static int install_entry_token(void) {
504 _cleanup_free_
char* p
= NULL
;
507 assert(arg_make_entry_directory
>= 0);
508 assert(arg_entry_token
);
510 /* Let's save the used entry token in /etc/kernel/entry-token if we used it to create the entry
511 * directory, or if anything else but the machine ID */
513 if (!arg_make_entry_directory
&& arg_entry_token_type
== BOOT_ENTRY_TOKEN_MACHINE_ID
)
516 p
= path_join(arg_root
, etc_kernel(), "entry-token");
520 r
= write_string_file(p
, arg_entry_token
, WRITE_STRING_FILE_CREATE
|WRITE_STRING_FILE_ATOMIC
|WRITE_STRING_FILE_MKDIR_0755
);
522 return log_error_errno(r
, "Failed to write entry token '%s' to %s: %m", arg_entry_token
, p
);
527 static bool same_entry(uint16_t id
, sd_id128_t uuid
, const char *path
) {
528 _cleanup_free_
char *opath
= NULL
;
532 r
= efi_get_boot_option(id
, NULL
, &ouuid
, &opath
, NULL
);
535 if (!sd_id128_equal(uuid
, ouuid
))
538 /* Some motherboards convert the path to uppercase under certain circumstances
539 * (e.g. after booting into the Boot Menu in the ASUS ROG STRIX B350-F GAMING),
540 * so use case-insensitive checking */
541 if (!strcaseeq_ptr(path
, opath
))
547 static int find_slot(sd_id128_t uuid
, const char *path
, uint16_t *id
) {
548 _cleanup_free_
uint16_t *options
= NULL
;
550 int n
= efi_get_boot_options(&options
);
554 /* find already existing systemd-boot entry */
555 for (int i
= 0; i
< n
; i
++)
556 if (same_entry(options
[i
], uuid
, path
)) {
561 /* find free slot in the sorted BootXXXX variable list */
562 for (int i
= 0; i
< n
; i
++)
563 if (i
!= options
[i
]) {
568 /* use the next one */
575 static int insert_into_order(uint16_t slot
, bool first
) {
576 _cleanup_free_
uint16_t *order
= NULL
;
580 n
= efi_get_boot_order(&order
);
582 /* no entry, add us */
583 return efi_set_boot_order(&slot
, 1);
585 /* are we the first and only one? */
586 if (n
== 1 && order
[0] == slot
)
589 /* are we already in the boot order? */
590 for (int i
= 0; i
< n
; i
++) {
591 if (order
[i
] != slot
)
594 /* we do not require to be the first one, all is fine */
598 /* move us to the first slot */
599 memmove(order
+ 1, order
, i
* sizeof(uint16_t));
601 return efi_set_boot_order(order
, n
);
605 t
= reallocarray(order
, n
+ 1, sizeof(uint16_t));
610 /* add us to the top or end of the list */
612 memmove(order
+ 1, order
, n
* sizeof(uint16_t));
617 return efi_set_boot_order(order
, n
+ 1);
620 static int remove_from_order(uint16_t slot
) {
621 _cleanup_free_
uint16_t *order
= NULL
;
624 n
= efi_get_boot_order(&order
);
628 for (int i
= 0; i
< n
; i
++) {
629 if (order
[i
] != slot
)
633 memmove(order
+ i
, order
+ i
+1, (n
- i
) * sizeof(uint16_t));
634 return efi_set_boot_order(order
, n
- 1);
640 static const char *pick_efi_boot_option_description(void) {
641 return arg_efi_boot_option_description
?: "Linux Boot Manager";
644 static int install_variables(
645 const char *esp_path
,
658 log_info("Acting on %s, skipping EFI variable setup.",
659 arg_image
? "image" : "root directory");
663 if (!is_efi_boot()) {
664 log_warning("Not booted with EFI, skipping EFI variable setup.");
668 r
= chase_and_access(path
, esp_path
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
, F_OK
, NULL
);
672 return log_error_errno(r
, "Cannot access \"%s/%s\": %m", esp_path
, path
);
674 r
= find_slot(uuid
, path
, &slot
);
676 int level
= graceful
? arg_quiet
? LOG_DEBUG
: LOG_INFO
: LOG_ERR
;
677 const char *skip
= graceful
? ", skipping" : "";
679 log_full_errno(level
, r
,
681 "Failed to access EFI variables%s. Is the \"efivarfs\" filesystem mounted?" :
682 "Failed to determine current boot order%s: %m", skip
);
684 return graceful
? 0 : r
;
687 if (first
|| r
== 0) {
688 r
= efi_add_boot_option(slot
, pick_efi_boot_option_description(),
692 int level
= graceful
? arg_quiet
? LOG_DEBUG
: LOG_INFO
: LOG_ERR
;
693 const char *skip
= graceful
? ", skipping" : "";
695 log_full_errno(level
, r
, "Failed to create EFI Boot variable entry%s: %m", skip
);
697 return graceful
? 0 : r
;
700 log_info("Created EFI boot entry \"%s\".", pick_efi_boot_option_description());
703 return insert_into_order(slot
, first
);
706 static int are_we_installed(const char *esp_path
) {
709 /* Tests whether systemd-boot is installed. It's not obvious what to use as check here: we could
710 * check EFI variables, we could check what binary /EFI/BOOT/BOOT*.EFI points to, or whether the
711 * loader entries directory exists. Here we opted to check whether /EFI/systemd/ is non-empty, which
712 * should be a suitable and very minimal check for a number of reasons:
714 * → The check is architecture independent (i.e. we check if any systemd-boot loader is installed,
715 * not a specific one.)
717 * → It doesn't assume we are the only boot loader (i.e doesn't check if we own the main
718 * /EFI/BOOT/BOOT*.EFI fallback binary.
720 * → It specifically checks for systemd-boot, not for other boot loaders (which a check for
721 * /boot/loader/entries would do). */
723 _cleanup_free_
char *p
= path_join(esp_path
, "/EFI/systemd/");
727 log_debug("Checking whether %s contains any files%s", p
, special_glyph(SPECIAL_GLYPH_ELLIPSIS
));
728 r
= dir_is_empty(p
, /* ignore_hidden_or_backup= */ false);
729 if (r
< 0 && r
!= -ENOENT
)
730 return log_error_errno(r
, "Failed to check whether %s contains any files: %m", p
);
735 int verb_install(int argc
, char *argv
[], void *userdata
) {
736 sd_id128_t uuid
= SD_ID128_NULL
;
737 uint64_t pstart
= 0, psize
= 0;
739 bool install
, graceful
;
742 /* Invoked for both "update" and "install" */
744 install
= streq(argv
[0], "install");
745 graceful
= !install
&& arg_graceful
; /* support graceful mode for updates */
747 r
= acquire_esp(/* unprivileged_mode= */ false, graceful
, &part
, &pstart
, &psize
, &uuid
, NULL
);
748 if (graceful
&& r
== -ENOKEY
)
749 return 0; /* If --graceful is specified and we can't find an ESP, handle this cleanly */
754 /* If we are updating, don't do anything if sd-boot wasn't actually installed. */
755 r
= are_we_installed(arg_esp_path
);
759 log_debug("Skipping update because sd-boot is not installed in the ESP.");
764 r
= acquire_xbootldr(/* unprivileged_mode= */ false, NULL
, NULL
);
768 r
= settle_make_entry_directory();
772 const char *arch
= arg_arch_all
? "" : get_efi_arch();
776 /* Don't create any of these directories when we are just updating. When we update
777 * we'll drop-in our files (unless there are newer ones already), but we won't create
778 * the directories for them in the first place. */
779 r
= create_subdirs(arg_esp_path
, esp_subdirs
);
783 r
= create_subdirs(arg_dollar_boot_path(), dollar_boot_subdirs
);
788 r
= install_binaries(arg_esp_path
, arch
, install
);
793 r
= install_loader_config(arg_esp_path
);
797 r
= install_entry_directory(arg_dollar_boot_path());
801 r
= install_entry_token();
805 r
= install_random_seed(arg_esp_path
);
810 r
= install_loader_specification(arg_dollar_boot_path());
815 (void) sync_everything();
817 if (!arg_touch_variables
)
821 log_info("Not changing EFI variables with --all-architectures.");
825 char *path
= strjoina("/EFI/systemd/systemd-boot", arch
, ".efi");
826 return install_variables(arg_esp_path
, part
, pstart
, psize
, uuid
, path
, install
, graceful
);
829 static int remove_boot_efi(const char *esp_path
) {
830 _cleanup_closedir_
DIR *d
= NULL
;
831 _cleanup_free_
char *p
= NULL
;
834 r
= chase_and_opendir("/EFI/BOOT", esp_path
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
, &p
, &d
);
838 return log_error_errno(r
, "Failed to open directory \"%s/EFI/BOOT\": %m", esp_path
);
840 FOREACH_DIRENT(de
, d
, break) {
841 _cleanup_close_
int fd
= -EBADF
;
842 _cleanup_free_
char *v
= NULL
;
844 if (!endswith_no_case(de
->d_name
, ".efi"))
847 if (!startswith_no_case(de
->d_name
, "boot"))
850 fd
= openat(dirfd(d
), de
->d_name
, O_RDONLY
|O_CLOEXEC
);
852 return log_error_errno(errno
, "Failed to open \"%s/%s\" for reading: %m", p
, de
->d_name
);
854 r
= get_file_version(fd
, &v
);
856 continue; /* No version information */
859 if (startswith(v
, "systemd-boot ")) {
860 r
= unlinkat(dirfd(d
), de
->d_name
, 0);
862 return log_error_errno(errno
, "Failed to remove \"%s/%s\": %m", p
, de
->d_name
);
864 log_info("Removed \"%s/%s\".", p
, de
->d_name
);
873 static int rmdir_one(const char *prefix
, const char *suffix
) {
876 p
= prefix_roota(prefix
, suffix
);
878 bool ignore
= IN_SET(errno
, ENOENT
, ENOTEMPTY
);
880 log_full_errno(ignore
? LOG_DEBUG
: LOG_ERR
, errno
,
881 "Failed to remove directory \"%s\": %m", p
);
885 log_info("Removed \"%s\".", p
);
890 static int remove_subdirs(const char *root
, const char *const *subdirs
) {
893 /* We use recursion here to destroy the directories in reverse order. Which should be safe given how
894 * short the array is. */
896 if (!subdirs
[0]) /* A the end of the list */
899 r
= remove_subdirs(root
, subdirs
+ 1);
900 q
= rmdir_one(root
, subdirs
[0]);
902 return r
< 0 ? r
: q
;
905 static int remove_entry_directory(const char *root
) {
907 assert(arg_make_entry_directory
>= 0);
909 if (!arg_make_entry_directory
|| !arg_entry_token
)
912 return rmdir_one(root
, arg_entry_token
);
915 static int remove_binaries(const char *esp_path
) {
919 p
= prefix_roota(esp_path
, "/EFI/systemd");
920 r
= rm_rf(p
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
922 q
= remove_boot_efi(esp_path
);
929 static int remove_file(const char *root
, const char *file
) {
935 p
= prefix_roota(root
, file
);
937 log_full_errno(errno
== ENOENT
? LOG_DEBUG
: LOG_ERR
, errno
,
938 "Failed to unlink file \"%s\": %m", p
);
940 return errno
== ENOENT
? 0 : -errno
;
943 log_info("Removed \"%s\".", p
);
947 static int remove_variables(sd_id128_t uuid
, const char *path
, bool in_order
) {
951 if (arg_root
|| !is_efi_boot())
954 r
= find_slot(uuid
, path
, &slot
);
958 r
= efi_remove_boot_option(slot
);
963 return remove_from_order(slot
);
968 static int remove_loader_variables(void) {
971 /* Remove all persistent loader variables we define */
974 EFI_LOADER_VARIABLE(LoaderConfigTimeout
),
975 EFI_LOADER_VARIABLE(LoaderConfigTimeoutOneShot
),
976 EFI_LOADER_VARIABLE(LoaderEntryDefault
),
977 EFI_LOADER_VARIABLE(LoaderEntryOneShot
),
978 EFI_LOADER_VARIABLE(LoaderSystemToken
)){
982 q
= efi_set_variable(var
, NULL
, 0);
986 log_warning_errno(q
, "Failed to remove EFI variable %s: %m", var
);
990 log_info("Removed EFI variable %s.", var
);
996 int verb_remove(int argc
, char *argv
[], void *userdata
) {
997 sd_id128_t uuid
= SD_ID128_NULL
;
1000 r
= acquire_esp(/* unprivileged_mode= */ false, /* graceful= */ false, NULL
, NULL
, NULL
, &uuid
, NULL
);
1004 r
= acquire_xbootldr(/* unprivileged_mode= */ false, NULL
, NULL
);
1008 r
= settle_make_entry_directory();
1012 r
= remove_binaries(arg_esp_path
);
1014 q
= remove_file(arg_esp_path
, "/loader/loader.conf");
1015 if (q
< 0 && r
>= 0)
1018 q
= remove_file(arg_esp_path
, "/loader/random-seed");
1019 if (q
< 0 && r
>= 0)
1022 q
= remove_file(arg_esp_path
, "/loader/entries.srel");
1023 if (q
< 0 && r
>= 0)
1026 q
= remove_subdirs(arg_esp_path
, esp_subdirs
);
1027 if (q
< 0 && r
>= 0)
1030 q
= remove_subdirs(arg_esp_path
, dollar_boot_subdirs
);
1031 if (q
< 0 && r
>= 0)
1034 q
= remove_entry_directory(arg_esp_path
);
1035 if (q
< 0 && r
>= 0)
1038 if (arg_xbootldr_path
) {
1039 /* Remove a subset of these also from the XBOOTLDR partition if it exists */
1041 q
= remove_file(arg_xbootldr_path
, "/loader/entries.srel");
1042 if (q
< 0 && r
>= 0)
1045 q
= remove_subdirs(arg_xbootldr_path
, dollar_boot_subdirs
);
1046 if (q
< 0 && r
>= 0)
1049 q
= remove_entry_directory(arg_xbootldr_path
);
1050 if (q
< 0 && r
>= 0)
1054 (void) sync_everything();
1056 if (!arg_touch_variables
)
1060 log_info("Not changing EFI variables with --all-architectures.");
1064 char *path
= strjoina("/EFI/systemd/systemd-boot", get_efi_arch(), ".efi");
1065 q
= remove_variables(uuid
, path
, true);
1066 if (q
< 0 && r
>= 0)
1069 q
= remove_loader_variables();
1070 if (q
< 0 && r
>= 0)
1076 int verb_is_installed(int argc
, char *argv
[], void *userdata
) {
1079 r
= acquire_esp(/* privileged_mode= */ false,
1080 /* graceful= */ arg_graceful
,
1081 NULL
, NULL
, NULL
, NULL
, NULL
);
1085 r
= are_we_installed(arg_esp_path
);
1092 return EXIT_SUCCESS
;
1096 return EXIT_FAILURE
;