Add support for sharing host directories into the VM via QEMU 9p (virtio-9p). The --mount flag can be repeated. The format is `<host-path>` or `<host-path>:<guest-path>` Relative guest paths are relative to the configured WORKDIR. Design: - QEMU: each --mount gets a short 9p tag (slim0, slim1, …) via -virtfs local,path=…,mount_tag=slimN,security_model=mapped-xattr. Tags are kept short because 9p mount_tag has a ~31-byte limit. - Kernel cmdline: the full destination path is passed as slim.mount=<tag>:<base64(path)> so the init script knows where to mount each tag. Base64 avoids issues with spaces/special chars. - slim-init.sh: after networking, parse slim.mount= entries, mkdir -p the destination, and mount -t 9p <tag> <dest> -o trans=virtio,version=9p2000.L Tests verify: 9p share detection via sysfs mount_tag, 9p entry in mount output, file content accessible at the expected path, and clean VM exit.
This commit is contained in:
1 parent
6111bbeb8d
commit
3a99da196d
5 files changed
+192
-7
No files matched your search
+38
@@ -32,6 +32,13 @@ pub struct RunCmd {
|
||||
/// cmdline as slim.cmd=<b64>). Overrides the CMD inferred at build time.
|
||||
#[clap(long)]
|
||||
cmd: Option<String>,
|
||||
|
||||
/// Share a host directory into the VM via 9p. Format:
|
||||
/// `<host-path>` or `<host-path>:<guest-path>`. When the guest path is
|
||||
/// omitted, the host path is used. Non-absolute guest paths are
|
||||
/// relative to the image's WORKINGDIR. Can be repeated.
|
||||
#[clap(long)]
|
||||
mount: Vec<String>,
|
||||
}
|
||||
|
||||
pub(crate) fn run(
|
||||
@@ -40,6 +47,7 @@ pub(crate) fn run(
|
||||
memory,
|
||||
forward,
|
||||
cmd,
|
||||
mount,
|
||||
}: RunCmd,
|
||||
) -> Result<()> {
|
||||
let reg_dir = registry_dir(&name)?;
|
||||
@@ -81,6 +89,35 @@ pub(crate) fn run(
|
||||
cmdline.push(format!("slim.cmd={encoded}"));
|
||||
}
|
||||
|
||||
// Build 9p shares for each --mount. Tags are short (slim0, slim1, …)
|
||||
// because 9p mount_tag has a ~31-byte limit. The guest destination path
|
||||
// is passed on the kernel cmdline as slim.mount=<tag>:<base64(guest_path)>.
|
||||
// If no guest path is specified, the host path is used as the guest path.
|
||||
let mut virtfs_args: Vec<String> = Vec::new();
|
||||
for (i, spec) in mount.iter().enumerate() {
|
||||
let (host_path, guest_path) = match spec.split_once(':') {
|
||||
Some((h, g)) => (h, g),
|
||||
None => (spec.as_str(), spec.as_str()),
|
||||
};
|
||||
let canonical = std::path::Path::new(host_path)
|
||||
.canonicalize()
|
||||
.with_context(|| format!("Cannot resolve mount path '{host_path}'"))?;
|
||||
let host_str = canonical
|
||||
.to_str()
|
||||
.context("Mount path is not valid UTF-8")?;
|
||||
let tag = format!("slim{i}");
|
||||
let dest_b64 = base64::engine::general_purpose::STANDARD.encode(guest_path.as_bytes());
|
||||
|
||||
// QEMU's QemuOpts splits on commas — escape literal commas in the
|
||||
// path as ",," per QEMU convention.
|
||||
let host_escaped = host_str.replace(',', ",,");
|
||||
virtfs_args.push("-virtfs".into());
|
||||
virtfs_args.push(format!(
|
||||
"local,path={host_escaped},mount_tag={tag},security_model=mapped-xattr"
|
||||
));
|
||||
cmdline.push(format!("slim.mount={tag}:{dest_b64}"));
|
||||
}
|
||||
|
||||
println!("Booting {name} from registry: {}", reg_dir.display());
|
||||
|
||||
let cmdline = cmdline.join(" ");
|
||||
@@ -101,6 +138,7 @@ pub(crate) fn run(
|
||||
.arg("-kernel")
|
||||
.arg(vmlinuz_path.as_os_str())
|
||||
.args(&fs_args)
|
||||
.args(&virtfs_args)
|
||||
.args(["-snapshot"])
|
||||
.args(["-no-reboot"])
|
||||
.args(["-append", &cmdline])
|
||||
|
||||
Reference in new issue
Block a user