| /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
| |
| #include <sys/mman.h> |
| |
| #include "bootctl.h" |
| #include "bootctl-util.h" |
| #include "fileio.h" |
| #include "os-util.h" |
| #include "path-util.h" |
| #include "stat-util.h" |
| #include "sync-util.h" |
| #include "utf8.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 *x = 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) |
| return log_error_errno(r, "EFI binary is not a regular file: %m"); |
| |
| if (st.st_size < 27 || file_offset_beyond_memory_size(st.st_size)) { |
| *ret = NULL; |
| return 0; |
| } |
| |
| buf = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); |
| if (buf == MAP_FAILED) |
| return log_error_errno(errno, "Failed to memory map EFI binary: %m"); |
| |
| s = mempmem_safe(buf, st.st_size - 8, "#### LoaderInfo: ", 17); |
| if (!s) { |
| r = -ESRCH; |
| goto finish; |
| } |
| |
| e = memmem_safe(s, st.st_size - (s - buf), " ####", 5); |
| if (!e || e - s < 3) { |
| r = log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Malformed version string."); |
| goto finish; |
| } |
| |
| x = strndup(s, e - s); |
| if (!x) { |
| r = log_oom(); |
| goto finish; |
| } |
| r = 1; |
| |
| finish: |
| (void) munmap(buf, st.st_size); |
| if (r >= 0) |
| *ret = x; |
| |
| return r; |
| } |
| |
| int settle_entry_token(void) { |
| int r; |
| |
| switch (arg_entry_token_type) { |
| |
| case ARG_ENTRY_TOKEN_AUTO: { |
| _cleanup_free_ char *buf = NULL, *p = NULL; |
| p = path_join(arg_root, etc_kernel(), "entry-token"); |
| if (!p) |
| return log_oom(); |
| r = read_one_line_file(p, &buf); |
| if (r < 0 && r != -ENOENT) |
| return log_error_errno(r, "Failed to read %s: %m", p); |
| |
| if (!isempty(buf)) { |
| free_and_replace(arg_entry_token, buf); |
| arg_entry_token_type = ARG_ENTRY_TOKEN_LITERAL; |
| } else if (sd_id128_is_null(arg_machine_id)) { |
| _cleanup_free_ char *id = NULL, *image_id = NULL; |
| |
| r = parse_os_release(arg_root, |
| "IMAGE_ID", &image_id, |
| "ID", &id); |
| if (r < 0) |
| return log_error_errno(r, "Failed to load /etc/os-release: %m"); |
| |
| if (!isempty(image_id)) { |
| free_and_replace(arg_entry_token, image_id); |
| arg_entry_token_type = ARG_ENTRY_TOKEN_OS_IMAGE_ID; |
| } else if (!isempty(id)) { |
| free_and_replace(arg_entry_token, id); |
| arg_entry_token_type = ARG_ENTRY_TOKEN_OS_ID; |
| } else |
| return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No machine ID set, and /etc/os-release carries no ID=/IMAGE_ID= fields."); |
| } else { |
| r = free_and_strdup_warn(&arg_entry_token, SD_ID128_TO_STRING(arg_machine_id)); |
| if (r < 0) |
| return r; |
| |
| arg_entry_token_type = ARG_ENTRY_TOKEN_MACHINE_ID; |
| } |
| |
| break; |
| } |
| |
| case ARG_ENTRY_TOKEN_MACHINE_ID: |
| if (sd_id128_is_null(arg_machine_id)) |
| return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No machine ID set."); |
| |
| r = free_and_strdup_warn(&arg_entry_token, SD_ID128_TO_STRING(arg_machine_id)); |
| if (r < 0) |
| return r; |
| |
| break; |
| |
| case ARG_ENTRY_TOKEN_OS_IMAGE_ID: { |
| _cleanup_free_ char *buf = NULL; |
| |
| r = parse_os_release(arg_root, "IMAGE_ID", &buf); |
| if (r < 0) |
| return log_error_errno(r, "Failed to load /etc/os-release: %m"); |
| |
| if (isempty(buf)) |
| return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "IMAGE_ID= field not set in /etc/os-release."); |
| |
| free_and_replace(arg_entry_token, buf); |
| break; |
| } |
| |
| case ARG_ENTRY_TOKEN_OS_ID: { |
| _cleanup_free_ char *buf = NULL; |
| |
| r = parse_os_release(arg_root, "ID", &buf); |
| if (r < 0) |
| return log_error_errno(r, "Failed to load /etc/os-release: %m"); |
| |
| if (isempty(buf)) |
| return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "ID= field not set in /etc/os-release."); |
| |
| free_and_replace(arg_entry_token, buf); |
| break; |
| } |
| |
| case ARG_ENTRY_TOKEN_LITERAL: |
| assert(!isempty(arg_entry_token)); /* already filled in by command line parser */ |
| break; |
| } |
| |
| if (isempty(arg_entry_token) || !(utf8_is_valid(arg_entry_token) && string_is_safe(arg_entry_token))) |
| return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Selected entry token not valid: %s", arg_entry_token); |
| |
| log_debug("Using entry token: %s", arg_entry_token); |
| return 0; |
| } |