From f9bf1c4c753899ea88cb20065bc8d4245bfacc02 Mon Sep 17 00:00:00 2001 From: Michael-F-Bryan Date: Thu, 9 Nov 2023 22:44:39 +0800 Subject: [PATCH] Updated `WasiRunner` to allow mounting `FileSystem` instances as well as directories on the host system --- lib/wasix/src/runners/mod.rs | 16 ++--- lib/wasix/src/runners/wasi.rs | 23 ++++-- lib/wasix/src/runners/wasi_common.rs | 102 ++++++++++++++++----------- lib/wasix/src/runners/wcgi/runner.rs | 6 +- 4 files changed, 86 insertions(+), 61 deletions(-) diff --git a/lib/wasix/src/runners/mod.rs b/lib/wasix/src/runners/mod.rs index e32221049ec..fa64d05a8cd 100644 --- a/lib/wasix/src/runners/mod.rs +++ b/lib/wasix/src/runners/mod.rs @@ -9,15 +9,7 @@ mod wasi_common; #[cfg(feature = "webc_runner_rt_wcgi")] pub mod wcgi; -pub use self::{runner::Runner, wasi_common::MappedCommand}; - -/// A directory that should be mapped from the host filesystem into a WASI -/// instance (the "guest"). -#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] -pub struct MappedDirectory { - /// The absolute path for a directory on the host filesystem. - pub host: std::path::PathBuf, - /// The absolute path specifying where the host directory should be mounted - /// inside the guest. - pub guest: String, -} +pub use self::{ + runner::Runner, + wasi_common::{MappedCommand, MappedDirectory, MountedDirectory}, +}; diff --git a/lib/wasix/src/runners/wasi.rs b/lib/wasix/src/runners/wasi.rs index 93911957bb7..d21bfaee71f 100644 --- a/lib/wasix/src/runners/wasi.rs +++ b/lib/wasix/src/runners/wasi.rs @@ -4,7 +4,7 @@ use std::{path::PathBuf, sync::Arc}; use anyhow::{Context, Error}; use tracing::Instrument; -use virtual_fs::{ArcBoxFile, TmpFileSystem, VirtualFile}; +use virtual_fs::{ArcBoxFile, FileSystem, TmpFileSystem, VirtualFile}; use wasmer::Module; use webc::metadata::{annotations::Wasi, Command}; @@ -12,7 +12,7 @@ use crate::{ bin_factory::BinaryPackage, capabilities::Capabilities, journal::{DynJournal, SnapshotTrigger}, - runners::{wasi_common::CommonWasiOptions, MappedDirectory}, + runners::{wasi_common::CommonWasiOptions, MappedDirectory, MountedDirectory}, runtime::{module_cache::ModuleHash, task_manager::VirtualTaskManagerExt}, Runtime, WasiEnvBuilder, WasiError, WasiRuntimeError, }; @@ -98,14 +98,25 @@ impl WasiRunner { self.wasi.forward_host_env = forward; } - pub fn with_mapped_directories(mut self, dirs: I) -> Self + pub fn with_mapped_directories(self, dirs: I) -> Self where I: IntoIterator, D: Into, { - self.wasi - .mapped_dirs - .extend(dirs.into_iter().map(|d| d.into())); + self.with_mounted_directories(dirs.into_iter().map(Into::into).map(MountedDirectory::from)) + } + + pub fn with_mounted_directories(mut self, dirs: I) -> Self + where + I: IntoIterator, + D: Into, + { + self.wasi.mounts.extend(dirs.into_iter().map(Into::into)); + self + } + + pub fn mount(&mut self, dest: String, fs: Arc) -> &mut Self { + self.wasi.mounts.push(MountedDirectory { guest: dest, fs }); self } diff --git a/lib/wasix/src/runners/wasi_common.rs b/lib/wasix/src/runners/wasi_common.rs index 453fa3c077c..da74542b959 100644 --- a/lib/wasix/src/runners/wasi_common.rs +++ b/lib/wasix/src/runners/wasi_common.rs @@ -32,8 +32,8 @@ pub(crate) struct CommonWasiOptions { pub(crate) args: Vec, pub(crate) env: HashMap, pub(crate) forward_host_env: bool, - pub(crate) mapped_dirs: Vec, pub(crate) mapped_host_commands: Vec, + pub(crate) mounts: Vec, pub(crate) injected_packages: Vec, pub(crate) capabilities: Capabilities, #[derivative(Debug = "ignore")] @@ -52,12 +52,11 @@ impl CommonWasiOptions { root_fs: Option, ) -> Result<(), anyhow::Error> { let root_fs = root_fs.unwrap_or_else(|| RootFileSystemBuilder::default().build()); - - let fs = prepare_filesystem(root_fs, &self.mapped_dirs, container_fs, builder)?; + let fs = prepare_filesystem(root_fs, &self.mounts, container_fs)?; builder.add_preopen_dir("/")?; - if self.mapped_dirs.iter().all(|m| m.guest != ".") { + if self.mounts.iter().all(|m| m.guest != ".") { // The user hasn't mounted "." to anything, so let's map it to "/" builder.add_map_dir(".", "/")?; } @@ -117,31 +116,24 @@ impl CommonWasiOptions { // OverlayFileSystem>; 1]>; fn build_directory_mappings( - builder: &mut WasiEnvBuilder, root_fs: &mut TmpFileSystem, - host_fs: &Arc, - mapped_dirs: &[MappedDirectory], + mounted_dirs: &[MountedDirectory], ) -> Result<(), anyhow::Error> { - for dir in mapped_dirs { - let MappedDirectory { - host: host_path, + for dir in mounted_dirs { + let MountedDirectory { guest: guest_path, + fs, } = dir; let mut guest_path = PathBuf::from(guest_path); tracing::debug!( guest=%guest_path.display(), - host=%host_path.display(), - "Mounting host folder", + "Mounting", ); if guest_path.is_relative() { guest_path = apply_relative_path_mounting_hack(&guest_path); } - let host_path = std::fs::canonicalize(host_path).with_context(|| { - format!("Unable to canonicalize host path '{}'", host_path.display()) - })?; - let guest_path = root_fs .canonicalize_unchecked(&guest_path) .with_context(|| { @@ -153,28 +145,18 @@ fn build_directory_mappings( if guest_path == Path::new("/") { root_fs - .mount_directory_entries(&guest_path, host_fs, &host_path) - .with_context(|| format!("Unable to mount \"{}\" to root", host_path.display(),))?; + .mount_directory_entries(&guest_path, fs, "/".as_ref()) + .context("Unable to mount to root")?; } else { if let Some(parent) = guest_path.parent() { - create_dir_all(root_fs, parent).with_context(|| { + create_dir_all(&*root_fs, parent).with_context(|| { format!("Unable to create the \"{}\" directory", parent.display()) })?; } root_fs - .mount(guest_path.clone(), host_fs, host_path.clone()) - .with_context(|| { - format!( - "Unable to mount \"{}\" to \"{}\"", - host_path.display(), - guest_path.display() - ) - })?; - - builder - .add_preopen_dir(&guest_path) - .with_context(|| format!("Unable to preopen \"{}\"", guest_path.display()))?; + .mount(guest_path.clone(), fs, "/".into()) + .with_context(|| format!("Unable to mount \"{}\"", guest_path.display()))?; } } @@ -183,13 +165,11 @@ fn build_directory_mappings( fn prepare_filesystem( mut root_fs: TmpFileSystem, - mapped_dirs: &[MappedDirectory], + mounted_dirs: &[MountedDirectory], container_fs: Option>, - builder: &mut WasiEnvBuilder, ) -> Result, Error> { - if !mapped_dirs.is_empty() { - let host_fs: Arc = Arc::new(crate::default_fs_backing()); - build_directory_mappings(builder, &mut root_fs, &host_fs, mapped_dirs)?; + if !mounted_dirs.is_empty() { + build_directory_mappings(&mut root_fs, mounted_dirs)?; } // HACK(Michael-F-Bryan): The WebcVolumeFileSystem only accepts relative @@ -257,6 +237,46 @@ fn create_dir_all(fs: &dyn FileSystem, path: &Path) -> Result<(), Error> { Ok(()) } +/// A directory that should be mapped from the host filesystem into a WASI +/// instance (the "guest"). +#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct MappedDirectory { + /// The absolute path for a directory on the host filesystem. + pub host: std::path::PathBuf, + /// The absolute path specifying where the host directory should be mounted + /// inside the guest. + pub guest: String, +} + +#[derive(Debug, Clone)] +pub struct MountedDirectory { + pub guest: String, + pub fs: Arc, +} + +impl From for MountedDirectory { + fn from(value: MappedDirectory) -> Self { + let MappedDirectory { host, guest } = value; + + // HACK: We don't have a FileSystem implementation that lets you mount + // just a single folder, so we're going to work around that using a mem + // fs and its mounting infrastructure. + let host_fs = Arc::new(crate::default_fs_backing()) as Arc; + let temp_fs = virtual_fs::mem_fs::FileSystem::default(); + if let Some(parent) = Path::new(&guest).parent() { + create_dir_all(&temp_fs, parent).unwrap(); + } + temp_fs + .mount(PathBuf::from(&guest), &host_fs, host) + .unwrap(); + + MountedDirectory { + guest, + fs: Arc::new(temp_fs), + } + } +} + #[derive(Debug)] struct RelativeOrAbsolutePathHack(F); @@ -330,6 +350,8 @@ mod tests { use virtual_fs::WebcVolumeFileSystem; use webc::Container; + use crate::runners::MappedDirectory; + use super::*; const PYTHON: &[u8] = include_bytes!("../../../c-api/examples/assets/python-0.1.0.wasmer"); @@ -400,17 +422,15 @@ mod tests { let sub_dir = temp.path().join("path").join("to"); std::fs::create_dir_all(&sub_dir).unwrap(); std::fs::write(sub_dir.join("file.txt"), b"Hello, World!").unwrap(); - let mapping = [MappedDirectory { + let mapping = [MountedDirectory::from(MappedDirectory { guest: "/home".to_string(), host: sub_dir, - }]; + })]; let container = Container::from_bytes(PYTHON).unwrap(); let webc_fs = WebcVolumeFileSystem::mount_all(&container); - let mut builder = WasiEnvBuilder::new(""); let root_fs = RootFileSystemBuilder::default().build(); - let fs = - prepare_filesystem(root_fs, &mapping, Some(Arc::new(webc_fs)), &mut builder).unwrap(); + let fs = prepare_filesystem(root_fs, &mapping, Some(Arc::new(webc_fs))).unwrap(); assert!(fs.metadata("/home/file.txt".as_ref()).unwrap().is_file()); assert!(fs.metadata("lib".as_ref()).unwrap().is_dir()); diff --git a/lib/wasix/src/runners/wcgi/runner.rs b/lib/wasix/src/runners/wcgi/runner.rs index 882e8ade5bf..529181cde07 100644 --- a/lib/wasix/src/runners/wcgi/runner.rs +++ b/lib/wasix/src/runners/wcgi/runner.rs @@ -237,7 +237,7 @@ impl Config { } pub fn map_directory(&mut self, dir: MappedDirectory) -> &mut Self { - self.wasi.mapped_dirs.push(dir); + self.wasi.mounts.push(dir.into()); self } @@ -245,7 +245,9 @@ impl Config { &mut self, mappings: impl IntoIterator, ) -> &mut Self { - self.wasi.mapped_dirs.extend(mappings.into_iter()); + for mapping in mappings { + self.map_directory(mapping); + } self }