diff --git a/Cargo.lock b/Cargo.lock index cbcd79b8f7..aabea2ee2b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2296,7 +2296,6 @@ dependencies = [ "libc", "thiserror", "tracing", - "wig", "wiggle", "winapi", "winx", @@ -2639,7 +2638,6 @@ dependencies = [ "wasmtime", "wasmtime-runtime", "wasmtime-wiggle", - "wig", "wiggle", ] @@ -2725,16 +2723,6 @@ dependencies = [ "libc", ] -[[package]] -name = "wig" -version = "0.21.0" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "witx", -] - [[package]] name = "wiggle" version = "0.21.0" diff --git a/crates/c-api/src/wasi.rs b/crates/c-api/src/wasi.rs index 2a637b8b25..763ecf4ba1 100644 --- a/crates/c-api/src/wasi.rs +++ b/crates/c-api/src/wasi.rs @@ -8,11 +8,8 @@ use std::os::raw::{c_char, c_int}; use std::path::{Path, PathBuf}; use std::slice; use std::str; -use wasi_common::{ - old::snapshot_0::WasiCtxBuilder as WasiSnapshot0CtxBuilder, preopen_dir, - WasiCtxBuilder as WasiPreview1CtxBuilder, -}; -use wasmtime::{Extern, Linker, Store, Trap}; +use wasi_common::{preopen_dir, WasiCtx, WasiCtxBuilder}; +use wasmtime::{Extern, Linker, Trap}; use wasmtime_wasi::{old::snapshot_0::Wasi as WasiSnapshot0, Wasi as WasiPreview1}; unsafe fn cstr_to_path<'a>(path: *const c_char) -> Option<&'a Path> { @@ -200,46 +197,10 @@ enum WasiInstance { Snapshot0(WasiSnapshot0), } -fn create_snapshot0_instance(store: &Store, config: wasi_config_t) -> Result { - let mut builder = WasiSnapshot0CtxBuilder::new(); - if config.inherit_args { - builder.inherit_args(); - } else if !config.args.is_empty() { - builder.args(config.args); - } - if config.inherit_env { - builder.inherit_env(); - } else if !config.env.is_empty() { - builder.envs(config.env); - } - if config.inherit_stdin { - builder.inherit_stdin(); - } else if let Some(file) = config.stdin { - builder.stdin(file); - } - if config.inherit_stdout { - builder.inherit_stdout(); - } else if let Some(file) = config.stdout { - builder.stdout(file); - } - if config.inherit_stderr { - builder.inherit_stderr(); - } else if let Some(file) = config.stderr { - builder.stderr(file); - } - for preopen in config.preopens { - builder.preopened_dir(preopen.0, preopen.1); - } - Ok(WasiInstance::Snapshot0(WasiSnapshot0::new( - store, - builder.build()?, - ))) -} - -fn create_preview1_instance(store: &Store, config: wasi_config_t) -> Result { +fn create_wasi_ctx(config: wasi_config_t) -> Result { use std::convert::TryFrom; use wasi_common::OsFile; - let mut builder = WasiPreview1CtxBuilder::new(); + let mut builder = WasiCtxBuilder::new(); if config.inherit_args { builder.inherit_args(); } else if !config.args.is_empty() { @@ -268,10 +229,7 @@ fn create_preview1_instance(store: &Store, config: wasi_config_t) -> Result { - create_preview1_instance(store, *config).map_err(|e| e.to_string()) + create_wasi_ctx(*config).map(|cx| WasiInstance::Preview1(WasiPreview1::new(store, cx))) } - "wasi_unstable" => create_snapshot0_instance(store, *config).map_err(|e| e.to_string()), - _ => Err("unsupported WASI version".into()), + "wasi_unstable" => create_wasi_ctx(*config) + .map(|cx| WasiInstance::Snapshot0(WasiSnapshot0::new(store, cx))), + _ => Err(anyhow::anyhow!("unsupported WASI version")), }; match result { @@ -312,7 +271,9 @@ pub unsafe extern "C" fn wasi_instance_new( export_cache: HashMap::new(), })), Err(e) => { - *trap = Box::into_raw(Box::new(wasm_trap_t { trap: Trap::new(e) })); + *trap = Box::into_raw(Box::new(wasm_trap_t { + trap: Trap::from(e), + })); None } diff --git a/crates/wasi-common/Cargo.toml b/crates/wasi-common/Cargo.toml index 6187e7a9cb..8909fc0dac 100644 --- a/crates/wasi-common/Cargo.toml +++ b/crates/wasi-common/Cargo.toml @@ -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" diff --git a/crates/wasi-common/src/fdpool.rs b/crates/wasi-common/src/fdpool.rs index 8043abe01c..b9523291b7 100644 --- a/crates/wasi-common/src/fdpool.rs +++ b/crates/wasi-common/src/fdpool.rs @@ -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; diff --git a/crates/wasi-common/src/lib.rs b/crates/wasi-common/src/lib.rs index 3034aca346..02a39f1c05 100644 --- a/crates/wasi-common/src/lib.rs +++ b/crates/wasi-common/src/lib.rs @@ -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; diff --git a/crates/wasi-common/src/old/mod.rs b/crates/wasi-common/src/old/mod.rs deleted file mode 100644 index 5d4d33030a..0000000000 --- a/crates/wasi-common/src/old/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod snapshot_0; diff --git a/crates/wasi-common/src/old/snapshot_0/ctx.rs b/crates/wasi-common/src/old/snapshot_0/ctx.rs deleted file mode 100644 index 7ffa536bf3..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/ctx.rs +++ /dev/null @@ -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 = std::result::Result; - -enum PendingEntry { - Thunk(fn() -> io::Result), - 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 - ), - Self::File(f) => write!(fmt, "PendingEntry::File({:?})", f), - } - } -} - -#[derive(Debug, Eq, Hash, PartialEq)] -enum PendingCString { - Bytes(Vec), - OsString(OsString), -} - -impl From> for PendingCString { - fn from(bytes: Vec) -> Self { - Self::Bytes(bytes) - } -} - -impl From for PendingCString { - fn from(s: OsString) -> Self { - Self::OsString(s) - } -} - -impl PendingCString { - fn into_string(self) -> WasiCtxBuilderResult { - 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 = 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 { - 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, - stdout: Option, - stderr: Option, - preopens: Option>, - args: Option>, - env: Option>, -} - -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>(&mut self, args: impl IntoIterator) -> &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>(&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>(&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, T: Borrow<(S, S)>>( - &mut self, - envs: impl IntoIterator, - ) -> &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>(&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 { - // 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::>>()?; - - 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::>>()?; - - let mut fd_pool = FdPool::new(); - let mut entries: HashMap = 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, - pub(crate) args: Vec, - pub(crate) env: Vec, -} - -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>(args: impl IntoIterator) -> WasiCtxBuilderResult { - 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 { - 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 { - 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 { - // 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) - } -} diff --git a/crates/wasi-common/src/old/snapshot_0/entry.rs b/crates/wasi-common/src/old/snapshot_0/entry.rs deleted file mode 100644 index f1863af112..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/entry.rs +++ /dev/null @@ -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, - // 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 { - 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 { - 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 { - 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 { - 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::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, - _ref: PhantomData<&'descriptor Descriptor>, -} - -impl<'descriptor> OsHandleRef<'descriptor> { - pub(crate) fn new(handle: ManuallyDrop) -> 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 - } -} diff --git a/crates/wasi-common/src/old/snapshot_0/helpers.rs b/crates/wasi-common/src/old/snapshot_0/helpers.rs deleted file mode 100644 index e4426ffc2e..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/helpers.rs +++ /dev/null @@ -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) -} diff --git a/crates/wasi-common/src/old/snapshot_0/host.rs b/crates/wasi-common/src/old/snapshot_0/host.rs deleted file mode 100644 index c77c8b8887..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/host.rs +++ /dev/null @@ -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> { - 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::::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) - } -} diff --git a/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/fs.rs b/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/fs.rs deleted file mode 100644 index 6ba1c4d99b..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/fs.rs +++ /dev/null @@ -1,1070 +0,0 @@ -#![allow(non_camel_case_types)] -use super::fs_helpers::path_get; -use crate::old::snapshot_0::ctx::WasiCtx; -use crate::old::snapshot_0::entry::{Descriptor, Entry}; -use crate::old::snapshot_0::helpers::*; -use crate::old::snapshot_0::memory::*; -use crate::old::snapshot_0::sys::entry_impl::determine_type_rights; -use crate::old::snapshot_0::sys::hostcalls_impl::fs_helpers::path_open_rights; -use crate::old::snapshot_0::sys::{host_impl, hostcalls_impl}; -use crate::old::snapshot_0::wasi::{self, WasiError, WasiResult}; -use crate::old::snapshot_0::{helpers, host, wasi32}; -use crate::sandboxed_tty_writer::SandboxedTTYWriter; -use filetime::{set_file_handle_times, FileTime}; -use std::fs::File; -use std::io::{self, Read, Seek, SeekFrom, Write}; -use std::ops::DerefMut; -use std::time::{Duration, SystemTime, UNIX_EPOCH}; -use tracing::trace; - -pub(crate) unsafe fn fd_close( - wasi_ctx: &mut WasiCtx, - _mem: &mut [u8], - fd: wasi::__wasi_fd_t, -) -> WasiResult<()> { - trace!("fd_close(fd={:?})", fd); - - if let Ok(fe) = wasi_ctx.get_entry(fd) { - // can't close preopened files - if fe.preopen_path.is_some() { - return Err(WasiError::ENOTSUP); - } - } - - wasi_ctx.remove_entry(fd)?; - Ok(()) -} - -pub(crate) unsafe fn fd_datasync( - wasi_ctx: &WasiCtx, - _mem: &mut [u8], - fd: wasi::__wasi_fd_t, -) -> WasiResult<()> { - trace!("fd_datasync(fd={:?})", fd); - - let fd = wasi_ctx - .get_entry(fd)? - .as_descriptor(wasi::__WASI_RIGHTS_FD_DATASYNC, 0)? - .as_file()?; - - fd.sync_data().map_err(Into::into) -} - -pub(crate) unsafe fn fd_pread( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - fd: wasi::__wasi_fd_t, - iovs_ptr: wasi32::uintptr_t, - iovs_len: wasi32::size_t, - offset: wasi::__wasi_filesize_t, - nread: wasi32::uintptr_t, -) -> WasiResult<()> { - trace!( - "fd_pread(fd={:?}, iovs_ptr={:#x?}, iovs_len={:?}, offset={}, nread={:#x?})", - fd, - iovs_ptr, - iovs_len, - offset, - nread - ); - - let fd = wasi_ctx - .get_entry(fd)? - .as_descriptor(wasi::__WASI_RIGHTS_FD_READ | wasi::__WASI_RIGHTS_FD_SEEK, 0)? - .as_file()?; - - let iovs = dec_iovec_slice(memory, iovs_ptr, iovs_len)?; - - if offset > i64::max_value() as u64 { - return Err(WasiError::EIO); - } - let buf_size = iovs.iter().map(|v| v.buf_len).sum(); - let mut buf = vec![0; buf_size]; - let host_nread = hostcalls_impl::fd_pread(fd, &mut buf, offset)?; - let mut buf_offset = 0; - let mut left = host_nread; - for iov in &iovs { - if left == 0 { - break; - } - let vec_len = std::cmp::min(iov.buf_len, left); - std::slice::from_raw_parts_mut(iov.buf as *mut u8, vec_len) - .copy_from_slice(&buf[buf_offset..buf_offset + vec_len]); - buf_offset += vec_len; - left -= vec_len; - } - - trace!(" | *nread={:?}", host_nread); - - enc_usize_byref(memory, nread, host_nread) -} - -pub(crate) unsafe fn fd_pwrite( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - fd: wasi::__wasi_fd_t, - iovs_ptr: wasi32::uintptr_t, - iovs_len: wasi32::size_t, - offset: wasi::__wasi_filesize_t, - nwritten: wasi32::uintptr_t, -) -> WasiResult<()> { - trace!( - "fd_pwrite(fd={:?}, iovs_ptr={:#x?}, iovs_len={:?}, offset={}, nwritten={:#x?})", - fd, - iovs_ptr, - iovs_len, - offset, - nwritten - ); - - let fd = wasi_ctx - .get_entry(fd)? - .as_descriptor( - wasi::__WASI_RIGHTS_FD_WRITE | wasi::__WASI_RIGHTS_FD_SEEK, - 0, - )? - .as_file()?; - let iovs = dec_ciovec_slice(memory, iovs_ptr, iovs_len)?; - - if offset > i64::max_value() as u64 { - return Err(WasiError::EIO); - } - let buf_size = iovs.iter().map(|v| v.buf_len).sum(); - let mut buf = Vec::with_capacity(buf_size); - for iov in &iovs { - buf.extend_from_slice(std::slice::from_raw_parts( - iov.buf as *const u8, - iov.buf_len, - )); - } - let host_nwritten = hostcalls_impl::fd_pwrite(fd, &buf, offset)?; - - trace!(" | *nwritten={:?}", host_nwritten); - - enc_usize_byref(memory, nwritten, host_nwritten) -} - -pub(crate) unsafe fn fd_read( - wasi_ctx: &mut WasiCtx, - memory: &mut [u8], - fd: wasi::__wasi_fd_t, - iovs_ptr: wasi32::uintptr_t, - iovs_len: wasi32::size_t, - nread: wasi32::uintptr_t, -) -> WasiResult<()> { - trace!( - "fd_read(fd={:?}, iovs_ptr={:#x?}, iovs_len={:?}, nread={:#x?})", - fd, - iovs_ptr, - iovs_len, - nread - ); - - let mut iovs = dec_iovec_slice(memory, iovs_ptr, iovs_len)?; - let mut iovs: Vec = iovs - .iter_mut() - .map(|vec| host::iovec_to_host_mut(vec)) - .collect(); - - let maybe_host_nread = match wasi_ctx - .get_entry_mut(fd)? - .as_descriptor_mut(wasi::__WASI_RIGHTS_FD_READ, 0)? - { - Descriptor::OsHandle(file) => file.read_vectored(&mut iovs), - Descriptor::Stdin => io::stdin().read_vectored(&mut iovs), - _ => return Err(WasiError::EBADF), - }; - - let host_nread = maybe_host_nread?; - - trace!(" | *nread={:?}", host_nread); - - enc_usize_byref(memory, nread, host_nread) -} - -pub(crate) unsafe fn fd_renumber( - wasi_ctx: &mut WasiCtx, - _mem: &mut [u8], - from: wasi::__wasi_fd_t, - to: wasi::__wasi_fd_t, -) -> WasiResult<()> { - trace!("fd_renumber(from={:?}, to={:?})", from, to); - - if !wasi_ctx.contains_entry(from) { - return Err(WasiError::EBADF); - } - - // Don't allow renumbering over a pre-opened resource. - // TODO: Eventually, we do want to permit this, once libpreopen in - // userspace is capable of removing entries from its tables as well. - let from_fe = wasi_ctx.get_entry(from)?; - if from_fe.preopen_path.is_some() { - return Err(WasiError::ENOTSUP); - } - if let Ok(to_fe) = wasi_ctx.get_entry(to) { - if to_fe.preopen_path.is_some() { - return Err(WasiError::ENOTSUP); - } - } - - let fe = wasi_ctx.remove_entry(from)?; - wasi_ctx.insert_entry_at(to, fe); - - Ok(()) -} - -pub(crate) unsafe fn fd_seek( - wasi_ctx: &mut WasiCtx, - memory: &mut [u8], - fd: wasi::__wasi_fd_t, - offset: wasi::__wasi_filedelta_t, - whence: wasi::__wasi_whence_t, - newoffset: wasi32::uintptr_t, -) -> WasiResult<()> { - trace!( - "fd_seek(fd={:?}, offset={:?}, whence={}, newoffset={:#x?})", - fd, - offset, - wasi::whence_to_str(whence), - newoffset - ); - - let rights = if offset == 0 && whence == wasi::__WASI_WHENCE_CUR { - wasi::__WASI_RIGHTS_FD_TELL - } else { - wasi::__WASI_RIGHTS_FD_SEEK | wasi::__WASI_RIGHTS_FD_TELL - }; - let fd = wasi_ctx - .get_entry_mut(fd)? - .as_descriptor_mut(rights, 0)? - .as_file_mut()?; - - let pos = match whence { - wasi::__WASI_WHENCE_CUR => SeekFrom::Current(offset), - wasi::__WASI_WHENCE_END => SeekFrom::End(offset), - wasi::__WASI_WHENCE_SET => SeekFrom::Start(offset as u64), - _ => return Err(WasiError::EINVAL), - }; - let host_newoffset = fd.seek(pos)?; - - trace!(" | *newoffset={:?}", host_newoffset); - - enc_filesize_byref(memory, newoffset, host_newoffset) -} - -pub(crate) unsafe fn fd_tell( - wasi_ctx: &mut WasiCtx, - memory: &mut [u8], - fd: wasi::__wasi_fd_t, - newoffset: wasi32::uintptr_t, -) -> WasiResult<()> { - trace!("fd_tell(fd={:?}, newoffset={:#x?})", fd, newoffset); - - let fd = wasi_ctx - .get_entry_mut(fd)? - .as_descriptor_mut(wasi::__WASI_RIGHTS_FD_TELL, 0)? - .as_file_mut()?; - - let host_offset = fd.seek(SeekFrom::Current(0))?; - - trace!(" | *newoffset={:?}", host_offset); - - enc_filesize_byref(memory, newoffset, host_offset) -} - -pub(crate) unsafe fn fd_fdstat_get( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - fd: wasi::__wasi_fd_t, - fdstat_ptr: wasi32::uintptr_t, // *mut wasi::__wasi_fdstat_t -) -> WasiResult<()> { - trace!("fd_fdstat_get(fd={:?}, fdstat_ptr={:#x?})", fd, fdstat_ptr); - - let mut fdstat = dec_fdstat_byref(memory, fdstat_ptr)?; - let host_fd = wasi_ctx.get_entry(fd)?.as_descriptor(0, 0)?.as_os_handle(); - - let fs_flags = hostcalls_impl::fd_fdstat_get(&host_fd)?; - - let fe = wasi_ctx.get_entry(fd)?; - fdstat.fs_filetype = fe.file_type; - fdstat.fs_rights_base = fe.rights_base; - fdstat.fs_rights_inheriting = fe.rights_inheriting; - fdstat.fs_flags = fs_flags; - - trace!(" | *buf={:?}", fdstat); - - enc_fdstat_byref(memory, fdstat_ptr, fdstat) -} - -pub(crate) unsafe fn fd_fdstat_set_flags( - wasi_ctx: &WasiCtx, - _mem: &mut [u8], - fd: wasi::__wasi_fd_t, - fdflags: wasi::__wasi_fdflags_t, -) -> WasiResult<()> { - trace!("fd_fdstat_set_flags(fd={:?}, fdflags={:#x?})", fd, fdflags); - - let fd = wasi_ctx.get_entry(fd)?.as_descriptor(0, 0)?.as_os_handle(); - - hostcalls_impl::fd_fdstat_set_flags(&fd, fdflags) -} - -pub(crate) unsafe fn fd_fdstat_set_rights( - wasi_ctx: &mut WasiCtx, - _mem: &mut [u8], - fd: wasi::__wasi_fd_t, - fs_rights_base: wasi::__wasi_rights_t, - fs_rights_inheriting: wasi::__wasi_rights_t, -) -> WasiResult<()> { - trace!( - "fd_fdstat_set_rights(fd={:?}, fs_rights_base={:#x?}, fs_rights_inheriting={:#x?})", - fd, - fs_rights_base, - fs_rights_inheriting - ); - - let fe = wasi_ctx.get_entry_mut(fd)?; - if fe.rights_base & fs_rights_base != fs_rights_base - || fe.rights_inheriting & fs_rights_inheriting != fs_rights_inheriting - { - return Err(WasiError::ENOTCAPABLE); - } - fe.rights_base = fs_rights_base; - fe.rights_inheriting = fs_rights_inheriting; - - Ok(()) -} - -pub(crate) unsafe fn fd_sync( - wasi_ctx: &WasiCtx, - _mem: &mut [u8], - fd: wasi::__wasi_fd_t, -) -> WasiResult<()> { - trace!("fd_sync(fd={:?})", fd); - - let fd = wasi_ctx - .get_entry(fd)? - .as_descriptor(wasi::__WASI_RIGHTS_FD_SYNC, 0)? - .as_file()?; - fd.sync_all().map_err(Into::into) -} - -pub(crate) unsafe fn fd_write( - wasi_ctx: &mut WasiCtx, - memory: &mut [u8], - fd: wasi::__wasi_fd_t, - iovs_ptr: wasi32::uintptr_t, - iovs_len: wasi32::size_t, - nwritten: wasi32::uintptr_t, -) -> WasiResult<()> { - trace!( - "fd_write(fd={:?}, iovs_ptr={:#x?}, iovs_len={:?}, nwritten={:#x?})", - fd, - iovs_ptr, - iovs_len, - nwritten - ); - - let iovs = dec_ciovec_slice(memory, iovs_ptr, iovs_len)?; - let iovs: Vec = iovs.iter().map(|vec| host::ciovec_to_host(vec)).collect(); - - // perform unbuffered writes - let entry = wasi_ctx.get_entry_mut(fd)?; - let isatty = entry.isatty(); - let desc = entry.as_descriptor_mut(wasi::__WASI_RIGHTS_FD_WRITE, 0)?; - let host_nwritten = match desc { - Descriptor::OsHandle(file) => { - if isatty { - SandboxedTTYWriter::new(file.deref_mut()).write_vectored(&iovs)? - } else { - file.write_vectored(&iovs)? - } - } - Descriptor::Stdin => return Err(WasiError::EBADF), - Descriptor::Stdout => { - // lock for the duration of the scope - let stdout = io::stdout(); - let mut stdout = stdout.lock(); - let nwritten = if isatty { - SandboxedTTYWriter::new(&mut stdout).write_vectored(&iovs)? - } else { - stdout.write_vectored(&iovs)? - }; - stdout.flush()?; - nwritten - } - // Always sanitize stderr, even if it's not directly connected to a tty, - // because stderr is meant for diagnostics rather than binary output, - // and may be redirected to a file which could end up being displayed - // on a tty later. - Descriptor::Stderr => SandboxedTTYWriter::new(&mut io::stderr()).write_vectored(&iovs)?, - }; - - trace!(" | *nwritten={:?}", host_nwritten); - - enc_usize_byref(memory, nwritten, host_nwritten) -} - -pub(crate) unsafe fn fd_advise( - wasi_ctx: &WasiCtx, - _mem: &mut [u8], - fd: wasi::__wasi_fd_t, - offset: wasi::__wasi_filesize_t, - len: wasi::__wasi_filesize_t, - advice: wasi::__wasi_advice_t, -) -> WasiResult<()> { - trace!( - "fd_advise(fd={:?}, offset={}, len={}, advice={:?})", - fd, - offset, - len, - advice - ); - - let fd = wasi_ctx - .get_entry(fd)? - .as_descriptor(wasi::__WASI_RIGHTS_FD_ADVISE, 0)? - .as_file()?; - - hostcalls_impl::fd_advise(fd, advice, offset, len) -} - -pub(crate) unsafe fn fd_allocate( - wasi_ctx: &WasiCtx, - _mem: &mut [u8], - fd: wasi::__wasi_fd_t, - offset: wasi::__wasi_filesize_t, - len: wasi::__wasi_filesize_t, -) -> WasiResult<()> { - trace!("fd_allocate(fd={:?}, offset={}, len={})", fd, offset, len); - - let fd = wasi_ctx - .get_entry(fd)? - .as_descriptor(wasi::__WASI_RIGHTS_FD_ALLOCATE, 0)? - .as_file()?; - - let metadata = fd.metadata()?; - - let current_size = metadata.len(); - let wanted_size = offset.checked_add(len).ok_or(WasiError::E2BIG)?; - // This check will be unnecessary when rust-lang/rust#63326 is fixed - if wanted_size > i64::max_value() as u64 { - return Err(WasiError::E2BIG); - } - - if wanted_size > current_size { - fd.set_len(wanted_size).map_err(Into::into) - } else { - Ok(()) - } -} - -pub(crate) unsafe fn path_create_directory( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - dirfd: wasi::__wasi_fd_t, - path_ptr: wasi32::uintptr_t, - path_len: wasi32::size_t, -) -> WasiResult<()> { - trace!( - "path_create_directory(dirfd={:?}, path_ptr={:#x?}, path_len={})", - dirfd, - path_ptr, - path_len, - ); - - let path = dec_slice_of_u8(memory, path_ptr, path_len).and_then(helpers::path_from_slice)?; - - trace!(" | (path_ptr,path_len)='{}'", path); - - let rights = wasi::__WASI_RIGHTS_PATH_OPEN | wasi::__WASI_RIGHTS_PATH_CREATE_DIRECTORY; - let fe = wasi_ctx.get_entry(dirfd)?; - let resolved = path_get(fe, rights, 0, 0, path, false)?; - - hostcalls_impl::path_create_directory(resolved) -} - -pub(crate) unsafe fn path_link( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - old_dirfd: wasi::__wasi_fd_t, - old_flags: wasi::__wasi_lookupflags_t, - old_path_ptr: wasi32::uintptr_t, - old_path_len: wasi32::size_t, - new_dirfd: wasi::__wasi_fd_t, - new_path_ptr: wasi32::uintptr_t, - new_path_len: wasi32::size_t, -) -> WasiResult<()> { - trace!( - "path_link(old_dirfd={:?}, old_flags={:?}, old_path_ptr={:#x?}, old_path_len={}, new_dirfd={:?}, new_path_ptr={:#x?}, new_path_len={})", - old_dirfd, - old_flags, - old_path_ptr, - old_path_len, - new_dirfd, - new_path_ptr, - new_path_len, - ); - - let old_path = dec_slice_of_u8(memory, old_path_ptr, old_path_len).and_then(path_from_slice)?; - let new_path = dec_slice_of_u8(memory, new_path_ptr, new_path_len).and_then(path_from_slice)?; - - trace!(" | (old_path_ptr,old_path_len)='{}'", old_path); - trace!(" | (new_path_ptr,new_path_len)='{}'", new_path); - - let old_fe = wasi_ctx.get_entry(old_dirfd)?; - let new_fe = wasi_ctx.get_entry(new_dirfd)?; - let resolved_old = path_get( - old_fe, - wasi::__WASI_RIGHTS_PATH_LINK_SOURCE, - 0, - 0, - old_path, - false, - )?; - let resolved_new = path_get( - new_fe, - wasi::__WASI_RIGHTS_PATH_LINK_TARGET, - 0, - 0, - new_path, - false, - )?; - - hostcalls_impl::path_link(resolved_old, resolved_new) -} - -pub(crate) unsafe fn path_open( - wasi_ctx: &mut WasiCtx, - memory: &mut [u8], - dirfd: wasi::__wasi_fd_t, - dirflags: wasi::__wasi_lookupflags_t, - path_ptr: wasi32::uintptr_t, - path_len: wasi32::size_t, - oflags: wasi::__wasi_oflags_t, - fs_rights_base: wasi::__wasi_rights_t, - fs_rights_inheriting: wasi::__wasi_rights_t, - fs_flags: wasi::__wasi_fdflags_t, - fd_out_ptr: wasi32::uintptr_t, -) -> WasiResult<()> { - trace!( - "path_open(dirfd={:?}, dirflags={:?}, path_ptr={:#x?}, path_len={:?}, oflags={:#x?}, fs_rights_base={:#x?}, fs_rights_inheriting={:#x?}, fs_flags={:#x?}, fd_out_ptr={:#x?})", - dirfd, - dirflags, - path_ptr, - path_len, - oflags, - fs_rights_base, - fs_rights_inheriting, - fs_flags, - fd_out_ptr - ); - - // pre-encode fd_out_ptr to -1 in case of error in opening a path - enc_fd_byref(memory, fd_out_ptr, wasi::__wasi_fd_t::max_value())?; - - let path = dec_slice_of_u8(memory, path_ptr, path_len).and_then(path_from_slice)?; - - trace!(" | (path_ptr,path_len)='{}'", path); - - let (needed_base, needed_inheriting) = - path_open_rights(fs_rights_base, fs_rights_inheriting, oflags, fs_flags); - let fe = wasi_ctx.get_entry(dirfd)?; - let resolved = path_get( - fe, - needed_base, - needed_inheriting, - dirflags, - path, - oflags & wasi::__WASI_OFLAGS_CREAT != 0, - )?; - - // which open mode do we need? - let read = fs_rights_base & (wasi::__WASI_RIGHTS_FD_READ | wasi::__WASI_RIGHTS_FD_READDIR) != 0; - let write = fs_rights_base - & (wasi::__WASI_RIGHTS_FD_DATASYNC - | wasi::__WASI_RIGHTS_FD_WRITE - | wasi::__WASI_RIGHTS_FD_ALLOCATE - | wasi::__WASI_RIGHTS_FD_FILESTAT_SET_SIZE) - != 0; - - let fd = hostcalls_impl::path_open(resolved, read, write, oflags, fs_flags)?; - - // Determine the type of the new file descriptor and which rights contradict with this type - let (_ty, max_base, max_inheriting) = determine_type_rights(&fd)?; - let mut fe = Entry::from(fd)?; - fe.rights_base &= max_base; - fe.rights_inheriting &= max_inheriting; - let guest_fd = wasi_ctx.insert_entry(fe)?; - - trace!(" | *fd={:?}", guest_fd); - - enc_fd_byref(memory, fd_out_ptr, guest_fd) -} - -pub(crate) unsafe fn path_readlink( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - dirfd: wasi::__wasi_fd_t, - path_ptr: wasi32::uintptr_t, - path_len: wasi32::size_t, - buf_ptr: wasi32::uintptr_t, - buf_len: wasi32::size_t, - buf_used: wasi32::uintptr_t, -) -> WasiResult<()> { - trace!( - "path_readlink(dirfd={:?}, path_ptr={:#x?}, path_len={:?}, buf_ptr={:#x?}, buf_len={}, buf_used={:#x?})", - dirfd, - path_ptr, - path_len, - buf_ptr, - buf_len, - buf_used, - ); - - enc_usize_byref(memory, buf_used, 0)?; - - let path = dec_slice_of_u8(memory, path_ptr, path_len).and_then(helpers::path_from_slice)?; - - trace!(" | (path_ptr,path_len)='{}'", &path); - - let fe = wasi_ctx.get_entry(dirfd)?; - let resolved = path_get(fe, wasi::__WASI_RIGHTS_PATH_READLINK, 0, 0, &path, false)?; - - let mut buf = dec_slice_of_mut_u8(memory, buf_ptr, buf_len)?; - - let host_bufused = hostcalls_impl::path_readlink(resolved, &mut buf)?; - - trace!(" | (buf_ptr,*buf_used)={:?}", buf); - trace!(" | *buf_used={:?}", host_bufused); - - enc_usize_byref(memory, buf_used, host_bufused) -} - -pub(crate) unsafe fn path_rename( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - old_dirfd: wasi::__wasi_fd_t, - old_path_ptr: wasi32::uintptr_t, - old_path_len: wasi32::size_t, - new_dirfd: wasi::__wasi_fd_t, - new_path_ptr: wasi32::uintptr_t, - new_path_len: wasi32::size_t, -) -> WasiResult<()> { - trace!( - "path_rename(old_dirfd={:?}, old_path_ptr={:#x?}, old_path_len={:?}, new_dirfd={:?}, new_path_ptr={:#x?}, new_path_len={:?})", - old_dirfd, - old_path_ptr, - old_path_len, - new_dirfd, - new_path_ptr, - new_path_len, - ); - - let old_path = dec_slice_of_u8(memory, old_path_ptr, old_path_len).and_then(path_from_slice)?; - let new_path = dec_slice_of_u8(memory, new_path_ptr, new_path_len).and_then(path_from_slice)?; - - trace!(" | (old_path_ptr,old_path_len)='{}'", old_path); - trace!(" | (new_path_ptr,new_path_len)='{}'", new_path); - - let old_fe = wasi_ctx.get_entry(old_dirfd)?; - let new_fe = wasi_ctx.get_entry(new_dirfd)?; - let resolved_old = path_get( - old_fe, - wasi::__WASI_RIGHTS_PATH_RENAME_SOURCE, - 0, - 0, - old_path, - true, - )?; - let resolved_new = path_get( - new_fe, - wasi::__WASI_RIGHTS_PATH_RENAME_TARGET, - 0, - 0, - new_path, - true, - )?; - - tracing::debug!("path_rename resolved_old={:?}", resolved_old); - tracing::debug!("path_rename resolved_new={:?}", resolved_new); - - hostcalls_impl::path_rename(resolved_old, resolved_new) -} - -pub(crate) unsafe fn fd_filestat_get( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - fd: wasi::__wasi_fd_t, - filestat_ptr: wasi32::uintptr_t, -) -> WasiResult<()> { - trace!( - "fd_filestat_get(fd={:?}, filestat_ptr={:#x?})", - fd, - filestat_ptr - ); - - let fd = wasi_ctx.get_entry(fd)?.as_descriptor(0, 0)?.as_file()?; - let host_filestat = hostcalls_impl::fd_filestat_get(fd)?; - - trace!(" | *filestat_ptr={:?}", host_filestat); - - enc_filestat_byref(memory, filestat_ptr, host_filestat) -} - -pub(crate) unsafe fn fd_filestat_set_times( - wasi_ctx: &WasiCtx, - _mem: &mut [u8], - fd: wasi::__wasi_fd_t, - st_atim: wasi::__wasi_timestamp_t, - st_mtim: wasi::__wasi_timestamp_t, - fst_flags: wasi::__wasi_fstflags_t, -) -> WasiResult<()> { - trace!( - "fd_filestat_set_times(fd={:?}, st_atim={}, st_mtim={}, fst_flags={:#x?})", - fd, - st_atim, - st_mtim, - fst_flags - ); - - let fd = wasi_ctx - .get_entry(fd)? - .as_descriptor(wasi::__WASI_RIGHTS_FD_FILESTAT_SET_TIMES, 0)? - .as_file()?; - - fd_filestat_set_times_impl(fd, st_atim, st_mtim, fst_flags) -} - -pub(crate) fn fd_filestat_set_times_impl( - fd: &File, - st_atim: wasi::__wasi_timestamp_t, - st_mtim: wasi::__wasi_timestamp_t, - fst_flags: wasi::__wasi_fstflags_t, -) -> WasiResult<()> { - 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 atim = if set_atim { - let time = UNIX_EPOCH + Duration::from_nanos(st_atim); - Some(FileTime::from_system_time(time)) - } else if set_atim_now { - let time = SystemTime::now(); - Some(FileTime::from_system_time(time)) - } else { - None - }; - - let mtim = if set_mtim { - let time = UNIX_EPOCH + Duration::from_nanos(st_mtim); - Some(FileTime::from_system_time(time)) - } else if set_mtim_now { - let time = SystemTime::now(); - Some(FileTime::from_system_time(time)) - } else { - None - }; - set_file_handle_times(fd, atim, mtim).map_err(Into::into) -} - -pub(crate) unsafe fn fd_filestat_set_size( - wasi_ctx: &WasiCtx, - _mem: &mut [u8], - fd: wasi::__wasi_fd_t, - st_size: wasi::__wasi_filesize_t, -) -> WasiResult<()> { - trace!("fd_filestat_set_size(fd={:?}, st_size={})", fd, st_size); - - let fd = wasi_ctx - .get_entry(fd)? - .as_descriptor(wasi::__WASI_RIGHTS_FD_FILESTAT_SET_SIZE, 0)? - .as_file()?; - - // This check will be unnecessary when rust-lang/rust#63326 is fixed - if st_size > i64::max_value() as u64 { - return Err(WasiError::E2BIG); - } - fd.set_len(st_size).map_err(Into::into) -} - -pub(crate) unsafe fn path_filestat_get( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - dirfd: wasi::__wasi_fd_t, - dirflags: wasi::__wasi_lookupflags_t, - path_ptr: wasi32::uintptr_t, - path_len: wasi32::size_t, - filestat_ptr: wasi32::uintptr_t, -) -> WasiResult<()> { - trace!( - "path_filestat_get(dirfd={:?}, dirflags={:?}, path_ptr={:#x?}, path_len={}, filestat_ptr={:#x?})", - dirfd, - dirflags, - path_ptr, - path_len, - filestat_ptr - ); - - let path = dec_slice_of_u8(memory, path_ptr, path_len).and_then(path_from_slice)?; - - trace!(" | (path_ptr,path_len)='{}'", path); - - let fe = wasi_ctx.get_entry(dirfd)?; - let resolved = path_get( - fe, - wasi::__WASI_RIGHTS_PATH_FILESTAT_GET, - 0, - dirflags, - path, - false, - )?; - let host_filestat = hostcalls_impl::path_filestat_get(resolved, dirflags)?; - - trace!(" | *filestat_ptr={:?}", host_filestat); - - enc_filestat_byref(memory, filestat_ptr, host_filestat) -} - -pub(crate) unsafe fn path_filestat_set_times( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - dirfd: wasi::__wasi_fd_t, - dirflags: wasi::__wasi_lookupflags_t, - path_ptr: wasi32::uintptr_t, - path_len: wasi32::size_t, - st_atim: wasi::__wasi_timestamp_t, - st_mtim: wasi::__wasi_timestamp_t, - fst_flags: wasi::__wasi_fstflags_t, -) -> WasiResult<()> { - trace!( - "path_filestat_set_times(dirfd={:?}, dirflags={:?}, path_ptr={:#x?}, path_len={}, st_atim={}, st_mtim={}, fst_flags={:#x?})", - dirfd, - dirflags, - path_ptr, - path_len, - st_atim, st_mtim, - fst_flags - ); - - let path = dec_slice_of_u8(memory, path_ptr, path_len).and_then(path_from_slice)?; - - trace!(" | (path_ptr,path_len)='{}'", path); - - let fe = wasi_ctx.get_entry(dirfd)?; - let resolved = path_get( - fe, - wasi::__WASI_RIGHTS_PATH_FILESTAT_SET_TIMES, - 0, - dirflags, - path, - false, - )?; - - hostcalls_impl::path_filestat_set_times(resolved, dirflags, st_atim, st_mtim, fst_flags) -} - -pub(crate) unsafe fn path_symlink( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - old_path_ptr: wasi32::uintptr_t, - old_path_len: wasi32::size_t, - dirfd: wasi::__wasi_fd_t, - new_path_ptr: wasi32::uintptr_t, - new_path_len: wasi32::size_t, -) -> WasiResult<()> { - trace!( - "path_symlink(old_path_ptr={:#x?}, old_path_len={}, dirfd={:?}, new_path_ptr={:#x?}, new_path_len={})", - old_path_ptr, - old_path_len, - dirfd, - new_path_ptr, - new_path_len - ); - - let old_path = dec_slice_of_u8(memory, old_path_ptr, old_path_len).and_then(path_from_slice)?; - let new_path = dec_slice_of_u8(memory, new_path_ptr, new_path_len).and_then(path_from_slice)?; - - trace!(" | (old_path_ptr,old_path_len)='{}'", old_path); - trace!(" | (new_path_ptr,new_path_len)='{}'", new_path); - - let fe = wasi_ctx.get_entry(dirfd)?; - let resolved_new = path_get(fe, wasi::__WASI_RIGHTS_PATH_SYMLINK, 0, 0, new_path, true)?; - - hostcalls_impl::path_symlink(old_path, resolved_new) -} - -pub(crate) unsafe fn path_unlink_file( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - dirfd: wasi::__wasi_fd_t, - path_ptr: wasi32::uintptr_t, - path_len: wasi32::size_t, -) -> WasiResult<()> { - trace!( - "path_unlink_file(dirfd={:?}, path_ptr={:#x?}, path_len={})", - dirfd, - path_ptr, - path_len - ); - - let path = dec_slice_of_u8(memory, path_ptr, path_len).and_then(path_from_slice)?; - - trace!(" | (path_ptr,path_len)='{}'", path); - - let fe = wasi_ctx.get_entry(dirfd)?; - let resolved = path_get(fe, wasi::__WASI_RIGHTS_PATH_UNLINK_FILE, 0, 0, path, false)?; - - hostcalls_impl::path_unlink_file(resolved) -} - -pub(crate) unsafe fn path_remove_directory( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - dirfd: wasi::__wasi_fd_t, - path_ptr: wasi32::uintptr_t, - path_len: wasi32::size_t, -) -> WasiResult<()> { - trace!( - "path_remove_directory(dirfd={:?}, path_ptr={:#x?}, path_len={})", - dirfd, - path_ptr, - path_len - ); - - let path = dec_slice_of_u8(memory, path_ptr, path_len).and_then(path_from_slice)?; - - trace!(" | (path_ptr,path_len)='{}'", path); - - let fe = wasi_ctx.get_entry(dirfd)?; - let resolved = path_get( - fe, - wasi::__WASI_RIGHTS_PATH_REMOVE_DIRECTORY, - 0, - 0, - path, - true, - )?; - - tracing::debug!("path_remove_directory resolved={:?}", resolved); - - hostcalls_impl::path_remove_directory(resolved) -} - -pub(crate) unsafe fn fd_prestat_get( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - fd: wasi::__wasi_fd_t, - prestat_ptr: wasi32::uintptr_t, -) -> WasiResult<()> { - trace!( - "fd_prestat_get(fd={:?}, prestat_ptr={:#x?})", - fd, - prestat_ptr - ); - - // TODO: should we validate any rights here? - let fe = wasi_ctx.get_entry(fd)?; - let po_path = fe.preopen_path.as_ref().ok_or(WasiError::ENOTSUP)?; - if fe.file_type != wasi::__WASI_FILETYPE_DIRECTORY { - return Err(WasiError::ENOTDIR); - } - - let path = host_impl::path_from_host(po_path.as_os_str())?; - - enc_prestat_byref( - memory, - prestat_ptr, - host::__wasi_prestat_t { - tag: wasi::__WASI_PREOPENTYPE_DIR, - u: host::__wasi_prestat_u_t { - dir: host::__wasi_prestat_dir_t { - pr_name_len: path.len(), - }, - }, - }, - ) -} - -pub(crate) unsafe fn fd_prestat_dir_name( - wasi_ctx: &WasiCtx, - memory: &mut [u8], - fd: wasi::__wasi_fd_t, - path_ptr: wasi32::uintptr_t, - path_len: wasi32::size_t, -) -> WasiResult<()> { - trace!( - "fd_prestat_dir_name(fd={:?}, path_ptr={:#x?}, path_len={})", - fd, - path_ptr, - path_len - ); - - // TODO: should we validate any rights here? - let fe = wasi_ctx.get_entry(fd)?; - let po_path = fe.preopen_path.as_ref().ok_or(WasiError::ENOTSUP)?; - if fe.file_type != wasi::__WASI_FILETYPE_DIRECTORY { - return Err(WasiError::ENOTDIR); - } - - let path = host_impl::path_from_host(po_path.as_os_str())?; - - if path.len() > dec_usize(path_len) { - return Err(WasiError::ENAMETOOLONG); - } - - trace!(" | (path_ptr,path_len)='{}'", path); - - enc_slice_of_u8(memory, path.as_bytes(), path_ptr) -} - -pub(crate) unsafe fn fd_readdir( - wasi_ctx: &mut WasiCtx, - memory: &mut [u8], - fd: wasi::__wasi_fd_t, - buf: wasi32::uintptr_t, - buf_len: wasi32::size_t, - cookie: wasi::__wasi_dircookie_t, - buf_used: wasi32::uintptr_t, -) -> WasiResult<()> { - trace!( - "fd_readdir(fd={:?}, buf={:#x?}, buf_len={}, cookie={:#x?}, buf_used={:#x?})", - fd, - buf, - buf_len, - cookie, - buf_used, - ); - - enc_usize_byref(memory, buf_used, 0)?; - - let file = wasi_ctx - .get_entry_mut(fd)? - .as_descriptor_mut(wasi::__WASI_RIGHTS_FD_READDIR, 0)? - .as_file_mut()?; - let mut host_buf = dec_slice_of_mut_u8(memory, buf, buf_len)?; - - trace!(" | (buf,buf_len)={:?}", host_buf); - - let iter = hostcalls_impl::fd_readdir(file, cookie)?; - let mut host_bufused = 0; - for dirent in iter { - let dirent_raw = dirent?.to_wasi_raw()?; - let offset = dirent_raw.len(); - if host_buf.len() < offset { - break; - } else { - host_buf[0..offset].copy_from_slice(&dirent_raw); - host_bufused += offset; - host_buf = &mut host_buf[offset..]; - } - } - - trace!(" | *buf_used={:?}", host_bufused); - - enc_usize_byref(memory, buf_used, host_bufused) -} diff --git a/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/fs_helpers.rs b/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/fs_helpers.rs deleted file mode 100644 index 59a94d1bec..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/fs_helpers.rs +++ /dev/null @@ -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 { - 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("."), - }); - } - } - } -} diff --git a/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/misc.rs b/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/misc.rs deleted file mode 100644 index 7016fb2162..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/misc.rs +++ /dev/null @@ -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 = 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 { - 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") -} diff --git a/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/mod.rs b/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/mod.rs deleted file mode 100644 index 924c8c1ba9..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/mod.rs +++ /dev/null @@ -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::*; diff --git a/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/sock.rs b/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/sock.rs deleted file mode 100644 index 0e92ce4e21..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/sock.rs +++ /dev/null @@ -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") -} diff --git a/crates/wasi-common/src/old/snapshot_0/memory.rs b/crates/wasi-common/src/old/snapshot_0/memory.rs deleted file mode 100644 index 324d8712cc..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/memory.rs +++ /dev/null @@ -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(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::() != 0 { - return Err(WasiError::EINVAL); - } - - dec_ptr(memory, ptr, size_of::()).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::() != 0 { - return Err(WasiError::EINVAL); - } - - dec_ptr_mut(memory, ptr, size_of::()).map(|p| unsafe { &mut *(p as *mut T) }) -} - -/// This function does not perform endianness conversions! -fn dec_raw_byref(memory: &[u8], ptr: wasi32::uintptr_t) -> WasiResult { - dec_ptr_to::(memory, ptr).map(|p| unsafe { ptr::read(p) }) -} - -/// This function does not perform endianness conversions! -fn enc_raw_byref(memory: &mut [u8], ptr: wasi32::uintptr_t, t: T) -> WasiResult<()> { - dec_ptr_to_mut::(memory, ptr).map(|p| unsafe { ptr::write(p, t) }) -} - -pub(crate) fn dec_int_byref(memory: &[u8], ptr: wasi32::uintptr_t) -> WasiResult -where - T: FromLe, -{ - dec_raw_byref::(memory, ptr).map(|i| FromLe::from_le(i)) -} - -pub(crate) fn enc_int_byref(memory: &mut [u8], ptr: wasi32::uintptr_t, t: T) -> WasiResult<()> { - enc_raw_byref::(memory, ptr, t) -} - -fn check_slice_of(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::() != 0 { - return Err(WasiError::EINVAL); - } - let len = dec_usize(len); - let len_bytes = if let Some(len) = size_of::().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::(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::(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::() != 0 { - return Err(WasiError::EINVAL); - } - // check that length doesn't overflow - let len_bytes = if let Some(len) = size_of::().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::(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::(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::(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::(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 { - dec_int_byref::(memory, ptr) - } - - pub(crate) fn $enc_byref( - memory: &mut [u8], - ptr: wasi32::uintptr_t, - x: wasi::$ty, - ) -> WasiResult<()> { - enc_int_byref::(memory, ptr, x.to_le()) - } - }; -} - -pub(crate) fn dec_ciovec_slice( - memory: &[u8], - ptr: wasi32::uintptr_t, - len: wasi32::size_t, -) -> WasiResult> { - let raw_slice = dec_raw_slice_of::(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> { - let raw_slice = dec_raw_slice_of::(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 { - let raw = dec_raw_byref::(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::(memory, filestat_ptr, raw) -} - -pub(crate) fn dec_fdstat_byref( - memory: &mut [u8], - fdstat_ptr: wasi32::uintptr_t, -) -> WasiResult { - let raw = dec_raw_byref::(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::(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 { - let raw = dec_raw_byref::(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::(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::(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> { - let raw_input_slice = - dec_raw_slice_of::(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::>>() -} - -pub(crate) fn enc_events( - memory: &mut [u8], - output: wasi32::uintptr_t, - nsubscriptions: wasi32::size_t, - events: Vec, -) -> WasiResult<()> { - let mut raw_output_iter = - dec_raw_slice_of_mut::(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); diff --git a/crates/wasi-common/src/old/snapshot_0/mod.rs b/crates/wasi-common/src/old/snapshot_0/mod.rs deleted file mode 100644 index 93d8697303..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/mod.rs +++ /dev/null @@ -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}; diff --git a/crates/wasi-common/src/old/snapshot_0/sys/mod.rs b/crates/wasi-common/src/old/snapshot_0/sys/mod.rs deleted file mode 100644 index 6d8b3fd82f..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/sys/mod.rs +++ /dev/null @@ -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"); - } -} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/host_impl.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/host_impl.rs deleted file mode 100644 index 37ed2e4e38..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/host_impl.rs +++ /dev/null @@ -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::try_from(dev).map_err(Into::into) -} - -pub(crate) fn stino_from_nix(ino: libc::ino_t) -> WasiResult { - wasi::__wasi_device_t::try_from(ino).map_err(Into::into) -} - -pub(crate) fn stnlink_from_nix(nlink: libc::nlink_t) -> WasiResult { - Ok(nlink.into()) -} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/hostcalls_impl.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/hostcalls_impl.rs deleted file mode 100644 index ca59b30d49..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/hostcalls_impl.rs +++ /dev/null @@ -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> { - 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()) - } -} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/mod.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/mod.rs deleted file mode 100644 index b47019d43d..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub(crate) mod host_impl; -pub(crate) mod hostcalls_impl; -pub(crate) mod oshandle; diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/oshandle.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/oshandle.rs deleted file mode 100644 index 70d1a8c4e8..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/oshandle.rs +++ /dev/null @@ -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>, -} - -impl From 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 - } -} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/emscripten/host_impl.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/emscripten/host_impl.rs deleted file mode 100644 index 2f9092cd4d..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/emscripten/host_impl.rs +++ /dev/null @@ -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 { - Ok(wasi::__wasi_device_t::from(dev)) -} - -pub(crate) fn stino_from_nix(ino: libc::ino_t) -> WasiResult { - Ok(wasi::__wasi_device_t::from(ino)) -} - -pub(crate) fn stnlink_from_nix(nlink: libc::nlink_t) -> WasiResult { - Ok(nlink) -} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/emscripten/mod.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/emscripten/mod.rs deleted file mode 100644 index 0b3cfa38fe..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/emscripten/mod.rs +++ /dev/null @@ -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; diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/entry_impl.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/entry_impl.rs deleted file mode 100644 index 325c627f60..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/entry_impl.rs +++ /dev/null @@ -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: &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: &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)) -} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/host_impl.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/host_impl.rs deleted file mode 100644 index c6ff0fac3e..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/host_impl.rs +++ /dev/null @@ -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 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 { - use std::convert::TryInto; - - fn filestat_to_timestamp(secs: u64, nsecs: u64) -> WasiResult { - 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: S) -> WasiResult { - helpers::path_from_slice(s.as_ref().as_bytes()).map(String::from) -} - -impl From 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? - } - } -} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/fs.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/fs.rs deleted file mode 100644 index f618dfca8c..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/fs.rs +++ /dev/null @@ -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 { - file.read_at(buf, offset).map_err(Into::into) -} - -pub(crate) fn fd_pwrite( - file: &File, - buf: &[u8], - offset: wasi::__wasi_filesize_t, -) -> WasiResult { - file.write_at(buf, offset).map_err(Into::into) -} - -pub(crate) fn fd_fdstat_get(fd: &File) -> WasiResult { - 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 { - 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 { - 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 { - 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 { - 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> + '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()?, - }) - })) -} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/fs_helpers.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/fs_helpers.rs deleted file mode 100644 index 4d812ebe02..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/fs_helpers.rs +++ /dev/null @@ -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 { - 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 { - 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) -} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/misc.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/misc.rs deleted file mode 100644 index 015f942efe..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/misc.rs +++ /dev/null @@ -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 { - // 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 { - 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 { - 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, - fd_events: Vec, - events: &mut Vec, -) -> 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, -) { - 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, yanix::poll::PollFd)>, - events: &mut Vec, -) -> 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(()) -} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/mod.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/mod.rs deleted file mode 100644 index 69a0d25ae2..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/mod.rs +++ /dev/null @@ -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::*; diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/host_impl.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/host_impl.rs deleted file mode 100644 index 0bdc802439..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/host_impl.rs +++ /dev/null @@ -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 { - Ok(wasi::__wasi_device_t::from(dev)) -} - -pub(crate) fn stino_from_nix(ino: libc::ino_t) -> WasiResult { - Ok(wasi::__wasi_device_t::from(ino)) -} - -pub(crate) fn stnlink_from_nix(nlink: libc::nlink_t) -> WasiResult { - nlink.try_into().map_err(Into::into) -} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/hostcalls_impl.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/hostcalls_impl.rs deleted file mode 100644 index 6489b5bf30..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/hostcalls_impl.rs +++ /dev/null @@ -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> { - // 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`. - Ok(Box::new(Dir::from(fd)?)) - } -} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/mod.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/mod.rs deleted file mode 100644 index b47019d43d..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub(crate) mod host_impl; -pub(crate) mod hostcalls_impl; -pub(crate) mod oshandle; diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/oshandle.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/oshandle.rs deleted file mode 100644 index 8104d9d5e3..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/oshandle.rs +++ /dev/null @@ -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 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 - } -} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/mod.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/mod.rs deleted file mode 100644 index 959545e370..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/mod.rs +++ /dev/null @@ -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 { - OpenOptions::new().read(true).write(true).open("/dev/null") -} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/windows/entry_impl.rs b/crates/wasi-common/src/old/snapshot_0/sys/windows/entry_impl.rs deleted file mode 100644 index 9e2251b5ba..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/sys/windows/entry_impl.rs +++ /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 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: &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: &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)) -} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/windows/host_impl.rs b/crates/wasi-common/src/old/snapshot_0/sys/windows/host_impl.rs deleted file mode 100644 index 1ebdd6bc48..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/sys/windows/host_impl.rs +++ /dev/null @@ -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 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 { - Ok(winx::file::get_fileinfo(file)?.nNumberOfLinks.into()) -} - -fn device_id(file: &File) -> io::Result { - Ok(winx::file::get_fileinfo(file)?.dwVolumeSerialNumber.into()) -} - -pub(crate) fn file_serial_no(file: &File) -> io::Result { - 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 { - winx::file::change_time(file) -} - -fn systemtime_to_timestamp(st: SystemTime) -> WasiResult { - 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 { - 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: S) -> WasiResult { - let vec: Vec = s.as_ref().encode_wide().collect(); - String::from_utf16(&vec).map_err(|_| WasiError::EILSEQ) -} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/windows/hostcalls_impl/fs.rs b/crates/wasi-common/src/old/snapshot_0/sys/windows/hostcalls_impl/fs.rs deleted file mode 100644 index d1830d8273..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/sys/windows/hostcalls_impl/fs.rs +++ /dev/null @@ -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 { - // 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 { - // 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 { - 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 { - write_at(file, buf, offset).map_err(Into::into) -} - -pub(crate) fn fd_fdstat_get(fd: &File) -> WasiResult { - 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 { - 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>( - path: P, - name: &str, - cookie: wasi::__wasi_dircookie_t, -) -> WasiResult { - 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>> { - 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 { - 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> { - 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 { - host_impl::filestat_from_win(file) -} - -pub(crate) fn path_filestat_get( - resolved: PathGet, - dirflags: wasi::__wasi_lookupflags_t, -) -> WasiResult { - 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) -} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/windows/hostcalls_impl/fs_helpers.rs b/crates/wasi-common/src/old/snapshot_0/sys/windows/hostcalls_impl/fs_helpers.rs deleted file mode 100644 index 7403a1f428..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/sys/windows/hostcalls_impl/fs_helpers.rs +++ /dev/null @@ -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; -} - -impl PathGetExt for PathGet { - fn concatenate(&self) -> WasiResult { - 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 { - 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 { - 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>(path: P) -> OsString { - let path: Vec = 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>(dirfd: &File, path: P) -> WasiResult { - 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) -} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/windows/hostcalls_impl/misc.rs b/crates/wasi-common/src/old/snapshot_0/sys/windows/hostcalls_impl/misc.rs deleted file mode 100644 index 395adce4f0..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/sys/windows/hostcalls_impl/misc.rs +++ /dev/null @@ -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 { - 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 { - 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, - fd_events: Vec, - events: &mut Vec, -) -> WasiResult> { - 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 { - SystemTime::now() - .duration_since(UNIX_EPOCH) - .map_err(|_| WasiError::EFAULT) -} - -fn get_proc_cputime() -> WasiResult { - Ok(ProcessTime::try_now()?.as_duration()) -} - -fn get_thread_cputime() -> WasiResult { - 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 -} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/windows/hostcalls_impl/mod.rs b/crates/wasi-common/src/old/snapshot_0/sys/windows/hostcalls_impl/mod.rs deleted file mode 100644 index c8c383831d..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/sys/windows/hostcalls_impl/mod.rs +++ /dev/null @@ -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::*; diff --git a/crates/wasi-common/src/old/snapshot_0/sys/windows/mod.rs b/crates/wasi-common/src/old/snapshot_0/sys/windows/mod.rs deleted file mode 100644 index 5fd95f92c8..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/sys/windows/mod.rs +++ /dev/null @@ -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 { - OpenOptions::new().read(true).write(true).open("NUL") -} diff --git a/crates/wasi-common/src/old/snapshot_0/wasi.rs b/crates/wasi-common/src/old/snapshot_0/wasi.rs deleted file mode 100644 index 99811717fb..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/wasi.rs +++ /dev/null @@ -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 = Result; - -#[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 for WasiError { - fn from(_err: std::convert::Infallible) -> Self { - unreachable!() - } -} - -impl From for WasiError { - fn from(_err: std::num::TryFromIntError) -> Self { - Self::EOVERFLOW - } -} - -impl From 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 - } -} diff --git a/crates/wasi-common/src/old/snapshot_0/wasi32.rs b/crates/wasi-common/src/old/snapshot_0/wasi32.rs deleted file mode 100644 index 76f1973198..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/wasi32.rs +++ /dev/null @@ -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"); diff --git a/crates/wasi-common/src/snapshots/mod.rs b/crates/wasi-common/src/snapshots/mod.rs index 612db9df66..ed30aed53d 100644 --- a/crates/wasi-common/src/snapshots/mod.rs +++ b/crates/wasi-common/src/snapshots/mod.rs @@ -1 +1,2 @@ pub mod wasi_snapshot_preview1; +pub mod wasi_unstable; diff --git a/crates/wasi-common/src/snapshots/wasi_snapshot_preview1.rs b/crates/wasi-common/src/snapshots/wasi_snapshot_preview1.rs index 4002a58d40..f5b80eb5a1 100644 --- a/crates/wasi-common/src/snapshots/wasi_snapshot_preview1.rs +++ b/crates/wasi-common/src/snapshots/wasi_snapshot_preview1.rs @@ -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, 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 { + 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> { let mut events = Vec::new(); let mut timeout: Option = 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, 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 { - unimplemented!("sock_send") - } - - fn sock_shutdown(&self, _fd: types::Fd, _how: types::Sdflags) -> Result<()> { - unimplemented!("sock_shutdown") + Ok(events) } } diff --git a/crates/wasi-common/src/snapshots/wasi_unstable.rs b/crates/wasi-common/src/snapshots/wasi_unstable.rs new file mode 100644 index 0000000000..6c05c21427 --- /dev/null +++ b/crates/wasi-common/src/snapshots/wasi_unstable.rs @@ -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 { + tracing::debug!("Error: {:?}", e); + Ok(e.into()) + } +} + +impl From for Errno { + fn from(e: Error) -> Errno { + types_new::Errno::from(e).into() + } +} + +impl From 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 { + WasiSnapshotPreview1::clock_res_get(self, id.into()) + } + + fn clock_time_get(&self, id: Clockid, precision: Timestamp) -> Result { + 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 { + 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 { + 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 { + WasiSnapshotPreview1::fd_pread(self, fd.into(), &cvt_iovec(iovs), offset) + } + + fn fd_prestat_get(&self, fd: Fd) -> Result { + 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 { + WasiSnapshotPreview1::fd_pwrite(self, fd.into(), &cvt_ciovec(iovs), offset) + } + + fn fd_read<'a>(&self, fd: Fd, iovs: &IovecArray<'a>) -> Result { + 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 { + 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 { + 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 { + WasiSnapshotPreview1::fd_tell(self, fd.into()) + } + + fn fd_write<'a>(&self, fd: Fd, iovs: &CiovecArray<'a>) -> Result { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 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 for types_new::Fd { + fn from(fd: Fd) -> types_new::Fd { + types_new::Fd::from(u32::from(fd)) + } +} + +impl From for Fd { + fn from(fd: types_new::Fd) -> Fd { + Fd::from(u32::from(fd)) + } +} + +impl From 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 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 for types_new::Rights { + fn from(e: Rights) -> types_new::Rights { + assert_rights_same(); + u64::from(e).try_into().unwrap() + } +} + +impl From for Rights { + fn from(e: types_new::Rights) -> Rights { + assert_rights_same(); + u64::from(e).try_into().unwrap() + } +} + +impl From 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 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 for types_new::Fdflags { + fn from(e: Fdflags) -> types_new::Fdflags { + assert_fdflags_same(); + u16::from(e).try_into().unwrap() + } +} + +impl From for Fdflags { + fn from(e: types_new::Fdflags) -> Fdflags { + assert_fdflags_same(); + u16::from(e).try_into().unwrap() + } +} + +impl TryFrom for Filestat { + type Error = Error; + + fn try_from(e: types_new::Filestat) -> Result { + 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 for types_new::Fstflags { + fn from(e: Fstflags) -> types_new::Fstflags { + assert_fstflags_same(); + u16::from(e).try_into().unwrap() + } +} + +impl From for Fstflags { + fn from(e: types_new::Fstflags) -> Fstflags { + assert_fstflags_same(); + u16::from(e).try_into().unwrap() + } +} + +impl From for Prestat { + fn from(e: types_new::Prestat) -> Prestat { + match e { + types_new::Prestat::Dir(d) => Prestat::Dir(d.into()), + } + } +} + +impl From for PrestatDir { + fn from(e: types_new::PrestatDir) -> PrestatDir { + PrestatDir { + pr_name_len: e.pr_name_len, + } + } +} + +impl From 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 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 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 for types_new::Sdflags { + fn from(e: Sdflags) -> types_new::Sdflags { + assert_sdflags_same(); + u8::from(e).try_into().unwrap() + } +} + +impl From 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 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 for Roflags { + fn from(e: types_new::Roflags) -> Roflags { + assert_roflags_same(); + u16::from(e).try_into().unwrap() + } +} + +impl From for types_new::Subscription { + fn from(e: Subscription) -> types_new::Subscription { + types_new::Subscription { + userdata: e.userdata, + u: e.u.into(), + } + } +} + +impl From 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 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 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 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 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 for EventFdReadwrite { + fn from(e: types_new::EventFdReadwrite) -> EventFdReadwrite { + EventFdReadwrite { + nbytes: e.nbytes, + flags: e.flags.into(), + } + } +} + +impl From 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() + } +} diff --git a/crates/wasi-common/wig/Cargo.toml b/crates/wasi-common/wig/Cargo.toml deleted file mode 100644 index 2d30e54405..0000000000 --- a/crates/wasi-common/wig/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "wig" -version = "0.21.0" -authors = ["Dan Gohman "] -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" } diff --git a/crates/wasi-common/wig/LICENSE b/crates/wasi-common/wig/LICENSE deleted file mode 100644 index f9d81955f4..0000000000 --- a/crates/wasi-common/wig/LICENSE +++ /dev/null @@ -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. - diff --git a/crates/wasi-common/wig/src/hostcalls.rs b/crates/wasi-common/wig/src/hostcalls.rs deleted file mode 100644 index 359792a1bf..0000000000 --- a/crates/wasi-common/wig/src/hostcalls.rs +++ /dev/null @@ -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}) - } - } -} diff --git a/crates/wasi-common/wig/src/lib.rs b/crates/wasi-common/wig/src/lib.rs deleted file mode 100644 index d0fd3f41e0..0000000000 --- a/crates/wasi-common/wig/src/lib.rs +++ /dev/null @@ -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() -} diff --git a/crates/wasi-common/wig/src/raw_types.rs b/crates/wasi-common/wig/src/raw_types.rs deleted file mode 100644 index 6105e05e6e..0000000000 --- a/crates/wasi-common/wig/src/raw_types.rs +++ /dev/null @@ -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, - } -} diff --git a/crates/wasi-common/wig/src/utils.rs b/crates/wasi-common/wig/src/utils.rs deleted file mode 100644 index e41da18d2f..0000000000 --- a/crates/wasi-common/wig/src/utils.rs +++ /dev/null @@ -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(), - } - ) -} diff --git a/crates/wasi-common/wig/src/wasi.rs b/crates/wasi-common/wig/src/wasi.rs deleted file mode 100644 index c052ed6609..0000000000 --- a/crates/wasi-common/wig/src/wasi.rs +++ /dev/null @@ -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(()) - } - } - } -} diff --git a/crates/wasi/Cargo.toml b/crates/wasi/Cargo.toml index 2ddad35e43..a0704e7e94 100644 --- a/crates/wasi/Cargo.toml +++ b/crates/wasi/Cargo.toml @@ -17,7 +17,6 @@ tracing = "0.1.15" wasi-common = { path = "../wasi-common", version = "0.21.0" } wasmtime = { path = "../wasmtime", version = "0.21.0", default-features = false } wasmtime-runtime = { path = "../runtime", version = "0.21.0" } -wig = { path = "../wasi-common/wig", version = "0.21.0" } wiggle = { path = "../wiggle", version = "0.21.0" } wasmtime-wiggle = { path = "../wiggle/wasmtime", version = "0.21.0" } diff --git a/crates/wasi/src/old/snapshot_0.rs b/crates/wasi/src/old/snapshot_0.rs index 578c8cfb9a..2b054c011b 100644 --- a/crates/wasi/src/old/snapshot_0.rs +++ b/crates/wasi/src/old/snapshot_0.rs @@ -1,13 +1,41 @@ -use wasi_common::old::snapshot_0::hostcalls; -pub use wasi_common::old::snapshot_0::{WasiCtx, WasiCtxBuilder}; +pub use wasi_common::virtfs; +pub use wasi_common::{WasiCtx, WasiCtxBuilder}; + +use crate::wasi_proc_exit; // Defines a `struct Wasi` with member fields and appropriate APIs for dealing // with all the various WASI exports. -wig::define_wasi_struct!("phases/old/snapshot_0/witx/wasi_unstable.witx"); +wasmtime_wiggle::wasmtime_integration!({ + // The wiggle code to integrate with lives here: + target: wasi_common::snapshots::wasi_unstable, + // This must be the same witx document as used above. This should be + // ensured by the `WASI_ROOT` env variable, which is set in wasi-common's + // `build.rs`. + witx: ["$WASI_ROOT/phases/old/snapshot_0/witx/wasi_unstable.witx"], + // This must be the same ctx type as used for the target: + ctx: WasiCtx, + // This macro will emit a struct to represent the instance, + // with this name and docs: + modules: { wasi_unstable => + { name: Wasi, + docs: "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.", + // Don't use the wiggle generated code to implement proc_exit, we need + // to hook directly into the runtime there: + function_override: { + proc_exit => wasi_proc_exit + } + }, + }, + // Error to return when caller module is missing memory export: + missing_memory: { wasi_common::wasi::types::Errno::Inval }, +}); pub fn is_wasi_module(name: &str) -> bool { - // FIXME: this should be more conservative, but while WASI is in flux and - // we're figuring out how to support multiple revisions, this should do the - // trick. - name.starts_with("wasi") + crate::is_wasi_module(name) } diff --git a/scripts/publish.rs b/scripts/publish.rs index 5931fa7d15..9cff25a4a0 100644 --- a/scripts/publish.rs +++ b/scripts/publish.rs @@ -41,7 +41,6 @@ const CRATES_TO_PUBLISH: &[&str] = &[ "cranelift", "cranelift-simplejit", // wig/wiggle - "wig", "wiggle-generate", "wiggle-macro", "wiggle", diff --git a/src/commands/run.rs b/src/commands/run.rs index 1567b90d49..e1b891d598 100644 --- a/src/commands/run.rs +++ b/src/commands/run.rs @@ -344,16 +344,18 @@ fn populate_with_wasi( argv: &[String], vars: &[(String, String)], ) -> Result<()> { - // Add the current snapshot to the linker. - let mut cx = WasiCtxBuilder::new(); - cx.inherit_stdio().args(argv).envs(vars); + let mk_cx = || { + // Add the current snapshot to the linker. + let mut cx = WasiCtxBuilder::new(); + cx.inherit_stdio().args(argv).envs(vars); - for (name, file) in preopen_dirs { - cx.preopened_dir(file.try_clone()?, name); - } + for (name, file) in preopen_dirs { + cx.preopened_dir(file.try_clone()?, name); + } - let cx = cx.build()?; - let wasi = Wasi::new(linker.store(), cx); + cx.build() + }; + let wasi = Wasi::new(linker.store(), mk_cx()?); wasi.add_to_linker(linker)?; #[cfg(feature = "wasi-nn")] @@ -362,16 +364,7 @@ fn populate_with_wasi( wasi_nn.add_to_linker(linker)?; } - // Repeat the above, but this time for snapshot 0. - let mut cx = wasi_common::old::snapshot_0::WasiCtxBuilder::new(); - cx.inherit_stdio().args(argv).envs(vars); - - for (name, file) in preopen_dirs { - cx.preopened_dir(file.try_clone()?, name); - } - - let cx = cx.build()?; - let wasi = wasmtime_wasi::old::snapshot_0::Wasi::new(linker.store(), cx); + let wasi = wasmtime_wasi::old::snapshot_0::Wasi::new(linker.store(), mk_cx()?); wasi.add_to_linker(linker)?; Ok(())