@@ -226,6 +226,23 @@
|
||||
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--efi-nvram-state=<replaceable>PATH</replaceable>|auto|off</option></term>
|
||||
|
||||
<listitem><para>Configures where to place the EFI variable NVRAM state. This takes an absolute file
|
||||
system path to a regular file to persistently place the state in. If the file is missing it is
|
||||
created as needed. If set to the special string <literal>auto</literal> a persistent path is
|
||||
automatically derived from the VM image path or directory path, with the
|
||||
<literal>.efinvramstate</literal> suffix appended. If set to the special string
|
||||
<literal>off</literal> the EFI variable NVRAM state is only maintained transiently and flushed out
|
||||
when the VM shuts down. Defaults to <literal>auto</literal>.</para>
|
||||
|
||||
<para>If <option>--ephemeral</option> is specified, <literal>auto</literal> behaves like
|
||||
<literal>off</literal>.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v261"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--linux=<replaceable>PATH</replaceable></option></term>
|
||||
|
||||
|
||||
@@ -142,7 +142,9 @@ static const char auxiliary_suffixes_nulstr[] =
|
||||
".roothash.p7s\0"
|
||||
".usrhash\0"
|
||||
".usrhash.p7s\0"
|
||||
".verity\0";
|
||||
".verity\0"
|
||||
".raw.tpmstate\0"
|
||||
".raw.efinvramstate\0";
|
||||
|
||||
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(image_dirname, ImageClass);
|
||||
|
||||
|
||||
@@ -187,6 +187,7 @@ shared_sources = files(
|
||||
'socket-netlink.c',
|
||||
'specifier.c',
|
||||
'switch-root.c',
|
||||
'swtpm-util.c',
|
||||
'tar-util.c',
|
||||
'tmpfile-util-label.c',
|
||||
'tomoyo-util.c',
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sd-json.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "escape.h"
|
||||
#include "fd-util.h"
|
||||
#include "json-util.h"
|
||||
#include "log.h"
|
||||
#include "memfd-util.h"
|
||||
#include "path-util.h"
|
||||
#include "pidref.h"
|
||||
#include "process-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "swtpm-util.h"
|
||||
|
||||
int manufacture_swtpm(const char *state_dir, const char *secret) {
|
||||
int r;
|
||||
|
||||
assert(state_dir);
|
||||
|
||||
_cleanup_free_ char *swtpm_setup = NULL;
|
||||
r = find_executable("swtpm_setup", &swtpm_setup);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to find 'swtpm_setup' binary: %m");
|
||||
|
||||
_cleanup_strv_free_ char **args = strv_new(
|
||||
swtpm_setup,
|
||||
"--tpm2",
|
||||
"--print-profiles");
|
||||
if (!args)
|
||||
return log_oom();
|
||||
|
||||
_cleanup_close_ int mfd = memfd_new("swtpm-profiles");
|
||||
if (mfd < 0)
|
||||
return log_error_errno(mfd, "Failed to allocate memfd: %m");
|
||||
|
||||
if (DEBUG_LOGGING) {
|
||||
_cleanup_free_ char *cmdline = quote_command_line(args, SHELL_ESCAPE_EMPTY);
|
||||
log_debug("About to spawn: %s", strnull(cmdline));
|
||||
}
|
||||
|
||||
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
|
||||
r = pidref_safe_fork_full(
|
||||
"(swtpm-lprof)",
|
||||
(int[]) { -EBADF, mfd, STDERR_FILENO },
|
||||
/* except_fds= */ NULL,
|
||||
/* n_except_fds= */ 0,
|
||||
FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_REARRANGE_STDIO|FORK_LOG,
|
||||
&pidref);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to run swtpm_setup: %m");
|
||||
if (r == 0) {
|
||||
/* Child */
|
||||
execvp(args[0], args);
|
||||
log_error_errno(errno, "Failed to execute '%s': %m", args[0]);
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
r = pidref_wait_for_terminate_and_check("(swtpm-lprof)", &pidref, WAIT_LOG_ABNORMAL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* NB: we ignore the exit status of --print-profiles, it's broken. Instead we check if we have
|
||||
* received a valid JSON object via STDOUT. */
|
||||
(void) r;
|
||||
|
||||
_cleanup_free_ char *text = NULL;
|
||||
r = read_full_file_full(
|
||||
mfd,
|
||||
/* filename= */ NULL,
|
||||
/* offset= */ 0,
|
||||
/* size= */ SIZE_MAX,
|
||||
/* flags= */ 0,
|
||||
/* bind_name= */ NULL,
|
||||
&text,
|
||||
/* ret_size= */ NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read memory fd: %m");
|
||||
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *j = NULL;
|
||||
const char *best_profile = NULL;
|
||||
if (isempty(text))
|
||||
log_notice("No list of supported profiles could be acquired from swtpm, assuming the implementation is too old to know the concept of profiles.");
|
||||
else {
|
||||
r = sd_json_parse(text, SD_JSON_PARSE_MUST_BE_OBJECT, &j, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse swtpm's --print-profiles output: %m");
|
||||
|
||||
sd_json_variant *v = sd_json_variant_by_key(j, "builtin");
|
||||
if (v) {
|
||||
if (!sd_json_variant_is_array(v))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "'builtin' field is not an array: %m");
|
||||
|
||||
sd_json_variant *i;
|
||||
JSON_VARIANT_ARRAY_FOREACH(i, v) {
|
||||
if (!sd_json_variant_is_object(i))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Profile object is not a JSON object.");
|
||||
|
||||
sd_json_variant *n = sd_json_variant_by_key(i, "Name");
|
||||
if (!n) {
|
||||
log_debug("Object in profiles array does not have a 'Name', skipping.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!sd_json_variant_is_string(n))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Profile's 'Name' field is not a string.");
|
||||
|
||||
const char *s = sd_json_variant_string(n);
|
||||
|
||||
/* Pick the best of the default-v1, default-v2, … profiles */
|
||||
if (!startswith(s, "default-v"))
|
||||
continue;
|
||||
if (!best_profile || strverscmp_improved(s, best_profile) > 0)
|
||||
best_profile = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
strv_free(args);
|
||||
args = strv_new(swtpm_setup,
|
||||
"--tpm-state", state_dir,
|
||||
"--tpm2",
|
||||
"--pcr-banks", "sha256",
|
||||
"--ecc",
|
||||
"--createek",
|
||||
"--create-ek-cert",
|
||||
"--create-platform-cert",
|
||||
"--not-overwrite");
|
||||
if (!args)
|
||||
return log_oom();
|
||||
|
||||
if (secret && strv_extendf(&args, "--keyfile=%s", secret) < 0)
|
||||
return log_oom();
|
||||
|
||||
if (best_profile && strv_extendf(&args, "--profile-name=%s", best_profile) < 0)
|
||||
return log_oom();
|
||||
|
||||
if (DEBUG_LOGGING) {
|
||||
_cleanup_free_ char *cmdline = quote_command_line(args, SHELL_ESCAPE_EMPTY);
|
||||
log_debug("About to spawn: %s", strnull(cmdline));
|
||||
}
|
||||
|
||||
r = pidref_safe_fork("(swtpm-setup)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_LOG|FORK_WAIT, /* ret= */ NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to run swtpm_setup: %m");
|
||||
if (r == 0) {
|
||||
/* Child */
|
||||
execvp(args[0], args);
|
||||
log_error_errno(errno, "Failed to execute '%s': %m", args[0]);
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
int manufacture_swtpm(const char *state_dir, const char *secret);
|
||||
+128
-89
@@ -72,6 +72,7 @@
|
||||
#include "stdio-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "swtpm-util.h"
|
||||
#include "sync-util.h"
|
||||
#include "terminal-util.h"
|
||||
#include "tmpfile-util.h"
|
||||
@@ -88,13 +89,15 @@
|
||||
|
||||
#define VM_TAP_HASH_KEY SD_ID128_MAKE(01,d0,c6,4c,2b,df,24,fb,c0,f8,b2,09,7d,59,b2,93)
|
||||
|
||||
typedef enum TpmStateMode {
|
||||
TPM_STATE_OFF, /* keep no state around */
|
||||
TPM_STATE_AUTO, /* keep state around if not ephemeral, derive path from image/directory */
|
||||
TPM_STATE_PATH, /* explicitly specified location */
|
||||
_TPM_STATE_MODE_MAX,
|
||||
_TPM_STATE_MODE_INVALID = -EINVAL,
|
||||
} TpmStateMode;
|
||||
/* An enum controlling how auxiliary state for the VM are maintained, i.e. the TPM state and the EFI variable
|
||||
* NVRAM. */
|
||||
typedef enum StateMode {
|
||||
STATE_OFF, /* keep no state around */
|
||||
STATE_AUTO, /* keep state around if not ephemeral, derive path from image/directory */
|
||||
STATE_PATH, /* explicitly specified location */
|
||||
_STATE_MODE_MAX,
|
||||
_STATE_MODE_INVALID = -EINVAL,
|
||||
} StateMode;
|
||||
|
||||
typedef struct SSHInfo {
|
||||
unsigned cid;
|
||||
@@ -144,7 +147,9 @@ static struct ether_addr arg_network_provided_mac = {};
|
||||
static char **arg_smbios11 = NULL;
|
||||
static uint64_t arg_grow_image = 0;
|
||||
static char *arg_tpm_state_path = NULL;
|
||||
static TpmStateMode arg_tpm_state_mode = TPM_STATE_AUTO;
|
||||
static StateMode arg_tpm_state_mode = STATE_AUTO;
|
||||
static char *arg_efi_nvram_state_path = NULL;
|
||||
static StateMode arg_efi_nvram_state_mode = STATE_AUTO;
|
||||
static bool arg_ask_password = true;
|
||||
static bool arg_notify_ready = true;
|
||||
static char **arg_bind_user = NULL;
|
||||
@@ -171,6 +176,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_background, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_ssh_key_type, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_smbios11, strv_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_tpm_state_path, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_efi_nvram_state_path, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_property, strv_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_bind_user, strv_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_bind_user_shell, freep);
|
||||
@@ -209,6 +215,8 @@ static int help(void) {
|
||||
" --tpm=BOOL Enable use of a virtual TPM\n"
|
||||
" --tpm-state=off|auto|PATH\n"
|
||||
" Where to store TPM state\n"
|
||||
" --efi-nvram-state=off|auto|PATH\n"
|
||||
" Where to store EFI Variable NVRAM state\n"
|
||||
" --linux=PATH Specify the linux kernel for direct kernel boot\n"
|
||||
" --initrd=PATH Specify the initrd for direct kernel boot\n"
|
||||
" -n --network-tap Create a TAP device for networking\n"
|
||||
@@ -317,6 +325,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_CONSOLE,
|
||||
ARG_BACKGROUND,
|
||||
ARG_TPM_STATE,
|
||||
ARG_EFI_NVRAM_STATE,
|
||||
ARG_NO_ASK_PASSWORD,
|
||||
ARG_PROPERTY,
|
||||
ARG_NOTIFY_READY,
|
||||
@@ -374,6 +383,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "smbios11", required_argument, NULL, 's' },
|
||||
{ "grow-image", required_argument, NULL, 'G' },
|
||||
{ "tpm-state", required_argument, NULL, ARG_TPM_STATE },
|
||||
{ "efi-nvram-state", required_argument, NULL, ARG_EFI_NVRAM_STATE },
|
||||
{ "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
|
||||
{ "property", required_argument, NULL, ARG_PROPERTY },
|
||||
{ "notify-ready", required_argument, NULL, ARG_NOTIFY_READY },
|
||||
@@ -710,7 +720,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
arg_tpm_state_mode = TPM_STATE_PATH;
|
||||
arg_tpm_state_mode = STATE_PATH;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -720,10 +730,30 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse --tpm-state= parameter: %s", optarg);
|
||||
|
||||
arg_tpm_state_mode = r ? TPM_STATE_AUTO : TPM_STATE_OFF;
|
||||
arg_tpm_state_mode = r ? STATE_AUTO : STATE_OFF;
|
||||
arg_tpm_state_path = mfree(arg_tpm_state_path);
|
||||
break;
|
||||
|
||||
case ARG_EFI_NVRAM_STATE:
|
||||
if (path_is_valid(optarg) && (path_is_absolute(optarg) || path_startswith(optarg, "./"))) {
|
||||
r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_efi_nvram_state_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
arg_efi_nvram_state_mode = STATE_PATH;
|
||||
break;
|
||||
}
|
||||
|
||||
r = isempty(optarg) ? false :
|
||||
streq(optarg, "auto") ? true :
|
||||
parse_boolean(optarg);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse --efi-nvram-state= parameter: %s", optarg);
|
||||
|
||||
arg_efi_nvram_state_mode = r ? STATE_AUTO : STATE_OFF;
|
||||
arg_efi_nvram_state_path = mfree(arg_efi_nvram_state_path);
|
||||
break;
|
||||
|
||||
case ARG_NO_ASK_PASSWORD:
|
||||
arg_ask_password = false;
|
||||
break;
|
||||
@@ -1325,48 +1355,11 @@ static int start_tpm(
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create TPM state directory '%s': %m", state_dir);
|
||||
|
||||
_cleanup_free_ char *swtpm_setup = NULL;
|
||||
r = find_executable("swtpm_setup", &swtpm_setup);
|
||||
r = manufacture_swtpm(state_dir, /* secret= */ NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to find swtpm_setup binary: %m");
|
||||
return r;
|
||||
|
||||
/* Try passing --profile-name default-v2 first, in order to support RSA4096 pcrsig keys, which was
|
||||
* added in 0.11. */
|
||||
_cleanup_strv_free_ char **argv = strv_new(
|
||||
swtpm_setup,
|
||||
"--tpm-state", state_dir,
|
||||
"--tpm2",
|
||||
"--pcr-banks", "sha256",
|
||||
"--not-overwrite",
|
||||
"--profile-name", "default-v2");
|
||||
if (!argv)
|
||||
return log_oom();
|
||||
|
||||
r = pidref_safe_fork("(swtpm-setup)", FORK_CLOSE_ALL_FDS|FORK_LOG|FORK_WAIT, /* ret= */ NULL);
|
||||
if (r == 0) {
|
||||
/* Child */
|
||||
execvp(argv[0], argv);
|
||||
log_error_errno(errno, "Failed to execute '%s': %m", argv[0]);
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
if (r == -EPROTO) {
|
||||
/* If swtpm_setup fails, try again removing the default-v2 profile, as it might be an older
|
||||
* version. */
|
||||
strv_remove(argv, "--profile-name");
|
||||
strv_remove(argv, "default-v2");
|
||||
|
||||
r = pidref_safe_fork("(swtpm-setup)", FORK_CLOSE_ALL_FDS|FORK_LOG|FORK_WAIT, /* ret= */ NULL);
|
||||
if (r == 0) {
|
||||
/* Child */
|
||||
execvp(argv[0], argv);
|
||||
log_error_errno(errno, "Failed to execute '%s': %m", argv[0]);
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to run swtpm_setup: %m");
|
||||
|
||||
strv_free(argv);
|
||||
_cleanup_strv_free_ char **argv = NULL;
|
||||
argv = strv_new(sd_socket_activate, "--listen", listen_address, swtpm, "socket", "--tpm2", "--tpmstate");
|
||||
if (!argv)
|
||||
return log_oom();
|
||||
@@ -1948,6 +1941,30 @@ static int on_request_stop(sd_bus_message *m, void *userdata, sd_bus_error *erro
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int make_sidecar_path(const char *suffix, char **ret) {
|
||||
int r;
|
||||
|
||||
assert(suffix);
|
||||
assert(ret);
|
||||
|
||||
const char *p = ASSERT_PTR(arg_image ?: arg_directory);
|
||||
|
||||
_cleanup_free_ char *parent = NULL, *filename = NULL;
|
||||
r = path_split_prefix_filename(p, &parent, &filename);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to extract parent directory and filename from '%s': %m", p);
|
||||
|
||||
if (!strextend(&filename, suffix))
|
||||
return log_oom();
|
||||
|
||||
_cleanup_free_ char *j = path_join(parent, filename);
|
||||
if (!j)
|
||||
return log_oom();
|
||||
|
||||
*ret = TAKE_PTR(j);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
||||
_cleanup_(ovmf_config_freep) OvmfConfig *ovmf_config = NULL;
|
||||
_cleanup_free_ char *qemu_binary = NULL, *mem = NULL, *kernel = NULL;
|
||||
@@ -2366,30 +2383,67 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
_cleanup_(unlink_and_freep) char *ovmf_vars_to = NULL;
|
||||
if (ovmf_config->supports_sb) {
|
||||
const char *ovmf_vars_from = ovmf_config->vars;
|
||||
_cleanup_free_ char *escaped_ovmf_vars_to = NULL;
|
||||
_cleanup_close_ int source_fd = -EBADF, target_fd = -EBADF;
|
||||
if (arg_efi_nvram_state_mode == STATE_AUTO && !arg_ephemeral) {
|
||||
assert(!arg_efi_nvram_state_path);
|
||||
|
||||
r = tempfn_random_child(NULL, "vmspawn-", &ovmf_vars_to);
|
||||
r = make_sidecar_path(".efinvramstate", &arg_efi_nvram_state_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
source_fd = open(ovmf_vars_from, O_RDONLY|O_CLOEXEC);
|
||||
if (source_fd < 0)
|
||||
return log_error_errno(source_fd, "Failed to open OVMF vars file %s: %m", ovmf_vars_from);
|
||||
log_debug("Storing EFI NVRAM state persistently under '%s'.", arg_efi_nvram_state_path);
|
||||
}
|
||||
|
||||
target_fd = open(ovmf_vars_to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0600);
|
||||
if (target_fd < 0)
|
||||
return log_error_errno(errno, "Failed to create regular file for OVMF vars at %s: %m", ovmf_vars_to);
|
||||
_cleanup_(unlink_and_freep) char *ovmf_vars = NULL;
|
||||
if (ovmf_config->vars) {
|
||||
_cleanup_close_ int target_fd = -EBADF;
|
||||
_cleanup_(unlink_and_freep) char *destroy_path = NULL;
|
||||
bool newly_created;
|
||||
const char *state;
|
||||
if (arg_efi_nvram_state_path) {
|
||||
_cleanup_free_ char *d = strdup(arg_efi_nvram_state_path);
|
||||
if (!d)
|
||||
return log_oom();
|
||||
|
||||
r = copy_bytes(source_fd, target_fd, UINT64_MAX, COPY_REFLINK);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to copy bytes from %s to %s: %m", ovmf_vars_from, ovmf_vars_to);
|
||||
target_fd = openat_report_new(AT_FDCWD, arg_efi_nvram_state_path, O_WRONLY|O_CREAT|O_CLOEXEC, 0600, &newly_created);
|
||||
if (target_fd < 0)
|
||||
return log_error_errno(target_fd, "Failed to open file for OVMF vars at %s: %m", arg_efi_nvram_state_path);
|
||||
|
||||
/* This isn't always available so don't raise an error if it fails */
|
||||
(void) copy_times(source_fd, target_fd, 0);
|
||||
if (newly_created)
|
||||
destroy_path = TAKE_PTR(d);
|
||||
|
||||
r = fd_verify_regular(target_fd);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Not a regular file for OVMF variables at %s: %m", arg_efi_nvram_state_path);
|
||||
|
||||
state = arg_efi_nvram_state_path;
|
||||
} else {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
r = tempfn_random_child(/* p= */ NULL, "vmspawn-", &t);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create temporary filename: %m");
|
||||
|
||||
target_fd = open(t, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0600);
|
||||
if (target_fd < 0)
|
||||
return log_error_errno(errno, "Failed to create regular file for OVMF vars at %s: %m", t);
|
||||
|
||||
newly_created = true;
|
||||
state = ovmf_vars = TAKE_PTR(t);
|
||||
}
|
||||
|
||||
if (newly_created) {
|
||||
_cleanup_close_ int source_fd = open(ovmf_config->vars, O_RDONLY|O_CLOEXEC);
|
||||
if (source_fd < 0)
|
||||
return log_error_errno(errno, "Failed to open OVMF vars file %s: %m", ovmf_config->vars);
|
||||
|
||||
r = copy_bytes(source_fd, target_fd, UINT64_MAX, COPY_REFLINK);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to copy bytes from %s to %s: %m", ovmf_config->vars, state);
|
||||
|
||||
/* This isn't always available so don't raise an error if it fails */
|
||||
(void) copy_times(source_fd, target_fd, 0);
|
||||
}
|
||||
|
||||
destroy_path = mfree(destroy_path); /* disarm auto-destroy */
|
||||
|
||||
r = strv_extend_many(
|
||||
&cmdline,
|
||||
@@ -2399,11 +2453,11 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
escaped_ovmf_vars_to = escape_qemu_value(ovmf_vars_to);
|
||||
if (!escaped_ovmf_vars_to)
|
||||
_cleanup_free_ char *escaped_state = escape_qemu_value(state);
|
||||
if (!escaped_state)
|
||||
return log_oom();
|
||||
|
||||
r = strv_extendf(&cmdline, "file=%s,if=pflash,format=%s", escaped_ovmf_vars_to, ovmf_config_format(ovmf_config));
|
||||
r = strv_extendf(&cmdline, "file=%s,if=pflash,format=%s", escaped_state, ovmf_config_format(ovmf_config));
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
@@ -2669,33 +2723,18 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM not supported on %s, refusing", architecture_to_string(native_architecture()));
|
||||
if (arg_tpm < 0) {
|
||||
arg_tpm = false;
|
||||
log_debug("TPM not support on %s, disabling tpm autodetection and continuing", architecture_to_string(native_architecture()));
|
||||
log_debug("TPM not supported on %s, disabling tpm autodetection and continuing", architecture_to_string(native_architecture()));
|
||||
}
|
||||
}
|
||||
|
||||
_cleanup_free_ char *swtpm = NULL;
|
||||
if (arg_tpm != 0) {
|
||||
if (arg_tpm_state_mode == TPM_STATE_AUTO && !arg_ephemeral) {
|
||||
if (arg_tpm_state_mode == STATE_AUTO && !arg_ephemeral) {
|
||||
assert(!arg_tpm_state_path);
|
||||
|
||||
const char *p = ASSERT_PTR(arg_image ?: arg_directory);
|
||||
|
||||
_cleanup_free_ char *parent = NULL;
|
||||
r = path_extract_directory(p, &parent);
|
||||
r = make_sidecar_path(".tpmstate", &arg_tpm_state_path);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to extract parent directory from '%s': %m", p);
|
||||
|
||||
_cleanup_free_ char *filename = NULL;
|
||||
r = path_extract_filename(p, &filename);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to extract filename from '%s': %m", p);
|
||||
|
||||
if (!strextend(&filename, ".tpmstate"))
|
||||
return log_oom();
|
||||
|
||||
arg_tpm_state_path = path_join(parent, filename);
|
||||
if (!arg_tpm_state_path)
|
||||
return log_oom();
|
||||
return r;
|
||||
|
||||
log_debug("Storing TPM state persistently under '%s'.", arg_tpm_state_path);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user