Skip to content

Commit

Permalink
Use wiggle in place of wig in wasi-common
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
kubkon authored and Jakub Konka committed Mar 19, 2020
1 parent f12fb29 commit 455e4bc
Show file tree
Hide file tree
Showing 61 changed files with 4,343 additions and 4,975 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))
}
58 changes: 35 additions & 23 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 @@ -323,7 +325,7 @@ 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: HashMap<types::Fd, Entry> = HashMap::new();
// Populate the non-preopen entries.
for pending in vec![
self.stdin.take().unwrap(),
Expand Down Expand Up @@ -358,7 +360,7 @@ impl WasiCtxBuilder {
}
}
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 @@ -377,16 +379,16 @@ impl WasiCtxBuilder {
Ok(WasiCtx {
args,
env,
entries,
fd_pool,
entries: RefCell::new(entries),
fd_pool: RefCell::new(fd_pool),
})
}
}

#[derive(Debug)]
pub struct WasiCtx {
fd_pool: FdPool,
entries: HashMap<wasi::__wasi_fd_t, Entry>,
fd_pool: RefCell<FdPool>,
entries: RefCell<HashMap<types::Fd, Entry>>,
pub(crate) args: Vec<CString>,
pub(crate) env: Vec<CString>,
}
Expand All @@ -408,42 +410,52 @@ 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) unsafe fn contains_entry(&self, fd: types::Fd) -> bool {
self.entries.borrow().contains_key(&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) unsafe 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)
pub(crate) unsafe 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);
pub(crate) fn insert_entry(&self, fe: Entry) -> Result<types::Fd> {
let fd = self.fd_pool.borrow_mut().allocate().ok_or(Errno::Mfile)?;
self.entries.borrow_mut().insert(fd, fe);
Ok(fd)
}

/// 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, fe: Entry) -> Option<Entry> {
self.entries.borrow_mut().insert(fd, fe)
}

/// 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> {
pub(crate) fn remove_entry(&self, fd: types::Fd) -> Result<Entry> {
// Remove the `fd` from valid entries.
let entry = self.entries.remove(&fd).ok_or(WasiError::EBADF)?;
let entry = self.entries.borrow_mut().remove(&fd).ok_or(Errno::Badf)?;
// Next, deallocate the `fd`.
self.fd_pool.deallocate(fd);
self.fd_pool.borrow_mut().deallocate(fd);
Ok(entry)
}
}
87 changes: 43 additions & 44 deletions crates/wasi-common/src/entry.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use crate::sys::dev_null;
use crate::sys::entry_impl::{descriptor_as_oshandle, determine_type_and_access_rights, OsHandle};
use crate::sys::entry::{descriptor_as_oshandle, determine_type_and_access_rights, OsHandle};
use crate::virtfs::VirtualFile;
use crate::wasi::{self, WasiError, WasiResult};
use crate::wasi::types::{Filetype, Rights};
use crate::wasi::{Errno, Result};
use std::marker::PhantomData;
use std::mem::ManuallyDrop;
use std::ops::{Deref, DerefMut};
Expand All @@ -18,58 +19,56 @@ pub(crate) enum Descriptor {

impl From<OsHandle> for Descriptor {
fn from(handle: OsHandle) -> Self {
Descriptor::OsHandle(handle)
Self::OsHandle(handle)
}
}

impl From<Box<dyn VirtualFile>> for Descriptor {
fn from(virt: Box<dyn VirtualFile>) -> Self {
Descriptor::VirtualFile(virt)
Self::VirtualFile(virt)
}
}

impl fmt::Debug for Descriptor {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Descriptor::OsHandle(handle) => write!(f, "{:?}", handle),
Descriptor::VirtualFile(_) => write!(f, "VirtualFile"),
Descriptor::Stdin => write!(f, "Stdin"),
Descriptor::Stdout => write!(f, "Stdout"),
Descriptor::Stderr => write!(f, "Stderr"),
Self::OsHandle(handle) => write!(f, "{:?}", handle),
Self::VirtualFile(_) => write!(f, "VirtualFile"),
Self::Stdin => write!(f, "Stdin"),
Self::Stdout => write!(f, "Stdout"),
Self::Stderr => write!(f, "Stderr"),
}
}
}

impl Descriptor {
pub(crate) fn try_clone(&self) -> io::Result<Descriptor> {
pub(crate) fn try_clone(&self) -> io::Result<Self> {
match self {
Descriptor::OsHandle(file) => file.try_clone().map(|f| OsHandle::from(f).into()),
Descriptor::VirtualFile(virt) => virt.try_clone().map(Descriptor::VirtualFile),
Descriptor::Stdin => Ok(Descriptor::Stdin),
Descriptor::Stdout => Ok(Descriptor::Stdout),
Descriptor::Stderr => Ok(Descriptor::Stderr),
Self::OsHandle(file) => file.try_clone().map(|f| OsHandle::from(f).into()),
Self::VirtualFile(virt) => virt.try_clone().map(Self::VirtualFile),
Self::Stdin => Ok(Self::Stdin),
Self::Stdout => Ok(Self::Stdout),
Self::Stderr => Ok(Self::Stderr),
}
}

/// Return a reference to the `OsHandle` or `VirtualFile` treating it as an
/// actual file/dir, and allowing operations which require an actual file and
/// not just a stream or socket file descriptor.
pub(crate) fn as_file<'descriptor>(&'descriptor self) -> WasiResult<&'descriptor Descriptor> {
pub(crate) fn as_file<'descriptor>(&'descriptor self) -> Result<&'descriptor Self> {
match self {
Self::OsHandle(_) => Ok(self),
Self::VirtualFile(_) => Ok(self),
_ => Err(WasiError::EBADF),
_ => Err(Errno::Badf),
}
}

