mirror of
https://github.com/systemd/systemd.git
synced 2024-12-22 17:35:35 +03:00
vmspawn: add support for --bind(-ro)=
This commit is contained in:
parent
7fe9c9909d
commit
a8f940c4b5
@ -262,7 +262,6 @@
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
</refsect2>
|
||||
|
||||
<refsect2>
|
||||
@ -290,6 +289,28 @@
|
||||
</variablelist>
|
||||
</refsect2>
|
||||
|
||||
<refsect2>
|
||||
<title>Mount Options</title>
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--bind=</option><replaceable>PATH</replaceable></term>
|
||||
<term><option>--bind-ro=</option><replaceable>PATH</replaceable></term>
|
||||
|
||||
<listitem><para>Mount a directory from the host into the virtual machine. Takes one of: a path
|
||||
argument — in which case the specified path will be mounted from the host to the same path in the virtual machine, or
|
||||
a colon-separated pair of paths — in which case the first specified path is the source in the host, and the
|
||||
second path is the destination in the virtual machine. If the source path is not absolute, it is resolved
|
||||
relative to the current working directory. The <option>--bind-ro=</option> option creates read-only bind mounts.
|
||||
Backslash escapes are interpreted, so <literal>\:</literal> may be used to embed colons in either path.
|
||||
This option may be specified multiple times for creating multiple independent bind mount points.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect2>
|
||||
|
||||
<refsect2>
|
||||
<title>Credentials</title>
|
||||
|
||||
|
@ -4,6 +4,7 @@ libvmspawn_core_sources = files(
|
||||
'vmspawn-settings.c',
|
||||
'vmspawn-util.c',
|
||||
'vmspawn-scope.c',
|
||||
'vmspawn-mount.c',
|
||||
)
|
||||
libvmspawn_core = static_library(
|
||||
'vmspawn-core',
|
||||
|
67
src/vmspawn/vmspawn-mount.c
Normal file
67
src/vmspawn/vmspawn-mount.c
Normal file
@ -0,0 +1,67 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "extract-word.h"
|
||||
#include "macro.h"
|
||||
#include "parse-argument.h"
|
||||
#include "path-util.h"
|
||||
#include "string-util.h"
|
||||
#include "vmspawn-mount.h"
|
||||
|
||||
static void runtime_mount_done(RuntimeMount *mount) {
|
||||
assert(mount);
|
||||
|
||||
mount->source = mfree(mount->source);
|
||||
mount->target = mfree(mount->target);
|
||||
}
|
||||
|
||||
void runtime_mount_context_done(RuntimeMountContext *ctx) {
|
||||
assert(ctx);
|
||||
|
||||
FOREACH_ARRAY(mount, ctx->mounts, ctx->n_mounts)
|
||||
runtime_mount_done(mount);
|
||||
|
||||
free(ctx->mounts);
|
||||
}
|
||||
|
||||
int runtime_mount_parse(RuntimeMountContext *ctx, const char *s, bool read_only) {
|
||||
_cleanup_(runtime_mount_done) RuntimeMount mount = { .read_only = read_only };
|
||||
_cleanup_free_ char *source_rel = NULL;
|
||||
int r;
|
||||
|
||||
assert(ctx);
|
||||
|
||||
r = extract_first_word(&s, &source_rel, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (isempty(source_rel))
|
||||
return -EINVAL;
|
||||
|
||||
r = path_make_absolute_cwd(source_rel, &mount.source);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* virtiofsd only supports directories */
|
||||
r = is_dir(mount.source, /* follow= */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (!r)
|
||||
return -ENOTDIR;
|
||||
|
||||
mount.target = s ? strdup(s) : TAKE_PTR(source_rel);
|
||||
if (!mount.target)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!path_is_absolute(mount.target))
|
||||
return -EINVAL;
|
||||
|
||||
if (!GREEDY_REALLOC(ctx->mounts, ctx->n_mounts + 1))
|
||||
return log_oom();
|
||||
|
||||
ctx->mounts[ctx->n_mounts++] = TAKE_STRUCT(mount);
|
||||
|
||||
return 0;
|
||||
}
|
19
src/vmspawn/vmspawn-mount.h
Normal file
19
src/vmspawn/vmspawn-mount.h
Normal file
@ -0,0 +1,19 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef struct RuntimeMount {
|
||||
bool read_only;
|
||||
char *source;
|
||||
char *target;
|
||||
} RuntimeMount;
|
||||
|
||||
typedef struct RuntimeMountContext {
|
||||
RuntimeMount *mounts;
|
||||
size_t n_mounts;
|
||||
} RuntimeMountContext;
|
||||
|
||||
void runtime_mount_context_done(RuntimeMountContext *ctx);
|
||||
int runtime_mount_parse(RuntimeMountContext *ctx, const char *s, bool read_only);
|
@ -5,6 +5,7 @@
|
||||
|
||||
typedef enum SettingsMask {
|
||||
SETTING_START_MODE = UINT64_C(1) << 0,
|
||||
SETTING_BIND_MOUNTS = UINT64_C(1) << 11,
|
||||
SETTING_DIRECTORY = UINT64_C(1) << 26,
|
||||
SETTING_CREDENTIALS = UINT64_C(1) << 30,
|
||||
_SETTING_FORCE_ENUM_WIDTH = UINT64_MAX
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "dissect-image.h"
|
||||
#include "escape.h"
|
||||
#include "event-util.h"
|
||||
#include "extract-word.h"
|
||||
#include "fileio.h"
|
||||
#include "format-util.h"
|
||||
#include "fs-util.h"
|
||||
@ -46,6 +47,7 @@
|
||||
#include "strv.h"
|
||||
#include "tmpfile-util.h"
|
||||
#include "unit-name.h"
|
||||
#include "vmspawn-mount.h"
|
||||
#include "vmspawn-scope.h"
|
||||
#include "vmspawn-settings.h"
|
||||
#include "vmspawn-util.h"
|
||||
@ -68,6 +70,7 @@ static QemuNetworkStack arg_network_stack = QEMU_NET_NONE;
|
||||
static int arg_secure_boot = -1;
|
||||
static MachineCredentialContext arg_credentials = {};
|
||||
static uid_t arg_uid_shift = UID_INVALID, arg_uid_range = 0x10000U;
|
||||
static RuntimeMountContext arg_runtime_mounts = {};
|
||||
static SettingsMask arg_settings_mask = 0;
|
||||
static char *arg_firmware = NULL;
|
||||
static char *arg_runtime_directory = NULL;
|
||||
@ -84,6 +87,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_credentials, machine_credential_context_done);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_firmware, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_linux, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_initrd, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_runtime_mounts, runtime_mount_context_done);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_kernel_cmdline_extra, strv_freep);
|
||||
|
||||
static int help(void) {
|
||||
@ -127,6 +131,12 @@ static int help(void) {
|
||||
" --private-users=UIDBASE[:NUIDS]\n"
|
||||
" Configure the UID/GID range to map into the\n"
|
||||
" virtiofsd namespace\n"
|
||||
"\n%3$sMounts:%4$s\n"
|
||||
" --bind=SOURCE[:TARGET]\n"
|
||||
" Mount a file or directory from the host into\n"
|
||||
" the VM.\n"
|
||||
" --bind-ro=SOURCE[:TARGET]\n"
|
||||
" Similar, but creates a read-only mount\n"
|
||||
"\n%3$sCredentials:%4$s\n"
|
||||
" --set-credential=ID:VALUE\n"
|
||||
" Pass a credential with literal value to the\n"
|
||||
@ -159,6 +169,8 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_INITRD,
|
||||
ARG_QEMU_GUI,
|
||||
ARG_NETWORK_USER_MODE,
|
||||
ARG_BIND,
|
||||
ARG_BIND_RO,
|
||||
ARG_SECURE_BOOT,
|
||||
ARG_PRIVATE_USERS,
|
||||
ARG_SET_CREDENTIAL,
|
||||
@ -185,6 +197,8 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "qemu-gui", no_argument, NULL, ARG_QEMU_GUI },
|
||||
{ "network-tap", no_argument, NULL, 'n' },
|
||||
{ "network-user-mode", no_argument, NULL, ARG_NETWORK_USER_MODE },
|
||||
{ "bind", required_argument, NULL, ARG_BIND },
|
||||
{ "bind-ro", required_argument, NULL, ARG_BIND_RO },
|
||||
{ "secure-boot", required_argument, NULL, ARG_SECURE_BOOT },
|
||||
{ "private-users", required_argument, NULL, ARG_PRIVATE_USERS },
|
||||
{ "set-credential", required_argument, NULL, ARG_SET_CREDENTIAL },
|
||||
@ -212,7 +226,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
break;
|
||||
|
||||
case 'D':
|
||||
r = parse_path_argument(optarg, false, &arg_directory);
|
||||
r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_directory);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -316,6 +330,15 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_network_stack = QEMU_NET_USER;
|
||||
break;
|
||||
|
||||
case ARG_BIND:
|
||||
case ARG_BIND_RO:
|
||||
r = runtime_mount_parse(&arg_runtime_mounts, optarg, c == ARG_BIND_RO);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse --bind(-ro)= argument %s: %m", optarg);
|
||||
|
||||
arg_settings_mask |= SETTING_BIND_MOUNTS;
|
||||
break;
|
||||
|
||||
case ARG_SECURE_BOOT:
|
||||
r = parse_tristate(optarg, &arg_secure_boot);
|
||||
if (r < 0)
|
||||
@ -689,7 +712,7 @@ static int find_virtiofsd(char **ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int start_virtiofsd(sd_bus *bus, const char *scope, const char *directory, char **ret_state_tempdir, char **ret_sock_name) {
|
||||
static int start_virtiofsd(sd_bus *bus, const char *scope, const char *directory, bool uidmap, char **ret_state_tempdir, char **ret_sock_name) {
|
||||
_cleanup_(rm_rf_physical_and_freep) char *state_dir = NULL;
|
||||
_cleanup_free_ char *virtiofsd = NULL, *sock_name = NULL, *scope_prefix = NULL;
|
||||
_cleanup_(socket_service_pair_done) SocketServicePair ssp = {
|
||||
@ -737,7 +760,7 @@ static int start_virtiofsd(sd_bus *bus, const char *scope, const char *directory
|
||||
if (!ssp.exec_start)
|
||||
return log_oom();
|
||||
|
||||
if (arg_uid_shift != UID_INVALID) {
|
||||
if (uidmap && arg_uid_shift != UID_INVALID) {
|
||||
r = strv_extend(&ssp.exec_start, "--uid-map");
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
@ -865,7 +888,7 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
||||
return log_oom();
|
||||
|
||||
/* if we are going to be starting any units with state then create our runtime dir */
|
||||
if (arg_tpm != 0 || arg_directory) {
|
||||
if (arg_tpm != 0 || arg_directory || arg_runtime_mounts.n_mounts != 0) {
|
||||
r = runtime_directory(&arg_runtime_directory, arg_privileged ? RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER, "systemd/vmspawn");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to lookup runtime directory: %m");
|
||||
@ -888,7 +911,7 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
||||
return log_oom();
|
||||
|
||||
/* A shared memory backend might increase ram usage so only add one if actually necessary for virtiofsd. */
|
||||
if (arg_directory) {
|
||||
if (arg_directory || arg_runtime_mounts.n_mounts != 0) {
|
||||
r = strv_extend(&cmdline, "-object");
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
@ -1088,7 +1111,7 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
||||
|
||||
if (arg_directory) {
|
||||
_cleanup_free_ char *sock_path = NULL, *sock_name = NULL;
|
||||
r = start_virtiofsd(bus, trans_scope, arg_directory, &sock_path, &sock_name);
|
||||
r = start_virtiofsd(bus, trans_scope, arg_directory, /* uidmap= */ true, &sock_path, &sock_name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -1117,6 +1140,38 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
FOREACH_ARRAY(mount, arg_runtime_mounts.mounts, arg_runtime_mounts.n_mounts) {
|
||||
_cleanup_free_ char *sock_path = NULL, *sock_name = NULL, *clean_target = NULL;
|
||||
r = start_virtiofsd(bus, trans_scope, mount->source, /* uidmap= */ false, &sock_path, &sock_name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = strv_extend(&cmdline, "-chardev");
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = strv_extendf(&cmdline, "socket,id=%1$s,path=%2$s/%1$s", sock_name, sock_path);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = strv_extend(&cmdline, "-device");
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = strv_extendf(&cmdline, "vhost-user-fs-pci,queue-size=1024,chardev=%1$s,tag=%1$s", sock_name);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
clean_target = xescape(mount->target, "\":");
|
||||
if (!clean_target)
|
||||
return log_oom();
|
||||
|
||||
r = strv_extendf(&arg_kernel_cmdline_extra, "systemd.mount-extra=\"%s:%s:virtiofs:%s\"",
|
||||
sock_name, clean_target, mount->read_only ? "ro" : "rw");
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
if (ARCHITECTURE_SUPPORTS_SMBIOS) {
|
||||
_cleanup_free_ char *kcl = strv_join(arg_kernel_cmdline_extra, " ");
|
||||
if (!kcl)
|
||||
@ -1219,6 +1274,8 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
||||
log_debug("Executing: %s", joined);
|
||||
}
|
||||
|
||||
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0);
|
||||
|
||||
_cleanup_(sd_event_source_unrefp) sd_event_source *notify_event_source = NULL;
|
||||
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
|
||||
r = sd_event_new(&event);
|
||||
@ -1376,8 +1433,6 @@ static int run(int argc, char *argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0);
|
||||
|
||||
return run_virtual_machine(kvm_device_fd, vhost_device_fd);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user