Skip to content

Commit 05c278d

Browse files
Jakub Konkakubkon
authored andcommitted
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.).
1 parent b65bd1c commit 05c278d

File tree

15 files changed

+124
-82
lines changed

15 files changed

+124
-82
lines changed

crates/c-api/src/wasi.rs

Lines changed: 70 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -200,62 +200,85 @@ enum WasiInstance {
200200
Snapshot0(WasiSnapshot0),
201201
}
202202

203-
macro_rules! config_to_builder {
204-
($builder:ident, $config:ident) => {{
205-
let mut builder = $builder::new();
206-
207-
if $config.inherit_args {
208-
builder.inherit_args();
209-
} else if !$config.args.is_empty() {
210-
builder.args($config.args);
211-
}
212-
213-
if $config.inherit_env {
214-
builder.inherit_env();
215-
} else if !$config.env.is_empty() {
216-
builder.envs($config.env);
217-
}
218-
219-
if $config.inherit_stdin {
220-
builder.inherit_stdin();
221-
} else if let Some(file) = $config.stdin {
222-
builder.stdin(file);
223-
}
224-
225-
if $config.inherit_stdout {
226-
builder.inherit_stdout();
227-
} else if let Some(file) = $config.stdout {
228-
builder.stdout(file);
229-
}
230-
231-
if $config.inherit_stderr {
232-
builder.inherit_stderr();
233-
} else if let Some(file) = $config.stderr {
234-
builder.stderr(file);
235-
}
236-
237-
for preopen in $config.preopens {
238-
builder.preopened_dir(preopen.0, preopen.1);
239-
}
240-
241-
builder
242-
}};
243-
}
244-
245203
fn create_snapshot0_instance(store: &Store, config: wasi_config_t) -> Result<WasiInstance, String> {
204+
let mut builder = WasiSnapshot0CtxBuilder::new();
205+
if config.inherit_args {
206+
builder.inherit_args();
207+
} else if !config.args.is_empty() {
208+
builder.args(config.args);
209+
}
210+
if config.inherit_env {
211+
builder.inherit_env();
212+
} else if !config.env.is_empty() {
213+
builder.envs(config.env);
214+
}
215+
if config.inherit_stdin {
216+
builder.inherit_stdin();
217+
} else if let Some(file) = config.stdin {
218+
builder.stdin(file);
219+
}
220+
if config.inherit_stdout {
221+
builder.inherit_stdout();
222+
} else if let Some(file) = config.stdout {
223+
builder.stdout(file);
224+
}
225+
if config.inherit_stderr {
226+
builder.inherit_stderr();
227+
} else if let Some(file) = config.stderr {
228+
builder.stderr(file);
229+
}
230+
for preopen in config.preopens {
231+
builder.preopened_dir(preopen.0, preopen.1);
232+
}
246233
Ok(WasiInstance::Snapshot0(WasiSnapshot0::new(
247234
store,
248-
config_to_builder!(WasiSnapshot0CtxBuilder, config)
249-
.build()
250-
.map_err(|e| e.to_string())?,
235+
builder.build().map_err(|e| e.to_string())?,
251236
)))
252237
}
253238

239+
fn wasi_preview_builder(config: wasi_config_t) -> Result<WasiPreview1CtxBuilder> {
240+
use std::convert::TryFrom;
241+
use wasi_common::OsOther;
242+
let mut builder = WasiPreview1CtxBuilder::new();
243+
if config.inherit_args {
244+
builder.inherit_args();
245+
} else if !config.args.is_empty() {
246+
builder.args(config.args);
247+
}
248+
if config.inherit_env {
249+
builder.inherit_env();
250+
} else if !config.env.is_empty() {
251+
builder.envs(config.env);
252+
}
253+
if config.inherit_stdin {
254+
builder.inherit_stdin();
255+
} else if let Some(file) = config.stdin {
256+
builder.stdin(OsOther::try_from(file)?);
257+
}
258+
if config.inherit_stdout {
259+
builder.inherit_stdout();
260+
} else if let Some(file) = config.stdout {
261+
builder.stdout(OsOther::try_from(file)?);
262+
}
263+
if config.inherit_stderr {
264+
builder.inherit_stderr();
265+
} else if let Some(file) = config.stderr {
266+
builder.stderr(OsOther::try_from(file)?);
267+
}
268+
for preopen in config.preopens {
269+
builder.preopened_dir(preopen.0, preopen.1);
270+
}
271+
Ok(builder)
272+
}
273+
254274
fn create_preview1_instance(store: &Store, config: wasi_config_t) -> Result<WasiInstance, String> {
255275
Ok(WasiInstance::Preview1(WasiPreview1::new(
256276
store,
257-
config_to_builder!(WasiPreview1CtxBuilder, config)
258-
.build()
277+
wasi_preview_builder(config)
278+
.and_then(|mut b| {
279+
let b = b.build()?;
280+
Ok(b)
281+
})
259282
.map_err(|e| e.to_string())?,
260283
)))
261284
}