/// Like `as_file`, but return a mutable reference.
pub(crate) fn as_file_mut<'descriptor>(
&'descriptor mut self,
) -> WasiResult<&'descriptor mut Descriptor> {
pub(crate) fn as_file_mut<'descriptor>(&'descriptor mut self) -> Result<&'descriptor mut Self> {
match self {
Self::OsHandle(_) => Ok(self),
Self::VirtualFile(_) => Ok(self),
_ => Err(WasiError::EBADF),
_ => Err(Errno::Badf),
}
}

Expand All @@ -89,10 +88,10 @@ impl Descriptor {
/// specified, verifying whether the stored `Descriptor` object is valid for the rights specified.
#[derive(Debug)]
pub(crate) struct Entry {
pub(crate) file_type: wasi::__wasi_filetype_t,
pub(crate) file_type: Filetype,
descriptor: Descriptor,
pub(crate) rights_base: wasi::__wasi_rights_t,
pub(crate) rights_inheriting: wasi::__wasi_rights_t,
pub(crate) rights_base: Rights,
pub(crate) rights_inheriting: Rights,
pub(crate) preopen_path: Option<PathBuf>,
// TODO: directories
}
Expand Down Expand Up @@ -173,12 +172,12 @@ impl Entry {
/// The `FdEntry` can only be converted into a valid `Descriptor` object if
/// the specified set of base rights `rights_base`, and inheriting rights `rights_inheriting`
/// is a subset of rights attached to this `FdEntry`. The check is performed using
/// `FdEntry::validate_rights` method. If the check fails, `WasiError::ENOTCAPABLE` is returned.
/// `FdEntry::validate_rights` method. If the check fails, `Errno::Notcapable` is returned.
pub(crate) fn as_descriptor(
&self,
rights_base: wasi::__wasi_rights_t,
rights_inheriting: wasi::__wasi_rights_t,
) -> WasiResult<&Descriptor> {
rights_base: Rights,
rights_inheriting: Rights,
) -> Result<&Descriptor> {
self.validate_rights(rights_base, rights_inheriting)?;
Ok(&self.descriptor)
}
Expand All @@ -189,12 +188,12 @@ impl Entry {
/// The `FdEntry` can only be converted into a valid `Descriptor` object if
/// the specified set of base rights `rights_base`, and inheriting rights `rights_inheriting`
/// is a subset of rights attached to this `FdEntry`. The check is performed using
/// `FdEntry::validate_rights` method. If the check fails, `WasiError::ENOTCAPABLE` is returned.
/// `FdEntry::validate_rights` method. If the check fails, `Errno::Notcapable` is returned.
pub(crate) fn as_descriptor_mut(
&mut self,
rights_base: wasi::__wasi_rights_t,
rights_inheriting: wasi::__wasi_rights_t,
) -> WasiResult<&mut Descriptor> {
rights_base: Rights,
rights_inheriting: Rights,
) -> Result<&mut Descriptor> {
self.validate_rights(rights_base, rights_inheriting)?;
Ok(&mut self.descriptor)
}
Expand All @@ -203,28 +202,28 @@ impl Entry {
/// inheriting rights `rights_inheriting`; i.e., if rights attached to this `FdEntry` object
/// are a superset.
///
/// Upon unsuccessful check, `WasiError::ENOTCAPABLE` is returned.
fn validate_rights(
/// Upon unsuccessful check, `Errno::Notcapable` is returned.
pub(crate) fn validate_rights(
&self,
rights_base: wasi::__wasi_rights_t,
rights_inheriting: wasi::__wasi_rights_t,
) -> WasiResult<()> {
rights_base: Rights,
rights_inheriting: Rights,
) -> Result<()> {
let missing_base = !self.rights_base & rights_base;
let missing_inheriting = !self.rights_inheriting & rights_inheriting;
if missing_base != 0 || missing_inheriting != 0 {
if missing_base != Rights::empty() || missing_inheriting != Rights::empty() {
log::trace!(
" | validate_rights failed: required: \
rights_base = {:#x}, rights_inheriting = {:#x}; \
actual: rights_base = {:#x}, rights_inheriting = {:#x}; \
missing_base = {:#x}, missing_inheriting = {:#x}",
rights_base = {}, rights_inheriting = {}; \
actual: rights_base = {}, rights_inheriting = {}; \
missing_base = {}, missing_inheriting = {}",
rights_base,
rights_inheriting,
self.rights_base,
self.rights_inheriting,
missing_base,
missing_inheriting
);
Err(WasiError::ENOTCAPABLE)
Err(Errno::Notcapable)
} else {
Ok(())
}
Expand All @@ -234,8 +233,8 @@ impl Entry {
/// Note that since WASI itself lacks an `isatty` syscall and relies
/// on a conservative approximation, we use the same approximation here.
pub(crate) fn isatty(&self) -> bool {
self.file_type == wasi::__WASI_FILETYPE_CHARACTER_DEVICE
&& (self.rights_base & (wasi::__WASI_RIGHTS_FD_SEEK | wasi::__WASI_RIGHTS_FD_TELL)) == 0
self.file_type == Filetype::CharacterDevice
&& (self.rights_base & (Rights::FD_SEEK | Rights::FD_TELL)) == Rights::empty()
}
}

Expand Down
Loading

0 comments on commit 455e4bc

Please sign in to comment.