get_file_version() would return:
- various negative errors if the file could not be accessed or if it was not a
regular file
- 0/NULL if the file was too small
- -ESRCH or -EINVAL if the file did not contain the marker
- -ENOMEM or permissions errors
- 1 if the marker was found
bootctl status iterates over /EFI/{systemd,BOOT}/*.efi and checks if the files
contain a systemd-boot version tag. Resource or permission errors should be
fatal, but lack of version information should be silently ignored.
OTOH, when updating or installing bootloader files, the version is expected
to be present.
get_file_version() is changed to return -ESRCH if the version is unavailable,
and other errnos for permission or resource errors.
The logging is reworked to always display an error if encountered, but also
to log the status at debug level what the result of the version inquiry is.
This makes it figure out what is going on:
/efi/EFI/systemd/systemd-bootx64.efi: EFI binary LoaderInfo marker: "systemd-boot 253-6.fc38"
/efi/EFI/BOOT/BOOTfbx64.efi: EFI binary has no LoaderInfo marker.
/efi/EFI/BOOT/BOOTIA32.EFI: EFI binary has no LoaderInfo marker.
/efi/EFI/BOOT/BOOTX64.EFI: EFI binary LoaderInfo marker: "systemd-boot 253-6.fc38"
Replaces #27034.
Fixes https://github.com/NixOS/nixpkgs/issues/223579.
131 lines
4.0 KiB
C
131 lines
4.0 KiB
C
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
|
|
#include <sys/mman.h>
|
|
|
|
#include "bootctl.h"
|
|
#include "bootctl-util.h"
|
|
#include "fileio.h"
|
|
#include "stat-util.h"
|
|
#include "sync-util.h"
|
|
|
|
int sync_everything(void) {
|
|
int ret = 0, k;
|
|
|
|
if (arg_esp_path) {
|
|
k = syncfs_path(AT_FDCWD, arg_esp_path);
|
|
if (k < 0)
|
|
ret = log_error_errno(k, "Failed to synchronize the ESP '%s': %m", arg_esp_path);
|
|
}
|
|
|
|
if (arg_xbootldr_path) {
|
|
k = syncfs_path(AT_FDCWD, arg_xbootldr_path);
|
|
if (k < 0)
|
|
ret = log_error_errno(k, "Failed to synchronize $BOOT '%s': %m", arg_xbootldr_path);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
const char *get_efi_arch(void) {
|
|
/* Detect EFI firmware architecture of the running system. On mixed mode systems, it could be 32bit
|
|
* while the kernel is running in 64bit. */
|
|
|
|
#ifdef __x86_64__
|
|
_cleanup_free_ char *platform_size = NULL;
|
|
int r;
|
|
|
|
r = read_one_line_file("/sys/firmware/efi/fw_platform_size", &platform_size);
|
|
if (r == -ENOENT)
|
|
return EFI_MACHINE_TYPE_NAME;
|
|
if (r < 0) {
|
|
log_warning_errno(r,
|
|
"Error reading EFI firmware word size, assuming machine type '%s': %m",
|
|
EFI_MACHINE_TYPE_NAME);
|
|
return EFI_MACHINE_TYPE_NAME;
|
|
}
|
|
|
|
if (streq(platform_size, "64"))
|
|
return EFI_MACHINE_TYPE_NAME;
|
|
if (streq(platform_size, "32"))
|
|
return "ia32";
|
|
|
|
log_warning(
|
|
"Unknown EFI firmware word size '%s', using machine type '%s'.",
|
|
platform_size,
|
|
EFI_MACHINE_TYPE_NAME);
|
|
#endif
|
|
|
|
return EFI_MACHINE_TYPE_NAME;
|
|
}
|
|
|
|
/* search for "#### LoaderInfo: systemd-boot 218 ####" string inside the binary */
|
|
int get_file_version(int fd, char **ret) {
|
|
struct stat st;
|
|
char *buf;
|
|
const char *s, *e;
|
|
char *marker = NULL;
|
|
int r;
|
|
|
|
assert(fd >= 0);
|
|
assert(ret);
|
|
|
|
if (fstat(fd, &st) < 0)
|
|
return log_error_errno(errno, "Failed to stat EFI binary: %m");
|
|
|
|
r = stat_verify_regular(&st);
|
|
if (r < 0) {
|
|
log_debug_errno(r, "EFI binary is not a regular file, assuming no version information: %m");
|
|
return -ESRCH;
|
|
}
|
|
|
|
if (st.st_size < 27 || file_offset_beyond_memory_size(st.st_size))
|
|
return log_debug_errno(SYNTHETIC_ERRNO(ESRCH),
|
|
"EFI binary size too %s: %"PRIi64,
|
|
st.st_size < 27 ? "small" : "large", st.st_size);
|
|
|
|
buf = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
|
if (buf == MAP_FAILED)
|
|
return log_error_errno(errno, "Failed to mmap EFI binary: %m");
|
|
|
|
s = mempmem_safe(buf, st.st_size - 8, "#### LoaderInfo: ", 17);
|
|
if (!s) {
|
|
r = log_debug_errno(SYNTHETIC_ERRNO(ESRCH), "EFI binary has no LoaderInfo marker.");
|
|
goto finish;
|
|
}
|
|
|
|
e = memmem_safe(s, st.st_size - (s - buf), " ####", 5);
|
|
if (!e || e - s < 3) {
|
|
r = log_error_errno(SYNTHETIC_ERRNO(EINVAL), "EFI binary has malformed LoaderInfo marker.");
|
|
goto finish;
|
|
}
|
|
|
|
marker = strndup(s, e - s);
|
|
if (!marker) {
|
|
r = log_oom();
|
|
goto finish;
|
|
}
|
|
|
|
log_debug("EFI binary LoaderInfo marker: \"%s\"", marker);
|
|
r = 0;
|
|
*ret = marker;
|
|
finish:
|
|
(void) munmap(buf, st.st_size);
|
|
return r;
|
|
}
|
|
|
|
int settle_entry_token(void) {
|
|
int r;
|
|
|
|
r = boot_entry_token_ensure(
|
|
arg_root,
|
|
etc_kernel(),
|
|
arg_machine_id,
|
|
&arg_entry_token_type,
|
|
&arg_entry_token);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
log_debug("Using entry token: %s", arg_entry_token);
|
|
return 0;
|
|
}
|