bootctl: override is_efi_boot() check by --variables= option (#40413)
Fixes #40392.
This commit is contained in:
+17
-11
@@ -319,20 +319,26 @@ int efi_set_variable_string(const char *variable, const char *value) {
|
||||
return efi_set_variable(variable, u16, (char16_strlen(u16) + 1) * sizeof(char16_t));
|
||||
}
|
||||
|
||||
bool is_efi_boot(void) {
|
||||
static int cache = -1;
|
||||
static int cache_efi_boot = -1;
|
||||
|
||||
if (cache < 0) {
|
||||
if (detect_container() > 0)
|
||||
cache = false;
|
||||
else {
|
||||
cache = access("/sys/firmware/efi/", F_OK) >= 0;
|
||||
if (!cache && errno != ENOENT)
|
||||
log_debug_errno(errno, "Unable to test whether /sys/firmware/efi/ exists, assuming EFI not available: %m");
|
||||
}
|
||||
bool set_efi_boot(bool b) {
|
||||
return (cache_efi_boot = b);
|
||||
}
|
||||
|
||||
bool is_efi_boot(void) {
|
||||
if (cache_efi_boot >= 0)
|
||||
return cache_efi_boot;
|
||||
|
||||
if (detect_container() > 0)
|
||||
return (cache_efi_boot = false);
|
||||
|
||||
if (access("/sys/firmware/efi/", F_OK) < 0) {
|
||||
if (errno != ENOENT)
|
||||
log_debug_errno(errno, "Unable to test whether /sys/firmware/efi/ exists, assuming EFI not available: %m");
|
||||
return (cache_efi_boot = false);
|
||||
}
|
||||
|
||||
return cache;
|
||||
return (cache_efi_boot = true);
|
||||
}
|
||||
|
||||
static int read_flag(const char *variable) {
|
||||
|
||||
@@ -45,6 +45,7 @@ int efi_get_variable_path(const char *variable, char **ret);
|
||||
int efi_set_variable(const char *variable, const void *value, size_t size) _nonnull_if_nonzero_(2, 3);
|
||||
int efi_set_variable_string(const char *variable, const char *value);
|
||||
|
||||
bool set_efi_boot(bool b);
|
||||
bool is_efi_boot(void);
|
||||
bool is_efi_secure_boot(void);
|
||||
SecureBootMode efi_get_secure_boot_mode(void);
|
||||
@@ -71,6 +72,10 @@ static inline int efi_set_variable_string(const char *variable, const char *p) {
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline bool set_efi_boot(bool b) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool is_efi_boot(void) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "alloc-util.h"
|
||||
#include "bootctl.h"
|
||||
#include "bootctl-random-seed.h"
|
||||
#include "bootctl-util.h"
|
||||
#include "efivars.h"
|
||||
#include "env-util.h"
|
||||
#include "fd-util.h"
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "sd-varlink.h"
|
||||
|
||||
#include "bootctl-reboot-to-firmware.h"
|
||||
#include "bootctl-util.h"
|
||||
#include "efi-api.h"
|
||||
#include "errno-util.h"
|
||||
#include "log.h"
|
||||
@@ -14,6 +15,10 @@
|
||||
int verb_reboot_to_firmware(int argc, char *argv[], void *userdata) {
|
||||
int r;
|
||||
|
||||
r = verify_touch_variables_allowed(argv[0]);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (argc < 2) {
|
||||
r = efi_get_reboot_to_firmware();
|
||||
if (r > 0) {
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <uchar.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "bootctl.h"
|
||||
#include "bootctl-set-efivar.h"
|
||||
#include "bootctl-util.h"
|
||||
#include "efi-loader.h"
|
||||
#include "efivars.h"
|
||||
#include "log.h"
|
||||
#include "stdio-util.h"
|
||||
#include "time-util.h"
|
||||
#include "utf8.h"
|
||||
#include "virt.h"
|
||||
|
||||
static int parse_timeout(const char *arg1, char16_t **ret_timeout, size_t *ret_timeout_size) {
|
||||
char buf[DECIMAL_STR_MAX(usec_t)];
|
||||
@@ -133,37 +132,9 @@ static int parse_loader_entry_target_arg(const char *arg1, char16_t **ret_target
|
||||
int verb_set_efivar(int argc, char *argv[], void *userdata) {
|
||||
int r;
|
||||
|
||||
/* Note: changing EFI variables is the primary purpose of these verbs, hence unlike in the other
|
||||
* verbs that might touch EFI variables where we skip things gracefully, here we fail loudly if we
|
||||
* are not run on EFI or EFI variable modifications were turned off. */
|
||||
|
||||
if (arg_touch_variables < 0) {
|
||||
if (arg_root)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"Acting on %s, refusing EFI variable setup.",
|
||||
arg_image ? "image" : "root directory");
|
||||
|
||||
if (detect_container() > 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"'%s' operation not supported in a container.",
|
||||
argv[0]);
|
||||
if (!is_efi_boot())
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"Not booted with UEFI.");
|
||||
|
||||
if (access(EFIVAR_PATH(EFI_LOADER_VARIABLE_STR("LoaderInfo")), F_OK) < 0) {
|
||||
if (errno == ENOENT) {
|
||||
log_error_errno(errno, "Not booted with a supported boot loader.");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return log_error_errno(errno, "Failed to detect whether boot loader supports '%s' operation: %m", argv[0]);
|
||||
}
|
||||
|
||||
} else if (!arg_touch_variables)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"'%s' operation cannot be combined with --variables=no.",
|
||||
argv[0]);
|
||||
r = verify_touch_variables_allowed(argv[0]);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
const char *variable;
|
||||
int (* arg_parser)(const char *, char16_t **, size_t *);
|
||||
|
||||
@@ -38,38 +38,51 @@ static int status_entries(
|
||||
|
||||
printf("%sBoot Loader Entry Locations:%s\n", ansi_underline(), ansi_normal());
|
||||
|
||||
printf(" ESP: %s (", esp_path);
|
||||
if (!sd_id128_is_null(esp_partition_uuid))
|
||||
printf("/dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR "",
|
||||
bool need_paren = false;
|
||||
printf(" ESP: %s", esp_path);
|
||||
if (!sd_id128_is_null(esp_partition_uuid)) {
|
||||
printf(" (/dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR "",
|
||||
SD_ID128_FORMAT_VAL(esp_partition_uuid));
|
||||
if (!xbootldr_path)
|
||||
need_paren = true;
|
||||
}
|
||||
if (!xbootldr_path) {
|
||||
if (!need_paren) {
|
||||
fputs(" (", stdout);
|
||||
need_paren = true;
|
||||
} else
|
||||
fputs(", ", stdout);
|
||||
|
||||
/* ESP is $BOOT if XBOOTLDR not present. */
|
||||
printf(", %s$BOOT%s", ansi_green(), ansi_normal());
|
||||
printf(")");
|
||||
printf("%s$BOOT%s", ansi_green(), ansi_normal());
|
||||
}
|
||||
if (need_paren)
|
||||
putchar(')');
|
||||
putchar('\n');
|
||||
|
||||
if (config->loader_conf_status != 0) {
|
||||
assert(esp_path);
|
||||
printf("\n config: %s%s/%s%s",
|
||||
printf(" config: %s%s/%s%s",
|
||||
ansi_grey(), esp_path, ansi_normal(), "/loader/loader.conf");
|
||||
if (config->loader_conf_status < 0)
|
||||
printf(": %s%s%s",
|
||||
config->loader_conf_status == -ENOENT ? ansi_grey() : ansi_highlight_yellow(),
|
||||
STRERROR(config->loader_conf_status),
|
||||
ansi_normal());
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
if (xbootldr_path) {
|
||||
printf("\n XBOOTLDR: %s (", xbootldr_path);
|
||||
printf(" XBOOTLDR: %s (", xbootldr_path);
|
||||
if (!sd_id128_is_null(xbootldr_partition_uuid))
|
||||
printf("/dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR ", ",
|
||||
SD_ID128_FORMAT_VAL(xbootldr_partition_uuid));
|
||||
/* XBOOTLDR is always $BOOT if present. */
|
||||
printf("%s$BOOT%s)", ansi_green(), ansi_normal());
|
||||
printf("%s$BOOT%s)\n", ansi_green(), ansi_normal());
|
||||
}
|
||||
|
||||
if (settle_entry_token() >= 0)
|
||||
printf("\n token: %s", arg_entry_token);
|
||||
printf("\n\n");
|
||||
printf(" token: %s\n", arg_entry_token);
|
||||
putchar('\n');
|
||||
|
||||
if (config->default_entry < 0)
|
||||
printf("%zu entries, no entry could be determined as default.\n", config->n_entries);
|
||||
@@ -318,6 +331,8 @@ int verb_status(int argc, char *argv[], void *userdata) {
|
||||
dev_t esp_devid = 0, xbootldr_devid = 0;
|
||||
int r, k;
|
||||
|
||||
bool has_efi = touch_variables();
|
||||
|
||||
r = acquire_esp(/* unprivileged_mode= */ -1,
|
||||
/* graceful= */ false,
|
||||
/* ret_part= */ NULL,
|
||||
@@ -359,13 +374,14 @@ int verb_status(int argc, char *argv[], void *userdata) {
|
||||
|
||||
pager_open(arg_pager_flags);
|
||||
|
||||
if (arg_root)
|
||||
log_debug("Skipping 'System' section, operating offline.");
|
||||
else if (!is_efi_boot())
|
||||
printf("%sSystem:%s\n"
|
||||
"Not booted with EFI\n\n",
|
||||
ansi_underline(), ansi_normal());
|
||||
else {
|
||||
if (!has_efi) {
|
||||
if (arg_root)
|
||||
log_debug("Skipping 'System' section, operating offline.");
|
||||
else
|
||||
printf("%sSystem:%s\n"
|
||||
"Not booted with EFI\n\n",
|
||||
ansi_underline(), ansi_normal());
|
||||
} else {
|
||||
static const struct {
|
||||
uint64_t flag;
|
||||
const char *name;
|
||||
@@ -584,7 +600,7 @@ int verb_status(int argc, char *argv[], void *userdata) {
|
||||
if (arg_esp_path)
|
||||
RET_GATHER(r, status_binaries(arg_esp_path, esp_uuid));
|
||||
|
||||
if (!arg_root && is_efi_boot())
|
||||
if (has_efi)
|
||||
RET_GATHER(r, status_variables());
|
||||
|
||||
if (arg_esp_path || arg_xbootldr_path) {
|
||||
@@ -615,6 +631,8 @@ int verb_list(int argc, char *argv[], void *userdata) {
|
||||
* Here we're interested in the latter but not the former, hence request the mode, and log about
|
||||
* EACCES. */
|
||||
|
||||
(void) touch_variables();
|
||||
|
||||
r = acquire_esp(/* unprivileged_mode= */ -1, /* graceful= */ false, NULL, NULL, NULL, NULL, &esp_devid);
|
||||
if (r == -EACCES) /* We really need the ESP path for this call, hence also log about access errors */
|
||||
return log_error_errno(r, "Failed to determine ESP location: %m");
|
||||
|
||||
@@ -2,17 +2,86 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "boot-entry.h"
|
||||
#include "bootctl.h"
|
||||
#include "bootctl-util.h"
|
||||
#include "efivars.h"
|
||||
#include "errno-util.h"
|
||||
#include "fileio.h"
|
||||
#include "log.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-util.h"
|
||||
#include "sync-util.h"
|
||||
#include "virt.h"
|
||||
|
||||
bool touch_variables(void) {
|
||||
/* If we run in a container or on a non-EFI system, automatically turn off EFI file system access,
|
||||
* unless explicitly overridden. */
|
||||
|
||||
if (arg_touch_variables >= 0)
|
||||
return set_efi_boot(arg_touch_variables);
|
||||
|
||||
if (arg_root) {
|
||||
log_once(LOG_NOTICE,
|
||||
"Operating on %s, skipping EFI variable modifications.",
|
||||
arg_image ? "image" : "root directory");
|
||||
return set_efi_boot(false);
|
||||
}
|
||||
|
||||
if (!is_efi_boot()) { /* NB: this internally checks if we run in a container */
|
||||
log_once(LOG_NOTICE,
|
||||
"Not booted with EFI or running in a container, skipping EFI variable modifications.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int verify_touch_variables_allowed(const char *command) {
|
||||
/* Note: changing EFI variables is the primary purpose of these verbs, hence unlike in the other
|
||||
* verbs that might touch EFI variables where we skip things gracefully, here we fail loudly if we
|
||||
* are not run on EFI or EFI variable modifications were turned off. */
|
||||
|
||||
if (arg_touch_variables > 0) {
|
||||
/* If we explicitly allowed to touch EFI variables, then skip the is_efi_boot() checks used
|
||||
* at various places. */
|
||||
set_efi_boot(true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (arg_touch_variables == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"'%s' operation cannot be combined with --variables=no.",
|
||||
command);
|
||||
|
||||
if (arg_root)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"Acting on %s, refusing EFI variable setup.",
|
||||
arg_image ? "image" : "root directory");
|
||||
|
||||
if (detect_container() > 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"'%s' operation not supported in a container.",
|
||||
command);
|
||||
|
||||
if (!is_efi_boot())
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"Not booted with UEFI.");
|
||||
|
||||
if (access(EFIVAR_PATH(EFI_LOADER_VARIABLE_STR("LoaderInfo")), F_OK) < 0) {
|
||||
if (errno == ENOENT) {
|
||||
log_error_errno(errno, "Not booted with a supported boot loader.");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return log_error_errno(errno, "Failed to detect whether boot loader supports '%s' operation: %m", command);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sync_everything(void) {
|
||||
int r = 0, k;
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
bool touch_variables(void);
|
||||
int verify_touch_variables_allowed(const char *command);
|
||||
|
||||
int sync_everything(void);
|
||||
|
||||
const char* get_efi_arch(void);
|
||||
|
||||
+7
-25
@@ -145,8 +145,8 @@ int acquire_xbootldr(
|
||||
int r;
|
||||
|
||||
r = find_xbootldr_and_warn(arg_root, arg_xbootldr_path, unprivileged_mode, &np, ret_uuid, ret_devid);
|
||||
if (r == -ENOKEY) {
|
||||
log_debug_errno(r, "Didn't find an XBOOTLDR partition, using the ESP as $BOOT.");
|
||||
if (r == -ENOKEY || path_equal(np, arg_esp_path)) {
|
||||
log_debug("Didn't find an XBOOTLDR partition, using the ESP as $BOOT.");
|
||||
arg_xbootldr_path = mfree(arg_xbootldr_path);
|
||||
|
||||
if (ret_uuid)
|
||||
@@ -229,29 +229,6 @@ static int print_loader_or_stub_path(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool touch_variables(void) {
|
||||
/* If we run in a container or on a non-EFI system, automatically turn off EFI file system access,
|
||||
* unless explicitly overridden. */
|
||||
|
||||
if (arg_touch_variables >= 0)
|
||||
return arg_touch_variables;
|
||||
|
||||
if (arg_root) {
|
||||
log_once(LOG_NOTICE,
|
||||
"Operating on %s, skipping EFI variable modifications.",
|
||||
arg_image ? "image" : "root directory");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_efi_boot()) { /* NB: this internally checks if we run in a container */
|
||||
log_once(LOG_NOTICE,
|
||||
"Not booted with EFI or running in a container, skipping EFI variable modifications.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
GracefulMode arg_graceful(void) {
|
||||
static bool chroot_checked = false;
|
||||
|
||||
@@ -524,6 +501,11 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
r = parse_tristate_argument_with_auto("--variables=", optarg, &arg_touch_variables);
|
||||
if (r < 0)
|
||||
return r;
|
||||
#if !ENABLE_EFI
|
||||
if (arg_touch_variables > 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"Compiled without support for EFI, --variables=%s cannot be specified.", optarg);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case ARG_NO_VARIABLES:
|
||||
|
||||
@@ -59,8 +59,6 @@ GracefulMode arg_graceful(void);
|
||||
int acquire_esp(int unprivileged_mode, bool graceful, uint32_t *ret_part, uint64_t *ret_pstart, uint64_t *ret_psize, sd_id128_t *ret_uuid, dev_t *ret_devid);
|
||||
int acquire_xbootldr(int unprivileged_mode, sd_id128_t *ret_uuid, dev_t *ret_devid);
|
||||
|
||||
bool touch_variables(void);
|
||||
|
||||
/* EFI_BOOT_OPTION_DESCRIPTION_MAX sets the maximum length for the boot option description
|
||||
* stored in NVRAM. The UEFI spec does not specify a minimum or maximum length for this
|
||||
* string, but we limit the length to something reasonable to prevent from the firmware
|
||||
|
||||
Reference in New Issue
Block a user