Skip to content

Commit

Permalink
It's wiggle time! (#1202)
Browse files Browse the repository at this point in the history
* Use wiggle in place of wig in wasi-common

This is a rather massive commit that introduces `wiggle` into the
picture. We still use `wig`'s macro in `old` snapshot and to generate
`wasmtime-wasi` glue, but everything else is now autogenerated by `wiggle`.
In summary, thanks to `wiggle`, we no longer need to worry about
serialising and deserialising to and from the guest memory, and
all guest (WASI) types are now proper idiomatic Rust types.

While we're here, in preparation for the ephemeral snapshot, I went
ahead and reorganised the internal structure of the crate. Instead of
modules like `hostcalls_impl` or `hostcalls_impl::fs`, the structure
now resembles that in ephemeral with modules like `path`, `fd`, etc.
Now, I'm not requiring we leave it like this, but I reckon it looks
cleaner this way after all.

* Fix wig to use new first-class access to caller's mem

* Ignore warning in proc_exit for the moment

* Group unsafes together in args and environ calls

* Simplify pwrite; more unsafe blocks

* Simplify fd_read

* Bundle up unsafes in fd_readdir

* Simplify fd_write

* Add comment to path_readlink re zero-len buffers

* Simplify unsafes in random_get

* Hide GuestPtr<str> to &str in path::get

* Rewrite pread and pwrite using SeekFrom and read/write_vectored

I've left the implementation of VirtualFs pretty much untouched
as I don't feel that comfortable in changing the API too much.
Having said that, I reckon `pread` and `pwrite` could be refactored
out, and `preadv` and `pwritev` could be entirely rewritten using
`seek` and `read_vectored` and `write_vectored`.

* Add comment about VirtFs unsafety

* Fix all mentions of FdEntry to Entry

* Fix warnings on Win

* Add aux struct EntryTable responsible for Fds and Entries

This commit adds aux struct `EntryTable` which is private to `WasiCtx`
and is basically responsible for `Fd` alloc/dealloc as well as storing
matching `Entry`s. This struct is entirely private to `WasiCtx` and
as such as should remain transparent to `WasiCtx` users.

* Remove redundant check for empty buffer in path_readlink

* Preserve and rewind file cursor in pread/pwrite

* Use GuestPtr<[u8]>::copy_from_slice wherever copying bytes directly

* Use GuestPtr<[u8]>::copy_from_slice in fd_readdir

* Clean up unsafes around WasiCtx accessors

* Fix bugs in args_get and environ_get

* Fix conflicts after rebase
  • Loading branch information
kubkon authored Mar 20, 2020
1 parent f700efe commit 32595fa
Show file tree
Hide file tree
Showing 62 changed files with 4,295 additions and 5,029 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions crates/wasi-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ filetime = "0.2.7"
lazy_static = "1.4.0"
num = { version = "0.2.0", default-features = false }
wig = { path = "wig", version = "0.12.0" }
wiggle = { path = "../wiggle" }
wiggle-runtime = { path = "../wiggle/crates/runtime" }

[target.'cfg(unix)'.dependencies]
yanix = { path = "yanix", version = "0.12.0" }
Expand Down
18 changes: 18 additions & 0 deletions crates/wasi-common/src/clock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use crate::sys;
use crate::wasi::types::{Subclockflags, SubscriptionClock};
use crate::wasi::{Errno, Result};
use std::time::SystemTime;

pub(crate) use sys::clock::*;

pub(crate) fn to_relative_ns_delay(clock: SubscriptionClock) -> Result<u128> {
if clock.flags != Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME {
return Ok(u128::from(clock.timeout));
}
let now: u128 = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.map_err(|_| Errno::Notcapable)?
.as_nanos();
let deadline = u128::from(clock.timeout);
Ok(deadline.saturating_sub(now))
}
146 changes: 94 additions & 52 deletions crates/wasi-common/src/ctx.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use crate::entry::{Descriptor, Entry};
use crate::fdpool::FdPool;
use crate::sys::entry_impl::OsHandle;
use crate::sys::entry::OsHandle;
use crate::virtfs::{VirtualDir, VirtualDirEntry};
use crate::wasi::{self, WasiError, WasiResult};
use crate::wasi::types;
use crate::wasi::{Errno, Result};
use std::borrow::Borrow;
use std::cell::{Ref, RefCell, RefMut};
use std::collections::HashMap;
use std::ffi::{self, CString, OsString};
use std::fs::File;
Expand Down Expand Up @@ -48,10 +50,10 @@ impl std::fmt::Debug for PendingEntry {
match self {
Self::Thunk(f) => write!(
fmt,
"PendingFdEntry::Thunk({:p})",
"PendingEntry::Thunk({:p})",
f as *const fn() -> io::Result<Entry>
),
Self::File(f) => write!(fmt, "PendingFdEntry::File({:?})", f),
Self::File(f) => write!(fmt, "PendingEntry::File({:?})", f),
}
}
}
Expand Down Expand Up @@ -322,43 +324,34 @@ impl WasiCtxBuilder {
})
.collect::<WasiCtxBuilderResult<Vec<CString>>>()?;

