Remove duplication in wasi-common for snapshot_0 (#2444)
This commit deletes the old `snapshot_0` implementation of wasi-common, along with the `wig` crate that was used to generate bindings for it. This then reimplements `snapshot_0` in terms of `wasi_snapshot_preview1`. There were very few changes between the two snapshots: * The `nlink` field of `FileStat` was increased from 32 to 64 bits. * The `set` field of `whence` was reordered. * Clock subscriptions in polling dropped their redundant userdata field. This makes all of the syscalls relatively straightforward to simply delegate to the next snapshot's implementation. Some trickery happens to avoid extra cost when dealing with iovecs, but since the memory layout of iovecs remained the same this should still work. Now that `snapshot_0` is using wiggle we simply have a trait to implement, and that's implemented for the same `WasiCtx` that has the `wasi_snapshot_preview1` trait implemented for it as well. While this theoretically means that you could share the file descriptor table between the two snapshots that's not supported in the generated bindings just yet. A separate `WasiCtx` will be created for each WASI module.
This commit is contained in:
@@ -24,7 +24,6 @@ getrandom = { version = "0.2.0", features = ["std"] }
|
||||
cfg-if = "1.0"
|
||||
filetime = "0.2.7"
|
||||
lazy_static = "1.4.0"
|
||||
wig = { path = "wig", version = "0.21.0" }
|
||||
wiggle = { path = "../wiggle", default-features = false, version = "0.21.0" }
|
||||
tracing = "0.1.19"
|
||||
|
||||
|
||||
@@ -14,6 +14,15 @@ pub(crate) trait Fd {
|
||||
fn from_raw(raw_fd: u32) -> Self;
|
||||
}
|
||||
|
||||
impl Fd for u32 {
|
||||
fn as_raw(&self) -> u32 {
|
||||
*self
|
||||
}
|
||||
fn from_raw(raw_fd: u32) -> Self {
|
||||
raw_fd
|
||||
}
|
||||
}
|
||||
|
||||
/// This container tracks and manages all file descriptors that
|
||||
/// were already allocated.
|
||||
/// Internally, we use `u32` to represent the file descriptors;
|
||||
|
||||
@@ -26,7 +26,6 @@ mod error;
|
||||
mod fdpool;
|
||||
pub mod fs;
|
||||
mod handle;
|
||||
pub mod old;
|
||||
mod path;
|
||||
mod sandboxed_tty_writer;
|
||||
pub(crate) mod sched;
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
pub mod snapshot_0;
|
||||
@@ -1,391 +0,0 @@
|
||||
use crate::fdpool::FdPool;
|
||||
use crate::old::snapshot_0::entry::Entry;
|
||||
use crate::old::snapshot_0::wasi::{self, WasiError, WasiResult};
|
||||
use std::borrow::Borrow;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::{self, CString, OsString};
|
||||
use std::fs::File;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::{env, io, string};
|
||||
|
||||
/// Possible errors when `WasiCtxBuilder` fails building
|
||||
/// `WasiCtx`.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum WasiCtxBuilderError {
|
||||
/// General I/O error was encountered.
|
||||
#[error("general I/O error encountered: {0}")]
|
||||
Io(#[from] io::Error),
|
||||
/// Provided sequence of bytes was not a valid UTF-8.
|
||||
#[error("provided sequence is not valid UTF-8: {0}")]
|
||||
InvalidUtf8(#[from] string::FromUtf8Error),
|
||||
/// Provided sequence of bytes was not a valid UTF-16.
|
||||
///
|
||||
/// This error is expected to only occur on Windows hosts.
|
||||
#[error("provided sequence is not valid UTF-16: {0}")]
|
||||
InvalidUtf16(#[from] string::FromUtf16Error),
|
||||
/// Provided sequence of bytes contained an unexpected NUL byte.
|
||||
#[error("provided sequence contained an unexpected NUL byte")]
|
||||
UnexpectedNul(#[from] ffi::NulError),
|
||||
/// Provided `File` is not a directory.
|
||||
#[error("preopened directory path {} is not a directory", .0.display())]
|
||||
NotADirectory(PathBuf),
|
||||
/// `WasiCtx` has too many opened files.
|
||||
#[error("context object has too many opened files")]
|
||||
TooManyFilesOpen,
|
||||
}
|
||||
|
||||
type WasiCtxBuilderResult<T> = std::result::Result<T, WasiCtxBuilderError>;
|
||||
|
||||
enum PendingEntry {
|
||||
Thunk(fn() -> io::Result<Entry>),
|
||||
File(File),
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for PendingEntry {
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Thunk(f) => write!(
|
||||
fmt,
|
||||
"PendingEntry::Thunk({:p})",
|
||||
f as *const fn() -> io::Result<Entry>
|
||||
),
|
||||
Self::File(f) => write!(fmt, "PendingEntry::File({:?})", f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, Hash, PartialEq)]
|
||||
enum PendingCString {
|
||||
Bytes(Vec<u8>),
|
||||
OsString(OsString),
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for PendingCString {
|
||||
fn from(bytes: Vec<u8>) -> Self {
|
||||
Self::Bytes(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<OsString> for PendingCString {
|
||||
fn from(s: OsString) -> Self {
|
||||
Self::OsString(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl PendingCString {
|
||||
fn into_string(self) -> WasiCtxBuilderResult<String> {
|
||||
let res = match self {
|
||||
Self::Bytes(v) => String::from_utf8(v)?,
|
||||
#[cfg(unix)]
|
||||
Self::OsString(s) => {
|
||||
use std::os::unix::ffi::OsStringExt;
|
||||
String::from_utf8(s.into_vec())?
|
||||
}
|
||||
#[cfg(windows)]
|
||||
Self::OsString(s) => {
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
let bytes: Vec<u16> = s.encode_wide().collect();
|
||||
String::from_utf16(&bytes)?
|
||||
}
|
||||
};
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Create a `CString` containing valid UTF-8, or fail.
|
||||
fn into_utf8_cstring(self) -> WasiCtxBuilderResult<CString> {
|
||||
let s = self.into_string()?;
|
||||
let s = CString::new(s)?;
|
||||
Ok(s)
|
||||
}
|
||||
}
|
||||
|
||||
/// A builder allowing customizable construction of `WasiCtx` instances.
|
||||
pub struct WasiCtxBuilder {
|
||||
stdin: Option<PendingEntry>,
|
||||
stdout: Option<PendingEntry>,
|
||||
stderr: Option<PendingEntry>,
|
||||
preopens: Option<Vec<(PathBuf, File)>>,
|
||||
args: Option<Vec<PendingCString>>,
|
||||
env: Option<HashMap<PendingCString, PendingCString>>,
|
||||
}
|
||||
|
||||
impl WasiCtxBuilder {
|
||||
/// Builder for a new `WasiCtx`.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
stdin: Some(PendingEntry::Thunk(Entry::null)),
|
||||
stdout: Some(PendingEntry::Thunk(Entry::null)),
|
||||
stderr: Some(PendingEntry::Thunk(Entry::null)),
|
||||
preopens: Some(Vec::new()),
|
||||
args: Some(Vec::new()),
|
||||
env: Some(HashMap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Add arguments to the command-line arguments list.
|
||||
///
|
||||
/// Arguments must be valid UTF-8 with no NUL bytes, or else `WasiCtxBuilder::build()` will fail.
|
||||
pub fn args<S: AsRef<[u8]>>(&mut self, args: impl IntoIterator<Item = S>) -> &mut Self {
|
||||
self.args
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.extend(args.into_iter().map(|a| a.as_ref().to_vec().into()));
|
||||
self
|
||||
}
|
||||
|
||||
/// Add an argument to the command-line arguments list.
|
||||
///
|
||||
/// Arguments must be valid UTF-8 with no NUL bytes, or else `WasiCtxBuilder::build()` will fail.
|
||||
pub fn arg<S: AsRef<[u8]>>(&mut self, arg: S) -> &mut Self {
|
||||
self.args
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.push(arg.as_ref().to_vec().into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Inherit the command-line arguments from the host process.
|
||||
///
|
||||
/// If any arguments from the host process contain invalid UTF-8, `WasiCtxBuilder::build()` will
|
||||
/// fail.
|
||||
pub fn inherit_args(&mut self) -> &mut Self {
|
||||
let args = self.args.as_mut().unwrap();
|
||||
args.clear();
|
||||
args.extend(env::args_os().map(PendingCString::OsString));
|
||||
self
|
||||
}
|
||||
|
||||
/// Inherit stdin from the host process.
|
||||
pub fn inherit_stdin(&mut self) -> &mut Self {
|
||||
self.stdin = Some(PendingEntry::Thunk(Entry::duplicate_stdin));
|
||||
self
|
||||
}
|
||||
|
||||
/// Inherit stdout from the host process.
|
||||
pub fn inherit_stdout(&mut self) -> &mut Self {
|
||||
self.stdout = Some(PendingEntry::Thunk(Entry::duplicate_stdout));
|
||||
self
|
||||
}
|
||||
|
||||
/// Inherit stdout from the host process.
|
||||
pub fn inherit_stderr(&mut self) -> &mut Self {
|
||||
self.stderr = Some(PendingEntry::Thunk(Entry::duplicate_stderr));
|
||||
self
|
||||
}
|
||||
|
||||
/// Inherit the stdin, stdout, and stderr streams from the host process.
|
||||
pub fn inherit_stdio(&mut self) -> &mut Self {
|
||||
self.stdin = Some(PendingEntry::Thunk(Entry::duplicate_stdin));
|
||||
self.stdout = Some(PendingEntry::Thunk(Entry::duplicate_stdout));
|
||||
self.stderr = Some(PendingEntry::Thunk(Entry::duplicate_stderr));
|
||||
self
|
||||
}
|
||||
|
||||
/// Inherit the environment variables from the host process.
|
||||
///
|
||||
/// If any environment variables from the host process contain invalid Unicode (UTF-16 for
|
||||
/// Windows, UTF-8 for other platforms), `WasiCtxBuilder::build()` will fail.
|
||||
pub fn inherit_env(&mut self) -> &mut Self {
|
||||
let env = self.env.as_mut().unwrap();
|
||||
env.clear();
|
||||
env.extend(std::env::vars_os().map(|(k, v)| (k.into(), v.into())));
|
||||
self
|
||||
}
|
||||
|
||||
/// Add an entry to the environment.
|
||||
///
|
||||
/// Environment variable keys and values must be valid UTF-8 with no NUL bytes, or else
|
||||
/// `WasiCtxBuilder::build()` will fail.
|
||||
pub fn env<S: AsRef<[u8]>>(&mut self, k: S, v: S) -> &mut Self {
|
||||
self.env
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.insert(k.as_ref().to_vec().into(), v.as_ref().to_vec().into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Add entries to the environment.
|
||||
///
|
||||
/// Environment variable keys and values must be valid UTF-8 with no NUL bytes, or else
|
||||
/// `WasiCtxBuilder::build()` will fail.
|
||||
pub fn envs<S: AsRef<[u8]>, T: Borrow<(S, S)>>(
|
||||
&mut self,
|
||||
envs: impl IntoIterator<Item = T>,
|
||||
) -> &mut Self {
|
||||
self.env.as_mut().unwrap().extend(envs.into_iter().map(|t| {
|
||||
let (k, v) = t.borrow();
|
||||
(k.as_ref().to_vec().into(), v.as_ref().to_vec().into())
|
||||
}));
|
||||
self
|
||||
}
|
||||
|
||||
/// Provide a File to use as stdin
|
||||
pub fn stdin(&mut self, file: File) -> &mut Self {
|
||||
self.stdin = Some(PendingEntry::File(file));
|
||||
self
|
||||
}
|
||||
|
||||
/// Provide a File to use as stdout
|
||||
pub fn stdout(&mut self, file: File) -> &mut Self {
|
||||
self.stdout = Some(PendingEntry::File(file));
|
||||
self
|
||||
}
|
||||
|
||||
/// Provide a File to use as stderr
|
||||
pub fn stderr(&mut self, file: File) -> &mut Self {
|
||||
self.stderr = Some(PendingEntry::File(file));
|
||||
self
|
||||
}
|
||||
|
||||
/// Add a preopened directory.
|
||||
pub fn preopened_dir<P: AsRef<Path>>(&mut self, dir: File, guest_path: P) -> &mut Self {
|
||||
self.preopens
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.push((guest_path.as_ref().to_owned(), dir));
|
||||
self
|
||||
}
|
||||
|
||||
/// Build a `WasiCtx`, consuming this `WasiCtxBuilder`.
|
||||
///
|
||||
/// If any of the arguments or environment variables in this builder cannot be converted into
|
||||
/// `CString`s, either due to NUL bytes or Unicode conversions, this returns an error.
|
||||
pub fn build(&mut self) -> WasiCtxBuilderResult<WasiCtx> {
|
||||
// Process arguments and environment variables into `CString`s, failing quickly if they
|
||||
// contain any NUL bytes, or if conversion from `OsString` fails.
|
||||
let args = self
|
||||
.args
|
||||
.take()
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(|arg| arg.into_utf8_cstring())
|
||||
.collect::<WasiCtxBuilderResult<Vec<CString>>>()?;
|
||||
|
||||
let env = self
|
||||
.env
|
||||
.take()
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(|(k, v)| {
|
||||
k.into_string().and_then(|mut pair| {
|
||||
v.into_string().and_then(|v| {
|
||||
pair.push('=');
|
||||
pair.push_str(v.as_str());
|
||||
// We have valid UTF-8, but the keys and values have not yet been checked
|
||||
// for NULs, so we do a final check here.
|
||||
let s = CString::new(pair)?;
|
||||
Ok(s)
|
||||
})
|
||||
})
|
||||
})
|
||||
.collect::<WasiCtxBuilderResult<Vec<CString>>>()?;
|
||||
|
||||
let mut fd_pool = FdPool::new();
|
||||
let mut entries: HashMap<wasi::__wasi_fd_t, Entry> = HashMap::new();
|
||||
// Populate the non-preopen fds.
|
||||
for pending in &mut [&mut self.stdin, &mut self.stdout, &mut self.stderr] {
|
||||
let fd = fd_pool
|
||||
.allocate()
|
||||
.ok_or(WasiCtxBuilderError::TooManyFilesOpen)?;
|
||||
tracing::debug!("WasiCtx inserting ({:?}, {:?})", fd, pending);
|
||||
match pending.take().unwrap() {
|
||||
PendingEntry::Thunk(f) => {
|
||||
entries.insert(fd, f()?);
|
||||
}
|
||||
PendingEntry::File(f) => {
|
||||
entries.insert(fd, Entry::from(f)?);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Then add the preopen fds.
|
||||
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)?;
|
||||
|
||||
if !dir.metadata()?.is_dir() {
|
||||
return Err(WasiCtxBuilderError::NotADirectory(guest_path));
|
||||
}
|
||||
|
||||
let mut fe = Entry::from(dir)?;
|
||||
fe.preopen_path = Some(guest_path);
|
||||
tracing::debug!("WasiCtx inserting ({:?}, {:?})", preopen_fd, fe);
|
||||
entries.insert(preopen_fd, fe);
|
||||
tracing::debug!("WasiCtx entries = {:?}", entries);
|
||||
}
|
||||
|
||||
Ok(WasiCtx {
|
||||
args,
|
||||
env,
|
||||
fd_pool,
|
||||
entries,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WasiCtx {
|
||||
fd_pool: FdPool,
|
||||
entries: HashMap<wasi::__wasi_fd_t, Entry>,
|
||||
pub(crate) args: Vec<CString>,
|
||||
pub(crate) env: Vec<CString>,
|
||||
}
|
||||
|
||||
impl WasiCtx {
|
||||
/// Make a new `WasiCtx` with some default settings.
|
||||
///
|
||||
/// - File descriptors 0, 1, and 2 inherit stdin, stdout, and stderr from the host process.
|
||||
///
|
||||
/// - Environment variables are inherited from the host process.
|
||||
///
|
||||
/// To override these behaviors, use `WasiCtxBuilder`.
|
||||
pub fn new<S: AsRef<[u8]>>(args: impl IntoIterator<Item = S>) -> WasiCtxBuilderResult<Self> {
|
||||
WasiCtxBuilder::new()
|
||||
.args(args)
|
||||
.inherit_stdio()
|
||||
.inherit_env()
|
||||
.build()
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
||||
/// Insert the specified `Entry` into the `WasiCtx` object.
|
||||
///
|
||||
/// 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)
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
}
|
||||
@@ -1,208 +0,0 @@
|
||||
use crate::old::snapshot_0::sys::dev_null;
|
||||
use crate::old::snapshot_0::sys::entry_impl::{
|
||||
descriptor_as_oshandle, determine_type_and_access_rights, OsHandle,
|
||||
};
|
||||
use crate::old::snapshot_0::wasi::{self, WasiError, WasiResult};
|
||||
use std::marker::PhantomData;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::path::PathBuf;
|
||||
use std::{fs, io};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum Descriptor {
|
||||
OsHandle(OsHandle),
|
||||
Stdin,
|
||||
Stdout,
|
||||
Stderr,
|
||||
}
|
||||
|
||||
impl Descriptor {
|
||||
/// Return a reference to the `OsHandle` 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(&self) -> WasiResult<&OsHandle> {
|
||||
match self {
|
||||
Self::OsHandle(file) => Ok(file),
|
||||
_ => Err(WasiError::EBADF),
|
||||
}
|
||||
}
|
||||
|
||||
/// Like `as_file`, but return a mutable reference.
|
||||
pub(crate) fn as_file_mut(&mut self) -> WasiResult<&mut OsHandle> {
|
||||
match self {
|
||||
Self::OsHandle(file) => Ok(file),
|
||||
_ => Err(WasiError::EBADF),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return an `OsHandle`, which may be a stream or socket file descriptor.
|
||||
pub(crate) fn as_os_handle<'descriptor>(&'descriptor self) -> OsHandleRef<'descriptor> {
|
||||
descriptor_as_oshandle(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// An abstraction struct serving as a wrapper for a host `Descriptor` object which requires
|
||||
/// certain base rights `rights_base` and inheriting rights `rights_inheriting` in order to be
|
||||
/// 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 `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,
|
||||
descriptor: Descriptor,
|
||||
pub(crate) rights_base: wasi::__wasi_rights_t,
|
||||
pub(crate) rights_inheriting: wasi::__wasi_rights_t,
|
||||
pub(crate) preopen_path: Option<PathBuf>,
|
||||
// TODO: directories
|
||||
}
|
||||
|
||||
impl Entry {
|
||||
/// Create an Entry with *maximal* possible rights from a given `File`.
|
||||
/// If this is not desired, the rights of the resulting `Entry` should
|
||||
/// be manually restricted.
|
||||
pub(crate) fn from(file: fs::File) -> io::Result<Self> {
|
||||
unsafe { determine_type_and_access_rights(&file) }.map(
|
||||
|(file_type, rights_base, rights_inheriting)| Self {
|
||||
file_type,
|
||||
descriptor: Descriptor::OsHandle(OsHandle::from(file)),
|
||||
rights_base,
|
||||
rights_inheriting,
|
||||
preopen_path: None,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn duplicate_stdin() -> io::Result<Self> {
|
||||
unsafe { determine_type_and_access_rights(&io::stdin()) }.map(
|
||||
|(file_type, rights_base, rights_inheriting)| Self {
|
||||
file_type,
|
||||
descriptor: Descriptor::Stdin,
|
||||
rights_base,
|
||||
rights_inheriting,
|
||||
preopen_path: None,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn duplicate_stdout() -> io::Result<Self> {
|
||||
unsafe { determine_type_and_access_rights(&io::stdout()) }.map(
|
||||
|(file_type, rights_base, rights_inheriting)| Self {
|
||||
file_type,
|
||||
descriptor: Descriptor::Stdout,
|
||||
rights_base,
|
||||
rights_inheriting,
|
||||
preopen_path: None,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn duplicate_stderr() -> io::Result<Self> {
|
||||
unsafe { determine_type_and_access_rights(&io::stderr()) }.map(
|
||||
|(file_type, rights_base, rights_inheriting)| Self {
|
||||
file_type,
|
||||
descriptor: Descriptor::Stderr,
|
||||
rights_base,
|
||||
rights_inheriting,
|
||||
preopen_path: None,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn null() -> io::Result<Self> {
|
||||
Self::from(dev_null()?)
|
||||
}
|
||||
|
||||
/// Convert this `Entry` into a host `Descriptor` object provided the specified
|
||||
/// `rights_base` and `rights_inheriting` rights are set on this `Entry` object.
|
||||
///
|
||||
/// 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 `Entry`. The check is performed using
|
||||
/// `Entry::validate_rights` method. If the check fails, `Error::ENOTCAPABLE` is returned.
|
||||
pub(crate) fn as_descriptor(
|
||||
&self,
|
||||
rights_base: wasi::__wasi_rights_t,
|
||||
rights_inheriting: wasi::__wasi_rights_t,
|
||||
) -> WasiResult<&Descriptor> {
|
||||
self.validate_rights(rights_base, rights_inheriting)?;
|
||||
Ok(&self.descriptor)
|
||||
}
|
||||
|
||||
/// 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 `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 `Entry`. The check is performed using
|
||||
/// `Entry::validate_rights` method. If the check fails, `Error::ENOTCAPABLE` 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> {
|
||||
self.validate_rights(rights_base, rights_inheriting)?;
|
||||
Ok(&mut self.descriptor)
|
||||
}
|
||||
|
||||
/// 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, `Error::ENOTCAPABLE` is returned.
|
||||
fn validate_rights(
|
||||
&self,
|
||||
rights_base: wasi::__wasi_rights_t,
|
||||
rights_inheriting: wasi::__wasi_rights_t,
|
||||
) -> WasiResult<()> {
|
||||
if !self.rights_base & rights_base != 0 || !self.rights_inheriting & rights_inheriting != 0
|
||||
{
|
||||
Err(WasiError::ENOTCAPABLE)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Test whether this descriptor is considered a tty within WASI.
|
||||
/// 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
|
||||
}
|
||||
}
|
||||
|
||||
/// This allows an `OsHandle` to be temporarily borrowed from a
|
||||
/// `Descriptor`. The `Descriptor` continues to own the resource,
|
||||
/// and `OsHandleRef`'s lifetime parameter ensures that it doesn't
|
||||
/// outlive the `Descriptor`.
|
||||
pub(crate) struct OsHandleRef<'descriptor> {
|
||||
handle: ManuallyDrop<OsHandle>,
|
||||
_ref: PhantomData<&'descriptor Descriptor>,
|
||||
}
|
||||
|
||||
impl<'descriptor> OsHandleRef<'descriptor> {
|
||||
pub(crate) fn new(handle: ManuallyDrop<OsHandle>) -> Self {
|
||||
OsHandleRef {
|
||||
handle,
|
||||
_ref: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'descriptor> Deref for OsHandleRef<'descriptor> {
|
||||
type Target = fs::File;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.handle
|
||||
}
|
||||
}
|
||||
|
||||
impl<'descriptor> DerefMut for OsHandleRef<'descriptor> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.handle
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
use crate::old::snapshot_0::wasi::WasiResult;
|
||||
use std::str;
|
||||
|
||||
/// Creates not-owned WASI path from byte slice.
|
||||
///
|
||||
/// NB WASI spec requires bytes to be valid UTF-8. Otherwise,
|
||||
/// `__WASI_ERRNO_ILSEQ` error is returned.
|
||||
pub(crate) fn path_from_slice<'a>(s: &'a [u8]) -> WasiResult<&'a str> {
|
||||
let s = str::from_utf8(s)?;
|
||||
Ok(s)
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
//! WASI host types. These are types that contain raw pointers and `usize`
|
||||
//! values, and so are platform-specific.
|
||||
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use crate::old::snapshot_0::wasi::*;
|
||||
use std::{convert::TryInto, io, mem, slice};
|
||||
use wig::witx_host_types;
|
||||
|
||||
witx_host_types!("phases/old/snapshot_0/witx/wasi_unstable.witx");
|
||||
|
||||
pub(crate) unsafe fn ciovec_to_host(ciovec: &__wasi_ciovec_t) -> io::IoSlice {
|
||||
let slice = slice::from_raw_parts(ciovec.buf as *const u8, ciovec.buf_len);
|
||||
io::IoSlice::new(slice)
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn iovec_to_host_mut(iovec: &mut __wasi_iovec_t) -> io::IoSliceMut {
|
||||
let slice = slice::from_raw_parts_mut(iovec.buf as *mut u8, iovec.buf_len);
|
||||
io::IoSliceMut::new(slice)
|
||||
}
|
||||
|
||||
#[allow(dead_code)] // trouble with sockets
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(u8)]
|
||||
pub(crate) enum FileType {
|
||||
Unknown = __WASI_FILETYPE_UNKNOWN,
|
||||
BlockDevice = __WASI_FILETYPE_BLOCK_DEVICE,
|
||||
CharacterDevice = __WASI_FILETYPE_CHARACTER_DEVICE,
|
||||
Directory = __WASI_FILETYPE_DIRECTORY,
|
||||
RegularFile = __WASI_FILETYPE_REGULAR_FILE,
|
||||
SocketDgram = __WASI_FILETYPE_SOCKET_DGRAM,
|
||||
SocketStream = __WASI_FILETYPE_SOCKET_STREAM,
|
||||
Symlink = __WASI_FILETYPE_SYMBOLIC_LINK,
|
||||
}
|
||||
|
||||
impl FileType {
|
||||
pub(crate) fn to_wasi(&self) -> __wasi_filetype_t {
|
||||
*self as __wasi_filetype_t
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct Dirent {
|
||||
pub name: String,
|
||||
pub ftype: FileType,
|
||||
pub ino: u64,
|
||||
pub cookie: __wasi_dircookie_t,
|
||||
}
|
||||
|
||||
impl Dirent {
|
||||
/// Serialize the directory entry to the format define by `__wasi_fd_readdir`,
|
||||
/// so that the serialized entries can be concatenated by the implementation.
|
||||
pub fn to_wasi_raw(&self) -> WasiResult<Vec<u8>> {
|
||||
let name = self.name.as_bytes();
|
||||
let namlen = name.len();
|
||||
let dirent_size = mem::size_of::<__wasi_dirent_t>();
|
||||
let offset = dirent_size
|
||||
.checked_add(namlen)
|
||||
.ok_or(WasiError::EOVERFLOW)?;
|
||||
|
||||
let mut raw = Vec::<u8>::with_capacity(offset);
|
||||
raw.resize(offset, 0);
|
||||
|
||||
let sys_dirent = raw.as_mut_ptr() as *mut __wasi_dirent_t;
|
||||
unsafe {
|
||||
sys_dirent.write_unaligned(__wasi_dirent_t {
|
||||
d_namlen: namlen.try_into()?,
|
||||
d_ino: self.ino,
|
||||
d_next: self.cookie,
|
||||
d_type: self.ftype.to_wasi(),
|
||||
});
|
||||
}
|
||||
|
||||
let sys_name = unsafe { sys_dirent.offset(1) as *mut u8 };
|
||||
let sys_name = unsafe { slice::from_raw_parts_mut(sys_name, namlen) };
|
||||
sys_name.copy_from_slice(&name);
|
||||
|
||||
Ok(raw)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,215 +0,0 @@
|
||||
#![allow(non_camel_case_types)]
|
||||
use crate::old::snapshot_0::entry::Entry;
|
||||
use crate::old::snapshot_0::sys::host_impl;
|
||||
use crate::old::snapshot_0::sys::hostcalls_impl::fs_helpers::*;
|
||||
use crate::old::snapshot_0::wasi::{self, WasiError, WasiResult};
|
||||
use std::fs::File;
|
||||
use std::path::{Component, Path};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct PathGet {
|
||||
dirfd: File,
|
||||
path: String,
|
||||
}
|
||||
|
||||
impl PathGet {
|
||||
pub(crate) fn dirfd(&self) -> &File {
|
||||
&self.dirfd
|
||||
}
|
||||
|
||||
pub(crate) fn path(&self) -> &str {
|
||||
&self.path
|
||||
}
|
||||
}
|
||||
|
||||
/// Normalizes a path to ensure that the target path is located under the directory provided.
|
||||
///
|
||||
/// This is a workaround for not having Capsicum support in the OS.
|
||||
pub(crate) fn path_get(
|
||||
fe: &Entry,
|
||||
rights_base: wasi::__wasi_rights_t,
|
||||
rights_inheriting: wasi::__wasi_rights_t,
|
||||
dirflags: wasi::__wasi_lookupflags_t,
|
||||
path: &str,
|
||||
needs_final_component: bool,
|
||||
) -> WasiResult<PathGet> {
|
||||
const MAX_SYMLINK_EXPANSIONS: usize = 128;
|
||||
|
||||
if path.contains('\0') {
|
||||
// if contains NUL, return EILSEQ
|
||||
return Err(WasiError::EILSEQ);
|
||||
}
|
||||
|
||||
if fe.file_type != wasi::__WASI_FILETYPE_DIRECTORY {
|
||||
// if `dirfd` doesn't refer to a directory, return `ENOTDIR`.
|
||||
return Err(WasiError::ENOTDIR);
|
||||
}
|
||||
|
||||
let dirfd = fe
|
||||
.as_descriptor(rights_base, rights_inheriting)?
|
||||
.as_file()?
|
||||
.try_clone()?;
|
||||
|
||||
// Stack of directory file descriptors. Index 0 always corresponds with the directory provided
|
||||
// to this function. Entering a directory causes a file descriptor to be pushed, while handling
|
||||
// ".." entries causes an entry to be popped. Index 0 cannot be popped, as this would imply
|
||||
// escaping the base directory.
|
||||
let mut dir_stack = vec![dirfd];
|
||||
|
||||
// Stack of paths left to process. This is initially the `path` argument to this function, but
|
||||
// any symlinks we encounter are processed by pushing them on the stack.
|
||||
let mut path_stack = vec![path.to_owned()];
|
||||
|
||||
// Track the number of symlinks we've expanded, so we can return `ELOOP` after too many.
|
||||
let mut symlink_expansions = 0;
|
||||
|
||||
// TODO: rewrite this using a custom posix path type, with a component iterator that respects
|
||||
// trailing slashes. This version does way too much allocation, and is way too fiddly.
|
||||
loop {
|
||||
match path_stack.pop() {
|
||||
Some(cur_path) => {
|
||||
tracing::debug!("path_get cur_path = {:?}", cur_path);
|
||||
|
||||
let ends_with_slash = cur_path.ends_with('/');
|
||||
let mut components = Path::new(&cur_path).components();
|
||||
let head = match components.next() {
|
||||
None => return Err(WasiError::ENOENT),
|
||||
Some(p) => p,
|
||||
};
|
||||
let tail = components.as_path();
|
||||
|
||||
if tail.components().next().is_some() {
|
||||
let mut tail = host_impl::path_from_host(tail.as_os_str())?;
|
||||
if ends_with_slash {
|
||||
tail.push('/');
|
||||
}
|
||||
path_stack.push(tail);
|
||||
}
|
||||
|
||||
tracing::debug!("path_get path_stack = {:?}", path_stack);
|
||||
|
||||
match head {
|
||||
Component::Prefix(_) | Component::RootDir => {
|
||||
// path is absolute!
|
||||
return Err(WasiError::ENOTCAPABLE);
|
||||
}
|
||||
Component::CurDir => {
|
||||
// "." so skip
|
||||
}
|
||||
Component::ParentDir => {
|
||||
// ".." so pop a dir
|
||||
let _ = dir_stack.pop().ok_or(WasiError::ENOTCAPABLE)?;
|
||||
|
||||
// we're not allowed to pop past the original directory
|
||||
if dir_stack.is_empty() {
|
||||
return Err(WasiError::ENOTCAPABLE);
|
||||
}
|
||||
}
|
||||
Component::Normal(head) => {
|
||||
let mut head = host_impl::path_from_host(head)?;
|
||||
if ends_with_slash {
|
||||
// preserve trailing slash
|
||||
head.push('/');
|
||||
}
|
||||
|
||||
if !path_stack.is_empty() || (ends_with_slash && !needs_final_component) {
|
||||
match openat(dir_stack.last().ok_or(WasiError::ENOTCAPABLE)?, &head) {
|
||||
Ok(new_dir) => {
|
||||
dir_stack.push(new_dir);
|
||||
}
|
||||
Err(e) => {
|
||||
match e {
|
||||
WasiError::ELOOP
|
||||
| WasiError::EMLINK
|
||||
| WasiError::ENOTDIR =>
|
||||
// Check to see if it was a symlink. Linux indicates
|
||||
// this with ENOTDIR because of the O_DIRECTORY flag.
|
||||
{
|
||||
// attempt symlink expansion
|
||||
let mut link_path = readlinkat(
|
||||
dir_stack.last().ok_or(WasiError::ENOTCAPABLE)?,
|
||||
&head,
|
||||
)?;
|
||||
|
||||
symlink_expansions += 1;
|
||||
if symlink_expansions > MAX_SYMLINK_EXPANSIONS {
|
||||
return Err(WasiError::ELOOP);
|
||||
}
|
||||
|
||||
if head.ends_with('/') {
|
||||
link_path.push('/');
|
||||
}
|
||||
|
||||
tracing::debug!(
|
||||
"attempted symlink expansion link_path={:?}",
|
||||
link_path
|
||||
);
|
||||
|
||||
path_stack.push(link_path);
|
||||
}
|
||||
_ => {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
} else if ends_with_slash
|
||||
|| (dirflags & wasi::__WASI_LOOKUPFLAGS_SYMLINK_FOLLOW) != 0
|
||||
{
|
||||
// if there's a trailing slash, or if `LOOKUP_SYMLINK_FOLLOW` is set, attempt
|
||||
// symlink expansion
|
||||
match readlinkat(dir_stack.last().ok_or(WasiError::ENOTCAPABLE)?, &head)
|
||||
{
|
||||
Ok(mut link_path) => {
|
||||
symlink_expansions += 1;
|
||||
if symlink_expansions > MAX_SYMLINK_EXPANSIONS {
|
||||
return Err(WasiError::ELOOP);
|
||||
}
|
||||
|
||||
if head.ends_with('/') {
|
||||
link_path.push('/');
|
||||
}
|
||||
|
||||
tracing::debug!(
|
||||
"attempted symlink expansion link_path={:?}",
|
||||
link_path
|
||||
);
|
||||
|
||||
path_stack.push(link_path);
|
||||
continue;
|
||||
}
|
||||
Err(e) => {
|
||||
if e != WasiError::EINVAL
|
||||
&& e != WasiError::ENOENT
|
||||
// this handles the cases when trying to link to
|
||||
// a destination that already exists, and the target
|
||||
// path contains a slash
|
||||
&& e != WasiError::ENOTDIR
|
||||
{
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// not a symlink, so we're done;
|
||||
return Ok(PathGet {
|
||||
dirfd: dir_stack.pop().ok_or(WasiError::ENOTCAPABLE)?,
|
||||
path: head,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// no further components to process. means we've hit a case like "." or "a/..", or if the
|
||||
// input path has trailing slashes and `needs_final_component` is not set
|
||||
return Ok(PathGet {
|
||||
dirfd: dir_stack.pop().ok_or(WasiError::ENOTCAPABLE)?,
|
||||
path: String::from("."),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,351 +0,0 @@
|
||||
#![allow(non_camel_case_types)]
|
||||
use crate::old::snapshot_0::ctx::WasiCtx;
|
||||
use crate::old::snapshot_0::entry::Descriptor;
|
||||
use crate::old::snapshot_0::memory::*;
|
||||
use crate::old::snapshot_0::sys::hostcalls_impl;
|
||||
use crate::old::snapshot_0::wasi::{self, WasiError, WasiResult};
|
||||
use crate::old::snapshot_0::wasi32;
|
||||
use std::convert::TryFrom;
|
||||
use tracing::{error, trace};
|
||||
|
||||
pub(crate) fn args_get(
|
||||
wasi_ctx: &WasiCtx,
|
||||
memory: &mut [u8],
|
||||
argv_ptr: wasi32::uintptr_t,
|
||||
argv_buf: wasi32::uintptr_t,
|
||||
) -> WasiResult<()> {
|
||||
trace!(
|
||||
"args_get(argv_ptr={:#x?}, argv_buf={:#x?})",
|
||||
argv_ptr,
|
||||
argv_buf,
|
||||
);
|
||||
|
||||
let mut argv_buf_offset = 0;
|
||||
let mut argv = vec![];
|
||||
|
||||
for arg in &wasi_ctx.args {
|
||||
let arg_bytes = arg.as_bytes_with_nul();
|
||||
let arg_ptr = argv_buf + argv_buf_offset;
|
||||
|
||||
enc_slice_of_u8(memory, arg_bytes, arg_ptr)?;
|
||||
|
||||
argv.push(arg_ptr);
|
||||
|
||||
let len = wasi32::uintptr_t::try_from(arg_bytes.len())?;
|
||||
argv_buf_offset = argv_buf_offset
|
||||
.checked_add(len)
|
||||
.ok_or(WasiError::EOVERFLOW)?;
|
||||
}
|
||||
|
||||
enc_slice_of_wasi32_uintptr(memory, argv.as_slice(), argv_ptr)
|
||||
}
|
||||
|
||||
pub(crate) fn args_sizes_get(
|
||||
wasi_ctx: &WasiCtx,
|
||||
memory: &mut [u8],
|
||||
argc_ptr: wasi32::uintptr_t,
|
||||
argv_buf_size_ptr: wasi32::uintptr_t,
|
||||
) -> WasiResult<()> {
|
||||
trace!(
|
||||
"args_sizes_get(argc_ptr={:#x?}, argv_buf_size_ptr={:#x?})",
|
||||
argc_ptr,
|
||||
argv_buf_size_ptr,
|
||||
);
|
||||
|
||||
let argc = wasi_ctx.args.len();
|
||||
let argv_size = wasi_ctx
|
||||
.args
|
||||
.iter()
|
||||
.map(|arg| arg.as_bytes_with_nul().len())
|
||||
.sum();
|
||||
|
||||
trace!(" | *argc_ptr={:?}", argc);
|
||||
|
||||
enc_usize_byref(memory, argc_ptr, argc)?;
|
||||
|
||||
trace!(" | *argv_buf_size_ptr={:?}", argv_size);
|
||||
|
||||
enc_usize_byref(memory, argv_buf_size_ptr, argv_size)
|
||||
}
|
||||
|
||||
pub(crate) fn environ_get(
|
||||
wasi_ctx: &WasiCtx,
|
||||
memory: &mut [u8],
|
||||
environ_ptr: wasi32::uintptr_t,
|
||||
environ_buf: wasi32::uintptr_t,
|
||||
) -> WasiResult<()> {
|
||||
trace!(
|
||||
"environ_get(environ_ptr={:#x?}, environ_buf={:#x?})",
|
||||
environ_ptr,
|
||||
environ_buf,
|
||||
);
|
||||
|
||||
let mut environ_buf_offset = 0;
|
||||
let mut environ = vec![];
|
||||
|
||||
for pair in &wasi_ctx.env {
|
||||
let env_bytes = pair.as_bytes_with_nul();
|
||||
let env_ptr = environ_buf + environ_buf_offset;
|
||||
|
||||
enc_slice_of_u8(memory, env_bytes, env_ptr)?;
|
||||
|
||||
environ.push(env_ptr);
|
||||
|
||||
let len = wasi32::uintptr_t::try_from(env_bytes.len())?;
|
||||
environ_buf_offset = environ_buf_offset
|
||||
.checked_add(len)
|
||||
.ok_or(WasiError::EOVERFLOW)?;
|
||||
}
|
||||
|
||||
enc_slice_of_wasi32_uintptr(memory, environ.as_slice(), environ_ptr)
|
||||
}
|
||||
|
||||
pub(crate) fn environ_sizes_get(
|
||||
wasi_ctx: &WasiCtx,
|
||||
memory: &mut [u8],
|
||||
environ_count_ptr: wasi32::uintptr_t,
|
||||
environ_size_ptr: wasi32::uintptr_t,
|
||||
) -> WasiResult<()> {
|
||||
trace!(
|
||||
"environ_sizes_get(environ_count_ptr={:#x?}, environ_size_ptr={:#x?})",
|
||||
environ_count_ptr,
|
||||
environ_size_ptr,
|
||||
);
|
||||
|
||||
let environ_count = wasi_ctx.env.len();
|
||||
let environ_size = wasi_ctx
|
||||
.env
|
||||
.iter()
|
||||
.try_fold(0, |acc: u32, pair| {
|
||||
acc.checked_add(pair.as_bytes_with_nul().len() as u32)
|
||||
})
|
||||
.ok_or(WasiError::EOVERFLOW)?;
|
||||
|
||||
trace!(" | *environ_count_ptr={:?}", environ_count);
|
||||
|
||||
enc_usize_byref(memory, environ_count_ptr, environ_count)?;
|
||||
|
||||
trace!(" | *environ_size_ptr={:?}", environ_size);
|
||||
|
||||
enc_usize_byref(memory, environ_size_ptr, environ_size as usize)
|
||||
}
|
||||
|
||||
pub(crate) fn random_get(
|
||||
_wasi_ctx: &WasiCtx,
|
||||
memory: &mut [u8],
|
||||
buf_ptr: wasi32::uintptr_t,
|
||||
buf_len: wasi32::size_t,
|
||||
) -> WasiResult<()> {
|
||||
trace!("random_get(buf_ptr={:#x?}, buf_len={:?})", buf_ptr, buf_len);
|
||||
|
||||
let buf = dec_slice_of_mut_u8(memory, buf_ptr, buf_len)?;
|
||||
|
||||
getrandom::getrandom(buf).map_err(|err| {
|
||||
error!("getrandom failure: {:?}", err);
|
||||
WasiError::EIO
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn clock_res_get(
|
||||
_wasi_ctx: &WasiCtx,
|
||||
memory: &mut [u8],
|
||||
clock_id: wasi::__wasi_clockid_t,
|
||||
resolution_ptr: wasi32::uintptr_t,
|
||||
) -> WasiResult<()> {
|
||||
trace!(
|
||||
"clock_res_get(clock_id={:?}, resolution_ptr={:#x?})",
|
||||
clock_id,
|
||||
resolution_ptr,
|
||||
);
|
||||
|
||||
let resolution = hostcalls_impl::clock_res_get(clock_id)?;
|
||||
|
||||
trace!(" | *resolution_ptr={:?}", resolution);
|
||||
|
||||
enc_timestamp_byref(memory, resolution_ptr, resolution)
|
||||
}
|
||||
|
||||
pub(crate) fn clock_time_get(
|
||||
_wasi_ctx: &WasiCtx,
|
||||
memory: &mut [u8],
|
||||
clock_id: wasi::__wasi_clockid_t,
|
||||
precision: wasi::__wasi_timestamp_t,
|
||||
time_ptr: wasi32::uintptr_t,
|
||||
) -> WasiResult<()> {
|
||||
trace!(
|
||||
"clock_time_get(clock_id={:?}, precision={:?}, time_ptr={:#x?})",
|
||||
clock_id,
|
||||
precision,
|
||||
time_ptr,
|
||||
);
|
||||
|
||||
let time = hostcalls_impl::clock_time_get(clock_id)?;
|
||||
|
||||
trace!(" | *time_ptr={:?}", time);
|
||||
|
||||
enc_timestamp_byref(memory, time_ptr, time)
|
||||
}
|
||||
|
||||
pub(crate) fn sched_yield(_wasi_ctx: &WasiCtx, _memory: &mut [u8]) -> WasiResult<()> {
|
||||
trace!("sched_yield()");
|
||||
|
||||
std::thread::yield_now();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn poll_oneoff(
|
||||
wasi_ctx: &WasiCtx,
|
||||
memory: &mut [u8],
|
||||
input: wasi32::uintptr_t,
|
||||
output: wasi32::uintptr_t,
|
||||
nsubscriptions: wasi32::size_t,
|
||||
nevents: wasi32::uintptr_t,
|
||||
) -> WasiResult<()> {
|
||||
trace!(
|
||||
"poll_oneoff(input={:#x?}, output={:#x?}, nsubscriptions={}, nevents={:#x?})",
|
||||
input,
|
||||
output,
|
||||
nsubscriptions,
|
||||
nevents,
|
||||
);
|
||||
|
||||
if u64::from(nsubscriptions) > wasi::__wasi_filesize_t::max_value() {
|
||||
return Err(WasiError::EINVAL);
|
||||
}
|
||||
|
||||
enc_int_byref(memory, nevents, 0)?;
|
||||
|
||||
let subscriptions = dec_subscriptions(memory, input, nsubscriptions)?;
|
||||
let mut events = Vec::new();
|
||||
|
||||
let mut timeout: Option<ClockEventData> = None;
|
||||
let mut fd_events = Vec::new();
|
||||
for subscription in subscriptions {
|
||||
match subscription.u.tag {
|
||||
wasi::__WASI_EVENTTYPE_CLOCK => {
|
||||
let clock = unsafe { subscription.u.u.clock };
|
||||
let delay = wasi_clock_to_relative_ns_delay(clock)?;
|
||||
|
||||
tracing::debug!("poll_oneoff event.u.clock = {:?}", clock);
|
||||
tracing::debug!("poll_oneoff delay = {:?}ns", delay);
|
||||
|
||||
let current = ClockEventData {
|
||||
delay,
|
||||
userdata: subscription.userdata,
|
||||
};
|
||||
let timeout = timeout.get_or_insert(current);
|
||||
if current.delay < timeout.delay {
|
||||
*timeout = current;
|
||||
}
|
||||
}
|
||||
|
||||
wasi::__WASI_EVENTTYPE_FD_READ => {
|
||||
let wasi_fd = unsafe { subscription.u.u.fd_read.file_descriptor };
|
||||
let rights = wasi::__WASI_RIGHTS_FD_READ | wasi::__WASI_RIGHTS_POLL_FD_READWRITE;
|
||||
match unsafe {
|
||||
wasi_ctx
|
||||
.get_entry(wasi_fd)
|
||||
.and_then(|fe| fe.as_descriptor(rights, 0))
|
||||
} {
|
||||
Ok(descriptor) => fd_events.push(FdEventData {
|
||||
descriptor,
|
||||
r#type: wasi::__WASI_EVENTTYPE_FD_READ,
|
||||
userdata: subscription.userdata,
|
||||
}),
|
||||
Err(err) => {
|
||||
let event = wasi::__wasi_event_t {
|
||||
userdata: subscription.userdata,
|
||||
error: err.as_raw_errno(),
|
||||
r#type: wasi::__WASI_EVENTTYPE_FD_READ,
|
||||
fd_readwrite: wasi::__wasi_event_fd_readwrite_t {
|
||||
nbytes: 0,
|
||||
flags: 0,
|
||||
},
|
||||
};
|
||||
events.push(event);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
wasi::__WASI_EVENTTYPE_FD_WRITE => {
|
||||
let wasi_fd = unsafe { subscription.u.u.fd_write.file_descriptor };
|
||||
let rights = wasi::__WASI_RIGHTS_FD_WRITE | wasi::__WASI_RIGHTS_POLL_FD_READWRITE;
|
||||
match unsafe {
|
||||
wasi_ctx
|
||||
.get_entry(wasi_fd)
|
||||
.and_then(|fe| fe.as_descriptor(rights, 0))
|
||||
} {
|
||||
Ok(descriptor) => fd_events.push(FdEventData {
|
||||
descriptor,
|
||||
r#type: wasi::__WASI_EVENTTYPE_FD_WRITE,
|
||||
userdata: subscription.userdata,
|
||||
}),
|
||||
Err(err) => {
|
||||
let event = wasi::__wasi_event_t {
|
||||
userdata: subscription.userdata,
|
||||
error: err.as_raw_errno(),
|
||||
r#type: wasi::__WASI_EVENTTYPE_FD_WRITE,
|
||||
fd_readwrite: wasi::__wasi_event_fd_readwrite_t {
|
||||
nbytes: 0,
|
||||
flags: 0,
|
||||
},
|
||||
};
|
||||
events.push(event);
|
||||
}
|
||||
};
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
tracing::debug!("poll_oneoff timeout = {:?}", timeout);
|
||||
tracing::debug!("poll_oneoff fd_events = {:?}", fd_events);
|
||||
|
||||
hostcalls_impl::poll_oneoff(timeout, fd_events, &mut events)?;
|
||||
|
||||
let events_count = u32::try_from(events.len()).map_err(|_| WasiError::EOVERFLOW)?;
|
||||
|
||||
enc_events(memory, output, nsubscriptions, events)?;
|
||||
|
||||
trace!(" | *nevents={:?}", events_count);
|
||||
|
||||
enc_int_byref(memory, nevents, events_count)
|
||||
}
|
||||
|
||||
fn wasi_clock_to_relative_ns_delay(
|
||||
wasi_clock: wasi::__wasi_subscription_clock_t,
|
||||
) -> WasiResult<u128> {
|
||||
use std::time::SystemTime;
|
||||
|
||||
if wasi_clock.flags != wasi::__WASI_SUBCLOCKFLAGS_SUBSCRIPTION_CLOCK_ABSTIME {
|
||||
return Ok(u128::from(wasi_clock.timeout));
|
||||
}
|
||||
let now: u128 = SystemTime::now()
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.map_err(|_| WasiError::ENOTCAPABLE)?
|
||||
.as_nanos();
|
||||
let deadline = u128::from(wasi_clock.timeout);
|
||||
Ok(deadline.saturating_sub(now))
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub(crate) struct ClockEventData {
|
||||
pub(crate) delay: u128, // delay is expressed in nanoseconds
|
||||
pub(crate) userdata: wasi::__wasi_userdata_t,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct FdEventData<'a> {
|
||||
pub(crate) descriptor: &'a Descriptor,
|
||||
pub(crate) r#type: wasi::__wasi_eventtype_t,
|
||||
pub(crate) userdata: wasi::__wasi_userdata_t,
|
||||
}
|
||||
|
||||
pub(crate) fn proc_raise(
|
||||
_wasi_ctx: &WasiCtx,
|
||||
_memory: &mut [u8],
|
||||
_sig: wasi::__wasi_signal_t,
|
||||
) -> WasiResult<()> {
|
||||
unimplemented!("proc_raise")
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
mod fs;
|
||||
mod fs_helpers;
|
||||
mod misc;
|
||||
mod sock;
|
||||
|
||||
pub(crate) use self::fs::*;
|
||||
pub(crate) use self::fs_helpers::PathGet;
|
||||
pub(crate) use self::misc::*;
|
||||
pub(crate) use self::sock::*;
|
||||
@@ -1,36 +0,0 @@
|
||||
use crate::old::snapshot_0::wasi::{self, WasiResult};
|
||||
use crate::old::snapshot_0::{wasi32, WasiCtx};
|
||||
|
||||
pub fn sock_recv(
|
||||
_wasi_ctx: &WasiCtx,
|
||||
_memory: &mut [u8],
|
||||
_sock: wasi::__wasi_fd_t,
|
||||
_ri_data: wasi32::uintptr_t,
|
||||
_ri_data_len: wasi32::size_t,
|
||||
_ri_flags: wasi::__wasi_riflags_t,
|
||||
_ro_datalen: wasi32::uintptr_t,
|
||||
_ro_flags: wasi32::uintptr_t,
|
||||
) -> WasiResult<()> {
|
||||
unimplemented!("sock_recv")
|
||||
}
|
||||
|
||||
pub fn sock_send(
|
||||
_wasi_ctx: &WasiCtx,
|
||||
_memory: &mut [u8],
|
||||
_sock: wasi::__wasi_fd_t,
|
||||
_si_data: wasi32::uintptr_t,
|
||||
_si_data_len: wasi32::size_t,
|
||||
_si_flags: wasi::__wasi_siflags_t,
|
||||
_so_datalen: wasi32::uintptr_t,
|
||||
) -> WasiResult<()> {
|
||||
unimplemented!("sock_send")
|
||||
}
|
||||
|
||||
pub fn sock_shutdown(
|
||||
_wasi_ctx: &WasiCtx,
|
||||
_memory: &mut [u8],
|
||||
_sock: wasi::__wasi_fd_t,
|
||||
_how: wasi::__wasi_sdflags_t,
|
||||
) -> WasiResult<()> {
|
||||
unimplemented!("sock_shutdown")
|
||||
}
|
||||
@@ -1,501 +0,0 @@
|
||||
//! Functions to store and load data to and from wasm linear memory,
|
||||
//! transforming them from and to host data types.
|
||||
//!
|
||||
//! Endianness concerns are completely encapsulated in this file, so
|
||||
//! that users outside this file holding a `wasi::*` value never need
|
||||
//! to consider what endianness it's in. Inside this file,
|
||||
//! wasm linear-memory-ordered values are called "raw" values, and
|
||||
//! are not held for long durations.
|
||||
|
||||
#![allow(unused)]
|
||||
use crate::old::snapshot_0::wasi::{self, WasiError, WasiResult};
|
||||
use crate::old::snapshot_0::{host, wasi32};
|
||||
use std::convert::TryFrom;
|
||||
use std::mem::{align_of, size_of};
|
||||
use std::{ptr, slice};
|
||||
|
||||
pub(crate) trait FromLe {
|
||||
fn from_le(bits: Self) -> Self;
|
||||
}
|
||||
|
||||
fn from_le<T: FromLe>(bits: T) -> T {
|
||||
FromLe::from_le(bits)
|
||||
}
|
||||
|
||||
macro_rules! impl_from_le {
|
||||
($($i:ident)*) => ($(
|
||||
impl FromLe for $i {
|
||||
fn from_le(bits: Self) -> Self {
|
||||
<$i>::from_le(bits)
|
||||
}
|
||||
}
|
||||
)*)
|
||||
}
|
||||
impl_from_le! {
|
||||
i8 i16 i32 i64 isize
|
||||
u8 u16 u32 u64 usize
|
||||
}
|
||||
|
||||
fn dec_ptr(memory: &[u8], ptr: wasi32::uintptr_t, len: usize) -> WasiResult<*const u8> {
|
||||
// check for overflow
|
||||
let checked_len = (ptr as usize).checked_add(len).ok_or(WasiError::EFAULT)?;
|
||||
|
||||
// translate the pointer
|
||||
memory
|
||||
.get(ptr as usize..checked_len)
|
||||
.ok_or(WasiError::EFAULT)
|
||||
.map(|mem| mem.as_ptr())
|
||||
}
|
||||
|
||||
fn dec_ptr_mut(memory: &mut [u8], ptr: wasi32::uintptr_t, len: usize) -> WasiResult<*mut u8> {
|
||||
// check for overflow
|
||||
let checked_len = (ptr as usize).checked_add(len).ok_or(WasiError::EFAULT)?;
|
||||
|
||||
// translate the pointer
|
||||
memory
|
||||
.get_mut(ptr as usize..checked_len)
|
||||
.ok_or(WasiError::EFAULT)
|
||||
.map(|mem| mem.as_mut_ptr())
|
||||
}
|
||||
|
||||
fn dec_ptr_to<'memory, T>(memory: &'memory [u8], ptr: wasi32::uintptr_t) -> WasiResult<&'memory T> {
|
||||
// check that the ptr is aligned
|
||||
if ptr as usize % align_of::<T>() != 0 {
|
||||
return Err(WasiError::EINVAL);
|
||||
}
|
||||
|
||||
dec_ptr(memory, ptr, size_of::<T>()).map(|p| unsafe { &*(p as *const T) })
|
||||
}
|
||||
|
||||
fn dec_ptr_to_mut<'memory, T>(
|
||||
memory: &'memory mut [u8],
|
||||
ptr: wasi32::uintptr_t,
|
||||
) -> WasiResult<&'memory mut T> {
|
||||
// check that the ptr is aligned
|
||||
if ptr as usize % align_of::<T>() != 0 {
|
||||
return Err(WasiError::EINVAL);
|
||||
}
|
||||
|
||||
dec_ptr_mut(memory, ptr, size_of::<T>()).map(|p| unsafe { &mut *(p as *mut T) })
|
||||
}
|
||||
|
||||
/// This function does not perform endianness conversions!
|
||||
fn dec_raw_byref<T>(memory: &[u8], ptr: wasi32::uintptr_t) -> WasiResult<T> {
|
||||
dec_ptr_to::<T>(memory, ptr).map(|p| unsafe { ptr::read(p) })
|
||||
}
|
||||
|
||||
/// This function does not perform endianness conversions!
|
||||
fn enc_raw_byref<T>(memory: &mut [u8], ptr: wasi32::uintptr_t, t: T) -> WasiResult<()> {
|
||||
dec_ptr_to_mut::<T>(memory, ptr).map(|p| unsafe { ptr::write(p, t) })
|
||||
}
|
||||
|
||||
pub(crate) fn dec_int_byref<T>(memory: &[u8], ptr: wasi32::uintptr_t) -> WasiResult<T>
|
||||
where
|
||||
T: FromLe,
|
||||
{
|
||||
dec_raw_byref::<T>(memory, ptr).map(|i| FromLe::from_le(i))
|
||||
}
|
||||
|
||||
pub(crate) fn enc_int_byref<T>(memory: &mut [u8], ptr: wasi32::uintptr_t, t: T) -> WasiResult<()> {
|
||||
enc_raw_byref::<T>(memory, ptr, t)
|
||||
}
|
||||
|
||||
fn check_slice_of<T>(ptr: wasi32::uintptr_t, len: wasi32::size_t) -> WasiResult<(usize, usize)> {
|
||||
// check alignment, and that length doesn't overflow
|
||||
if ptr as usize % align_of::<T>() != 0 {
|
||||
return Err(WasiError::EINVAL);
|
||||
}
|
||||
let len = dec_usize(len);
|
||||
let len_bytes = if let Some(len) = size_of::<T>().checked_mul(len) {
|
||||
len
|
||||
} else {
|
||||
return Err(WasiError::EOVERFLOW);
|
||||
};
|
||||
|
||||
Ok((len, len_bytes))
|
||||
}
|
||||
|
||||
fn dec_raw_slice_of<'memory, T>(
|
||||
memory: &'memory [u8],
|
||||
ptr: wasi32::uintptr_t,
|
||||
len: wasi32::size_t,
|
||||
) -> WasiResult<&'memory [T]> {
|
||||
let (len, len_bytes) = check_slice_of::<T>(ptr, len)?;
|
||||
let ptr = dec_ptr(memory, ptr, len_bytes)? as *const T;
|
||||
Ok(unsafe { slice::from_raw_parts(ptr, len) })
|
||||
}
|
||||
|
||||
fn dec_raw_slice_of_mut<'memory, T>(
|
||||
memory: &'memory mut [u8],
|
||||
ptr: wasi32::uintptr_t,
|
||||
len: wasi32::size_t,
|
||||
) -> WasiResult<&'memory mut [T]> {
|
||||
let (len, len_bytes) = check_slice_of::<T>(ptr, len)?;
|
||||
let ptr = dec_ptr_mut(memory, ptr, len_bytes)? as *mut T;
|
||||
Ok(unsafe { slice::from_raw_parts_mut(ptr, len) })
|
||||
}
|
||||
|
||||
fn raw_slice_for_enc<'memory, T>(
|
||||
memory: &'memory mut [u8],
|
||||
slice: &[T],
|
||||
ptr: wasi32::uintptr_t,
|
||||
) -> WasiResult<&'memory mut [T]> {
|
||||
// check alignment
|
||||
if ptr as usize % align_of::<T>() != 0 {
|
||||
return Err(WasiError::EINVAL);
|
||||
}
|
||||
// check that length doesn't overflow
|
||||
let len_bytes = if let Some(len) = size_of::<T>().checked_mul(slice.len()) {
|
||||
len
|
||||
} else {
|
||||
return Err(WasiError::EOVERFLOW);
|
||||
};
|
||||
|
||||
// get the pointer into guest memory
|
||||
let ptr = dec_ptr_mut(memory, ptr, len_bytes)? as *mut T;
|
||||
|
||||
Ok(unsafe { slice::from_raw_parts_mut(ptr, slice.len()) })
|
||||
}
|
||||
|
||||
pub(crate) fn dec_slice_of_u8<'memory>(
|
||||
memory: &'memory [u8],
|
||||
ptr: wasi32::uintptr_t,
|
||||
len: wasi32::size_t,
|
||||
) -> WasiResult<&'memory [u8]> {
|
||||
dec_raw_slice_of::<u8>(memory, ptr, len)
|
||||
}
|
||||
|
||||
pub(crate) fn dec_slice_of_mut_u8<'memory>(
|
||||
memory: &'memory mut [u8],
|
||||
ptr: wasi32::uintptr_t,
|
||||
len: wasi32::size_t,
|
||||
) -> WasiResult<&'memory mut [u8]> {
|
||||
dec_raw_slice_of_mut::<u8>(memory, ptr, len)
|
||||
}
|
||||
|
||||
pub(crate) fn enc_slice_of_u8(
|
||||
memory: &mut [u8],
|
||||
slice: &[u8],
|
||||
ptr: wasi32::uintptr_t,
|
||||
) -> WasiResult<()> {
|
||||
let output = raw_slice_for_enc::<u8>(memory, slice, ptr)?;
|
||||
|
||||
output.copy_from_slice(slice);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn enc_slice_of_wasi32_uintptr(
|
||||
memory: &mut [u8],
|
||||
slice: &[wasi32::uintptr_t],
|
||||
ptr: wasi32::uintptr_t,
|
||||
) -> WasiResult<()> {
|
||||
let mut output_iter = raw_slice_for_enc::<wasi32::uintptr_t>(memory, slice, ptr)?.into_iter();
|
||||
|
||||
for p in slice {
|
||||
*output_iter.next().unwrap() = p.to_le();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
macro_rules! dec_enc_scalar {
|
||||
($ty:ident, $dec_byref:ident, $enc_byref:ident) => {
|
||||
pub(crate) fn $dec_byref(
|
||||
memory: &mut [u8],
|
||||
ptr: wasi32::uintptr_t,
|
||||
) -> WasiResult<wasi::$ty> {
|
||||
dec_int_byref::<wasi::$ty>(memory, ptr)
|
||||
}
|
||||
|
||||
pub(crate) fn $enc_byref(
|
||||
memory: &mut [u8],
|
||||
ptr: wasi32::uintptr_t,
|
||||
x: wasi::$ty,
|
||||
) -> WasiResult<()> {
|
||||
enc_int_byref::<wasi::$ty>(memory, ptr, x.to_le())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) fn dec_ciovec_slice(
|
||||
memory: &[u8],
|
||||
ptr: wasi32::uintptr_t,
|
||||
len: wasi32::size_t,
|
||||
) -> WasiResult<Vec<host::__wasi_ciovec_t>> {
|
||||
let raw_slice = dec_raw_slice_of::<wasi32::__wasi_ciovec_t>(memory, ptr, len)?;
|
||||
|
||||
raw_slice
|
||||
.iter()
|
||||
.map(|raw_iov| {
|
||||
let len = dec_usize(from_le(raw_iov.buf_len));
|
||||
let buf = from_le(raw_iov.buf);
|
||||
Ok(host::__wasi_ciovec_t {
|
||||
buf: dec_ptr(memory, buf, len)? as *const u8,
|
||||
buf_len: len,
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn dec_iovec_slice(
|
||||
memory: &[u8],
|
||||
ptr: wasi32::uintptr_t,
|
||||
len: wasi32::size_t,
|
||||
) -> WasiResult<Vec<host::__wasi_iovec_t>> {
|
||||
let raw_slice = dec_raw_slice_of::<wasi32::__wasi_iovec_t>(memory, ptr, len)?;
|
||||
|
||||
raw_slice
|
||||
.iter()
|
||||
.map(|raw_iov| {
|
||||
let len = dec_usize(from_le(raw_iov.buf_len));
|
||||
let buf = from_le(raw_iov.buf);
|
||||
Ok(host::__wasi_iovec_t {
|
||||
buf: dec_ptr(memory, buf, len)? as *mut u8,
|
||||
buf_len: len,
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
dec_enc_scalar!(__wasi_clockid_t, dec_clockid_byref, enc_clockid_byref);
|
||||
dec_enc_scalar!(__wasi_errno_t, dec_errno_byref, enc_errno_byref);
|
||||
dec_enc_scalar!(__wasi_exitcode_t, dec_exitcode_byref, enc_exitcode_byref);
|
||||
dec_enc_scalar!(__wasi_fd_t, dec_fd_byref, enc_fd_byref);
|
||||
dec_enc_scalar!(__wasi_fdflags_t, dec_fdflags_byref, enc_fdflags_byref);
|
||||
dec_enc_scalar!(__wasi_device_t, dev_device_byref, enc_device_byref);
|
||||
dec_enc_scalar!(__wasi_inode_t, dev_inode_byref, enc_inode_byref);
|
||||
dec_enc_scalar!(__wasi_linkcount_t, dev_linkcount_byref, enc_linkcount_byref);
|
||||
|
||||
pub(crate) fn dec_filestat_byref(
|
||||
memory: &mut [u8],
|
||||
filestat_ptr: wasi32::uintptr_t,
|
||||
) -> WasiResult<wasi::__wasi_filestat_t> {
|
||||
let raw = dec_raw_byref::<wasi::__wasi_filestat_t>(memory, filestat_ptr)?;
|
||||
|
||||
Ok(wasi::__wasi_filestat_t {
|
||||
dev: from_le(raw.dev),
|
||||
ino: from_le(raw.ino),
|
||||
filetype: from_le(raw.filetype),
|
||||
nlink: from_le(raw.nlink),
|
||||
size: from_le(raw.size),
|
||||
atim: from_le(raw.atim),
|
||||
mtim: from_le(raw.mtim),
|
||||
ctim: from_le(raw.ctim),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn enc_filestat_byref(
|
||||
memory: &mut [u8],
|
||||
filestat_ptr: wasi32::uintptr_t,
|
||||
filestat: wasi::__wasi_filestat_t,
|
||||
) -> WasiResult<()> {
|
||||
let raw = wasi::__wasi_filestat_t {
|
||||
dev: filestat.dev.to_le(),
|
||||
ino: filestat.ino.to_le(),
|
||||
filetype: filestat.filetype.to_le(),
|
||||
nlink: filestat.nlink.to_le(),
|
||||
size: filestat.size.to_le(),
|
||||
atim: filestat.atim.to_le(),
|
||||
mtim: filestat.mtim.to_le(),
|
||||
ctim: filestat.ctim.to_le(),
|
||||
};
|
||||
|
||||
enc_raw_byref::<wasi::__wasi_filestat_t>(memory, filestat_ptr, raw)
|
||||
}
|
||||
|
||||
pub(crate) fn dec_fdstat_byref(
|
||||
memory: &mut [u8],
|
||||
fdstat_ptr: wasi32::uintptr_t,
|
||||
) -> WasiResult<wasi::__wasi_fdstat_t> {
|
||||
let raw = dec_raw_byref::<wasi::__wasi_fdstat_t>(memory, fdstat_ptr)?;
|
||||
|
||||
Ok(wasi::__wasi_fdstat_t {
|
||||
fs_filetype: from_le(raw.fs_filetype),
|
||||
fs_flags: from_le(raw.fs_flags),
|
||||
fs_rights_base: from_le(raw.fs_rights_base),
|
||||
fs_rights_inheriting: from_le(raw.fs_rights_inheriting),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn enc_fdstat_byref(
|
||||
memory: &mut [u8],
|
||||
fdstat_ptr: wasi32::uintptr_t,
|
||||
fdstat: wasi::__wasi_fdstat_t,
|
||||
) -> WasiResult<()> {
|
||||
let raw = wasi::__wasi_fdstat_t {
|
||||
fs_filetype: fdstat.fs_filetype.to_le(),
|
||||
fs_flags: fdstat.fs_flags.to_le(),
|
||||
fs_rights_base: fdstat.fs_rights_base.to_le(),
|
||||
fs_rights_inheriting: fdstat.fs_rights_inheriting.to_le(),
|
||||
};
|
||||
|
||||
enc_raw_byref::<wasi::__wasi_fdstat_t>(memory, fdstat_ptr, raw)
|
||||
}
|
||||
|
||||
dec_enc_scalar!(__wasi_filedelta_t, dec_filedelta_byref, enc_filedelta_byref);
|
||||
dec_enc_scalar!(__wasi_filesize_t, dec_filesize_byref, enc_filesize_byref);
|
||||
dec_enc_scalar!(__wasi_filetype_t, dec_filetype_byref, enc_filetype_byref);
|
||||
|
||||
dec_enc_scalar!(
|
||||
__wasi_lookupflags_t,
|
||||
dec_lookupflags_byref,
|
||||
enc_lookupflags_byref
|
||||
);
|
||||
|
||||
dec_enc_scalar!(__wasi_oflags_t, dec_oflags_byref, enc_oflags_byref);
|
||||
|
||||
pub(crate) fn dec_prestat_byref(
|
||||
memory: &mut [u8],
|
||||
prestat_ptr: wasi32::uintptr_t,
|
||||
) -> WasiResult<host::__wasi_prestat_t> {
|
||||
let raw = dec_raw_byref::<wasi32::__wasi_prestat_t>(memory, prestat_ptr)?;
|
||||
|
||||
match from_le(raw.tag) {
|
||||
wasi::__WASI_PREOPENTYPE_DIR => Ok(host::__wasi_prestat_t {
|
||||
tag: wasi::__WASI_PREOPENTYPE_DIR,
|
||||
u: host::__wasi_prestat_u_t {
|
||||
dir: host::__wasi_prestat_dir_t {
|
||||
pr_name_len: dec_usize(from_le(unsafe { raw.u.dir.pr_name_len })),
|
||||
},
|
||||
},
|
||||
}),
|
||||
_ => Err(WasiError::EINVAL),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn enc_prestat_byref(
|
||||
memory: &mut [u8],
|
||||
prestat_ptr: wasi32::uintptr_t,
|
||||
prestat: host::__wasi_prestat_t,
|
||||
) -> WasiResult<()> {
|
||||
let raw = match prestat.tag {
|
||||
wasi::__WASI_PREOPENTYPE_DIR => Ok(wasi32::__wasi_prestat_t {
|
||||
tag: wasi::__WASI_PREOPENTYPE_DIR.to_le(),
|
||||
u: wasi32::__wasi_prestat_u_t {
|
||||
dir: wasi32::__wasi_prestat_dir_t {
|
||||
pr_name_len: enc_usize(unsafe { prestat.u.dir.pr_name_len }),
|
||||
},
|
||||
},
|
||||
}),
|
||||
_ => Err(WasiError::EINVAL),
|
||||
}?;
|
||||
|
||||
enc_raw_byref::<wasi32::__wasi_prestat_t>(memory, prestat_ptr, raw)
|
||||
}
|
||||
|
||||
dec_enc_scalar!(__wasi_rights_t, dec_rights_byref, enc_rights_byref);
|
||||
dec_enc_scalar!(__wasi_timestamp_t, dec_timestamp_byref, enc_timestamp_byref);
|
||||
|
||||
pub(crate) fn dec_usize(size: wasi32::size_t) -> usize {
|
||||
usize::try_from(size).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn enc_usize(size: usize) -> wasi32::size_t {
|
||||
wasi32::size_t::try_from(size).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn enc_usize_byref(
|
||||
memory: &mut [u8],
|
||||
usize_ptr: wasi32::uintptr_t,
|
||||
host_usize: usize,
|
||||
) -> WasiResult<()> {
|
||||
enc_int_byref::<wasi32::size_t>(memory, usize_ptr, enc_usize(host_usize).to_le())
|
||||
}
|
||||
|
||||
dec_enc_scalar!(__wasi_whence_t, dec_whence_byref, enc_whence_byref);
|
||||
|
||||
dec_enc_scalar!(
|
||||
__wasi_subclockflags_t,
|
||||
dec_subclockflags_byref,
|
||||
enc_subclockflags_byref
|
||||
);
|
||||
|
||||
dec_enc_scalar!(
|
||||
__wasi_eventrwflags_t,
|
||||
dec_eventrwflags_byref,
|
||||
enc_eventrwflags_byref
|
||||
);
|
||||
|
||||
dec_enc_scalar!(__wasi_eventtype_t, dec_eventtype_byref, enc_eventtype_byref);
|
||||
dec_enc_scalar!(__wasi_userdata_t, dec_userdata_byref, enc_userdata_byref);
|
||||
|
||||
pub(crate) fn dec_subscriptions(
|
||||
memory: &mut [u8],
|
||||
input: wasi32::uintptr_t,
|
||||
nsubscriptions: wasi32::size_t,
|
||||
) -> WasiResult<Vec<wasi::__wasi_subscription_t>> {
|
||||
let raw_input_slice =
|
||||
dec_raw_slice_of::<wasi::__wasi_subscription_t>(memory, input, nsubscriptions)?;
|
||||
|
||||
raw_input_slice
|
||||
.into_iter()
|
||||
.map(|raw_subscription| {
|
||||
let userdata = from_le(raw_subscription.userdata);
|
||||
let tag = from_le(raw_subscription.u.tag);
|
||||
let raw_u = raw_subscription.u.u;
|
||||
let u = match tag {
|
||||
wasi::__WASI_EVENTTYPE_CLOCK => wasi::__wasi_subscription_u_u_t {
|
||||
clock: unsafe {
|
||||
wasi::__wasi_subscription_clock_t {
|
||||
identifier: from_le(raw_u.clock.identifier),
|
||||
id: from_le(raw_u.clock.id),
|
||||
timeout: from_le(raw_u.clock.timeout),
|
||||
precision: from_le(raw_u.clock.precision),
|
||||
flags: from_le(raw_u.clock.flags),
|
||||
}
|
||||
},
|
||||
},
|
||||
wasi::__WASI_EVENTTYPE_FD_READ => wasi::__wasi_subscription_u_u_t {
|
||||
fd_read: wasi::__wasi_subscription_fd_readwrite_t {
|
||||
file_descriptor: from_le(unsafe { raw_u.fd_read.file_descriptor }),
|
||||
},
|
||||
},
|
||||
wasi::__WASI_EVENTTYPE_FD_WRITE => wasi::__wasi_subscription_u_u_t {
|
||||
fd_write: wasi::__wasi_subscription_fd_readwrite_t {
|
||||
file_descriptor: from_le(unsafe { raw_u.fd_write.file_descriptor }),
|
||||
},
|
||||
},
|
||||
_ => return Err(WasiError::EINVAL),
|
||||
};
|
||||
Ok(wasi::__wasi_subscription_t {
|
||||
userdata,
|
||||
u: wasi::__wasi_subscription_u_t { tag, u },
|
||||
})
|
||||
})
|
||||
.collect::<WasiResult<Vec<_>>>()
|
||||
}
|
||||
|
||||
pub(crate) fn enc_events(
|
||||
memory: &mut [u8],
|
||||
output: wasi32::uintptr_t,
|
||||
nsubscriptions: wasi32::size_t,
|
||||
events: Vec<wasi::__wasi_event_t>,
|
||||
) -> WasiResult<()> {
|
||||
let mut raw_output_iter =
|
||||
dec_raw_slice_of_mut::<wasi::__wasi_event_t>(memory, output, nsubscriptions)?.into_iter();
|
||||
|
||||
for event in events.iter() {
|
||||
*raw_output_iter
|
||||
.next()
|
||||
.expect("the number of events cannot exceed the number of subscriptions") = {
|
||||
let userdata = event.userdata.to_le();
|
||||
let error = event.error.to_le();
|
||||
let r#type = event.r#type.to_le();
|
||||
let flags = event.fd_readwrite.flags.to_le();
|
||||
let nbytes = event.fd_readwrite.nbytes.to_le();
|
||||
wasi::__wasi_event_t {
|
||||
userdata,
|
||||
error,
|
||||
r#type,
|
||||
fd_readwrite: wasi::__wasi_event_fd_readwrite_t { flags, nbytes },
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
dec_enc_scalar!(__wasi_advice_t, dec_advice_byref, enc_advice_byref);
|
||||
dec_enc_scalar!(__wasi_fstflags_t, dec_fstflags_byref, enc_fstflags_byref);
|
||||
dec_enc_scalar!(__wasi_dircookie_t, dec_dircookie_byref, enc_dircookie_byref);
|
||||
@@ -1,15 +0,0 @@
|
||||
mod ctx;
|
||||
mod entry;
|
||||
mod helpers;
|
||||
mod host;
|
||||
mod hostcalls_impl;
|
||||
mod memory;
|
||||
mod sys;
|
||||
pub mod wasi;
|
||||
pub mod wasi32;
|
||||
|
||||
pub mod hostcalls {
|
||||
wig::define_hostcalls!("phases/old/snapshot_0/witx/wasi_unstable.witx");
|
||||
}
|
||||
|
||||
pub use ctx::{WasiCtx, WasiCtxBuilder};
|
||||
@@ -1,13 +0,0 @@
|
||||
use cfg_if::cfg_if;
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(unix)] {
|
||||
mod unix;
|
||||
pub(crate) use self::unix::*;
|
||||
} else if #[cfg(windows)] {
|
||||
mod windows;
|
||||
pub(crate) use self::windows::*;
|
||||
} else {
|
||||
compile_error!("wasi-common doesn't compile for this platform yet");
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
use crate::old::snapshot_0::wasi::{self, WasiResult};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
pub(crate) const O_RSYNC: yanix::file::OFlags = yanix::file::OFlags::SYNC;
|
||||
|
||||
pub(crate) fn stdev_from_nix(dev: libc::dev_t) -> WasiResult<wasi::__wasi_device_t> {
|
||||
wasi::__wasi_device_t::try_from(dev).map_err(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) fn stino_from_nix(ino: libc::ino_t) -> WasiResult<wasi::__wasi_inode_t> {
|
||||
wasi::__wasi_device_t::try_from(ino).map_err(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) fn stnlink_from_nix(nlink: libc::nlink_t) -> WasiResult<wasi::__wasi_linkcount_t> {
|
||||
Ok(nlink.into())
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
use crate::old::snapshot_0::hostcalls_impl::PathGet;
|
||||
use crate::old::snapshot_0::wasi::{WasiError, WasiResult};
|
||||
use std::os::unix::prelude::AsRawFd;
|
||||
|
||||
pub(crate) fn path_unlink_file(resolved: PathGet) -> WasiResult<()> {
|
||||
use yanix::file::{unlinkat, AtFlags};
|
||||
match unsafe {
|
||||
unlinkat(
|
||||
resolved.dirfd().as_raw_fd(),
|
||||
resolved.path(),
|
||||
AtFlags::empty(),
|
||||
)
|
||||
} {
|
||||
Err(err) => {
|
||||
let raw_errno = err.raw_os_error().unwrap();
|
||||
// Non-Linux implementations may return EPERM when attempting to remove a
|
||||
// directory without REMOVEDIR. While that's what POSIX specifies, it's
|
||||
// less useful. Adjust this to EISDIR. It doesn't matter that this is not
|
||||
// atomic with the unlinkat, because if the file is removed and a directory
|
||||
// is created before fstatat sees it, we're racing with that change anyway
|
||||
// and unlinkat could have legitimately seen the directory if the race had
|
||||
// turned out differently.
|
||||
use yanix::file::{fstatat, FileType};
|
||||
|
||||
if raw_errno == libc::EPERM {
|
||||
match unsafe {
|
||||
fstatat(
|
||||
resolved.dirfd().as_raw_fd(),
|
||||
resolved.path(),
|
||||
AtFlags::SYMLINK_NOFOLLOW,
|
||||
)
|
||||
} {
|
||||
Ok(stat) => {
|
||||
if FileType::from_stat_st_mode(stat.st_mode) == FileType::Directory {
|
||||
return Err(WasiError::EISDIR);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::debug!("path_unlink_file fstatat error: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(err.into())
|
||||
}
|
||||
Ok(()) => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> WasiResult<()> {
|
||||
use yanix::file::{fstatat, symlinkat, AtFlags};
|
||||
|
||||
tracing::debug!("path_symlink old_path = {:?}", old_path);
|
||||
tracing::debug!("path_symlink resolved = {:?}", resolved);
|
||||
|
||||
match unsafe { symlinkat(old_path, resolved.dirfd().as_raw_fd(), resolved.path()) } {
|
||||
Err(err) => {
|
||||
if err.raw_os_error().unwrap() == libc::ENOTDIR {
|
||||
// On BSD, symlinkat returns ENOTDIR when it should in fact
|
||||
// return a EEXIST. It seems that it gets confused with by
|
||||
// the trailing slash in the target path. Thus, we strip
|
||||
// the trailing slash and check if the path exists, and
|
||||
// adjust the error code appropriately.
|
||||
let new_path = resolved.path().trim_end_matches('/');
|
||||
match unsafe {
|
||||
fstatat(
|
||||
resolved.dirfd().as_raw_fd(),
|
||||
new_path,
|
||||
AtFlags::SYMLINK_NOFOLLOW,
|
||||
)
|
||||
} {
|
||||
Ok(_) => return Err(WasiError::EEXIST),
|
||||
Err(err) => {
|
||||
tracing::debug!("path_symlink fstatat error: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err.into())
|
||||
}
|
||||
Ok(()) => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> WasiResult<()> {
|
||||
use yanix::file::{fstatat, renameat, AtFlags};
|
||||
match unsafe {
|
||||
renameat(
|
||||
resolved_old.dirfd().as_raw_fd(),
|
||||
resolved_old.path(),
|
||||
resolved_new.dirfd().as_raw_fd(),
|
||||
resolved_new.path(),
|
||||
)
|
||||
} {
|
||||
Err(err) => {
|
||||
// Currently, this is verified to be correct on macOS, where
|
||||
// ENOENT can be returned in case when we try to rename a file
|
||||
// into a name with a trailing slash. On macOS, if the latter does
|
||||
// not exist, an ENOENT is thrown, whereas on Linux we observe the
|
||||
// correct behaviour of throwing an ENOTDIR since the destination is
|
||||
// indeed not a directory.
|
||||
//
|
||||
// TODO
|
||||
// Verify on other BSD-based OSes.
|
||||
if err.raw_os_error().unwrap() == libc::ENOENT {
|
||||
// check if the source path exists
|
||||
match unsafe {
|
||||
fstatat(
|
||||
resolved_old.dirfd().as_raw_fd(),
|
||||
resolved_old.path(),
|
||||
AtFlags::SYMLINK_NOFOLLOW,
|
||||
)
|
||||
} {
|
||||
Ok(_) => {
|
||||
// check if destination contains a trailing slash
|
||||
if resolved_new.path().contains('/') {
|
||||
return Err(WasiError::ENOTDIR);
|
||||
} else {
|
||||
return Err(WasiError::ENOENT);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::debug!("path_rename fstatat error: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(err.into())
|
||||
}
|
||||
Ok(()) => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod fd_readdir_impl {
|
||||
use crate::old::snapshot_0::sys::entry_impl::OsHandle;
|
||||
use crate::old::snapshot_0::wasi::WasiResult;
|
||||
use std::sync::{Mutex, MutexGuard};
|
||||
use yanix::dir::Dir;
|
||||
|
||||
pub(crate) fn get_dir_from_os_handle<'a>(
|
||||
os_handle: &'a mut OsHandle,
|
||||
) -> WasiResult<MutexGuard<'a, Dir>> {
|
||||
let dir = match os_handle.dir {
|
||||
Some(ref mut dir) => dir,
|
||||
None => {
|
||||
// We need to duplicate the fd, because `opendir(3)`:
|
||||
// Upon successful return from fdopendir(), the file descriptor is under
|
||||
// control of the system, and if any attempt is made to close the file
|
||||
// descriptor, or to modify the state of the associated description other
|
||||
// than by means of closedir(), readdir(), readdir_r(), or rewinddir(),
|
||||
// the behaviour is undefined.
|
||||
let fd = (*os_handle).try_clone()?;
|
||||
let dir = Dir::from(fd)?;
|
||||
os_handle.dir.get_or_insert(Mutex::new(dir))
|
||||
}
|
||||
};
|
||||
// Note that from this point on, until the end of the parent scope (i.e., enclosing this
|
||||
// function), we're locking the `Dir` member of this `OsHandle`.
|
||||
Ok(dir.lock().unwrap())
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
pub(crate) mod host_impl;
|
||||
pub(crate) mod hostcalls_impl;
|
||||
pub(crate) mod oshandle;
|
||||
@@ -1,48 +0,0 @@
|
||||
use std::fs;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::unix::prelude::{AsRawFd, RawFd};
|
||||
use std::sync::Mutex;
|
||||
use yanix::dir::Dir;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct OsHandle {
|
||||
pub(crate) file: fs::File,
|
||||
// In case that this `OsHandle` actually refers to a directory,
|
||||
// when the client makes a `fd_readdir` syscall on this descriptor,
|
||||
// we will need to cache the `libc::DIR` pointer manually in order
|
||||
// to be able to seek on it later. While on Linux, this is handled
|
||||
// by the OS, BSD Unixes require the client to do this caching.
|
||||
//
|
||||
// This comes directly from the BSD man pages on `readdir`:
|
||||
// > Values returned by telldir() are good only for the lifetime
|
||||
// > of the DIR pointer, dirp, from which they are derived.
|
||||
// > If the directory is closed and then reopened, prior values
|
||||
// > returned by telldir() will no longer be valid.
|
||||
pub(crate) dir: Option<Mutex<Dir>>,
|
||||
}
|
||||
|
||||
impl From<fs::File> for OsHandle {
|
||||
fn from(file: fs::File) -> Self {
|
||||
Self { file, dir: None }
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for OsHandle {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.file.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for OsHandle {
|
||||
type Target = fs::File;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.file
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for OsHandle {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.file
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
use crate::old::snapshot_0::wasi::{self, WasiResult};
|
||||
|
||||
pub(crate) const O_RSYNC: yanix::file::OFlags = yanix::file::OFlags::RSYNC;
|
||||
|
||||
pub(crate) fn stdev_from_nix(dev: libc::dev_t) -> WasiResult<wasi::__wasi_device_t> {
|
||||
Ok(wasi::__wasi_device_t::from(dev))
|
||||
}
|
||||
|
||||
pub(crate) fn stino_from_nix(ino: libc::ino_t) -> WasiResult<wasi::__wasi_inode_t> {
|
||||
Ok(wasi::__wasi_device_t::from(ino))
|
||||
}
|
||||
|
||||
pub(crate) fn stnlink_from_nix(nlink: libc::nlink_t) -> WasiResult<wasi::__wasi_linkcount_t> {
|
||||
Ok(nlink)
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
pub(crate) mod host_impl;
|
||||
#[path = "../linux/hostcalls_impl.rs"]
|
||||
pub(crate) mod hostcalls_impl;
|
||||
#[path = "../linux/oshandle.rs"]
|
||||
pub(crate) mod oshandle;
|
||||
@@ -1,130 +0,0 @@
|
||||
use crate::old::snapshot_0::entry::{Descriptor, OsHandleRef};
|
||||
use crate::old::snapshot_0::{sys::unix::sys_impl, wasi};
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::os::unix::prelude::{AsRawFd, FileTypeExt, FromRawFd, RawFd};
|
||||
|
||||
pub(crate) use sys_impl::oshandle::*;
|
||||
|
||||
impl AsRawFd for Descriptor {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
match self {
|
||||
Self::OsHandle(file) => file.as_raw_fd(),
|
||||
Self::Stdin => io::stdin().as_raw_fd(),
|
||||
Self::Stdout => io::stdout().as_raw_fd(),
|
||||
Self::Stderr => io::stderr().as_raw_fd(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn descriptor_as_oshandle<'lifetime>(
|
||||
desc: &'lifetime Descriptor,
|
||||
) -> OsHandleRef<'lifetime> {
|
||||
OsHandleRef::new(ManuallyDrop::new(OsHandle::from(unsafe {
|
||||
File::from_raw_fd(desc.as_raw_fd())
|
||||
})))
|
||||
}
|
||||
|
||||
/// This function is unsafe because it operates on a raw file descriptor.
|
||||
pub(crate) unsafe fn determine_type_and_access_rights<Fd: AsRawFd>(
|
||||
fd: &Fd,
|
||||
) -> io::Result<(
|
||||
wasi::__wasi_filetype_t,
|
||||
wasi::__wasi_rights_t,
|
||||
wasi::__wasi_rights_t,
|
||||
)> {
|
||||
let (file_type, mut rights_base, rights_inheriting) = determine_type_rights(fd)?;
|
||||
|
||||
use yanix::{fcntl, file::OFlags};
|
||||
let flags = fcntl::get_status_flags(fd.as_raw_fd())?;
|
||||
let accmode = flags & OFlags::ACCMODE;
|
||||
if accmode == OFlags::RDONLY {
|
||||
rights_base &= !wasi::__WASI_RIGHTS_FD_WRITE;
|
||||
} else if accmode == OFlags::WRONLY {
|
||||
rights_base &= !wasi::__WASI_RIGHTS_FD_READ;
|
||||
}
|
||||
|
||||
Ok((file_type, rights_base, rights_inheriting))
|
||||
}
|
||||
|
||||
/// This function is unsafe because it operates on a raw file descriptor.
|
||||
pub(crate) unsafe fn determine_type_rights<Fd: AsRawFd>(
|
||||
fd: &Fd,
|
||||
) -> io::Result<(
|
||||
wasi::__wasi_filetype_t,
|
||||
wasi::__wasi_rights_t,
|
||||
wasi::__wasi_rights_t,
|
||||
)> {
|
||||
let (file_type, rights_base, rights_inheriting) = {
|
||||
// we just make a `File` here for convenience; we don't want it to close when it drops
|
||||
let file = std::mem::ManuallyDrop::new(std::fs::File::from_raw_fd(fd.as_raw_fd()));
|
||||
let ft = file.metadata()?.file_type();
|
||||
if ft.is_block_device() {
|
||||
tracing::debug!("Host fd {:?} is a block device", fd.as_raw_fd());
|
||||
(
|
||||
wasi::__WASI_FILETYPE_BLOCK_DEVICE,
|
||||
wasi::RIGHTS_BLOCK_DEVICE_BASE,
|
||||
wasi::RIGHTS_BLOCK_DEVICE_INHERITING,
|
||||
)
|
||||
} else if ft.is_char_device() {
|
||||
tracing::debug!("Host fd {:?} is a char device", fd.as_raw_fd());
|
||||
use yanix::file::isatty;
|
||||
if isatty(fd.as_raw_fd())? {
|
||||
(
|
||||
wasi::__WASI_FILETYPE_CHARACTER_DEVICE,
|
||||
wasi::RIGHTS_TTY_BASE,
|
||||
wasi::RIGHTS_TTY_BASE,
|
||||
)
|
||||
} else {
|
||||
(
|
||||
wasi::__WASI_FILETYPE_CHARACTER_DEVICE,
|
||||
wasi::RIGHTS_CHARACTER_DEVICE_BASE,
|
||||
wasi::RIGHTS_CHARACTER_DEVICE_INHERITING,
|
||||
)
|
||||
}
|
||||
} else if ft.is_dir() {
|
||||
tracing::debug!("Host fd {:?} is a directory", fd.as_raw_fd());
|
||||
(
|
||||
wasi::__WASI_FILETYPE_DIRECTORY,
|
||||
wasi::RIGHTS_DIRECTORY_BASE,
|
||||
wasi::RIGHTS_DIRECTORY_INHERITING,
|
||||
)
|
||||
} else if ft.is_file() {
|
||||
tracing::debug!("Host fd {:?} is a file", fd.as_raw_fd());
|
||||
(
|
||||
wasi::__WASI_FILETYPE_REGULAR_FILE,
|
||||
wasi::RIGHTS_REGULAR_FILE_BASE,
|
||||
wasi::RIGHTS_REGULAR_FILE_INHERITING,
|
||||
)
|
||||
} else if ft.is_socket() {
|
||||
tracing::debug!("Host fd {:?} is a socket", fd.as_raw_fd());
|
||||
use yanix::socket::{get_socket_type, SockType};
|
||||
match get_socket_type(fd.as_raw_fd())? {
|
||||
SockType::Datagram => (
|
||||
wasi::__WASI_FILETYPE_SOCKET_DGRAM,
|
||||
wasi::RIGHTS_SOCKET_BASE,
|
||||
wasi::RIGHTS_SOCKET_INHERITING,
|
||||
),
|
||||
SockType::Stream => (
|
||||
wasi::__WASI_FILETYPE_SOCKET_STREAM,
|
||||
wasi::RIGHTS_SOCKET_BASE,
|
||||
wasi::RIGHTS_SOCKET_INHERITING,
|
||||
),
|
||||
_ => return Err(io::Error::from_raw_os_error(libc::EINVAL)),
|
||||
}
|
||||
} else if ft.is_fifo() {
|
||||
tracing::debug!("Host fd {:?} is a fifo", fd.as_raw_fd());
|
||||
(
|
||||
wasi::__WASI_FILETYPE_UNKNOWN,
|
||||
wasi::RIGHTS_REGULAR_FILE_BASE,
|
||||
wasi::RIGHTS_REGULAR_FILE_INHERITING,
|
||||
)
|
||||
} else {
|
||||
tracing::debug!("Host fd {:?} is unknown", fd.as_raw_fd());
|
||||
return Err(io::Error::from_raw_os_error(libc::EINVAL));
|
||||
}
|
||||
};
|
||||
|
||||
Ok((file_type, rights_base, rights_inheriting))
|
||||
}
|
||||
@@ -1,227 +0,0 @@
|
||||
//! WASI host types specific to *nix host.
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(dead_code)]
|
||||
use crate::old::snapshot_0::host::FileType;
|
||||
use crate::old::snapshot_0::wasi::{self, WasiError, WasiResult};
|
||||
use crate::old::snapshot_0::{helpers, sys::unix::sys_impl};
|
||||
use std::ffi::OsStr;
|
||||
use std::io;
|
||||
use std::os::unix::prelude::OsStrExt;
|
||||
use yanix::file::OFlags;
|
||||
|
||||
pub(crate) use sys_impl::host_impl::*;
|
||||
|
||||
impl From<io::Error> for WasiError {
|
||||
fn from(err: io::Error) -> Self {
|
||||
match err.raw_os_error() {
|
||||
Some(code) => match code {
|
||||
libc::EPERM => Self::EPERM,
|
||||
libc::ENOENT => Self::ENOENT,
|
||||
libc::ESRCH => Self::ESRCH,
|
||||
libc::EINTR => Self::EINTR,
|
||||
libc::EIO => Self::EIO,
|
||||
libc::ENXIO => Self::ENXIO,
|
||||
libc::E2BIG => Self::E2BIG,
|
||||
libc::ENOEXEC => Self::ENOEXEC,
|
||||
libc::EBADF => Self::EBADF,
|
||||
libc::ECHILD => Self::ECHILD,
|
||||
libc::EAGAIN => Self::EAGAIN,
|
||||
libc::ENOMEM => Self::ENOMEM,
|
||||
libc::EACCES => Self::EACCES,
|
||||
libc::EFAULT => Self::EFAULT,
|
||||
libc::EBUSY => Self::EBUSY,
|
||||
libc::EEXIST => Self::EEXIST,
|
||||
libc::EXDEV => Self::EXDEV,
|
||||
libc::ENODEV => Self::ENODEV,
|
||||
libc::ENOTDIR => Self::ENOTDIR,
|
||||
libc::EISDIR => Self::EISDIR,
|
||||
libc::EINVAL => Self::EINVAL,
|
||||
libc::ENFILE => Self::ENFILE,
|
||||
libc::EMFILE => Self::EMFILE,
|
||||
libc::ENOTTY => Self::ENOTTY,
|
||||
libc::ETXTBSY => Self::ETXTBSY,
|
||||
libc::EFBIG => Self::EFBIG,
|
||||
libc::ENOSPC => Self::ENOSPC,
|
||||
libc::ESPIPE => Self::ESPIPE,
|
||||
libc::EROFS => Self::EROFS,
|
||||
libc::EMLINK => Self::EMLINK,
|
||||
libc::EPIPE => Self::EPIPE,
|
||||
libc::EDOM => Self::EDOM,
|
||||
libc::ERANGE => Self::ERANGE,
|
||||
libc::EDEADLK => Self::EDEADLK,
|
||||
libc::ENAMETOOLONG => Self::ENAMETOOLONG,
|
||||
libc::ENOLCK => Self::ENOLCK,
|
||||
libc::ENOSYS => Self::ENOSYS,
|
||||
libc::ENOTEMPTY => Self::ENOTEMPTY,
|
||||
libc::ELOOP => Self::ELOOP,
|
||||
libc::ENOMSG => Self::ENOMSG,
|
||||
libc::EIDRM => Self::EIDRM,
|
||||
libc::ENOLINK => Self::ENOLINK,
|
||||
libc::EPROTO => Self::EPROTO,
|
||||
libc::EMULTIHOP => Self::EMULTIHOP,
|
||||
libc::EBADMSG => Self::EBADMSG,
|
||||
libc::EOVERFLOW => Self::EOVERFLOW,
|
||||
libc::EILSEQ => Self::EILSEQ,
|
||||
libc::ENOTSOCK => Self::ENOTSOCK,
|
||||
libc::EDESTADDRREQ => Self::EDESTADDRREQ,
|
||||
libc::EMSGSIZE => Self::EMSGSIZE,
|
||||
libc::EPROTOTYPE => Self::EPROTOTYPE,
|
||||
libc::ENOPROTOOPT => Self::ENOPROTOOPT,
|
||||
libc::EPROTONOSUPPORT => Self::EPROTONOSUPPORT,
|
||||
libc::EAFNOSUPPORT => Self::EAFNOSUPPORT,
|
||||
libc::EADDRINUSE => Self::EADDRINUSE,
|
||||
libc::EADDRNOTAVAIL => Self::EADDRNOTAVAIL,
|
||||
libc::ENETDOWN => Self::ENETDOWN,
|
||||
libc::ENETUNREACH => Self::ENETUNREACH,
|
||||
libc::ENETRESET => Self::ENETRESET,
|
||||
libc::ECONNABORTED => Self::ECONNABORTED,
|
||||
libc::ECONNRESET => Self::ECONNRESET,
|
||||
libc::ENOBUFS => Self::ENOBUFS,
|
||||
libc::EISCONN => Self::EISCONN,
|
||||
libc::ENOTCONN => Self::ENOTCONN,
|
||||
libc::ETIMEDOUT => Self::ETIMEDOUT,
|
||||
libc::ECONNREFUSED => Self::ECONNREFUSED,
|
||||
libc::EHOSTUNREACH => Self::EHOSTUNREACH,
|
||||
libc::EALREADY => Self::EALREADY,
|
||||
libc::EINPROGRESS => Self::EINPROGRESS,
|
||||
libc::ESTALE => Self::ESTALE,
|
||||
libc::EDQUOT => Self::EDQUOT,
|
||||
libc::ECANCELED => Self::ECANCELED,
|
||||
libc::EOWNERDEAD => Self::EOWNERDEAD,
|
||||
libc::ENOTRECOVERABLE => Self::ENOTRECOVERABLE,
|
||||
libc::ENOTSUP => Self::ENOTSUP,
|
||||
x => {
|
||||
tracing::debug!("Unknown errno value: {}", x);
|
||||
Self::EIO
|
||||
}
|
||||
},
|
||||
None => {
|
||||
tracing::debug!("Other I/O error: {}", err);
|
||||
Self::EIO
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn nix_from_fdflags(fdflags: wasi::__wasi_fdflags_t) -> OFlags {
|
||||
let mut nix_flags = OFlags::empty();
|
||||
if fdflags & wasi::__WASI_FDFLAGS_APPEND != 0 {
|
||||
nix_flags.insert(OFlags::APPEND);
|
||||
}
|
||||
if fdflags & wasi::__WASI_FDFLAGS_DSYNC != 0 {
|
||||
nix_flags.insert(OFlags::DSYNC);
|
||||
}
|
||||
if fdflags & wasi::__WASI_FDFLAGS_NONBLOCK != 0 {
|
||||
nix_flags.insert(OFlags::NONBLOCK);
|
||||
}
|
||||
if fdflags & wasi::__WASI_FDFLAGS_RSYNC != 0 {
|
||||
nix_flags.insert(O_RSYNC);
|
||||
}
|
||||
if fdflags & wasi::__WASI_FDFLAGS_SYNC != 0 {
|
||||
nix_flags.insert(OFlags::SYNC);
|
||||
}
|
||||
nix_flags
|
||||
}
|
||||
|
||||
pub(crate) fn fdflags_from_nix(oflags: OFlags) -> wasi::__wasi_fdflags_t {
|
||||
let mut fdflags = 0;
|
||||
if oflags.contains(OFlags::APPEND) {
|
||||
fdflags |= wasi::__WASI_FDFLAGS_APPEND;
|
||||
}
|
||||
if oflags.contains(OFlags::DSYNC) {
|
||||
fdflags |= wasi::__WASI_FDFLAGS_DSYNC;
|
||||
}
|
||||
if oflags.contains(OFlags::NONBLOCK) {
|
||||
fdflags |= wasi::__WASI_FDFLAGS_NONBLOCK;
|
||||
}
|
||||
if oflags.contains(O_RSYNC) {
|
||||
fdflags |= wasi::__WASI_FDFLAGS_RSYNC;
|
||||
}
|
||||
if oflags.contains(OFlags::SYNC) {
|
||||
fdflags |= wasi::__WASI_FDFLAGS_SYNC;
|
||||
}
|
||||
fdflags
|
||||
}
|
||||
|
||||
pub(crate) fn nix_from_oflags(oflags: wasi::__wasi_oflags_t) -> OFlags {
|
||||
let mut nix_flags = OFlags::empty();
|
||||
if oflags & wasi::__WASI_OFLAGS_CREAT != 0 {
|
||||
nix_flags.insert(OFlags::CREAT);
|
||||
}
|
||||
if oflags & wasi::__WASI_OFLAGS_DIRECTORY != 0 {
|
||||
nix_flags.insert(OFlags::DIRECTORY);
|
||||
}
|
||||
if oflags & wasi::__WASI_OFLAGS_EXCL != 0 {
|
||||
nix_flags.insert(OFlags::EXCL);
|
||||
}
|
||||
if oflags & wasi::__WASI_OFLAGS_TRUNC != 0 {
|
||||
nix_flags.insert(OFlags::TRUNC);
|
||||
}
|
||||
nix_flags
|
||||
}
|
||||
|
||||
pub(crate) fn filestat_from_nix(filestat: libc::stat) -> WasiResult<wasi::__wasi_filestat_t> {
|
||||
use std::convert::TryInto;
|
||||
|
||||
fn filestat_to_timestamp(secs: u64, nsecs: u64) -> WasiResult<wasi::__wasi_timestamp_t> {
|
||||
secs.checked_mul(1_000_000_000)
|
||||
.and_then(|sec_nsec| sec_nsec.checked_add(nsecs))
|
||||
.ok_or(WasiError::EOVERFLOW)
|
||||
}
|
||||
|
||||
let filetype = yanix::file::FileType::from_stat_st_mode(filestat.st_mode);
|
||||
let dev = stdev_from_nix(filestat.st_dev)?;
|
||||
let ino = stino_from_nix(filestat.st_ino)?;
|
||||
let atim = filestat_to_timestamp(
|
||||
filestat.st_atime.try_into()?,
|
||||
filestat.st_atime_nsec.try_into()?,
|
||||
)?;
|
||||
let ctim = filestat_to_timestamp(
|
||||
filestat.st_ctime.try_into()?,
|
||||
filestat.st_ctime_nsec.try_into()?,
|
||||
)?;
|
||||
let mtim = filestat_to_timestamp(
|
||||
filestat.st_mtime.try_into()?,
|
||||
filestat.st_mtime_nsec.try_into()?,
|
||||
)?;
|
||||
|
||||
Ok(wasi::__wasi_filestat_t {
|
||||
dev,
|
||||
ino,
|
||||
nlink: stnlink_from_nix(filestat.st_nlink)?,
|
||||
size: filestat.st_size as wasi::__wasi_filesize_t,
|
||||
atim,
|
||||
ctim,
|
||||
mtim,
|
||||
filetype: FileType::from(filetype).to_wasi(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates owned WASI path from OS string.
|
||||
///
|
||||
/// NB WASI spec requires OS string to be valid UTF-8. Otherwise,
|
||||
/// `__WASI_ERRNO_ILSEQ` error is returned.
|
||||
pub(crate) fn path_from_host<S: AsRef<OsStr>>(s: S) -> WasiResult<String> {
|
||||
helpers::path_from_slice(s.as_ref().as_bytes()).map(String::from)
|
||||
}
|
||||
|
||||
impl From<yanix::file::FileType> for FileType {
|
||||
fn from(ft: yanix::file::FileType) -> Self {
|
||||
use yanix::file::FileType::*;
|
||||
match ft {
|
||||
RegularFile => Self::RegularFile,
|
||||
Symlink => Self::Symlink,
|
||||
Directory => Self::Directory,
|
||||
BlockDevice => Self::BlockDevice,
|
||||
CharacterDevice => Self::CharacterDevice,
|
||||
/* Unknown | Socket | Fifo */
|
||||
_ => Self::Unknown,
|
||||
// TODO how to discriminate between STREAM and DGRAM?
|
||||
// Perhaps, we should create a more general WASI filetype
|
||||
// such as __WASI_FILETYPE_SOCKET, and then it would be
|
||||
// up to the client to check whether it's actually
|
||||
// STREAM or DGRAM?
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,318 +0,0 @@
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(unused_unsafe)]
|
||||
use crate::old::snapshot_0::host::Dirent;
|
||||
use crate::old::snapshot_0::hostcalls_impl::PathGet;
|
||||
use crate::old::snapshot_0::sys::{entry_impl::OsHandle, host_impl, unix::sys_impl};
|
||||
use crate::old::snapshot_0::wasi::{self, WasiError, WasiResult};
|
||||
use std::convert::TryInto;
|
||||
use std::fs::File;
|
||||
use std::os::unix::fs::FileExt;
|
||||
use std::os::unix::prelude::{AsRawFd, FromRawFd};
|
||||
|
||||
pub(crate) use sys_impl::hostcalls_impl::*;
|
||||
|
||||
pub(crate) fn fd_pread(
|
||||
file: &File,
|
||||
buf: &mut [u8],
|
||||
offset: wasi::__wasi_filesize_t,
|
||||
) -> WasiResult<usize> {
|
||||
file.read_at(buf, offset).map_err(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) fn fd_pwrite(
|
||||
file: &File,
|
||||
buf: &[u8],
|
||||
offset: wasi::__wasi_filesize_t,
|
||||
) -> WasiResult<usize> {
|
||||
file.write_at(buf, offset).map_err(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) fn fd_fdstat_get(fd: &File) -> WasiResult<wasi::__wasi_fdflags_t> {
|
||||
unsafe { yanix::fcntl::get_status_flags(fd.as_raw_fd()) }
|
||||
.map(host_impl::fdflags_from_nix)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) fn fd_fdstat_set_flags(fd: &File, fdflags: wasi::__wasi_fdflags_t) -> WasiResult<()> {
|
||||
let nix_flags = host_impl::nix_from_fdflags(fdflags);
|
||||
unsafe { yanix::fcntl::set_status_flags(fd.as_raw_fd(), nix_flags) }.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) fn fd_advise(
|
||||
file: &File,
|
||||
advice: wasi::__wasi_advice_t,
|
||||
offset: wasi::__wasi_filesize_t,
|
||||
len: wasi::__wasi_filesize_t,
|
||||
) -> WasiResult<()> {
|
||||
use yanix::fadvise::{posix_fadvise, PosixFadviseAdvice};
|
||||
let offset = offset.try_into()?;
|
||||
let len = len.try_into()?;
|
||||
let host_advice = match advice {
|
||||
wasi::__WASI_ADVICE_DONTNEED => PosixFadviseAdvice::DontNeed,
|
||||
wasi::__WASI_ADVICE_SEQUENTIAL => PosixFadviseAdvice::Sequential,
|
||||
wasi::__WASI_ADVICE_WILLNEED => PosixFadviseAdvice::WillNeed,
|
||||
wasi::__WASI_ADVICE_NOREUSE => PosixFadviseAdvice::NoReuse,
|
||||
wasi::__WASI_ADVICE_RANDOM => PosixFadviseAdvice::Random,
|
||||
wasi::__WASI_ADVICE_NORMAL => PosixFadviseAdvice::Normal,
|
||||
_ => return Err(WasiError::EINVAL),
|
||||
};
|
||||
unsafe { posix_fadvise(file.as_raw_fd(), offset, len, host_advice) }.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) fn path_create_directory(resolved: PathGet) -> WasiResult<()> {
|
||||
use yanix::file::{mkdirat, Mode};
|
||||
unsafe {
|
||||
mkdirat(
|
||||
resolved.dirfd().as_raw_fd(),
|
||||
resolved.path(),
|
||||
Mode::from_bits_truncate(0o777),
|
||||
)
|
||||
}
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) fn path_link(resolved_old: PathGet, resolved_new: PathGet) -> WasiResult<()> {
|
||||
use yanix::file::{linkat, AtFlags};
|
||||
unsafe {
|
||||
linkat(
|
||||
resolved_old.dirfd().as_raw_fd(),
|
||||
resolved_old.path(),
|
||||
resolved_new.dirfd().as_raw_fd(),
|
||||
resolved_new.path(),
|
||||
AtFlags::SYMLINK_FOLLOW,
|
||||
)
|
||||
}
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) fn path_open(
|
||||
resolved: PathGet,
|
||||
read: bool,
|
||||
write: bool,
|
||||
oflags: wasi::__wasi_oflags_t,
|
||||
fs_flags: wasi::__wasi_fdflags_t,
|
||||
) -> WasiResult<File> {
|
||||
use yanix::file::{fstatat, openat, AtFlags, FileType, Mode, OFlags};
|
||||
|
||||
let mut nix_all_oflags = if read && write {
|
||||
OFlags::RDWR
|
||||
} else if write {
|
||||
OFlags::WRONLY
|
||||
} else {
|
||||
OFlags::RDONLY
|
||||
};
|
||||
|
||||
// on non-Capsicum systems, we always want nofollow
|
||||
nix_all_oflags.insert(OFlags::NOFOLLOW);
|
||||
|
||||
// convert open flags
|
||||
nix_all_oflags.insert(host_impl::nix_from_oflags(oflags));
|
||||
|
||||
// convert file descriptor flags
|
||||
nix_all_oflags.insert(host_impl::nix_from_fdflags(fs_flags));
|
||||
|
||||
// Call openat. Use mode 0o666 so that we follow whatever the user's
|
||||
// umask is, but don't set the executable flag, because it isn't yet
|
||||
// meaningful for WASI programs to create executable files.
|
||||
|
||||
tracing::debug!("path_open resolved = {:?}", resolved);
|
||||
tracing::debug!("path_open oflags = {:?}", nix_all_oflags);
|
||||
|
||||
let new_fd = match unsafe {
|
||||
openat(
|
||||
resolved.dirfd().as_raw_fd(),
|
||||
resolved.path(),
|
||||
nix_all_oflags,
|
||||
Mode::from_bits_truncate(0o666),
|
||||
)
|
||||
} {
|
||||
Ok(fd) => fd,
|
||||
Err(e) => {
|
||||
match e.raw_os_error().unwrap() {
|
||||
// Linux returns ENXIO instead of EOPNOTSUPP when opening a socket
|
||||
libc::ENXIO => {
|
||||
match unsafe {
|
||||
fstatat(
|
||||
resolved.dirfd().as_raw_fd(),
|
||||
resolved.path(),
|
||||
AtFlags::SYMLINK_NOFOLLOW,
|
||||
)
|
||||
} {
|
||||
Ok(stat) => {
|
||||
if FileType::from_stat_st_mode(stat.st_mode) == FileType::Socket {
|
||||
return Err(WasiError::ENOTSUP);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::debug!("path_open fstatat error: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Linux returns ENOTDIR instead of ELOOP when using O_NOFOLLOW|O_DIRECTORY
|
||||
// on a symlink.
|
||||
libc::ENOTDIR
|
||||
if !(nix_all_oflags & (OFlags::NOFOLLOW | OFlags::DIRECTORY)).is_empty() =>
|
||||
{
|
||||
match unsafe {
|
||||
fstatat(
|
||||
resolved.dirfd().as_raw_fd(),
|
||||
resolved.path(),
|
||||
AtFlags::SYMLINK_NOFOLLOW,
|
||||
)
|
||||
} {
|
||||
Ok(stat) => {
|
||||
if FileType::from_stat_st_mode(stat.st_mode) == FileType::Symlink {
|
||||
return Err(WasiError::ELOOP);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::debug!("path_open fstatat error: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
// FreeBSD returns EMLINK instead of ELOOP when using O_NOFOLLOW on
|
||||
// a symlink.
|
||||
libc::EMLINK if !(nix_all_oflags & OFlags::NOFOLLOW).is_empty() => {
|
||||
return Err(WasiError::ELOOP);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
return Err(e.into());
|
||||
}
|
||||
};
|
||||
|
||||
tracing::debug!("path_open (host) new_fd = {:?}", new_fd);
|
||||
|
||||
// Determine the type of the new file descriptor and which rights contradict with this type
|
||||
Ok(unsafe { File::from_raw_fd(new_fd) })
|
||||
}
|
||||
|
||||
pub(crate) fn path_readlink(resolved: PathGet, buf: &mut [u8]) -> WasiResult<usize> {
|
||||
use std::cmp::min;
|
||||
use yanix::file::readlinkat;
|
||||
let read_link = unsafe { readlinkat(resolved.dirfd().as_raw_fd(), resolved.path()) }
|
||||
.map_err(Into::into)
|
||||
.and_then(host_impl::path_from_host)?;
|
||||
let copy_len = min(read_link.len(), buf.len());
|
||||
if copy_len > 0 {
|
||||
buf[..copy_len].copy_from_slice(&read_link.as_bytes()[..copy_len]);
|
||||
}
|
||||
Ok(copy_len)
|
||||
}
|
||||
|
||||
pub(crate) fn fd_filestat_get(file: &std::fs::File) -> WasiResult<wasi::__wasi_filestat_t> {
|
||||
use yanix::file::fstat;
|
||||
unsafe { fstat(file.as_raw_fd()) }
|
||||
.map_err(Into::into)
|
||||
.and_then(host_impl::filestat_from_nix)
|
||||
}
|
||||
|
||||
pub(crate) fn path_filestat_get(
|
||||
resolved: PathGet,
|
||||
dirflags: wasi::__wasi_lookupflags_t,
|
||||
) -> WasiResult<wasi::__wasi_filestat_t> {
|
||||
use yanix::file::{fstatat, AtFlags};
|
||||
let atflags = match dirflags {
|
||||
0 => AtFlags::empty(),
|
||||
_ => AtFlags::SYMLINK_NOFOLLOW,
|
||||
};
|
||||
unsafe { fstatat(resolved.dirfd().as_raw_fd(), resolved.path(), atflags) }
|
||||
.map_err(Into::into)
|
||||
.and_then(host_impl::filestat_from_nix)
|
||||
}
|
||||
|
||||
pub(crate) fn path_filestat_set_times(
|
||||
resolved: PathGet,
|
||||
dirflags: wasi::__wasi_lookupflags_t,
|
||||
st_atim: wasi::__wasi_timestamp_t,
|
||||
st_mtim: wasi::__wasi_timestamp_t,
|
||||
fst_flags: wasi::__wasi_fstflags_t,
|
||||
) -> WasiResult<()> {
|
||||
use std::time::{Duration, UNIX_EPOCH};
|
||||
use yanix::filetime::*;
|
||||
|
||||
let set_atim = fst_flags & wasi::__WASI_FSTFLAGS_ATIM != 0;
|
||||
let set_atim_now = fst_flags & wasi::__WASI_FSTFLAGS_ATIM_NOW != 0;
|
||||
let set_mtim = fst_flags & wasi::__WASI_FSTFLAGS_MTIM != 0;
|
||||
let set_mtim_now = fst_flags & wasi::__WASI_FSTFLAGS_MTIM_NOW != 0;
|
||||
|
||||
if (set_atim && set_atim_now) || (set_mtim && set_mtim_now) {
|
||||
return Err(WasiError::EINVAL);
|
||||
}
|
||||
|
||||
let symlink_nofollow = wasi::__WASI_LOOKUPFLAGS_SYMLINK_FOLLOW != dirflags;
|
||||
let atim = if set_atim {
|
||||
let time = UNIX_EPOCH + Duration::from_nanos(st_atim);
|
||||
FileTime::FileTime(filetime::FileTime::from_system_time(time))
|
||||
} else if set_atim_now {
|
||||
FileTime::Now
|
||||
} else {
|
||||
FileTime::Omit
|
||||
};
|
||||
let mtim = if set_mtim {
|
||||
let time = UNIX_EPOCH + Duration::from_nanos(st_mtim);
|
||||
FileTime::FileTime(filetime::FileTime::from_system_time(time))
|
||||
} else if set_mtim_now {
|
||||
FileTime::Now
|
||||
} else {
|
||||
FileTime::Omit
|
||||
};
|
||||
|
||||
utimensat(
|
||||
resolved.dirfd(),
|
||||
resolved.path(),
|
||||
atim,
|
||||
mtim,
|
||||
symlink_nofollow,
|
||||
)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) fn path_remove_directory(resolved: PathGet) -> WasiResult<()> {
|
||||
use yanix::file::{unlinkat, AtFlags};
|
||||
unsafe {
|
||||
unlinkat(
|
||||
resolved.dirfd().as_raw_fd(),
|
||||
resolved.path(),
|
||||
AtFlags::REMOVEDIR,
|
||||
)
|
||||
}
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) fn fd_readdir<'a>(
|
||||
os_handle: &'a mut OsHandle,
|
||||
cookie: wasi::__wasi_dircookie_t,
|
||||
) -> WasiResult<impl Iterator<Item = WasiResult<Dirent>> + 'a> {
|
||||
use yanix::dir::{DirIter, Entry, EntryExt, SeekLoc};
|
||||
|
||||
// Get an instance of `Dir`; this is host-specific due to intricasies
|
||||
// of managing a dir stream between Linux and BSD *nixes
|
||||
let mut dir = fd_readdir_impl::get_dir_from_os_handle(os_handle)?;
|
||||
|
||||
// Seek if needed. Unless cookie is wasi::__WASI_DIRCOOKIE_START,
|
||||
// new items may not be returned to the caller.
|
||||
if cookie == wasi::__WASI_DIRCOOKIE_START {
|
||||
tracing::trace!(" | fd_readdir: doing rewinddir");
|
||||
dir.rewind();
|
||||
} else {
|
||||
tracing::trace!(" | fd_readdir: doing seekdir to {}", cookie);
|
||||
let loc = unsafe { SeekLoc::from_raw(cookie as i64)? };
|
||||
dir.seek(loc);
|
||||
}
|
||||
|
||||
Ok(DirIter::new(dir).map(|entry| {
|
||||
let entry: Entry = entry?;
|
||||
Ok(Dirent {
|
||||
name: entry
|
||||
// TODO can we reuse path_from_host for CStr?
|
||||
.file_name()
|
||||
.to_str()?
|
||||
.to_owned(),
|
||||
ino: entry.ino(),
|
||||
ftype: entry.file_type().into(),
|
||||
cookie: entry.seek_loc()?.to_raw().try_into()?,
|
||||
})
|
||||
}))
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(unused_unsafe)]
|
||||
use crate::old::snapshot_0::sys::host_impl;
|
||||
use crate::old::snapshot_0::wasi::{self, WasiResult};
|
||||
use std::fs::File;
|
||||
use yanix::file::OFlags;
|
||||
|
||||
pub(crate) fn path_open_rights(
|
||||
rights_base: wasi::__wasi_rights_t,
|
||||
rights_inheriting: wasi::__wasi_rights_t,
|
||||
oflags: wasi::__wasi_oflags_t,
|
||||
fs_flags: wasi::__wasi_fdflags_t,
|
||||
) -> (wasi::__wasi_rights_t, wasi::__wasi_rights_t) {
|
||||
// which rights are needed on the dirfd?
|
||||
let mut needed_base = wasi::__WASI_RIGHTS_PATH_OPEN;
|
||||
let mut needed_inheriting = rights_base | rights_inheriting;
|
||||
|
||||
// convert open flags
|
||||
let oflags = host_impl::nix_from_oflags(oflags);
|
||||
if oflags.contains(OFlags::CREAT) {
|
||||
needed_base |= wasi::__WASI_RIGHTS_PATH_CREATE_FILE;
|
||||
}
|
||||
if oflags.contains(OFlags::TRUNC) {
|
||||
needed_base |= wasi::__WASI_RIGHTS_PATH_FILESTAT_SET_SIZE;
|
||||
}
|
||||
|
||||
// convert file descriptor flags
|
||||
let fdflags = host_impl::nix_from_fdflags(fs_flags);
|
||||
if fdflags.contains(OFlags::DSYNC) {
|
||||
needed_inheriting |= wasi::__WASI_RIGHTS_FD_DATASYNC;
|
||||
}
|
||||
if fdflags.intersects(host_impl::O_RSYNC | OFlags::SYNC) {
|
||||
needed_inheriting |= wasi::__WASI_RIGHTS_FD_SYNC;
|
||||
}
|
||||
|
||||
(needed_base, needed_inheriting)
|
||||
}
|
||||
|
||||
pub(crate) fn openat(dirfd: &File, path: &str) -> WasiResult<File> {
|
||||
use std::os::unix::prelude::{AsRawFd, FromRawFd};
|
||||
use yanix::file::{openat, Mode};
|
||||
|
||||
tracing::debug!("path_get openat path = {:?}", path);
|
||||
|
||||
unsafe {
|
||||
openat(
|
||||
dirfd.as_raw_fd(),
|
||||
path,
|
||||
OFlags::RDONLY | OFlags::DIRECTORY | OFlags::NOFOLLOW,
|
||||
Mode::empty(),
|
||||
)
|
||||
}
|
||||
.map(|new_fd| unsafe { File::from_raw_fd(new_fd) })
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) fn readlinkat(dirfd: &File, path: &str) -> WasiResult<String> {
|
||||
use std::os::unix::prelude::AsRawFd;
|
||||
use yanix::file::readlinkat;
|
||||
|
||||
tracing::debug!("path_get readlinkat path = {:?}", path);
|
||||
|
||||
unsafe { readlinkat(dirfd.as_raw_fd(), path) }
|
||||
.map_err(Into::into)
|
||||
.and_then(host_impl::path_from_host)
|
||||
}
|
||||
@@ -1,198 +0,0 @@
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(unused_unsafe)]
|
||||
use crate::old::snapshot_0::hostcalls_impl::{ClockEventData, FdEventData};
|
||||
use crate::old::snapshot_0::wasi::{self, WasiError, WasiResult};
|
||||
use std::io;
|
||||
use yanix::clock::{clock_getres, clock_gettime, ClockId};
|
||||
|
||||
fn wasi_clock_id_to_unix(clock_id: wasi::__wasi_clockid_t) -> WasiResult<ClockId> {
|
||||
// convert the supported clocks to libc types, or return EINVAL
|
||||
match clock_id {
|
||||
wasi::__WASI_CLOCKID_REALTIME => Ok(ClockId::Realtime),
|
||||
wasi::__WASI_CLOCKID_MONOTONIC => Ok(ClockId::Monotonic),
|
||||
wasi::__WASI_CLOCKID_PROCESS_CPUTIME_ID => Ok(ClockId::ProcessCPUTime),
|
||||
wasi::__WASI_CLOCKID_THREAD_CPUTIME_ID => Ok(ClockId::ThreadCPUTime),
|
||||
_ => Err(WasiError::EINVAL),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn clock_res_get(
|
||||
clock_id: wasi::__wasi_clockid_t,
|
||||
) -> WasiResult<wasi::__wasi_timestamp_t> {
|
||||
let clock_id = wasi_clock_id_to_unix(clock_id)?;
|
||||
let timespec = clock_getres(clock_id)?;
|
||||
|
||||
// convert to nanoseconds, returning EOVERFLOW in case of overflow;
|
||||
// this is freelancing a bit from the spec but seems like it'll
|
||||
// be an unusual situation to hit
|
||||
(timespec.tv_sec as wasi::__wasi_timestamp_t)
|
||||
.checked_mul(1_000_000_000)
|
||||
.and_then(|sec_ns| sec_ns.checked_add(timespec.tv_nsec as wasi::__wasi_timestamp_t))
|
||||
.map_or(Err(WasiError::EOVERFLOW), |resolution| {
|
||||
// a supported clock can never return zero; this case will probably never get hit, but
|
||||
// make sure we follow the spec
|
||||
if resolution == 0 {
|
||||
Err(WasiError::EINVAL)
|
||||
} else {
|
||||
Ok(resolution)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn clock_time_get(
|
||||
clock_id: wasi::__wasi_clockid_t,
|
||||
) -> WasiResult<wasi::__wasi_timestamp_t> {
|
||||
let clock_id = wasi_clock_id_to_unix(clock_id)?;
|
||||
let timespec = clock_gettime(clock_id)?;
|
||||
|
||||
// convert to nanoseconds, returning EOVERFLOW in case of overflow; this is freelancing a bit
|
||||
// from the spec but seems like it'll be an unusual situation to hit
|
||||
(timespec.tv_sec as wasi::__wasi_timestamp_t)
|
||||
.checked_mul(1_000_000_000)
|
||||
.and_then(|sec_ns| sec_ns.checked_add(timespec.tv_nsec as wasi::__wasi_timestamp_t))
|
||||
.map_or(Err(WasiError::EOVERFLOW), Ok)
|
||||
}
|
||||
|
||||
pub(crate) fn poll_oneoff(
|
||||
timeout: Option<ClockEventData>,
|
||||
fd_events: Vec<FdEventData>,
|
||||
events: &mut Vec<wasi::__wasi_event_t>,
|
||||
) -> WasiResult<()> {
|
||||
use std::{convert::TryInto, os::unix::prelude::AsRawFd};
|
||||
use yanix::poll::{poll, PollFd, PollFlags};
|
||||
|
||||
if fd_events.is_empty() && timeout.is_none() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut poll_fds: Vec<_> = fd_events
|
||||
.iter()
|
||||
.map(|event| {
|
||||
let mut flags = PollFlags::empty();
|
||||
match event.r#type {
|
||||
wasi::__WASI_EVENTTYPE_FD_READ => flags.insert(PollFlags::POLLIN),
|
||||
wasi::__WASI_EVENTTYPE_FD_WRITE => flags.insert(PollFlags::POLLOUT),
|
||||
// An event on a file descriptor can currently only be of type FD_READ or FD_WRITE
|
||||
// Nothing else has been defined in the specification, and these are also the only two
|
||||
// events we filtered before. If we get something else here, the code has a serious bug.
|
||||
_ => unreachable!(),
|
||||
};
|
||||
unsafe { PollFd::new(event.descriptor.as_raw_fd(), flags) }
|
||||
})
|
||||
.collect();
|
||||
|
||||
let poll_timeout = timeout.map_or(-1, |timeout| {
|
||||
let delay = timeout.delay / 1_000_000; // poll syscall requires delay to expressed in milliseconds
|
||||
delay.try_into().unwrap_or(libc::c_int::max_value())
|
||||
});
|
||||
tracing::debug!("poll_oneoff poll_timeout = {:?}", poll_timeout);
|
||||
|
||||
let ready = loop {
|
||||
match poll(&mut poll_fds, poll_timeout) {
|
||||
Err(_) => {
|
||||
let last_err = io::Error::last_os_error();
|
||||
if last_err.raw_os_error().unwrap() == libc::EINTR {
|
||||
continue;
|
||||
}
|
||||
return Err(last_err.into());
|
||||
}
|
||||
Ok(ready) => break ready,
|
||||
}
|
||||
};
|
||||
|
||||
Ok(if ready == 0 {
|
||||
poll_oneoff_handle_timeout_event(timeout.expect("timeout should not be None"), events)
|
||||
} else {
|
||||
let ready_events = fd_events.into_iter().zip(poll_fds.into_iter()).take(ready);
|
||||
poll_oneoff_handle_fd_event(ready_events, events)?
|
||||
})
|
||||
}
|
||||
|
||||
fn poll_oneoff_handle_timeout_event(
|
||||
timeout: ClockEventData,
|
||||
events: &mut Vec<wasi::__wasi_event_t>,
|
||||
) {
|
||||
events.push(wasi::__wasi_event_t {
|
||||
userdata: timeout.userdata,
|
||||
error: wasi::__WASI_ERRNO_SUCCESS,
|
||||
r#type: wasi::__WASI_EVENTTYPE_CLOCK,
|
||||
fd_readwrite: wasi::__wasi_event_fd_readwrite_t {
|
||||
flags: 0,
|
||||
nbytes: 0,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
fn poll_oneoff_handle_fd_event<'a>(
|
||||
ready_events: impl Iterator<Item = (FdEventData<'a>, yanix::poll::PollFd)>,
|
||||
events: &mut Vec<wasi::__wasi_event_t>,
|
||||
) -> WasiResult<()> {
|
||||
use std::{convert::TryInto, os::unix::prelude::AsRawFd};
|
||||
use yanix::{file::fionread, poll::PollFlags};
|
||||
|
||||
for (fd_event, poll_fd) in ready_events {
|
||||
tracing::debug!("poll_oneoff_handle_fd_event fd_event = {:?}", fd_event);
|
||||
tracing::debug!("poll_oneoff_handle_fd_event poll_fd = {:?}", poll_fd);
|
||||
|
||||
let revents = match poll_fd.revents() {
|
||||
Some(revents) => revents,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
tracing::debug!("poll_oneoff_handle_fd_event revents = {:?}", revents);
|
||||
|
||||
let nbytes = if fd_event.r#type == wasi::__WASI_EVENTTYPE_FD_READ {
|
||||
unsafe { fionread(fd_event.descriptor.as_raw_fd())? }
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
let output_event = if revents.contains(PollFlags::POLLNVAL) {
|
||||
wasi::__wasi_event_t {
|
||||
userdata: fd_event.userdata,
|
||||
error: wasi::__WASI_ERRNO_BADF,
|
||||
r#type: fd_event.r#type,
|
||||
fd_readwrite: wasi::__wasi_event_fd_readwrite_t {
|
||||
nbytes: 0,
|
||||
flags: wasi::__WASI_EVENTRWFLAGS_FD_READWRITE_HANGUP,
|
||||
},
|
||||
}
|
||||
} else if revents.contains(PollFlags::POLLERR) {
|
||||
wasi::__wasi_event_t {
|
||||
userdata: fd_event.userdata,
|
||||
error: wasi::__WASI_ERRNO_IO,
|
||||
r#type: fd_event.r#type,
|
||||
fd_readwrite: wasi::__wasi_event_fd_readwrite_t {
|
||||
nbytes: 0,
|
||||
flags: wasi::__WASI_EVENTRWFLAGS_FD_READWRITE_HANGUP,
|
||||
},
|
||||
}
|
||||
} else if revents.contains(PollFlags::POLLHUP) {
|
||||
wasi::__wasi_event_t {
|
||||
userdata: fd_event.userdata,
|
||||
error: wasi::__WASI_ERRNO_SUCCESS,
|
||||
r#type: fd_event.r#type,
|
||||
fd_readwrite: wasi::__wasi_event_fd_readwrite_t {
|
||||
nbytes: 0,
|
||||
flags: wasi::__WASI_EVENTRWFLAGS_FD_READWRITE_HANGUP,
|
||||
},
|
||||
}
|
||||
} else if revents.contains(PollFlags::POLLIN) | revents.contains(PollFlags::POLLOUT) {
|
||||
wasi::__wasi_event_t {
|
||||
userdata: fd_event.userdata,
|
||||
error: wasi::__WASI_ERRNO_SUCCESS,
|
||||
r#type: fd_event.r#type,
|
||||
fd_readwrite: wasi::__wasi_event_fd_readwrite_t {
|
||||
nbytes: nbytes.try_into()?,
|
||||
flags: 0,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
|
||||
events.push(output_event);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
//! Unix-specific hostcalls that implement
|
||||
//! [WASI](https://github.com/WebAssembly/WASI).
|
||||
mod fs;
|
||||
pub(crate) mod fs_helpers;
|
||||
mod misc;
|
||||
|
||||
pub(crate) use self::fs::*;
|
||||
pub(crate) use self::misc::*;
|
||||
@@ -1,16 +0,0 @@
|
||||
use crate::old::snapshot_0::wasi::{self, WasiResult};
|
||||
use std::convert::TryInto;
|
||||
|
||||
pub(crate) const O_RSYNC: yanix::file::OFlags = yanix::file::OFlags::RSYNC;
|
||||
|
||||
pub(crate) fn stdev_from_nix(dev: libc::dev_t) -> WasiResult<wasi::__wasi_device_t> {
|
||||
Ok(wasi::__wasi_device_t::from(dev))
|
||||
}
|
||||
|
||||
pub(crate) fn stino_from_nix(ino: libc::ino_t) -> WasiResult<wasi::__wasi_inode_t> {
|
||||
Ok(wasi::__wasi_device_t::from(ino))
|
||||
}
|
||||
|
||||
pub(crate) fn stnlink_from_nix(nlink: libc::nlink_t) -> WasiResult<wasi::__wasi_linkcount_t> {
|
||||
nlink.try_into().map_err(Into::into)
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
use crate::old::snapshot_0::hostcalls_impl::PathGet;
|
||||
use crate::old::snapshot_0::wasi::WasiResult;
|
||||
use std::os::unix::prelude::AsRawFd;
|
||||
|
||||
pub(crate) fn path_unlink_file(resolved: PathGet) -> WasiResult<()> {
|
||||
use yanix::file::{unlinkat, AtFlags};
|
||||
unsafe {
|
||||
unlinkat(
|
||||
resolved.dirfd().as_raw_fd(),
|
||||
resolved.path(),
|
||||
AtFlags::empty(),
|
||||
)
|
||||
}
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> WasiResult<()> {
|
||||
use yanix::file::symlinkat;
|
||||
|
||||
tracing::debug!("path_symlink old_path = {:?}", old_path);
|
||||
tracing::debug!("path_symlink resolved = {:?}", resolved);
|
||||
|
||||
unsafe { symlinkat(old_path, resolved.dirfd().as_raw_fd(), resolved.path()) }
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> WasiResult<()> {
|
||||
use yanix::file::renameat;
|
||||
unsafe {
|
||||
renameat(
|
||||
resolved_old.dirfd().as_raw_fd(),
|
||||
resolved_old.path(),
|
||||
resolved_new.dirfd().as_raw_fd(),
|
||||
resolved_new.path(),
|
||||
)
|
||||
}
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) mod fd_readdir_impl {
|
||||
use crate::old::snapshot_0::sys::entry_impl::OsHandle;
|
||||
use crate::old::snapshot_0::wasi::WasiResult;
|
||||
use yanix::dir::Dir;
|
||||
|
||||
pub(crate) fn get_dir_from_os_handle(os_handle: &mut OsHandle) -> WasiResult<Box<Dir>> {
|
||||
// We need to duplicate the fd, because `opendir(3)`:
|
||||
// After a successful call to fdopendir(), fd is used internally by the implementation,
|
||||
// and should not otherwise be used by the application.
|
||||
// `opendir(3p)` also says that it's undefined behavior to
|
||||
// modify the state of the fd in a different way than by accessing DIR*.
|
||||
//
|
||||
// Still, rewinddir will be needed because the two file descriptors
|
||||
// share progress. But we can safely execute closedir now.
|
||||
let fd = os_handle.try_clone()?;
|
||||
// TODO This doesn't look very clean. Can we do something about it?
|
||||
// Boxing is needed here in order to satisfy `yanix`'s trait requirement for the `DirIter`
|
||||
// where `T: Deref<Target = Dir>`.
|
||||
Ok(Box::new(Dir::from(fd)?))
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
pub(crate) mod host_impl;
|
||||
pub(crate) mod hostcalls_impl;
|
||||
pub(crate) mod oshandle;
|
||||
@@ -1,32 +0,0 @@
|
||||
use std::fs;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::unix::prelude::{AsRawFd, RawFd};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct OsHandle(fs::File);
|
||||
|
||||
impl From<fs::File> for OsHandle {
|
||||
fn from(file: fs::File) -> Self {
|
||||
Self(file)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for OsHandle {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.0.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for OsHandle {
|
||||
type Target = fs::File;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for OsHandle {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
pub(crate) mod entry_impl;
|
||||
pub(crate) mod host_impl;
|
||||
pub(crate) mod hostcalls_impl;
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(target_os = "linux")] {
|
||||
mod linux;
|
||||
use self::linux as sys_impl;
|
||||
} else if #[cfg(target_os = "emscripten")] {
|
||||
mod emscripten;
|
||||
use self::emscripten as sys_impl;
|
||||
} else if #[cfg(any(target_os = "macos",
|
||||
target_os = "netbsd",
|
||||
target_os = "freebsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "ios",
|
||||
target_os = "dragonfly"))] {
|
||||
mod bsd;
|
||||
use self::bsd as sys_impl;
|
||||
}
|
||||
}
|
||||
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::Result;
|
||||
|
||||
pub(crate) fn dev_null() -> Result<File> {
|
||||
OpenOptions::new().read(true).write(true).open("/dev/null")
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
use crate::old::snapshot_0::entry::{Descriptor, OsHandleRef};
|
||||
use crate::old::snapshot_0::wasi;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::windows::prelude::{AsRawHandle, FromRawHandle, RawHandle};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct OsHandle(File);
|
||||
|
||||
impl From<File> for OsHandle {
|
||||
fn from(file: File) -> Self {
|
||||
Self(file)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawHandle for OsHandle {
|
||||
fn as_raw_handle(&self) -> RawHandle {
|
||||
self.0.as_raw_handle()
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for OsHandle {
|
||||
type Target = File;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for OsHandle {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawHandle for Descriptor {
|
||||
fn as_raw_handle(&self) -> RawHandle {
|
||||
match self {
|
||||
Self::OsHandle(file) => file.as_raw_handle(),
|
||||
Self::Stdin => io::stdin().as_raw_handle(),
|
||||
Self::Stdout => io::stdout().as_raw_handle(),
|
||||
Self::Stderr => io::stderr().as_raw_handle(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn descriptor_as_oshandle<'lifetime>(
|
||||
desc: &'lifetime Descriptor,
|
||||
) -> OsHandleRef<'lifetime> {
|
||||
OsHandleRef::new(ManuallyDrop::new(OsHandle::from(unsafe {
|
||||
File::from_raw_handle(desc.as_raw_handle())
|
||||
})))
|
||||
}
|
||||
|
||||
/// This function is unsafe because it operates on a raw file handle.
|
||||
pub(crate) unsafe fn determine_type_and_access_rights<Handle: AsRawHandle>(
|
||||
handle: &Handle,
|
||||
) -> io::Result<(
|
||||
wasi::__wasi_filetype_t,
|
||||
wasi::__wasi_rights_t,
|
||||
wasi::__wasi_rights_t,
|
||||
)> {
|
||||
use winx::file::{query_access_information, AccessMode};
|
||||
|
||||
let (file_type, mut rights_base, rights_inheriting) = determine_type_rights(handle)?;
|
||||
|
||||
match file_type {
|
||||
wasi::__WASI_FILETYPE_DIRECTORY | wasi::__WASI_FILETYPE_REGULAR_FILE => {
|
||||
let mode = query_access_information(handle.as_raw_handle())?;
|
||||
if mode.contains(AccessMode::FILE_GENERIC_READ) {
|
||||
rights_base |= wasi::__WASI_RIGHTS_FD_READ;
|
||||
}
|
||||
if mode.contains(AccessMode::FILE_GENERIC_WRITE) {
|
||||
rights_base |= wasi::__WASI_RIGHTS_FD_WRITE;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// TODO: is there a way around this? On windows, it seems
|
||||
// we cannot check access rights for anything but dirs and regular files
|
||||
}
|
||||
}
|
||||
|
||||
Ok((file_type, rights_base, rights_inheriting))
|
||||
}
|
||||
|
||||
/// This function is unsafe because it operates on a raw file handle.
|
||||
pub(crate) unsafe fn determine_type_rights<Handle: AsRawHandle>(
|
||||
handle: &Handle,
|
||||
) -> io::Result<(
|
||||
wasi::__wasi_filetype_t,
|
||||
wasi::__wasi_rights_t,
|
||||
wasi::__wasi_rights_t,
|
||||
)> {
|
||||
let (file_type, rights_base, rights_inheriting) = {
|
||||
let file_type = winx::file::get_file_type(handle.as_raw_handle())?;
|
||||
if file_type.is_char() {
|
||||
// character file: LPT device or console
|
||||
// TODO: rule out LPT device
|
||||
(
|
||||
wasi::__WASI_FILETYPE_CHARACTER_DEVICE,
|
||||
wasi::RIGHTS_TTY_BASE,
|
||||
wasi::RIGHTS_TTY_BASE,
|
||||
)
|
||||
} else if file_type.is_disk() {
|
||||
// disk file: file, dir or disk device
|
||||
let file = std::mem::ManuallyDrop::new(File::from_raw_handle(handle.as_raw_handle()));
|
||||
let meta = file.metadata()?;
|
||||
if meta.is_dir() {
|
||||
(
|
||||
wasi::__WASI_FILETYPE_DIRECTORY,
|
||||
wasi::RIGHTS_DIRECTORY_BASE,
|
||||
wasi::RIGHTS_DIRECTORY_INHERITING,
|
||||
)
|
||||
} else if meta.is_file() {
|
||||
(
|
||||
wasi::__WASI_FILETYPE_REGULAR_FILE,
|
||||
wasi::RIGHTS_REGULAR_FILE_BASE,
|
||||
wasi::RIGHTS_REGULAR_FILE_INHERITING,
|
||||
)
|
||||
} else {
|
||||
return Err(io::Error::from_raw_os_error(libc::EINVAL));
|
||||
}
|
||||
} else if file_type.is_pipe() {
|
||||
// pipe object: socket, named pipe or anonymous pipe
|
||||
// TODO: what about pipes, etc?
|
||||
(
|
||||
wasi::__WASI_FILETYPE_SOCKET_STREAM,
|
||||
wasi::RIGHTS_SOCKET_BASE,
|
||||
wasi::RIGHTS_SOCKET_INHERITING,
|
||||
)
|
||||
} else {
|
||||
return Err(io::Error::from_raw_os_error(libc::EINVAL));
|
||||
}
|
||||
};
|
||||
Ok((file_type, rights_base, rights_inheriting))
|
||||
}
|
||||
@@ -1,177 +0,0 @@
|
||||
//! WASI host types specific to Windows host.
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(unused)]
|
||||
use crate::old::snapshot_0::host::FileType;
|
||||
use crate::old::snapshot_0::wasi::{self, WasiError, WasiResult};
|
||||
use std::convert::TryInto;
|
||||
use std::ffi::OsStr;
|
||||
use std::fs::OpenOptions;
|
||||
use std::fs::{self, File};
|
||||
use std::io;
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
use std::os::windows::fs::OpenOptionsExt;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use winapi::shared::winerror;
|
||||
use winx::file::{AccessMode, Attributes, CreationDisposition, Flags};
|
||||
|
||||
impl From<io::Error> for WasiError {
|
||||
fn from(err: io::Error) -> Self {
|
||||
match err.raw_os_error() {
|
||||
Some(code) => match code as u32 {
|
||||
winerror::ERROR_SUCCESS => Self::ESUCCESS,
|
||||
winerror::ERROR_BAD_ENVIRONMENT => Self::E2BIG,
|
||||
winerror::ERROR_FILE_NOT_FOUND => Self::ENOENT,
|
||||
winerror::ERROR_PATH_NOT_FOUND => Self::ENOENT,
|
||||
winerror::ERROR_TOO_MANY_OPEN_FILES => Self::ENFILE,
|
||||
winerror::ERROR_ACCESS_DENIED => Self::EACCES,
|
||||
winerror::ERROR_SHARING_VIOLATION => Self::EACCES,
|
||||
winerror::ERROR_PRIVILEGE_NOT_HELD => Self::ENOTCAPABLE,
|
||||
winerror::ERROR_INVALID_HANDLE => Self::EBADF,
|
||||
winerror::ERROR_INVALID_NAME => Self::ENOENT,
|
||||
winerror::ERROR_NOT_ENOUGH_MEMORY => Self::ENOMEM,
|
||||
winerror::ERROR_OUTOFMEMORY => Self::ENOMEM,
|
||||
winerror::ERROR_DIR_NOT_EMPTY => Self::ENOTEMPTY,
|
||||
winerror::ERROR_NOT_READY => Self::EBUSY,
|
||||
winerror::ERROR_BUSY => Self::EBUSY,
|
||||
winerror::ERROR_NOT_SUPPORTED => Self::ENOTSUP,
|
||||
winerror::ERROR_FILE_EXISTS => Self::EEXIST,
|
||||
winerror::ERROR_BROKEN_PIPE => Self::EPIPE,
|
||||
winerror::ERROR_BUFFER_OVERFLOW => Self::ENAMETOOLONG,
|
||||
winerror::ERROR_NOT_A_REPARSE_POINT => Self::EINVAL,
|
||||
winerror::ERROR_NEGATIVE_SEEK => Self::EINVAL,
|
||||
winerror::ERROR_DIRECTORY => Self::ENOTDIR,
|
||||
winerror::ERROR_ALREADY_EXISTS => Self::EEXIST,
|
||||
x => {
|
||||
tracing::debug!("unknown error value: {}", x);
|
||||
Self::EIO
|
||||
}
|
||||
},
|
||||
None => {
|
||||
tracing::debug!("Other I/O error: {}", err);
|
||||
Self::EIO
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn fdflags_from_win(mode: AccessMode) -> wasi::__wasi_fdflags_t {
|
||||
let mut fdflags = 0;
|
||||
// TODO verify this!
|
||||
if mode.contains(AccessMode::FILE_APPEND_DATA) {
|
||||
fdflags |= wasi::__WASI_FDFLAGS_APPEND;
|
||||
}
|
||||
if mode.contains(AccessMode::SYNCHRONIZE) {
|
||||
fdflags |= wasi::__WASI_FDFLAGS_DSYNC;
|
||||
fdflags |= wasi::__WASI_FDFLAGS_RSYNC;
|
||||
fdflags |= wasi::__WASI_FDFLAGS_SYNC;
|
||||
}
|
||||
// The NONBLOCK equivalent is FILE_FLAG_OVERLAPPED
|
||||
// but it seems winapi doesn't provide a mechanism
|
||||
// for checking whether the handle supports async IO.
|
||||
// On the contrary, I've found some dicsussion online
|
||||
// which suggests that on Windows all handles should
|
||||
// generally be assumed to be opened with async support
|
||||
// and then the program should fallback should that **not**
|
||||
// be the case at the time of the operation.
|
||||
// TODO: this requires further investigation
|
||||
fdflags
|
||||
}
|
||||
|
||||
pub(crate) fn win_from_fdflags(fdflags: wasi::__wasi_fdflags_t) -> (AccessMode, Flags) {
|
||||
let mut access_mode = AccessMode::empty();
|
||||
let mut flags = Flags::empty();
|
||||
|
||||
// TODO verify this!
|
||||
if fdflags & wasi::__WASI_FDFLAGS_NONBLOCK != 0 {
|
||||
flags.insert(Flags::FILE_FLAG_OVERLAPPED);
|
||||
}
|
||||
if fdflags & wasi::__WASI_FDFLAGS_APPEND != 0 {
|
||||
access_mode.insert(AccessMode::FILE_APPEND_DATA);
|
||||
}
|
||||
if fdflags & wasi::__WASI_FDFLAGS_DSYNC != 0
|
||||
|| fdflags & wasi::__WASI_FDFLAGS_RSYNC != 0
|
||||
|| fdflags & wasi::__WASI_FDFLAGS_SYNC != 0
|
||||
{
|
||||
access_mode.insert(AccessMode::SYNCHRONIZE);
|
||||
}
|
||||
|
||||
(access_mode, flags)
|
||||
}
|
||||
|
||||
pub(crate) fn win_from_oflags(oflags: wasi::__wasi_oflags_t) -> CreationDisposition {
|
||||
if oflags & wasi::__WASI_OFLAGS_CREAT != 0 {
|
||||
if oflags & wasi::__WASI_OFLAGS_EXCL != 0 {
|
||||
CreationDisposition::CREATE_NEW
|
||||
} else {
|
||||
CreationDisposition::CREATE_ALWAYS
|
||||
}
|
||||
} else if oflags & wasi::__WASI_OFLAGS_TRUNC != 0 {
|
||||
CreationDisposition::TRUNCATE_EXISTING
|
||||
} else {
|
||||
CreationDisposition::OPEN_EXISTING
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn filetype_from_std(ftype: &fs::FileType) -> FileType {
|
||||
if ftype.is_file() {
|
||||
FileType::RegularFile
|
||||
} else if ftype.is_dir() {
|
||||
FileType::Directory
|
||||
} else if ftype.is_symlink() {
|
||||
FileType::Symlink
|
||||
} else {
|
||||
FileType::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
fn num_hardlinks(file: &File) -> io::Result<u64> {
|
||||
Ok(winx::file::get_fileinfo(file)?.nNumberOfLinks.into())
|
||||
}
|
||||
|
||||
fn device_id(file: &File) -> io::Result<u64> {
|
||||
Ok(winx::file::get_fileinfo(file)?.dwVolumeSerialNumber.into())
|
||||
}
|
||||
|
||||
pub(crate) fn file_serial_no(file: &File) -> io::Result<u64> {
|
||||
let info = winx::file::get_fileinfo(file)?;
|
||||
let high = info.nFileIndexHigh;
|
||||
let low = info.nFileIndexLow;
|
||||
let no = (u64::from(high) << 32) | u64::from(low);
|
||||
Ok(no)
|
||||
}
|
||||
|
||||
fn change_time(file: &File) -> io::Result<i64> {
|
||||
winx::file::change_time(file)
|
||||
}
|
||||
|
||||
fn systemtime_to_timestamp(st: SystemTime) -> WasiResult<u64> {
|
||||
st.duration_since(UNIX_EPOCH)
|
||||
.map_err(|_| WasiError::EINVAL)? // date earlier than UNIX_EPOCH
|
||||
.as_nanos()
|
||||
.try_into()
|
||||
.map_err(Into::into) // u128 doesn't fit into u64
|
||||
}
|
||||
|
||||
pub(crate) fn filestat_from_win(file: &File) -> WasiResult<wasi::__wasi_filestat_t> {
|
||||
let metadata = file.metadata()?;
|
||||
Ok(wasi::__wasi_filestat_t {
|
||||
dev: device_id(file)?,
|
||||
ino: file_serial_no(file)?,
|
||||
nlink: num_hardlinks(file)?.try_into()?, // u64 doesn't fit into u32
|
||||
size: metadata.len(),
|
||||
atim: systemtime_to_timestamp(metadata.accessed()?)?,
|
||||
ctim: change_time(file)?.try_into()?, // i64 doesn't fit into u64
|
||||
mtim: systemtime_to_timestamp(metadata.modified()?)?,
|
||||
filetype: filetype_from_std(&metadata.file_type()).to_wasi(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates owned WASI path from OS string.
|
||||
///
|
||||
/// NB WASI spec requires OS string to be valid UTF-8. Otherwise,
|
||||
/// `__WASI_ERRNO_ILSEQ` error is returned.
|
||||
pub(crate) fn path_from_host<S: AsRef<OsStr>>(s: S) -> WasiResult<String> {
|
||||
let vec: Vec<u16> = s.as_ref().encode_wide().collect();
|
||||
String::from_utf16(&vec).map_err(|_| WasiError::EILSEQ)
|
||||
}
|
||||
@@ -1,554 +0,0 @@
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(unused)]
|
||||
use super::fs_helpers::*;
|
||||
use crate::old::snapshot_0::ctx::WasiCtx;
|
||||
use crate::old::snapshot_0::entry::Entry;
|
||||
use crate::old::snapshot_0::host::{Dirent, FileType};
|
||||
use crate::old::snapshot_0::hostcalls_impl::{fd_filestat_set_times_impl, PathGet};
|
||||
use crate::old::snapshot_0::sys::entry_impl::determine_type_rights;
|
||||
use crate::old::snapshot_0::sys::host_impl::{self, path_from_host};
|
||||
use crate::old::snapshot_0::sys::hostcalls_impl::fs_helpers::PathGetExt;
|
||||
use crate::old::snapshot_0::wasi::{self, WasiError, WasiResult};
|
||||
use std::convert::TryInto;
|
||||
use std::fs::{File, Metadata, OpenOptions};
|
||||
use std::io::{self, Seek, SeekFrom};
|
||||
use std::os::windows::fs::{FileExt, OpenOptionsExt};
|
||||
use std::os::windows::prelude::{AsRawHandle, FromRawHandle};
|
||||
use std::path::{Path, PathBuf};
|
||||
use tracing::{debug, trace};
|
||||
use winapi::shared::winerror;
|
||||
use winx::file::{AccessMode, CreationDisposition, FileModeInformation, Flags};
|
||||
|
||||
fn read_at(mut file: &File, buf: &mut [u8], offset: u64) -> io::Result<usize> {
|
||||
// get current cursor position
|
||||
let cur_pos = file.seek(SeekFrom::Current(0))?;
|
||||
// perform a seek read by a specified offset
|
||||
let nread = file.seek_read(buf, offset)?;
|
||||
// rewind the cursor back to the original position
|
||||
file.seek(SeekFrom::Start(cur_pos))?;
|
||||
Ok(nread)
|
||||
}
|
||||
|
||||
fn write_at(mut file: &File, buf: &[u8], offset: u64) -> io::Result<usize> {
|
||||
// get current cursor position
|
||||
let cur_pos = file.seek(SeekFrom::Current(0))?;
|
||||
// perform a seek write by a specified offset
|
||||
let nwritten = file.seek_write(buf, offset)?;
|
||||
// rewind the cursor back to the original position
|
||||
file.seek(SeekFrom::Start(cur_pos))?;
|
||||
Ok(nwritten)
|
||||
}
|
||||
|
||||
// TODO refactor common code with unix
|
||||
pub(crate) fn fd_pread(
|
||||
file: &File,
|
||||
buf: &mut [u8],
|
||||
offset: wasi::__wasi_filesize_t,
|
||||
) -> WasiResult<usize> {
|
||||
read_at(file, buf, offset).map_err(Into::into)
|
||||
}
|
||||
|
||||
// TODO refactor common code with unix
|
||||
pub(crate) fn fd_pwrite(
|
||||
file: &File,
|
||||
buf: &[u8],
|
||||
offset: wasi::__wasi_filesize_t,
|
||||
) -> WasiResult<usize> {
|
||||
write_at(file, buf, offset).map_err(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) fn fd_fdstat_get(fd: &File) -> WasiResult<wasi::__wasi_fdflags_t> {
|
||||
let mut fdflags = 0;
|
||||
|
||||
let handle = unsafe { fd.as_raw_handle() };
|
||||
|
||||
let access_mode = winx::file::query_access_information(handle)?;
|
||||
let mode = winx::file::query_mode_information(handle)?;
|
||||
|
||||
// Append without write implies append-only (__WASI_FDFLAGS_APPEND)
|
||||
if access_mode.contains(AccessMode::FILE_APPEND_DATA)
|
||||
&& !access_mode.contains(AccessMode::FILE_WRITE_DATA)
|
||||
{
|
||||
fdflags |= wasi::__WASI_FDFLAGS_APPEND;
|
||||
}
|
||||
|
||||
if mode.contains(FileModeInformation::FILE_WRITE_THROUGH) {
|
||||
// Only report __WASI_FDFLAGS_SYNC
|
||||
// This is technically the only one of the O_?SYNC flags Windows supports.
|
||||
fdflags |= wasi::__WASI_FDFLAGS_SYNC;
|
||||
}
|
||||
|
||||
// Files do not support the `__WASI_FDFLAGS_NONBLOCK` flag
|
||||
|
||||
Ok(fdflags)
|
||||
}
|
||||
|
||||
pub(crate) fn fd_fdstat_set_flags(fd: &File, fdflags: wasi::__wasi_fdflags_t) -> WasiResult<()> {
|
||||
unimplemented!("fd_fdstat_set_flags")
|
||||
}
|
||||
|
||||
pub(crate) fn fd_advise(
|
||||
_file: &File,
|
||||
advice: wasi::__wasi_advice_t,
|
||||
_offset: wasi::__wasi_filesize_t,
|
||||
_len: wasi::__wasi_filesize_t,
|
||||
) -> WasiResult<()> {
|
||||
match advice {
|
||||
wasi::__WASI_ADVICE_DONTNEED
|
||||
| wasi::__WASI_ADVICE_SEQUENTIAL
|
||||
| wasi::__WASI_ADVICE_WILLNEED
|
||||
| wasi::__WASI_ADVICE_NOREUSE
|
||||
| wasi::__WASI_ADVICE_RANDOM
|
||||
| wasi::__WASI_ADVICE_NORMAL => {}
|
||||
_ => return Err(WasiError::EINVAL),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn path_create_directory(resolved: PathGet) -> WasiResult<()> {
|
||||
let path = resolved.concatenate()?;
|
||||
std::fs::create_dir(&path).map_err(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) fn path_link(resolved_old: PathGet, resolved_new: PathGet) -> WasiResult<()> {
|
||||
unimplemented!("path_link")
|
||||
}
|
||||
|
||||
pub(crate) fn path_open(
|
||||
resolved: PathGet,
|
||||
read: bool,
|
||||
write: bool,
|
||||
oflags: wasi::__wasi_oflags_t,
|
||||
fdflags: wasi::__wasi_fdflags_t,
|
||||
) -> WasiResult<File> {
|
||||
use winx::file::{AccessMode, CreationDisposition, Flags};
|
||||
|
||||
// convert open flags
|
||||
// note: the calls to `write(true)` are to bypass an internal OpenOption check
|
||||
// the write flag will ultimately be ignored when `access_mode` is called below.
|
||||
let mut opts = OpenOptions::new();
|
||||
match creation_disposition_from_oflags(oflags) {
|
||||
CreationDisposition::CREATE_ALWAYS => {
|
||||
opts.create(true).write(true);
|
||||
}
|
||||
CreationDisposition::CREATE_NEW => {
|
||||
opts.create_new(true).write(true);
|
||||
}
|
||||
CreationDisposition::TRUNCATE_EXISTING => {
|
||||
opts.truncate(true).write(true);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let path = resolved.concatenate()?;
|
||||
|
||||
match path.symlink_metadata().map(|metadata| metadata.file_type()) {
|
||||
Ok(file_type) => {
|
||||
// check if we are trying to open a symlink
|
||||
if file_type.is_symlink() {
|
||||
return Err(WasiError::ELOOP);
|
||||
}
|
||||
// check if we are trying to open a file as a dir
|
||||
if file_type.is_file() && oflags & wasi::__WASI_OFLAGS_DIRECTORY != 0 {
|
||||
return Err(WasiError::ENOTDIR);
|
||||
}
|
||||
}
|
||||
Err(err) => match err.raw_os_error() {
|
||||
Some(code) => {
|
||||
tracing::debug!("path_open at symlink_metadata error code={:?}", code);
|
||||
|
||||
if code as u32 != winerror::ERROR_FILE_NOT_FOUND {
|
||||
return Err(err.into());
|
||||
}
|
||||
// file not found, let it proceed to actually
|
||||
// trying to open it
|
||||
}
|
||||
None => {
|
||||
tracing::debug!("Inconvertible OS error: {}", err);
|
||||
return Err(WasiError::EIO);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
opts.access_mode(file_access_mode_from_fdflags(fdflags, read, write).bits())
|
||||
.custom_flags(file_flags_from_fdflags(fdflags).bits())
|
||||
.open(&path)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
fn creation_disposition_from_oflags(oflags: wasi::__wasi_oflags_t) -> CreationDisposition {
|
||||
if oflags & wasi::__WASI_OFLAGS_CREAT != 0 {
|
||||
if oflags & wasi::__WASI_OFLAGS_EXCL != 0 {
|
||||
CreationDisposition::CREATE_NEW
|
||||
} else {
|
||||
CreationDisposition::CREATE_ALWAYS
|
||||
}
|
||||
} else if oflags & wasi::__WASI_OFLAGS_TRUNC != 0 {
|
||||
CreationDisposition::TRUNCATE_EXISTING
|
||||
} else {
|
||||
CreationDisposition::OPEN_EXISTING
|
||||
}
|
||||
}
|
||||
|
||||
fn file_access_mode_from_fdflags(
|
||||
fdflags: wasi::__wasi_fdflags_t,
|
||||
read: bool,
|
||||
write: bool,
|
||||
) -> AccessMode {
|
||||
let mut access_mode = AccessMode::READ_CONTROL;
|
||||
|
||||
if read {
|
||||
access_mode.insert(AccessMode::GENERIC_READ);
|
||||
}
|
||||
|
||||
if write {
|
||||
access_mode.insert(AccessMode::GENERIC_WRITE);
|
||||
}
|
||||
|
||||
// For append, grant the handle FILE_APPEND_DATA access but *not* FILE_WRITE_DATA.
|
||||
// This makes the handle "append only".
|
||||
// Changes to the file pointer will be ignored (like POSIX's O_APPEND behavior).
|
||||
if fdflags & wasi::__WASI_FDFLAGS_APPEND != 0 {
|
||||
access_mode.insert(AccessMode::FILE_APPEND_DATA);
|
||||
access_mode.remove(AccessMode::FILE_WRITE_DATA);
|
||||
}
|
||||
|
||||
access_mode
|
||||
}
|
||||
|
||||
fn file_flags_from_fdflags(fdflags: wasi::__wasi_fdflags_t) -> Flags {
|
||||
// Enable backup semantics so directories can be opened as files
|
||||
let mut flags = Flags::FILE_FLAG_BACKUP_SEMANTICS;
|
||||
|
||||
// Note: __WASI_FDFLAGS_NONBLOCK is purposely being ignored for files
|
||||
// While Windows does inherently support a non-blocking mode on files, the WASI API will
|
||||
// treat I/O operations on files as synchronous. WASI might have an async-io API in the future.
|
||||
|
||||
// Technically, Windows only supports __WASI_FDFLAGS_SYNC, but treat all the flags as the same.
|
||||
if fdflags & wasi::__WASI_FDFLAGS_DSYNC != 0
|
||||
|| fdflags & wasi::__WASI_FDFLAGS_RSYNC != 0
|
||||
|| fdflags & wasi::__WASI_FDFLAGS_SYNC != 0
|
||||
{
|
||||
flags.insert(Flags::FILE_FLAG_WRITE_THROUGH);
|
||||
}
|
||||
|
||||
flags
|
||||
}
|
||||
|
||||
fn dirent_from_path<P: AsRef<Path>>(
|
||||
path: P,
|
||||
name: &str,
|
||||
cookie: wasi::__wasi_dircookie_t,
|
||||
) -> WasiResult<Dirent> {
|
||||
let path = path.as_ref();
|
||||
trace!("dirent_from_path: opening {}", path.to_string_lossy());
|
||||
|
||||
// To open a directory on Windows, FILE_FLAG_BACKUP_SEMANTICS flag must be used
|
||||
let file = OpenOptions::new()
|
||||
.custom_flags(Flags::FILE_FLAG_BACKUP_SEMANTICS.bits())
|
||||
.read(true)
|
||||
.open(path)?;
|
||||
let ty = file.metadata()?.file_type();
|
||||
Ok(Dirent {
|
||||
ftype: host_impl::filetype_from_std(&ty),
|
||||
name: name.to_owned(),
|
||||
cookie,
|
||||
ino: host_impl::file_serial_no(&file)?,
|
||||
})
|
||||
}
|
||||
|
||||
// On Windows there is apparently no support for seeking the directory stream in the OS.
|
||||
// cf. https://github.com/WebAssembly/WASI/issues/61
|
||||
//
|
||||
// The implementation here may perform in O(n^2) if the host buffer is O(1)
|
||||
// and the number of directory entries is O(n).
|
||||
// TODO: Add a heuristic optimization to achieve O(n) time in the most common case
|
||||
// where fd_readdir is resumed where it previously finished
|
||||
//
|
||||
// Correctness of this approach relies upon one assumption: that the order of entries
|
||||
// returned by `FindNextFileW` is stable, i.e. doesn't change if the directory
|
||||
// contents stay the same. This invariant is crucial to be able to implement
|
||||
// any kind of seeking whatsoever without having to read the whole directory at once
|
||||
// and then return the data from cache. (which leaks memory)
|
||||
//
|
||||
// The MSDN documentation explicitly says that the order in which the search returns the files
|
||||
// is not guaranteed, and is dependent on the file system.
|
||||
// cf. https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findnextfilew
|
||||
//
|
||||
// This stackoverflow post suggests that `FindNextFileW` is indeed stable and that
|
||||
// the order of directory entries depends **only** on the filesystem used, but the
|
||||
// MSDN documentation is not clear about this.
|
||||
// cf. https://stackoverflow.com/questions/47380739/is-findfirstfile-and-findnextfile-order-random-even-for-dvd
|
||||
//
|
||||
// Implementation details:
|
||||
// Cookies for the directory entries start from 1. (0 is reserved by wasi::__WASI_DIRCOOKIE_START)
|
||||
// . gets cookie = 1
|
||||
// .. gets cookie = 2
|
||||
// other entries, in order they were returned by FindNextFileW get subsequent integers as their cookies
|
||||
pub(crate) fn fd_readdir(
|
||||
fd: &File,
|
||||
cookie: wasi::__wasi_dircookie_t,
|
||||
) -> WasiResult<impl Iterator<Item = WasiResult<Dirent>>> {
|
||||
use winx::file::get_file_path;
|
||||
|
||||
let cookie = cookie.try_into()?;
|
||||
let path = get_file_path(fd)?;
|
||||
// std::fs::ReadDir doesn't return . and .., so we need to emulate it
|
||||
let path = Path::new(&path);
|
||||
// The directory /.. is the same as / on Unix (at least on ext4), so emulate this behavior too
|
||||
let parent = path.parent().unwrap_or(path);
|
||||
let dot = dirent_from_path(path, ".", 1)?;
|
||||
let dotdot = dirent_from_path(parent, "..", 2)?;
|
||||
|
||||
trace!(" | fd_readdir impl: executing std::fs::ReadDir");
|
||||
let iter = path.read_dir()?.zip(3..).map(|(dir, no)| {
|
||||
let dir: std::fs::DirEntry = dir?;
|
||||
|
||||
Ok(Dirent {
|
||||
name: path_from_host(dir.file_name())?,
|
||||
ftype: host_impl::filetype_from_std(&dir.file_type()?),
|
||||
ino: File::open(dir.path()).and_then(|f| host_impl::file_serial_no(&f))?,
|
||||
cookie: no,
|
||||
})
|
||||
});
|
||||
|
||||
// into_iter for arrays is broken and returns references instead of values,
|
||||
// so we need to use vec![...] and do heap allocation
|
||||
// See https://github.com/rust-lang/rust/issues/25725
|
||||
let iter = vec![dot, dotdot].into_iter().map(Ok).chain(iter);
|
||||
|
||||
// Emulate seekdir(). This may give O(n^2) complexity if used with a
|
||||
// small host_buf, but this is difficult to implement efficiently.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/issues/61 for more details.
|
||||
Ok(iter.skip(cookie))
|
||||
}
|
||||
|
||||
pub(crate) fn path_readlink(resolved: PathGet, buf: &mut [u8]) -> WasiResult<usize> {
|
||||
use winx::file::get_file_path;
|
||||
|
||||
let path = resolved.concatenate()?;
|
||||
let target_path = path.read_link()?;
|
||||
|
||||
// since on Windows we are effectively emulating 'at' syscalls
|
||||
// we need to strip the prefix from the absolute path
|
||||
// as otherwise we will error out since WASI is not capable
|
||||
// of dealing with absolute paths
|
||||
let dir_path = get_file_path(resolved.dirfd())?;
|
||||
let dir_path = PathBuf::from(strip_extended_prefix(dir_path));
|
||||
let target_path = target_path
|
||||
.strip_prefix(dir_path)
|
||||
.map_err(|_| WasiError::ENOTCAPABLE)
|
||||
.and_then(|path| path.to_str().map(String::from).ok_or(WasiError::EILSEQ))?;
|
||||
|
||||
if buf.len() > 0 {
|
||||
let mut chars = target_path.chars();
|
||||
let mut nread = 0usize;
|
||||
|
||||
for i in 0..buf.len() {
|
||||
match chars.next() {
|
||||
Some(ch) => {
|
||||
buf[i] = ch as u8;
|
||||
nread += 1;
|
||||
}
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
|
||||
Ok(nread)
|
||||
} else {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
fn strip_trailing_slashes_and_concatenate(resolved: &PathGet) -> WasiResult<Option<PathBuf>> {
|
||||
if resolved.path().ends_with('/') {
|
||||
let suffix = resolved.path().trim_end_matches('/');
|
||||
concatenate(resolved.dirfd(), Path::new(suffix)).map(Some)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> WasiResult<()> {
|
||||
use std::fs;
|
||||
|
||||
let old_path = resolved_old.concatenate()?;
|
||||
let new_path = resolved_new.concatenate()?;
|
||||
|
||||
// First sanity check: check we're not trying to rename dir to file or vice versa.
|
||||
// NB on Windows, the former is actually permitted [std::fs::rename].
|
||||
//
|
||||
// [std::fs::rename]: https://doc.rust-lang.org/std/fs/fn.rename.html
|
||||
if old_path.is_dir() && new_path.is_file() {
|
||||
return Err(WasiError::ENOTDIR);
|
||||
}
|
||||
// Second sanity check: check we're not trying to rename a file into a path
|
||||
// ending in a trailing slash.
|
||||
if old_path.is_file() && resolved_new.path().ends_with('/') {
|
||||
return Err(WasiError::ENOTDIR);
|
||||
}
|
||||
|
||||
// TODO handle symlinks
|
||||
let err = match fs::rename(&old_path, &new_path) {
|
||||
Ok(()) => return Ok(()),
|
||||
Err(e) => e,
|
||||
};
|
||||
match err.raw_os_error() {
|
||||
Some(code) => {
|
||||
tracing::debug!("path_rename at rename error code={:?}", code);
|
||||
match code as u32 {
|
||||
winerror::ERROR_ACCESS_DENIED => {
|
||||
// So most likely dealing with new_path == dir.
|
||||
// Eliminate case old_path == file first.
|
||||
if old_path.is_file() {
|
||||
return Err(WasiError::EISDIR);
|
||||
} else {
|
||||
// Ok, let's try removing an empty dir at new_path if it exists
|
||||
// and is a nonempty dir.
|
||||
fs::remove_dir(&new_path)?;
|
||||
fs::rename(old_path, new_path)?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
winerror::ERROR_INVALID_NAME => {
|
||||
// If source contains trailing slashes, check if we are dealing with
|
||||
// a file instead of a dir, and if so, throw ENOTDIR.
|
||||
if let Some(path) = strip_trailing_slashes_and_concatenate(&resolved_old)? {
|
||||
if path.is_file() {
|
||||
return Err(WasiError::ENOTDIR);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Err(err.into())
|
||||
}
|
||||
None => {
|
||||
tracing::debug!("Inconvertible OS error: {}", err);
|
||||
Err(WasiError::EIO)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn fd_filestat_get(file: &std::fs::File) -> WasiResult<wasi::__wasi_filestat_t> {
|
||||
host_impl::filestat_from_win(file)
|
||||
}
|
||||
|
||||
pub(crate) fn path_filestat_get(
|
||||
resolved: PathGet,
|
||||
dirflags: wasi::__wasi_lookupflags_t,
|
||||
) -> WasiResult<wasi::__wasi_filestat_t> {
|
||||
let path = resolved.concatenate()?;
|
||||
let file = File::open(path)?;
|
||||
host_impl::filestat_from_win(&file)
|
||||
}
|
||||
|
||||
pub(crate) fn path_filestat_set_times(
|
||||
resolved: PathGet,
|
||||
dirflags: wasi::__wasi_lookupflags_t,
|
||||
st_atim: wasi::__wasi_timestamp_t,
|
||||
mut st_mtim: wasi::__wasi_timestamp_t,
|
||||
fst_flags: wasi::__wasi_fstflags_t,
|
||||
) -> WasiResult<()> {
|
||||
use winx::file::AccessMode;
|
||||
let path = resolved.concatenate()?;
|
||||
let file = OpenOptions::new()
|
||||
.access_mode(AccessMode::FILE_WRITE_ATTRIBUTES.bits())
|
||||
.open(path)?;
|
||||
fd_filestat_set_times_impl(&file, st_atim, st_mtim, fst_flags)
|
||||
}
|
||||
|
||||
pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> WasiResult<()> {
|
||||
use std::os::windows::fs::{symlink_dir, symlink_file};
|
||||
|
||||
let old_path = concatenate(resolved.dirfd(), Path::new(old_path))?;
|
||||
let new_path = resolved.concatenate()?;
|
||||
|
||||
// try creating a file symlink
|
||||
let err = match symlink_file(&old_path, &new_path) {
|
||||
Ok(()) => return Ok(()),
|
||||
Err(e) => e,
|
||||
};
|
||||
match err.raw_os_error() {
|
||||
Some(code) => {
|
||||
tracing::debug!("path_symlink at symlink_file error code={:?}", code);
|
||||
match code as u32 {
|
||||
winerror::ERROR_NOT_A_REPARSE_POINT => {
|
||||
// try creating a dir symlink instead
|
||||
return symlink_dir(old_path, new_path).map_err(Into::into);
|
||||
}
|
||||
winerror::ERROR_ACCESS_DENIED => {
|
||||
// does the target exist?
|
||||
if new_path.exists() {
|
||||
return Err(WasiError::EEXIST);
|
||||
}
|
||||
}
|
||||
winerror::ERROR_INVALID_NAME => {
|
||||
// does the target without trailing slashes exist?
|
||||
if let Some(path) = strip_trailing_slashes_and_concatenate(&resolved)? {
|
||||
if path.exists() {
|
||||
return Err(WasiError::EEXIST);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Err(err.into())
|
||||
}
|
||||
None => {
|
||||
tracing::debug!("Inconvertible OS error: {}", err);
|
||||
Err(WasiError::EIO)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn path_unlink_file(resolved: PathGet) -> WasiResult<()> {
|
||||
use std::fs;
|
||||
|
||||
let path = resolved.concatenate()?;
|
||||
let file_type = path
|
||||
.symlink_metadata()
|
||||
.map(|metadata| metadata.file_type())?;
|
||||
|
||||
// check if we're unlinking a symlink
|
||||
// NB this will get cleaned up a lot when [std::os::windows::fs::FileTypeExt]
|
||||
// stabilises
|
||||
//
|
||||
// [std::os::windows::fs::FileTypeExt]: https://doc.rust-lang.org/std/os/windows/fs/trait.FileTypeExt.html
|
||||
if file_type.is_symlink() {
|
||||
let err = match fs::remove_file(&path) {
|
||||
Ok(()) => return Ok(()),
|
||||
Err(e) => e,
|
||||
};
|
||||
match err.raw_os_error() {
|
||||
Some(code) => {
|
||||
tracing::debug!("path_unlink_file at symlink_file error code={:?}", code);
|
||||
if code as u32 == winerror::ERROR_ACCESS_DENIED {
|
||||
// try unlinking a dir symlink instead
|
||||
return fs::remove_dir(path).map_err(Into::into);
|
||||
}
|
||||
|
||||
Err(err.into())
|
||||
}
|
||||
None => {
|
||||
tracing::debug!("Inconvertible OS error: {}", err);
|
||||
Err(WasiError::EIO)
|
||||
}
|
||||
}
|
||||
} else if file_type.is_dir() {
|
||||
Err(WasiError::EISDIR)
|
||||
} else if file_type.is_file() {
|
||||
fs::remove_file(path).map_err(Into::into)
|
||||
} else {
|
||||
Err(WasiError::EINVAL)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn path_remove_directory(resolved: PathGet) -> WasiResult<()> {
|
||||
let path = resolved.concatenate()?;
|
||||
std::fs::remove_dir(&path).map_err(Into::into)
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
#![allow(non_camel_case_types)]
|
||||
use crate::old::snapshot_0::hostcalls_impl::PathGet;
|
||||
use crate::old::snapshot_0::wasi::{self, WasiError, WasiResult};
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::fs::File;
|
||||
use std::os::windows::ffi::{OsStrExt, OsStringExt};
|
||||
use std::path::{Path, PathBuf};
|
||||
use winapi::shared::winerror;
|
||||
|
||||
pub(crate) trait PathGetExt {
|
||||
fn concatenate(&self) -> WasiResult<PathBuf>;
|
||||
}
|
||||
|
||||
impl PathGetExt for PathGet {
|
||||
fn concatenate(&self) -> WasiResult<PathBuf> {
|
||||
concatenate(self.dirfd(), Path::new(self.path()))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn path_open_rights(
|
||||
rights_base: wasi::__wasi_rights_t,
|
||||
rights_inheriting: wasi::__wasi_rights_t,
|
||||
oflags: wasi::__wasi_oflags_t,
|
||||
fdflags: wasi::__wasi_fdflags_t,
|
||||
) -> (wasi::__wasi_rights_t, wasi::__wasi_rights_t) {
|
||||
// which rights are needed on the dirfd?
|
||||
let mut needed_base = wasi::__WASI_RIGHTS_PATH_OPEN;
|
||||
let mut needed_inheriting = rights_base | rights_inheriting;
|
||||
|
||||
// convert open flags
|
||||
if oflags & wasi::__WASI_OFLAGS_CREAT != 0 {
|
||||
needed_base |= wasi::__WASI_RIGHTS_PATH_CREATE_FILE;
|
||||
} else if oflags & wasi::__WASI_OFLAGS_TRUNC != 0 {
|
||||
needed_base |= wasi::__WASI_RIGHTS_PATH_FILESTAT_SET_SIZE;
|
||||
}
|
||||
|
||||
// convert file descriptor flags
|
||||
if fdflags & wasi::__WASI_FDFLAGS_DSYNC != 0
|
||||
|| fdflags & wasi::__WASI_FDFLAGS_RSYNC != 0
|
||||
|| fdflags & wasi::__WASI_FDFLAGS_SYNC != 0
|
||||
{
|
||||
needed_inheriting |= wasi::__WASI_RIGHTS_FD_DATASYNC;
|
||||
needed_inheriting |= wasi::__WASI_RIGHTS_FD_SYNC;
|
||||
}
|
||||
|
||||
(needed_base, needed_inheriting)
|
||||
}
|
||||
|
||||
pub(crate) fn openat(dirfd: &File, path: &str) -> WasiResult<File> {
|
||||
use std::fs::OpenOptions;
|
||||
use std::os::windows::fs::OpenOptionsExt;
|
||||
use winx::file::Flags;
|
||||
|
||||
let path = concatenate(dirfd, Path::new(path))?;
|
||||
let err = match OpenOptions::new()
|
||||
.read(true)
|
||||
.custom_flags(Flags::FILE_FLAG_BACKUP_SEMANTICS.bits())
|
||||
.open(&path)
|
||||
{
|
||||
Ok(file) => return Ok(file),
|
||||
Err(e) => e,
|
||||
};
|
||||
if let Some(code) = err.raw_os_error() {
|
||||
tracing::debug!("openat error={:?}", code);
|
||||
if code as u32 == winerror::ERROR_INVALID_NAME {
|
||||
return Err(WasiError::ENOTDIR);
|
||||
}
|
||||
}
|
||||
Err(err.into())
|
||||
}
|
||||
|
||||
pub(crate) fn readlinkat(dirfd: &File, s_path: &str) -> WasiResult<String> {
|
||||
use winx::file::get_file_path;
|
||||
|
||||
let path = concatenate(dirfd, Path::new(s_path))?;
|
||||
let err = match path.read_link() {
|
||||
Ok(target_path) => {
|
||||
// since on Windows we are effectively emulating 'at' syscalls
|
||||
// we need to strip the prefix from the absolute path
|
||||
// as otherwise we will error out since WASI is not capable
|
||||
// of dealing with absolute paths
|
||||
let dir_path = get_file_path(dirfd)?;
|
||||
let dir_path = PathBuf::from(strip_extended_prefix(dir_path));
|
||||
let target_path = target_path
|
||||
.strip_prefix(dir_path)
|
||||
.map_err(|_| WasiError::ENOTCAPABLE)?;
|
||||
let target_path = target_path.to_str().ok_or(WasiError::EILSEQ)?;
|
||||
return Ok(target_path.to_owned());
|
||||
}
|
||||
Err(e) => e,
|
||||
};
|
||||
if let Some(code) = err.raw_os_error() {
|
||||
tracing::debug!("readlinkat error={:?}", code);
|
||||
if code as u32 == winerror::ERROR_INVALID_NAME {
|
||||
if s_path.ends_with('/') {
|
||||
// strip "/" and check if exists
|
||||
let path = concatenate(dirfd, Path::new(s_path.trim_end_matches('/')))?;
|
||||
if path.exists() && !path.is_dir() {
|
||||
return Err(WasiError::ENOTDIR);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err.into())
|
||||
}
|
||||
|
||||
pub(crate) fn strip_extended_prefix<P: AsRef<OsStr>>(path: P) -> OsString {
|
||||
let path: Vec<u16> = path.as_ref().encode_wide().collect();
|
||||
if &[92, 92, 63, 92] == &path[0..4] {
|
||||
OsString::from_wide(&path[4..])
|
||||
} else {
|
||||
OsString::from_wide(&path)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn concatenate<P: AsRef<Path>>(dirfd: &File, path: P) -> WasiResult<PathBuf> {
|
||||
use winx::file::get_file_path;
|
||||
|
||||
// WASI is not able to deal with absolute paths
|
||||
// so error out if absolute
|
||||
if path.as_ref().is_absolute() {
|
||||
return Err(WasiError::ENOTCAPABLE);
|
||||
}
|
||||
|
||||
let dir_path = get_file_path(dirfd)?;
|
||||
// concatenate paths
|
||||
let mut out_path = PathBuf::from(dir_path);
|
||||
out_path.push(path.as_ref());
|
||||
// strip extended prefix; otherwise we will error out on any relative
|
||||
// components with `out_path`
|
||||
let out_path = PathBuf::from(strip_extended_prefix(out_path));
|
||||
|
||||
tracing::debug!("out_path={:?}", out_path);
|
||||
|
||||
Ok(out_path)
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(unused_unsafe)]
|
||||
#![allow(unused)]
|
||||
use crate::old::snapshot_0::hostcalls_impl::{ClockEventData, FdEventData};
|
||||
use crate::old::snapshot_0::memory::*;
|
||||
use crate::old::snapshot_0::sys::host_impl;
|
||||
use crate::old::snapshot_0::wasi::{self, WasiError, WasiResult};
|
||||
use crate::old::snapshot_0::wasi32;
|
||||
use cpu_time::{ProcessTime, ThreadTime};
|
||||
use lazy_static::lazy_static;
|
||||
use std::convert::TryInto;
|
||||
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
|
||||
|
||||
lazy_static! {
|
||||
static ref START_MONOTONIC: Instant = Instant::now();
|
||||
static ref PERF_COUNTER_RES: u64 = get_perf_counter_resolution_ns();
|
||||
}
|
||||
|
||||
// Timer resolution on Windows is really hard. We may consider exposing the resolution of the respective
|
||||
// timers as an associated function in the future.
|
||||
pub(crate) fn clock_res_get(
|
||||
clock_id: wasi::__wasi_clockid_t,
|
||||
) -> WasiResult<wasi::__wasi_timestamp_t> {
|
||||
Ok(match clock_id {
|
||||
// This is the best that we can do with std::time::SystemTime.
|
||||
// Rust uses GetSystemTimeAsFileTime, which is said to have the resolution of
|
||||
// 10ms or 55ms, [1] but MSDN doesn't confirm this in any way.
|
||||
// Even the MSDN article on high resolution timestamps doesn't even mention the precision
|
||||
// for this method. [3]
|
||||
//
|
||||
// The timer resolution can be queried using one of the functions: [2, 5]
|
||||
// * NtQueryTimerResolution, which is undocumented and thus not exposed by the winapi crate
|
||||
// * timeGetDevCaps, which returns the upper and lower bound for the precision, in ms.
|
||||
// While the upper bound seems like something we could use, it's typically too high to be meaningful.
|
||||
// For instance, the intervals return by the syscall are:
|
||||
// * [1, 65536] on Wine
|
||||
// * [1, 1000000] on Windows 10, which is up to (sic) 1000 seconds.
|
||||
//
|
||||
// It's possible to manually set the timer resolution, but this sounds like something which should
|
||||
// only be done temporarily. [5]
|
||||
//
|
||||
// Alternatively, we could possibly use GetSystemTimePreciseAsFileTime in clock_time_get, but
|
||||
// this syscall is only available starting from Windows 8.
|
||||
// (we could possibly emulate it on earlier versions of Windows, see [4])
|
||||
// The MSDN are not clear on the resolution of GetSystemTimePreciseAsFileTime either, but a
|
||||
// Microsoft devblog entry [1] suggests that it kind of combines GetSystemTimeAsFileTime with
|
||||
// QueryPeformanceCounter, which probably means that those two should have the same resolution.
|
||||
//
|
||||
// See also this discussion about the use of GetSystemTimePreciseAsFileTime in Python stdlib,
|
||||
// which in particular contains some resolution benchmarks.
|
||||
//
|
||||
// [1] https://devblogs.microsoft.com/oldnewthing/20170921-00/?p=97057
|
||||
// [2] http://www.windowstimestamp.com/description
|
||||
// [3] https://docs.microsoft.com/en-us/windows/win32/sysinfo/acquiring-high-resolution-time-stamps?redirectedfrom=MSDN
|
||||
// [4] https://www.codeproject.com/Tips/1011902/High-Resolution-Time-For-Windows
|
||||
// [5] https://stackoverflow.com/questions/7685762/windows-7-timing-functions-how-to-use-getsystemtimeadjustment-correctly
|
||||
// [6] https://bugs.python.org/issue19007
|
||||
wasi::__WASI_CLOCKID_REALTIME => 55_000_000,
|
||||
// std::time::Instant uses QueryPerformanceCounter & QueryPerformanceFrequency internally
|
||||
wasi::__WASI_CLOCKID_MONOTONIC => *PERF_COUNTER_RES,
|
||||
// The best we can do is to hardcode the value from the docs.
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getprocesstimes
|
||||
wasi::__WASI_CLOCKID_PROCESS_CPUTIME_ID => 100,
|
||||
// The best we can do is to hardcode the value from the docs.
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getthreadtimes
|
||||
wasi::__WASI_CLOCKID_THREAD_CPUTIME_ID => 100,
|
||||
_ => return Err(WasiError::EINVAL),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn clock_time_get(
|
||||
clock_id: wasi::__wasi_clockid_t,
|
||||
) -> WasiResult<wasi::__wasi_timestamp_t> {
|
||||
let duration = match clock_id {
|
||||
wasi::__WASI_CLOCKID_REALTIME => get_monotonic_time(),
|
||||
wasi::__WASI_CLOCKID_MONOTONIC => get_realtime_time()?,
|
||||
wasi::__WASI_CLOCKID_PROCESS_CPUTIME_ID => get_proc_cputime()?,
|
||||
wasi::__WASI_CLOCKID_THREAD_CPUTIME_ID => get_thread_cputime()?,
|
||||
_ => return Err(WasiError::EINVAL),
|
||||
};
|
||||
duration.as_nanos().try_into().map_err(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) fn poll_oneoff(
|
||||
timeout: Option<ClockEventData>,
|
||||
fd_events: Vec<FdEventData>,
|
||||
events: &mut Vec<wasi::__wasi_event_t>,
|
||||
) -> WasiResult<Vec<wasi::__wasi_event_t>> {
|
||||
unimplemented!("poll_oneoff")
|
||||
}
|
||||
|
||||
fn get_monotonic_time() -> Duration {
|
||||
// We're circumventing the fact that we can't get a Duration from an Instant
|
||||
// The epoch of __WASI_CLOCKID_MONOTONIC is undefined, so we fix a time point once
|
||||
// and count relative to this time point.
|
||||
//
|
||||
// The alternative would be to copy over the implementation of std::time::Instant
|
||||
// to our source tree and add a conversion to std::time::Duration
|
||||
START_MONOTONIC.elapsed()
|
||||
}
|
||||
|
||||
fn get_realtime_time() -> WasiResult<Duration> {
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.map_err(|_| WasiError::EFAULT)
|
||||
}
|
||||
|
||||
fn get_proc_cputime() -> WasiResult<Duration> {
|
||||
Ok(ProcessTime::try_now()?.as_duration())
|
||||
}
|
||||
|
||||
fn get_thread_cputime() -> WasiResult<Duration> {
|
||||
Ok(ThreadTime::try_now()?.as_duration())
|
||||
}
|
||||
|
||||
fn get_perf_counter_resolution_ns() -> u64 {
|
||||
use winx::time::perf_counter_frequency;
|
||||
const NANOS_PER_SEC: u64 = 1_000_000_000;
|
||||
// This should always succeed starting from Windows XP, so it's fine to panic in case of an error.
|
||||
let freq = perf_counter_frequency().expect("QueryPerformanceFrequency returned an error");
|
||||
let epsilon = NANOS_PER_SEC / freq;
|
||||
epsilon
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
//! Windows-specific hostcalls that implement
|
||||
//! [WASI](https://github.com/WebAssembly/WASI).
|
||||
mod fs;
|
||||
pub(crate) mod fs_helpers;
|
||||
mod misc;
|
||||
|
||||
pub(crate) use self::fs::*;
|
||||
pub(crate) use self::misc::*;
|
||||
@@ -1,10 +0,0 @@
|
||||
pub(crate) mod entry_impl;
|
||||
pub(crate) mod host_impl;
|
||||
pub(crate) mod hostcalls_impl;
|
||||
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::Result;
|
||||
|
||||
pub(crate) fn dev_null() -> Result<File> {
|
||||
OpenOptions::new().read(true).write(true).open("NUL")
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
//! Types and constants shared between 32-bit and 64-bit wasi. Types involving
|
||||
//! pointer or `usize`-sized data are excluded here, so this file only contains
|
||||
//! fixed-size types, so it's host/target independent.
|
||||
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
use wig::witx_wasi_types;
|
||||
|
||||
witx_wasi_types!("phases/old/snapshot_0/witx/wasi_unstable.witx");
|
||||
|
||||
pub type WasiResult<T> = Result<T, WasiError>;
|
||||
|
||||
#[derive(Clone, Copy, Debug, thiserror::Error, Eq, PartialEq)]
|
||||
#[repr(u16)]
|
||||
#[error("{:?} ({})", self, strerror(*self as __wasi_errno_t))]
|
||||
pub enum WasiError {
|
||||
ESUCCESS = __WASI_ERRNO_SUCCESS,
|
||||
E2BIG = __WASI_ERRNO_2BIG,
|
||||
EACCES = __WASI_ERRNO_ACCES,
|
||||
EADDRINUSE = __WASI_ERRNO_ADDRINUSE,
|
||||
EADDRNOTAVAIL = __WASI_ERRNO_ADDRNOTAVAIL,
|
||||
EAFNOSUPPORT = __WASI_ERRNO_AFNOSUPPORT,
|
||||
EAGAIN = __WASI_ERRNO_AGAIN,
|
||||
EALREADY = __WASI_ERRNO_ALREADY,
|
||||
EBADF = __WASI_ERRNO_BADF,
|
||||
EBADMSG = __WASI_ERRNO_BADMSG,
|
||||
EBUSY = __WASI_ERRNO_BUSY,
|
||||
ECANCELED = __WASI_ERRNO_CANCELED,
|
||||
ECHILD = __WASI_ERRNO_CHILD,
|
||||
ECONNABORTED = __WASI_ERRNO_CONNABORTED,
|
||||
ECONNREFUSED = __WASI_ERRNO_CONNREFUSED,
|
||||
ECONNRESET = __WASI_ERRNO_CONNRESET,
|
||||
EDEADLK = __WASI_ERRNO_DEADLK,
|
||||
EDESTADDRREQ = __WASI_ERRNO_DESTADDRREQ,
|
||||
EDOM = __WASI_ERRNO_DOM,
|
||||
EDQUOT = __WASI_ERRNO_DQUOT,
|
||||
EEXIST = __WASI_ERRNO_EXIST,
|
||||
EFAULT = __WASI_ERRNO_FAULT,
|
||||
EFBIG = __WASI_ERRNO_FBIG,
|
||||
EHOSTUNREACH = __WASI_ERRNO_HOSTUNREACH,
|
||||
EIDRM = __WASI_ERRNO_IDRM,
|
||||
EILSEQ = __WASI_ERRNO_ILSEQ,
|
||||
EINPROGRESS = __WASI_ERRNO_INPROGRESS,
|
||||
EINTR = __WASI_ERRNO_INTR,
|
||||
EINVAL = __WASI_ERRNO_INVAL,
|
||||
EIO = __WASI_ERRNO_IO,
|
||||
EISCONN = __WASI_ERRNO_ISCONN,
|
||||
EISDIR = __WASI_ERRNO_ISDIR,
|
||||
ELOOP = __WASI_ERRNO_LOOP,
|
||||
EMFILE = __WASI_ERRNO_MFILE,
|
||||
EMLINK = __WASI_ERRNO_MLINK,
|
||||
EMSGSIZE = __WASI_ERRNO_MSGSIZE,
|
||||
EMULTIHOP = __WASI_ERRNO_MULTIHOP,
|
||||
ENAMETOOLONG = __WASI_ERRNO_NAMETOOLONG,
|
||||
ENETDOWN = __WASI_ERRNO_NETDOWN,
|
||||
ENETRESET = __WASI_ERRNO_NETRESET,
|
||||
ENETUNREACH = __WASI_ERRNO_NETUNREACH,
|
||||
ENFILE = __WASI_ERRNO_NFILE,
|
||||
ENOBUFS = __WASI_ERRNO_NOBUFS,
|
||||
ENODEV = __WASI_ERRNO_NODEV,
|
||||
ENOENT = __WASI_ERRNO_NOENT,
|
||||
ENOEXEC = __WASI_ERRNO_NOEXEC,
|
||||
ENOLCK = __WASI_ERRNO_NOLCK,
|
||||
ENOLINK = __WASI_ERRNO_NOLINK,
|
||||
ENOMEM = __WASI_ERRNO_NOMEM,
|
||||
ENOMSG = __WASI_ERRNO_NOMSG,
|
||||
ENOPROTOOPT = __WASI_ERRNO_NOPROTOOPT,
|
||||
ENOSPC = __WASI_ERRNO_NOSPC,
|
||||
ENOSYS = __WASI_ERRNO_NOSYS,
|
||||
ENOTCONN = __WASI_ERRNO_NOTCONN,
|
||||
ENOTDIR = __WASI_ERRNO_NOTDIR,
|
||||
ENOTEMPTY = __WASI_ERRNO_NOTEMPTY,
|
||||
ENOTRECOVERABLE = __WASI_ERRNO_NOTRECOVERABLE,
|
||||
ENOTSOCK = __WASI_ERRNO_NOTSOCK,
|
||||
ENOTSUP = __WASI_ERRNO_NOTSUP,
|
||||
ENOTTY = __WASI_ERRNO_NOTTY,
|
||||
ENXIO = __WASI_ERRNO_NXIO,
|
||||
EOVERFLOW = __WASI_ERRNO_OVERFLOW,
|
||||
EOWNERDEAD = __WASI_ERRNO_OWNERDEAD,
|
||||
EPERM = __WASI_ERRNO_PERM,
|
||||
EPIPE = __WASI_ERRNO_PIPE,
|
||||
EPROTO = __WASI_ERRNO_PROTO,
|
||||
EPROTONOSUPPORT = __WASI_ERRNO_PROTONOSUPPORT,
|
||||
EPROTOTYPE = __WASI_ERRNO_PROTOTYPE,
|
||||
ERANGE = __WASI_ERRNO_RANGE,
|
||||
EROFS = __WASI_ERRNO_ROFS,
|
||||
ESPIPE = __WASI_ERRNO_SPIPE,
|
||||
ESRCH = __WASI_ERRNO_SRCH,
|
||||
ESTALE = __WASI_ERRNO_STALE,
|
||||
ETIMEDOUT = __WASI_ERRNO_TIMEDOUT,
|
||||
ETXTBSY = __WASI_ERRNO_TXTBSY,
|
||||
EXDEV = __WASI_ERRNO_XDEV,
|
||||
ENOTCAPABLE = __WASI_ERRNO_NOTCAPABLE,
|
||||
}
|
||||
|
||||
impl WasiError {
|
||||
pub fn as_raw_errno(self) -> __wasi_errno_t {
|
||||
self as __wasi_errno_t
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::convert::Infallible> for WasiError {
|
||||
fn from(_err: std::convert::Infallible) -> Self {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::num::TryFromIntError> for WasiError {
|
||||
fn from(_err: std::num::TryFromIntError) -> Self {
|
||||
Self::EOVERFLOW
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::str::Utf8Error> for WasiError {
|
||||
fn from(_err: std::str::Utf8Error) -> Self {
|
||||
Self::EILSEQ
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) const RIGHTS_ALL: __wasi_rights_t = __WASI_RIGHTS_FD_DATASYNC
|
||||
| __WASI_RIGHTS_FD_READ
|
||||
| __WASI_RIGHTS_FD_SEEK
|
||||
| __WASI_RIGHTS_FD_FDSTAT_SET_FLAGS
|
||||
| __WASI_RIGHTS_FD_SYNC
|
||||
| __WASI_RIGHTS_FD_TELL
|
||||
| __WASI_RIGHTS_FD_WRITE
|
||||
| __WASI_RIGHTS_FD_ADVISE
|
||||
| __WASI_RIGHTS_FD_ALLOCATE
|
||||
| __WASI_RIGHTS_PATH_CREATE_DIRECTORY
|
||||
| __WASI_RIGHTS_PATH_CREATE_FILE
|
||||
| __WASI_RIGHTS_PATH_LINK_SOURCE
|
||||
| __WASI_RIGHTS_PATH_LINK_TARGET
|
||||
| __WASI_RIGHTS_PATH_OPEN
|
||||
| __WASI_RIGHTS_FD_READDIR
|
||||
| __WASI_RIGHTS_PATH_READLINK
|
||||
| __WASI_RIGHTS_PATH_RENAME_SOURCE
|
||||
| __WASI_RIGHTS_PATH_RENAME_TARGET
|
||||
| __WASI_RIGHTS_PATH_FILESTAT_GET
|
||||
| __WASI_RIGHTS_PATH_FILESTAT_SET_SIZE
|
||||
| __WASI_RIGHTS_PATH_FILESTAT_SET_TIMES
|
||||
| __WASI_RIGHTS_FD_FILESTAT_GET
|
||||
| __WASI_RIGHTS_FD_FILESTAT_SET_SIZE
|
||||
| __WASI_RIGHTS_FD_FILESTAT_SET_TIMES
|
||||
| __WASI_RIGHTS_PATH_SYMLINK
|
||||
| __WASI_RIGHTS_PATH_UNLINK_FILE
|
||||
| __WASI_RIGHTS_PATH_REMOVE_DIRECTORY
|
||||
| __WASI_RIGHTS_POLL_FD_READWRITE
|
||||
| __WASI_RIGHTS_SOCK_SHUTDOWN;
|
||||
|
||||
// Block and character device interaction is outside the scope of
|
||||
// WASI. Simply allow everything.
|
||||
pub(crate) const RIGHTS_BLOCK_DEVICE_BASE: __wasi_rights_t = RIGHTS_ALL;
|
||||
pub(crate) const RIGHTS_BLOCK_DEVICE_INHERITING: __wasi_rights_t = RIGHTS_ALL;
|
||||
pub(crate) const RIGHTS_CHARACTER_DEVICE_BASE: __wasi_rights_t = RIGHTS_ALL;
|
||||
pub(crate) const RIGHTS_CHARACTER_DEVICE_INHERITING: __wasi_rights_t = RIGHTS_ALL;
|
||||
|
||||
// Only allow directory operations on directories. Directories can only
|
||||
// yield file descriptors to other directories and files.
|
||||
pub(crate) const RIGHTS_DIRECTORY_BASE: __wasi_rights_t = __WASI_RIGHTS_FD_FDSTAT_SET_FLAGS
|
||||
| __WASI_RIGHTS_FD_SYNC
|
||||
| __WASI_RIGHTS_FD_ADVISE
|
||||
| __WASI_RIGHTS_PATH_CREATE_DIRECTORY
|
||||
| __WASI_RIGHTS_PATH_CREATE_FILE
|
||||
| __WASI_RIGHTS_PATH_LINK_SOURCE
|
||||
| __WASI_RIGHTS_PATH_LINK_TARGET
|
||||
| __WASI_RIGHTS_PATH_OPEN
|
||||
| __WASI_RIGHTS_FD_READDIR
|
||||
| __WASI_RIGHTS_PATH_READLINK
|
||||
| __WASI_RIGHTS_PATH_RENAME_SOURCE
|
||||
| __WASI_RIGHTS_PATH_RENAME_TARGET
|
||||
| __WASI_RIGHTS_PATH_FILESTAT_GET
|
||||
| __WASI_RIGHTS_PATH_FILESTAT_SET_SIZE
|
||||
| __WASI_RIGHTS_PATH_FILESTAT_SET_TIMES
|
||||
| __WASI_RIGHTS_FD_FILESTAT_GET
|
||||
| __WASI_RIGHTS_FD_FILESTAT_SET_TIMES
|
||||
| __WASI_RIGHTS_PATH_SYMLINK
|
||||
| __WASI_RIGHTS_PATH_UNLINK_FILE
|
||||
| __WASI_RIGHTS_PATH_REMOVE_DIRECTORY
|
||||
| __WASI_RIGHTS_POLL_FD_READWRITE;
|
||||
pub(crate) const RIGHTS_DIRECTORY_INHERITING: __wasi_rights_t =
|
||||
RIGHTS_DIRECTORY_BASE | RIGHTS_REGULAR_FILE_BASE;
|
||||
|
||||
// Operations that apply to regular files.
|
||||
pub(crate) const RIGHTS_REGULAR_FILE_BASE: __wasi_rights_t = __WASI_RIGHTS_FD_DATASYNC
|
||||
| __WASI_RIGHTS_FD_READ
|
||||
| __WASI_RIGHTS_FD_SEEK
|
||||
| __WASI_RIGHTS_FD_FDSTAT_SET_FLAGS
|
||||
| __WASI_RIGHTS_FD_SYNC
|
||||
| __WASI_RIGHTS_FD_TELL
|
||||
| __WASI_RIGHTS_FD_WRITE
|
||||
| __WASI_RIGHTS_FD_ADVISE
|
||||
| __WASI_RIGHTS_FD_ALLOCATE
|
||||
| __WASI_RIGHTS_FD_FILESTAT_GET
|
||||
| __WASI_RIGHTS_FD_FILESTAT_SET_SIZE
|
||||
| __WASI_RIGHTS_FD_FILESTAT_SET_TIMES
|
||||
| __WASI_RIGHTS_POLL_FD_READWRITE;
|
||||
pub(crate) const RIGHTS_REGULAR_FILE_INHERITING: __wasi_rights_t = 0;
|
||||
|
||||
// Operations that apply to sockets and socket pairs.
|
||||
pub(crate) const RIGHTS_SOCKET_BASE: __wasi_rights_t = __WASI_RIGHTS_FD_READ
|
||||
| __WASI_RIGHTS_FD_FDSTAT_SET_FLAGS
|
||||
| __WASI_RIGHTS_FD_WRITE
|
||||
| __WASI_RIGHTS_FD_FILESTAT_GET
|
||||
| __WASI_RIGHTS_POLL_FD_READWRITE
|
||||
| __WASI_RIGHTS_SOCK_SHUTDOWN;
|
||||
pub(crate) const RIGHTS_SOCKET_INHERITING: __wasi_rights_t = RIGHTS_ALL;
|
||||
|
||||
// Operations that apply to TTYs.
|
||||
pub(crate) const RIGHTS_TTY_BASE: __wasi_rights_t = __WASI_RIGHTS_FD_READ
|
||||
| __WASI_RIGHTS_FD_FDSTAT_SET_FLAGS
|
||||
| __WASI_RIGHTS_FD_WRITE
|
||||
| __WASI_RIGHTS_FD_FILESTAT_GET
|
||||
| __WASI_RIGHTS_POLL_FD_READWRITE;
|
||||
#[allow(unused)]
|
||||
pub(crate) const RIGHTS_TTY_INHERITING: __wasi_rights_t = 0;
|
||||
|
||||
pub fn whence_to_str(whence: __wasi_whence_t) -> &'static str {
|
||||
match whence {
|
||||
__WASI_WHENCE_CUR => "__WASI_WHENCE_CUR",
|
||||
__WASI_WHENCE_END => "__WASI_WHENCE_END",
|
||||
__WASI_WHENCE_SET => "__WASI_WHENCE_SET",
|
||||
other => panic!("Undefined whence value {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
pub const __WASI_DIRCOOKIE_START: __wasi_dircookie_t = 0;
|
||||
|
||||
impl crate::fdpool::Fd for __wasi_fd_t {
|
||||
fn as_raw(&self) -> u32 {
|
||||
*self
|
||||
}
|
||||
fn from_raw(raw_fd: u32) -> Self {
|
||||
raw_fd
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
//! Types and constants specific to 32-bit wasi. These are similar to the types
|
||||
//! in the `host` module, but pointers and `usize` values are replaced with
|
||||
//! `u32`-sized types.
|
||||
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
use crate::old::snapshot_0::wasi::*;
|
||||
use wig::witx_wasi32_types;
|
||||
|
||||
pub type uintptr_t = u32;
|
||||
pub type size_t = u32;
|
||||
|
||||
witx_wasi32_types!("phases/old/snapshot_0/witx/wasi_unstable.witx");
|
||||
@@ -1 +1,2 @@
|
||||
pub mod wasi_snapshot_preview1;
|
||||
pub mod wasi_unstable;
|
||||
|
||||
@@ -680,6 +680,69 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
|
||||
subscriptions.push(sub);
|
||||
}
|
||||
|
||||
let events = self.poll_oneoff_impl(&subscriptions)?;
|
||||
let nevents = events.len().try_into()?;
|
||||
|
||||
let out_events = out.as_array(nevents);
|
||||
for (event, event_ptr) in events.into_iter().zip(out_events.iter()) {
|
||||
let event_ptr = event_ptr?;
|
||||
event_ptr.write(event)?;
|
||||
}
|
||||
|
||||
trace!(nevents = nevents);
|
||||
|
||||
Ok(nevents)
|
||||
}
|
||||
|
||||
fn proc_exit(&self, _rval: types::Exitcode) -> std::result::Result<(), ()> {
|
||||
// proc_exit is special in that it's expected to unwind the stack, which
|
||||
// typically requires runtime-specific logic.
|
||||
unimplemented!("runtimes are expected to override this implementation")
|
||||
}
|
||||
|
||||
fn proc_raise(&self, _sig: types::Signal) -> Result<()> {
|
||||
unimplemented!("proc_raise")
|
||||
}
|
||||
|
||||
fn sched_yield(&self) -> Result<()> {
|
||||
std::thread::yield_now();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn random_get(&self, buf: &GuestPtr<u8>, buf_len: types::Size) -> Result<()> {
|
||||
let mut slice = buf.as_array(buf_len).as_slice_mut()?;
|
||||
getrandom::getrandom(&mut *slice)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn sock_recv(
|
||||
&self,
|
||||
_fd: types::Fd,
|
||||
_ri_data: &types::IovecArray<'_>,
|
||||
_ri_flags: types::Riflags,
|
||||
) -> Result<(types::Size, types::Roflags)> {
|
||||
unimplemented!("sock_recv")
|
||||
}
|
||||
|
||||
fn sock_send(
|
||||
&self,
|
||||
_fd: types::Fd,
|
||||
_si_data: &types::CiovecArray<'_>,
|
||||
_si_flags: types::Siflags,
|
||||
) -> Result<types::Size> {
|
||||
unimplemented!("sock_send")
|
||||
}
|
||||
|
||||
fn sock_shutdown(&self, _fd: types::Fd, _how: types::Sdflags) -> Result<()> {
|
||||
unimplemented!("sock_shutdown")
|
||||
}
|
||||
}
|
||||
|
||||
impl WasiCtx {
|
||||
pub(crate) fn poll_oneoff_impl(
|
||||
&self,
|
||||
subscriptions: &[types::Subscription],
|
||||
) -> Result<Vec<types::Event>> {
|
||||
let mut events = Vec::new();
|
||||
let mut timeout: Option<sched::ClockEventData> = None;
|
||||
let mut fd_events = Vec::new();
|
||||
@@ -691,7 +754,7 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
|
||||
}
|
||||
|
||||
for subscription in subscriptions {
|
||||
match subscription.u {
|
||||
match &subscription.u {
|
||||
types::SubscriptionU::Clock(clock) => {
|
||||
let delay = clock::to_relative_ns_delay(&clock)?;
|
||||
debug!(
|
||||
@@ -771,59 +834,6 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
|
||||
// if no events have been passed. Such situation may occur if all provided
|
||||
// events have been filtered out as errors in the code above.
|
||||
poll::oneoff(timeout, fd_events, &mut events)?;
|
||||
let nevents = events.len().try_into()?;
|
||||
|
||||
let out_events = out.as_array(nevents);
|
||||
for (event, event_ptr) in events.into_iter().zip(out_events.iter()) {
|
||||
let event_ptr = event_ptr?;
|
||||
event_ptr.write(event)?;
|
||||
}
|
||||
|
||||
trace!(nevents = nevents);
|
||||
|
||||
Ok(nevents)
|
||||
}
|
||||
|
||||
fn proc_exit(&self, _rval: types::Exitcode) -> std::result::Result<(), ()> {
|
||||
// proc_exit is special in that it's expected to unwind the stack, which
|
||||
// typically requires runtime-specific logic.
|
||||
unimplemented!("runtimes are expected to override this implementation")
|
||||
}
|
||||
|
||||
fn proc_raise(&self, _sig: types::Signal) -> Result<()> {
|
||||
unimplemented!("proc_raise")
|
||||
}
|
||||
|
||||
fn sched_yield(&self) -> Result<()> {
|
||||
std::thread::yield_now();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn random_get(&self, buf: &GuestPtr<u8>, buf_len: types::Size) -> Result<()> {
|
||||
let mut slice = buf.as_array(buf_len).as_slice_mut()?;
|
||||
getrandom::getrandom(&mut *slice)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn sock_recv(
|
||||
&self,
|
||||
_fd: types::Fd,
|
||||
_ri_data: &types::IovecArray<'_>,
|
||||
_ri_flags: types::Riflags,
|
||||
) -> Result<(types::Size, types::Roflags)> {
|
||||
unimplemented!("sock_recv")
|
||||
}
|
||||
|
||||
fn sock_send(
|
||||
&self,
|
||||
_fd: types::Fd,
|
||||
_si_data: &types::CiovecArray<'_>,
|
||||
_si_flags: types::Siflags,
|
||||
) -> Result<types::Size> {
|
||||
unimplemented!("sock_send")
|
||||
}
|
||||
|
||||
fn sock_shutdown(&self, _fd: types::Fd, _how: types::Sdflags) -> Result<()> {
|
||||
unimplemented!("sock_shutdown")
|
||||
Ok(events)
|
||||
}
|
||||
}
|
||||
|
||||
938
crates/wasi-common/src/snapshots/wasi_unstable.rs
Normal file
938
crates/wasi-common/src/snapshots/wasi_unstable.rs
Normal file
@@ -0,0 +1,938 @@
|
||||
use crate::wasi::{types as types_new, wasi_snapshot_preview1::WasiSnapshotPreview1};
|
||||
use crate::{Error, WasiCtx};
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use types::*;
|
||||
|
||||
wiggle::from_witx!({
|
||||
witx: ["$WASI_ROOT/phases/old/snapshot_0/witx/wasi_unstable.witx"],
|
||||
ctx: WasiCtx,
|
||||
errors: { errno => Error },
|
||||
});
|
||||
|
||||
impl wiggle::GuestErrorType for Errno {
|
||||
fn success() -> Self {
|
||||
Self::Success
|
||||
}
|
||||
}
|
||||
|
||||
impl types::GuestErrorConversion for WasiCtx {
|
||||
fn into_errno(&self, e: wiggle::GuestError) -> Errno {
|
||||
tracing::debug!("Guest error: {:?}", e);
|
||||
e.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl types::UserErrorConversion for WasiCtx {
|
||||
fn errno_from_error(&self, e: Error) -> Result<Errno, String> {
|
||||
tracing::debug!("Error: {:?}", e);
|
||||
Ok(e.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for Errno {
|
||||
fn from(e: Error) -> Errno {
|
||||
types_new::Errno::from(e).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<wiggle::GuestError> for Errno {
|
||||
fn from(err: wiggle::GuestError) -> Self {
|
||||
types_new::Errno::from(err).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl wasi_unstable::WasiUnstable for WasiCtx {
|
||||
fn args_get<'a>(
|
||||
&self,
|
||||
argv: &wiggle::GuestPtr<'a, wiggle::GuestPtr<'a, u8>>,
|
||||
argv_buf: &wiggle::GuestPtr<'a, u8>,
|
||||
) -> Result<(), Error> {
|
||||
WasiSnapshotPreview1::args_get(self, argv, argv_buf)
|
||||
}
|
||||
|
||||
fn args_sizes_get(&self) -> Result<(Size, Size), Error> {
|
||||
WasiSnapshotPreview1::args_sizes_get(self)
|
||||
}
|
||||
|
||||
fn environ_get<'a>(
|
||||
&self,
|
||||
environ: &wiggle::GuestPtr<'a, wiggle::GuestPtr<'a, u8>>,
|
||||
environ_buf: &wiggle::GuestPtr<'a, u8>,
|
||||
) -> Result<(), Error> {
|
||||
WasiSnapshotPreview1::environ_get(self, environ, environ_buf)
|
||||
}
|
||||
|
||||
fn environ_sizes_get(&self) -> Result<(Size, Size), Error> {
|
||||
WasiSnapshotPreview1::environ_sizes_get(self)
|
||||
}
|
||||
|
||||
fn clock_res_get(&self, id: Clockid) -> Result<Timestamp, Error> {
|
||||
WasiSnapshotPreview1::clock_res_get(self, id.into())
|
||||
}
|
||||
|
||||
fn clock_time_get(&self, id: Clockid, precision: Timestamp) -> Result<Timestamp, Error> {
|
||||
WasiSnapshotPreview1::clock_time_get(self, id.into(), precision)
|
||||
}
|
||||
|
||||
fn fd_advise(
|
||||
&self,
|
||||
fd: Fd,
|
||||
offset: Filesize,
|
||||
len: Filesize,
|
||||
advice: Advice,
|
||||
) -> Result<(), Error> {
|
||||
WasiSnapshotPreview1::fd_advise(self, fd.into(), offset, len, advice.into())
|
||||
}
|
||||
|
||||
fn fd_allocate(&self, fd: Fd, offset: Filesize, len: Filesize) -> Result<(), Error> {
|
||||
WasiSnapshotPreview1::fd_allocate(self, fd.into(), offset, len)
|
||||
}
|
||||
|
||||
fn fd_close(&self, fd: Fd) -> Result<(), Error> {
|
||||
WasiSnapshotPreview1::fd_close(self, fd.into())
|
||||
}
|
||||
|
||||
fn fd_datasync(&self, fd: Fd) -> Result<(), Error> {
|
||||
WasiSnapshotPreview1::fd_datasync(self, fd.into())
|
||||
}
|
||||
|
||||
fn fd_fdstat_get(&self, fd: Fd) -> Result<Fdstat, Error> {
|
||||
WasiSnapshotPreview1::fd_fdstat_get(self, fd.into()).map(|s| s.into())
|
||||
}
|
||||
|
||||
fn fd_fdstat_set_flags(&self, fd: Fd, flags: Fdflags) -> Result<(), Error> {
|
||||
WasiSnapshotPreview1::fd_fdstat_set_flags(self, fd.into(), flags.into())
|
||||
}
|
||||
|
||||
fn fd_fdstat_set_rights(
|
||||
&self,
|
||||
fd: Fd,
|
||||
fs_rights_base: Rights,
|
||||
fs_rights_inheriting: Rights,
|
||||
) -> Result<(), Error> {
|
||||
WasiSnapshotPreview1::fd_fdstat_set_rights(
|
||||
self,
|
||||
fd.into(),
|
||||
fs_rights_base.into(),
|
||||
fs_rights_inheriting.into(),
|
||||
)
|
||||
}
|
||||
|
||||
fn fd_filestat_get(&self, fd: Fd) -> Result<Filestat, Error> {
|
||||
WasiSnapshotPreview1::fd_filestat_get(self, fd.into()).and_then(|e| e.try_into())
|
||||
}
|
||||
|
||||
fn fd_filestat_set_size(&self, fd: Fd, size: Filesize) -> Result<(), Error> {
|
||||
WasiSnapshotPreview1::fd_filestat_set_size(self, fd.into(), size)
|
||||
}
|
||||
|
||||
fn fd_filestat_set_times(
|
||||
&self,
|
||||
fd: Fd,
|
||||
atim: Timestamp,
|
||||
mtim: Timestamp,
|
||||
fst_flags: Fstflags,
|
||||
) -> Result<(), Error> {
|
||||
WasiSnapshotPreview1::fd_filestat_set_times(self, fd.into(), atim, mtim, fst_flags.into())
|
||||
}
|
||||
|
||||
fn fd_pread<'a>(&self, fd: Fd, iovs: &IovecArray<'a>, offset: Filesize) -> Result<Size, Error> {
|
||||
WasiSnapshotPreview1::fd_pread(self, fd.into(), &cvt_iovec(iovs), offset)
|
||||
}
|
||||
|
||||
fn fd_prestat_get(&self, fd: Fd) -> Result<Prestat, Error> {
|
||||
WasiSnapshotPreview1::fd_prestat_get(self, fd.into()).map(|e| e.into())
|
||||
}
|
||||
|
||||
fn fd_prestat_dir_name<'a>(
|
||||
&self,
|
||||
fd: Fd,
|
||||
path: &wiggle::GuestPtr<'a, u8>,
|
||||
path_len: Size,
|
||||
) -> Result<(), Error> {
|
||||
WasiSnapshotPreview1::fd_prestat_dir_name(self, fd.into(), path, path_len)
|
||||
}
|
||||
|
||||
fn fd_pwrite<'a>(
|
||||
&self,
|
||||
fd: Fd,
|
||||
iovs: &CiovecArray<'a>,
|
||||
offset: Filesize,
|
||||
) -> Result<Size, Error> {
|
||||
WasiSnapshotPreview1::fd_pwrite(self, fd.into(), &cvt_ciovec(iovs), offset)
|
||||
}
|
||||
|
||||
fn fd_read<'a>(&self, fd: Fd, iovs: &IovecArray<'a>) -> Result<Size, Error> {
|
||||
WasiSnapshotPreview1::fd_read(self, fd.into(), &cvt_iovec(iovs))
|
||||
}
|
||||
|
||||
fn fd_readdir<'a>(
|
||||
&self,
|
||||
fd: Fd,
|
||||
buf: &wiggle::GuestPtr<'a, u8>,
|
||||
buf_len: Size,
|
||||
cookie: Dircookie,
|
||||
) -> Result<Size, Error> {
|
||||
WasiSnapshotPreview1::fd_readdir(self, fd.into(), buf, buf_len, cookie)
|
||||
}
|
||||
|
||||
fn fd_renumber(&self, from: Fd, to: Fd) -> Result<(), Error> {
|
||||
WasiSnapshotPreview1::fd_renumber(self, from.into(), to.into())
|
||||
}
|
||||
|
||||
fn fd_seek(&self, fd: Fd, offset: Filedelta, whence: Whence) -> Result<Filesize, Error> {
|
||||
WasiSnapshotPreview1::fd_seek(self, fd.into(), offset, whence.into())
|
||||
}
|
||||
|
||||
fn fd_sync(&self, fd: Fd) -> Result<(), Error> {
|
||||
WasiSnapshotPreview1::fd_sync(self, fd.into())
|
||||
}
|
||||
|
||||
fn fd_tell(&self, fd: Fd) -> Result<Filesize, Error> {
|
||||
WasiSnapshotPreview1::fd_tell(self, fd.into())
|
||||
}
|
||||
|
||||
fn fd_write<'a>(&self, fd: Fd, iovs: &CiovecArray<'a>) -> Result<Size, Error> {
|
||||
WasiSnapshotPreview1::fd_write(self, fd.into(), &cvt_ciovec(iovs))
|
||||
}
|
||||
|
||||
fn path_create_directory<'a>(
|
||||
&self,
|
||||
fd: Fd,
|
||||
path: &wiggle::GuestPtr<'a, str>,
|
||||
) -> Result<(), Error> {
|
||||
WasiSnapshotPreview1::path_create_directory(self, fd.into(), path)
|
||||
}
|
||||
|
||||
fn path_filestat_get<'a>(
|
||||
&self,
|
||||
fd: Fd,
|
||||
flags: Lookupflags,
|
||||
path: &wiggle::GuestPtr<'a, str>,
|
||||
) -> Result<Filestat, Error> {
|
||||
WasiSnapshotPreview1::path_filestat_get(self, fd.into(), flags.into(), path)
|
||||
.and_then(|e| e.try_into())
|
||||
}
|
||||
|
||||
fn path_filestat_set_times<'a>(
|
||||
&self,
|
||||
fd: Fd,
|
||||
flags: Lookupflags,
|
||||
path: &wiggle::GuestPtr<'a, str>,
|
||||
atim: Timestamp,
|
||||
mtim: Timestamp,
|
||||
fst_flags: Fstflags,
|
||||
) -> Result<(), Error> {
|
||||
WasiSnapshotPreview1::path_filestat_set_times(
|
||||
self,
|
||||
fd.into(),
|
||||
flags.into(),
|
||||
path,
|
||||
atim,
|
||||
mtim,
|
||||
fst_flags.into(),
|
||||
)
|
||||
}
|
||||
|
||||
fn path_link<'a>(
|
||||
&self,
|
||||
old_fd: Fd,
|
||||
old_flags: Lookupflags,
|
||||
old_path: &wiggle::GuestPtr<'a, str>,
|
||||
new_fd: Fd,
|
||||
new_path: &wiggle::GuestPtr<'a, str>,
|
||||
) -> Result<(), Error> {
|
||||
WasiSnapshotPreview1::path_link(
|
||||
self,
|
||||
old_fd.into(),
|
||||
old_flags.into(),
|
||||
old_path,
|
||||
new_fd.into(),
|
||||
new_path,
|
||||
)
|
||||
}
|
||||
|
||||
fn path_open<'a>(
|
||||
&self,
|
||||
fd: Fd,
|
||||
dirflags: Lookupflags,
|
||||
path: &wiggle::GuestPtr<'a, str>,
|
||||
oflags: Oflags,
|
||||
fs_rights_base: Rights,
|
||||
fs_rights_inheriting: Rights,
|
||||
fdflags: Fdflags,
|
||||
) -> Result<Fd, Error> {
|
||||
WasiSnapshotPreview1::path_open(
|
||||
self,
|
||||
fd.into(),
|
||||
dirflags.into(),
|
||||
path,
|
||||
oflags.into(),
|
||||
fs_rights_base.into(),
|
||||
fs_rights_inheriting.into(),
|
||||
fdflags.into(),
|
||||
)
|
||||
.map(|e| e.into())
|
||||
}
|
||||
|
||||
fn path_readlink<'a>(
|
||||
&self,
|
||||
fd: Fd,
|
||||
path: &wiggle::GuestPtr<'a, str>,
|
||||
buf: &wiggle::GuestPtr<'a, u8>,
|
||||
buf_len: Size,
|
||||
) -> Result<Size, Error> {
|
||||
WasiSnapshotPreview1::path_readlink(self, fd.into(), path, buf, buf_len)
|
||||
}
|
||||
|
||||
fn path_remove_directory<'a>(
|
||||
&self,
|
||||
fd: Fd,
|
||||
path: &wiggle::GuestPtr<'a, str>,
|
||||
) -> Result<(), Error> {
|
||||
WasiSnapshotPreview1::path_remove_directory(self, fd.into(), path)
|
||||
}
|
||||
|
||||
fn path_rename<'a>(
|
||||
&self,
|
||||
fd: Fd,
|
||||
old_path: &wiggle::GuestPtr<'a, str>,
|
||||
new_fd: Fd,
|
||||
new_path: &wiggle::GuestPtr<'a, str>,
|
||||
) -> Result<(), Error> {
|
||||
WasiSnapshotPreview1::path_rename(self, fd.into(), old_path, new_fd.into(), new_path)
|
||||
}
|
||||
|
||||
fn path_symlink<'a>(
|
||||
&self,
|
||||
old_path: &wiggle::GuestPtr<'a, str>,
|
||||
fd: Fd,
|
||||
new_path: &wiggle::GuestPtr<'a, str>,
|
||||
) -> Result<(), Error> {
|
||||
WasiSnapshotPreview1::path_symlink(self, old_path, fd.into(), new_path)
|
||||
}
|
||||
|
||||
fn path_unlink_file<'a>(&self, fd: Fd, path: &wiggle::GuestPtr<'a, str>) -> Result<(), Error> {
|
||||
WasiSnapshotPreview1::path_unlink_file(self, fd.into(), path)
|
||||
}
|
||||
|
||||
fn poll_oneoff<'a>(
|
||||
&self,
|
||||
in_: &wiggle::GuestPtr<'a, Subscription>,
|
||||
out: &wiggle::GuestPtr<'a, Event>,
|
||||
nsubscriptions: Size,
|
||||
) -> Result<Size, Error> {
|
||||
if u64::from(nsubscriptions) > types::Filesize::max_value() {
|
||||
return Err(Error::Inval);
|
||||
}
|
||||
|
||||
let mut subscriptions = Vec::new();
|
||||
let subs = in_.as_array(nsubscriptions);
|
||||
for sub_ptr in subs.iter() {
|
||||
let sub_ptr = sub_ptr?;
|
||||
let sub: types::Subscription = sub_ptr.read()?;
|
||||
subscriptions.push(sub.into());
|
||||
}
|
||||
|
||||
let events = self.poll_oneoff_impl(&subscriptions)?;
|
||||
let nevents = events.len().try_into()?;
|
||||
|
||||
let out_events = out.as_array(nevents);
|
||||
for (event, event_ptr) in events.into_iter().zip(out_events.iter()) {
|
||||
let event_ptr = event_ptr?;
|
||||
event_ptr.write(event.into())?;
|
||||
}
|
||||
|
||||
Ok(nevents)
|
||||
}
|
||||
|
||||
fn proc_exit(&self, rval: Exitcode) -> Result<(), ()> {
|
||||
WasiSnapshotPreview1::proc_exit(self, rval)
|
||||
}
|
||||
|
||||
fn proc_raise(&self, sig: Signal) -> Result<(), Error> {
|
||||
WasiSnapshotPreview1::proc_raise(self, sig.into())
|
||||
}
|
||||
|
||||
fn sched_yield(&self) -> Result<(), Error> {
|
||||
WasiSnapshotPreview1::sched_yield(self)
|
||||
}
|
||||
|
||||
fn random_get<'a>(&self, buf: &wiggle::GuestPtr<'a, u8>, buf_len: Size) -> Result<(), Error> {
|
||||
WasiSnapshotPreview1::random_get(self, buf, buf_len)
|
||||
}
|
||||
|
||||
fn sock_recv<'a>(
|
||||
&self,
|
||||
fd: Fd,
|
||||
ri_data: &IovecArray<'a>,
|
||||
ri_flags: Riflags,
|
||||
) -> Result<(Size, Roflags), Error> {
|
||||
WasiSnapshotPreview1::sock_recv(self, fd.into(), &cvt_iovec(ri_data), ri_flags.into())
|
||||
.map(|(s, f)| (s, f.into()))
|
||||
}
|
||||
|
||||
fn sock_send<'a>(
|
||||
&self,
|
||||
fd: Fd,
|
||||
si_data: &CiovecArray<'a>,
|
||||
si_flags: Siflags,
|
||||
) -> Result<Size, Error> {
|
||||
WasiSnapshotPreview1::sock_send(self, fd.into(), &cvt_ciovec(si_data), si_flags.into())
|
||||
}
|
||||
|
||||
fn sock_shutdown(&self, fd: Fd, how: Sdflags) -> Result<(), Error> {
|
||||
WasiSnapshotPreview1::sock_shutdown(self, fd.into(), how.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Clockid> for types_new::Clockid {
|
||||
fn from(id: Clockid) -> types_new::Clockid {
|
||||
match id {
|
||||
Clockid::Realtime => types_new::Clockid::Realtime,
|
||||
Clockid::Monotonic => types_new::Clockid::Monotonic,
|
||||
Clockid::ProcessCputimeId => types_new::Clockid::ProcessCputimeId,
|
||||
Clockid::ThreadCputimeId => types_new::Clockid::ThreadCputimeId,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Fd> for types_new::Fd {
|
||||
fn from(fd: Fd) -> types_new::Fd {
|
||||
types_new::Fd::from(u32::from(fd))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<types_new::Fd> for Fd {
|
||||
fn from(fd: types_new::Fd) -> Fd {
|
||||
Fd::from(u32::from(fd))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Advice> for types_new::Advice {
|
||||
fn from(e: Advice) -> types_new::Advice {
|
||||
match e {
|
||||
Advice::Normal => types_new::Advice::Normal,
|
||||
Advice::Sequential => types_new::Advice::Sequential,
|
||||
Advice::Random => types_new::Advice::Random,
|
||||
Advice::Willneed => types_new::Advice::Willneed,
|
||||
Advice::Dontneed => types_new::Advice::Dontneed,
|
||||
Advice::Noreuse => types_new::Advice::Noreuse,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<types_new::Fdstat> for Fdstat {
|
||||
fn from(e: types_new::Fdstat) -> Fdstat {
|
||||
Fdstat {
|
||||
fs_filetype: e.fs_filetype.into(),
|
||||
fs_flags: e.fs_flags.into(),
|
||||
fs_rights_base: e.fs_rights_base.into(),
|
||||
fs_rights_inheriting: e.fs_rights_inheriting.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_rights_same() {
|
||||
macro_rules! assert_same {
|
||||
($($id:ident)*) => ({$(
|
||||
assert_eq!(u64::from(Rights::$id), u64::from(types_new::Rights::$id));
|
||||
)*});
|
||||
}
|
||||
assert_same! {
|
||||
FD_DATASYNC
|
||||
FD_READ
|
||||
FD_SEEK
|
||||
FD_FDSTAT_SET_FLAGS
|
||||
FD_SYNC
|
||||
FD_TELL
|
||||
FD_WRITE
|
||||
FD_ADVISE
|
||||
FD_ALLOCATE
|
||||
PATH_CREATE_DIRECTORY
|
||||
PATH_CREATE_FILE
|
||||
PATH_LINK_SOURCE
|
||||
PATH_LINK_TARGET
|
||||
PATH_OPEN
|
||||
FD_READDIR
|
||||
PATH_READLINK
|
||||
PATH_RENAME_SOURCE
|
||||
PATH_RENAME_TARGET
|
||||
PATH_FILESTAT_GET
|
||||
PATH_FILESTAT_SET_TIMES
|
||||
PATH_FILESTAT_SET_SIZE
|
||||
FD_FILESTAT_GET
|
||||
FD_FILESTAT_SET_SIZE
|
||||
FD_FILESTAT_SET_TIMES
|
||||
PATH_SYMLINK
|
||||
PATH_REMOVE_DIRECTORY
|
||||
PATH_UNLINK_FILE
|
||||
POLL_FD_READWRITE
|
||||
SOCK_SHUTDOWN
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Rights> for types_new::Rights {
|
||||
fn from(e: Rights) -> types_new::Rights {
|
||||
assert_rights_same();
|
||||
u64::from(e).try_into().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<types_new::Rights> for Rights {
|
||||
fn from(e: types_new::Rights) -> Rights {
|
||||
assert_rights_same();
|
||||
u64::from(e).try_into().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Filetype> for types_new::Filetype {
|
||||
fn from(e: Filetype) -> types_new::Filetype {
|
||||
match e {
|
||||
Filetype::Unknown => types_new::Filetype::Unknown,
|
||||
Filetype::BlockDevice => types_new::Filetype::BlockDevice,
|
||||
Filetype::CharacterDevice => types_new::Filetype::CharacterDevice,
|
||||
Filetype::Directory => types_new::Filetype::Directory,
|
||||
Filetype::RegularFile => types_new::Filetype::RegularFile,
|
||||
Filetype::SocketDgram => types_new::Filetype::SocketDgram,
|
||||
Filetype::SocketStream => types_new::Filetype::SocketStream,
|
||||
Filetype::SymbolicLink => types_new::Filetype::SymbolicLink,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<types_new::Filetype> for Filetype {
|
||||
fn from(e: types_new::Filetype) -> Filetype {
|
||||
match e {
|
||||
types_new::Filetype::Unknown => Filetype::Unknown,
|
||||
types_new::Filetype::BlockDevice => Filetype::BlockDevice,
|
||||
types_new::Filetype::CharacterDevice => Filetype::CharacterDevice,
|
||||
types_new::Filetype::Directory => Filetype::Directory,
|
||||
types_new::Filetype::RegularFile => Filetype::RegularFile,
|
||||
types_new::Filetype::SocketDgram => Filetype::SocketDgram,
|
||||
types_new::Filetype::SocketStream => Filetype::SocketStream,
|
||||
types_new::Filetype::SymbolicLink => Filetype::SymbolicLink,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_fdflags_same() {
|
||||
macro_rules! assert_same {
|
||||
($($id:ident)*) => ({$(
|
||||
assert_eq!(u16::from(Fdflags::$id), u16::from(types_new::Fdflags::$id));
|
||||
)*});
|
||||
}
|
||||
assert_same! {
|
||||
APPEND
|
||||
DSYNC
|
||||
NONBLOCK
|
||||
RSYNC
|
||||
SYNC
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Fdflags> for types_new::Fdflags {
|
||||
fn from(e: Fdflags) -> types_new::Fdflags {
|
||||
assert_fdflags_same();
|
||||
u16::from(e).try_into().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<types_new::Fdflags> for Fdflags {
|
||||
fn from(e: types_new::Fdflags) -> Fdflags {
|
||||
assert_fdflags_same();
|
||||
u16::from(e).try_into().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<types_new::Filestat> for Filestat {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(e: types_new::Filestat) -> Result<Filestat, Error> {
|
||||
Ok(Filestat {
|
||||
dev: e.dev,
|
||||
ino: e.ino,
|
||||
filetype: e.filetype.into(),
|
||||
// wasi_snapshot_preview1 has a 64-bit nlink field but we have a
|
||||
// 32-bit field, so we need to perform a fallible conversion.
|
||||
nlink: e.nlink.try_into()?,
|
||||
size: e.size,
|
||||
atim: e.atim,
|
||||
mtim: e.mtim,
|
||||
ctim: e.ctim,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_fstflags_same() {
|
||||
macro_rules! assert_same {
|
||||
($($id:ident)*) => ({$(
|
||||
assert_eq!(u16::from(Fstflags::$id), u16::from(types_new::Fstflags::$id));
|
||||
)*});
|
||||
}
|
||||
assert_same! {
|
||||
ATIM
|
||||
ATIM_NOW
|
||||
MTIM
|
||||
MTIM_NOW
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Fstflags> for types_new::Fstflags {
|
||||
fn from(e: Fstflags) -> types_new::Fstflags {
|
||||
assert_fstflags_same();
|
||||
u16::from(e).try_into().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<types_new::Fstflags> for Fstflags {
|
||||
fn from(e: types_new::Fstflags) -> Fstflags {
|
||||
assert_fstflags_same();
|
||||
u16::from(e).try_into().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<types_new::Prestat> for Prestat {
|
||||
fn from(e: types_new::Prestat) -> Prestat {
|
||||
match e {
|
||||
types_new::Prestat::Dir(d) => Prestat::Dir(d.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<types_new::PrestatDir> for PrestatDir {
|
||||
fn from(e: types_new::PrestatDir) -> PrestatDir {
|
||||
PrestatDir {
|
||||
pr_name_len: e.pr_name_len,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Whence> for types_new::Whence {
|
||||
fn from(e: Whence) -> types_new::Whence {
|
||||
match e {
|
||||
Whence::Set => types_new::Whence::Set,
|
||||
Whence::Cur => types_new::Whence::Cur,
|
||||
Whence::End => types_new::Whence::End,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_lookupflags_same() {
|
||||
macro_rules! assert_same {
|
||||
($($id:ident)*) => ({$(
|
||||
assert_eq!(u32::from(Lookupflags::$id), u32::from(types_new::Lookupflags::$id));
|
||||
)*});
|
||||
}
|
||||
assert_same! {
|
||||
SYMLINK_FOLLOW
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Lookupflags> for types_new::Lookupflags {
|
||||
fn from(e: Lookupflags) -> types_new::Lookupflags {
|
||||
assert_lookupflags_same();
|
||||
u32::from(e).try_into().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_oflags_same() {
|
||||
macro_rules! assert_same {
|
||||
($($id:ident)*) => ({$(
|
||||
assert_eq!(u16::from(Oflags::$id), u16::from(types_new::Oflags::$id));
|
||||
)*});
|
||||
}
|
||||
assert_same! {
|
||||
CREAT
|
||||
DIRECTORY
|
||||
EXCL
|
||||
TRUNC
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Oflags> for types_new::Oflags {
|
||||
fn from(e: Oflags) -> types_new::Oflags {
|
||||
assert_oflags_same();
|
||||
u16::from(e).try_into().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_sdflags_same() {
|
||||
macro_rules! assert_same {
|
||||
($($id:ident)*) => ({$(
|
||||
assert_eq!(u8::from(Sdflags::$id), u8::from(types_new::Sdflags::$id));
|
||||
)*});
|
||||
}
|
||||
assert_same! {
|
||||
RD WR
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Sdflags> for types_new::Sdflags {
|
||||
fn from(e: Sdflags) -> types_new::Sdflags {
|
||||
assert_sdflags_same();
|
||||
u8::from(e).try_into().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Signal> for types_new::Signal {
|
||||
fn from(e: Signal) -> types_new::Signal {
|
||||
match e {
|
||||
Signal::None => types_new::Signal::None,
|
||||
Signal::Hup => types_new::Signal::Hup,
|
||||
Signal::Int => types_new::Signal::Int,
|
||||
Signal::Quit => types_new::Signal::Quit,
|
||||
Signal::Ill => types_new::Signal::Ill,
|
||||
Signal::Trap => types_new::Signal::Trap,
|
||||
Signal::Abrt => types_new::Signal::Abrt,
|
||||
Signal::Bus => types_new::Signal::Bus,
|
||||
Signal::Fpe => types_new::Signal::Fpe,
|
||||
Signal::Kill => types_new::Signal::Kill,
|
||||
Signal::Usr1 => types_new::Signal::Usr1,
|
||||
Signal::Segv => types_new::Signal::Segv,
|
||||
Signal::Usr2 => types_new::Signal::Usr2,
|
||||
Signal::Pipe => types_new::Signal::Pipe,
|
||||
Signal::Alrm => types_new::Signal::Alrm,
|
||||
Signal::Term => types_new::Signal::Term,
|
||||
Signal::Chld => types_new::Signal::Chld,
|
||||
Signal::Cont => types_new::Signal::Cont,
|
||||
Signal::Stop => types_new::Signal::Stop,
|
||||
Signal::Tstp => types_new::Signal::Tstp,
|
||||
Signal::Ttin => types_new::Signal::Ttin,
|
||||
Signal::Ttou => types_new::Signal::Ttou,
|
||||
Signal::Urg => types_new::Signal::Urg,
|
||||
Signal::Xcpu => types_new::Signal::Xcpu,
|
||||
Signal::Xfsz => types_new::Signal::Xfsz,
|
||||
Signal::Vtalrm => types_new::Signal::Vtalrm,
|
||||
Signal::Prof => types_new::Signal::Prof,
|
||||
Signal::Winch => types_new::Signal::Winch,
|
||||
Signal::Poll => types_new::Signal::Poll,
|
||||
Signal::Pwr => types_new::Signal::Pwr,
|
||||
Signal::Sys => types_new::Signal::Sys,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For `wasi_unstable` and `wasi_snapshot_preview1` the memory layout of these
|
||||
// two types was manually verified. It should be fine to effectively cast
|
||||
// between the two types and get the same behavior.
|
||||
fn cvt_iovec<'a>(e: &IovecArray<'a>) -> types_new::IovecArray<'a> {
|
||||
wiggle::GuestPtr::new(e.mem(), (e.offset_base(), e.len()))
|
||||
}
|
||||
|
||||
fn cvt_ciovec<'a>(e: &CiovecArray<'a>) -> types_new::CiovecArray<'a> {
|
||||
wiggle::GuestPtr::new(e.mem(), (e.offset_base(), e.len()))
|
||||
}
|
||||
|
||||
fn assert_riflags_same() {
|
||||
macro_rules! assert_same {
|
||||
($($id:ident)*) => ({$(
|
||||
assert_eq!(u16::from(Riflags::$id), u16::from(types_new::Riflags::$id));
|
||||
)*});
|
||||
}
|
||||
assert_same! {
|
||||
RECV_PEEK
|
||||
RECV_WAITALL
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Riflags> for types_new::Riflags {
|
||||
fn from(e: Riflags) -> types_new::Riflags {
|
||||
assert_riflags_same();
|
||||
u16::from(e).try_into().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_roflags_same() {
|
||||
macro_rules! assert_same {
|
||||
($($id:ident)*) => ({$(
|
||||
assert_eq!(u16::from(Roflags::$id), u16::from(types_new::Roflags::$id));
|
||||
)*});
|
||||
}
|
||||
assert_same! {
|
||||
RECV_DATA_TRUNCATED
|
||||
}
|
||||
}
|
||||
|
||||
impl From<types_new::Roflags> for Roflags {
|
||||
fn from(e: types_new::Roflags) -> Roflags {
|
||||
assert_roflags_same();
|
||||
u16::from(e).try_into().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Subscription> for types_new::Subscription {
|
||||
fn from(e: Subscription) -> types_new::Subscription {
|
||||
types_new::Subscription {
|
||||
userdata: e.userdata,
|
||||
u: e.u.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SubscriptionU> for types_new::SubscriptionU {
|
||||
fn from(e: SubscriptionU) -> types_new::SubscriptionU {
|
||||
match e {
|
||||
SubscriptionU::Clock(c) => {
|
||||
types_new::SubscriptionU::Clock(types_new::SubscriptionClock {
|
||||
id: c.id.into(),
|
||||
timeout: c.timeout,
|
||||
precision: c.precision,
|
||||
flags: c.flags.into(),
|
||||
})
|
||||
}
|
||||
SubscriptionU::FdRead(c) => {
|
||||
types_new::SubscriptionU::FdRead(types_new::SubscriptionFdReadwrite {
|
||||
file_descriptor: c.file_descriptor.into(),
|
||||
})
|
||||
}
|
||||
SubscriptionU::FdWrite(c) => {
|
||||
types_new::SubscriptionU::FdWrite(types_new::SubscriptionFdReadwrite {
|
||||
file_descriptor: c.file_descriptor.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Subclockflags> for types_new::Subclockflags {
|
||||
fn from(e: Subclockflags) -> types_new::Subclockflags {
|
||||
macro_rules! assert_same {
|
||||
($($id:ident)*) => ({$(
|
||||
assert_eq!(u16::from(Subclockflags::$id), u16::from(types_new::Subclockflags::$id));
|
||||
)*});
|
||||
}
|
||||
assert_same! {
|
||||
SUBSCRIPTION_CLOCK_ABSTIME
|
||||
}
|
||||
u16::from(e).try_into().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<types_new::Event> for Event {
|
||||
fn from(e: types_new::Event) -> Event {
|
||||
Event {
|
||||
userdata: e.userdata,
|
||||
error: e.error.into(),
|
||||
type_: e.type_.into(),
|
||||
fd_readwrite: e.fd_readwrite.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<types_new::Errno> for Errno {
|
||||
fn from(e: types_new::Errno) -> Errno {
|
||||
match e {
|
||||
types_new::Errno::Success => Errno::Success,
|
||||
types_new::Errno::TooBig => Errno::TooBig,
|
||||
types_new::Errno::Acces => Errno::Acces,
|
||||
types_new::Errno::Addrinuse => Errno::Addrinuse,
|
||||
types_new::Errno::Addrnotavail => Errno::Addrnotavail,
|
||||
types_new::Errno::Afnosupport => Errno::Afnosupport,
|
||||
types_new::Errno::Again => Errno::Again,
|
||||
types_new::Errno::Already => Errno::Already,
|
||||
types_new::Errno::Badf => Errno::Badf,
|
||||
types_new::Errno::Badmsg => Errno::Badmsg,
|
||||
types_new::Errno::Busy => Errno::Busy,
|
||||
types_new::Errno::Canceled => Errno::Canceled,
|
||||
types_new::Errno::Child => Errno::Child,
|
||||
types_new::Errno::Connaborted => Errno::Connaborted,
|
||||
types_new::Errno::Connrefused => Errno::Connrefused,
|
||||
types_new::Errno::Connreset => Errno::Connreset,
|
||||
types_new::Errno::Deadlk => Errno::Deadlk,
|
||||
types_new::Errno::Destaddrreq => Errno::Destaddrreq,
|
||||
types_new::Errno::Dom => Errno::Dom,
|
||||
types_new::Errno::Dquot => Errno::Dquot,
|
||||
types_new::Errno::Exist => Errno::Exist,
|
||||
types_new::Errno::Fault => Errno::Fault,
|
||||
types_new::Errno::Fbig => Errno::Fbig,
|
||||
types_new::Errno::Hostunreach => Errno::Hostunreach,
|
||||
types_new::Errno::Idrm => Errno::Idrm,
|
||||
types_new::Errno::Ilseq => Errno::Ilseq,
|
||||
types_new::Errno::Inprogress => Errno::Inprogress,
|
||||
types_new::Errno::Intr => Errno::Intr,
|
||||
types_new::Errno::Inval => Errno::Inval,
|
||||
types_new::Errno::Io => Errno::Io,
|
||||
types_new::Errno::Isconn => Errno::Isconn,
|
||||
types_new::Errno::Isdir => Errno::Isdir,
|
||||
types_new::Errno::Loop => Errno::Loop,
|
||||
types_new::Errno::Mfile => Errno::Mfile,
|
||||
types_new::Errno::Mlink => Errno::Mlink,
|
||||
types_new::Errno::Msgsize => Errno::Msgsize,
|
||||
types_new::Errno::Multihop => Errno::Multihop,
|
||||
types_new::Errno::Nametoolong => Errno::Nametoolong,
|
||||
types_new::Errno::Netdown => Errno::Netdown,
|
||||
types_new::Errno::Netreset => Errno::Netreset,
|
||||
types_new::Errno::Netunreach => Errno::Netunreach,
|
||||
types_new::Errno::Nfile => Errno::Nfile,
|
||||
types_new::Errno::Nobufs => Errno::Nobufs,
|
||||
types_new::Errno::Nodev => Errno::Nodev,
|
||||
types_new::Errno::Noent => Errno::Noent,
|
||||
types_new::Errno::Noexec => Errno::Noexec,
|
||||
types_new::Errno::Nolck => Errno::Nolck,
|
||||
types_new::Errno::Nolink => Errno::Nolink,
|
||||
types_new::Errno::Nomem => Errno::Nomem,
|
||||
types_new::Errno::Nomsg => Errno::Nomsg,
|
||||
types_new::Errno::Noprotoopt => Errno::Noprotoopt,
|
||||
types_new::Errno::Nospc => Errno::Nospc,
|
||||
types_new::Errno::Nosys => Errno::Nosys,
|
||||
types_new::Errno::Notconn => Errno::Notconn,
|
||||
types_new::Errno::Notdir => Errno::Notdir,
|
||||
types_new::Errno::Notempty => Errno::Notempty,
|
||||
types_new::Errno::Notrecoverable => Errno::Notrecoverable,
|
||||
types_new::Errno::Notsock => Errno::Notsock,
|
||||
types_new::Errno::Notsup => Errno::Notsup,
|
||||
types_new::Errno::Notty => Errno::Notty,
|
||||
types_new::Errno::Nxio => Errno::Nxio,
|
||||
types_new::Errno::Overflow => Errno::Overflow,
|
||||
types_new::Errno::Ownerdead => Errno::Ownerdead,
|
||||
types_new::Errno::Perm => Errno::Perm,
|
||||
types_new::Errno::Pipe => Errno::Pipe,
|
||||
types_new::Errno::Proto => Errno::Proto,
|
||||
types_new::Errno::Protonosupport => Errno::Protonosupport,
|
||||
types_new::Errno::Prototype => Errno::Prototype,
|
||||
types_new::Errno::Range => Errno::Range,
|
||||
types_new::Errno::Rofs => Errno::Rofs,
|
||||
types_new::Errno::Spipe => Errno::Spipe,
|
||||
types_new::Errno::Srch => Errno::Srch,
|
||||
types_new::Errno::Stale => Errno::Stale,
|
||||
types_new::Errno::Timedout => Errno::Timedout,
|
||||
types_new::Errno::Txtbsy => Errno::Txtbsy,
|
||||
types_new::Errno::Xdev => Errno::Xdev,
|
||||
types_new::Errno::Notcapable => Errno::Notcapable,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<types_new::Eventtype> for Eventtype {
|
||||
fn from(e: types_new::Eventtype) -> Eventtype {
|
||||
match e {
|
||||
types_new::Eventtype::Clock => Eventtype::Clock,
|
||||
types_new::Eventtype::FdRead => Eventtype::FdRead,
|
||||
types_new::Eventtype::FdWrite => Eventtype::FdWrite,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<types_new::EventFdReadwrite> for EventFdReadwrite {
|
||||
fn from(e: types_new::EventFdReadwrite) -> EventFdReadwrite {
|
||||
EventFdReadwrite {
|
||||
nbytes: e.nbytes,
|
||||
flags: e.flags.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<types_new::Eventrwflags> for Eventrwflags {
|
||||
fn from(e: types_new::Eventrwflags) -> Eventrwflags {
|
||||
macro_rules! assert_same {
|
||||
($($id:ident)*) => ({$(
|
||||
assert_eq!(u16::from(Eventrwflags::$id), u16::from(types_new::Eventrwflags::$id));
|
||||
)*});
|
||||
}
|
||||
assert_same! {
|
||||
FD_READWRITE_HANGUP
|
||||
}
|
||||
u16::from(e).try_into().unwrap()
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
[package]
|
||||
name = "wig"
|
||||
version = "0.21.0"
|
||||
authors = ["Dan Gohman <sunfish@mozilla.com>"]
|
||||
description = "WebAssembly Interface Generator"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
categories = ["wasm"]
|
||||
keywords = ["webassembly", "wasm"]
|
||||
repository = "https://github.com/bytecodealliance/wasmtime"
|
||||
edition = "2018"
|
||||
include = ["src/**/*", "LICENSE", "WASI"]
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
test = false
|
||||
|
||||
[dependencies]
|
||||
quote = "1.0.2"
|
||||
proc-macro2 = "1.0.6"
|
||||
heck = "0.3.1"
|
||||
witx = { path = "../WASI/tools/witx", version = "0.8.7" }
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
||||
@@ -1,220 +0,0 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
|
||||
--- LLVM Exceptions to the Apache 2.0 License ----
|
||||
|
||||
As an exception, if, as a result of your compiling your source code, portions
|
||||
of this Software are embedded into an Object form of such source code, you
|
||||
may redistribute such embedded portions in such Object form without complying
|
||||
with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
|
||||
|
||||
In addition, if you combine or link compiled forms of this Software with
|
||||
software that is licensed under the GPLv2 ("Combined Software") and if a
|
||||
court of competent jurisdiction determines that the patent provision (Section
|
||||
3), the indemnity provision (Section 9) or other Section of the License
|
||||
conflicts with the conditions of the GPLv2, you may retroactively and
|
||||
prospectively choose to deem waived or otherwise exclude such Section(s) of
|
||||
the License, but only in their entirety and only with respect to the Combined
|
||||
Software.
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
use crate::utils;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
pub fn define(args: TokenStream) -> TokenStream {
|
||||
let path = utils::witx_path_from_args(args);
|
||||
let doc = match witx::load(&[&path]) {
|
||||
Ok(doc) => doc,
|
||||
Err(e) => {
|
||||
panic!("error opening file {}: {}", path.display(), e);
|
||||
}
|
||||
};
|
||||
|
||||
let mut ret = TokenStream::new();
|
||||
|
||||
for module in doc.modules() {
|
||||
for func in module.funcs() {
|
||||
// `proc_exit` is special; it's essentially an unwinding primitive,
|
||||
// so we implement it in the runtime rather than use the implementation
|
||||
// in wasi-common.
|
||||
if func.name.as_str() == "proc_exit" {
|
||||
continue;
|
||||
}
|
||||
|
||||
ret.extend(generate_wrappers(&func));
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
fn generate_wrappers(func: &witx::InterfaceFunc) -> TokenStream {
|
||||
let name = format_ident!("{}", func.name.as_str());
|
||||
let mut arg_declarations = Vec::new();
|
||||
let mut arg_names = Vec::new();
|
||||
|
||||
for param in func.params.iter() {
|
||||
let name = utils::param_name(param);
|
||||
|
||||
if let witx::TypePassedBy::PointerLengthPair = param.tref.type_().passed_by() {
|
||||
let ptr = format_ident!("{}_ptr", name);
|
||||
let len = format_ident!("{}_len", name);
|
||||
arg_declarations.push(quote! { #ptr: super::wasi32::uintptr_t });
|
||||
arg_declarations.push(quote! { #len: super::wasi32::size_t });
|
||||
arg_names.push(ptr);
|
||||
arg_names.push(len);
|
||||
continue;
|
||||
}
|
||||
|
||||
match ¶m.tref {
|
||||
witx::TypeRef::Name(n) => {
|
||||
if n.name.as_str() == "size" {
|
||||
arg_declarations.push(quote! { #name: super::wasi32::size_t });
|
||||
} else {
|
||||
let ty_name = format_ident!("__wasi_{}_t", n.name.as_str());
|
||||
arg_declarations.push(quote! { #name: super::wasi::#ty_name });
|
||||
}
|
||||
}
|
||||
witx::TypeRef::Value(v) => match &**v {
|
||||
witx::Type::ConstPointer(_) | witx::Type::Pointer(_) => {
|
||||
arg_declarations.push(quote! { #name: super::wasi32::uintptr_t });
|
||||
}
|
||||
_ => panic!("unexpected value type"),
|
||||
},
|
||||
}
|
||||
arg_names.push(name);
|
||||
}
|
||||
|
||||
let mut ret = quote!(());
|
||||
|
||||
for (i, result) in func.results.iter().enumerate() {
|
||||
if i == 0 {
|
||||
match &result.tref {
|
||||
witx::TypeRef::Name(n) => {
|
||||
let ty_name = format_ident!("__wasi_{}_t", n.name.as_str());
|
||||
ret = quote! { super::wasi::#ty_name };
|
||||
}
|
||||
witx::TypeRef::Value(_) => panic!("unexpected value type"),
|
||||
}
|
||||
continue;
|
||||
}
|
||||
let name = utils::param_name(result);
|
||||
arg_declarations.push(quote! { #name: super::wasi32::uintptr_t });
|
||||
arg_names.push(name);
|
||||
}
|
||||
|
||||
let call = quote! {
|
||||
super::hostcalls_impl::#name(wasi_ctx, memory, #(#arg_names,)*)
|
||||
};
|
||||
let body = if func.results.len() == 0 {
|
||||
call
|
||||
} else {
|
||||
quote! {
|
||||
let ret = #call
|
||||
.err()
|
||||
.unwrap_or(super::wasi::WasiError::ESUCCESS);
|
||||
tracing::trace!(" | errno={}", ret);
|
||||
ret.as_raw_errno()
|
||||
}
|
||||
};
|
||||
|
||||
quote! {
|
||||
pub unsafe fn #name(
|
||||
wasi_ctx: &mut super::WasiCtx,
|
||||
memory: &mut [u8],
|
||||
#(#arg_declarations,)*
|
||||
) -> Result<#ret, String> {
|
||||
Ok({#body})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
extern crate proc_macro;
|
||||
|
||||
mod hostcalls;
|
||||
mod raw_types;
|
||||
mod utils;
|
||||
mod wasi;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
|
||||
#[proc_macro]
|
||||
pub fn witx_host_types(args: TokenStream) -> TokenStream {
|
||||
TokenStream::from(raw_types::gen(
|
||||
TokenStream2::from(args),
|
||||
raw_types::Mode::Host,
|
||||
))
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn witx_wasi_types(args: TokenStream) -> TokenStream {
|
||||
TokenStream::from(raw_types::gen(
|
||||
TokenStream2::from(args),
|
||||
raw_types::Mode::Wasi,
|
||||
))
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn witx_wasi32_types(args: TokenStream) -> TokenStream {
|
||||
TokenStream::from(raw_types::gen(
|
||||
TokenStream2::from(args),
|
||||
raw_types::Mode::Wasi32,
|
||||
))
|
||||
}
|
||||
|
||||
/// A single-use macro in the `wasmtime-wasi` crate.
|
||||
#[proc_macro]
|
||||
pub fn define_wasi_struct(args: TokenStream) -> TokenStream {
|
||||
wasi::define_struct(args.into()).into()
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn define_hostcalls(args: TokenStream) -> TokenStream {
|
||||
hostcalls::define(args.into()).into()
|
||||
}
|
||||
@@ -1,359 +0,0 @@
|
||||
//! Translate witx types to Rust.
|
||||
|
||||
use crate::utils;
|
||||
use heck::ShoutySnakeCase;
|
||||
use proc_macro2::{Delimiter, Group, Literal, TokenStream, TokenTree};
|
||||
use quote::{format_ident, quote};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Mode {
|
||||
Host,
|
||||
Wasi32,
|
||||
Wasi,
|
||||
}
|
||||
|
||||
impl Mode {
|
||||
pub fn include_target_types(&self) -> bool {
|
||||
match self {
|
||||
Mode::Host | Mode::Wasi32 => true,
|
||||
Mode::Wasi => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gen(args: TokenStream, mode: Mode) -> TokenStream {
|
||||
let mut output = TokenStream::new();
|
||||
|
||||
let path = utils::witx_path_from_args(args);
|
||||
let doc = match witx::load(&[&path]) {
|
||||
Ok(doc) => doc,
|
||||
Err(e) => {
|
||||
panic!("error opening file {}: {}", path.display(), e);
|
||||
}
|
||||
};
|
||||
|
||||
gen_datatypes(&mut output, &doc, mode);
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
fn gen_datatypes(output: &mut TokenStream, doc: &witx::Document, mode: Mode) {
|
||||
let mut test_contents = TokenStream::new();
|
||||
for namedtype in doc.typenames() {
|
||||
if mode.include_target_types() != namedtype_has_target_size(&namedtype) {
|
||||
continue;
|
||||
}
|
||||
gen_datatype(output, &mut test_contents, mode, &namedtype);
|
||||
}
|
||||
match mode {
|
||||
Mode::Wasi | Mode::Wasi32 => output.extend(quote! {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
#test_contents
|
||||
}
|
||||
}),
|
||||
Mode::Host => {} // Don't emit tests for host reprs - the layout is different
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_datatype(
|
||||
output: &mut TokenStream,
|
||||
test_contents: &mut TokenStream,
|
||||
mode: Mode,
|
||||
namedtype: &witx::NamedType,
|
||||
) {
|
||||
let wasi_name = format_ident!("__wasi_{}_t", namedtype.name.as_str());
|
||||
let (size, align) = {
|
||||
use witx::Layout;
|
||||
let sa = namedtype.type_().mem_size_align();
|
||||
(sa.size, sa.align)
|
||||
};
|
||||
let mut test_code = quote! {
|
||||
assert_eq!(::std::mem::size_of::<#wasi_name>(), #size, concat!("Size of: ", stringify!(#wasi_name)));
|
||||
assert_eq!(::std::mem::align_of::<#wasi_name>(), #align, concat!("Align of: ", stringify!(#wasi_name)));
|
||||
};
|
||||
match &namedtype.tref {
|
||||
witx::TypeRef::Name(alias_to) => {
|
||||
let to = tref_tokens(mode, &alias_to.tref);
|
||||
output.extend(quote!(pub type #wasi_name = #to;));
|
||||
}
|
||||
witx::TypeRef::Value(v) => match &**v {
|
||||
witx::Type::Int(_) => panic!("unsupported int datatype"),
|
||||
witx::Type::Enum(e) => {
|
||||
let repr = int_repr_tokens(e.repr);
|
||||
output.extend(quote!(pub type #wasi_name = #repr;));
|
||||
for (index, variant) in e.variants.iter().enumerate() {
|
||||
let value_name = format_ident!(
|
||||
"__WASI_{}_{}",
|
||||
namedtype.name.as_str().to_shouty_snake_case(),
|
||||
variant.name.as_str().to_shouty_snake_case()
|
||||
);
|
||||
let index_name = Literal::usize_unsuffixed(index);
|
||||
output.extend(quote!(pub const #value_name: #wasi_name = #index_name;));
|
||||
}
|
||||
}
|
||||
witx::Type::Flags(f) => {
|
||||
let repr = int_repr_tokens(f.repr);
|
||||
output.extend(quote!(pub type #wasi_name = #repr;));
|
||||
for (index, flag) in f.flags.iter().enumerate() {
|
||||
let value_name = format_ident!(
|
||||
"__WASI_{}_{}",
|
||||
namedtype.name.as_str().to_shouty_snake_case(),
|
||||
flag.name.as_str().to_shouty_snake_case()
|
||||
);
|
||||
let flag_value = Literal::u128_unsuffixed(
|
||||
1u128
|
||||
.checked_shl(u32::try_from(index).expect("flag value overflow"))
|
||||
.expect("flag value overflow"),
|
||||
);
|
||||
output.extend(quote!(pub const #value_name: #wasi_name = #flag_value;));
|
||||
}
|
||||
}
|
||||
witx::Type::Struct(s) => {
|
||||
output.extend(quote!(#[repr(C)]));
|
||||
// Types which contain unions can't trivially implement Debug,
|
||||
// Hash, or Eq, because the type itself doesn't record which
|
||||
// union member is active.
|
||||
if struct_has_union(&s) {
|
||||
output.extend(quote!(#[derive(Copy, Clone)]));
|
||||
output.extend(quote!(#[allow(missing_debug_implementations)]));
|
||||
} else {
|
||||
output.extend(quote!(#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]));
|
||||
}
|
||||
|
||||
output.extend(quote!(pub struct #wasi_name));
|
||||
|
||||
let mut inner = TokenStream::new();
|
||||
for ml in s.member_layout().iter() {
|
||||
let member_name = format_ident!("r#{}", ml.member.name.as_str());
|
||||
let member_type = tref_tokens(mode, &ml.member.tref);
|
||||
let offset = ml.offset;
|
||||
inner.extend(quote!(pub #member_name: #member_type,));
|
||||
test_code.extend(quote!{
|
||||
assert_eq!(
|
||||
unsafe { &(*(::std::ptr::null::<#wasi_name>())).#member_name as *const _ as usize },
|
||||
#offset,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(#wasi_name),
|
||||
"::",
|
||||
stringify!(#member_name),
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
let braced = Group::new(Delimiter::Brace, inner);
|
||||
output.extend(TokenStream::from(TokenTree::Group(braced)));
|
||||
}
|
||||
witx::Type::Union(u) => {
|
||||
let u_name = format_ident!("__wasi_{}_u_t", namedtype.name.as_str());
|
||||
output.extend(quote!(#[repr(C)]));
|
||||
output.extend(quote!(#[derive(Copy, Clone)]));
|
||||
output.extend(quote!(#[allow(missing_debug_implementations)]));
|
||||
|
||||
output.extend(quote!(pub union #u_name));
|
||||
|
||||
let mut inner = TokenStream::new();
|
||||
for variant in &u.variants {
|
||||
let variant_name = format_ident!("r#{}", variant.name.as_str());
|
||||
if let Some(ref tref) = variant.tref {
|
||||
let variant_type = tref_tokens(mode, tref);
|
||||
inner.extend(quote!(pub #variant_name: #variant_type,));
|
||||
} else {
|
||||
inner.extend(quote!(pub #variant_name: (),));
|
||||
}
|
||||
}
|
||||
let braced = Group::new(Delimiter::Brace, inner);
|
||||
output.extend(TokenStream::from(TokenTree::Group(braced)));
|
||||
|
||||
output.extend(quote!(#[repr(C)]));
|
||||
output.extend(quote!(#[derive(Copy, Clone)]));
|
||||
output.extend(quote!(#[allow(missing_debug_implementations)]));
|
||||
|
||||
output.extend(quote!(pub struct #wasi_name));
|
||||
let tag_name = format_ident!("__wasi_{}_t", u.tag.name.as_str());
|
||||
let inner = quote!(pub tag: #tag_name, pub u: #u_name,);
|
||||
output.extend(TokenStream::from(TokenTree::Group(Group::new(
|
||||
Delimiter::Brace,
|
||||
inner,
|
||||
))));
|
||||
}
|
||||
witx::Type::Handle(_h) => {
|
||||
output.extend(quote!(pub type #wasi_name = u32;));
|
||||
}
|
||||
witx::Type::Builtin(b) => {
|
||||
if namedtype.name.as_str() == "size" {
|
||||
match mode {
|
||||
Mode::Host => output.extend(quote!(pub type #wasi_name = usize;)),
|
||||
Mode::Wasi => panic!("size has target-specific size"),
|
||||
Mode::Wasi32 => output.extend(quote!(pub type #wasi_name = u32;)),
|
||||
}
|
||||
} else {
|
||||
let b_type = builtin_tokens(mode, *b);
|
||||
output.extend(quote!(pub type #wasi_name = #b_type;));
|
||||
}
|
||||
}
|
||||
witx::Type::Pointer { .. }
|
||||
| witx::Type::ConstPointer { .. }
|
||||
| witx::Type::Array { .. } => {
|
||||
let tref_tokens = tref_tokens(mode, &namedtype.tref);
|
||||
output.extend(quote!(pub type #wasi_name = #tref_tokens;));
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
if namedtype.name.as_str() == "errno" {
|
||||
// Generate strerror for errno type
|
||||
gen_errno_strerror(output, namedtype);
|
||||
}
|
||||
|
||||
let test_func_name = format_ident!("wig_test_layout_{}", namedtype.name.as_str());
|
||||
test_contents.extend(quote! {
|
||||
#[test]
|
||||
fn #test_func_name() {
|
||||
#test_code
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn gen_errno_strerror(output: &mut TokenStream, namedtype: &witx::NamedType) {
|
||||
let inner = match &namedtype.tref {
|
||||
witx::TypeRef::Value(v) => match &**v {
|
||||
witx::Type::Enum(e) => e,
|
||||
x => panic!("expected Enum('errno'), instead received {:?}", x),
|
||||
},
|
||||
x => panic!("expected Enum('errno'), instead received {:?}", x),
|
||||
};
|
||||
let mut inner_group = TokenStream::new();
|
||||
for variant in &inner.variants {
|
||||
let value_name = format_ident!(
|
||||
"__WASI_ERRNO_{}",
|
||||
variant.name.as_str().to_shouty_snake_case()
|
||||
);
|
||||
let docs = variant.docs.trim();
|
||||
inner_group.extend(quote!(#value_name => #docs,));
|
||||
}
|
||||
output.extend(
|
||||
quote!(pub fn strerror(errno: __wasi_errno_t) -> &'static str {
|
||||
match errno {
|
||||
#inner_group
|
||||
other => panic!("Undefined errno value {:?}", other),
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
fn int_repr_tokens(int_repr: witx::IntRepr) -> TokenStream {
|
||||
match int_repr {
|
||||
witx::IntRepr::U8 => quote!(u8),
|
||||
witx::IntRepr::U16 => quote!(u16),
|
||||
witx::IntRepr::U32 => quote!(u32),
|
||||
witx::IntRepr::U64 => quote!(u64),
|
||||
}
|
||||
}
|
||||
|
||||
fn builtin_tokens(mode: Mode, builtin: witx::BuiltinType) -> TokenStream {
|
||||
match builtin {
|
||||
witx::BuiltinType::String => match mode {
|
||||
Mode::Host => quote!((*const u8, usize)),
|
||||
Mode::Wasi => panic!("strings have target-specific size"),
|
||||
Mode::Wasi32 => quote!((u32, u32)),
|
||||
},
|
||||
witx::BuiltinType::Char8 => quote!(i8),
|
||||
witx::BuiltinType::U8 => quote!(u8),
|
||||
witx::BuiltinType::U16 => quote!(u16),
|
||||
witx::BuiltinType::U32 => quote!(u32),
|
||||
witx::BuiltinType::U64 => quote!(u64),
|
||||
witx::BuiltinType::S8 => quote!(i8),
|
||||
witx::BuiltinType::S16 => quote!(i16),
|
||||
witx::BuiltinType::S32 => quote!(i32),
|
||||
witx::BuiltinType::S64 => quote!(i64),
|
||||
witx::BuiltinType::F32 => quote!(f32),
|
||||
witx::BuiltinType::F64 => quote!(f64),
|
||||
witx::BuiltinType::USize => match mode {
|
||||
Mode::Host => quote!(usize),
|
||||
Mode::Wasi => panic!("usize has target-specific size"),
|
||||
Mode::Wasi32 => quote!(u32),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn tref_tokens(mode: Mode, tref: &witx::TypeRef) -> TokenStream {
|
||||
match tref {
|
||||
witx::TypeRef::Name(n) => TokenStream::from(TokenTree::Ident(format_ident!(
|
||||
"__wasi_{}_t",
|
||||
n.name.as_str()
|
||||
))),
|
||||
witx::TypeRef::Value(v) => match &**v {
|
||||
witx::Type::Builtin(b) => builtin_tokens(mode, *b),
|
||||
witx::Type::Pointer(pointee) => {
|
||||
let pointee = tref_tokens(mode, pointee);
|
||||
match mode {
|
||||
Mode::Host => quote!(*mut #pointee),
|
||||
Mode::Wasi => panic!("pointers have target-specific size"),
|
||||
Mode::Wasi32 => quote!(u32),
|
||||
}
|
||||
}
|
||||
witx::Type::ConstPointer(pointee) => {
|
||||
let pointee = tref_tokens(mode, pointee);
|
||||
match mode {
|
||||
Mode::Host => quote!(*const #pointee),
|
||||
Mode::Wasi => panic!("pointers have target-specific size"),
|
||||
Mode::Wasi32 => quote!(u32),
|
||||
}
|
||||
}
|
||||
witx::Type::Array(element) => {
|
||||
let element_name = tref_tokens(mode, element);
|
||||
match mode {
|
||||
Mode::Host => quote!((*const #element_name, usize)),
|
||||
Mode::Wasi => panic!("arrays have target-specific size"),
|
||||
Mode::Wasi32 => quote!((u32, u32)),
|
||||
}
|
||||
}
|
||||
t => panic!("cannot give name to anonymous type {:?}", t),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Test whether the given struct contains any union members.
|
||||
fn struct_has_union(s: &witx::StructDatatype) -> bool {
|
||||
s.members.iter().any(|member| match &*member.tref.type_() {
|
||||
witx::Type::Union { .. } => true,
|
||||
witx::Type::Struct(s) => struct_has_union(&s),
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
|
||||
/// Test whether the type referred to has a target-specific size.
|
||||
fn tref_has_target_size(tref: &witx::TypeRef) -> bool {
|
||||
match tref {
|
||||
witx::TypeRef::Name(nt) => namedtype_has_target_size(&nt),
|
||||
witx::TypeRef::Value(t) => type_has_target_size(&t),
|
||||
}
|
||||
}
|
||||
|
||||
/// Test whether the given named type has a target-specific size.
|
||||
fn namedtype_has_target_size(nt: &witx::NamedType) -> bool {
|
||||
if nt.name.as_str() == "size" {
|
||||
true
|
||||
} else {
|
||||
tref_has_target_size(&nt.tref)
|
||||
}
|
||||
}
|
||||
|
||||
/// Test whether the given type has a target-specific size.
|
||||
fn type_has_target_size(ty: &witx::Type) -> bool {
|
||||
match ty {
|
||||
witx::Type::Builtin(witx::BuiltinType::String) => true,
|
||||
witx::Type::Pointer { .. } | witx::Type::ConstPointer { .. } => true,
|
||||
witx::Type::Array(elem) => tref_has_target_size(elem),
|
||||
witx::Type::Struct(s) => s.members.iter().any(|m| tref_has_target_size(&m.tref)),
|
||||
witx::Type::Union(u) => u
|
||||
.variants
|
||||
.iter()
|
||||
.any(|v| v.tref.as_ref().map(tref_has_target_size).unwrap_or(false)),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
use proc_macro2::{Ident, Literal, TokenStream, TokenTree};
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Given the input tokens to a macro invocation, return the path to the
|
||||
/// witx file to process.
|
||||
pub(crate) fn witx_path_from_args(args: TokenStream) -> PathBuf {
|
||||
let mut strings = Vec::new();
|
||||
|
||||
for arg in args {
|
||||
if let TokenTree::Literal(literal) = arg {
|
||||
let parsed = parse_string_literal(literal);
|
||||
|
||||
strings.push(parsed);
|
||||
} else {
|
||||
panic!("arguments must be string literals");
|
||||
}
|
||||
}
|
||||
|
||||
if strings.len() != 1 {
|
||||
panic!("expected one string literals");
|
||||
}
|
||||
let root = PathBuf::from(std::env::var("WASI_ROOT").unwrap());
|
||||
return root.join(&strings[0]);
|
||||
}
|
||||
|
||||
// Convert a `Literal` holding a string literal into the `String`.
|
||||
//
|
||||
// FIXME: It feels like there should be an easier way to do this.
|
||||
fn parse_string_literal(literal: Literal) -> String {
|
||||
let s = literal.to_string();
|
||||
assert!(
|
||||
s.starts_with('"') && s.ends_with('"'),
|
||||
"string literal must be enclosed in double-quotes"
|
||||
);
|
||||
|
||||
let trimmed = s[1..s.len() - 1].to_owned();
|
||||
assert!(
|
||||
!trimmed.contains('"'),
|
||||
"string literal must not contain embedded quotes for now"
|
||||
);
|
||||
assert!(
|
||||
!trimmed.contains('\\'),
|
||||
"string literal must not contain embedded backslashes for now"
|
||||
);
|
||||
|
||||
trimmed
|
||||
}
|
||||
|
||||
pub fn param_name(param: &witx::InterfaceFuncParam) -> Ident {
|
||||
quote::format_ident!(
|
||||
"{}",
|
||||
match param.name.as_str() {
|
||||
"in" | "type" => format!("r#{}", param.name.as_str()),
|
||||
s => s.to_string(),
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -1,279 +0,0 @@
|
||||
use crate::utils;
|
||||
use proc_macro2::{Ident, Span, TokenStream};
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
enum Abi {
|
||||
I32,
|
||||
I64,
|
||||
F32,
|
||||
F64,
|
||||
}
|
||||
|
||||
/// This is a single-use macro intended to be used in the `wasmtime-wasi` crate.
|
||||
///
|
||||
/// This macro will generate a structure, `Wasi`, which will create all the
|
||||
/// functions necessary to bind wasi and hook everything up via the `wasmtime`
|
||||
/// crate.
|
||||
///
|
||||
/// The generated shim functions here will also `trace!` their arguments for
|
||||
/// logging purposes. Otherwise this is hopefully somewhat straightforward!
|
||||
///
|
||||
/// I'd recommend using `cargo +nightly expand` to explore the output of this
|
||||
/// macro some more.
|
||||
pub fn define_struct(args: TokenStream) -> TokenStream {
|
||||
let path = utils::witx_path_from_args(args);
|
||||
let doc = match witx::load(&[&path]) {
|
||||
Ok(doc) => doc,
|
||||
Err(e) => {
|
||||
panic!("error opening file {}: {}", path.display(), e);
|
||||
}
|
||||
};
|
||||
|
||||
let mut fields = Vec::new();
|
||||
let mut get_exports = Vec::new();
|
||||
let mut ctor_externs = Vec::new();
|
||||
let mut ctor_fields = Vec::new();
|
||||
let mut linker_add = Vec::new();
|
||||
|
||||
for module in doc.modules() {
|
||||
let module_name = module.name.as_str();
|
||||
for func in module.funcs() {
|
||||
let name = func.name.as_str();
|
||||
let name_ident = Ident::new(name, Span::call_site());
|
||||
fields.push(quote! { pub #name_ident: wasmtime::Func });
|
||||
get_exports.push(quote! { #name => Some(&self.#name_ident) });
|
||||
ctor_fields.push(name_ident.clone());
|
||||
linker_add.push(quote! {
|
||||
linker.define(#module_name, #name, self.#name_ident.clone())?;
|
||||
});
|
||||
// `proc_exit` is special; it's essentially an unwinding primitive,
|
||||
// so we implement it in the runtime rather than use the implementation
|
||||
// in wasi-common.
|
||||
if name == "proc_exit" {
|
||||
ctor_externs.push(quote! {
|
||||
let #name_ident = wasmtime::Func::wrap(store, crate::wasi_proc_exit);
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut shim_arg_decls = Vec::new();
|
||||
let mut params = Vec::new();
|
||||
let mut formats = Vec::new();
|
||||
let mut format_args = Vec::new();
|
||||
let mut hostcall_args = Vec::new();
|
||||
|
||||
for param in func.params.iter() {
|
||||
let name = utils::param_name(param);
|
||||
|
||||
// Registers a new parameter to the shim we're making with the
|
||||
// given `name`, the `abi_ty` wasm type and `hex` defines
|
||||
// whether it's debug-printed in a hex format or not.
|
||||
//
|
||||
// This will register a whole bunch of things:
|
||||
//
|
||||
// * The cranelift type for the parameter
|
||||
// * Syntax to specify the actual function parameter
|
||||
// * How to log the parameter value in a call to `trace!`
|
||||
// * How to actually pass this argument to the host
|
||||
// implementation, converting as necessary.
|
||||
let mut add_param = |name: &Ident, abi_ty: Abi, hex: bool| {
|
||||
match abi_ty {
|
||||
Abi::I32 => {
|
||||
params.push(quote! { types::I32 });
|
||||
shim_arg_decls.push(quote! { #name: i32 });
|
||||
}
|
||||
Abi::I64 => {
|
||||
params.push(quote! { types::I64 });
|
||||
shim_arg_decls.push(quote! { #name: i64 });
|
||||
}
|
||||
Abi::F32 => {
|
||||
params.push(quote! { types::F32 });
|
||||
shim_arg_decls.push(quote! { #name: f32 });
|
||||
}
|
||||
Abi::F64 => {
|
||||
params.push(quote! { types::F64 });
|
||||
shim_arg_decls.push(quote! { #name: f64 });
|
||||
}
|
||||
}
|
||||
formats.push(format!("{}={}", name, if hex { "{:#x}" } else { "{}" },));
|
||||
format_args.push(name.clone());
|
||||
hostcall_args.push(quote! { #name as _ });
|
||||
};
|
||||
|
||||
match &*param.tref.type_() {
|
||||
witx::Type::Int(e) => match e.repr {
|
||||
witx::IntRepr::U64 => add_param(&name, Abi::I64, false),
|
||||
_ => add_param(&name, Abi::I32, false),
|
||||
},
|
||||
|
||||
witx::Type::Enum(e) => match e.repr {
|
||||
witx::IntRepr::U64 => add_param(&name, Abi::I64, false),
|
||||
_ => add_param(&name, Abi::I32, false),
|
||||
},
|
||||
|
||||
witx::Type::Flags(f) => match f.repr {
|
||||
witx::IntRepr::U64 => add_param(&name, Abi::I64, true),
|
||||
_ => add_param(&name, Abi::I32, true),
|
||||
},
|
||||
|
||||
witx::Type::Builtin(witx::BuiltinType::Char8)
|
||||
| witx::Type::Builtin(witx::BuiltinType::S8)
|
||||
| witx::Type::Builtin(witx::BuiltinType::U8)
|
||||
| witx::Type::Builtin(witx::BuiltinType::S16)
|
||||
| witx::Type::Builtin(witx::BuiltinType::U16)
|
||||
| witx::Type::Builtin(witx::BuiltinType::S32)
|
||||
| witx::Type::Builtin(witx::BuiltinType::U32)
|
||||
| witx::Type::Builtin(witx::BuiltinType::USize) => {
|
||||
add_param(&name, Abi::I32, false);
|
||||
}
|
||||
|
||||
witx::Type::Builtin(witx::BuiltinType::S64)
|
||||
| witx::Type::Builtin(witx::BuiltinType::U64) => {
|
||||
add_param(&name, Abi::I64, false);
|
||||
}
|
||||
|
||||
witx::Type::Builtin(witx::BuiltinType::F32) => {
|
||||
add_param(&name, Abi::F32, false);
|
||||
}
|
||||
|
||||
witx::Type::Builtin(witx::BuiltinType::F64) => {
|
||||
add_param(&name, Abi::F64, false);
|
||||
}
|
||||
|
||||
// strings/arrays have an extra ABI parameter for the length
|
||||
// of the array passed.
|
||||
witx::Type::Builtin(witx::BuiltinType::String) | witx::Type::Array(_) => {
|
||||
add_param(&name, Abi::I32, true);
|
||||
let len = format_ident!("{}_len", name);
|
||||
add_param(&len, Abi::I32, false);
|
||||
}
|
||||
|
||||
witx::Type::ConstPointer(_)
|
||||
| witx::Type::Handle(_)
|
||||
| witx::Type::Pointer(_) => {
|
||||
add_param(&name, Abi::I32, true);
|
||||
}
|
||||
|
||||
witx::Type::Struct(_) | witx::Type::Union(_) => {
|
||||
panic!("unsupported argument type")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut results = func.results.iter();
|
||||
let mut ret_ty = quote! { () };
|
||||
let mut cvt_ret = quote! {};
|
||||
let mut returns = Vec::new();
|
||||
let mut handle_early_error = quote! { panic!("error: {:?}", e) };
|
||||
|
||||
// The first result is returned bare right now...
|
||||
if let Some(ret) = results.next() {
|
||||
handle_early_error = quote! { return Ok(e.into()) };
|
||||
match &*ret.tref.type_() {
|
||||
// Eventually we'll want to add support for more returned
|
||||
// types, but for now let's just conform to what `*.witx`
|
||||
// definitions currently use.
|
||||
witx::Type::Enum(e) => match e.repr {
|
||||
witx::IntRepr::U16 => {
|
||||
returns.push(quote! { types::I32 });
|
||||
ret_ty = quote! { i32 };
|
||||
cvt_ret = quote! { .into() }
|
||||
}
|
||||
other => panic!("unsupported ret enum repr {:?}", other),
|
||||
},
|
||||
other => panic!("unsupported first return {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
// ... and all remaining results are returned via out-poiners
|
||||
for result in results {
|
||||
let name = format_ident!("{}", result.name.as_str());
|
||||
params.push(quote! { types::I32 });
|
||||
shim_arg_decls.push(quote! { #name: i32 });
|
||||
formats.push(format!("{}={{:#x}}", name));
|
||||
format_args.push(name.clone());
|
||||
hostcall_args.push(quote! { #name as u32 });
|
||||
}
|
||||
|
||||
let format_str = format!("{}({})", name, formats.join(", "));
|
||||
ctor_externs.push(quote! {
|
||||
let my_cx = cx.clone();
|
||||
let #name_ident = wasmtime::Func::wrap(
|
||||
store,
|
||||
move |caller: wasmtime::Caller<'_> #(,#shim_arg_decls)*| -> Result<#ret_ty, wasmtime::Trap> {
|
||||
tracing::trace!(
|
||||
#format_str,
|
||||
#(#format_args),*
|
||||
);
|
||||
unsafe {
|
||||
let memory = match caller.get_export("memory") {
|
||||
Some(wasmtime::Extern::Memory(m)) => m,
|
||||
_ => {
|
||||
tracing::warn!("callee does not export a memory as \"memory\"");
|
||||
let e = wasi_common::old::snapshot_0::wasi::__WASI_ERRNO_INVAL;
|
||||
#handle_early_error
|
||||
}
|
||||
};
|
||||
let result = hostcalls::#name_ident(
|
||||
&mut my_cx.borrow_mut(),
|
||||
memory.data_unchecked_mut(),
|
||||
#(#hostcall_args),*
|
||||
);
|
||||
match result {
|
||||
Ok(r) => { return Ok(r #cvt_ret); },
|
||||
Err(err) => { return Err(wasmtime::Trap::new(err)); },
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
quote! {
|
||||
/// An instantiated instance of the wasi exports.
|
||||
///
|
||||
/// This represents a wasi module which can be used to instantiate other
|
||||
/// wasm modules. This structure exports all that various fields of the
|
||||
/// wasi instance as fields which can be used to implement your own
|
||||
/// instantiation logic, if necessary. Additionally [`Wasi::get_export`]
|
||||
/// can be used to do name-based resolution.
|
||||
pub struct Wasi {
|
||||
#(#fields,)*
|
||||
}
|
||||
|
||||
impl Wasi {
|
||||
/// Creates a new [`Wasi`] instance.
|
||||
///
|
||||
/// External values are allocated into the `store` provided and
|
||||
/// configuration of the wasi instance itself should be all
|
||||
/// contained in the `cx` parameter.
|
||||
pub fn new(store: &wasmtime::Store, cx: WasiCtx) -> Wasi {
|
||||
let cx = std::rc::Rc::new(std::cell::RefCell::new(cx));
|
||||
#(#ctor_externs)*
|
||||
|
||||
Wasi {
|
||||
#(#ctor_fields,)*
|
||||
}
|
||||
}
|
||||
|
||||
/// Looks up a field called `name` in this structure, returning it
|
||||
/// if found.
|
||||
///
|
||||
/// This is often useful when instantiating a `wasmtime` instance
|
||||
/// where name resolution often happens with strings.
|
||||
pub fn get_export(&self, name: &str) -> Option<&wasmtime::Func> {
|
||||
match name {
|
||||
#(#get_exports,)*
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds all wasi items to the specified `Linker`.
|
||||
pub fn add_to_linker(&self, linker: &mut wasmtime::Linker) -> anyhow::Result<()> {
|
||||
#(#linker_add)*
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user