From d6e5001e7f58e316b12df8d300e9eef4d244c29d Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 25 Apr 2020 20:53:46 +0200 Subject: [PATCH 1/6] 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.). --- crates/c-api/src/wasi.rs | 117 +++++++++++------- .../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 | 30 +++-- crates/wasi-common/src/lib.rs | 4 + crates/wasi-common/src/sys/osdir.rs | 2 +- crates/wasi-common/src/sys/osfile.rs | 2 +- crates/wasi-common/src/sys/osother.rs | 4 +- crates/wasi-common/src/sys/unix/bsd/osdir.rs | 2 +- .../wasi-common/src/sys/unix/linux/osdir.rs | 2 +- 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 | 2 +- .../wasi-common/src/sys/windows/oshandle.rs | 2 +- 15 files changed, 124 insertions(+), 82 deletions(-) diff --git a/crates/c-api/src/wasi.rs b/crates/c-api/src/wasi.rs index 4a7ed6c30a52..6faf2f8f025d 100644 --- a/crates/c-api/src/wasi.rs +++ b/crates/c-api/src/wasi.rs @@ -201,62 +201,85 @@ 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 { + 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().map_err(|e| e.to_string())?, ))) } +fn wasi_preview_builder(config: wasi_config_t) -> Result { + use std::convert::TryFrom; + use wasi_common::OsOther; + 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(OsOther::try_from(file)?); + } + if config.inherit_stdout { + builder.inherit_stdout(); + } else if let Some(file) = config.stdout { + builder.stdout(OsOther::try_from(file)?); + } + if config.inherit_stderr { + builder.inherit_stderr(); + } else if let Some(file) = config.stderr { + builder.stderr(OsOther::try_from(file)?); + } + for preopen in config.preopens { + builder.preopened_dir(preopen.0, preopen.1); + } + Ok(builder) +} + fn create_preview1_instance(store: &Store, config: wasi_config_t) -> Result { Ok(WasiInstance::Preview1(WasiPreview1::new( store, - config_to_builder!(WasiPreview1CtxBuilder, config) - .build() + wasi_preview_builder(config) + .and_then(|mut b| { + let b = b.build()?; + Ok(b) + }) .map_err(|e| e.to_string())?, ))) } diff --git a/crates/test-programs/tests/wasm_tests/runtime.rs b/crates/test-programs/tests/wasm_tests/runtime.rs index 0b4ba305e098..f10e94456cc5 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 895c96ad6f17..3121cb14623c 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 eb311a7b10b2..ba9e90c16089 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 574db3de1112..0f6bd080da0e 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,8 @@ impl fmt::Display for HandleRights { } } -pub(crate) trait Handle { +// TODO docs +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 4c0ccf7b4e0a..f9c17841a97a 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 b1051b886208..6ba3cc63a92c 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 a3491f5432d9..e9ced03fc673 100644 --- a/crates/wasi-common/src/sys/osfile.rs +++ b/crates/wasi-common/src/sys/osfile.rs @@ -9,7 +9,7 @@ use std::io::{self, Read, Seek, SeekFrom, Write}; use std::ops::Deref; #[derive(Debug)] -pub(crate) struct OsFile { +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 de30a4f2dd0a..42f15c579b09 100644 --- a/crates/wasi-common/src/sys/osother.rs +++ b/crates/wasi-common/src/sys/osother.rs @@ -10,7 +10,7 @@ use std::fs::File; use std::io::{self, Read, Write}; use std::ops::Deref; -pub(crate) trait OsOtherExt { +pub trait OsOtherExt { /// Create `OsOther` as `dyn Handle` from null device. fn from_null() -> io::Result>; } @@ -21,7 +21,7 @@ pub(crate) trait OsOtherExt { /// pipe should be encapsulated within this instance _and not_ `OsFile` which represents a regular /// OS file. #[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 0726ad2ef6ed..7baa1939ed02 100644 --- a/crates/wasi-common/src/sys/unix/bsd/osdir.rs +++ b/crates/wasi-common/src/sys/unix/bsd/osdir.rs @@ -6,7 +6,7 @@ use std::io; use yanix::dir::Dir; #[derive(Debug)] -pub(crate) struct OsDir { +pub struct OsDir { pub(crate) rights: Cell, pub(crate) handle: RawOsHandle, // When the client makes a `fd_readdir` syscall on this descriptor, diff --git a/crates/wasi-common/src/sys/unix/linux/osdir.rs b/crates/wasi-common/src/sys/unix/linux/osdir.rs index 2fff99ec7bbf..f15d89a4c5f6 100644 --- a/crates/wasi-common/src/sys/unix/linux/osdir.rs +++ b/crates/wasi-common/src/sys/unix/linux/osdir.rs @@ -6,7 +6,7 @@ use std::io; use yanix::dir::Dir; #[derive(Debug)] -pub(crate) struct OsDir { +pub struct OsDir { pub(crate) rights: Cell, pub(crate) handle: RawOsHandle, } diff --git a/crates/wasi-common/src/sys/unix/osdir.rs b/crates/wasi-common/src/sys/unix/osdir.rs index 47b264208fa5..6c4ba35655fe 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 513e4b7787ae..b706a5bb0f11 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 80cee50bce55..132bdceee433 100644 --- a/crates/wasi-common/src/sys/windows/osdir.rs +++ b/crates/wasi-common/src/sys/windows/osdir.rs @@ -8,7 +8,7 @@ use std::io; use std::os::windows::prelude::{AsRawHandle, FromRawHandle, IntoRawHandle}; #[derive(Debug)] -pub(crate) struct OsDir { +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 89884d03a8d9..74aacb80192b 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`. From 3dcb495315d0fe77c7104d75be6a123440dacda6 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 7 Jun 2020 08:47:10 +0200 Subject: [PATCH 2/6] Use OsFile in c-api --- crates/c-api/src/wasi.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/c-api/src/wasi.rs b/crates/c-api/src/wasi.rs index 6faf2f8f025d..9ff6e3cb0e58 100644 --- a/crates/c-api/src/wasi.rs +++ b/crates/c-api/src/wasi.rs @@ -239,7 +239,7 @@ fn create_snapshot0_instance(store: &Store, config: wasi_config_t) -> Result Result { use std::convert::TryFrom; - use wasi_common::OsOther; + use wasi_common::OsFile; let mut builder = WasiPreview1CtxBuilder::new(); if config.inherit_args { builder.inherit_args(); @@ -254,17 +254,17 @@ fn wasi_preview_builder(config: wasi_config_t) -> Result if config.inherit_stdin { builder.inherit_stdin(); } else if let Some(file) = config.stdin { - builder.stdin(OsOther::try_from(file)?); + builder.stdin(OsFile::try_from(file)?); } if config.inherit_stdout { builder.inherit_stdout(); } else if let Some(file) = config.stdout { - builder.stdout(OsOther::try_from(file)?); + builder.stdout(OsFile::try_from(file)?); } if config.inherit_stderr { builder.inherit_stderr(); } else if let Some(file) = config.stderr { - builder.stderr(OsOther::try_from(file)?); + builder.stderr(OsFile::try_from(file)?); } for preopen in config.preopens { builder.preopened_dir(preopen.0, preopen.1); From 5521106a4ca36106b9bba7d805f7cab17ecdf4aa Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Mon, 8 Jun 2020 14:23:32 -0700 Subject: [PATCH 3/6] Add some documention to the types exposed by this PR, and a few others Signed-off-by: Jakub Konka --- crates/wasi-common/src/sys/osfile.rs | 3 +++ crates/wasi-common/src/sys/osother.rs | 2 ++ crates/wasi-common/src/sys/unix/bsd/osdir.rs | 7 ++++++- crates/wasi-common/src/sys/unix/linux/osdir.rs | 6 +++++- crates/wasi-common/src/virtfs.rs | 4 ++++ 5 files changed, 20 insertions(+), 2 deletions(-) diff --git a/crates/wasi-common/src/sys/osfile.rs b/crates/wasi-common/src/sys/osfile.rs index e9ced03fc673..d1ffd63695d7 100644 --- a/crates/wasi-common/src/sys/osfile.rs +++ b/crates/wasi-common/src/sys/osfile.rs @@ -9,6 +9,9 @@ use std::io::{self, Read, Seek, SeekFrom, Write}; use std::ops::Deref; #[derive(Debug)] +/// 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. 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 42f15c579b09..fcfa979acfd7 100644 --- a/crates/wasi-common/src/sys/osother.rs +++ b/crates/wasi-common/src/sys/osother.rs @@ -10,6 +10,8 @@ use std::fs::File; use std::io::{self, Read, Write}; use std::ops::Deref; +/// 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>; diff --git a/crates/wasi-common/src/sys/unix/bsd/osdir.rs b/crates/wasi-common/src/sys/unix/bsd/osdir.rs index 7baa1939ed02..19d2e1f1b2db 100644 --- a/crates/wasi-common/src/sys/unix/bsd/osdir.rs +++ b/crates/wasi-common/src/sys/unix/bsd/osdir.rs @@ -6,6 +6,9 @@ use std::io; use yanix::dir::Dir; #[derive(Debug)] +/// 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. pub struct OsDir { pub(crate) rights: Cell, pub(crate) handle: RawOsHandle, @@ -39,7 +42,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 f15d89a4c5f6..e0747b72bf5c 100644 --- a/crates/wasi-common/src/sys/unix/linux/osdir.rs +++ b/crates/wasi-common/src/sys/unix/linux/osdir.rs @@ -6,6 +6,9 @@ use std::io; use yanix::dir::Dir; #[derive(Debug)] +/// A directory in the operating system's file system. Its impl of `Handle` is +/// in sys::osdir. This type is exposed to all other moduleas as +/// sys::osdir::OsDir when configured. pub struct OsDir { pub(crate) rights: Cell, pub(crate) handle: RawOsHandle, @@ -16,7 +19,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/virtfs.rs b/crates/wasi-common/src/virtfs.rs index 3d9b61b197a6..141a9cc693a3 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()) } From 4e1fc02681f9f89e4a929679571ad948b92e27ac Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 9 Jun 2020 14:16:27 +0200 Subject: [PATCH 4/6] Add construction examples and missing docs for Handle trait --- crates/wasi-common/src/handle.rs | 19 ++++++++++++++++++- crates/wasi-common/src/sys/osfile.rs | 14 ++++++++++++++ crates/wasi-common/src/sys/osother.rs | 14 ++++++++++++++ crates/wasi-common/src/sys/unix/bsd/osdir.rs | 18 ++++++++++++++++-- .../wasi-common/src/sys/unix/linux/osdir.rs | 18 ++++++++++++++++-- crates/wasi-common/src/sys/windows/osdir.rs | 18 ++++++++++++++++++ 6 files changed, 96 insertions(+), 5 deletions(-) diff --git a/crates/wasi-common/src/handle.rs b/crates/wasi-common/src/handle.rs index 0f6bd080da0e..8cdf6a4c57fe 100644 --- a/crates/wasi-common/src/handle.rs +++ b/crates/wasi-common/src/handle.rs @@ -61,7 +61,24 @@ impl fmt::Display for HandleRights { } } -// TODO docs +/// 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>; diff --git a/crates/wasi-common/src/sys/osfile.rs b/crates/wasi-common/src/sys/osfile.rs index d1ffd63695d7..f07ddf6240cf 100644 --- a/crates/wasi-common/src/sys/osfile.rs +++ b/crates/wasi-common/src/sys/osfile.rs @@ -12,6 +12,20 @@ use std::ops::Deref; /// 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 fcfa979acfd7..314d91dea3fb 100644 --- a/crates/wasi-common/src/sys/osother.rs +++ b/crates/wasi-common/src/sys/osother.rs @@ -22,6 +22,20 @@ pub 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 struct OsOther { file_type: Filetype, diff --git a/crates/wasi-common/src/sys/unix/bsd/osdir.rs b/crates/wasi-common/src/sys/unix/bsd/osdir.rs index 19d2e1f1b2db..ed665329d632 100644 --- a/crates/wasi-common/src/sys/unix/bsd/osdir.rs +++ b/crates/wasi-common/src/sys/unix/bsd/osdir.rs @@ -7,8 +7,22 @@ use yanix::dir::Dir; #[derive(Debug)] /// 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. +/// 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, diff --git a/crates/wasi-common/src/sys/unix/linux/osdir.rs b/crates/wasi-common/src/sys/unix/linux/osdir.rs index e0747b72bf5c..c803178c6ac2 100644 --- a/crates/wasi-common/src/sys/unix/linux/osdir.rs +++ b/crates/wasi-common/src/sys/unix/linux/osdir.rs @@ -7,8 +7,22 @@ use yanix::dir::Dir; #[derive(Debug)] /// A directory in the operating system's file system. Its impl of `Handle` is -/// in sys::osdir. This type is exposed to all other moduleas as -/// sys::osdir::OsDir when configured. +/// 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, diff --git a/crates/wasi-common/src/sys/windows/osdir.rs b/crates/wasi-common/src/sys/windows/osdir.rs index 132bdceee433..563b9bbd361f 100644 --- a/crates/wasi-common/src/sys/windows/osdir.rs +++ b/crates/wasi-common/src/sys/windows/osdir.rs @@ -8,6 +8,24 @@ use std::io; use std::os::windows::prelude::{AsRawHandle, FromRawHandle, IntoRawHandle}; #[derive(Debug)] +/// 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; +/// 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, From e2fd57f536ab7f1779bf31834237eebcff3b95dc Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 9 Jun 2020 19:02:10 +0200 Subject: [PATCH 5/6] Fix example on Windows --- crates/wasi-common/src/sys/windows/osdir.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/wasi-common/src/sys/windows/osdir.rs b/crates/wasi-common/src/sys/windows/osdir.rs index 563b9bbd361f..ee3273052502 100644 --- a/crates/wasi-common/src/sys/windows/osdir.rs +++ b/crates/wasi-common/src/sys/windows/osdir.rs @@ -20,6 +20,7 @@ use std::os::windows::prelude::{AsRawHandle, FromRawHandle, IntoRawHandle}; /// ```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; /// From 11edb95f9eb172e275244ba13c06a1eaec0b995e Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 9 Jun 2020 19:14:46 +0200 Subject: [PATCH 6/6] Merge wasi_preview_builder into create_preview1_instance --- crates/c-api/src/wasi.rs | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/crates/c-api/src/wasi.rs b/crates/c-api/src/wasi.rs index 9ff6e3cb0e58..6daad6aaef1a 100644 --- a/crates/c-api/src/wasi.rs +++ b/crates/c-api/src/wasi.rs @@ -201,7 +201,7 @@ enum WasiInstance { Snapshot0(WasiSnapshot0), } -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(); @@ -233,11 +233,11 @@ fn create_snapshot0_instance(store: &Store, config: wasi_config_t) -> Result 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(); @@ -269,18 +269,9 @@ fn wasi_preview_builder(config: wasi_config_t) -> Result for preopen in config.preopens { builder.preopened_dir(preopen.0, preopen.1); } - Ok(builder) -} - -fn create_preview1_instance(store: &Store, config: wasi_config_t) -> Result { Ok(WasiInstance::Preview1(WasiPreview1::new( store, - wasi_preview_builder(config) - .and_then(|mut b| { - let b = b.build()?; - Ok(b) - }) - .map_err(|e| e.to_string())?, + builder.build()?, ))) } @@ -309,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()), };