crates/test-programs/tests/wasm_tests/runtime.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use anyhow::{bail, Context};
2+
use std::convert::TryFrom;
23
use std::fs::File;
34
use std::path::Path;
4-
use wasi_common::VirtualDirEntry;
5+
use wasi_common::{OsOther, VirtualDirEntry};
56
use wasmtime::{Instance, Module, Store};
67

78
#[derive(Clone, Copy, Debug)]
@@ -46,7 +47,9 @@ pub fn instantiate(
4647
// where `stdin` is never ready to be read. In some CI systems, however,
4748
// stdin is closed which causes tests to fail.
4849
let (reader, _writer) = os_pipe::pipe()?;
49-
builder.stdin(reader_to_file(reader));
50+
let file = reader_to_file(reader);
51+
let handle = OsOther::try_from(file).context("failed to create OsOther from PipeReader")?;
52+
builder.stdin(handle);
5053
let snapshot1 = wasmtime_wasi::Wasi::new(&store, builder.build()?);
5154
let module = Module::new(&store, &data).context("failed to create wasm module")?;
5255
let imports = module

crates/wasi-common/src/ctx.rs

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ type WasiCtxBuilderResult<T> = std::result::Result<T, WasiCtxBuilderError>;
4747

4848
enum PendingEntry {
4949
Thunk(fn() -> io::Result<Box<dyn Handle>>),
50-
OsHandle(File),
50+
Handle(Box<dyn Handle>),
5151
}
5252

5353
impl std::fmt::Debug for PendingEntry {
@@ -58,7 +58,7 @@ impl std::fmt::Debug for PendingEntry {
5858
"PendingEntry::Thunk({:p})",
5959
f as *const fn() -> io::Result<Box<dyn Handle>>
6060
),
61-
Self::OsHandle(f) => write!(fmt, "PendingEntry::OsHandle({:?})", f),
61+
Self::Handle(handle) => write!(fmt, "PendingEntry::Handle({:p})", handle),
6262
}
6363
}
6464
}
@@ -247,21 +247,21 @@ impl WasiCtxBuilder {
247247
self
248248
}
249249

250-
/// Provide a File to use as stdin
251-
pub fn stdin(&mut self, file: File) -> &mut Self {
252-
self.stdin = Some(PendingEntry::OsHandle(file));
250+
/// Provide a `Handle` to use as stdin
251+
pub fn stdin<T: Handle + 'static>(&mut self, handle: T) -> &mut Self {
252+
self.stdin = Some(PendingEntry::Handle(Box::new(handle)));
253253
self
254254
}
255255

256-
/// Provide a File to use as stdout
257-
pub fn stdout(&mut self, file: File) -> &mut Self {
258-
self.stdout = Some(PendingEntry::OsHandle(file));
256+
/// Provide a `Handle` to use as stdout
257+
pub fn stdout<T: Handle + 'static>(&mut self, handle: T) -> &mut Self {
258+
self.stdout = Some(PendingEntry::Handle(Box::new(handle)));
259259
self
260260
}
261261

262-
/// Provide a File to use as stderr
263-
pub fn stderr(&mut self, file: File) -> &mut Self {
264-
self.stderr = Some(PendingEntry::OsHandle(file));
262+
/// Provide a `Handle` to use as stderr
263+
pub fn stderr<T: Handle + 'static>(&mut self, handle: T) -> &mut Self {
264+
self.stderr = Some(PendingEntry::Handle(Box::new(handle)));
265265
self
266266
}
267267

