It's wiggle time! (#1202)
* 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
This commit is contained in:
@@ -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};
|
||||
@@ -54,22 +55,20 @@ impl Descriptor {
|
||||
/// 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 Self> {
|
||||
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 Self> {
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,15 +83,15 @@ impl Descriptor {
|
||||
/// accessed correctly.
|
||||
///
|
||||
/// Here, the `descriptor` field stores the host `Descriptor` object (such as a file descriptor, or
|
||||
/// stdin handle), and accessing it can only be done via the provided `FdEntry::as_descriptor` and
|
||||
/// stdin handle), and accessing it can only be done via the provided `Entry::as_descriptor` and
|
||||
/// `Entry::as_descriptor_mut` methods which require a set of base and inheriting rights to be
|
||||
/// 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
|
||||
}
|
||||
@@ -122,7 +121,7 @@ impl Entry {
|
||||
})
|
||||
}
|
||||
Descriptor::Stdin | Descriptor::Stdout | Descriptor::Stderr => {
|
||||
panic!("implementation error, stdin/stdout/stderr FdEntry must not be constructed from FdEntry::from");
|
||||
panic!("implementation error, stdin/stdout/stderr Entry must not be constructed from Entry::from");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -167,56 +166,56 @@ impl Entry {
|
||||
Self::from(OsHandle::from(dev_null()?).into())
|
||||
}
|
||||
|
||||
/// Convert this `FdEntry` into a host `Descriptor` object provided the specified
|
||||
/// `rights_base` and `rights_inheriting` rights are set on this `FdEntry` object.
|
||||
/// Convert this `Entry` into a host `Descriptor` object provided the specified
|
||||
/// `rights_base` and `rights_inheriting` rights are set on this `Entry` object.
|
||||
///
|
||||
/// The `FdEntry` can only be converted into a valid `Descriptor` object if
|
||||
/// The `Entry` 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.
|
||||
/// is a subset of rights attached to this `Entry`. The check is performed using
|
||||
/// `Entry::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)
|
||||
}
|
||||
|
||||
/// Convert this `FdEntry` into a mutable host `Descriptor` object provided the specified
|
||||
/// `rights_base` and `rights_inheriting` rights are set on this `FdEntry` object.
|
||||
/// Convert this `Entry` into a mutable host `Descriptor` object provided the specified
|
||||
/// `rights_base` and `rights_inheriting` rights are set on this `Entry` object.
|
||||
///
|
||||
/// The `FdEntry` can only be converted into a valid `Descriptor` object if
|
||||
/// The `Entry` 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.
|
||||
/// is a subset of rights attached to this `Entry`. The check is performed using
|
||||
/// `Entry::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)
|
||||
}
|
||||
|
||||
/// Check if this `FdEntry` object satisfies the specified base rights `rights_base`, and
|
||||
/// inheriting rights `rights_inheriting`; i.e., if rights attached to this `FdEntry` object
|
||||
/// Check if this `Entry` object satisfies the specified base rights `rights_base`, and
|
||||
/// inheriting rights `rights_inheriting`; i.e., if rights attached to this `Entry` 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,
|
||||
@@ -224,7 +223,7 @@ impl Entry {
|
||||
missing_base,
|
||||
missing_inheriting
|
||||
);
|
||||
Err(WasiError::ENOTCAPABLE)
|
||||
Err(Errno::Notcapable)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user