From f47133b229f420d82fa0a2abe06589d172049ec6 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 9 Jun 2020 20:19:20 +0200 Subject: [PATCH] Allow different Handles to act as stdio (#1600) * Allow any type which implements Handle to act as stdio There have been requests to allow more than just raw OS handles to act as stdio in `wasi-common`. This commit makes this possible by loosening the requirement of the `WasiCtxBuilder` to accept any type `T: Handle + 'static` to act as any of the stdio handles. A couple words about correctness of this approach. Currently, since we only have a single `Handle` super-trait to represent all possible WASI handle types (files, dirs, stdio, pipes, virtual, etc.), it is possible to pass in any type to act as stdio which can be wrong. However, I envision this being a problem only in the near(est) future until we work out how to split `Handle` into several traits, each representing a different type of WASI resource. In this particular case, this would be a resource which would implement the interface required for a handle to act as a stdio (with appropriate rights, etc.). * Use OsFile in c-api * Add some documention to the types exposed by this PR, and a few others Signed-off-by: Jakub Konka * Add construction examples and missing docs for Handle trait * Fix example on Windows * Merge wasi_preview_builder into create_preview1_instance Co-authored-by: Pat Hickey --- crates/c-api/src/wasi.rs | 120 ++++++++++-------- .../test-programs/tests/wasm_tests/runtime.rs | 7 +- crates/wasi-common/src/ctx.rs | 27 ++-- crates/wasi-common/src/entry.rs | 1 + crates/wasi-common/src/handle.rs | 47 +++++-- crates/wasi-common/src/lib.rs | 4 + crates/wasi-common/src/sys/osdir.rs | 2 +- crates/wasi-common/src/sys/osfile.rs | 19 ++- crates/wasi-common/src/sys/osother.rs | 20 ++- crates/wasi-common/src/sys/unix/bsd/osdir.rs | 23 +++- .../wasi-common/src/sys/unix/linux/osdir.rs | 22 +++- crates/wasi-common/src/sys/unix/osdir.rs | 2 +- crates/wasi-common/src/sys/unix/oshandle.rs | 2 +- crates/wasi-common/src/sys/windows/osdir.rs | 21 ++- .../wasi-common/src/sys/windows/oshandle.rs | 2 +- crates/wasi-common/src/virtfs.rs | 4 + 16 files changed, 234 insertions(+), 89 deletions(-) diff --git a/crates/c-api/src/wasi.rs b/crates/c-api/src/wasi.rs index 4a7ed6c30a..6daad6aaef 100644 --- a/crates/c-api/src/wasi.rs +++ b/crates/c-api/src/wasi.rs @@ -201,63 +201,77 @@ enum WasiInstance { Snapshot0(WasiSnapshot0), } -macro_rules! config_to_builder { - ($builder:ident, $config:ident) => {{ - let mut builder = $builder::new(); - - if $config.inherit_args { - builder.inherit_args(); - } else if !$config.args.is_empty() { - builder.args($config.args); - } - - if $config.inherit_env { - builder.inherit_env(); - } else if !$config.env.is_empty() { - builder.envs($config.env); - } - - if $config.inherit_stdin { - builder.inherit_stdin(); - } else if let Some(file) = $config.stdin { - builder.stdin(file); - } - - if $config.inherit_stdout { - builder.inherit_stdout(); - } else if let Some(file) = $config.stdout { - builder.stdout(file); - } - - if $config.inherit_stderr { - builder.inherit_stderr(); - } else if let Some(file) = $config.stderr { - builder.stderr(file); - } - - for preopen in $config.preopens { - builder.preopened_dir(preopen.0, preopen.1); - } - - builder - }}; -} - -fn create_snapshot0_instance(store: &Store, config: wasi_config_t) -> Result { +fn create_snapshot0_instance(store: &Store, config: wasi_config_t) -> Result { + let mut builder = WasiSnapshot0CtxBuilder::new(); + if config.inherit_args { + builder.inherit_args(); + } else if !config.args.is_empty() { + builder.args(config.args); + } + if config.inherit_env { + builder.inherit_env(); + } else if !config.env.is_empty() { + builder.envs(config.env); + } + if config.inherit_stdin { + builder.inherit_stdin(); + } else if let Some(file) = config.stdin { + builder.stdin(file); + } + if config.inherit_stdout { + builder.inherit_stdout(); + } else if let Some(file) = config.stdout { + builder.stdout(file); + } + if config.inherit_stderr { + builder.inherit_stderr(); + } else if let Some(file) = config.stderr { + builder.stderr(file); + } + for preopen in config.preopens { + builder.preopened_dir(preopen.0, preopen.1); + } Ok(WasiInstance::Snapshot0(WasiSnapshot0::new( store, - config_to_builder!(WasiSnapshot0CtxBuilder, config) - .build() - .map_err(|e| e.to_string())?, + builder.build()?, ))) } -fn create_preview1_instance(store: &Store, config: wasi_config_t) -> Result { +fn create_preview1_instance(store: &Store, config: wasi_config_t) -> Result { + use std::convert::TryFrom; + use wasi_common::OsFile; + let mut builder = WasiPreview1CtxBuilder::new(); + if config.inherit_args { + builder.inherit_args(); + } else if !config.args.is_empty() { + builder.args(config.args); + } + if config.inherit_env { + builder.inherit_env(); + } else if !config.env.is_empty() { + builder.envs(config.env); + } + if config.inherit_stdin { + builder.inherit_stdin(); + } else if let Some(file) = config.stdin { + builder.stdin(OsFile::try_from(file)?); + } + if config.inherit_stdout { + builder.inherit_stdout(); + } else if let Some(file) = config.stdout { + builder.stdout(OsFile::try_from(file)?); + } + if config.inherit_stderr { + builder.inherit_stderr(); + } else if let Some(file) = config.stderr { + builder.stderr(OsFile::try_from(file)?); + } + for preopen in config.preopens { + builder.preopened_dir(preopen.0, preopen.1); + } Ok(WasiInstance::Preview1(WasiPreview1::new( store, - config_to_builder!(WasiPreview1CtxBuilder, config) - .build() - .map_err(|e| e.to_string())?, + builder.build()?, ))) } @@ -286,8 +300,10 @@ pub unsafe extern "C" fn wasi_instance_new( let store = &store.store; let result = match CStr::from_ptr(name).to_str().unwrap_or("") { - "wasi_snapshot_preview1" => create_preview1_instance(store, *config), - "wasi_unstable" => create_snapshot0_instance(store, *config), + "wasi_snapshot_preview1" => { + create_preview1_instance(store, *config).map_err(|e| e.to_string()) + } + "wasi_unstable" => create_snapshot0_instance(store, *config).map_err(|e| e.to_string()), _ => Err("unsupported WASI version".into()), }; diff --git a/crates/test-programs/tests/wasm_tests/runtime.rs b/crates/test-programs/tests/wasm_tests/runtime.rs index 0b4ba305e0..f10e94456c 100644 --- a/crates/test-programs/tests/wasm_tests/runtime.rs +++ b/crates/test-programs/tests/wasm_tests/runtime.rs @@ -1,7 +1,8 @@ use anyhow::Context; +use std::convert::TryFrom; use std::fs::File; use std::path::Path; -use wasi_common::VirtualDirEntry; +use wasi_common::{OsOther, VirtualDirEntry}; use wasmtime::{Linker, Module, Store}; #[derive(Clone, Copy, Debug)] @@ -46,7 +47,9 @@ pub fn instantiate( // where `stdin` is never ready to be read. In some CI systems, however, // stdin is closed which causes tests to fail. let (reader, _writer) = os_pipe::pipe()?; - builder.stdin(reader_to_file(reader)); + let file = reader_to_file(reader); + let handle = OsOther::try_from(file).context("failed to create OsOther from PipeReader")?; + builder.stdin(handle); let snapshot1 = wasmtime_wasi::Wasi::new(&store, builder.build()?); let mut linker = Linker::new(&store); diff --git a/crates/wasi-common/src/ctx.rs b/crates/wasi-common/src/ctx.rs index 895c96ad6f..3121cb1462 100644 --- a/crates/wasi-common/src/ctx.rs +++ b/crates/wasi-common/src/ctx.rs @@ -47,7 +47,7 @@ type WasiCtxBuilderResult = std::result::Result; enum PendingEntry { Thunk(fn() -> io::Result>), - OsHandle(File), + Handle(Box), } impl std::fmt::Debug for PendingEntry { @@ -58,7 +58,7 @@ impl std::fmt::Debug for PendingEntry { "PendingEntry::Thunk({:p})", f as *const fn() -> io::Result> ), - Self::OsHandle(f) => write!(fmt, "PendingEntry::OsHandle({:?})", f), + Self::Handle(handle) => write!(fmt, "PendingEntry::Handle({:p})", handle), } } } @@ -247,21 +247,21 @@ impl WasiCtxBuilder { self } - /// Provide a File to use as stdin - pub fn stdin(&mut self, file: File) -> &mut Self { - self.stdin = Some(PendingEntry::OsHandle(file)); + /// Provide a `Handle` to use as stdin + pub fn stdin(&mut self, handle: T) -> &mut Self { + self.stdin = Some(PendingEntry::Handle(Box::new(handle))); self } - /// Provide a File to use as stdout - pub fn stdout(&mut self, file: File) -> &mut Self { - self.stdout = Some(PendingEntry::OsHandle(file)); + /// Provide a `Handle` to use as stdout + pub fn stdout(&mut self, handle: T) -> &mut Self { + self.stdout = Some(PendingEntry::Handle(Box::new(handle))); self } - /// Provide a File to use as stderr - pub fn stderr(&mut self, file: File) -> &mut Self { - self.stderr = Some(PendingEntry::OsHandle(file)); + /// Provide a `Handle` to use as stderr + pub fn stderr(&mut self, handle: T) -> &mut Self { + self.stderr = Some(PendingEntry::Handle(Box::new(handle))); self } @@ -368,9 +368,8 @@ impl WasiCtxBuilder { .insert(entry) .ok_or(WasiCtxBuilderError::TooManyFilesOpen)? } - PendingEntry::OsHandle(f) => { - let handle = OsOther::try_from(f)?; - let handle = EntryHandle::new(handle); + PendingEntry::Handle(handle) => { + let handle = EntryHandle::from(handle); let entry = Entry::new(handle); entries .insert(entry) diff --git a/crates/wasi-common/src/entry.rs b/crates/wasi-common/src/entry.rs index eb311a7b10..ba9e90c160 100644 --- a/crates/wasi-common/src/entry.rs +++ b/crates/wasi-common/src/entry.rs @@ -8,6 +8,7 @@ use std::rc::Rc; pub(crate) struct EntryHandle(Rc); impl EntryHandle { + #[allow(dead_code)] pub(crate) fn new(handle: T) -> Self { Self(Rc::new(handle)) } diff --git a/crates/wasi-common/src/handle.rs b/crates/wasi-common/src/handle.rs index 574db3de11..8cdf6a4c57 100644 --- a/crates/wasi-common/src/handle.rs +++ b/crates/wasi-common/src/handle.rs @@ -6,38 +6,49 @@ use std::io::{self, SeekFrom}; /// Represents rights of a `Handle`, either already held or required. #[derive(Debug, Copy, Clone)] -pub(crate) struct HandleRights { +pub struct HandleRights { pub(crate) base: Rights, pub(crate) inheriting: Rights, } impl HandleRights { - pub(crate) fn new(base: Rights, inheriting: Rights) -> Self { + /// Creates new `HandleRights` instance from `base` and `inheriting` rights. + pub fn new(base: Rights, inheriting: Rights) -> Self { Self { base, inheriting } } - /// Create new `HandleRights` instance from `base` rights only, keeping + /// Creates new `HandleRights` instance from `base` rights only, keeping /// `inheriting` set to none. - pub(crate) fn from_base(base: Rights) -> Self { + pub fn from_base(base: Rights) -> Self { Self { base, inheriting: Rights::empty(), } } - /// Create new `HandleRights` instance with both `base` and `inheriting` + /// Creates new `HandleRights` instance with both `base` and `inheriting` /// rights set to none. - pub(crate) fn empty() -> Self { + pub fn empty() -> Self { Self { base: Rights::empty(), inheriting: Rights::empty(), } } - /// Check if `other` is a subset of those rights. - pub(crate) fn contains(&self, other: &Self) -> bool { + /// Checks if `other` is a subset of those rights. + pub fn contains(&self, other: &Self) -> bool { self.base.contains(&other.base) && self.inheriting.contains(&other.inheriting) } + + /// Returns base rights. + pub fn base(&self) -> Rights { + self.base + } + + /// Returns inheriting rights. + pub fn inheriting(&self) -> Rights { + self.inheriting + } } impl fmt::Display for HandleRights { @@ -50,7 +61,25 @@ impl fmt::Display for HandleRights { } } -pub(crate) trait Handle { +/// Generic interface for all WASI-compatible handles. We currently group these into two groups: +/// * OS-based resources (actual, real resources): `OsFile`, `OsDir`, `OsOther`, and `Stdio`, +/// * virtual files and directories: VirtualDir`, and `InMemoryFile`. +/// +/// # Constructing `Handle`s representing OS-based resources +/// +/// Each type of handle can either be constructed directly (see docs entry for a specific handle +/// type such as `OsFile`), or you can let the `wasi_common` crate's machinery work it out +/// automatically for you using `std::convert::TryInto` from `std::fs::File`: +/// +/// ```rust,no_run +/// use std::convert::TryInto; +/// use wasi_common::Handle; +/// use std::fs::OpenOptions; +/// +/// let some_file = OpenOptions::new().read(true).open("some_file").unwrap(); +/// let wasi_handle: Box = some_file.try_into().unwrap(); +/// ``` +pub trait Handle { fn as_any(&self) -> &dyn Any; fn try_clone(&self) -> io::Result>; fn get_file_type(&self) -> types::Filetype; diff --git a/crates/wasi-common/src/lib.rs b/crates/wasi-common/src/lib.rs index 4c0ccf7b4e..f9c17841a9 100644 --- a/crates/wasi-common/src/lib.rs +++ b/crates/wasi-common/src/lib.rs @@ -36,5 +36,9 @@ mod virtfs; pub mod wasi; pub use ctx::{WasiCtx, WasiCtxBuilder, WasiCtxBuilderError}; +pub use handle::{Handle, HandleRights}; +pub use sys::osdir::OsDir; +pub use sys::osfile::OsFile; +pub use sys::osother::{OsOther, OsOtherExt}; pub use sys::preopen_dir; pub use virtfs::{FileContents, VirtualDirEntry}; diff --git a/crates/wasi-common/src/sys/osdir.rs b/crates/wasi-common/src/sys/osdir.rs index b1051b8862..6ba3cc63a9 100644 --- a/crates/wasi-common/src/sys/osdir.rs +++ b/crates/wasi-common/src/sys/osdir.rs @@ -10,7 +10,7 @@ use std::ops::Deref; // TODO could this be cleaned up? // The actual `OsDir` struct is OS-dependent, therefore we delegate // its definition to OS-specific modules. -pub(crate) use super::sys_impl::osdir::OsDir; +pub use super::sys_impl::osdir::OsDir; impl Deref for OsDir { type Target = RawOsHandle; diff --git a/crates/wasi-common/src/sys/osfile.rs b/crates/wasi-common/src/sys/osfile.rs index a3491f5432..f07ddf6240 100644 --- a/crates/wasi-common/src/sys/osfile.rs +++ b/crates/wasi-common/src/sys/osfile.rs @@ -9,7 +9,24 @@ use std::io::{self, Read, Seek, SeekFrom, Write}; use std::ops::Deref; #[derive(Debug)] -pub(crate) struct OsFile { +/// A file backed by the operating system's file system. Dereferences to a +/// `RawOsHandle`. Its impl of `Handle` uses Rust's `std` to implement all +/// file descriptor operations. +/// +/// # Constructing `OsFile` +/// +/// `OsFile` can currently only be constructed from `std::fs::File` using +/// the `std::convert::TryFrom` trait: +/// +/// ```rust,no_run +/// use std::fs::OpenOptions; +/// use std::convert::TryFrom; +/// use wasi_common::OsFile; +/// +/// let file = OpenOptions::new().read(true).open("some_file").unwrap(); +/// let os_file = OsFile::try_from(file).unwrap(); +/// ``` +pub struct OsFile { rights: Cell, handle: RawOsHandle, } diff --git a/crates/wasi-common/src/sys/osother.rs b/crates/wasi-common/src/sys/osother.rs index de30a4f2dd..314d91dea3 100644 --- a/crates/wasi-common/src/sys/osother.rs +++ b/crates/wasi-common/src/sys/osother.rs @@ -10,7 +10,9 @@ use std::fs::File; use std::io::{self, Read, Write}; use std::ops::Deref; -pub(crate) trait OsOtherExt { +/// Extra methods for `OsOther` that are only available when configured for +/// some operating systems. +pub trait OsOtherExt { /// Create `OsOther` as `dyn Handle` from null device. fn from_null() -> io::Result>; } @@ -20,8 +22,22 @@ pub(crate) trait OsOtherExt { /// sockets, streams, etc. As such, when redirecting stdio within `WasiCtxBuilder`, the redirected /// pipe should be encapsulated within this instance _and not_ `OsFile` which represents a regular /// OS file. +/// +/// # Constructing `OsOther` +/// +/// `OsOther` can currently only be constructed from `std::fs::File` using +/// the `std::convert::TryFrom` trait: +/// +/// ```rust,no_run +/// use std::fs::OpenOptions; +/// use std::convert::TryFrom; +/// use wasi_common::OsOther; +/// +/// let pipe = OpenOptions::new().read(true).open("a_pipe").unwrap(); +/// let os_other = OsOther::try_from(pipe).unwrap(); +/// ``` #[derive(Debug)] -pub(crate) struct OsOther { +pub struct OsOther { file_type: Filetype, rights: Cell, handle: RawOsHandle, diff --git a/crates/wasi-common/src/sys/unix/bsd/osdir.rs b/crates/wasi-common/src/sys/unix/bsd/osdir.rs index 0726ad2ef6..ed665329d6 100644 --- a/crates/wasi-common/src/sys/unix/bsd/osdir.rs +++ b/crates/wasi-common/src/sys/unix/bsd/osdir.rs @@ -6,7 +6,24 @@ use std::io; use yanix::dir::Dir; #[derive(Debug)] -pub(crate) struct OsDir { +/// A directory in the operating system's file system. Its impl of `Handle` is +/// in `sys::osdir`. This type is exposed to all other modules as +/// `sys::osdir::OsDir` when configured. +/// +/// # Constructing `OsDir` +/// +/// `OsDir` can currently only be constructed from `std::fs::File` using +/// the `std::convert::TryFrom` trait: +/// +/// ```rust,no_run +/// use std::fs::OpenOptions; +/// use std::convert::TryFrom; +/// use wasi_common::OsDir; +/// +/// let dir = OpenOptions::new().read(true).open("some_dir").unwrap(); +/// let os_dir = OsDir::try_from(dir).unwrap(); +/// ``` +pub struct OsDir { pub(crate) rights: Cell, pub(crate) handle: RawOsHandle, // When the client makes a `fd_readdir` syscall on this descriptor, @@ -39,7 +56,9 @@ impl OsDir { stream_ptr, }) } - /// Returns the `Dir` stream pointer associated with this `OsDir`. + /// Returns the `Dir` stream pointer associated with this `OsDir`. Duck + /// typing: sys::unix::fd::readdir expects the configured OsDir to have + /// this method. pub(crate) fn stream_ptr(&self) -> Result> { Ok(self.stream_ptr.borrow_mut()) } diff --git a/crates/wasi-common/src/sys/unix/linux/osdir.rs b/crates/wasi-common/src/sys/unix/linux/osdir.rs index 2fff99ec7b..c803178c6a 100644 --- a/crates/wasi-common/src/sys/unix/linux/osdir.rs +++ b/crates/wasi-common/src/sys/unix/linux/osdir.rs @@ -6,7 +6,24 @@ use std::io; use yanix::dir::Dir; #[derive(Debug)] -pub(crate) struct OsDir { +/// A directory in the operating system's file system. Its impl of `Handle` is +/// in `sys::osdir`. This type is exposed to all other modules as +/// `sys::osdir::OsDir` when configured. +/// +/// # Constructing `OsDir` +/// +/// `OsDir` can currently only be constructed from `std::fs::File` using +/// the `std::convert::TryFrom` trait: +/// +/// ```rust,no_run +/// use std::fs::OpenOptions; +/// use std::convert::TryFrom; +/// use wasi_common::OsDir; +/// +/// let dir = OpenOptions::new().read(true).open("some_dir").unwrap(); +/// let os_dir = OsDir::try_from(dir).unwrap(); +/// ``` +pub struct OsDir { pub(crate) rights: Cell, pub(crate) handle: RawOsHandle, } @@ -16,7 +33,8 @@ impl OsDir { let rights = Cell::new(rights); Ok(Self { rights, handle }) } - /// Returns the `Dir` stream pointer associated with this `OsDir`. + /// Returns the `Dir` stream pointer associated with this `OsDir`. Duck typing: + /// sys::unix::fd::readdir expects the configured OsDir to have this method. pub(crate) fn stream_ptr(&self) -> Result> { // We need to duplicate the handle, because `opendir(3)`: // After a successful call to fdopendir(), fd is used internally by the implementation, diff --git a/crates/wasi-common/src/sys/unix/osdir.rs b/crates/wasi-common/src/sys/unix/osdir.rs index 47b264208f..6c4ba35655 100644 --- a/crates/wasi-common/src/sys/unix/osdir.rs +++ b/crates/wasi-common/src/sys/unix/osdir.rs @@ -6,7 +6,7 @@ use std::fs::File; use std::io; use std::os::unix::prelude::{AsRawFd, FromRawFd, IntoRawFd}; -pub(crate) use super::sys_impl::osdir::OsDir; +pub use super::sys_impl::osdir::OsDir; impl TryFrom for OsDir { type Error = io::Error; diff --git a/crates/wasi-common/src/sys/unix/oshandle.rs b/crates/wasi-common/src/sys/unix/oshandle.rs index 513e4b7787..b706a5bb0f 100644 --- a/crates/wasi-common/src/sys/unix/oshandle.rs +++ b/crates/wasi-common/src/sys/unix/oshandle.rs @@ -3,7 +3,7 @@ use std::io; use std::os::unix::prelude::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; #[derive(Debug)] -pub(crate) struct RawOsHandle(File); +pub struct RawOsHandle(File); impl RawOsHandle { /// Tries clone `self`. diff --git a/crates/wasi-common/src/sys/windows/osdir.rs b/crates/wasi-common/src/sys/windows/osdir.rs index 80cee50bce..ee32730525 100644 --- a/crates/wasi-common/src/sys/windows/osdir.rs +++ b/crates/wasi-common/src/sys/windows/osdir.rs @@ -8,7 +8,26 @@ use std::io; use std::os::windows::prelude::{AsRawHandle, FromRawHandle, IntoRawHandle}; #[derive(Debug)] -pub(crate) struct OsDir { +/// A directory in the operating system's file system. Its impl of `Handle` is +/// in `sys::osdir`. This type is exposed to all other modules as +/// `sys::osdir::OsDir` when configured. +/// +/// # Constructing `OsDir` +/// +/// `OsDir` can currently only be constructed from `std::fs::File` using +/// the `std::convert::TryFrom` trait: +/// +/// ```rust,no_run +/// use std::fs::OpenOptions; +/// use std::convert::TryFrom; +/// use std::os::windows::fs::OpenOptionsExt; +/// use wasi_common::OsDir; +/// use winapi::um::winbase::FILE_FLAG_BACKUP_SEMANTICS; +/// +/// let dir = OpenOptions::new().read(true).attributes(FILE_FLAG_BACKUP_SEMANTICS).open("some_dir").unwrap(); +/// let os_dir = OsDir::try_from(dir).unwrap(); +/// ``` +pub struct OsDir { pub(crate) rights: Cell, pub(crate) handle: RawOsHandle, } diff --git a/crates/wasi-common/src/sys/windows/oshandle.rs b/crates/wasi-common/src/sys/windows/oshandle.rs index 89884d03a8..74aacb8019 100644 --- a/crates/wasi-common/src/sys/windows/oshandle.rs +++ b/crates/wasi-common/src/sys/windows/oshandle.rs @@ -6,7 +6,7 @@ use std::mem::ManuallyDrop; use std::os::windows::prelude::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; #[derive(Debug)] -pub(crate) struct RawOsHandle(Cell); +pub struct RawOsHandle(Cell); impl RawOsHandle { /// Tries cloning `self`. diff --git a/crates/wasi-common/src/virtfs.rs b/crates/wasi-common/src/virtfs.rs index 3d9b61b197..141a9cc693 100644 --- a/crates/wasi-common/src/virtfs.rs +++ b/crates/wasi-common/src/virtfs.rs @@ -11,12 +11,16 @@ use std::io::SeekFrom; use std::path::{Path, PathBuf}; use std::rc::Rc; +/// An entry in a virtual filesystem pub enum VirtualDirEntry { + /// The contents of a child directory Directory(HashMap), + /// A file File(Box), } impl VirtualDirEntry { + /// Construct an empty directory pub fn empty_directory() -> Self { Self::Directory(HashMap::new()) }