@@ -368,9 +368,8 @@ impl WasiCtxBuilder {
368368
.insert(entry)
369369
.ok_or(WasiCtxBuilderError::TooManyFilesOpen)?
370370
}
371-
PendingEntry::OsHandle(f) => {
372-
let handle = OsOther::try_from(f)?;
373-
let handle = EntryHandle::new(handle);
371+
PendingEntry::Handle(handle) => {
372+
let handle = EntryHandle::from(handle);
374373
let entry = Entry::new(handle);
375374
entries
376375
.insert(entry)

crates/wasi-common/src/entry.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use std::rc::Rc;
88
pub(crate) struct EntryHandle(Rc<dyn Handle>);
99

1010
impl EntryHandle {
11+
#[allow(dead_code)]
1112
pub(crate) fn new<T: Handle + 'static>(handle: T) -> Self {
1213
Self(Rc::new(handle))
1314
}

crates/wasi-common/src/handle.rs

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,38 +6,49 @@ use std::io::{self, SeekFrom};
66

77
/// Represents rights of a `Handle`, either already held or required.
88
#[derive(Debug, Copy, Clone)]
9-
pub(crate) struct HandleRights {
9+
pub struct HandleRights {
1010
pub(crate) base: Rights,
1111
pub(crate) inheriting: Rights,
1212
}
1313

1414
impl HandleRights {
15-
pub(crate) fn new(base: Rights, inheriting: Rights) -> Self {
15+
/// Creates new `HandleRights` instance from `base` and `inheriting` rights.
16+
pub fn new(base: Rights, inheriting: Rights) -> Self {
1617
Self { base, inheriting }
1718
}
1819

19-
/// Create new `HandleRights` instance from `base` rights only, keeping
20+
/// Creates new `HandleRights` instance from `base` rights only, keeping
2021
/// `inheriting` set to none.
21-
pub(crate) fn from_base(base: Rights) -> Self {
22+
pub fn from_base(base: Rights) -> Self {
2223
Self {
2324
base,
2425
inheriting: Rights::empty(),
2526
}
2627
}
2728

28-
/// Create new `HandleRights` instance with both `base` and `inheriting`
29+
/// Creates new `HandleRights` instance with both `base` and `inheriting`
2930
/// rights set to none.
30-
pub(crate) fn empty() -> Self {
31+
pub fn empty() -> Self {
3132
Self {
3233
base: Rights::empty(),
3334
inheriting: Rights::empty(),
3435
}
3536
}
3637

37-
/// Check if `other` is a subset of those rights.
38-
pub(crate) fn contains(&self, other: &Self) -> bool {
38+
/// Checks if `other` is a subset of those rights.
39+
pub fn contains(&self, other: &Self) -> bool {
3940
self.base.contains(&other.base) && self.inheriting.contains(&other.inheriting)
4041
}
42+
43+
/// Returns base rights.
44+
pub fn base(&self) -> Rights {
45+
self.base
46+
}
47+
48+
/// Returns inheriting rights.
49+
pub fn inheriting(&self) -> Rights {
50+
self.inheriting
51+
}
4152
}
4253

4354
impl fmt::Display for HandleRights {
@@ -50,7 +61,8 @@ impl fmt::Display for HandleRights {
5061
}
5162
}
5263

53-
pub(crate) trait Handle {
64+
// TODO docs
65+
pub trait Handle {
5466
fn as_any(&self) -> &dyn Any;
5567
fn try_clone(&self) -> io::Result<Box<dyn Handle>>;
5668
fn get_file_type(&self) -> types::Filetype;

crates/wasi-common/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,9 @@ mod virtfs;
3636
pub mod wasi;
3737

3838
pub use ctx::{WasiCtx, WasiCtxBuilder, WasiCtxBuilderError};
39+
pub use handle::{Handle, HandleRights};
40+
pub use sys::osdir::OsDir;
41+
pub use sys::osfile::OsFile;
42+
pub use sys::osother::{OsOther, OsOtherExt};
3943
pub use sys::preopen_dir;
4044
pub use virtfs::{FileContents, VirtualDirEntry};

crates/wasi-common/src/sys/osdir.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use std::ops::Deref;
1010
// TODO could this be cleaned up?
1111
// The actual `OsDir` struct is OS-dependent, therefore we delegate
1212
// its definition to OS-specific modules.
13-
pub(crate) use super::sys_impl::osdir::OsDir;
13+
pub use super::sys_impl::osdir::OsDir;
1414

1515
impl Deref for OsDir {
1616
type Target = RawOsHandle;

crates/wasi-common/src/sys/osfile.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use std::io::{self, Read, Seek, SeekFrom, Write};
99
use std::ops::Deref;
1010

1111
#[derive(Debug)]
12-
pub(crate) struct OsFile {
12+
pub struct OsFile {
1313
rights: Cell<HandleRights>,
1414
handle: RawOsHandle,
1515
}

crates/wasi-common/src/sys/osother.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use std::fs::File;
1010
use std::io::{self, Read, Write};
1111
use std::ops::Deref;
1212

13-
pub(crate) trait OsOtherExt {
13+
pub trait OsOtherExt {
1414
/// Create `OsOther` as `dyn Handle` from null device.
1515
fn from_null() -> io::Result<Box<dyn Handle>>;
1616
}
@@ -21,7 +21,7 @@ pub(crate) trait OsOtherExt {
2121
/// pipe should be encapsulated within this instance _and not_ `OsFile` which represents a regular
2222
/// OS file.
2323
#[derive(Debug)]
24-
pub(crate) struct OsOther {
24+
pub struct OsOther {
2525
file_type: Filetype,
2626
rights: Cell<HandleRights>,
2727
handle: RawOsHandle,

crates/wasi-common/src/sys/unix/bsd/osdir.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::io;
66
use yanix::dir::Dir;
77

88
#[derive(Debug)]
9-
pub(crate) struct OsDir {
9+
pub struct OsDir {
1010
pub(crate) rights: Cell<HandleRights>,
1111
pub(crate) handle: RawOsHandle,
1212
// When the client makes a `fd_readdir` syscall on this descriptor,

0 commit comments

Comments
 (0)