let mut fd_pool = FdPool::new();
let mut entries: HashMap<wasi::__wasi_fd_t, Entry> = HashMap::new();
let mut entries = EntryTable::new();
// Populate the non-preopen entries.
for pending in vec![
self.stdin.take().unwrap(),
self.stdout.take().unwrap(),
self.stderr.take().unwrap(),
] {
let fd = fd_pool
.allocate()
.ok_or(WasiCtxBuilderError::TooManyFilesOpen)?;
log::debug!("WasiCtx inserting ({:?}, {:?})", fd, pending);
match pending {
PendingEntry::Thunk(f) => {
entries.insert(fd, f()?);
}
PendingEntry::File(f) => {
entries.insert(fd, Entry::from(Descriptor::OsHandle(OsHandle::from(f)))?);
}
}
log::debug!("WasiCtx inserting entry {:?}", pending);
let fd = match pending {
PendingEntry::Thunk(f) => entries
.insert(f()?)
.ok_or(WasiCtxBuilderError::TooManyFilesOpen)?,
PendingEntry::File(f) => entries
.insert(Entry::from(Descriptor::OsHandle(OsHandle::from(f)))?)
.ok_or(WasiCtxBuilderError::TooManyFilesOpen)?,
};
log::debug!("WasiCtx inserted at {:?}", fd);
}
// Then add the preopen entries.
for (guest_path, dir) in self.preopens.take().unwrap() {
// We do the increment at the beginning of the loop body, so that we don't overflow
// unnecessarily if we have exactly the maximum number of file descriptors.
let preopen_fd = fd_pool
.allocate()
.ok_or(WasiCtxBuilderError::TooManyFilesOpen)?;

match &dir {
Descriptor::OsHandle(handle) => {
if !handle.metadata()?.is_dir() {
return Err(WasiCtxBuilderError::NotADirectory(guest_path));
}
}
Descriptor::VirtualFile(virt) => {
if virt.get_file_type() != wasi::__WASI_FILETYPE_DIRECTORY {
if virt.get_file_type() != types::Filetype::Directory {
return Err(WasiCtxBuilderError::NotADirectory(guest_path));
}
}
Expand All @@ -367,26 +360,70 @@ impl WasiCtxBuilder {
}
}

let mut fe = Entry::from(dir)?;
fe.preopen_path = Some(guest_path);
log::debug!("WasiCtx inserting ({:?}, {:?})", preopen_fd, fe);
entries.insert(preopen_fd, fe);
let mut entry = Entry::from(dir)?;
entry.preopen_path = Some(guest_path);
log::debug!("WasiCtx inserting {:?}", entry);
let fd = entries
.insert(entry)
.ok_or(WasiCtxBuilderError::TooManyFilesOpen)?;
log::debug!("WasiCtx inserted at {:?}", fd);
log::debug!("WasiCtx entries = {:?}", entries);
}

Ok(WasiCtx {
args,
env,
entries,
fd_pool,
entries: RefCell::new(entries),
})
}
}

