diff --git a/lib/vfs/src/lib.rs b/lib/vfs/src/lib.rs index 580905e5983..c41c285aca4 100644 --- a/lib/vfs/src/lib.rs +++ b/lib/vfs/src/lib.rs @@ -2,6 +2,7 @@ use std::any::Any; use std::ffi::OsString; use std::fmt; use std::io; +use std::ops::Deref; use std::path::{Path, PathBuf}; use std::pin::Pin; use std::task::Context; @@ -71,6 +72,27 @@ pub trait FileSystem: fmt::Debug + Send + Sync + 'static + Upcastable { fn remove_file(&self, path: &Path) -> Result<()>; fn new_open_options(&self) -> OpenOptions; + + /// Create a directory and all its parent directories. + fn create_dir_all(&self, path: &Path) -> Result<()> { + create_dir_all(self, path) + } +} + +fn create_dir_all(fs: &(impl FileSystem + ?Sized), path: &Path) -> Result<()> { + match fs.create_dir(path) { + Ok(_) | Err(FsError::AlreadyExists) => Ok(()), + Err(FsError::EntryNotFound) => { + // Note: paths don't normally have many segments, so it should be + // fine to use recursion here. This function should also be elegible + // for TCO. + match path.parent() { + Some(parent) => create_dir_all(fs, parent), + None => Err(FsError::EntryNotFound), + } + } + Err(e) => Err(e), + } } impl dyn FileSystem + 'static { @@ -84,6 +106,40 @@ impl dyn FileSystem + 'static { } } +impl FileSystem for D +where + D: Deref + std::fmt::Debug + Send + Sync + 'static, + F: FileSystem + ?Sized, +{ + fn read_dir(&self, path: &Path) -> Result { + (**self).read_dir(path) + } + + fn create_dir(&self, path: &Path) -> Result<()> { + (**self).create_dir(path) + } + + fn remove_dir(&self, path: &Path) -> Result<()> { + (**self).remove_dir(path) + } + + fn rename(&self, from: &Path, to: &Path) -> Result<()> { + (**self).rename(from, to) + } + + fn metadata(&self, path: &Path) -> Result { + (**self).metadata(path) + } + + fn remove_file(&self, path: &Path) -> Result<()> { + (**self).remove_file(path) + } + + fn new_open_options(&self) -> OpenOptions { + (**self).new_open_options() + } +} + pub trait FileOpener { fn open( &self, diff --git a/lib/wasi/src/runners/container.rs b/lib/wasi/src/runners/container.rs index 955e77ea070..a41830b7ddf 100644 --- a/lib/wasi/src/runners/container.rs +++ b/lib/wasi/src/runners/container.rs @@ -111,10 +111,10 @@ impl WapmContainer { /// Get the entire container as a single filesystem and a list of suggested /// directories to preopen. - pub(crate) fn container_fs(&self) -> Arc { + pub(crate) fn container_fs(&self) -> Box { match &self.repr { - Repr::V1Mmap(mapped) => Arc::new(WebcFileSystem::init_all(Arc::clone(mapped))), - Repr::V1Owned(owned) => Arc::new(WebcFileSystem::init_all(Arc::clone(owned))), + Repr::V1Mmap(mapped) => Box::new(WebcFileSystem::init_all(Arc::clone(mapped))), + Repr::V1Owned(owned) => Box::new(WebcFileSystem::init_all(Arc::clone(owned))), } } } diff --git a/lib/wasi/src/runners/wasi.rs b/lib/wasi/src/runners/wasi.rs index f00bd28ec89..902ebd555c4 100644 --- a/lib/wasi/src/runners/wasi.rs +++ b/lib/wasi/src/runners/wasi.rs @@ -194,13 +194,25 @@ fn unioned_filesystem( mapped_dirs: &[MappedDirectory], container: &WapmContainer, ) -> Result { - let root_fs = RootFileSystemBuilder::new().build(); + let mut fs = wasmer_vfs::UnionFileSystem::new(); - let filesystem = container.container_fs(); - root_fs.union(&filesystem); + // First, mount the root filesystem so we get things like "/dev/" + fs.mount( + "base", + "/", + false, + Box::new(RootFileSystemBuilder::new().build()), + None, + ); + // We also want the container's volumes + fs.mount("webc", "/", false, container.container_fs(), None); + + // Now, let's merge in the host volumes. if !mapped_dirs.is_empty() { - let fs_backing: Arc = + let mapped_fs = wasmer_vfs::TmpFileSystem::new(); + + let host_fs: Arc = Arc::new(PassthruFileSystem::new(crate::default_fs_backing())); for MappedDirectory { host, guest } in mapped_dirs.iter() { @@ -214,8 +226,12 @@ fn unioned_filesystem( "mounting host directory", ); - root_fs - .mount(guest.clone(), &fs_backing, host.clone()) + if let Some(parent) = guest.parent() { + mapped_fs.create_dir_all(parent.as_ref())?; + } + + mapped_fs + .mount(guest.clone(), &host_fs, host.clone()) .with_context(|| { format!( "Unable to mount \"{}\" to \"{}\"", @@ -224,9 +240,11 @@ fn unioned_filesystem( ) })?; } + + fs.mount("host", "/", true, Box::new(mapped_fs), None); } - Ok(root_fs) + Ok(fs) } #[cfg(test)]