#[derive(Debug)]
pub struct WasiCtx {
struct EntryTable {
fd_pool: FdPool,
entries: HashMap<wasi::__wasi_fd_t, Entry>,
entries: HashMap<types::Fd, Entry>,
}

impl EntryTable {
fn new() -> Self {
Self {
fd_pool: FdPool::new(),
entries: HashMap::new(),
}
}

fn contains(&self, fd: &types::Fd) -> bool {
self.entries.contains_key(fd)
}

fn insert(&mut self, entry: Entry) -> Option<types::Fd> {
let fd = self.fd_pool.allocate()?;
self.entries.insert(fd, entry);
Some(fd)
}

fn insert_at(&mut self, fd: &types::Fd, entry: Entry) {
self.entries.insert(*fd, entry);
}

fn get(&self, fd: &types::Fd) -> Option<&Entry> {
self.entries.get(fd)
}

fn get_mut(&mut self, fd: &types::Fd) -> Option<&mut Entry> {
self.entries.get_mut(fd)
}

fn remove(&mut self, fd: types::Fd) -> Option<Entry> {
let entry = self.entries.remove(&fd)?;
self.fd_pool.deallocate(fd);
Some(entry)
}
}

#[derive(Debug)]
pub struct WasiCtx {
entries: RefCell<EntryTable>,
pub(crate) args: Vec<CString>,
pub(crate) env: Vec<CString>,
}
Expand All @@ -408,42 +445,47 @@ impl WasiCtx {
}

/// Check if `WasiCtx` contains the specified raw WASI `fd`.
pub(crate) unsafe fn contains_entry(&self, fd: wasi::__wasi_fd_t) -> bool {
self.entries.contains_key(&fd)
pub(crate) fn contains_entry(&self, fd: types::Fd) -> bool {
self.entries.borrow().contains(&fd)
}

/// Get an immutable `Entry` corresponding to the specified raw WASI `fd`.
pub(crate) unsafe fn get_entry(&self, fd: wasi::__wasi_fd_t) -> WasiResult<&Entry> {
self.entries.get(&fd).ok_or(WasiError::EBADF)
pub(crate) fn get_entry(&self, fd: types::Fd) -> Result<Ref<Entry>> {
if !self.contains_entry(fd) {
return Err(Errno::Badf);
}
Ok(Ref::map(self.entries.borrow(), |entries| {
entries.get(&fd).unwrap()
}))
}

/// Get a mutable `Entry` corresponding to the specified raw WASI `fd`.
pub(crate) unsafe fn get_entry_mut(&mut self, fd: wasi::__wasi_fd_t) -> WasiResult<&mut Entry> {
self.entries.get_mut(&fd).ok_or(WasiError::EBADF)
// TODO This runs the risk of a potential difficult-to-predict panic down-the-line.
pub(crate) fn get_entry_mut(&self, fd: types::Fd) -> Result<RefMut<Entry>> {
if !self.contains_entry(fd) {
return Err(Errno::Badf);
}
Ok(RefMut::map(self.entries.borrow_mut(), |entries| {
entries.get_mut(&fd).unwrap()
}))
}

/// Insert the specified `Entry` into the `WasiCtx` object.
///
/// The `FdEntry` will automatically get another free raw WASI `fd` assigned. Note that
/// The `Entry` will automatically get another free raw WASI `fd` assigned. Note that
/// the two subsequent free raw WASI `fd`s do not have to be stored contiguously.
pub(crate) fn insert_entry(&mut self, fe: Entry) -> WasiResult<wasi::__wasi_fd_t> {
let fd = self.fd_pool.allocate().ok_or(WasiError::EMFILE)?;
self.entries.insert(fd, fe);
Ok(fd)
pub(crate) fn insert_entry(&self, entry: Entry) -> Result<types::Fd> {
self.entries.borrow_mut().insert(entry).ok_or(Errno::Mfile)
}

/// Insert the specified `Entry` with the specified raw WASI `fd` key into the `WasiCtx`
/// object.
pub(crate) fn insert_entry_at(&mut self, fd: wasi::__wasi_fd_t, fe: Entry) -> Option<Entry> {
self.entries.insert(fd, fe)
pub(crate) fn insert_entry_at(&self, fd: types::Fd, entry: Entry) {
self.entries.borrow_mut().insert_at(&fd, entry)
}

/// Remove `Entry` corresponding to the specified raw WASI `fd` from the `WasiCtx` object.
pub(crate) fn remove_entry(&mut self, fd: wasi::__wasi_fd_t) -> WasiResult<Entry> {
// Remove the `fd` from valid entries.
let entry = self.entries.remove(&fd).ok_or(WasiError::EBADF)?;
// Next, deallocate the `fd`.
self.fd_pool.deallocate(fd);
Ok(entry)
pub(crate) fn remove_entry(&self, fd: types::Fd) -> Result<Entry> {
self.entries.borrow_mut().remove(fd).ok_or(Errno::Badf)
}
}
Loading

0 comments on commit 32595fa

Please sign in to comment.