From dd005208b6fbaf2c95f10a533e64ae37a1ba1b06 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Thu, 28 Jan 2021 15:12:38 -0800 Subject: [PATCH] delete wasi-common, yanix, winx --- crates/wasi-common/Cargo.toml | 49 - crates/wasi-common/src/ctx.rs | 433 -------- crates/wasi-common/src/entry.rs | 103 -- crates/wasi-common/src/error.rs | 199 ---- crates/wasi-common/src/fdpool.rs | 142 --- crates/wasi-common/src/fs/dir.rs | 217 ---- crates/wasi-common/src/fs/dir_builder.rs | 49 - crates/wasi-common/src/fs/dir_entry.rs | 53 - crates/wasi-common/src/fs/file.rs | 113 --- crates/wasi-common/src/fs/file_type.rs | 49 - crates/wasi-common/src/fs/metadata.rs | 106 -- crates/wasi-common/src/fs/mod.rs | 52 - crates/wasi-common/src/fs/open_options.rs | 99 -- crates/wasi-common/src/fs/permissions.rs | 37 - crates/wasi-common/src/fs/readdir.rs | 31 - crates/wasi-common/src/handle.rs | 360 ------- crates/wasi-common/src/lib.rs | 45 - crates/wasi-common/src/path.rs | 190 ---- .../wasi-common/src/sandboxed_tty_writer.rs | 199 ---- crates/wasi-common/src/sched.rs | 17 - crates/wasi-common/src/snapshots/mod.rs | 2 - .../src/snapshots/wasi_snapshot_preview1.rs | 862 ---------------- .../src/snapshots/wasi_unstable.rs | 939 ------------------ crates/wasi-common/src/string_array.rs | 145 --- crates/wasi-common/src/sys/clock.rs | 17 - crates/wasi-common/src/sys/fd.rs | 45 - crates/wasi-common/src/sys/mod.rs | 95 -- crates/wasi-common/src/sys/osdir.rs | 145 --- crates/wasi-common/src/sys/osfile.rs | 148 --- crates/wasi-common/src/sys/osother.rs | 103 -- crates/wasi-common/src/sys/stdio.rs | 241 ----- crates/wasi-common/src/sys/unix/bsd/mod.rs | 4 - crates/wasi-common/src/sys/unix/bsd/osdir.rs | 65 -- crates/wasi-common/src/sys/unix/bsd/path.rs | 118 --- crates/wasi-common/src/sys/unix/clock.rs | 36 - .../src/sys/unix/emscripten/mod.rs | 6 - crates/wasi-common/src/sys/unix/fd.rs | 77 -- crates/wasi-common/src/sys/unix/linux/mod.rs | 4 - .../wasi-common/src/sys/unix/linux/osdir.rs | 53 - crates/wasi-common/src/sys/unix/linux/path.rs | 41 - crates/wasi-common/src/sys/unix/mod.rs | 276 ----- crates/wasi-common/src/sys/unix/osdir.rs | 35 - crates/wasi-common/src/sys/unix/osfile.rs | 37 - crates/wasi-common/src/sys/unix/oshandle.rs | 40 - crates/wasi-common/src/sys/unix/osother.rs | 22 - crates/wasi-common/src/sys/unix/path.rs | 271 ----- crates/wasi-common/src/sys/unix/poll.rs | 165 --- crates/wasi-common/src/sys/unix/stdio.rs | 59 -- crates/wasi-common/src/sys/windows/clock.rs | 105 -- crates/wasi-common/src/sys/windows/fd.rs | 195 ---- crates/wasi-common/src/sys/windows/mod.rs | 183 ---- crates/wasi-common/src/sys/windows/osdir.rs | 66 -- crates/wasi-common/src/sys/windows/osfile.rs | 37 - .../wasi-common/src/sys/windows/oshandle.rs | 56 -- crates/wasi-common/src/sys/windows/osother.rs | 22 - crates/wasi-common/src/sys/windows/path.rs | 539 ---------- crates/wasi-common/src/sys/windows/poll.rs | 317 ------ crates/wasi-common/src/sys/windows/stdio.rs | 59 -- crates/wasi-common/src/virtfs.rs | 781 --------------- crates/wasi-common/src/virtfs/pipe.rs | 403 -------- crates/wasi-common/src/wasi.rs | 103 -- crates/wasi-common/winx/Cargo.toml | 29 - crates/wasi-common/winx/LICENSE | 220 ---- crates/wasi-common/winx/src/file.rs | 455 --------- crates/wasi-common/winx/src/lib.rs | 25 - crates/wasi-common/winx/src/ntdll.rs | 90 -- crates/wasi-common/winx/src/time.rs | 11 - crates/wasi-common/yanix/Cargo.toml | 19 - crates/wasi-common/yanix/LICENSE | 220 ---- crates/wasi-common/yanix/src/clock.rs | 34 - crates/wasi-common/yanix/src/dir.rs | 150 --- crates/wasi-common/yanix/src/fcntl.rs | 37 - crates/wasi-common/yanix/src/file.rs | 304 ------ crates/wasi-common/yanix/src/filetime.rs | 78 -- crates/wasi-common/yanix/src/lib.rs | 89 -- crates/wasi-common/yanix/src/poll.rs | 49 - crates/wasi-common/yanix/src/socket.rs | 32 - crates/wasi-common/yanix/src/sys/bsd/dir.rs | 59 -- .../wasi-common/yanix/src/sys/bsd/fadvise.rs | 91 -- crates/wasi-common/yanix/src/sys/bsd/file.rs | 20 - .../wasi-common/yanix/src/sys/bsd/filetime.rs | 77 -- crates/wasi-common/yanix/src/sys/bsd/mod.rs | 6 - .../yanix/src/sys/emscripten/filetime.rs | 30 - .../yanix/src/sys/emscripten/mod.rs | 7 - crates/wasi-common/yanix/src/sys/linux/dir.rs | 47 - .../yanix/src/sys/linux/fadvise.rs | 23 - .../wasi-common/yanix/src/sys/linux/file.rs | 26 - .../yanix/src/sys/linux/filetime.rs | 60 -- crates/wasi-common/yanix/src/sys/linux/mod.rs | 6 - .../yanix/src/sys/linux/utimesat.rs | 81 -- crates/wasi-common/yanix/src/sys/mod.rs | 32 - .../wasi-common/yanix/src/sys/wasi/fadvise.rs | 23 - crates/wasi-common/yanix/src/sys/wasi/file.rs | 21 - .../yanix/src/sys/wasi/filetime.rs | 33 - crates/wasi-common/yanix/src/sys/wasi/mod.rs | 3 - 95 files changed, 11947 deletions(-) delete mode 100644 crates/wasi-common/Cargo.toml delete mode 100644 crates/wasi-common/src/ctx.rs delete mode 100644 crates/wasi-common/src/entry.rs delete mode 100644 crates/wasi-common/src/error.rs delete mode 100644 crates/wasi-common/src/fdpool.rs delete mode 100644 crates/wasi-common/src/fs/dir.rs delete mode 100644 crates/wasi-common/src/fs/dir_builder.rs delete mode 100644 crates/wasi-common/src/fs/dir_entry.rs delete mode 100644 crates/wasi-common/src/fs/file.rs delete mode 100644 crates/wasi-common/src/fs/file_type.rs delete mode 100644 crates/wasi-common/src/fs/metadata.rs delete mode 100644 crates/wasi-common/src/fs/mod.rs delete mode 100644 crates/wasi-common/src/fs/open_options.rs delete mode 100644 crates/wasi-common/src/fs/permissions.rs delete mode 100644 crates/wasi-common/src/fs/readdir.rs delete mode 100644 crates/wasi-common/src/handle.rs delete mode 100644 crates/wasi-common/src/lib.rs delete mode 100644 crates/wasi-common/src/path.rs delete mode 100644 crates/wasi-common/src/sandboxed_tty_writer.rs delete mode 100644 crates/wasi-common/src/sched.rs delete mode 100644 crates/wasi-common/src/snapshots/mod.rs delete mode 100644 crates/wasi-common/src/snapshots/wasi_snapshot_preview1.rs delete mode 100644 crates/wasi-common/src/snapshots/wasi_unstable.rs delete mode 100644 crates/wasi-common/src/string_array.rs delete mode 100644 crates/wasi-common/src/sys/clock.rs delete mode 100644 crates/wasi-common/src/sys/fd.rs delete mode 100644 crates/wasi-common/src/sys/mod.rs delete mode 100644 crates/wasi-common/src/sys/osdir.rs delete mode 100644 crates/wasi-common/src/sys/osfile.rs delete mode 100644 crates/wasi-common/src/sys/osother.rs delete mode 100644 crates/wasi-common/src/sys/stdio.rs delete mode 100644 crates/wasi-common/src/sys/unix/bsd/mod.rs delete mode 100644 crates/wasi-common/src/sys/unix/bsd/osdir.rs delete mode 100644 crates/wasi-common/src/sys/unix/bsd/path.rs delete mode 100644 crates/wasi-common/src/sys/unix/clock.rs delete mode 100644 crates/wasi-common/src/sys/unix/emscripten/mod.rs delete mode 100644 crates/wasi-common/src/sys/unix/fd.rs delete mode 100644 crates/wasi-common/src/sys/unix/linux/mod.rs delete mode 100644 crates/wasi-common/src/sys/unix/linux/osdir.rs delete mode 100644 crates/wasi-common/src/sys/unix/linux/path.rs delete mode 100644 crates/wasi-common/src/sys/unix/mod.rs delete mode 100644 crates/wasi-common/src/sys/unix/osdir.rs delete mode 100644 crates/wasi-common/src/sys/unix/osfile.rs delete mode 100644 crates/wasi-common/src/sys/unix/oshandle.rs delete mode 100644 crates/wasi-common/src/sys/unix/osother.rs delete mode 100644 crates/wasi-common/src/sys/unix/path.rs delete mode 100644 crates/wasi-common/src/sys/unix/poll.rs delete mode 100644 crates/wasi-common/src/sys/unix/stdio.rs delete mode 100644 crates/wasi-common/src/sys/windows/clock.rs delete mode 100644 crates/wasi-common/src/sys/windows/fd.rs delete mode 100644 crates/wasi-common/src/sys/windows/mod.rs delete mode 100644 crates/wasi-common/src/sys/windows/osdir.rs delete mode 100644 crates/wasi-common/src/sys/windows/osfile.rs delete mode 100644 crates/wasi-common/src/sys/windows/oshandle.rs delete mode 100644 crates/wasi-common/src/sys/windows/osother.rs delete mode 100644 crates/wasi-common/src/sys/windows/path.rs delete mode 100644 crates/wasi-common/src/sys/windows/poll.rs delete mode 100644 crates/wasi-common/src/sys/windows/stdio.rs delete mode 100644 crates/wasi-common/src/virtfs.rs delete mode 100644 crates/wasi-common/src/virtfs/pipe.rs delete mode 100644 crates/wasi-common/src/wasi.rs delete mode 100644 crates/wasi-common/winx/Cargo.toml delete mode 100644 crates/wasi-common/winx/LICENSE delete mode 100644 crates/wasi-common/winx/src/file.rs delete mode 100644 crates/wasi-common/winx/src/lib.rs delete mode 100644 crates/wasi-common/winx/src/ntdll.rs delete mode 100644 crates/wasi-common/winx/src/time.rs delete mode 100644 crates/wasi-common/yanix/Cargo.toml delete mode 100644 crates/wasi-common/yanix/LICENSE delete mode 100644 crates/wasi-common/yanix/src/clock.rs delete mode 100644 crates/wasi-common/yanix/src/dir.rs delete mode 100644 crates/wasi-common/yanix/src/fcntl.rs delete mode 100644 crates/wasi-common/yanix/src/file.rs delete mode 100644 crates/wasi-common/yanix/src/filetime.rs delete mode 100644 crates/wasi-common/yanix/src/lib.rs delete mode 100644 crates/wasi-common/yanix/src/poll.rs delete mode 100644 crates/wasi-common/yanix/src/socket.rs delete mode 100644 crates/wasi-common/yanix/src/sys/bsd/dir.rs delete mode 100644 crates/wasi-common/yanix/src/sys/bsd/fadvise.rs delete mode 100644 crates/wasi-common/yanix/src/sys/bsd/file.rs delete mode 100644 crates/wasi-common/yanix/src/sys/bsd/filetime.rs delete mode 100644 crates/wasi-common/yanix/src/sys/bsd/mod.rs delete mode 100644 crates/wasi-common/yanix/src/sys/emscripten/filetime.rs delete mode 100644 crates/wasi-common/yanix/src/sys/emscripten/mod.rs delete mode 100644 crates/wasi-common/yanix/src/sys/linux/dir.rs delete mode 100644 crates/wasi-common/yanix/src/sys/linux/fadvise.rs delete mode 100644 crates/wasi-common/yanix/src/sys/linux/file.rs delete mode 100644 crates/wasi-common/yanix/src/sys/linux/filetime.rs delete mode 100644 crates/wasi-common/yanix/src/sys/linux/mod.rs delete mode 100644 crates/wasi-common/yanix/src/sys/linux/utimesat.rs delete mode 100644 crates/wasi-common/yanix/src/sys/mod.rs delete mode 100644 crates/wasi-common/yanix/src/sys/wasi/fadvise.rs delete mode 100644 crates/wasi-common/yanix/src/sys/wasi/file.rs delete mode 100644 crates/wasi-common/yanix/src/sys/wasi/filetime.rs delete mode 100644 crates/wasi-common/yanix/src/sys/wasi/mod.rs diff --git a/crates/wasi-common/Cargo.toml b/crates/wasi-common/Cargo.toml deleted file mode 100644 index c12d0c7be1..0000000000 --- a/crates/wasi-common/Cargo.toml +++ /dev/null @@ -1,49 +0,0 @@ -[package] -name = "wasi-common" -version = "0.22.0" -authors = ["The Wasmtime Project Developers"] -description = "WASI implementation in Rust" -license = "Apache-2.0 WITH LLVM-exception" -categories = ["wasm"] -keywords = ["webassembly", "wasm"] -repository = "https://github.com/bytecodealliance/wasmtime" -readme = "README.md" -edition = "2018" -include = ["src/**/*", "LICENSE", "WASI/phases", "build.rs"] - -# This doesn't actually link to a native library, but it allows us to set env -# vars like `DEP_WASI_COMMON_19_*` for crates that have build scripts and depend -# on this crate, allowing other crates to use the same witx files. -links = "wasi-common-19" - -[dependencies] -anyhow = "1.0" -thiserror = "1.0" -libc = "0.2" -getrandom = { version = "0.2.0", features = ["std"] } -cfg-if = "1.0" -filetime = "0.2.7" -lazy_static = "1.4.0" -wiggle = { path = "../wiggle", default-features = false, version = "0.22.0" } -tracing = "0.1.19" - -[target.'cfg(unix)'.dependencies] -yanix = { path = "yanix", version = "0.22.0" } - -[target.'cfg(windows)'.dependencies] -winx = { path = "winx", version = "0.22.0" } -winapi = "0.3" -cpu-time = "1.0" - -[badges] -maintenance = { status = "actively-developed" } - -[features] -default = ["trace_log"] -# This feature enables the `tracing` logs in the calls to target the `log` -# ecosystem of backends (e.g. `env_logger`. Disable this if you want to use -# `tracing-subscriber`. -trace_log = [ "wiggle/tracing_log", "tracing/log" ] -# Need to make the wiggle_metadata feature available to consumers of this -# crate if they want the snapshots to have metadata available. -wiggle_metadata = ["wiggle/wiggle_metadata"] diff --git a/crates/wasi-common/src/ctx.rs b/crates/wasi-common/src/ctx.rs deleted file mode 100644 index e73fbb4ea6..0000000000 --- a/crates/wasi-common/src/ctx.rs +++ /dev/null @@ -1,433 +0,0 @@ -use crate::entry::{Entry, EntryHandle}; -use crate::fdpool::FdPool; -use crate::handle::Handle; -use crate::string_array::{PendingString, StringArray, StringArrayError}; -use crate::sys::osdir::OsDir; -use crate::sys::stdio::NullDevice; -use crate::sys::stdio::{Stderr, StderrExt, Stdin, StdinExt, Stdout, StdoutExt}; -use crate::virtfs::{VirtualDir, VirtualDirEntry}; -use crate::wasi::types::Fd; -use crate::Error; -use std::borrow::Borrow; -use std::cell::RefCell; -use std::collections::HashMap; -use std::convert::TryFrom; -use std::fs::File; -use std::path::{Path, PathBuf}; -use std::rc::Rc; -use std::{env, io}; - -/// 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), - /// Error constructing arguments - #[error("while constructing arguments: {0}")] - Args(#[source] StringArrayError), - /// Error constructing environment - #[error("while constructing environment: {0}")] - Env(#[source] StringArrayError), - /// The root of a VirtualDirEntry tree must be a VirtualDirEntry::Directory. - #[error("the root of a VirtualDirEntry tree at {} must be a VirtualDirEntry::Directory", .0.display())] - VirtualDirEntryRootNotADirectory(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>), - Handle(Box), -} - -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::Handle(handle) => write!(fmt, "PendingEntry::Handle({:p})", handle), - } - } -} - -struct PendingPreopen(Box WasiCtxBuilderResult>>); - -impl PendingPreopen { - fn new(f: F) -> Self - where - F: FnOnce() -> WasiCtxBuilderResult> + 'static, - { - Self(Box::new(f)) - } - - fn into(self) -> WasiCtxBuilderResult> { - self.0() - } -} - -/// 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 { - let stdin = Some(PendingEntry::Handle(Box::new(NullDevice::new()))); - let stdout = Some(PendingEntry::Handle(Box::new(NullDevice::new()))); - let stderr = Some(PendingEntry::Handle(Box::new(NullDevice::new()))); - - Self { - stdin, - stdout, - stderr, - 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(PendingString::OsString)); - self - } - - /// Inherit stdin from the host process. - pub fn inherit_stdin(&mut self) -> &mut Self { - self.stdin = Some(PendingEntry::Thunk(Stdin::stdin)); - self - } - - /// Inherit stdout from the host process. - pub fn inherit_stdout(&mut self) -> &mut Self { - self.stdout = Some(PendingEntry::Thunk(Stdout::stdout)); - self - } - - /// Inherit stderr from the host process. - pub fn inherit_stderr(&mut self) -> &mut Self { - self.stderr = Some(PendingEntry::Thunk(Stderr::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(Stdin::stdin)); - self.stdout = Some(PendingEntry::Thunk(Stdout::stdout)); - self.stderr = Some(PendingEntry::Thunk(Stderr::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 `Handle` to use as stdin - pub fn stdin(&mut self, handle: T) -> &mut Self { - self.stdin = Some(PendingEntry::Handle(Box::new(handle))); - self - } - - /// Provide a `Handle` to use as stdout - pub fn stdout(&mut self, handle: T) -> &mut Self { - self.stdout = Some(PendingEntry::Handle(Box::new(handle))); - self - } - - /// Provide a `Handle` to use as stderr - pub fn stderr(&mut self, handle: T) -> &mut Self { - self.stderr = Some(PendingEntry::Handle(Box::new(handle))); - self - } - - /// Add a preopened directory. - pub fn preopened_dir>(&mut self, dir: File, guest_path: P) -> &mut Self { - let preopen = PendingPreopen::new(move || { - let dir = OsDir::try_from(dir).map_err(WasiCtxBuilderError::from)?; - Ok(Box::new(dir)) - }); - self.preopens - .as_mut() - .unwrap() - .push((guest_path.as_ref().to_owned(), preopen)); - self - } - - /// Add a preopened virtual directory. - pub fn preopened_virt>( - &mut self, - dir: VirtualDirEntry, - guest_path: P, - ) -> &mut Self { - fn populate_directory(virtentry: HashMap, dir: &mut VirtualDir) { - for (path, entry) in virtentry.into_iter() { - match entry { - VirtualDirEntry::Directory(dir_entries) => { - let mut subdir = VirtualDir::new(true); - populate_directory(dir_entries, &mut subdir); - dir.add_dir(subdir, path); - } - VirtualDirEntry::File(content) => { - dir.add_file(content, path); - } - } - } - } - - let guest_path_owned = guest_path.as_ref().to_owned(); - let preopen = PendingPreopen::new(move || { - if let VirtualDirEntry::Directory(entries) = dir { - let mut dir = VirtualDir::new(true); - populate_directory(entries, &mut dir); - Ok(Box::new(dir)) - } else { - Err(WasiCtxBuilderError::VirtualDirEntryRootNotADirectory( - guest_path_owned, - )) - } - }); - self.preopens - .as_mut() - .unwrap() - .push((guest_path.as_ref().to_owned(), preopen)); - 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 will fail. - pub fn build(&mut self) -> WasiCtxBuilderResult { - // Process arguments and environment variables into `String`s, failing quickly if they - // contain any NUL bytes, or if conversion from `OsString` fails. - let args = - StringArray::from_pending_vec(self.args.take().expect("WasiCtxBuilder has args")) - .map_err(WasiCtxBuilderError::Args)?; - let env = StringArray::from_pending_map(self.env.take().expect("WasiCtxBuilder has env")) - .map_err(WasiCtxBuilderError::Env)?; - - let mut entries = EntryTable::new(); - // Populate the non-preopen entries. - for pending in vec![ - self.stdin.take().unwrap(), - self.stdout.take().unwrap(), - self.stderr.take().unwrap(), - ] { - tracing::debug!( - pending = tracing::field::debug(&pending), - "WasiCtx inserting entry" - ); - let fd = match pending { - PendingEntry::Thunk(f) => { - let handle = EntryHandle::from(f()?); - let entry = Entry::new(handle); - entries - .insert(entry) - .ok_or(WasiCtxBuilderError::TooManyFilesOpen)? - } - PendingEntry::Handle(handle) => { - let handle = EntryHandle::from(handle); - let entry = Entry::new(handle); - entries - .insert(entry) - .ok_or(WasiCtxBuilderError::TooManyFilesOpen)? - } - }; - tracing::debug!(fd = tracing::field::debug(fd), "WasiCtx inserted"); - } - // Then add the preopen entries. - for (guest_path, preopen) in self.preopens.take().unwrap() { - let handle = EntryHandle::from(preopen.into()?); - let mut entry = Entry::new(handle); - entry.preopen_path = Some(guest_path); - let fd = entries - .insert(entry) - .ok_or(WasiCtxBuilderError::TooManyFilesOpen)?; - tracing::debug!(fd = tracing::field::debug(fd), "WasiCtx inserted",); - } - - Ok(WasiCtx { - args, - env, - entries: RefCell::new(entries), - }) - } -} - -struct EntryTable { - fd_pool: FdPool, - entries: HashMap>, -} - -impl EntryTable { - fn new() -> Self { - Self { - fd_pool: FdPool::new(), - entries: HashMap::new(), - } - } - - fn contains(&self, fd: &Fd) -> bool { - self.entries.contains_key(fd) - } - - fn insert(&mut self, entry: Entry) -> Option { - let fd = self.fd_pool.allocate()?; - self.entries.insert(fd, Rc::new(entry)); - Some(fd) - } - - fn insert_at(&mut self, fd: &Fd, entry: Rc) { - self.entries.insert(*fd, entry); - } - - fn get(&self, fd: &Fd) -> Option> { - self.entries.get(fd).map(Rc::clone) - } - - fn remove(&mut self, fd: Fd) -> Option> { - let entry = self.entries.remove(&fd)?; - self.fd_pool.deallocate(fd); - Some(entry) - } -} - -pub struct WasiCtx { - entries: RefCell, - pub(crate) args: StringArray, - pub(crate) env: StringArray, -} - -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) fn contains_entry(&self, fd: Fd) -> bool { - self.entries.borrow().contains(&fd) - } - - /// Get an immutable `Entry` corresponding to the specified raw WASI `fd`. - pub(crate) fn get_entry(&self, fd: Fd) -> Result, Error> { - match self.entries.borrow().get(&fd) { - Some(entry) => Ok(entry), - None => Err(Error::Badf), - } - } - - /// 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(&self, entry: Entry) -> Result { - self.entries.borrow_mut().insert(entry).ok_or(Error::Mfile) - } - - /// Insert the specified `Entry` with the specified raw WASI `fd` key into the `WasiCtx` - /// object. - pub(crate) fn insert_entry_at(&self, fd: Fd, entry: Rc) { - self.entries.borrow_mut().insert_at(&fd, entry) - } - - /// Remove `Entry` corresponding to the specified raw WASI `fd` from the `WasiCtx` object. - pub(crate) fn remove_entry(&self, fd: Fd) -> Result, Error> { - self.entries.borrow_mut().remove(fd).ok_or(Error::Badf) - } - - /* - pub(crate) fn args(&self) -> &impl StringArrayWriter { - &self.args - } - - pub(crate) fn env(&self) -> &impl StringArrayWriter { - &self.env - } - */ -} diff --git a/crates/wasi-common/src/entry.rs b/crates/wasi-common/src/entry.rs deleted file mode 100644 index a8a5accaf0..0000000000 --- a/crates/wasi-common/src/entry.rs +++ /dev/null @@ -1,103 +0,0 @@ -use crate::handle::{Filetype, Handle, HandleRights}; -use crate::{Error, Result}; -use std::ops::Deref; -use std::path::PathBuf; -use std::rc::Rc; - -pub struct EntryHandle(Rc); - -impl EntryHandle { - #[allow(dead_code)] - pub(crate) fn new(handle: T) -> Self { - Self(Rc::new(handle)) - } - - pub(crate) fn get(&self) -> Self { - Self(Rc::clone(&self.0)) - } -} - -impl std::fmt::Debug for EntryHandle { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - f.debug_struct("EntryHandle").field("opaque", &()).finish() - } -} - -impl From> for EntryHandle { - fn from(handle: Box) -> Self { - Self(handle.into()) - } -} - -impl Deref for EntryHandle { - type Target = dyn Handle; - - fn deref(&self) -> &Self::Target { - &*self.0 - } -} - -/// An abstraction struct serving as a wrapper for a `Handle` object. -/// -/// Here, the `handle` field stores an instance of `Handle` type (such as a file descriptor, or -/// stdin handle), and accessing it can only be done via the provided `Entry::as_handle` method -/// which require a set of base and inheriting rights to be specified, verifying whether the stored -/// `Handle` object is valid for the rights specified. -pub(crate) struct Entry { - handle: EntryHandle, - pub(crate) preopen_path: Option, - // TODO: directories -} - -impl Entry { - pub(crate) fn new(handle: EntryHandle) -> Self { - let preopen_path = None; - Self { - handle, - preopen_path, - } - } - - pub(crate) fn get_file_type(&self) -> Filetype { - self.handle.get_file_type() - } - - pub(crate) fn get_rights(&self) -> HandleRights { - self.handle.get_rights() - } - - pub(crate) fn set_rights(&self, rights: HandleRights) { - self.handle.set_rights(rights) - } - - /// Convert this `Entry` into a `Handle` object provided the specified - /// `rights` rights are set on this `Entry` object. - /// - /// The `Entry` can only be converted into a valid `Handle` object if - /// the specified set of base rights, and inheriting rights encapsulated within `rights` - /// `HandleRights` structure is a subset of rights attached to this `Entry`. The check is - /// performed using `Entry::validate_rights` method. If the check fails, `Error::Notcapable` - /// is returned. - pub(crate) fn as_handle(&self, rights: HandleRights) -> Result { - self.validate_rights(rights)?; - Ok(self.handle.get()) - } - - /// Check if this `Entry` object satisfies the specified `HandleRights`; i.e., if - /// rights attached to this `Entry` object are a superset. - /// - /// Upon unsuccessful check, `Error::Notcapable` is returned. - pub(crate) fn validate_rights(&self, rights: HandleRights) -> Result<()> { - let this_rights = self.handle.get_rights(); - if this_rights.contains(rights) { - Ok(()) - } else { - tracing::trace!( - required = tracing::field::display(rights), - actual = tracing::field::display(this_rights), - "validate_rights failed", - ); - Err(Error::Notcapable) - } - } -} diff --git a/crates/wasi-common/src/error.rs b/crates/wasi-common/src/error.rs deleted file mode 100644 index 2b0fb5b7ac..0000000000 --- a/crates/wasi-common/src/error.rs +++ /dev/null @@ -1,199 +0,0 @@ -use cfg_if::cfg_if; -use thiserror::Error; - -pub type Result = std::result::Result; - -/// Internal error type for the `wasi-common` crate. -/// Contains variants of the WASI `$errno` type are added according to what is actually used internally by -/// the crate. Not all values are represented presently. -#[derive(Debug, Error)] -pub enum Error { - #[error("Wiggle GuestError: {0}")] - Guest(#[from] wiggle::GuestError), - #[error("TryFromIntError: {0}")] - TryFromInt(#[from] std::num::TryFromIntError), - #[error("Utf8Error: {0}")] - Utf8(#[from] std::str::Utf8Error), - #[error("GetRandom: {0}")] - GetRandom(#[from] getrandom::Error), - - /// Some corners of the WASI standard are unsupported. - #[error("Unsupported: {0}")] - Unsupported(&'static str), - - /// The host OS may return an io error that doesn't match one of the - /// wasi errno variants we expect. We do not expose the details of this - /// error to the user. - #[error("Unexpected IoError: {0}")] - UnexpectedIo(#[source] std::io::Error), - - // Below this, all variants are from the `$errno` type: - /// Errno::TooBig: Argument list too long - #[error("TooBig: Argument list too long")] - TooBig, - /// Errno::Acces: Permission denied - #[error("Acces: Permission denied")] - Acces, - /// Errno::Badf: Bad file descriptor - #[error("Badf: Bad file descriptor")] - Badf, - /// Errno::Busy: Device or resource busy - #[error("Busy: Device or resource busy")] - Busy, - /// Errno::Exist: File exists - #[error("Exist: File exists")] - Exist, - /// Errno::Fault: Bad address - #[error("Fault: Bad address")] - Fault, - /// Errno::Fbig: File too large - #[error("Fbig: File too large")] - Fbig, - /// Errno::Ilseq: Illegal byte sequence - #[error("Ilseq: Illegal byte sequence")] - Ilseq, - /// Errno::Inval: Invalid argument - #[error("Inval: Invalid argument")] - Inval, - /// Errno::Io: I/O error - #[error("Io: I/o error")] - Io, - /// Errno::Isdir: Is a directory - #[error("Isdir: Is a directory")] - Isdir, - /// Errno::Loop: Too many levels of symbolic links - #[error("Loop: Too many levels of symbolic links")] - Loop, - /// Errno::Mfile: File descriptor value too large - #[error("Mfile: File descriptor value too large")] - Mfile, - /// Errno::Mlink: Too many links - #[error("Mlink: Too many links")] - Mlink, - /// Errno::Nametoolong: Filename too long - #[error("Nametoolong: Filename too long")] - Nametoolong, - /// Errno::Nfile: Too many files open in system - #[error("Nfile: Too many files open in system")] - Nfile, - /// Errno::Noent: No such file or directory - #[error("Noent: No such file or directory")] - Noent, - /// Errno::Nomem: Not enough space - #[error("Nomem: Not enough space")] - Nomem, - /// Errno::Nospc: No space left on device - #[error("Nospc: No space left on device")] - Nospc, - /// Errno::Notdir: Not a directory or a symbolic link to a directory. - #[error("Notdir: Not a directory or a symbolic link to a directory")] - Notdir, - /// Errno::Notempty: Directory not empty. - #[error("Notempty: Directory not empty")] - Notempty, - /// Errno::Notsup: Not supported, or operation not supported on socket. - #[error("Notsup: Not supported, or operation not supported on socket")] - Notsup, - /// Errno::Overflow: Value too large to be stored in data type. - #[error("Overflow: Value too large to be stored in data type")] - Overflow, - /// Errno::Pipe: Broken pipe - #[error("Pipe: Broken pipe")] - Pipe, - /// Errno::Perm: Operation not permitted - #[error("Perm: Operation not permitted")] - Perm, - /// Errno::Spipe: Invalid seek - #[error("Spipe: Invalid seek")] - Spipe, - /// Errno::Notcapable: Extension: Capabilities insufficient - #[error("Notcapable: cabailities insufficient")] - Notcapable, -} - -impl From for Error { - fn from(_err: std::convert::Infallible) -> Self { - unreachable!("should be impossible: From") - } -} - -// Turning an io::Error into an Error has platform-specific behavior -cfg_if! { - if #[cfg(windows)] { -use winapi::shared::winerror; -use std::io; -impl From for Error { - fn from(err: io::Error) -> Self { - match err.raw_os_error() { - Some(code) => match code as u32 { - winerror::ERROR_BAD_ENVIRONMENT => Self::TooBig, - winerror::ERROR_FILE_NOT_FOUND => Self::Noent, - winerror::ERROR_PATH_NOT_FOUND => Self::Noent, - winerror::ERROR_TOO_MANY_OPEN_FILES => Self::Nfile, - winerror::ERROR_ACCESS_DENIED => Self::Acces, - winerror::ERROR_SHARING_VIOLATION => Self::Acces, - winerror::ERROR_PRIVILEGE_NOT_HELD => Self::Notcapable, - winerror::ERROR_INVALID_HANDLE => Self::Badf, - winerror::ERROR_INVALID_NAME => Self::Noent, - winerror::ERROR_NOT_ENOUGH_MEMORY => Self::Nomem, - winerror::ERROR_OUTOFMEMORY => Self::Nomem, - winerror::ERROR_DIR_NOT_EMPTY => Self::Notempty, - winerror::ERROR_NOT_READY => Self::Busy, - winerror::ERROR_BUSY => Self::Busy, - winerror::ERROR_NOT_SUPPORTED => Self::Notsup, - winerror::ERROR_FILE_EXISTS => Self::Exist, - winerror::ERROR_BROKEN_PIPE => Self::Pipe, - winerror::ERROR_BUFFER_OVERFLOW => Self::Nametoolong, - winerror::ERROR_NOT_A_REPARSE_POINT => Self::Inval, - winerror::ERROR_NEGATIVE_SEEK => Self::Inval, - winerror::ERROR_DIRECTORY => Self::Notdir, - winerror::ERROR_ALREADY_EXISTS => Self::Exist, - _ => Self::UnexpectedIo(err), - }, - None => Self::UnexpectedIo(err), - } - } -} - - } else { -use std::io; -impl From for Error { - fn from(err: io::Error) -> Self { - match err.raw_os_error() { - Some(code) => match code { - libc::EPIPE => Self::Pipe, - libc::EPERM => Self::Perm, - libc::ENOENT => Self::Noent, - libc::ENOMEM => Self::Nomem, - libc::E2BIG => Self::TooBig, - libc::EIO => Self::Io, - libc::EBADF => Self::Badf, - libc::EBUSY => Self::Busy, - libc::EACCES => Self::Acces, - libc::EFAULT => Self::Fault, - libc::ENOTDIR => Self::Notdir, - libc::EISDIR => Self::Isdir, - libc::EINVAL => Self::Inval, - libc::EEXIST => Self::Exist, - libc::EFBIG => Self::Fbig, - libc::ENOSPC => Self::Nospc, - libc::ESPIPE => Self::Spipe, - libc::EMFILE => Self::Mfile, - libc::EMLINK => Self::Mlink, - libc::ENAMETOOLONG => Self::Nametoolong, - libc::ENFILE => Self::Nfile, - libc::ENOTEMPTY => Self::Notempty, - libc::ELOOP => Self::Loop, - libc::EOVERFLOW => Self::Overflow, - libc::EILSEQ => Self::Ilseq, - libc::ENOTSUP => Self::Notsup, - _ => Self::UnexpectedIo(err), - }, - None => { - Self::UnexpectedIo(err) - } - } - } -} - } -} diff --git a/crates/wasi-common/src/fdpool.rs b/crates/wasi-common/src/fdpool.rs deleted file mode 100644 index b9523291b7..0000000000 --- a/crates/wasi-common/src/fdpool.rs +++ /dev/null @@ -1,142 +0,0 @@ -//! Contains mechanism for managing the WASI file descriptor -//! pool. It's intended to be mainly used within the `WasiCtx` -//! object(s). - -/// Any type wishing to be treated as a valid WASI file descriptor -/// should implement this trait. -/// -/// This trait is required as internally we use `u32` to represent -/// and manage raw file descriptors. -pub(crate) trait Fd { - /// Convert to `u32`. - fn as_raw(&self) -> u32; - /// Convert from `u32`. - 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; -/// however, the caller may supply any type `T` such that it -/// implements the `Fd` trait when requesting a new descriptor -/// via the `allocate` method, or when returning one back via -/// the `deallocate` method. -#[derive(Debug)] -pub(crate) struct FdPool { - next_alloc: Option, - available: Vec, -} - -impl FdPool { - pub fn new() -> Self { - Self { - next_alloc: Some(0), - available: Vec::new(), - } - } - - /// Obtain another valid WASI file descriptor. - /// - /// If we've handed out the maximum possible amount of file - /// descriptors (which would be equal to `2^32 + 1` accounting for `0`), - /// then this method will return `None` to signal that case. - /// Otherwise, a new file descriptor is return as `Some(fd)`. - pub fn allocate(&mut self) -> Option { - if let Some(fd) = self.available.pop() { - // Since we've had free, unclaimed handle in the pool, - // simply claim it and return. - return Some(T::from_raw(fd)); - } - // There are no free handles available in the pool, so try - // allocating an additional one into the pool. If we've - // reached our max number of handles, we will fail with None - // instead. - let fd = self.next_alloc.take()?; - // It's OK to not unpack the result of `fd.checked_add()` here which - // can fail since we check for `None` in the snippet above. - self.next_alloc = fd.checked_add(1); - Some(T::from_raw(fd)) - } - - /// Return a file descriptor back to the pool. - /// - /// If the caller tries to return a file descriptor that was - /// not yet allocated (via spoofing, etc.), this method - /// will panic. - pub fn deallocate(&mut self, fd: T) { - let fd = fd.as_raw(); - if let Some(next_alloc) = self.next_alloc { - assert!(fd < next_alloc); - } - debug_assert!(!self.available.contains(&fd)); - self.available.push(fd); - } -} - -#[cfg(test)] -mod test { - use super::FdPool; - use std::ops::Deref; - - #[derive(Debug)] - struct Fd(u32); - - impl super::Fd for Fd { - fn as_raw(&self) -> u32 { - self.0 - } - fn from_raw(raw_fd: u32) -> Self { - Self(raw_fd) - } - } - - impl Deref for Fd { - type Target = u32; - fn deref(&self) -> &Self::Target { - &self.0 - } - } - - #[test] - fn basics() { - let mut fd_pool = FdPool::new(); - let mut fd: Fd = fd_pool.allocate().expect("success allocating 0"); - assert_eq!(*fd, 0); - fd = fd_pool.allocate().expect("success allocating 1"); - assert_eq!(*fd, 1); - fd = fd_pool.allocate().expect("success allocating 2"); - assert_eq!(*fd, 2); - fd_pool.deallocate(1u32); - fd_pool.deallocate(0u32); - fd = fd_pool.allocate().expect("success reallocating 0"); - assert_eq!(*fd, 0); - fd = fd_pool.allocate().expect("success reallocating 1"); - assert_eq!(*fd, 1); - fd = fd_pool.allocate().expect("success allocating 3"); - assert_eq!(*fd, 3); - } - - #[test] - #[should_panic] - fn deallocate_nonexistent() { - let mut fd_pool = FdPool::new(); - fd_pool.deallocate(0u32); - } - - #[test] - fn max_allocation() { - let mut fd_pool = FdPool::new(); - // Spoof reaching the limit of allocs. - fd_pool.next_alloc = None; - assert!(fd_pool.allocate::().is_none()); - } -} diff --git a/crates/wasi-common/src/fs/dir.rs b/crates/wasi-common/src/fs/dir.rs deleted file mode 100644 index d07734f657..0000000000 --- a/crates/wasi-common/src/fs/dir.rs +++ /dev/null @@ -1,217 +0,0 @@ -use crate::fs::{Fd, File, OpenOptions, ReadDir}; -use crate::wasi::wasi_snapshot_preview1::WasiSnapshotPreview1; -use crate::WasiCtx; -#[cfg(unix)] -use std::os::unix::ffi::OsStrExt; -use std::{io, path::Path}; - -/// A reference to an open directory on the filesystem. -/// -/// TODO: Implement `Dir`-using versions of `std::fs`'s free functions: -/// `copy`, `create_dir`, `create_dir_all`, `hard_link`, `metadata`, -/// `read_link`, `read_to_string`, `remove_dir`, `remove_dir_all`, -/// `remove_file`, `rename`, `set_permissions`, `symlink_metadata`, and -/// `write`. -/// -/// Unlike `std::fs`, this API has no `canonicalize`, because absolute paths -/// don't interoperate well with the capability-oriented security model. -pub struct Dir<'ctx> { - ctx: &'ctx WasiCtx, - fd: Fd, -} - -impl<'ctx> Dir<'ctx> { - /// Constructs a new instance of `Self` from the given raw WASI file descriptor. - pub unsafe fn from_raw_wasi_fd(ctx: &'ctx WasiCtx, fd: Fd) -> Self { - Self { ctx, fd } - } - - /// Attempts to open a file in read-only mode. - /// - /// This corresponds to [`std::fs::File::open`], but only accesses paths - /// relative to and within `self`. - /// - /// TODO: Not yet implemented. Refactor the hostcalls functions to split out the - /// encoding/decoding parts from the underlying functionality, so that we can call - /// into the underlying functionality directly. - /// - /// [`std::fs::File::open`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.open - pub fn open_file>(&mut self, path: P) -> io::Result { - let path = path.as_ref(); - let mut fd = Fd::from(0); - - // TODO: Refactor the hostcalls functions to split out the encoding/decoding - // parts from the underlying functionality, so that we can call into the - // underlying functionality directly. - // - // TODO: Set the requested rights to be readonly. - // - // TODO: Handle paths for non-Unix platforms which don't have `as_bytes()` - // on `OsStrExt`. - unimplemented!("Dir::open_file"); - /* - wasi_errno_to_io_error(hostcalls::path_open( - self.ctx, - self.fd, - wasi::__WASI_LOOKUPFLAGS_SYMLINK_FOLLOW, - path.as_os_str().as_bytes(), - path.as_os_str().len(), - 0, - !0, - !0, - 0, - &mut fd, - ))?; - */ - - let ctx = self.ctx; - Ok(unsafe { File::from_raw_wasi_fd(ctx, fd) }) - } - - /// Opens a file at `path` with the options specified by `self`. - /// - /// This corresponds to [`std::fs::OpenOptions::open`]. - /// - /// Instead of being a method on `OpenOptions`, this is a method on `Dir`, - /// and it only accesses functions relative to and within `self`. - /// - /// TODO: Not yet implemented. - /// - /// [`std::fs::OpenOptions::open`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.open - pub fn open_file_with>( - &mut self, - path: P, - options: &OpenOptions, - ) -> io::Result { - unimplemented!("Dir::open_file_with"); - } - - /// Attempts to open a directory. - /// - /// TODO: Not yet implemented. See the comment in `open_file`. - pub fn open_dir>(&mut self, path: P) -> io::Result { - let path = path.as_ref(); - let mut fd = Fd::from(0); - - // TODO: See the comment in `open_file`. - unimplemented!("Dir::open_dir"); - /* - wasi_errno_to_io_error(hostcalls::path_open( - self.ctx, - self.fd, - wasi::__WASI_LOOKUPFLAGS_SYMLINK_FOLLOW, - path.as_os_str().as_bytes(), - wasi::__WASI_OFLAGS_DIRECTORY, - !0, - !0, - 0, - &mut fd, - ))?; - */ - - let ctx = self.ctx; - Ok(unsafe { Dir::from_raw_wasi_fd(ctx, fd) }) - } - - /// Opens a file in write-only mode. - /// - /// This corresponds to [`std::fs::File::create`], but only accesses paths - /// relative to and within `self`. - /// - /// TODO: Not yet implemented. See the comment in `open_file`. - /// - /// [`std::fs::File::create`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.create - pub fn create_file>(&mut self, path: P) -> io::Result { - let path = path.as_ref(); - let mut fd = Fd::from(0); - - // TODO: See the comments in `open_file`. - // - // TODO: Set the requested rights to be read+write. - unimplemented!("Dir::create_file"); - /* - wasi_errno_to_io_error(hostcalls::path_open( - self.ctx, - self.fd, - wasi::__WASI_LOOKUPFLAGS_SYMLINK_FOLLOW, - path.as_os_str().as_bytes(), - path.as_os_str().len(), - wasi::__WASI_OFLAGS_CREAT | wasi::__WASI_OFLAGS_TRUNC, - !0, - !0, - 0, - &mut fd, - ))?; - */ - - let ctx = self.ctx; - Ok(unsafe { File::from_raw_wasi_fd(ctx, fd) }) - } - - /// Returns an iterator over the entries within a directory. - /// - /// This corresponds to [`std::fs::read_dir`], but reads the directory - /// represented by `self`. - /// - /// TODO: Not yet implemented. We may need to wait until we have the ability - /// to duplicate file descriptors before we can implement read safely. For - /// now, use `into_read` instead. - /// - /// [`std::fs::read_dir`]: https://doc.rust-lang.org/std/fs/fn.read_dir.html - pub fn read(&mut self) -> io::Result { - unimplemented!("Dir::read") - } - - /// Consumes self and returns an iterator over the entries within a directory - /// in the manner of `read`. - pub fn into_read(self) -> ReadDir { - unsafe { ReadDir::from_raw_wasi_fd(self.fd) } - } - - /// Read the entire contents of a file into a bytes vector. - /// - /// This corresponds to [`std::fs::read`], but only accesses paths - /// relative to and within `self`. - /// - /// [`std::fs::read`]: https://doc.rust-lang.org/std/fs/fn.read.html - pub fn read_file>(&mut self, path: P) -> io::Result> { - use io::Read; - let mut file = self.open_file(path)?; - let mut bytes = Vec::with_capacity(initial_buffer_size(&file)); - file.read_to_end(&mut bytes)?; - Ok(bytes) - } - - /// Returns an iterator over the entries within a directory. - /// - /// This corresponds to [`std::fs::read_dir`], but only accesses paths - /// relative to and within `self`. - /// - /// [`std::fs::read_dir`]: https://doc.rust-lang.org/std/fs/fn.read_dir.html - pub fn read_dir>(&mut self, path: P) -> io::Result { - self.open_dir(path)?.read() - } -} - -impl<'ctx> Drop for Dir<'ctx> { - fn drop(&mut self) { - // Note that errors are ignored when closing a file descriptor. The - // reason for this is that if an error occurs we don't actually know if - // the file descriptor was closed or not, and if we retried (for - // something like EINTR), we might close another valid file descriptor - // opened after we closed ours. - let _ = self.ctx.fd_close(self.fd); - } -} - -/// Indicates how large a buffer to pre-allocate before reading the entire file. -/// -/// Derived from the function of the same name in libstd. -fn initial_buffer_size(file: &File) -> usize { - // Allocate one extra byte so the buffer doesn't need to grow before the - // final `read` call at the end of the file. Don't worry about `usize` - // overflow because reading will fail regardless in that case. - file.metadata().map(|m| m.len() as usize + 1).unwrap_or(0) -} - -// TODO: impl Debug for Dir diff --git a/crates/wasi-common/src/fs/dir_builder.rs b/crates/wasi-common/src/fs/dir_builder.rs deleted file mode 100644 index aaf44b7345..0000000000 --- a/crates/wasi-common/src/fs/dir_builder.rs +++ /dev/null @@ -1,49 +0,0 @@ -use std::{io, path::Path}; - -/// A builder used to create directories in various manners. -/// -/// This corresponds to [`std::fs::DirBuilder`]. -/// -/// TODO: Not yet implemented. -/// -/// [`std::fs::DirBuilder`]: https://doc.rust-lang.org/std/fs/struct.DirBuilder.html -pub struct DirBuilder {} - -impl DirBuilder { - /// Creates a new set of options with default mode/security settings for all platforms and also non-recursive. - /// - /// This corresponds to [`std::fs::DirBuilder::new`]. - /// - /// TODO: Not yet implemented. - /// - /// [`std::fs::DirBuilder::new`]: https://doc.rust-lang.org/std/fs/struct.DirBuilder.html#method.new - pub fn new() -> Self { - unimplemented!("DirBuilder::new"); - } - - /// Indicates that directories should be created recursively, creating all parent directories. - /// - /// This corresponds to [`std::fs::DirBuilder::recursive`]. - /// - /// TODO: Not yet implemented. - /// - /// [`std::fs::DirBuilder::recursive`]: https://doc.rust-lang.org/std/fs/struct.DirBuilder.html#method.recursive - pub fn recursive(&mut self, recursive: bool) -> &mut Self { - unimplemented!("DirBuilder::recursive"); - } - - /// Creates the specified directory with the options configured in this builder. - /// - /// This corresponds to [`std::fs::DirBuilder::create`]. - /// - /// TODO: Not yet implemented. - /// - /// [`std::fs::DirBuilder::create`]: https://doc.rust-lang.org/std/fs/struct.DirBuilder.html#method.create - pub fn create>(&self, path: P) -> io::Result<()> { - unimplemented!("DirBuilder::create"); - } -} - -// TODO: functions from DirBuilderExt? - -// TODO: impl Debug for DirBuilder diff --git a/crates/wasi-common/src/fs/dir_entry.rs b/crates/wasi-common/src/fs/dir_entry.rs deleted file mode 100644 index 0c0c34cf80..0000000000 --- a/crates/wasi-common/src/fs/dir_entry.rs +++ /dev/null @@ -1,53 +0,0 @@ -use crate::fs::{FileType, Metadata}; -use std::{ffi, io}; - -/// Entries returned by the ReadDir iterator. -/// -/// This corresponds to [`std::fs::DirEntry`]. -/// -/// Unlike `std::fs::DirEntry`, this API has no `DirEntry::path`, because -/// absolute paths don't interoperate well with the capability-oriented -/// security model. -/// -/// TODO: Not yet implemented. -/// -/// [`std::fs::DirEntry`]: https://doc.rust-lang.org/std/fs/struct.DirEntry.html -pub struct DirEntry {} - -impl DirEntry { - /// Returns the metadata for the file that this entry points at. - /// - /// This corresponds to [`std::fs::DirEntry::metadata`]. - /// - /// TODO: Not yet implemented. - /// - /// [`std::fs::DirEntry::metadata`]: https://doc.rust-lang.org/std/fs/struct.DirEntry.html#method.metadata - pub fn metadata(&self) -> io::Result { - unimplemented!("DirEntry::metadata"); - } - - /// Returns the file type for the file that this entry points at. - /// - /// This to [`std::fs::DirEntry::file_type`]. - /// - /// TODO: Not yet implemented. - /// - /// [`std::fs::DirEntry::file_type`]: https://doc.rust-lang.org/std/fs/struct.DirEntry.html#method.file_type - pub fn file_type(&self) -> io::Result { - unimplemented!("DirEntry::file_type"); - } - - /// Returns the bare file name of this directory entry without any other leading path component. - /// - /// This corresponds to [`std::fs::DirEntry::file_name`], though it returns - /// `String` rather than `OsString`. - /// - /// TODO: Not yet implemented. - /// - /// [`std::fs::DirEntry::file_name`]: https://doc.rust-lang.org/std/fs/struct.DirEntry.html#method.file_name - pub fn file_name(&self) -> String { - unimplemented!("DirEntry::file_name"); - } -} - -// TODO: impl Debug for DirEntry diff --git a/crates/wasi-common/src/fs/file.rs b/crates/wasi-common/src/fs/file.rs deleted file mode 100644 index be720a3c5f..0000000000 --- a/crates/wasi-common/src/fs/file.rs +++ /dev/null @@ -1,113 +0,0 @@ -use crate::fs::{Fd, Metadata}; -use crate::wasi::wasi_snapshot_preview1::WasiSnapshotPreview1; -use crate::Result; -use crate::WasiCtx; -use std::io; - -/// A reference to an open file on the filesystem. -/// -/// This corresponds to [`std::fs::File`]. -/// -/// Note that this `File` has no `open` or `create` methods. To open or create -/// a file, you must first obtain a [`Dir`] containing the file, and then call -/// [`Dir::open_file`] or [`Dir::create_file`]. -/// -/// [`std::fs::File`]: https://doc.rust-lang.org/std/fs/struct.File.html -/// [`Dir`]: struct.Dir.html -/// [`Dir::open_file`]: struct.Dir.html#method.open_file -/// [`Dir::create_file`]: struct.Dir.html#method.create_file -pub struct File<'ctx> { - ctx: &'ctx WasiCtx, - fd: Fd, -} - -impl<'ctx> File<'ctx> { - /// Constructs a new instance of `Self` from the given raw WASI file descriptor. - /// - /// This corresponds to [`std::fs::File::from_raw_fd`]. - /// - /// [`std::fs::File::from_raw_fd`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.from_raw_fd - pub unsafe fn from_raw_wasi_fd(ctx: &'ctx WasiCtx, fd: Fd) -> Self { - Self { ctx, fd } - } - - /// Attempts to sync all OS-internal metadata to disk. - /// - /// This corresponds to [`std::fs::File::sync_all`]. - /// - /// [`std::fs::File::sync_all`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.sync_all - pub fn sync_all(&self) -> Result<()> { - self.ctx.fd_sync(self.fd)?; - Ok(()) - } - - /// This function is similar to `sync_all`, except that it may not synchronize - /// file metadata to the filesystem. - /// - /// This corresponds to [`std::fs::File::sync_data`]. - /// - /// [`std::fs::File::sync_data`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.sync_data - pub fn sync_data(&self) -> Result<()> { - self.ctx.fd_datasync(self.fd)?; - Ok(()) - } - - /// Truncates or extends the underlying file, updating the size of this file - /// to become size. - /// - /// This corresponds to [`std::fs::File::set_len`]. - /// - /// [`std::fs::File::set_len`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.set_len - pub fn set_len(&self, size: u64) -> Result<()> { - self.ctx.fd_filestat_set_size(self.fd, size)?; - Ok(()) - } - - /// Queries metadata about the underlying file. - /// - /// This corresponds to [`std::fs::File::metadata`]. - /// - /// [`std::fs::File::metadata`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.metadata - pub fn metadata(&self) -> Result { - Ok(Metadata {}) - } -} - -impl<'ctx> Drop for File<'ctx> { - fn drop(&mut self) { - // Note that errors are ignored when closing a file descriptor. The - // reason for this is that if an error occurs we don't actually know if - // the file descriptor was closed or not, and if we retried (for - // something like EINTR), we might close another valid file descriptor - // opened after we closed ours. - let _ = self.ctx.fd_close(self.fd); - } -} - -impl<'ctx> io::Read for File<'ctx> { - /// TODO: Not yet implemented. See the comment in `Dir::open_file`. - fn read(&mut self, buf: &mut [u8]) -> io::Result { - // TODO - // let iov = [Iovec { - // buf: buf.as_mut_ptr() as *mut u8, - // buf_len: buf.len(), - // }]; - let mut nread = 0; - - // TODO: See the comment in `Dir::open_file`. - unimplemented!("File::read"); - /* - wasi_errno_to_io_error(unsafe { - hostcalls::fd_read(self.ctx, self.fd, &iov, 1, &mut nread) - })?; - */ - - Ok(nread) - } -} - -// TODO: traits to implement: Write, Seek - -// TODO: functions from FileExt? - -// TODO: impl Debug for File diff --git a/crates/wasi-common/src/fs/file_type.rs b/crates/wasi-common/src/fs/file_type.rs deleted file mode 100644 index a97f53a8e1..0000000000 --- a/crates/wasi-common/src/fs/file_type.rs +++ /dev/null @@ -1,49 +0,0 @@ -/// A structure representing a type of file with accessors for each file type. -/// It is returned by `Metadata::file_type` method. -/// -/// This corresponds to [`std::fs::FileType`]. -/// -/// TODO: Not yet implemented. -/// -/// [`std::fs::FileType`]: https://doc.rust-lang.org/std/fs/struct.FileType.html -#[derive(Copy, Clone, Eq, PartialEq, Hash)] -pub struct FileType {} - -impl FileType { - /// Tests whether this file type represents a directory. - /// - /// This corresponds to [`std::fs::FileType::is_dir`]. - /// - /// TODO: Not yet implemented. - /// - /// [`std::fs::FileType::is_dir`]: https://doc.rust-lang.org/std/fs/struct.FileType.html#method.is_dir - pub fn is_dir(&self) -> bool { - unimplemented!("FileType::is_dir"); - } - - /// Tests whether this file type represents a regular file. - /// - /// This corresponds to [`std::fs::FileType::is_file`]. - /// - /// TODO: Not yet implemented. - /// - /// [`std::fs::FileType::is_file`]: https://doc.rust-lang.org/std/fs/struct.FileType.html#method.is_file - pub fn is_file(&self) -> bool { - unimplemented!("FileType::is_file"); - } - - /// Tests whether this file type represents a symbolic link. - /// - /// This corresponds to [`std::fs::FileType::is_symlink`]. - /// - /// TODO: Not yet implemented. - /// - /// [`std::fs::FileType::is_symlink`]: https://doc.rust-lang.org/std/fs/struct.FileType.html#method.is_symlink - pub fn is_symlink(&self) -> bool { - unimplemented!("FileType::is_symlink"); - } -} - -// TODO: functions from FileTypeExt? - -// TODO: impl Debug for FileType diff --git a/crates/wasi-common/src/fs/metadata.rs b/crates/wasi-common/src/fs/metadata.rs deleted file mode 100644 index e55afb73a8..0000000000 --- a/crates/wasi-common/src/fs/metadata.rs +++ /dev/null @@ -1,106 +0,0 @@ -use crate::fs::{FileType, Permissions}; -use std::{io, time::SystemTime}; - -/// Metadata information about a file. -/// -/// This corresponds to [`std::fs::Metadata`]. -/// -/// TODO: Not yet implemented. -/// -/// [`std::fs::Metadata`]: https://doc.rust-lang.org/std/fs/struct.Metadata.html -#[derive(Clone)] -pub struct Metadata {} - -impl Metadata { - /// Returns the file type for this metadata. - /// - /// This corresponds to [`std::fs::Metadata::file_type`]. - /// - /// TODO: Not yet implemented. - /// - /// [`std::fs::Metadata::file_type`]: https://doc.rust-lang.org/std/fs/struct.Metadata.html#method.file_type - pub fn file_type(&self) -> FileType { - unimplemented!("Metadata::file_type"); - } - - /// Returns true if this metadata is for a directory. - /// - /// This corresponds to [`std::fs::Metadata::is_dir`]. - /// - /// TODO: Not yet implemented. - /// - /// [`std::fs::Metadata::is_dir`]: https://doc.rust-lang.org/std/fs/struct.Metadata.html#method.is_dir - pub fn is_dir(&self) -> bool { - unimplemented!("Metadata::is_dir"); - } - - /// Returns true if this metadata is for a regular file. - /// - /// This corresponds to [`std::fs::Metadata::is_file`]. - /// - /// TODO: Not yet implemented. - /// - /// [`std::fs::Metadata::is_file`]: https://doc.rust-lang.org/std/fs/struct.Metadata.html#method.is_file - pub fn is_file(&self) -> bool { - unimplemented!("Metadata::is_file"); - } - - /// Returns the size of the file, in bytes, this metadata is for. - /// - /// This corresponds to [`std::fs::Metadata::len`]. - /// - /// TODO: Not yet implemented. - /// - /// [`std::fs::Metadata::len`]: https://doc.rust-lang.org/std/fs/struct.Metadata.html#method.len - pub fn len(&self) -> u64 { - unimplemented!("Metadata::len"); - } - - /// Returns the permissions of the file this metadata is for. - /// - /// This corresponds to [`std::fs::Metadata::permissions`]. - /// - /// TODO: Not yet implemented. - /// - /// [`std::fs::Metadata::permissions`]: https://doc.rust-lang.org/std/fs/struct.Metadata.html#method.permissions - pub fn permissions(&self) -> Permissions { - unimplemented!("Metadata::permissions"); - } - - /// Returns the last modification time listed in this metadata. - /// - /// This corresponds to [`std::fs::Metadata::modified`]. - /// - /// TODO: Not yet implemented. - /// - /// [`std::fs::Metadata::modified`]: https://doc.rust-lang.org/std/fs/struct.Metadata.html#method.modified - pub fn modified(&self) -> io::Result { - unimplemented!("Metadata::modified"); - } - - /// Returns the last access time of this metadata. - /// - /// This corresponds to [`std::fs::Metadata::accessed`]. - /// - /// TODO: Not yet implemented. - /// - /// [`std::fs::Metadata::accessed`]: https://doc.rust-lang.org/std/fs/struct.Metadata.html#method.accessed - pub fn accessed(&self) -> io::Result { - unimplemented!("Metadata::accessed"); - } - - /// Returns the creation time listed in this metadata. - /// - /// This corresponds to [`std::fs::Metadata::created`]. - /// - /// TODO: Not yet implemented. - /// - /// [`std::fs::Metadata::created`]: https://doc.rust-lang.org/std/fs/struct.Metadata.html#method.created - pub fn created(&self) -> io::Result { - unimplemented!("Metadata::created"); - } -} - -// TODO: Functions from MetadataExt? - -// TODO: impl Debug for Metadata diff --git a/crates/wasi-common/src/fs/mod.rs b/crates/wasi-common/src/fs/mod.rs deleted file mode 100644 index 213d5bf234..0000000000 --- a/crates/wasi-common/src/fs/mod.rs +++ /dev/null @@ -1,52 +0,0 @@ -//! A very experimental module modeled providing a high-level and safe -//! filesystem interface, modeled after `std::fs`, implemented on top of -//! WASI functions. -//! -//! Most functions in this API are not yet implemented! -//! -//! This corresponds to [`std::fs`]. -//! -//! Instead of [`std::fs`'s free functions] which operate on paths, this -//! crate has methods on [`Dir`] which operate on paths which must be -//! relative to and within the directory. -//! -//! Since all functions which expose raw file descriptors are `unsafe`, -//! I/O handles in this API are unforgeable (unsafe code notwithstanding). -//! This combined with WASI's lack of absolute paths provides a natural -//! capability-oriented interface. -//! -//! [`std::fs`]: https://doc.rust-lang.org/std/fs/index.html -//! [`std::fs`'s free functions]: https://doc.rust-lang.org/std/fs/index.html#functions -//! [`DIR`]: struct.Dir.html - -// TODO: When more things are implemented, remove these. -#![allow( - unused_imports, - unreachable_code, - unused_variables, - unused_mut, - unused_unsafe, - dead_code -)] - -mod dir; -mod dir_builder; -mod dir_entry; -mod file; -mod file_type; -mod metadata; -mod open_options; -mod permissions; -mod readdir; - -pub use crate::wasi::types::Fd; - -pub use dir::*; -pub use dir_builder::*; -pub use dir_entry::*; -pub use file::*; -pub use file_type::*; -pub use metadata::*; -pub use open_options::*; -pub use permissions::*; -pub use readdir::*; diff --git a/crates/wasi-common/src/fs/open_options.rs b/crates/wasi-common/src/fs/open_options.rs deleted file mode 100644 index 0de1a43841..0000000000 --- a/crates/wasi-common/src/fs/open_options.rs +++ /dev/null @@ -1,99 +0,0 @@ -/// Options and flags which can be used to configure how a file is opened. -/// -/// This corresponds to [`std::fs::OpenOptions`]. -/// -/// Note that this `OpenOptions` has no `open` method. To open a file with -/// an `OptionOptions`, you must first obtain a [`Dir`] containing the file, and -/// then call [`Dir::open_file_with`]. -/// -/// [`std::fs::OpenOptions`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html -/// [`Dir`]: struct.Dir.html -/// [`Dir::open_file_with`]: struct.Dir.html#method.open_file_with -pub struct OpenOptions { - pub(crate) read: bool, - pub(crate) write: bool, - pub(crate) append: bool, - pub(crate) truncate: bool, - pub(crate) create: bool, - pub(crate) create_new: bool, -} - -impl OpenOptions { - /// Creates a blank new set of options ready for configuration. - /// - /// This corresponds to [`std::fs::OpenOptions::new`]. - /// - /// [`std::fs::OpenOptions::new`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.new - pub fn new() -> Self { - Self { - read: false, - write: false, - append: false, - truncate: false, - create: false, - create_new: false, - } - } - - /// Sets the option for read access. - /// - /// This corresponds to [`std::fs::OpenOptions::read`]. - /// - /// [`std::fs::OpenOptions::read`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.read - pub fn read(&mut self, read: bool) -> &mut Self { - self.read = read; - self - } - - /// Sets the option for write access. - /// - /// This corresponds to [`std::fs::OpenOptions::write`]. - /// - /// [`std::fs::OpenOptions::write`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.write - pub fn write(&mut self, write: bool) -> &mut Self { - self.write = write; - self - } - - /// Sets the option for the append mode. - /// - /// This corresponds to [`std::fs::OpenOptions::append`]. - /// - /// [`std::fs::OpenOptions::append`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.append - pub fn append(&mut self, append: bool) -> &mut Self { - self.append = append; - self - } - - /// Sets the option for truncating a previous file. - /// - /// This corresponds to [`std::fs::OpenOptions::truncate`]. - /// - /// [`std::fs::OpenOptions::truncate`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.truncate - pub fn truncate(&mut self, truncate: bool) -> &mut Self { - self.truncate = truncate; - self - } - - /// Sets the option to create a new file. - /// - /// This corresponds to [`std::fs::OpenOptions::create`]. - /// - /// [`std::fs::OpenOptions::create`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.create - pub fn create(&mut self, create: bool) -> &mut Self { - self.create = create; - self - } - - /// Sets the option to always create a new file. - /// - /// This corresponds to [`std::fs::OpenOptions::create_new`]. - /// - /// [`std::fs::OpenOptions::create_new`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.create_new - pub fn create_new(&mut self, create_new: bool) -> &mut Self { - self.create_new = create_new; - self - } -} - -// TODO: Functions from OpenOptionsExt? diff --git a/crates/wasi-common/src/fs/permissions.rs b/crates/wasi-common/src/fs/permissions.rs deleted file mode 100644 index 498a1898ff..0000000000 --- a/crates/wasi-common/src/fs/permissions.rs +++ /dev/null @@ -1,37 +0,0 @@ -/// Representation of the various permissions on a file. -/// -/// This corresponds to [`std::fs::Permissions`]. -/// -/// TODO: Not yet implemented. -/// -/// [`std::fs::Permissions`]: https://doc.rust-lang.org/std/fs/struct.Permissions.html -#[derive(Eq, PartialEq, Clone)] -pub struct Permissions {} - -impl Permissions { - /// Returns true if these permissions describe a readonly (unwritable) file. - /// - /// This corresponds to [`std::fs::Permissions::readonly`]. - /// - /// TODO: Not yet implemented. - /// - /// [`std::fs::Permissions::readonly`]: https://doc.rust-lang.org/std/fs/struct.Permissions.html#method.readonly - pub fn readonly(&self) -> bool { - unimplemented!("Permissions::readonly"); - } - - /// Modifies the readonly flag for this set of permissions. - /// - /// This corresponds to [`std::fs::Permissions::set_readonly`]. - /// - /// TODO: Not yet implemented. - /// - /// [`std::fs::Permissions::set_readonly`]: https://doc.rust-lang.org/std/fs/struct.Permissions.html#method.set_readonly - pub fn set_readonly(&mut self, readonly: bool) { - unimplemented!("Permissions::set_readonly"); - } -} - -// TODO: functions from PermissionsExt? - -// TODO: impl Debug for Permissions diff --git a/crates/wasi-common/src/fs/readdir.rs b/crates/wasi-common/src/fs/readdir.rs deleted file mode 100644 index b3b3c4969c..0000000000 --- a/crates/wasi-common/src/fs/readdir.rs +++ /dev/null @@ -1,31 +0,0 @@ -use crate::fs::{DirEntry, Fd}; - -/// Iterator over the entries in a directory. -/// -/// This corresponds to [`std::fs::ReadDir`]. -/// -/// TODO: Not yet implemented. -/// -/// [`std::fs::ReadDir`]: https://doc.rust-lang.org/std/fs/struct.ReadDir.html -pub struct ReadDir { - fd: Fd, -} - -impl ReadDir { - /// Constructs a new instance of `Self` from the given raw WASI file descriptor. - pub unsafe fn from_raw_wasi_fd(fd: Fd) -> Self { - Self { fd } - } -} - -/// TODO: Not yet implemented. -impl Iterator for ReadDir { - type Item = DirEntry; - - /// TODO: Not yet implemented. - fn next(&mut self) -> Option { - unimplemented!("ReadDir::next"); - } -} - -// TODO: impl Debug for ReadDir diff --git a/crates/wasi-common/src/handle.rs b/crates/wasi-common/src/handle.rs deleted file mode 100644 index 9bef53eacc..0000000000 --- a/crates/wasi-common/src/handle.rs +++ /dev/null @@ -1,360 +0,0 @@ -pub use crate::wasi::types::{ - Advice, Dircookie, Dirent, Fdflags, Fdstat, Filedelta, Filesize, Filestat, Filetype, Fstflags, - Lookupflags, Oflags, Prestat, PrestatDir, Rights, Size, Timestamp, Whence, -}; -use crate::{Error, Result}; -use std::any::Any; -use std::fmt; -use std::io::{self, SeekFrom}; - -/// Represents rights of a `Handle`, either already held or required. -#[derive(Debug, Copy, Clone)] -pub struct HandleRights { - pub(crate) base: Rights, - pub(crate) inheriting: Rights, -} - -impl HandleRights { - /// Creates new `HandleRights` instance from `base` and `inheriting` rights. - pub fn new(base: Rights, inheriting: Rights) -> Self { - Self { base, inheriting } - } - - /// Creates new `HandleRights` instance from `base` rights only, keeping - /// `inheriting` set to none. - pub fn from_base(base: Rights) -> Self { - Self { - base, - inheriting: Rights::empty(), - } - } - - /// Creates new `HandleRights` instance with both `base` and `inheriting` - /// rights set to none. - pub fn empty() -> Self { - Self { - base: Rights::empty(), - inheriting: Rights::empty(), - } - } - - /// Checks if `other` is a subset of those rights. - pub fn contains(&self, other: Self) -> bool { - self.base.contains(other.base) && self.inheriting.contains(other.inheriting) - } - - /// Returns base rights. - pub fn base(&self) -> Rights { - self.base - } - - /// Returns inheriting rights. - pub fn inheriting(&self) -> Rights { - self.inheriting - } -} - -impl fmt::Display for HandleRights { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "HandleRights {{ base: {}, inheriting: {} }}", - self.base, self.inheriting - ) - } -} - -/// Generic interface for all WASI-compatible handles. We currently group these into two groups: -/// * OS-based resources (actual, real resources): `OsFile`, `OsDir`, `OsOther`, and `Stdio`, -/// * virtual files and directories: VirtualDir`, and `InMemoryFile`. -/// -/// # Constructing `Handle`s representing OS-based resources -/// -/// Each type of handle can either be constructed directly (see docs entry for a specific handle -/// type such as `OsFile`), or you can let the `wasi_common` crate's machinery work it out -/// automatically for you using `std::convert::TryInto` from `std::fs::File`: -/// -/// ```rust,no_run -/// use std::convert::TryInto; -/// use wasi_common::Handle; -/// use std::fs::OpenOptions; -/// -/// let some_file = OpenOptions::new().read(true).open("some_file").unwrap(); -/// let wasi_handle: Box = some_file.try_into().unwrap(); -/// ``` -pub trait Handle { - fn as_any(&self) -> &dyn Any; - fn try_clone(&self) -> io::Result>; - fn get_file_type(&self) -> Filetype; - fn get_rights(&self) -> HandleRights { - HandleRights::empty() - } - fn set_rights(&self, rights: HandleRights); - fn is_directory(&self) -> bool { - self.get_file_type() == Filetype::Directory - } - /// 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. - fn is_tty(&self) -> bool { - let file_type = self.get_file_type(); - let rights = self.get_rights(); - let required_rights = HandleRights::from_base(Rights::FD_SEEK | Rights::FD_TELL); - file_type == Filetype::CharacterDevice && rights.contains(required_rights) - } - // TODO perhaps should be a separate trait? - // FdOps - fn advise(&self, _advice: Advice, _offset: Filesize, _len: Filesize) -> Result<()> { - Err(Error::Badf) - } - fn allocate(&self, _offset: Filesize, _len: Filesize) -> Result<()> { - Err(Error::Badf) - } - fn datasync(&self) -> Result<()> { - Err(Error::Inval) - } - fn fdstat_get(&self) -> Result { - Ok(Fdflags::empty()) - } - fn fdstat_set_flags(&self, _fdflags: Fdflags) -> Result<()> { - Err(Error::Badf) - } - fn filestat_get(&self) -> Result { - Err(Error::Badf) - } - fn filestat_set_size(&self, _st_size: Filesize) -> Result<()> { - Err(Error::Badf) - } - fn filestat_set_times( - &self, - _atim: Timestamp, - _mtim: Timestamp, - _fst_flags: Fstflags, - ) -> Result<()> { - Err(Error::Badf) - } - fn preadv(&self, _buf: &mut [io::IoSliceMut], _offset: u64) -> Result { - Err(Error::Badf) - } - fn pwritev(&self, _buf: &[io::IoSlice], _offset: u64) -> Result { - Err(Error::Badf) - } - fn read_vectored(&self, _iovs: &mut [io::IoSliceMut]) -> Result { - Err(Error::Badf) - } - fn readdir<'a>( - &'a self, - _cookie: Dircookie, - ) -> Result> + 'a>> { - Err(Error::Badf) - } - fn seek(&self, _offset: SeekFrom) -> Result { - Err(Error::Badf) - } - fn sync(&self) -> Result<()> { - Ok(()) - } - fn write_vectored(&self, _iovs: &[io::IoSlice]) -> Result { - Err(Error::Badf) - } - // TODO perhaps should be a separate trait? - // PathOps - fn create_directory(&self, _path: &str) -> Result<()> { - Err(Error::Acces) - } - fn filestat_get_at(&self, _path: &str, _follow: bool) -> Result { - Err(Error::Acces) - } - fn filestat_set_times_at( - &self, - _path: &str, - _atim: Timestamp, - _mtim: Timestamp, - _fst_flags: Fstflags, - _follow: bool, - ) -> Result<()> { - Err(Error::Acces) - } - fn openat( - &self, - _path: &str, - _read: bool, - _write: bool, - _oflags: Oflags, - _fd_flags: Fdflags, - ) -> Result> { - Err(Error::Acces) - } - fn link( - &self, - _old_path: &str, - _new_handle: Box, - _new_path: &str, - _follow: bool, - ) -> Result<()> { - Err(Error::Acces) - } - fn readlink(&self, _path: &str, _buf: &mut [u8]) -> Result { - Err(Error::Acces) - } - fn readlinkat(&self, _path: &str) -> Result { - Err(Error::Acces) - } - fn remove_directory(&self, _path: &str) -> Result<()> { - Err(Error::Acces) - } - fn rename(&self, _old_path: &str, _new_handle: Box, _new_path: &str) -> Result<()> { - Err(Error::Acces) - } - fn symlink(&self, _old_path: &str, _new_path: &str) -> Result<()> { - Err(Error::Acces) - } - fn unlink_file(&self, _path: &str) -> Result<()> { - Err(Error::Acces) - } -} - -impl From for Filetype { - fn from(ftype: std::fs::FileType) -> Self { - if ftype.is_file() { - Self::RegularFile - } else if ftype.is_dir() { - Self::Directory - } else if ftype.is_symlink() { - Self::SymbolicLink - } else { - Self::Unknown - } - } -} - -pub(crate) trait AsBytes { - fn as_bytes(&self) -> Result>; -} - -impl AsBytes for Dirent { - fn as_bytes(&self) -> Result> { - use std::convert::TryInto; - use wiggle::GuestType; - - assert_eq!( - Self::guest_size(), - std::mem::size_of::() as _, - "guest repr of Dirent and host repr should match" - ); - - let offset = Self::guest_size().try_into()?; - let mut bytes: Vec = Vec::with_capacity(offset); - bytes.resize(offset, 0); - let ptr = bytes.as_mut_ptr() as *mut Self; - unsafe { ptr.write_unaligned(self.clone()) }; - Ok(bytes) - } -} - -pub(crate) trait RightsExt: Sized { - fn block_device_base() -> Self; - fn block_device_inheriting() -> Self; - fn character_device_base() -> Self; - fn character_device_inheriting() -> Self; - fn directory_base() -> Self; - fn directory_inheriting() -> Self; - fn regular_file_base() -> Self; - fn regular_file_inheriting() -> Self; - fn socket_base() -> Self; - fn socket_inheriting() -> Self; - fn tty_base() -> Self; - fn tty_inheriting() -> Self; -} - -impl RightsExt for Rights { - // Block and character device interaction is outside the scope of - // WASI. Simply allow everything. - fn block_device_base() -> Self { - Self::all() - } - fn block_device_inheriting() -> Self { - Self::all() - } - fn character_device_base() -> Self { - Self::all() - } - fn character_device_inheriting() -> Self { - Self::all() - } - - // Only allow directory operations on directories. Directories can only - // yield file descriptors to other directories and files. - fn directory_base() -> Self { - Self::FD_FDSTAT_SET_FLAGS - | Self::FD_SYNC - | Self::FD_ADVISE - | Self::PATH_CREATE_DIRECTORY - | Self::PATH_CREATE_FILE - | Self::PATH_LINK_SOURCE - | Self::PATH_LINK_TARGET - | Self::PATH_OPEN - | Self::FD_READDIR - | Self::PATH_READLINK - | Self::PATH_RENAME_SOURCE - | Self::PATH_RENAME_TARGET - | Self::PATH_FILESTAT_GET - | Self::PATH_FILESTAT_SET_SIZE - | Self::PATH_FILESTAT_SET_TIMES - | Self::FD_FILESTAT_GET - | Self::FD_FILESTAT_SET_TIMES - | Self::PATH_SYMLINK - | Self::PATH_UNLINK_FILE - | Self::PATH_REMOVE_DIRECTORY - | Self::POLL_FD_READWRITE - } - fn directory_inheriting() -> Self { - Self::all() ^ Self::SOCK_SHUTDOWN - } - - // Operations that apply to regular files. - fn regular_file_base() -> Self { - Self::FD_DATASYNC - | Self::FD_READ - | Self::FD_SEEK - | Self::FD_FDSTAT_SET_FLAGS - | Self::FD_SYNC - | Self::FD_TELL - | Self::FD_WRITE - | Self::FD_ADVISE - | Self::FD_ALLOCATE - | Self::FD_FILESTAT_GET - | Self::FD_FILESTAT_SET_SIZE - | Self::FD_FILESTAT_SET_TIMES - | Self::POLL_FD_READWRITE - } - fn regular_file_inheriting() -> Self { - Self::empty() - } - - // Operations that apply to sockets and socket pairs. - fn socket_base() -> Self { - Self::FD_READ - | Self::FD_FDSTAT_SET_FLAGS - | Self::FD_WRITE - | Self::FD_FILESTAT_GET - | Self::POLL_FD_READWRITE - | Self::SOCK_SHUTDOWN - } - fn socket_inheriting() -> Self { - Self::all() - } - - // Operations that apply to TTYs. - fn tty_base() -> Self { - Self::FD_READ - | Self::FD_FDSTAT_SET_FLAGS - | Self::FD_WRITE - | Self::FD_FILESTAT_GET - | Self::POLL_FD_READWRITE - } - fn tty_inheriting() -> Self { - Self::empty() - } -} -pub(crate) const DIRCOOKIE_START: Dircookie = 0; diff --git a/crates/wasi-common/src/lib.rs b/crates/wasi-common/src/lib.rs deleted file mode 100644 index 02a39f1c05..0000000000 --- a/crates/wasi-common/src/lib.rs +++ /dev/null @@ -1,45 +0,0 @@ -#![deny( - // missing_docs, - trivial_numeric_casts, - unused_extern_crates, - unstable_features, - clippy::use_self -)] -#![warn(unused_import_braces)] -#![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../clippy.toml")))] -#![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))] -#![cfg_attr( - feature = "cargo-clippy", - warn( - clippy::float_arithmetic, - clippy::mut_mut, - clippy::nonminimal_bool, - clippy::map_unwrap_or, - clippy::clippy::unicode_not_nfc, - clippy::use_self - ) -)] - -mod ctx; -mod entry; -mod error; -mod fdpool; -pub mod fs; -mod handle; -mod path; -mod sandboxed_tty_writer; -pub(crate) mod sched; -pub mod snapshots; -mod string_array; -mod sys; -pub mod virtfs; -pub mod wasi; - -pub use ctx::{WasiCtx, WasiCtxBuilder, WasiCtxBuilderError}; -pub use error::{Error, Result}; -pub use handle::{Handle, HandleRights}; -pub use sys::osdir::OsDir; -pub use sys::osfile::OsFile; -pub use sys::osother::OsOther; -pub use sys::preopen_dir; -pub use virtfs::{FileContents, VirtualDirEntry}; diff --git a/crates/wasi-common/src/path.rs b/crates/wasi-common/src/path.rs deleted file mode 100644 index df04e48f41..0000000000 --- a/crates/wasi-common/src/path.rs +++ /dev/null @@ -1,190 +0,0 @@ -use crate::entry::Entry; -use crate::handle::{Fdflags, Filetype, Handle, HandleRights, Lookupflags, Oflags}; -use crate::{Error, Result}; -use std::path::{Component, Path}; -use std::str; - -pub(crate) use crate::sys::path::{from_host, open_rights}; - -/// 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 get( - entry: &Entry, - required_rights: HandleRights, - dirflags: Lookupflags, - path: &str, - needs_final_component: bool, -) -> Result<(Box, String)> { - const MAX_SYMLINK_EXPANSIONS: usize = 128; - - tracing::trace!(path = path); - - if path.contains('\0') { - // if contains NUL, return Ilseq - return Err(Error::Ilseq); - } - - if entry.get_file_type() != Filetype::Directory { - // if `dirfd` doesn't refer to a directory, return `Notdir`. - return Err(Error::Notdir); - } - - let handle = entry.as_handle(required_rights)?; - let dirfd = handle.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!(cur_path = tracing::field::display(&cur_path), "path get"); - - let ends_with_slash = cur_path.ends_with('/'); - let mut components = Path::new(&cur_path).components(); - let head = match components.next() { - None => return Err(Error::Noent), - Some(p) => p, - }; - let tail = components.as_path(); - - if tail.components().next().is_some() { - let mut tail = from_host(tail.as_os_str())?; - if ends_with_slash { - tail.push('/'); - } - path_stack.push(tail); - } - - tracing::debug!(path_stack = tracing::field::debug(&path_stack), "path_get"); - - match head { - Component::Prefix(_) | Component::RootDir => { - // path is absolute! - return Err(Error::Notcapable); - } - Component::CurDir => { - // "." so skip - } - Component::ParentDir => { - // ".." so pop a dir - let _ = dir_stack.pop().ok_or(Error::Notcapable)?; - - // we're not allowed to pop past the original directory - if dir_stack.is_empty() { - return Err(Error::Notcapable); - } - } - Component::Normal(head) => { - let mut head = from_host(head)?; - if ends_with_slash { - // preserve trailing slash - head.push('/'); - } - - if !path_stack.is_empty() || (ends_with_slash && !needs_final_component) { - let fd = dir_stack.last().ok_or(Error::Notcapable)?; - match fd.openat( - &head, - false, - false, - Oflags::DIRECTORY, - Fdflags::empty(), - ) { - Ok(new_dir) => { - dir_stack.push(new_dir); - } - Err(e) => { - match e { - Error::Loop | Error::Mlink | Error::Notdir => - // Check to see if it was a symlink. Linux indicates - // this with ENOTDIR because of the O_DIRECTORY flag. - { - // attempt symlink expansion - let fd = dir_stack.last().ok_or(Error::Notcapable)?; - let mut link_path = fd.readlinkat(&head)?; - symlink_expansions += 1; - if symlink_expansions > MAX_SYMLINK_EXPANSIONS { - return Err(Error::Loop); - } - - 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.contains(Lookupflags::SYMLINK_FOLLOW) - { - // if there's a trailing slash, or if `LOOKUP_SYMLINK_FOLLOW` is set, attempt - // symlink expansion - let fd = dir_stack.last().ok_or(Error::Notcapable)?; - match fd.readlinkat(&head) { - Ok(mut link_path) => { - symlink_expansions += 1; - if symlink_expansions > MAX_SYMLINK_EXPANSIONS { - return Err(Error::Loop); - } - - if head.ends_with('/') { - link_path.push('/'); - } - - tracing::debug!( - "attempted symlink expansion link_path={:?}", - link_path - ); - - path_stack.push(link_path); - continue; - } - Err(Error::Inval) | Err(Error::Noent) | Err(Error::Notdir) => { - // this handles the cases when trying to link to - // a destination that already exists, and the target - // path contains a slash - } - Err(e) => { - return Err(e); - } - } - } - - // not a symlink, so we're done; - return Ok((dir_stack.pop().ok_or(Error::Notcapable)?, 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((dir_stack.pop().ok_or(Error::Notcapable)?, String::from("."))); - } - } - } -} diff --git a/crates/wasi-common/src/sandboxed_tty_writer.rs b/crates/wasi-common/src/sandboxed_tty_writer.rs deleted file mode 100644 index 3152926157..0000000000 --- a/crates/wasi-common/src/sandboxed_tty_writer.rs +++ /dev/null @@ -1,199 +0,0 @@ -use std::io::{IoSlice, Result, Write}; - -/// An adapter around a `Write` stream that guarantees that its output -/// is valid UTF-8 and contains no control characters. It does this by -/// replacing characters with inert control pictures and replacement -/// characters. -pub(crate) struct SandboxedTTYWriter<'writer, Writer> -where - Writer: Write, -{ - inner: &'writer mut Writer, -} - -impl<'writer, Writer> SandboxedTTYWriter<'writer, Writer> -where - Writer: Write, -{ - /// Construct a new `SandboxedTTYWriter` with the given inner `Writer`. - pub(crate) fn new(inner: &'writer mut Writer) -> Self { - Self { inner } - } - - /// Write a single character to the output. - pub(crate) fn write_char(&mut self, c: char) -> Result<()> { - self.inner.write( - match c { - '\u{0000}' => '␀', - '\u{0001}' => '␁', - '\u{0002}' => '␂', - '\u{0003}' => '␃', - '\u{0004}' => '␄', - '\u{0005}' => '␅', - '\u{0006}' => '␆', - '\u{0007}' => '␇', - '\u{0008}' => '␈', - '\u{0009}' => '\t', - '\u{000A}' => '\n', - '\u{000B}' => '␋', - '\u{000C}' => '␌', - '\u{000D}' => '\r', - '\u{000E}' => '␎', - '\u{000F}' => '␏', - '\u{0010}' => '␐', - '\u{0011}' => '␑', - '\u{0012}' => '␒', - '\u{0013}' => '␓', - '\u{0014}' => '␔', - '\u{0015}' => '␕', - '\u{0016}' => '␖', - '\u{0017}' => '␗', - '\u{0018}' => '␘', - '\u{0019}' => '␙', - '\u{001A}' => '␚', - '\u{001B}' => '␛', - '\u{001C}' => '␜', - '\u{001D}' => '␝', - '\u{001E}' => '␞', - '\u{001F}' => '␟', - '\u{007F}' => '␡', - x if x.is_control() => '�', - x => x, - } - .encode_utf8(&mut [0; 4]) // UTF-8 encoding of a `char` is at most 4 bytes. - .as_bytes(), - )?; - - Ok(()) - } - - /// Write a string to the output. - pub(crate) fn write_str(&mut self, s: &str) -> Result { - let mut result = 0; - - for c in s.chars() { - self.write_char(c)?; - // Note that we use the encoding length of the given char, rather than - // how many bytes we actually wrote, because our users don't know about - // what's really being written. - result += c.len_utf8(); - } - - Ok(result) - } -} - -impl<'writer, Writer> Write for SandboxedTTYWriter<'writer, Writer> -where - Writer: Write, -{ - fn write(&mut self, buf: &[u8]) -> Result { - let mut input = buf; - let mut result = 0; - - // Decode the string without heap-allocating it. See the example here - // for more details: - // https://doc.rust-lang.org/std/str/struct.Utf8Error.html#examples - loop { - match std::str::from_utf8(input) { - Ok(valid) => { - result += self.write_str(valid)?; - break; - } - Err(error) => { - let (valid, after_valid) = input.split_at(error.valid_up_to()); - result += self.write_str(unsafe { std::str::from_utf8_unchecked(valid) })?; - self.write_char('�')?; - - if let Some(invalid_sequence_length) = error.error_len() { - // An invalid sequence was encountered. Tell the application we've - // written those bytes (though actually, we replaced them with U+FFFD). - result += invalid_sequence_length; - // Set up `input` to resume writing after the end of the sequence. - input = &after_valid[invalid_sequence_length..]; - } else { - // The end of the buffer was encountered unexpectedly. Tell the application - // we've written out the remainder of the buffer. - result += after_valid.len(); - break; - } - } - } - } - - return Ok(result); - } - - fn write_vectored(&mut self, bufs: &[IoSlice]) -> Result { - // Terminal output is [not expected to be atomic], so just write all the - // individual buffers in sequence. - // - // [not expected to be atomic]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/read.html#tag_16_474_08 - let mut total_written = 0; - - for buf in bufs { - let written = self.write(buf)?; - - total_written += written; - - // Stop at the first point where the OS writes less than we asked. - if written < buf.len() { - break; - } - } - - Ok(total_written) - } - - fn flush(&mut self) -> Result<()> { - self.inner.flush() - } -} - -#[cfg(test)] -mod tests { - use super::SandboxedTTYWriter; - use std::io::{Result, Write}; - - #[test] - fn basic() -> Result<()> { - let mut buffer = Vec::new(); - let mut safe = SandboxedTTYWriter::new(&mut buffer); - safe.write_str("a\0b\u{0080}")?; - safe.write_char('\u{0007}')?; - safe.write(&[0x80])?; - safe.write(&[0xed, 0xa0, 0x80, 0xff, 0xfe])?; - assert_eq!( - buffer, - "a\u{2400}b\u{FFFD}\u{2407}\u{FFFD}\u{FFFD}\u{FFFD}\u{FFFD}\u{FFFD}\u{FFFD}".as_bytes() - ); - Ok(()) - } - - #[test] - fn how_many_replacements() -> Result<()> { - // See https://hsivonen.fi/broken-utf-8/ for background. - - let mut buffer = Vec::new(); - let mut safe = SandboxedTTYWriter::new(&mut buffer); - safe.write(&[0x80, 0x80, 0x80, 0x80])?; - assert_eq!(buffer, "\u{FFFD}\u{FFFD}\u{FFFD}\u{FFFD}".as_bytes()); - - let mut buffer = Vec::new(); - let mut safe = SandboxedTTYWriter::new(&mut buffer); - safe.write(&[0xF0, 0x80, 0x80, 0x41])?; - assert_eq!(buffer, "\u{FFFD}\u{FFFD}\u{FFFD}A".as_bytes()); - - let mut buffer = Vec::new(); - let mut safe = SandboxedTTYWriter::new(&mut buffer); - safe.write(&[0xF0, 0x80, 0x80])?; - assert_eq!(buffer, "\u{FFFD}\u{FFFD}\u{FFFD}".as_bytes()); - - let mut buffer = Vec::new(); - let mut safe = SandboxedTTYWriter::new(&mut buffer); - safe.write(&[0xF4, 0x80, 0x80, 0xC0])?; - assert_eq!(buffer, "\u{FFFD}\u{FFFD}".as_bytes()); - - Ok(()) - } -} diff --git a/crates/wasi-common/src/sched.rs b/crates/wasi-common/src/sched.rs deleted file mode 100644 index b51ebaa123..0000000000 --- a/crates/wasi-common/src/sched.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::entry::EntryHandle; -pub use crate::wasi::types::{ - Clockid, Errno, Event, EventFdReadwrite, Eventrwflags, Eventtype, Subclockflags, - SubscriptionClock, Timestamp, Userdata, -}; -#[derive(Debug, Copy, Clone)] -pub struct ClockEventData { - pub delay: u128, // delay is expressed in nanoseconds - pub userdata: Userdata, -} - -#[derive(Debug)] -pub struct FdEventData { - pub handle: EntryHandle, - pub r#type: Eventtype, - pub userdata: Userdata, -} diff --git a/crates/wasi-common/src/snapshots/mod.rs b/crates/wasi-common/src/snapshots/mod.rs deleted file mode 100644 index ed30aed53d..0000000000 --- a/crates/wasi-common/src/snapshots/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -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 deleted file mode 100644 index fac1358fa2..0000000000 --- a/crates/wasi-common/src/snapshots/wasi_snapshot_preview1.rs +++ /dev/null @@ -1,862 +0,0 @@ -use crate::entry::{Entry, EntryHandle}; -use crate::handle::{AsBytes, HandleRights}; -use crate::sys::{clock, poll}; -use crate::wasi::types; -use crate::wasi::wasi_snapshot_preview1::WasiSnapshotPreview1; -use crate::{path, sched, Error, Result, WasiCtx}; -use std::cmp::min; -use std::convert::TryInto; -use std::io::{self, SeekFrom}; -use std::ops::Deref; -use tracing::{debug, trace}; -use wiggle::{GuestPtr, GuestSliceMut}; - -impl<'a> WasiSnapshotPreview1 for WasiCtx { - fn args_get<'b>( - &self, - argv: &GuestPtr<'b, GuestPtr<'b, u8>>, - argv_buf: &GuestPtr<'b, u8>, - ) -> Result<()> { - self.args.write_to_guest(argv_buf, argv) - } - - fn args_sizes_get(&self) -> Result<(types::Size, types::Size)> { - Ok((self.args.number_elements, self.args.cumulative_size)) - } - - fn environ_get<'b>( - &self, - environ: &GuestPtr<'b, GuestPtr<'b, u8>>, - environ_buf: &GuestPtr<'b, u8>, - ) -> Result<()> { - self.env.write_to_guest(environ_buf, environ) - } - - fn environ_sizes_get(&self) -> Result<(types::Size, types::Size)> { - Ok((self.env.number_elements, self.env.cumulative_size)) - } - - fn clock_res_get(&self, id: types::Clockid) -> Result { - let resolution = clock::res_get(id)?; - Ok(resolution) - } - - fn clock_time_get( - &self, - id: types::Clockid, - _precision: types::Timestamp, - ) -> Result { - let time = clock::time_get(id)?; - Ok(time) - } - - fn fd_advise( - &self, - fd: types::Fd, - offset: types::Filesize, - len: types::Filesize, - advice: types::Advice, - ) -> Result<()> { - let required_rights = HandleRights::from_base(types::Rights::FD_ADVISE); - let entry = self.get_entry(fd)?; - entry - .as_handle(required_rights)? - .advise(advice, offset, len) - } - - fn fd_allocate( - &self, - fd: types::Fd, - offset: types::Filesize, - len: types::Filesize, - ) -> Result<()> { - let required_rights = HandleRights::from_base(types::Rights::FD_ALLOCATE); - let entry = self.get_entry(fd)?; - entry.as_handle(required_rights)?.allocate(offset, len) - } - - fn fd_close(&self, fd: types::Fd) -> Result<()> { - if let Ok(fe) = self.get_entry(fd) { - // can't close preopened files - if fe.preopen_path.is_some() { - return Err(Error::Notsup); - } - } - let _ = self.remove_entry(fd)?; - Ok(()) - } - - fn fd_datasync(&self, fd: types::Fd) -> Result<()> { - let required_rights = HandleRights::from_base(types::Rights::FD_DATASYNC); - let entry = self.get_entry(fd)?; - entry.as_handle(required_rights)?.datasync() - } - - fn fd_fdstat_get(&self, fd: types::Fd) -> Result { - let entry = self.get_entry(fd)?; - let file = entry.as_handle(HandleRights::empty())?; - let fs_flags = file.fdstat_get()?; - let rights = entry.get_rights(); - let fdstat = types::Fdstat { - fs_filetype: entry.get_file_type(), - fs_rights_base: rights.base, - fs_rights_inheriting: rights.inheriting, - fs_flags, - }; - Ok(fdstat) - } - - fn fd_fdstat_set_flags(&self, fd: types::Fd, flags: types::Fdflags) -> Result<()> { - let required_rights = HandleRights::from_base(types::Rights::FD_FDSTAT_SET_FLAGS); - let entry = self.get_entry(fd)?; - entry.as_handle(required_rights)?.fdstat_set_flags(flags) - } - - fn fd_fdstat_set_rights( - &self, - fd: types::Fd, - fs_rights_base: types::Rights, - fs_rights_inheriting: types::Rights, - ) -> Result<()> { - let rights = HandleRights::new(fs_rights_base, fs_rights_inheriting); - let entry = self.get_entry(fd)?; - if !entry.get_rights().contains(rights) { - return Err(Error::Notcapable); - } - entry.set_rights(rights); - Ok(()) - } - - fn fd_filestat_get(&self, fd: types::Fd) -> Result { - let required_rights = HandleRights::from_base(types::Rights::FD_FILESTAT_GET); - let entry = self.get_entry(fd)?; - let host_filestat = entry.as_handle(required_rights)?.filestat_get()?; - Ok(host_filestat) - } - - fn fd_filestat_set_size(&self, fd: types::Fd, size: types::Filesize) -> Result<()> { - let required_rights = HandleRights::from_base(types::Rights::FD_FILESTAT_SET_SIZE); - let entry = self.get_entry(fd)?; - entry.as_handle(required_rights)?.filestat_set_size(size) - } - - fn fd_filestat_set_times( - &self, - fd: types::Fd, - atim: types::Timestamp, - mtim: types::Timestamp, - fst_flags: types::Fstflags, - ) -> Result<()> { - let required_rights = HandleRights::from_base(types::Rights::FD_FILESTAT_SET_TIMES); - let entry = self.get_entry(fd)?; - entry - .as_handle(required_rights)? - .filestat_set_times(atim, mtim, fst_flags) - } - - fn fd_pread( - &self, - fd: types::Fd, - iovs: &types::IovecArray<'_>, - offset: types::Filesize, - ) -> Result { - let mut guest_slices: Vec> = Vec::new(); - for iov_ptr in iovs.iter() { - let iov_ptr = iov_ptr?; - let iov: types::Iovec = iov_ptr.read()?; - guest_slices.push(iov.buf.as_array(iov.buf_len).as_slice_mut()?); - } - - let required_rights = - HandleRights::from_base(types::Rights::FD_READ | types::Rights::FD_SEEK); - let entry = self.get_entry(fd)?; - if offset > i64::max_value() as u64 { - return Err(Error::Io); - } - - let host_nread = { - let mut buf = guest_slices - .iter_mut() - .map(|s| io::IoSliceMut::new(&mut *s)) - .collect::>>(); - entry - .as_handle(required_rights)? - .preadv(&mut buf, offset)? - .try_into()? - }; - Ok(host_nread) - } - - fn fd_prestat_get(&self, fd: types::Fd) -> Result { - // TODO: should we validate any rights here? - let entry = self.get_entry(fd)?; - let po_path = entry.preopen_path.as_ref().ok_or(Error::Notsup)?; - if entry.get_file_type() != types::Filetype::Directory { - return Err(Error::Notdir); - } - - let path = path::from_host(po_path.as_os_str())?; - let prestat = types::PrestatDir { - pr_name_len: path.len().try_into()?, - }; - Ok(types::Prestat::Dir(prestat)) - } - - fn fd_prestat_dir_name( - &self, - fd: types::Fd, - path: &GuestPtr, - path_len: types::Size, - ) -> Result<()> { - // TODO: should we validate any rights here? - let entry = self.get_entry(fd)?; - let po_path = entry.preopen_path.as_ref().ok_or(Error::Notsup)?; - if entry.get_file_type() != types::Filetype::Directory { - return Err(Error::Notdir); - } - - let host_path = path::from_host(po_path.as_os_str())?; - let host_path_len = host_path.len().try_into()?; - - if host_path_len > path_len { - return Err(Error::Nametoolong); - } - - trace!(" | path='{}'", host_path); - - path.as_array(host_path_len) - .copy_from_slice(host_path.as_bytes())?; - - Ok(()) - } - - fn fd_pwrite( - &self, - fd: types::Fd, - ciovs: &types::CiovecArray<'_>, - offset: types::Filesize, - ) -> Result { - let mut guest_slices = Vec::new(); - for ciov_ptr in ciovs.iter() { - let ciov_ptr = ciov_ptr?; - let ciov: types::Ciovec = ciov_ptr.read()?; - guest_slices.push(ciov.buf.as_array(ciov.buf_len).as_slice()?); - } - - let required_rights = - HandleRights::from_base(types::Rights::FD_WRITE | types::Rights::FD_SEEK); - let entry = self.get_entry(fd)?; - - if offset > i64::max_value() as u64 { - return Err(Error::Io); - } - - let host_nwritten = { - let buf: Vec = - guest_slices.iter().map(|s| io::IoSlice::new(&*s)).collect(); - entry - .as_handle(required_rights)? - .pwritev(&buf, offset)? - .try_into()? - }; - Ok(host_nwritten) - } - - fn fd_read(&self, fd: types::Fd, iovs: &types::IovecArray<'_>) -> Result { - let mut guest_slices = Vec::new(); - for iov_ptr in iovs.iter() { - let iov_ptr = iov_ptr?; - let iov: types::Iovec = iov_ptr.read()?; - guest_slices.push(iov.buf.as_array(iov.buf_len).as_slice_mut()?); - } - - let required_rights = HandleRights::from_base(types::Rights::FD_READ); - let entry = self.get_entry(fd)?; - let host_nread = { - let mut slices: Vec = guest_slices - .iter_mut() - .map(|s| io::IoSliceMut::new(&mut *s)) - .collect(); - entry - .as_handle(required_rights)? - .read_vectored(&mut slices)? - .try_into()? - }; - - Ok(host_nread) - } - - fn fd_readdir( - &self, - fd: types::Fd, - buf: &GuestPtr, - buf_len: types::Size, - cookie: types::Dircookie, - ) -> Result { - let required_rights = HandleRights::from_base(types::Rights::FD_READDIR); - let entry = self.get_entry(fd)?; - - let mut bufused = 0; - let mut buf = buf.clone(); - for pair in entry.as_handle(required_rights)?.readdir(cookie)? { - let (dirent, name) = pair?; - let dirent_raw = dirent.as_bytes()?; - let dirent_len: types::Size = dirent_raw.len().try_into()?; - let name_raw = name.as_bytes(); - let name_len = name_raw.len().try_into()?; - let offset = dirent_len.checked_add(name_len).ok_or(Error::Overflow)?; - - // Copy as many bytes of the dirent as we can, up to the end of the buffer. - let dirent_copy_len = min(dirent_len, buf_len - bufused); - buf.as_array(dirent_copy_len) - .copy_from_slice(&dirent_raw[..dirent_copy_len as usize])?; - - // If the dirent struct wasn't copied entirely, return that we - // filled the buffer, which tells libc that we're not at EOF. - if dirent_copy_len < dirent_len { - return Ok(buf_len); - } - - buf = buf.add(dirent_copy_len)?; - - // Copy as many bytes of the name as we can, up to the end of the buffer. - let name_copy_len = min(name_len, buf_len - bufused); - buf.as_array(name_copy_len) - .copy_from_slice(&name_raw[..name_copy_len as usize])?; - - // If the dirent struct wasn't copied entirely, return that we - // filled the buffer, which tells libc that we're not at EOF. - if name_copy_len < name_len { - return Ok(buf_len); - } - - buf = buf.add(name_copy_len)?; - - bufused += offset; - } - - Ok(bufused) - } - - fn fd_renumber(&self, from: types::Fd, to: types::Fd) -> Result<()> { - if !self.contains_entry(from) { - return Err(Error::Badf); - } - - // 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. - if let Ok(from_fe) = self.get_entry(from) { - if from_fe.preopen_path.is_some() { - return Err(Error::Notsup); - } - } - if let Ok(to_fe) = self.get_entry(to) { - if to_fe.preopen_path.is_some() { - return Err(Error::Notsup); - } - } - let fe = self.remove_entry(from)?; - self.insert_entry_at(to, fe); - Ok(()) - } - - fn fd_seek( - &self, - fd: types::Fd, - offset: types::Filedelta, - whence: types::Whence, - ) -> Result { - let base = if offset == 0 && whence == types::Whence::Cur { - types::Rights::FD_TELL - } else { - types::Rights::FD_SEEK | types::Rights::FD_TELL - }; - let required_rights = HandleRights::from_base(base); - let entry = self.get_entry(fd)?; - let pos = match whence { - types::Whence::Cur => SeekFrom::Current(offset), - types::Whence::End => SeekFrom::End(offset), - types::Whence::Set => SeekFrom::Start(offset as u64), - }; - let host_newoffset = entry.as_handle(required_rights)?.seek(pos)?; - Ok(host_newoffset) - } - - fn fd_sync(&self, fd: types::Fd) -> Result<()> { - let required_rights = HandleRights::from_base(types::Rights::FD_SYNC); - let entry = self.get_entry(fd)?; - entry.as_handle(required_rights)?.sync() - } - - fn fd_tell(&self, fd: types::Fd) -> Result { - let required_rights = HandleRights::from_base(types::Rights::FD_TELL); - let entry = self.get_entry(fd)?; - let host_offset = entry - .as_handle(required_rights)? - .seek(SeekFrom::Current(0))?; - Ok(host_offset) - } - - fn fd_write(&self, fd: types::Fd, ciovs: &types::CiovecArray<'_>) -> Result { - let mut guest_slices = Vec::new(); - for ciov_ptr in ciovs.iter() { - let ciov_ptr = ciov_ptr?; - let ciov: types::Ciovec = ciov_ptr.read()?; - guest_slices.push(ciov.buf.as_array(ciov.buf_len).as_slice()?); - } - let required_rights = HandleRights::from_base(types::Rights::FD_WRITE); - let entry = self.get_entry(fd)?; - let host_nwritten = { - let slices: Vec = - guest_slices.iter().map(|s| io::IoSlice::new(&*s)).collect(); - entry - .as_handle(required_rights)? - .write_vectored(&slices)? - .try_into()? - }; - Ok(host_nwritten) - } - - fn path_create_directory(&self, dirfd: types::Fd, path: &GuestPtr<'_, str>) -> Result<()> { - let required_rights = HandleRights::from_base( - types::Rights::PATH_OPEN | types::Rights::PATH_CREATE_DIRECTORY, - ); - let entry = self.get_entry(dirfd)?; - let path = path.as_str()?; - let (dirfd, path) = path::get( - &entry, - required_rights, - types::Lookupflags::empty(), - path.deref(), - false, - )?; - dirfd.create_directory(&path) - } - - fn path_filestat_get( - &self, - dirfd: types::Fd, - flags: types::Lookupflags, - path: &GuestPtr<'_, str>, - ) -> Result { - let required_rights = HandleRights::from_base(types::Rights::PATH_FILESTAT_GET); - let entry = self.get_entry(dirfd)?; - let path = path.as_str()?; - let (dirfd, path) = path::get(&entry, required_rights, flags, path.deref(), false)?; - let host_filestat = - dirfd.filestat_get_at(&path, flags.contains(types::Lookupflags::SYMLINK_FOLLOW))?; - Ok(host_filestat) - } - - fn path_filestat_set_times( - &self, - dirfd: types::Fd, - flags: types::Lookupflags, - path: &GuestPtr<'_, str>, - atim: types::Timestamp, - mtim: types::Timestamp, - fst_flags: types::Fstflags, - ) -> Result<()> { - let required_rights = HandleRights::from_base(types::Rights::PATH_FILESTAT_SET_TIMES); - let entry = self.get_entry(dirfd)?; - let path = path.as_str()?; - let (dirfd, path) = path::get(&entry, required_rights, flags, path.deref(), false)?; - dirfd.filestat_set_times_at( - &path, - atim, - mtim, - fst_flags, - flags.contains(types::Lookupflags::SYMLINK_FOLLOW), - )?; - Ok(()) - } - - fn path_link( - &self, - old_fd: types::Fd, - old_flags: types::Lookupflags, - old_path: &GuestPtr<'_, str>, - new_fd: types::Fd, - new_path: &GuestPtr<'_, str>, - ) -> Result<()> { - let required_rights = HandleRights::from_base(types::Rights::PATH_LINK_SOURCE); - let old_entry = self.get_entry(old_fd)?; - let (old_dirfd, old_path) = { - // Borrow old_path for just this scope - let old_path = old_path.as_str()?; - path::get( - &old_entry, - required_rights, - types::Lookupflags::empty(), - old_path.deref(), - false, - )? - }; - let required_rights = HandleRights::from_base(types::Rights::PATH_LINK_TARGET); - let new_entry = self.get_entry(new_fd)?; - let (new_dirfd, new_path) = { - // Borrow new_path for just this scope - let new_path = new_path.as_str()?; - path::get( - &new_entry, - required_rights, - types::Lookupflags::empty(), - new_path.deref(), - false, - )? - }; - old_dirfd.link( - &old_path, - new_dirfd, - &new_path, - old_flags.contains(types::Lookupflags::SYMLINK_FOLLOW), - ) - } - - fn path_open( - &self, - dirfd: types::Fd, - dirflags: types::Lookupflags, - path: &GuestPtr<'_, str>, - oflags: types::Oflags, - fs_rights_base: types::Rights, - fs_rights_inheriting: types::Rights, - fdflags: types::Fdflags, - ) -> Result { - let needed_rights = path::open_rights( - &HandleRights::new(fs_rights_base, fs_rights_inheriting), - oflags, - fdflags, - ); - trace!(" | needed_rights={}", needed_rights); - let entry = self.get_entry(dirfd)?; - let (dirfd, path) = { - let path = path.as_str()?; - path::get( - &entry, - needed_rights, - dirflags, - path.deref(), - oflags & types::Oflags::CREAT != types::Oflags::empty(), - )? - }; - // which open mode do we need? - let read = fs_rights_base & (types::Rights::FD_READ | types::Rights::FD_READDIR) - != types::Rights::empty(); - let write = fs_rights_base - & (types::Rights::FD_DATASYNC - | types::Rights::FD_WRITE - | types::Rights::FD_ALLOCATE - | types::Rights::FD_FILESTAT_SET_SIZE) - != types::Rights::empty(); - trace!( - " | calling path_open impl: read={}, write={}", - read, - write - ); - let fd = dirfd.openat(&path, read, write, oflags, fdflags)?; - let entry = Entry::new(EntryHandle::from(fd)); - // We need to manually deny the rights which are not explicitly requested - // because Entry::from will assign maximal consistent rights. - let mut rights = entry.get_rights(); - rights.base &= fs_rights_base; - rights.inheriting &= fs_rights_inheriting; - entry.set_rights(rights); - let guest_fd = self.insert_entry(entry)?; - Ok(guest_fd) - } - - fn path_readlink( - &self, - dirfd: types::Fd, - path: &GuestPtr<'_, str>, - buf: &GuestPtr, - buf_len: types::Size, - ) -> Result { - let required_rights = HandleRights::from_base(types::Rights::PATH_READLINK); - let entry = self.get_entry(dirfd)?; - let (dirfd, path) = { - // borrow path for just this scope - let path = path.as_str()?; - path::get( - &entry, - required_rights, - types::Lookupflags::empty(), - path.deref(), - false, - )? - }; - let mut slice = buf.as_array(buf_len).as_slice_mut()?; - let host_bufused = dirfd.readlink(&path, &mut *slice)?.try_into()?; - Ok(host_bufused) - } - - fn path_remove_directory(&self, dirfd: types::Fd, path: &GuestPtr<'_, str>) -> Result<()> { - let required_rights = HandleRights::from_base(types::Rights::PATH_REMOVE_DIRECTORY); - let entry = self.get_entry(dirfd)?; - let (dirfd, path) = { - let path = path.as_str()?; - path::get( - &entry, - required_rights, - types::Lookupflags::empty(), - path.deref(), - true, - )? - }; - dirfd.remove_directory(&path) - } - - fn path_rename( - &self, - old_fd: types::Fd, - old_path: &GuestPtr<'_, str>, - new_fd: types::Fd, - new_path: &GuestPtr<'_, str>, - ) -> Result<()> { - let required_rights = HandleRights::from_base(types::Rights::PATH_RENAME_SOURCE); - let entry = self.get_entry(old_fd)?; - let (old_dirfd, old_path) = { - let old_path = old_path.as_str()?; - path::get( - &entry, - required_rights, - types::Lookupflags::empty(), - old_path.deref(), - true, - )? - }; - let required_rights = HandleRights::from_base(types::Rights::PATH_RENAME_TARGET); - let entry = self.get_entry(new_fd)?; - let (new_dirfd, new_path) = { - let new_path = new_path.as_str()?; - path::get( - &entry, - required_rights, - types::Lookupflags::empty(), - new_path.deref(), - true, - )? - }; - old_dirfd.rename(&old_path, new_dirfd, &new_path) - } - - fn path_symlink( - &self, - old_path: &GuestPtr<'_, str>, - dirfd: types::Fd, - new_path: &GuestPtr<'_, str>, - ) -> Result<()> { - let required_rights = HandleRights::from_base(types::Rights::PATH_SYMLINK); - let entry = self.get_entry(dirfd)?; - let (new_fd, new_path) = { - let new_path = new_path.as_str()?; - path::get( - &entry, - required_rights, - types::Lookupflags::empty(), - new_path.deref(), - true, - )? - }; - let old_path = old_path.as_str()?; - trace!(old_path = old_path.deref()); - new_fd.symlink(&old_path, &new_path) - } - - fn path_unlink_file(&self, dirfd: types::Fd, path: &GuestPtr<'_, str>) -> Result<()> { - let required_rights = HandleRights::from_base(types::Rights::PATH_UNLINK_FILE); - let entry = self.get_entry(dirfd)?; - let (dirfd, path) = { - let path = path.as_str()?; - path::get( - &entry, - required_rights, - types::Lookupflags::empty(), - path.deref(), - false, - )? - }; - dirfd.unlink_file(&path)?; - Ok(()) - } - - fn poll_oneoff( - &self, - in_: &GuestPtr, - out: &GuestPtr, - nsubscriptions: types::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); - } - - 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, status: types::Exitcode) -> wiggle::Trap { - // Check that the status is within WASI's range. - if status < 126 { - wiggle::Trap::I32Exit(status as i32) - } else { - wiggle::Trap::String("exit with invalid exit status outside of [0..126)".to_owned()) - } - } - - fn proc_raise(&self, _sig: types::Signal) -> Result<()> { - Err(Error::Unsupported("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)> { - Err(Error::Unsupported("sock_recv")) - } - - fn sock_send( - &self, - _fd: types::Fd, - _si_data: &types::CiovecArray<'_>, - _si_flags: types::Siflags, - ) -> Result { - Err(Error::Unsupported("sock_send")) - } - - fn sock_shutdown(&self, _fd: types::Fd, _how: types::Sdflags) -> Result<()> { - Err(Error::Unsupported("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(); - - // As mandated by the WASI spec: - // > If `nsubscriptions` is 0, returns `errno::inval`. - if subscriptions.is_empty() { - return Err(Error::Inval); - } - - for subscription in subscriptions { - match &subscription.u { - types::SubscriptionU::Clock(clock) => { - let delay = clock::to_relative_ns_delay(&clock)?; - debug!( - clock = tracing::field::debug(&clock), - delay_ns = tracing::field::debug(delay), - "poll_oneoff" - ); - let current = sched::ClockEventData { - delay, - userdata: subscription.userdata, - }; - let timeout = timeout.get_or_insert(current); - if current.delay < timeout.delay { - *timeout = current; - } - } - types::SubscriptionU::FdRead(fd_read) => { - let fd = fd_read.file_descriptor; - let required_rights = HandleRights::from_base( - types::Rights::FD_READ | types::Rights::POLL_FD_READWRITE, - ); - let entry = match self.get_entry(fd) { - Ok(entry) => entry, - Err(error) => { - events.push(types::Event { - userdata: subscription.userdata, - error: error.try_into().expect("non-trapping error"), - type_: types::Eventtype::FdRead, - fd_readwrite: types::EventFdReadwrite { - nbytes: 0, - flags: types::Eventrwflags::empty(), - }, - }); - continue; - } - }; - fd_events.push(sched::FdEventData { - handle: entry.as_handle(required_rights)?, - r#type: types::Eventtype::FdRead, - userdata: subscription.userdata, - }); - } - types::SubscriptionU::FdWrite(fd_write) => { - let fd = fd_write.file_descriptor; - let required_rights = HandleRights::from_base( - types::Rights::FD_WRITE | types::Rights::POLL_FD_READWRITE, - ); - let entry = match self.get_entry(fd) { - Ok(entry) => entry, - Err(error) => { - events.push(types::Event { - userdata: subscription.userdata, - error: error.try_into().expect("non-trapping error"), - type_: types::Eventtype::FdWrite, - fd_readwrite: types::EventFdReadwrite { - nbytes: 0, - flags: types::Eventrwflags::empty(), - }, - }); - continue; - } - }; - fd_events.push(sched::FdEventData { - handle: entry.as_handle(required_rights)?, - r#type: types::Eventtype::FdWrite, - userdata: subscription.userdata, - }); - } - } - } - debug!( - events = tracing::field::debug(&events), - timeout = tracing::field::debug(timeout), - "poll_oneoff" - ); - // The underlying implementation should successfully and immediately return - // 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)?; - Ok(events) - } -} diff --git a/crates/wasi-common/src/snapshots/wasi_unstable.rs b/crates/wasi-common/src/snapshots/wasi_unstable.rs deleted file mode 100644 index dbdef63e42..0000000000 --- a/crates/wasi-common/src/snapshots/wasi_unstable.rs +++ /dev/null @@ -1,939 +0,0 @@ -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); - e.try_into() - } -} - -impl TryFrom for Errno { - type Error = wiggle::Trap; - fn try_from(e: Error) -> Result { - Ok(types_new::Errno::try_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) -> wiggle::Trap { - 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/src/string_array.rs b/crates/wasi-common/src/string_array.rs deleted file mode 100644 index cfb3d75af2..0000000000 --- a/crates/wasi-common/src/string_array.rs +++ /dev/null @@ -1,145 +0,0 @@ -use crate::Error; -use std::collections::HashMap; -use std::convert::TryInto; -use std::ffi::{CString, OsString}; -use wiggle::GuestPtr; - -#[derive(Debug, Eq, Hash, PartialEq)] -pub enum PendingString { - Bytes(Vec), - OsString(OsString), -} - -impl From> for PendingString { - fn from(bytes: Vec) -> Self { - Self::Bytes(bytes) - } -} - -impl From for PendingString { - fn from(s: OsString) -> Self { - Self::OsString(s) - } -} - -impl PendingString { - pub fn into_string(self) -> Result { - 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) - } -} - -#[derive(Debug, thiserror::Error)] -pub enum StringArrayError { - /// Provided sequence of bytes contained an unexpected NUL byte. - #[error("provided sequence contained an unexpected NUL byte")] - Nul(#[from] std::ffi::NulError), - /// Too many elements: must fit into u32 - #[error("too many elements")] - NumElements, - /// Element size: must fit into u32 - #[error("element too big")] - ElemSize, - /// Cumulative element size: must fit into u32 - #[error("cumulative element size too big")] - CumElemSize, - /// Provided sequence of bytes was not a valid UTF-8. - #[error("provided sequence is not valid UTF-8: {0}")] - InvalidUtf8(#[from] std::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] std::string::FromUtf16Error), -} - -pub struct StringArray { - elems: Vec, - pub number_elements: u32, - pub cumulative_size: u32, -} -impl StringArray { - pub fn from_pending_vec(elems: Vec) -> Result { - let elems = elems - .into_iter() - .map(|arg| arg.into_string()) - .collect::, StringArrayError>>()?; - Self::from_strings(elems) - } - pub fn from_pending_map( - elems: HashMap, - ) -> Result { - let mut pairs = Vec::new(); - for (k, v) in elems.into_iter() { - let mut pair = k.into_string()?; - pair.push('='); - pair.push_str(&v.into_string()?); - pairs.push(pair); - } - Self::from_strings(pairs) - } - pub fn from_strings(elems: Vec) -> Result { - let elems = elems - .into_iter() - .map(|s| CString::new(s)) - .collect::, _>>()?; - let number_elements = elems - .len() - .try_into() - .map_err(|_| StringArrayError::NumElements)?; - let mut cumulative_size: u32 = 0; - for elem in elems.iter() { - let elem_bytes = elem - .as_bytes_with_nul() - .len() - .try_into() - .map_err(|_| StringArrayError::ElemSize)?; - cumulative_size = cumulative_size - .checked_add(elem_bytes) - .ok_or(StringArrayError::CumElemSize)?; - } - Ok(Self { - elems, - number_elements, - cumulative_size, - }) - } - - pub fn write_to_guest<'a>( - &self, - buffer: &GuestPtr<'a, u8>, - element_heads: &GuestPtr<'a, GuestPtr<'a, u8>>, - ) -> Result<(), Error> { - let element_heads = element_heads.as_array(self.number_elements); - let buffer = buffer.as_array(self.cumulative_size); - let mut cursor = 0; - for (elem, head) in self.elems.iter().zip(element_heads.iter()) { - let bytes = elem.as_bytes_with_nul(); - let len: u32 = bytes.len().try_into()?; - let elem_buffer = buffer - .get_range(cursor..(cursor + len)) - .ok_or(Error::Inval)?; // Elements don't fit in buffer provided - elem_buffer.copy_from_slice(bytes)?; - head?.write( - elem_buffer - .get(0) - .expect("all elem buffers at least length 1"), - )?; - cursor += len; - } - Ok(()) - } -} diff --git a/crates/wasi-common/src/sys/clock.rs b/crates/wasi-common/src/sys/clock.rs deleted file mode 100644 index ddfe9f5683..0000000000 --- a/crates/wasi-common/src/sys/clock.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::sched::{Subclockflags, SubscriptionClock}; -use crate::{Error, Result}; -use std::time::SystemTime; - -pub(crate) use super::sys_impl::clock::*; - -pub(crate) fn to_relative_ns_delay(clock: &SubscriptionClock) -> Result { - if clock.flags != Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME { - return Ok(u128::from(clock.timeout)); - } - let now: u128 = SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .map_err(|_| Error::Notcapable)? - .as_nanos(); - let deadline = u128::from(clock.timeout); - Ok(deadline.saturating_sub(now)) -} diff --git a/crates/wasi-common/src/sys/fd.rs b/crates/wasi-common/src/sys/fd.rs deleted file mode 100644 index bcee2956bc..0000000000 --- a/crates/wasi-common/src/sys/fd.rs +++ /dev/null @@ -1,45 +0,0 @@ -use crate::handle::{Fstflags, Timestamp}; -use crate::{Error, Result}; -use filetime::{set_file_handle_times, FileTime}; -use std::fs::File; -use std::time::{Duration, SystemTime, UNIX_EPOCH}; - -pub(crate) use super::sys_impl::fd::*; - -pub(crate) fn filestat_set_times( - file: &File, - st_atim: Timestamp, - st_mtim: Timestamp, - fst_flags: Fstflags, -) -> Result<()> { - let set_atim = fst_flags.contains(Fstflags::ATIM); - let set_atim_now = fst_flags.contains(Fstflags::ATIM_NOW); - let set_mtim = fst_flags.contains(Fstflags::MTIM); - let set_mtim_now = fst_flags.contains(Fstflags::MTIM_NOW); - - if (set_atim && set_atim_now) || (set_mtim && set_mtim_now) { - return Err(Error::Inval); - } - 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(file, atim, mtim)?; - Ok(()) -} diff --git a/crates/wasi-common/src/sys/mod.rs b/crates/wasi-common/src/sys/mod.rs deleted file mode 100644 index 2036f2f08b..0000000000 --- a/crates/wasi-common/src/sys/mod.rs +++ /dev/null @@ -1,95 +0,0 @@ -pub(crate) mod clock; -pub(crate) mod fd; -pub(crate) mod osdir; -pub(crate) mod osfile; -pub(crate) mod osother; -pub(crate) mod stdio; - -use cfg_if::cfg_if; - -cfg_if! { - if #[cfg(unix)] { - mod unix; - use unix as sys_impl; - pub use unix::preopen_dir; - } else if #[cfg(windows)] { - mod windows; - use windows as sys_impl; - pub use windows::preopen_dir; - } else { - compile_error!("wasi-common doesn't compile for this platform yet"); - } -} - -pub(crate) use sys_impl::path; -pub(crate) use sys_impl::poll; - -use super::handle::{Filetype, Handle}; -use osdir::OsDir; -use osfile::OsFile; -use osother::OsOther; -use std::convert::TryFrom; -use std::fs::File; -use std::io; -use std::mem::ManuallyDrop; -use stdio::{Stderr, Stdin, Stdout}; -use sys_impl::get_file_type; - -pub(crate) trait AsFile { - fn as_file(&self) -> io::Result>; -} - -impl AsFile for dyn Handle + 'static { - fn as_file(&self) -> io::Result> { - if let Some(file) = self.as_any().downcast_ref::() { - file.as_file() - } else if let Some(dir) = self.as_any().downcast_ref::() { - dir.as_file() - } else if let Some(stdin) = self.as_any().downcast_ref::() { - stdin.as_file() - } else if let Some(stdout) = self.as_any().downcast_ref::() { - stdout.as_file() - } else if let Some(stderr) = self.as_any().downcast_ref::() { - stderr.as_file() - } else if let Some(other) = self.as_any().downcast_ref::() { - other.as_file() - } else { - tracing::error!("tried to make std::fs::File from non-OS handle"); - Err(io::Error::from_raw_os_error(libc::EBADF)) - } - } -} - -impl TryFrom for Box { - type Error = io::Error; - - fn try_from(file: File) -> io::Result { - let file_type = get_file_type(&file)?; - match file_type { - Filetype::RegularFile => { - let handle = OsFile::try_from(file)?; - tracing::debug!( - handle = tracing::field::debug(&handle), - "Created new instance of OsFile" - ); - Ok(Box::new(handle)) - } - Filetype::Directory => { - let handle = OsDir::try_from(file)?; - tracing::debug!( - handle = tracing::field::debug(&handle), - "Created new instance of OsDir" - ); - Ok(Box::new(handle)) - } - _ => { - let handle = OsOther::try_from(file)?; - tracing::debug!( - handle = tracing::field::debug(&handle), - "Created new instance of OsOther" - ); - Ok(Box::new(handle)) - } - } - } -} diff --git a/crates/wasi-common/src/sys/osdir.rs b/crates/wasi-common/src/sys/osdir.rs deleted file mode 100644 index b936aa2997..0000000000 --- a/crates/wasi-common/src/sys/osdir.rs +++ /dev/null @@ -1,145 +0,0 @@ -use super::sys_impl::oshandle::RawOsHandle; -use super::{fd, path, AsFile}; -use crate::handle::{ - Dircookie, Dirent, Fdflags, Filestat, Filetype, Fstflags, Handle, HandleRights, Oflags, -}; -use crate::sched::Timestamp; -use crate::{Error, Result}; -use std::any::Any; -use std::io; -use std::ops::Deref; -use tracing::{debug, error}; - -// TODO could this be cleaned up? -// The actual `OsDir` struct is OS-dependent, therefore we delegate -// its definition to OS-specific modules. -pub use super::sys_impl::osdir::OsDir; - -impl Deref for OsDir { - type Target = RawOsHandle; - - fn deref(&self) -> &Self::Target { - &self.handle - } -} - -impl Handle for OsDir { - fn as_any(&self) -> &dyn Any { - self - } - fn try_clone(&self) -> io::Result> { - let handle = self.handle.try_clone()?; - let new = Self::new(self.rights.get(), handle)?; - Ok(Box::new(new)) - } - fn get_file_type(&self) -> Filetype { - Filetype::Directory - } - fn get_rights(&self) -> HandleRights { - self.rights.get() - } - fn set_rights(&self, rights: HandleRights) { - self.rights.set(rights) - } - // FdOps - fn fdstat_get(&self) -> Result { - fd::fdstat_get(&*self.as_file()?) - } - fn fdstat_set_flags(&self, fdflags: Fdflags) -> Result<()> { - if let Some(new_file) = fd::fdstat_set_flags(&*self.as_file()?, fdflags)? { - self.handle.update_from(new_file); - } - Ok(()) - } - fn filestat_get(&self) -> Result { - fd::filestat_get(&*self.as_file()?) - } - fn filestat_set_times( - &self, - atim: Timestamp, - mtim: Timestamp, - fst_flags: Fstflags, - ) -> Result<()> { - fd::filestat_set_times(&*self.as_file()?, atim, mtim, fst_flags) - } - fn readdir<'a>( - &'a self, - cookie: Dircookie, - ) -> Result> + 'a>> { - fd::readdir(self, cookie) - } - // PathOps - fn create_directory(&self, path: &str) -> Result<()> { - path::create_directory(self, path) - } - fn filestat_get_at(&self, path: &str, follow: bool) -> Result { - path::filestat_get_at(self, path, follow) - } - fn filestat_set_times_at( - &self, - path: &str, - atim: Timestamp, - mtim: Timestamp, - fst_flags: Fstflags, - follow: bool, - ) -> Result<()> { - path::filestat_set_times_at(self, path, atim, mtim, fst_flags, follow) - } - fn openat( - &self, - path: &str, - read: bool, - write: bool, - oflags: Oflags, - fd_flags: Fdflags, - ) -> Result> { - path::open(self, path, read, write, oflags, fd_flags) - } - fn link( - &self, - old_path: &str, - new_handle: Box, - new_path: &str, - follow: bool, - ) -> Result<()> { - let new_handle = match new_handle.as_any().downcast_ref::() { - None => { - error!("Tried to link with handle that's not an OsDir"); - return Err(Error::Badf); - } - Some(handle) => handle, - }; - path::link(self, old_path, new_handle, new_path, follow) - } - fn symlink(&self, old_path: &str, new_path: &str) -> Result<()> { - path::symlink(old_path, self, new_path) - } - fn readlink(&self, path: &str, buf: &mut [u8]) -> Result { - path::readlink(self, path, buf) - } - fn readlinkat(&self, path: &str) -> Result { - path::readlinkat(self, path) - } - fn rename(&self, old_path: &str, new_handle: Box, new_path: &str) -> Result<()> { - let new_handle = match new_handle.as_any().downcast_ref::() { - None => { - error!("Tried to rename with handle that's not an OsDir"); - return Err(Error::Badf); - } - Some(handle) => handle, - }; - debug!("rename (old_dirfd, old_path)=({:?}, {:?})", self, old_path); - debug!( - "rename (new_dirfd, new_path)=({:?}, {:?})", - new_handle, new_path - ); - path::rename(self, old_path, new_handle, new_path) - } - fn remove_directory(&self, path: &str) -> Result<()> { - debug!("remove_directory (dirfd, path)=({:?}, {:?})", self, path); - path::remove_directory(self, path) - } - fn unlink_file(&self, path: &str) -> Result<()> { - path::unlink_file(self, path) - } -} diff --git a/crates/wasi-common/src/sys/osfile.rs b/crates/wasi-common/src/sys/osfile.rs deleted file mode 100644 index 927cee63cb..0000000000 --- a/crates/wasi-common/src/sys/osfile.rs +++ /dev/null @@ -1,148 +0,0 @@ -use super::sys_impl::oshandle::RawOsHandle; -use super::{fd, AsFile}; -use crate::handle::{ - Advice, Fdflags, Filesize, Filestat, Filetype, Fstflags, Handle, HandleRights, -}; -use crate::sched::Timestamp; -use crate::{Error, Result}; -use std::any::Any; -use std::cell::Cell; -use std::fs::File; -use std::io::{self, Read, Seek, SeekFrom, Write}; -use std::ops::Deref; - -#[derive(Debug)] -/// A file backed by the operating system's file system. Dereferences to a -/// `RawOsHandle`. Its impl of `Handle` uses Rust's `std` to implement all -/// file descriptor operations. -/// -/// # Constructing `OsFile` -/// -/// `OsFile` can currently only be constructed from `std::fs::File` using -/// the `std::convert::TryFrom` trait: -/// -/// ```rust,no_run -/// use std::fs::OpenOptions; -/// use std::convert::TryFrom; -/// use wasi_common::OsFile; -/// -/// let file = OpenOptions::new().read(true).open("some_file").unwrap(); -/// let os_file = OsFile::try_from(file).unwrap(); -/// ``` -pub struct OsFile { - rights: Cell, - handle: RawOsHandle, -} - -impl OsFile { - pub(super) fn new(rights: HandleRights, handle: RawOsHandle) -> Self { - let rights = Cell::new(rights); - Self { rights, handle } - } -} - -impl Deref for OsFile { - type Target = RawOsHandle; - - fn deref(&self) -> &Self::Target { - &self.handle - } -} - -impl Handle for OsFile { - fn as_any(&self) -> &dyn Any { - self - } - fn try_clone(&self) -> io::Result> { - let handle = self.handle.try_clone()?; - let rights = self.rights.clone(); - Ok(Box::new(Self { rights, handle })) - } - fn get_file_type(&self) -> Filetype { - Filetype::RegularFile - } - fn get_rights(&self) -> HandleRights { - self.rights.get() - } - fn set_rights(&self, rights: HandleRights) { - self.rights.set(rights) - } - // FdOps - fn advise(&self, advice: Advice, offset: Filesize, len: Filesize) -> Result<()> { - fd::advise(self, advice, offset, len) - } - fn allocate(&self, offset: Filesize, len: Filesize) -> Result<()> { - let fd = self.as_file()?; - let metadata = fd.metadata()?; - let current_size = metadata.len(); - let wanted_size = offset.checked_add(len).ok_or(Error::TooBig)?; - // This check will be unnecessary when rust-lang/rust#63326 is fixed - if wanted_size > i64::max_value() as u64 { - return Err(Error::TooBig); - } - if wanted_size > current_size { - fd.set_len(wanted_size)?; - } - Ok(()) - } - fn datasync(&self) -> Result<()> { - self.as_file()?.sync_data()?; - Ok(()) - } - fn fdstat_get(&self) -> Result { - fd::fdstat_get(&*self.as_file()?) - } - fn fdstat_set_flags(&self, fdflags: Fdflags) -> Result<()> { - if let Some(new_handle) = fd::fdstat_set_flags(&*self.as_file()?, fdflags)? { - self.handle.update_from(new_handle); - } - Ok(()) - } - fn filestat_get(&self) -> Result { - fd::filestat_get(&*self.as_file()?) - } - fn filestat_set_size(&self, size: Filesize) -> Result<()> { - self.as_file()?.set_len(size)?; - Ok(()) - } - fn filestat_set_times( - &self, - atim: Timestamp, - mtim: Timestamp, - fst_flags: Fstflags, - ) -> Result<()> { - fd::filestat_set_times(&*self.as_file()?, atim, mtim, fst_flags) - } - fn preadv(&self, buf: &mut [io::IoSliceMut], offset: u64) -> Result { - let mut fd: &File = &*self.as_file()?; - let cur_pos = fd.seek(SeekFrom::Current(0))?; - fd.seek(SeekFrom::Start(offset))?; - let nread = self.read_vectored(buf)?; - fd.seek(SeekFrom::Start(cur_pos))?; - Ok(nread) - } - fn pwritev(&self, buf: &[io::IoSlice], offset: u64) -> Result { - let mut fd: &File = &*self.as_file()?; - let cur_pos = fd.seek(SeekFrom::Current(0))?; - fd.seek(SeekFrom::Start(offset))?; - let nwritten = self.write_vectored(&buf)?; - fd.seek(SeekFrom::Start(cur_pos))?; - Ok(nwritten) - } - fn read_vectored(&self, iovs: &mut [io::IoSliceMut]) -> Result { - let nread = self.as_file()?.read_vectored(iovs)?; - Ok(nread) - } - fn seek(&self, offset: SeekFrom) -> Result { - let pos = self.as_file()?.seek(offset)?; - Ok(pos) - } - fn sync(&self) -> Result<()> { - self.as_file()?.sync_all()?; - Ok(()) - } - fn write_vectored(&self, iovs: &[io::IoSlice]) -> Result { - let nwritten = self.as_file()?.write_vectored(&iovs)?; - Ok(nwritten) - } -} diff --git a/crates/wasi-common/src/sys/osother.rs b/crates/wasi-common/src/sys/osother.rs deleted file mode 100644 index 43df52fb3f..0000000000 --- a/crates/wasi-common/src/sys/osother.rs +++ /dev/null @@ -1,103 +0,0 @@ -use super::sys_impl::oshandle::RawOsHandle; -use super::{fd, AsFile}; -use crate::handle::{Fdflags, Filetype, Handle, HandleRights}; -use crate::sandboxed_tty_writer::SandboxedTTYWriter; -use crate::Result; -use std::any::Any; -use std::cell::Cell; -use std::fs::File; -use std::io::{self, Read, Write}; -use std::ops::Deref; - -/// `OsOther` is something of a catch-all for everything not covered with the specific handle -/// types (`OsFile`, `OsDir`, `Stdio`). It currently encapsulates handles such as OS pipes, -/// sockets, streams, etc. As such, when redirecting stdio within `WasiCtxBuilder`, the redirected -/// pipe should be encapsulated within this instance _and not_ `OsFile` which represents a regular -/// OS file. -/// -/// # Constructing `OsOther` -/// -/// `OsOther` can currently only be constructed from `std::fs::File` using -/// the `std::convert::TryFrom` trait: -/// -/// ```rust,no_run -/// use std::fs::OpenOptions; -/// use std::convert::TryFrom; -/// use wasi_common::OsOther; -/// -/// let pipe = OpenOptions::new().read(true).open("a_pipe").unwrap(); -/// let os_other = OsOther::try_from(pipe).unwrap(); -/// ``` -#[derive(Debug)] -pub struct OsOther { - file_type: Filetype, - rights: Cell, - handle: RawOsHandle, -} - -impl OsOther { - pub(super) fn new(file_type: Filetype, rights: HandleRights, handle: RawOsHandle) -> Self { - let rights = Cell::new(rights); - Self { - file_type, - rights, - handle, - } - } -} - -impl Deref for OsOther { - type Target = RawOsHandle; - - fn deref(&self) -> &Self::Target { - &self.handle - } -} - -impl Handle for OsOther { - fn as_any(&self) -> &dyn Any { - self - } - fn try_clone(&self) -> io::Result> { - let file_type = self.file_type; - let handle = self.handle.try_clone()?; - let rights = self.rights.clone(); - Ok(Box::new(Self { - file_type, - rights, - handle, - })) - } - fn get_file_type(&self) -> Filetype { - self.file_type - } - fn get_rights(&self) -> HandleRights { - self.rights.get() - } - fn set_rights(&self, new_rights: HandleRights) { - self.rights.set(new_rights) - } - // FdOps - fn fdstat_get(&self) -> Result { - fd::fdstat_get(&*self.as_file()?) - } - fn fdstat_set_flags(&self, fdflags: Fdflags) -> Result<()> { - if let Some(handle) = fd::fdstat_set_flags(&*self.as_file()?, fdflags)? { - self.handle.update_from(handle); - } - Ok(()) - } - fn read_vectored(&self, iovs: &mut [io::IoSliceMut]) -> Result { - let nread = self.as_file()?.read_vectored(iovs)?; - Ok(nread) - } - fn write_vectored(&self, iovs: &[io::IoSlice]) -> Result { - let mut fd: &File = &*self.as_file()?; - let nwritten = if self.is_tty() { - SandboxedTTYWriter::new(&mut fd).write_vectored(&iovs)? - } else { - fd.write_vectored(iovs)? - }; - Ok(nwritten) - } -} diff --git a/crates/wasi-common/src/sys/stdio.rs b/crates/wasi-common/src/sys/stdio.rs deleted file mode 100644 index 9ac6d6c9ca..0000000000 --- a/crates/wasi-common/src/sys/stdio.rs +++ /dev/null @@ -1,241 +0,0 @@ -// The reason we have a separate Stdio wrappers is to correctly facilitate redirects on Windows. -// To elaborate further, in POSIX, we can get a stdio handle by opening a specific fd {0,1,2}. -// On Windows however, we need to issue a syscall that's separate from standard Windows "open" -// to get a console handle, and this is GetStdHandle. This is exactly what Rust does and what -// is wrapped inside their Stdio object in the libstd. We wrap it here as well because of this -// nuance on Windows: -// -// The standard handles of a process may be redirected by a call to SetStdHandle, in which -// case GetStdHandle returns the redirected handle. -// -// The MSDN also says this however: -// -// If the standard handles have been redirected, you can specify the CONIN$ value in a call -// to the CreateFile function to get a handle to a console's input buffer. Similarly, you -// can specify the CONOUT$ value to get a handle to a console's active screen buffer. -// -// TODO it might worth re-investigating the suitability of this type on Windows. - -use super::{fd, AsFile}; -use crate::handle::{Fdflags, Filestat, Filetype, Handle, HandleRights, Rights, RightsExt, Size}; -use crate::sandboxed_tty_writer::SandboxedTTYWriter; -use crate::{Error, Result}; -use std::any::Any; -use std::cell::Cell; -use std::convert::TryInto; -use std::io::{self, Read, Write}; - -pub(crate) trait StdinExt: Sized { - /// Create `Stdin` from `io::stdin`. - fn stdin() -> io::Result>; -} - -#[derive(Debug, Clone)] -pub(crate) struct Stdin { - pub(crate) file_type: Filetype, - pub(crate) rights: Cell, -} - -impl Handle for Stdin { - fn as_any(&self) -> &dyn Any { - self - } - fn try_clone(&self) -> io::Result> { - Ok(Box::new(self.clone())) - } - fn get_file_type(&self) -> Filetype { - self.file_type - } - fn get_rights(&self) -> HandleRights { - self.rights.get() - } - fn set_rights(&self, new_rights: HandleRights) { - self.rights.set(new_rights) - } - // FdOps - fn fdstat_get(&self) -> Result { - fd::fdstat_get(&*self.as_file()?) - } - fn fdstat_set_flags(&self, fdflags: Fdflags) -> Result<()> { - if let Some(_) = fd::fdstat_set_flags(&*self.as_file()?, fdflags)? { - // OK, this means we should somehow update the underlying os handle, - // and we can't do that with `std::io::std{in, out, err}`, so we'll - // panic for now. - panic!("Tried updating Fdflags on Stdio handle by re-opening as file!"); - } - Ok(()) - } - fn filestat_get(&self) -> Result { - fd::filestat_get(&*self.as_file()?) - } - fn read_vectored(&self, iovs: &mut [io::IoSliceMut]) -> Result { - let nread = io::stdin().read_vectored(iovs)?; - Ok(nread) - } -} - -pub(crate) trait StdoutExt: Sized { - /// Create `Stdout` from `io::stdout`. - fn stdout() -> io::Result>; -} - -#[derive(Debug, Clone)] -pub(crate) struct Stdout { - pub(crate) file_type: Filetype, - pub(crate) rights: Cell, -} - -impl Handle for Stdout { - fn as_any(&self) -> &dyn Any { - self - } - fn try_clone(&self) -> io::Result> { - Ok(Box::new(self.clone())) - } - fn get_file_type(&self) -> Filetype { - self.file_type - } - fn get_rights(&self) -> HandleRights { - self.rights.get() - } - fn set_rights(&self, new_rights: HandleRights) { - self.rights.set(new_rights) - } - // FdOps - fn fdstat_get(&self) -> Result { - fd::fdstat_get(&*self.as_file()?) - } - fn fdstat_set_flags(&self, fdflags: Fdflags) -> Result<()> { - if let Some(_) = fd::fdstat_set_flags(&*self.as_file()?, fdflags)? { - // OK, this means we should somehow update the underlying os handle, - // and we can't do that with `std::io::std{in, out, err}`, so we'll - // panic for now. - panic!("Tried updating Fdflags on Stdio handle by re-opening as file!"); - } - Ok(()) - } - fn filestat_get(&self) -> Result { - fd::filestat_get(&*self.as_file()?) - } - fn write_vectored(&self, iovs: &[io::IoSlice]) -> Result { - // lock for the duration of the scope - let stdout = io::stdout(); - let mut stdout = stdout.lock(); - let nwritten = if self.is_tty() { - SandboxedTTYWriter::new(&mut stdout).write_vectored(&iovs)? - } else { - stdout.write_vectored(iovs)? - }; - stdout.flush()?; - Ok(nwritten) - } -} - -pub(crate) trait StderrExt: Sized { - /// Create `Stderr` from `io::stderr`. - fn stderr() -> io::Result>; -} - -#[derive(Debug, Clone)] -pub(crate) struct Stderr { - pub(crate) file_type: Filetype, - pub(crate) rights: Cell, -} - -impl Handle for Stderr { - fn as_any(&self) -> &dyn Any { - self - } - fn try_clone(&self) -> io::Result> { - Ok(Box::new(self.clone())) - } - fn get_file_type(&self) -> Filetype { - self.file_type - } - fn get_rights(&self) -> HandleRights { - self.rights.get() - } - fn set_rights(&self, new_rights: HandleRights) { - self.rights.set(new_rights) - } - // FdOps - fn fdstat_get(&self) -> Result { - fd::fdstat_get(&*self.as_file()?) - } - fn fdstat_set_flags(&self, fdflags: Fdflags) -> Result<()> { - if let Some(_) = fd::fdstat_set_flags(&*self.as_file()?, fdflags)? { - // OK, this means we should somehow update the underlying os handle, - // and we can't do that with `std::io::std{in, out, err}`, so we'll - // panic for now. - panic!("Tried updating Fdflags on Stdio handle by re-opening as file!"); - } - Ok(()) - } - fn filestat_get(&self) -> Result { - fd::filestat_get(&*self.as_file()?) - } - fn write_vectored(&self, iovs: &[io::IoSlice]) -> Result { - // 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. - let nwritten = SandboxedTTYWriter::new(&mut io::stderr()).write_vectored(&iovs)?; - Ok(nwritten) - } -} - -#[derive(Debug, Clone)] -pub(crate) struct NullDevice { - pub(crate) rights: Cell, - pub(crate) fd_flags: Cell, -} - -impl NullDevice { - pub(crate) fn new() -> Self { - let rights = HandleRights::new( - Rights::character_device_base(), - Rights::character_device_inheriting(), - ); - let rights = Cell::new(rights); - let fd_flags = Fdflags::empty(); - let fd_flags = Cell::new(fd_flags); - Self { rights, fd_flags } - } -} - -impl Handle for NullDevice { - fn as_any(&self) -> &dyn Any { - self - } - fn try_clone(&self) -> io::Result> { - Ok(Box::new(self.clone())) - } - fn get_file_type(&self) -> Filetype { - Filetype::CharacterDevice - } - fn get_rights(&self) -> HandleRights { - self.rights.get() - } - fn set_rights(&self, rights: HandleRights) { - self.rights.set(rights) - } - // FdOps - fn fdstat_get(&self) -> Result { - Ok(self.fd_flags.get()) - } - fn fdstat_set_flags(&self, fdflags: Fdflags) -> Result<()> { - self.fd_flags.set(fdflags); - Ok(()) - } - fn read_vectored(&self, _iovs: &mut [io::IoSliceMut]) -> Result { - Ok(0) - } - fn write_vectored(&self, iovs: &[io::IoSlice]) -> Result { - let mut total_len = 0u32; - for iov in iovs { - let len: Size = iov.len().try_into()?; - total_len = total_len.checked_add(len).ok_or(Error::Overflow)?; - } - Ok(total_len as usize) - } -} diff --git a/crates/wasi-common/src/sys/unix/bsd/mod.rs b/crates/wasi-common/src/sys/unix/bsd/mod.rs deleted file mode 100644 index e4941cce14..0000000000 --- a/crates/wasi-common/src/sys/unix/bsd/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub(crate) mod osdir; -pub(crate) mod path; - -pub(crate) const O_RSYNC: yanix::file::OFlags = yanix::file::OFlags::SYNC; diff --git a/crates/wasi-common/src/sys/unix/bsd/osdir.rs b/crates/wasi-common/src/sys/unix/bsd/osdir.rs deleted file mode 100644 index b375254baa..0000000000 --- a/crates/wasi-common/src/sys/unix/bsd/osdir.rs +++ /dev/null @@ -1,65 +0,0 @@ -use crate::handle::HandleRights; -use crate::sys::sys_impl::oshandle::RawOsHandle; -use crate::Result; -use std::cell::{Cell, RefCell, RefMut}; -use std::io; -use yanix::dir::Dir; - -#[derive(Debug)] -/// A directory in the operating system's file system. Its impl of `Handle` is -/// in `sys::osdir`. This type is exposed to all other modules as -/// `sys::osdir::OsDir` when configured. -/// -/// # Constructing `OsDir` -/// -/// `OsDir` can currently only be constructed from `std::fs::File` using -/// the `std::convert::TryFrom` trait: -/// -/// ```rust,no_run -/// use std::fs::OpenOptions; -/// use std::convert::TryFrom; -/// use wasi_common::OsDir; -/// -/// let dir = OpenOptions::new().read(true).open("some_dir").unwrap(); -/// let os_dir = OsDir::try_from(dir).unwrap(); -/// ``` -pub struct OsDir { - pub(crate) rights: Cell, - pub(crate) handle: RawOsHandle, - // 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. - stream_ptr: RefCell, -} - -impl OsDir { - pub(crate) fn new(rights: HandleRights, handle: RawOsHandle) -> io::Result { - let rights = Cell::new(rights); - // We need to duplicate the handle, 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 stream_ptr = Dir::from(handle.try_clone()?)?; - let stream_ptr = RefCell::new(stream_ptr); - Ok(Self { - rights, - handle, - stream_ptr, - }) - } - /// Returns the `Dir` stream pointer associated with this `OsDir`. Duck - /// typing: sys::unix::fd::readdir expects the configured OsDir to have - /// this method. - pub(crate) fn stream_ptr(&self) -> Result> { - Ok(self.stream_ptr.borrow_mut()) - } -} diff --git a/crates/wasi-common/src/sys/unix/bsd/path.rs b/crates/wasi-common/src/sys/unix/bsd/path.rs deleted file mode 100644 index 7d4b619c52..0000000000 --- a/crates/wasi-common/src/sys/unix/bsd/path.rs +++ /dev/null @@ -1,118 +0,0 @@ -use crate::sys::osdir::OsDir; -use crate::{Error, Result}; -use std::os::unix::prelude::AsRawFd; - -pub(crate) fn unlink_file(dirfd: &OsDir, path: &str) -> Result<()> { - use yanix::file::{unlinkat, AtFlags}; - match unsafe { unlinkat(dirfd.as_raw_fd(), 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(dirfd.as_raw_fd(), path, AtFlags::SYMLINK_NOFOLLOW) } { - Ok(stat) => { - if FileType::from_stat_st_mode(stat.st_mode) == FileType::Directory { - return Err(Error::Isdir); - } - } - Err(err) => { - tracing::debug!("path_unlink_file fstatat error: {:?}", err); - } - } - } - - Err(err.into()) - } - Ok(()) => Ok(()), - } -} - -pub(crate) fn symlink(old_path: &str, new_dirfd: &OsDir, new_path: &str) -> Result<()> { - use yanix::file::{fstatat, symlinkat, AtFlags}; - - tracing::debug!("path_symlink old_path = {:?}", old_path); - tracing::debug!( - "path_symlink (new_dirfd, new_path) = ({:?}, {:?})", - new_dirfd, - new_path - ); - - match unsafe { symlinkat(old_path, new_dirfd.as_raw_fd(), new_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 = new_path.trim_end_matches('/'); - match unsafe { fstatat(new_dirfd.as_raw_fd(), new_path, AtFlags::SYMLINK_NOFOLLOW) } - { - Ok(_) => return Err(Error::Exist), - Err(err) => { - tracing::debug!("path_symlink fstatat error: {:?}", err); - } - } - } - Err(err.into()) - } - Ok(()) => Ok(()), - } -} - -pub(crate) fn rename( - old_dirfd: &OsDir, - old_path: &str, - new_dirfd: &OsDir, - new_path: &str, -) -> Result<()> { - use yanix::file::{fstatat, renameat, AtFlags}; - match unsafe { - renameat( - old_dirfd.as_raw_fd(), - old_path, - new_dirfd.as_raw_fd(), - 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(old_dirfd.as_raw_fd(), old_path, AtFlags::SYMLINK_NOFOLLOW) } - { - Ok(_) => { - // check if destination contains a trailing slash - if new_path.contains('/') { - return Err(Error::Notdir); - } else { - return Err(Error::Noent); - } - } - Err(err) => { - tracing::debug!("path_rename fstatat error: {:?}", err); - } - } - } - - Err(err.into()) - } - Ok(()) => Ok(()), - } -} diff --git a/crates/wasi-common/src/sys/unix/clock.rs b/crates/wasi-common/src/sys/unix/clock.rs deleted file mode 100644 index f0d74919b6..0000000000 --- a/crates/wasi-common/src/sys/unix/clock.rs +++ /dev/null @@ -1,36 +0,0 @@ -use crate::sched::{Clockid, Timestamp}; -use crate::{Error, Result}; -use yanix::clock::{clock_getres, clock_gettime, ClockId}; - -pub(crate) fn res_get(clock_id: Clockid) -> Result { - let clock_id: ClockId = clock_id.into(); - 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 Timestamp) - .checked_mul(1_000_000_000) - .and_then(|sec_ns| sec_ns.checked_add(timespec.tv_nsec as Timestamp)) - .map_or(Err(Error::Overflow), |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(Error::Inval) - } else { - Ok(resolution) - } - }) -} - -pub(crate) fn time_get(clock_id: Clockid) -> Result { - let clock_id: ClockId = clock_id.into(); - 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 Timestamp) - .checked_mul(1_000_000_000) - .and_then(|sec_ns| sec_ns.checked_add(timespec.tv_nsec as Timestamp)) - .map_or(Err(Error::Overflow), Ok) -} diff --git a/crates/wasi-common/src/sys/unix/emscripten/mod.rs b/crates/wasi-common/src/sys/unix/emscripten/mod.rs deleted file mode 100644 index de004ed605..0000000000 --- a/crates/wasi-common/src/sys/unix/emscripten/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -#[path = "../linux/osdir.rs"] -pub(crate) mod osdir; -#[path = "../linux/path.rs"] -pub(crate) mod path; - -pub(crate) const O_RSYNC: yanix::file::OFlags = yanix::file::OFlags::RSYNC; diff --git a/crates/wasi-common/src/sys/unix/fd.rs b/crates/wasi-common/src/sys/unix/fd.rs deleted file mode 100644 index c3c3e817d1..0000000000 --- a/crates/wasi-common/src/sys/unix/fd.rs +++ /dev/null @@ -1,77 +0,0 @@ -use super::oshandle::RawOsHandle; -use crate::handle::{Advice, Dircookie, Dirent, Fdflags, Filesize, Filestat, DIRCOOKIE_START}; -use crate::sys::osdir::OsDir; -use crate::sys::osfile::OsFile; -use crate::Result; -use std::convert::TryInto; -use std::fs::File; -use std::os::unix::prelude::AsRawFd; - -pub(crate) fn fdstat_get(fd: &File) -> Result { - let fdflags = unsafe { yanix::fcntl::get_status_flags(fd.as_raw_fd())? }; - Ok(fdflags.into()) -} - -pub(crate) fn fdstat_set_flags(fd: &File, fdflags: Fdflags) -> Result> { - unsafe { yanix::fcntl::set_status_flags(fd.as_raw_fd(), fdflags.into())? }; - // We return None here to signal that the operation succeeded on the original - // file descriptor and mutating the original WASI Descriptor is thus unnecessary. - // This is needed as on Windows this operation required reopening a file. - Ok(None) -} - -pub(crate) fn advise(file: &OsFile, advice: Advice, offset: Filesize, len: Filesize) -> Result<()> { - use yanix::fadvise::{posix_fadvise, PosixFadviseAdvice}; - let offset = offset.try_into()?; - let len = len.try_into()?; - let host_advice = match advice { - Advice::Dontneed => PosixFadviseAdvice::DontNeed, - Advice::Sequential => PosixFadviseAdvice::Sequential, - Advice::Willneed => PosixFadviseAdvice::WillNeed, - Advice::Noreuse => PosixFadviseAdvice::NoReuse, - Advice::Random => PosixFadviseAdvice::Random, - Advice::Normal => PosixFadviseAdvice::Normal, - }; - unsafe { posix_fadvise(file.as_raw_fd(), offset, len, host_advice)? }; - Ok(()) -} - -pub(crate) fn filestat_get(file: &File) -> Result { - use yanix::file::fstat; - let stat = unsafe { fstat(file.as_raw_fd())? }; - Ok(stat.try_into()?) -} - -pub(crate) fn readdir<'a>( - dirfd: &'a OsDir, - cookie: Dircookie, -) -> Result> + '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 = dirfd.stream_ptr()?; - - // Seek if needed. Unless cookie is wasi::__WASI_DIRCOOKIE_START, - // new items may not be returned to the caller. - if cookie == 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(Box::new(DirIter::new(dir).map(|entry| { - let entry: Entry = entry?; - let name = entry.file_name().to_str()?.to_owned(); - let dirent = Dirent { - d_next: entry.seek_loc()?.to_raw().try_into()?, - d_ino: entry.ino(), - d_namlen: name.len().try_into()?, - d_type: entry.file_type().into(), - }; - Ok((dirent, name)) - }))) -} diff --git a/crates/wasi-common/src/sys/unix/linux/mod.rs b/crates/wasi-common/src/sys/unix/linux/mod.rs deleted file mode 100644 index 15e04ef570..0000000000 --- a/crates/wasi-common/src/sys/unix/linux/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub(crate) mod osdir; -pub(crate) mod path; - -pub(crate) const O_RSYNC: yanix::file::OFlags = yanix::file::OFlags::RSYNC; diff --git a/crates/wasi-common/src/sys/unix/linux/osdir.rs b/crates/wasi-common/src/sys/unix/linux/osdir.rs deleted file mode 100644 index 57350afbaa..0000000000 --- a/crates/wasi-common/src/sys/unix/linux/osdir.rs +++ /dev/null @@ -1,53 +0,0 @@ -use crate::handle::HandleRights; -use crate::sys::sys_impl::oshandle::RawOsHandle; -use crate::Result; -use std::cell::Cell; -use std::io; -use yanix::dir::Dir; - -#[derive(Debug)] -/// A directory in the operating system's file system. Its impl of `Handle` is -/// in `sys::osdir`. This type is exposed to all other modules as -/// `sys::osdir::OsDir` when configured. -/// -/// # Constructing `OsDir` -/// -/// `OsDir` can currently only be constructed from `std::fs::File` using -/// the `std::convert::TryFrom` trait: -/// -/// ```rust,no_run -/// use std::fs::OpenOptions; -/// use std::convert::TryFrom; -/// use wasi_common::OsDir; -/// -/// let dir = OpenOptions::new().read(true).open("some_dir").unwrap(); -/// let os_dir = OsDir::try_from(dir).unwrap(); -/// ``` -pub struct OsDir { - pub(crate) rights: Cell, - pub(crate) handle: RawOsHandle, -} - -impl OsDir { - pub(crate) fn new(rights: HandleRights, handle: RawOsHandle) -> io::Result { - let rights = Cell::new(rights); - Ok(Self { rights, handle }) - } - /// Returns the `Dir` stream pointer associated with this `OsDir`. Duck typing: - /// sys::unix::fd::readdir expects the configured OsDir to have this method. - pub(crate) fn stream_ptr(&self) -> Result> { - // We need to duplicate the handle, 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 file = self.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(file)?)) - } -} diff --git a/crates/wasi-common/src/sys/unix/linux/path.rs b/crates/wasi-common/src/sys/unix/linux/path.rs deleted file mode 100644 index fb2249dc53..0000000000 --- a/crates/wasi-common/src/sys/unix/linux/path.rs +++ /dev/null @@ -1,41 +0,0 @@ -use crate::sys::osdir::OsDir; -use crate::Result; -use std::os::unix::prelude::AsRawFd; - -pub(crate) fn unlink_file(dirfd: &OsDir, path: &str) -> Result<()> { - use yanix::file::{unlinkat, AtFlags}; - unsafe { unlinkat(dirfd.as_raw_fd(), path, AtFlags::empty())? }; - Ok(()) -} - -pub(crate) fn symlink(old_path: &str, new_dirfd: &OsDir, new_path: &str) -> Result<()> { - use yanix::file::symlinkat; - - tracing::debug!( - old_path = old_path, - new_dirfd = tracing::field::debug(new_dirfd), - new_path = new_path, - "path symlink" - ); - - unsafe { symlinkat(old_path, new_dirfd.as_raw_fd(), new_path)? }; - Ok(()) -} - -pub(crate) fn rename( - old_dirfd: &OsDir, - old_path: &str, - new_dirfd: &OsDir, - new_path: &str, -) -> Result<()> { - use yanix::file::renameat; - unsafe { - renameat( - old_dirfd.as_raw_fd(), - old_path, - new_dirfd.as_raw_fd(), - new_path, - )? - }; - Ok(()) -} diff --git a/crates/wasi-common/src/sys/unix/mod.rs b/crates/wasi-common/src/sys/unix/mod.rs deleted file mode 100644 index 9c7fd5ece8..0000000000 --- a/crates/wasi-common/src/sys/unix/mod.rs +++ /dev/null @@ -1,276 +0,0 @@ -pub(crate) mod clock; -pub(crate) mod fd; -pub(crate) mod osdir; -pub(crate) mod osfile; -pub(crate) mod oshandle; -pub(crate) mod osother; -pub(crate) mod path; -pub(crate) mod poll; -pub(crate) mod stdio; - -cfg_if::cfg_if! { - if #[cfg(any(target_os = "linux", - target_os = "android"))] { - mod linux; - use linux as sys_impl; - } else if #[cfg(target_os = "emscripten")] { - mod emscripten; - use 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 bsd as sys_impl; - } -} - -use crate::handle::{ - Fdflags, Filesize, Filestat, Filetype, HandleRights, Lookupflags, Oflags, Rights, RightsExt, -}; -use crate::sched::{Clockid, Timestamp}; -use crate::sys::AsFile; -use crate::{Error, Result}; -use std::convert::{TryFrom, TryInto}; -use std::fs::File; -use std::io; -use std::mem::ManuallyDrop; -use std::os::unix::prelude::{AsRawFd, FileTypeExt, FromRawFd}; -use std::path::Path; -use yanix::clock::ClockId; -use yanix::file::{AtFlags, OFlags}; - -pub(crate) use sys_impl::*; - -impl AsFile for T { - fn as_file(&self) -> io::Result> { - let file = unsafe { File::from_raw_fd(self.as_raw_fd()) }; - Ok(ManuallyDrop::new(file)) - } -} - -pub(super) fn get_file_type(file: &File) -> io::Result { - let ft = file.metadata()?.file_type(); - let file_type = if ft.is_block_device() { - tracing::debug!( - host_fd = tracing::field::display(file.as_raw_fd()), - "Host fd is a block device" - ); - Filetype::BlockDevice - } else if ft.is_char_device() { - tracing::debug!("Host fd {:?} is a char device", file.as_raw_fd()); - Filetype::CharacterDevice - } else if ft.is_dir() { - tracing::debug!("Host fd {:?} is a directory", file.as_raw_fd()); - Filetype::Directory - } else if ft.is_file() { - tracing::debug!("Host fd {:?} is a file", file.as_raw_fd()); - Filetype::RegularFile - } else if ft.is_socket() { - tracing::debug!("Host fd {:?} is a socket", file.as_raw_fd()); - use yanix::socket::{get_socket_type, SockType}; - match unsafe { get_socket_type(file.as_raw_fd())? } { - SockType::Datagram => Filetype::SocketDgram, - SockType::Stream => Filetype::SocketStream, - _ => return Err(io::Error::from_raw_os_error(libc::EINVAL)), - } - } else if ft.is_fifo() { - tracing::debug!("Host fd {:?} is a fifo", file.as_raw_fd()); - Filetype::Unknown - } else { - tracing::debug!("Host fd {:?} is unknown", file.as_raw_fd()); - return Err(io::Error::from_raw_os_error(libc::EINVAL)); - }; - Ok(file_type) -} - -pub(super) fn get_rights(file: &File, file_type: &Filetype) -> io::Result { - use yanix::{fcntl, file::OFlags}; - let (base, inheriting) = match file_type { - Filetype::BlockDevice => ( - Rights::block_device_base(), - Rights::block_device_inheriting(), - ), - Filetype::CharacterDevice => { - use yanix::file::isatty; - if unsafe { isatty(file.as_raw_fd())? } { - (Rights::tty_base(), Rights::tty_base()) - } else { - ( - Rights::character_device_base(), - Rights::character_device_inheriting(), - ) - } - } - Filetype::SocketDgram | Filetype::SocketStream => { - (Rights::socket_base(), Rights::socket_inheriting()) - } - Filetype::SymbolicLink | Filetype::Unknown => ( - Rights::regular_file_base(), - Rights::regular_file_inheriting(), - ), - Filetype::Directory => (Rights::directory_base(), Rights::directory_inheriting()), - Filetype::RegularFile => ( - Rights::regular_file_base(), - Rights::regular_file_inheriting(), - ), - }; - let mut rights = HandleRights::new(base, inheriting); - let flags = unsafe { fcntl::get_status_flags(file.as_raw_fd())? }; - let accmode = flags & OFlags::ACCMODE; - if accmode == OFlags::RDONLY { - rights.base &= !Rights::FD_WRITE; - } else if accmode == OFlags::WRONLY { - rights.base &= !Rights::FD_READ; - } - Ok(rights) -} - -pub fn preopen_dir>(path: P) -> io::Result { - File::open(path) -} - -impl From for ClockId { - fn from(clock_id: Clockid) -> Self { - use Clockid::*; - match clock_id { - Realtime => Self::Realtime, - Monotonic => Self::Monotonic, - ProcessCputimeId => Self::ProcessCPUTime, - ThreadCputimeId => Self::ThreadCPUTime, - } - } -} - -impl From for OFlags { - fn from(fdflags: Fdflags) -> Self { - let mut nix_flags = Self::empty(); - if fdflags.contains(Fdflags::APPEND) { - nix_flags.insert(Self::APPEND); - } - if fdflags.contains(Fdflags::DSYNC) { - nix_flags.insert(Self::DSYNC); - } - if fdflags.contains(Fdflags::NONBLOCK) { - nix_flags.insert(Self::NONBLOCK); - } - if fdflags.contains(Fdflags::RSYNC) { - nix_flags.insert(O_RSYNC); - } - if fdflags.contains(Fdflags::SYNC) { - nix_flags.insert(Self::SYNC); - } - nix_flags - } -} - -impl From for Fdflags { - fn from(oflags: OFlags) -> Self { - let mut fdflags = Self::empty(); - if oflags.contains(OFlags::APPEND) { - fdflags |= Self::APPEND; - } - if oflags.contains(OFlags::DSYNC) { - fdflags |= Self::DSYNC; - } - if oflags.contains(OFlags::NONBLOCK) { - fdflags |= Self::NONBLOCK; - } - if oflags.contains(O_RSYNC) { - fdflags |= Self::RSYNC; - } - if oflags.contains(OFlags::SYNC) { - fdflags |= Self::SYNC; - } - fdflags - } -} - -impl From for OFlags { - fn from(oflags: Oflags) -> Self { - let mut nix_flags = Self::empty(); - if oflags.contains(Oflags::CREAT) { - nix_flags.insert(Self::CREAT); - } - if oflags.contains(Oflags::DIRECTORY) { - nix_flags.insert(Self::DIRECTORY); - } - if oflags.contains(Oflags::EXCL) { - nix_flags.insert(Self::EXCL); - } - if oflags.contains(Oflags::TRUNC) { - nix_flags.insert(Self::TRUNC); - } - nix_flags - } -} - -impl TryFrom for Filestat { - type Error = Error; - - fn try_from(filestat: libc::stat) -> Result { - fn filestat_to_timestamp(secs: u64, nsecs: u64) -> Result { - secs.checked_mul(1_000_000_000) - .and_then(|sec_nsec| sec_nsec.checked_add(nsecs)) - .ok_or(Error::Overflow) - } - - let filetype = yanix::file::FileType::from_stat_st_mode(filestat.st_mode); - let dev = filestat.st_dev.try_into()?; - let ino = filestat.st_ino.try_into()?; - 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(Self { - dev, - ino, - nlink: filestat.st_nlink.into(), - size: filestat.st_size as Filesize, - atim, - ctim, - mtim, - filetype: filetype.into(), - }) - } -} - -impl From for Filetype { - fn from(ft: yanix::file::FileType) -> Self { - use yanix::file::FileType::*; - match ft { - RegularFile => Self::RegularFile, - Symlink => Self::SymbolicLink, - 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? - } - } -} - -impl From for AtFlags { - fn from(flags: Lookupflags) -> Self { - match flags { - Lookupflags::SYMLINK_FOLLOW => Self::empty(), - _ => Self::SYMLINK_NOFOLLOW, - } - } -} diff --git a/crates/wasi-common/src/sys/unix/osdir.rs b/crates/wasi-common/src/sys/unix/osdir.rs deleted file mode 100644 index d143fc98d0..0000000000 --- a/crates/wasi-common/src/sys/unix/osdir.rs +++ /dev/null @@ -1,35 +0,0 @@ -use super::oshandle::RawOsHandle; -use crate::handle::{HandleRights, Rights, RightsExt}; -use std::convert::TryFrom; -use std::fs::File; -use std::io; -use std::os::unix::prelude::{AsRawFd, FromRawFd, IntoRawFd}; - -pub use super::sys_impl::osdir::OsDir; - -impl TryFrom for OsDir { - type Error = io::Error; - - fn try_from(file: File) -> io::Result { - let ft = file.metadata()?.file_type(); - if !ft.is_dir() { - return Err(io::Error::from_raw_os_error(libc::EINVAL)); - } - let rights = get_rights(&file)?; - let handle = unsafe { RawOsHandle::from_raw_fd(file.into_raw_fd()) }; - Self::new(rights, handle) - } -} - -fn get_rights(file: &File) -> io::Result { - use yanix::{fcntl, file::OFlags}; - let mut rights = HandleRights::new(Rights::directory_base(), Rights::directory_inheriting()); - let flags = unsafe { fcntl::get_status_flags(file.as_raw_fd())? }; - let accmode = flags & OFlags::ACCMODE; - if accmode == OFlags::RDONLY { - rights.base &= !Rights::FD_WRITE; - } else if accmode == OFlags::WRONLY { - rights.base &= !Rights::FD_READ; - } - Ok(rights) -} diff --git a/crates/wasi-common/src/sys/unix/osfile.rs b/crates/wasi-common/src/sys/unix/osfile.rs deleted file mode 100644 index 07fc57563c..0000000000 --- a/crates/wasi-common/src/sys/unix/osfile.rs +++ /dev/null @@ -1,37 +0,0 @@ -use super::oshandle::RawOsHandle; -use crate::handle::{HandleRights, Rights, RightsExt}; -use crate::sys::osfile::OsFile; -use std::convert::TryFrom; -use std::fs::File; -use std::io; -use std::os::unix::prelude::{AsRawFd, FromRawFd, IntoRawFd}; - -impl TryFrom for OsFile { - type Error = io::Error; - - fn try_from(file: File) -> io::Result { - let ft = file.metadata()?.file_type(); - if !ft.is_file() { - return Err(io::Error::from_raw_os_error(libc::EINVAL)); - } - let rights = get_rights(&file)?; - let handle = unsafe { RawOsHandle::from_raw_fd(file.into_raw_fd()) }; - Ok(Self::new(rights, handle)) - } -} - -fn get_rights(file: &File) -> io::Result { - use yanix::{fcntl, file::OFlags}; - let mut rights = HandleRights::new( - Rights::regular_file_base(), - Rights::regular_file_inheriting(), - ); - let flags = unsafe { fcntl::get_status_flags(file.as_raw_fd())? }; - let accmode = flags & OFlags::ACCMODE; - if accmode == OFlags::RDONLY { - rights.base &= !Rights::FD_WRITE; - } else if accmode == OFlags::WRONLY { - rights.base &= !Rights::FD_READ; - } - Ok(rights) -} diff --git a/crates/wasi-common/src/sys/unix/oshandle.rs b/crates/wasi-common/src/sys/unix/oshandle.rs deleted file mode 100644 index b706a5bb0f..0000000000 --- a/crates/wasi-common/src/sys/unix/oshandle.rs +++ /dev/null @@ -1,40 +0,0 @@ -use std::fs::File; -use std::io; -use std::os::unix::prelude::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; - -#[derive(Debug)] -pub struct RawOsHandle(File); - -impl RawOsHandle { - /// Tries clone `self`. - pub(crate) fn try_clone(&self) -> io::Result { - let fd = self.0.try_clone()?; - Ok(unsafe { Self::from_raw_fd(fd.into_raw_fd()) }) - } - /// Consumes `other` taking the ownership of the underlying - /// `RawFd` file descriptor. - /// - /// Note that the state of `Dir` stream pointer *will* not be carried - /// across from `other` to `self`. - pub(crate) fn update_from(&self, _other: Self) { - panic!("RawOsHandle::update_from should never be issued on Unix!") - } -} - -impl AsRawFd for RawOsHandle { - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() - } -} - -impl IntoRawFd for RawOsHandle { - fn into_raw_fd(self) -> RawFd { - self.0.into_raw_fd() - } -} - -impl FromRawFd for RawOsHandle { - unsafe fn from_raw_fd(raw: RawFd) -> Self { - Self(File::from_raw_fd(raw)) - } -} diff --git a/crates/wasi-common/src/sys/unix/osother.rs b/crates/wasi-common/src/sys/unix/osother.rs deleted file mode 100644 index d42ce1e58b..0000000000 --- a/crates/wasi-common/src/sys/unix/osother.rs +++ /dev/null @@ -1,22 +0,0 @@ -use super::oshandle::RawOsHandle; -use super::{get_file_type, get_rights}; -use crate::handle::Filetype; -use crate::sys::osother::OsOther; -use std::convert::TryFrom; -use std::fs::File; -use std::io; -use std::os::unix::prelude::{FromRawFd, IntoRawFd}; - -impl TryFrom for OsOther { - type Error = io::Error; - - fn try_from(file: File) -> io::Result { - let file_type = get_file_type(&file)?; - if file_type == Filetype::RegularFile || file_type == Filetype::Directory { - return Err(io::Error::from_raw_os_error(libc::EINVAL)); - } - let rights = get_rights(&file, &file_type)?; - let handle = unsafe { RawOsHandle::from_raw_fd(file.into_raw_fd()) }; - Ok(Self::new(file_type, rights, handle)) - } -} diff --git a/crates/wasi-common/src/sys/unix/path.rs b/crates/wasi-common/src/sys/unix/path.rs deleted file mode 100644 index d952790eeb..0000000000 --- a/crates/wasi-common/src/sys/unix/path.rs +++ /dev/null @@ -1,271 +0,0 @@ -use crate::handle::{Fdflags, Filestat, Fstflags, Handle, HandleRights, Oflags, Rights}; -use crate::sched::Timestamp; -use crate::sys::osdir::OsDir; -use crate::sys::AsFile; -use crate::{Error, Result}; -use std::convert::{TryFrom, TryInto}; -use std::ffi::OsStr; -use std::fs::File; -use std::os::unix::prelude::{AsRawFd, FromRawFd, OsStrExt}; -use std::str; -use yanix::file::OFlags; - -pub(crate) use super::sys_impl::path::*; - -/// 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 from_host>(s: S) -> Result { - let s = str::from_utf8(s.as_ref().as_bytes())?; - Ok(s.to_owned()) -} - -pub(crate) fn open_rights( - input_rights: &HandleRights, - oflags: Oflags, - fs_flags: Fdflags, -) -> HandleRights { - // which rights are needed on the dirfd? - let mut needed_base = Rights::PATH_OPEN; - let mut needed_inheriting = input_rights.base | input_rights.inheriting; - - // convert open flags - let oflags: OFlags = oflags.into(); - if oflags.contains(OFlags::CREAT) { - needed_base |= Rights::PATH_CREATE_FILE; - } - if oflags.contains(OFlags::TRUNC) { - needed_base |= Rights::PATH_FILESTAT_SET_SIZE; - } - - // convert file descriptor flags - let fdflags: OFlags = fs_flags.into(); - if fdflags.contains(OFlags::DSYNC) { - needed_inheriting |= Rights::FD_DATASYNC; - } - if fdflags.intersects(super::O_RSYNC | OFlags::SYNC) { - needed_inheriting |= Rights::FD_SYNC; - } - - HandleRights::new(needed_base, needed_inheriting) -} - -pub(crate) fn readlinkat(dirfd: &OsDir, path: &str) -> Result { - use std::os::unix::prelude::AsRawFd; - use yanix::file::readlinkat; - - tracing::debug!(path = path, "path_get readlinkat"); - - let path = unsafe { readlinkat(dirfd.as_raw_fd(), path)? }; - let path = from_host(path)?; - Ok(path) -} - -pub(crate) fn create_directory(base: &OsDir, path: &str) -> Result<()> { - use yanix::file::{mkdirat, Mode}; - unsafe { mkdirat(base.as_raw_fd(), path, Mode::from_bits_truncate(0o777))? }; - Ok(()) -} - -pub(crate) fn link( - old_dirfd: &OsDir, - old_path: &str, - new_dirfd: &OsDir, - new_path: &str, - follow_symlinks: bool, -) -> Result<()> { - use yanix::file::{linkat, AtFlags}; - let flags = if follow_symlinks { - AtFlags::SYMLINK_FOLLOW - } else { - AtFlags::empty() - }; - unsafe { - linkat( - old_dirfd.as_raw_fd(), - old_path, - new_dirfd.as_raw_fd(), - new_path, - flags, - )? - }; - Ok(()) -} - -pub(crate) fn open( - dirfd: &OsDir, - path: &str, - read: bool, - write: bool, - oflags: Oflags, - fs_flags: Fdflags, -) -> Result> { - 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(oflags.into()); - - // convert file descriptor flags - nix_all_oflags.insert(fs_flags.into()); - - // 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!( - dirfd = tracing::field::debug(dirfd), - path = tracing::field::debug(path), - oflags = tracing::field::debug(nix_all_oflags), - "path_open" - ); - - let fd_no = unsafe { - openat( - dirfd.as_raw_fd(), - path, - nix_all_oflags, - Mode::from_bits_truncate(0o666), - ) - }; - let new_fd = match fd_no { - 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(dirfd.as_raw_fd(), path, AtFlags::SYMLINK_NOFOLLOW) } { - Ok(stat) => { - if FileType::from_stat_st_mode(stat.st_mode) == FileType::Socket { - return Err(Error::Notsup); - } - } - Err(err) => { - tracing::debug!( - error = tracing::field::debug(&err), - "path_open fstatat error", - ); - } - } - } - // 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(dirfd.as_raw_fd(), path, AtFlags::SYMLINK_NOFOLLOW) } { - Ok(stat) => { - if FileType::from_stat_st_mode(stat.st_mode) == FileType::Symlink { - return Err(Error::Loop); - } - } - Err(err) => { - tracing::debug!( - error = tracing::field::debug(&err), - "path_open fstatat error", - ); - } - } - } - // 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(Error::Loop); - } - _ => {} - } - - return Err(e.into()); - } - }; - - tracing::debug!(new_fd = tracing::field::debug(new_fd)); - - // Determine the type of the new file descriptor and which rights contradict with this type - let file = unsafe { File::from_raw_fd(new_fd) }; - let handle = >::try_from(file)?; - Ok(handle) -} - -pub(crate) fn readlink(dirfd: &OsDir, path: &str, buf: &mut [u8]) -> Result { - use std::cmp::min; - use yanix::file::readlinkat; - let read_link = unsafe { readlinkat(dirfd.as_raw_fd(), path)? }; - let read_link = from_host(read_link)?; - 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 remove_directory(dirfd: &OsDir, path: &str) -> Result<()> { - use yanix::file::{unlinkat, AtFlags}; - unsafe { unlinkat(dirfd.as_raw_fd(), path, AtFlags::REMOVEDIR)? }; - Ok(()) -} - -pub(crate) fn filestat_get_at(dirfd: &OsDir, path: &str, follow: bool) -> Result { - use yanix::file::{fstatat, AtFlags}; - let flags = if follow { - AtFlags::empty() - } else { - AtFlags::SYMLINK_NOFOLLOW - }; - let stat = unsafe { fstatat(dirfd.as_raw_fd(), path, flags)? }; - let stat = stat.try_into()?; - Ok(stat) -} - -pub(crate) fn filestat_set_times_at( - dirfd: &OsDir, - path: &str, - atim: Timestamp, - mtim: Timestamp, - fst_flags: Fstflags, - follow: bool, -) -> Result<()> { - use std::time::{Duration, UNIX_EPOCH}; - use yanix::filetime::*; - - let set_atim = fst_flags.contains(Fstflags::ATIM); - let set_atim_now = fst_flags.contains(Fstflags::ATIM_NOW); - let set_mtim = fst_flags.contains(Fstflags::MTIM); - let set_mtim_now = fst_flags.contains(Fstflags::MTIM_NOW); - - if (set_atim && set_atim_now) || (set_mtim && set_mtim_now) { - return Err(Error::Inval); - } - - let atim = if set_atim { - let time = UNIX_EPOCH + Duration::from_nanos(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(mtim); - FileTime::FileTime(filetime::FileTime::from_system_time(time)) - } else if set_mtim_now { - FileTime::Now - } else { - FileTime::Omit - }; - - utimensat(&*dirfd.as_file()?, path, atim, mtim, !follow)?; - - Ok(()) -} diff --git a/crates/wasi-common/src/sys/unix/poll.rs b/crates/wasi-common/src/sys/unix/poll.rs deleted file mode 100644 index 2b45c7110f..0000000000 --- a/crates/wasi-common/src/sys/unix/poll.rs +++ /dev/null @@ -1,165 +0,0 @@ -use crate::entry::EntryHandle; -use crate::handle::Filetype; -use crate::sched::{ - ClockEventData, Errno, Event, EventFdReadwrite, Eventrwflags, Eventtype, FdEventData, -}; -use crate::sys::AsFile; -use crate::{Error, Result}; -use std::io; -use std::{convert::TryInto, os::unix::prelude::AsRawFd}; -use yanix::file::fionread; -use yanix::poll::{poll, PollFd, PollFlags}; - -pub(crate) fn oneoff( - timeout: Option, - fd_events: Vec, - events: &mut Vec, -) -> Result<()> { - if fd_events.is_empty() && timeout.is_none() { - return Ok(()); - } - - let poll_fds: Result> = fd_events - .iter() - .map(|event| { - let mut flags = PollFlags::empty(); - match event.r#type { - Eventtype::FdRead => flags.insert(PollFlags::POLLIN), - Eventtype::FdWrite => 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!(), - }; - let file = event.handle.as_file()?; - unsafe { Ok(PollFd::new(file.as_raw_fd(), flags)) } - }) - .collect(); - let mut poll_fds = poll_fds?; - - 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_timeout = tracing::field::debug(poll_timeout), - "poll_oneoff" - ); - - 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 { - 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); - handle_fd_event(ready_events, events)? - }) -} - -fn handle_timeout_event(timeout: ClockEventData, events: &mut Vec) { - events.push(Event { - userdata: timeout.userdata, - error: Errno::Success, - type_: Eventtype::Clock, - fd_readwrite: EventFdReadwrite { - flags: Eventrwflags::empty(), - nbytes: 0, - }, - }); -} - -fn handle_fd_event( - ready_events: impl Iterator, - events: &mut Vec, -) -> Result<()> { - fn query_nbytes(handle: EntryHandle) -> Result { - let file = handle.as_file()?; - if handle.get_file_type() == Filetype::RegularFile { - // fionread may overflow for large files, so use another way for regular files. - use yanix::file::tell; - let meta = file.metadata()?; - let len = meta.len(); - let host_offset = unsafe { tell(file.as_raw_fd())? }; - return Ok(len - host_offset); - } - Ok(unsafe { fionread(file.as_raw_fd())?.into() }) - } - - for (fd_event, poll_fd) in ready_events { - tracing::debug!( - poll_fd = tracing::field::debug(poll_fd), - poll_event = tracing::field::debug(&fd_event), - "poll_oneoff handle_fd_event" - ); - - let revents = match poll_fd.revents() { - Some(revents) => revents, - None => continue, - }; - - let nbytes = if fd_event.r#type == Eventtype::FdRead { - query_nbytes(fd_event.handle)? - } else { - 0 - }; - - let output_event = if revents.contains(PollFlags::POLLNVAL) { - Event { - userdata: fd_event.userdata, - error: Error::Badf.try_into().unwrap(), - type_: fd_event.r#type, - fd_readwrite: EventFdReadwrite { - nbytes: 0, - flags: Eventrwflags::FD_READWRITE_HANGUP, - }, - } - } else if revents.contains(PollFlags::POLLERR) { - Event { - userdata: fd_event.userdata, - error: Error::Io.try_into().unwrap(), - type_: fd_event.r#type, - fd_readwrite: EventFdReadwrite { - nbytes: 0, - flags: Eventrwflags::FD_READWRITE_HANGUP, - }, - } - } else if revents.contains(PollFlags::POLLHUP) { - Event { - userdata: fd_event.userdata, - error: Errno::Success, - type_: fd_event.r#type, - fd_readwrite: EventFdReadwrite { - nbytes: 0, - flags: Eventrwflags::FD_READWRITE_HANGUP, - }, - } - } else if revents.contains(PollFlags::POLLIN) | revents.contains(PollFlags::POLLOUT) { - Event { - userdata: fd_event.userdata, - error: Errno::Success, - type_: fd_event.r#type, - fd_readwrite: EventFdReadwrite { - nbytes: nbytes.try_into()?, - flags: Eventrwflags::empty(), - }, - } - } else { - continue; - }; - - events.push(output_event); - } - - Ok(()) -} diff --git a/crates/wasi-common/src/sys/unix/stdio.rs b/crates/wasi-common/src/sys/unix/stdio.rs deleted file mode 100644 index 5a38a2992b..0000000000 --- a/crates/wasi-common/src/sys/unix/stdio.rs +++ /dev/null @@ -1,59 +0,0 @@ -use super::{get_file_type, get_rights}; -use crate::handle::Handle; -use crate::sys::stdio::{Stderr, StderrExt, Stdin, StdinExt, Stdout, StdoutExt}; -use std::cell::Cell; -use std::fs::File; -use std::io; -use std::mem::ManuallyDrop; -use std::os::unix::prelude::{AsRawFd, FromRawFd, RawFd}; - -impl AsRawFd for Stdin { - fn as_raw_fd(&self) -> RawFd { - io::stdin().as_raw_fd() - } -} - -impl AsRawFd for Stdout { - fn as_raw_fd(&self) -> RawFd { - io::stdout().as_raw_fd() - } -} - -impl AsRawFd for Stderr { - fn as_raw_fd(&self) -> RawFd { - io::stderr().as_raw_fd() - } -} - -impl StdinExt for Stdin { - fn stdin() -> io::Result> { - let file = unsafe { File::from_raw_fd(io::stdin().as_raw_fd()) }; - let file = ManuallyDrop::new(file); - let file_type = get_file_type(&file)?; - let rights = get_rights(&file, &file_type)?; - let rights = Cell::new(rights); - Ok(Box::new(Self { file_type, rights })) - } -} - -impl StdoutExt for Stdout { - fn stdout() -> io::Result> { - let file = unsafe { File::from_raw_fd(io::stdout().as_raw_fd()) }; - let file = ManuallyDrop::new(file); - let file_type = get_file_type(&file)?; - let rights = get_rights(&file, &file_type)?; - let rights = Cell::new(rights); - Ok(Box::new(Self { file_type, rights })) - } -} - -impl StderrExt for Stderr { - fn stderr() -> io::Result> { - let file = unsafe { File::from_raw_fd(io::stderr().as_raw_fd()) }; - let file = ManuallyDrop::new(file); - let file_type = get_file_type(&file)?; - let rights = get_rights(&file, &file_type)?; - let rights = Cell::new(rights); - Ok(Box::new(Self { file_type, rights })) - } -} diff --git a/crates/wasi-common/src/sys/windows/clock.rs b/crates/wasi-common/src/sys/windows/clock.rs deleted file mode 100644 index 2359884f23..0000000000 --- a/crates/wasi-common/src/sys/windows/clock.rs +++ /dev/null @@ -1,105 +0,0 @@ -use crate::sched::{Clockid, Timestamp}; -use crate::{Error, Result}; -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 res_get(clock_id: Clockid) -> Result { - let ts = 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 - Clockid::Realtime => 55_000_000, - // std::time::Instant uses QueryPerformanceCounter & QueryPerformanceFrequency internally - 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 - Clockid::ProcessCputimeId => 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 - Clockid::ThreadCputimeId => 100, - }; - Ok(ts) -} - -pub(crate) fn time_get(clock_id: Clockid) -> Result { - let duration = match clock_id { - Clockid::Realtime => get_monotonic_time(), - Clockid::Monotonic => get_realtime_time()?, - Clockid::ProcessCputimeId => get_proc_cputime()?, - Clockid::ThreadCputimeId => get_thread_cputime()?, - }; - let duration = duration.as_nanos().try_into()?; - Ok(duration) -} - -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() -> Result { - SystemTime::now() - .duration_since(UNIX_EPOCH) - .map_err(|_| Error::Fault) -} - -fn get_proc_cputime() -> Result { - Ok(ProcessTime::try_now()?.as_duration()) -} - -fn get_thread_cputime() -> Result { - 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/sys/windows/fd.rs b/crates/wasi-common/src/sys/windows/fd.rs deleted file mode 100644 index 4e3e16021c..0000000000 --- a/crates/wasi-common/src/sys/windows/fd.rs +++ /dev/null @@ -1,195 +0,0 @@ -use super::file_serial_no; -use super::oshandle::RawOsHandle; -use crate::handle::{Advice, Dircookie, Dirent, Fdflags, Filesize, Filestat}; -use crate::path; -use crate::sys::osdir::OsDir; -use crate::sys::osfile::OsFile; -use crate::sys::AsFile; -use crate::Result; -use std::convert::TryInto; -use std::fs::{File, OpenOptions}; -use std::os::windows::fs::OpenOptionsExt; -use std::os::windows::prelude::{AsRawHandle, FromRawHandle}; -use std::path::Path; -use tracing::trace; -use winx::file::{AccessMode, FileModeInformation, Flags}; - -pub(crate) fn fdstat_get(file: &File) -> Result { - let mut fdflags = Fdflags::empty(); - let handle = file.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 |= 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 |= Fdflags::SYNC; - } - - // Files do not support the `__WASI_FDFLAGS_NONBLOCK` flag - - Ok(fdflags) -} - -// TODO Investigate further for Stdio handles. `ReOpenFile` requires the file -// handle came from `CreateFile`, but the Rust's libstd will use `GetStdHandle` -// rather than `CreateFile`. Relevant discussion can be found in: -// https://github.com/rust-lang/rust/issues/40490 -pub(crate) fn fdstat_set_flags(file: &File, fdflags: Fdflags) -> Result> { - let handle = file.as_raw_handle(); - let access_mode = winx::file::query_access_information(handle)?; - let new_access_mode = file_access_mode_from_fdflags( - fdflags, - access_mode.contains(AccessMode::FILE_READ_DATA), - access_mode.contains(AccessMode::FILE_WRITE_DATA) - | access_mode.contains(AccessMode::FILE_APPEND_DATA), - ); - unsafe { - Ok(Some(RawOsHandle::from_raw_handle(winx::file::reopen_file( - handle, - new_access_mode, - fdflags.into(), - )?))) - } -} - -pub(crate) fn advise( - _file: &OsFile, - _advice: Advice, - _offset: Filesize, - _len: Filesize, -) -> Result<()> { - Ok(()) -} - -fn file_access_mode_from_fdflags(fdflags: Fdflags, read: bool, write: bool) -> AccessMode { - let mut access_mode = AccessMode::READ_CONTROL; - - // Note that `GENERIC_READ` and `GENERIC_WRITE` cannot be used to properly support append-only mode - // The file-specific flags `FILE_GENERIC_READ` and `FILE_GENERIC_WRITE` are used here instead - // These flags have the same semantic meaning for file objects, but allow removal of specific permissions (see below) - if read { - access_mode.insert(AccessMode::FILE_GENERIC_READ); - } - - if write { - access_mode.insert(AccessMode::FILE_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.contains(&Fdflags::APPEND) { - access_mode.insert(AccessMode::FILE_APPEND_DATA); - access_mode.remove(AccessMode::FILE_WRITE_DATA); - } - - access_mode -} - -// 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 readdir( - dirfd: &OsDir, - cookie: Dircookie, -) -> Result>>> { - use winx::file::get_file_path; - - let cookie = cookie.try_into()?; - let path = get_file_path(&*dirfd.as_file()?)?; - // 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?; - let ftype = dir.file_type()?; - let name = path::from_host(dir.file_name())?; - let d_ino = File::open(dir.path()).and_then(|f| file_serial_no(&f))?; - let dirent = Dirent { - d_namlen: name.len().try_into()?, - d_type: ftype.into(), - d_ino, - d_next: no, - }; - - Ok((dirent, name)) - }); - - // 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(Box::new(iter.skip(cookie))) -} - -fn dirent_from_path>( - path: P, - name: &str, - cookie: Dircookie, -) -> Result<(Dirent, String)> { - 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(); - let name = name.to_owned(); - let dirent = Dirent { - d_namlen: name.len().try_into()?, - d_next: cookie, - d_type: ty.into(), - d_ino: file_serial_no(&file)?, - }; - Ok((dirent, name)) -} - -pub(crate) fn filestat_get(file: &File) -> Result { - let filestat = file.try_into()?; - Ok(filestat) -} diff --git a/crates/wasi-common/src/sys/windows/mod.rs b/crates/wasi-common/src/sys/windows/mod.rs deleted file mode 100644 index bd31ea89da..0000000000 --- a/crates/wasi-common/src/sys/windows/mod.rs +++ /dev/null @@ -1,183 +0,0 @@ -pub(crate) mod clock; -pub(crate) mod fd; -pub(crate) mod osdir; -pub(crate) mod osfile; -pub(crate) mod oshandle; -pub(crate) mod osother; -pub(crate) mod path; -pub(crate) mod poll; -pub(crate) mod stdio; - -use crate::handle::{Fdflags, Filestat, Filetype, HandleRights, Oflags, Rights, RightsExt}; -use crate::sys::AsFile; -use crate::{Error, Result}; -use std::convert::{TryFrom, TryInto}; -use std::fs::File; -use std::mem::ManuallyDrop; -use std::os::windows::prelude::{AsRawHandle, FromRawHandle}; -use std::path::Path; -use std::time::{SystemTime, UNIX_EPOCH}; -use std::{io, string}; -use winx::file::{CreationDisposition, Flags}; - -impl AsFile for T { - fn as_file(&self) -> io::Result> { - let file = unsafe { File::from_raw_handle(self.as_raw_handle()) }; - Ok(ManuallyDrop::new(file)) - } -} - -pub(super) fn get_file_type(file: &File) -> io::Result { - let file_type = unsafe { winx::file::get_file_type(file.as_raw_handle())? }; - let file_type = if file_type.is_char() { - // character file: LPT device or console - // TODO: rule out LPT device - Filetype::CharacterDevice - } else if file_type.is_disk() { - // disk file: file, dir or disk device - let meta = file.metadata()?; - if meta.is_dir() { - Filetype::Directory - } else if meta.is_file() { - Filetype::RegularFile - } 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? - Filetype::SocketStream - } else { - return Err(io::Error::from_raw_os_error(libc::EINVAL)); - }; - Ok(file_type) -} - -pub(super) fn get_rights(file_type: &Filetype) -> io::Result { - let (base, inheriting) = match file_type { - Filetype::BlockDevice => ( - Rights::block_device_base(), - Rights::block_device_inheriting(), - ), - Filetype::CharacterDevice => (Rights::tty_base(), Rights::tty_base()), - Filetype::SocketDgram | Filetype::SocketStream => { - (Rights::socket_base(), Rights::socket_inheriting()) - } - Filetype::SymbolicLink | Filetype::Unknown => ( - Rights::regular_file_base(), - Rights::regular_file_inheriting(), - ), - Filetype::Directory => (Rights::directory_base(), Rights::directory_inheriting()), - Filetype::RegularFile => ( - Rights::regular_file_base(), - Rights::regular_file_inheriting(), - ), - }; - let rights = HandleRights::new(base, inheriting); - Ok(rights) -} - -pub fn preopen_dir>(path: P) -> io::Result { - use std::fs::OpenOptions; - use std::os::windows::fs::OpenOptionsExt; - use winapi::um::winbase::FILE_FLAG_BACKUP_SEMANTICS; - - // To open a directory using CreateFile, specify the - // FILE_FLAG_BACKUP_SEMANTICS flag as part of dwFileFlags... - // cf. https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-createfile2 - OpenOptions::new() - .create(false) - .write(true) - .read(true) - .attributes(FILE_FLAG_BACKUP_SEMANTICS) - .open(path) -} - -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) -} - -impl From for Error { - fn from(_err: string::FromUtf16Error) -> Self { - Self::Ilseq - } -} - -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()) -} - -fn change_time(file: &File) -> io::Result { - winx::file::change_time(file) -} - -fn systemtime_to_timestamp(st: SystemTime) -> Result { - st.duration_since(UNIX_EPOCH) - .map_err(|_| Error::Inval)? // date earlier than UNIX_EPOCH - .as_nanos() - .try_into() - .map_err(Into::into) // u128 doesn't fit into u64 -} - -impl TryFrom<&File> for Filestat { - type Error = Error; - - fn try_from(file: &File) -> Result { - let metadata = file.metadata()?; - Ok(Filestat { - 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: metadata.file_type().into(), - }) - } -} - -impl From for CreationDisposition { - fn from(oflags: Oflags) -> Self { - if oflags.contains(&Oflags::CREAT) { - if oflags.contains(&Oflags::EXCL) { - CreationDisposition::CREATE_NEW - } else { - CreationDisposition::CREATE_ALWAYS - } - } else if oflags.contains(&Oflags::TRUNC) { - CreationDisposition::TRUNCATE_EXISTING - } else { - CreationDisposition::OPEN_EXISTING - } - } -} - -impl From for Flags { - fn from(fdflags: Fdflags) -> Self { - // 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.contains(&Fdflags::DSYNC) - || fdflags.contains(&Fdflags::RSYNC) - || fdflags.contains(&Fdflags::SYNC) - { - flags.insert(Flags::FILE_FLAG_WRITE_THROUGH); - } - - flags - } -} diff --git a/crates/wasi-common/src/sys/windows/osdir.rs b/crates/wasi-common/src/sys/windows/osdir.rs deleted file mode 100644 index cf4303e7b2..0000000000 --- a/crates/wasi-common/src/sys/windows/osdir.rs +++ /dev/null @@ -1,66 +0,0 @@ -use super::oshandle::RawOsHandle; -use crate::handle::{HandleRights, Rights, RightsExt}; -use std::cell::Cell; -use std::convert::TryFrom; -use std::fs::File; -use std::io; -use std::os::windows::prelude::{AsRawHandle, FromRawHandle, IntoRawHandle}; - -#[derive(Debug)] -/// A directory in the operating system's file system. Its impl of `Handle` is -/// in `sys::osdir`. This type is exposed to all other modules as -/// `sys::osdir::OsDir` when configured. -/// -/// # Constructing `OsDir` -/// -/// `OsDir` can currently only be constructed from `std::fs::File` using -/// the `std::convert::TryFrom` trait: -/// -/// ```rust,no_run -/// use std::fs::OpenOptions; -/// use std::convert::TryFrom; -/// use std::os::windows::fs::OpenOptionsExt; -/// use wasi_common::OsDir; -/// use winapi::um::winbase::FILE_FLAG_BACKUP_SEMANTICS; -/// -/// let dir = OpenOptions::new().read(true).attributes(FILE_FLAG_BACKUP_SEMANTICS).open("some_dir").unwrap(); -/// let os_dir = OsDir::try_from(dir).unwrap(); -/// ``` -pub struct OsDir { - pub(crate) rights: Cell, - pub(crate) handle: RawOsHandle, -} - -impl OsDir { - pub(crate) fn new(rights: HandleRights, handle: RawOsHandle) -> io::Result { - let rights = Cell::new(rights); - Ok(Self { rights, handle }) - } -} - -impl TryFrom for OsDir { - type Error = io::Error; - - fn try_from(file: File) -> io::Result { - let ft = file.metadata()?.file_type(); - if !ft.is_dir() { - return Err(io::Error::from_raw_os_error(libc::EINVAL)); - } - let rights = get_rights(&file)?; - let handle = unsafe { RawOsHandle::from_raw_handle(file.into_raw_handle()) }; - Self::new(rights, handle) - } -} - -fn get_rights(file: &File) -> io::Result { - use winx::file::{query_access_information, AccessMode}; - let mut rights = HandleRights::new(Rights::directory_base(), Rights::directory_inheriting()); - let mode = query_access_information(file.as_raw_handle())?; - if mode.contains(AccessMode::FILE_GENERIC_READ) { - rights.base |= Rights::FD_READ; - } - if mode.contains(AccessMode::FILE_GENERIC_WRITE) { - rights.base |= Rights::FD_WRITE; - } - Ok(rights) -} diff --git a/crates/wasi-common/src/sys/windows/osfile.rs b/crates/wasi-common/src/sys/windows/osfile.rs deleted file mode 100644 index 047a09facc..0000000000 --- a/crates/wasi-common/src/sys/windows/osfile.rs +++ /dev/null @@ -1,37 +0,0 @@ -use super::oshandle::RawOsHandle; -use crate::handle::{HandleRights, Rights, RightsExt}; -use crate::sys::osfile::OsFile; -use std::convert::TryFrom; -use std::fs::File; -use std::io; -use std::os::windows::prelude::{AsRawHandle, FromRawHandle, IntoRawHandle}; - -impl TryFrom for OsFile { - type Error = io::Error; - - fn try_from(file: File) -> io::Result { - let ft = file.metadata()?.file_type(); - if !ft.is_file() { - return Err(io::Error::from_raw_os_error(libc::EINVAL)); - } - let rights = get_rights(&file)?; - let handle = unsafe { RawOsHandle::from_raw_handle(file.into_raw_handle()) }; - Ok(Self::new(rights, handle)) - } -} - -fn get_rights(file: &File) -> io::Result { - use winx::file::{query_access_information, AccessMode}; - let mut rights = HandleRights::new( - Rights::regular_file_base(), - Rights::regular_file_inheriting(), - ); - let mode = query_access_information(file.as_raw_handle())?; - if mode.contains(AccessMode::FILE_GENERIC_READ) { - rights.base |= Rights::FD_READ; - } - if mode.contains(AccessMode::FILE_GENERIC_WRITE) { - rights.base |= Rights::FD_WRITE; - } - Ok(rights) -} diff --git a/crates/wasi-common/src/sys/windows/oshandle.rs b/crates/wasi-common/src/sys/windows/oshandle.rs deleted file mode 100644 index 74aacb8019..0000000000 --- a/crates/wasi-common/src/sys/windows/oshandle.rs +++ /dev/null @@ -1,56 +0,0 @@ -use crate::sys::AsFile; -use std::cell::Cell; -use std::fs::File; -use std::io; -use std::mem::ManuallyDrop; -use std::os::windows::prelude::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; - -#[derive(Debug)] -pub struct RawOsHandle(Cell); - -impl RawOsHandle { - /// Tries cloning `self`. - pub(crate) fn try_clone(&self) -> io::Result { - let handle = self.as_file()?.try_clone()?; - Ok(Self(Cell::new(handle.into_raw_handle()))) - } - /// Consumes `other` taking the ownership of the underlying - /// `RawHandle` file handle. - pub(crate) fn update_from(&self, other: Self) { - let new_handle = other.into_raw_handle(); - let old_handle = self.0.get(); - self.0.set(new_handle); - // We need to remember to close the old_handle. - unsafe { - File::from_raw_handle(old_handle); - } - } -} - -impl Drop for RawOsHandle { - fn drop(&mut self) { - unsafe { - File::from_raw_handle(self.as_raw_handle()); - } - } -} - -impl AsRawHandle for RawOsHandle { - fn as_raw_handle(&self) -> RawHandle { - self.0.get() - } -} - -impl FromRawHandle for RawOsHandle { - unsafe fn from_raw_handle(handle: RawHandle) -> Self { - Self(Cell::new(handle)) - } -} - -impl IntoRawHandle for RawOsHandle { - fn into_raw_handle(self) -> RawHandle { - // We need to prevent dropping of the OsFile - let wrapped = ManuallyDrop::new(self); - wrapped.0.get() - } -} diff --git a/crates/wasi-common/src/sys/windows/osother.rs b/crates/wasi-common/src/sys/windows/osother.rs deleted file mode 100644 index 02e0f81390..0000000000 --- a/crates/wasi-common/src/sys/windows/osother.rs +++ /dev/null @@ -1,22 +0,0 @@ -use super::oshandle::RawOsHandle; -use super::{get_file_type, get_rights}; -use crate::handle::Filetype; -use crate::sys::osother::OsOther; -use std::convert::TryFrom; -use std::fs::File; -use std::io; -use std::os::windows::prelude::{FromRawHandle, IntoRawHandle}; - -impl TryFrom for OsOther { - type Error = io::Error; - - fn try_from(file: File) -> io::Result { - let file_type = get_file_type(&file)?; - if file_type == Filetype::RegularFile || file_type == Filetype::Directory { - return Err(io::Error::from_raw_os_error(libc::EINVAL)); - } - let rights = get_rights(&file_type)?; - let handle = unsafe { RawOsHandle::from_raw_handle(file.into_raw_handle()) }; - Ok(Self::new(file_type, rights, handle)) - } -} diff --git a/crates/wasi-common/src/sys/windows/path.rs b/crates/wasi-common/src/sys/windows/path.rs deleted file mode 100644 index 70b9e9b42f..0000000000 --- a/crates/wasi-common/src/sys/windows/path.rs +++ /dev/null @@ -1,539 +0,0 @@ -use crate::handle::{Fdflags, Filestat, Fstflags, Handle, HandleRights, Oflags, Rights}; -use crate::sched::Timestamp; -use crate::sys::osdir::OsDir; -use crate::sys::{fd, AsFile}; -use crate::{Error, Result}; -use std::convert::TryFrom; -use std::ffi::{OsStr, OsString}; -use std::fs::{self, Metadata, OpenOptions}; -use std::os::windows::ffi::{OsStrExt, OsStringExt}; -use std::os::windows::fs::OpenOptionsExt; -use std::path::{Path, PathBuf}; -use winapi::shared::winerror; -use winx::file::AccessMode; - -fn strip_trailing_slashes_and_concatenate(dirfd: &OsDir, path: &str) -> Result> { - if path.ends_with('/') { - let suffix = path.trim_end_matches('/'); - concatenate(dirfd, Path::new(suffix)).map(Some) - } else { - Ok(None) - } -} - -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) - } -} - -fn concatenate>(file: &OsDir, path: P) -> Result { - 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(Error::Notcapable); - } - - let dir_path = get_file_path(&*file.as_file()?)?; - // 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 = tracing::field::debug(&out_path)); - - Ok(out_path) -} - -fn file_access_mode_from_fdflags(fdflags: Fdflags, read: bool, write: bool) -> AccessMode { - let mut access_mode = AccessMode::READ_CONTROL; - - // We always need `FILE_WRITE_ATTRIBUTES` so that we can set attributes such as filetimes, etc. - access_mode.insert(AccessMode::FILE_WRITE_ATTRIBUTES); - - // Note that `GENERIC_READ` and `GENERIC_WRITE` cannot be used to properly support append-only mode - // The file-specific flags `FILE_GENERIC_READ` and `FILE_GENERIC_WRITE` are used here instead - // These flags have the same semantic meaning for file objects, but allow removal of specific permissions (see below) - if read { - access_mode.insert(AccessMode::FILE_GENERIC_READ); - } - - if write { - access_mode.insert(AccessMode::FILE_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.contains(&Fdflags::APPEND) { - access_mode.insert(AccessMode::FILE_APPEND_DATA); - access_mode.remove(AccessMode::FILE_WRITE_DATA); - } - - access_mode -} - -/// 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 from_host>(s: S) -> Result { - let vec: Vec = s.as_ref().encode_wide().collect(); - let s = String::from_utf16(&vec)?; - Ok(s) -} - -pub(crate) fn open_rights( - input_rights: &HandleRights, - oflags: Oflags, - fdflags: Fdflags, -) -> HandleRights { - // which rights are needed on the dirfd? - let mut needed_base = Rights::PATH_OPEN; - let mut needed_inheriting = input_rights.base | input_rights.inheriting; - - // convert open flags - if oflags.contains(&Oflags::CREAT) { - needed_base |= Rights::PATH_CREATE_FILE; - } else if oflags.contains(&Oflags::TRUNC) { - needed_base |= Rights::PATH_FILESTAT_SET_SIZE; - } - - // convert file descriptor flags - if fdflags.contains(&Fdflags::DSYNC) - || fdflags.contains(&Fdflags::RSYNC) - || fdflags.contains(&Fdflags::SYNC) - { - needed_inheriting |= Rights::FD_DATASYNC; - needed_inheriting |= Rights::FD_SYNC; - } - - HandleRights::new(needed_base, needed_inheriting) -} - -pub(crate) fn readlinkat(dirfd: &OsDir, s_path: &str) -> Result { - 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.as_file()?)?; - let dir_path = PathBuf::from(strip_extended_prefix(dir_path)); - let target_path = target_path - .strip_prefix(dir_path) - .map_err(|_| Error::Notcapable)?; - let target_path = target_path.to_str().ok_or(Error::Ilseq)?; - 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(Error::Notdir); - } - } - } - } - Err(err.into()) -} - -pub(crate) fn create_directory(file: &OsDir, path: &str) -> Result<()> { - let path = concatenate(file, path)?; - std::fs::create_dir(&path)?; - Ok(()) -} - -pub(crate) fn link( - old_dirfd: &OsDir, - old_path: &str, - new_dirfd: &OsDir, - new_path: &str, - follow_symlinks: bool, -) -> Result<()> { - use std::fs; - let mut old_path = concatenate(old_dirfd, old_path)?; - let new_path = concatenate(new_dirfd, new_path)?; - if follow_symlinks { - // in particular, this will return an error if the target path doesn't exist - tracing::debug!( - old_path = tracing::field::display(old_path.display()), - "Following symlinks" - ); - old_path = fs::canonicalize(&old_path).map_err(|e| match e.raw_os_error() { - // fs::canonicalize under Windows will return: - // * ERROR_FILE_NOT_FOUND, if it encounters a dangling symlink - // * ERROR_CANT_RESOLVE_FILENAME, if it encounters a symlink loop - Some(code) if code as u32 == winerror::ERROR_CANT_RESOLVE_FILENAME => Error::Loop, - _ => e.into(), - })?; - } - let err = match fs::hard_link(&old_path, &new_path) { - Ok(()) => return Ok(()), - Err(e) => e, - }; - if let Some(code) = err.raw_os_error() { - tracing::debug!("path_link at fs::hard_link error code={:?}", code); - if code as u32 == winerror::ERROR_ACCESS_DENIED { - // If an attempt is made to create a hard link to a directory, POSIX-compliant - // implementations of link return `EPERM`, but `ERROR_ACCESS_DENIED` is converted - // to `EACCES`. We detect and correct this case here. - if fs::metadata(&old_path).map(|m| m.is_dir()).unwrap_or(false) { - return Err(Error::Perm); - } - } - } - Err(err.into()) -} - -pub(crate) fn open( - dirfd: &OsDir, - path: &str, - read: bool, - write: bool, - oflags: Oflags, - fdflags: Fdflags, -) -> Result> { - use winx::file::{AccessMode, CreationDisposition, Flags}; - - let is_trunc = oflags.contains(&Oflags::TRUNC); - - if is_trunc { - // Windows does not support append mode when opening for truncation - // This is because truncation requires `GENERIC_WRITE` access, which will override the removal - // of the `FILE_WRITE_DATA` permission. - if fdflags.contains(&Fdflags::APPEND) { - return Err(Error::Notsup); - } - } - - // 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 calculated below. - let mut opts = OpenOptions::new(); - match oflags.into() { - CreationDisposition::CREATE_ALWAYS => { - opts.create(true).truncate(true).write(true); - } - CreationDisposition::CREATE_NEW => { - opts.create_new(true).write(true); - } - CreationDisposition::TRUNCATE_EXISTING => { - opts.truncate(true).write(true); - } - _ => {} - } - let path = concatenate(dirfd, path)?; - 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(Error::Loop); - } - // check if we are trying to open a file as a dir - if file_type.is_file() && oflags.contains(&Oflags::DIRECTORY) { - return Err(Error::Notdir); - } - } - Err(err) => match err.raw_os_error() { - Some(code) => { - tracing::debug!("path_open at symlink_metadata error code={:?}", code); - match code as u32 { - winerror::ERROR_FILE_NOT_FOUND => { - // file not found, let it proceed to actually - // trying to open it - } - winerror::ERROR_INVALID_NAME => { - // TODO rethink this. For now, migrate how we handled - // it in `path::openat` on Windows. - return Err(Error::Notdir); - } - _ => return Err(err.into()), - }; - } - None => { - tracing::debug!("Inconvertible OS error: {}", err); - return Err(Error::Io); - } - }, - } - - let mut access_mode = file_access_mode_from_fdflags(fdflags, read, write); - - // Truncation requires the special `GENERIC_WRITE` bit set (this is why it doesn't work with append-only mode) - if is_trunc { - access_mode |= AccessMode::GENERIC_WRITE; - } - - let flags: Flags = fdflags.into(); - let file = opts - .access_mode(access_mode.bits()) - .custom_flags(flags.bits()) - .open(&path)?; - let handle = >::try_from(file)?; - Ok(handle) -} - -pub(crate) fn readlink(dirfd: &OsDir, path: &str, buf: &mut [u8]) -> Result { - use winx::file::get_file_path; - - let path = concatenate(dirfd, path)?; - 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(&*dirfd.as_file()?)?; - let dir_path = PathBuf::from(strip_extended_prefix(dir_path)); - let target_path = target_path - .strip_prefix(dir_path) - .map_err(|_| Error::Notcapable) - .and_then(|path| path.to_str().map(String::from).ok_or(Error::Ilseq))?; - - 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) - } -} - -pub(crate) fn rename( - old_dirfd: &OsDir, - old_path_: &str, - new_dirfd: &OsDir, - new_path_: &str, -) -> Result<()> { - use std::fs; - - let old_path = concatenate(old_dirfd, old_path_)?; - let new_path = concatenate(new_dirfd, new_path_)?; - - // 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(Error::Notdir); - } - // 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() && new_path_.ends_with('/') { - return Err(Error::Notdir); - } - - // 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(Error::Isdir); - } 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(old_dirfd, old_path_)? - { - if path.is_file() { - return Err(Error::Notdir); - } - } - } - _ => {} - } - - Err(err.into()) - } - None => { - tracing::debug!("Inconvertible OS error: {}", err); - Err(Error::Io) - } - } -} - -pub(crate) fn symlink(old_path: &str, new_dirfd: &OsDir, new_path_: &str) -> Result<()> { - use std::os::windows::fs::{symlink_dir, symlink_file}; - - let old_path = concatenate(new_dirfd, Path::new(old_path))?; - let new_path = concatenate(new_dirfd, new_path_)?; - - // Windows distinguishes between file and directory symlinks. - // If the source doesn't exist or is an exotic file type, we fall back - // to regular file symlinks. - let use_dir_symlink = fs::metadata(&new_path) - .as_ref() - .map(Metadata::is_dir) - .unwrap_or(false); - - let res = if use_dir_symlink { - symlink_dir(&old_path, &new_path) - } else { - symlink_file(&old_path, &new_path) - }; - - let err = match res { - 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 { - // If the target contains a trailing slash, the Windows API returns - // ERROR_INVALID_NAME (which corresponds to ENOENT) instead of - // ERROR_ALREADY_EXISTS (which corresponds to EEXIST) - // - // This concerns only trailing slashes (not backslashes) and - // only symbolic links (not hard links). - // - // Since POSIX will return EEXIST in such case, we simulate this behavior - winerror::ERROR_INVALID_NAME => { - if let Some(path) = - strip_trailing_slashes_and_concatenate(new_dirfd, new_path_)? - { - if path.exists() { - return Err(Error::Exist); - } - } - } - _ => {} - } - - Err(err.into()) - } - None => { - tracing::debug!("Inconvertible OS error: {}", err); - Err(Error::Io) - } - } -} - -pub(crate) fn unlink_file(dirfd: &OsDir, path: &str) -> Result<()> { - use std::fs; - - let path = concatenate(dirfd, path)?; - 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(Error::Io) - } - } - } else if file_type.is_dir() { - Err(Error::Isdir) - } else if file_type.is_file() { - fs::remove_file(path).map_err(Into::into) - } else { - Err(Error::Inval) - } -} - -pub(crate) fn remove_directory(dirfd: &OsDir, path: &str) -> Result<()> { - let path = concatenate(dirfd, path)?; - std::fs::remove_dir(&path).map_err(Into::into) -} - -pub(crate) fn filestat_get_at(dirfd: &OsDir, path: &str, follow: bool) -> Result { - use winx::file::Flags; - let path = concatenate(dirfd, path)?; - let mut opts = OpenOptions::new(); - - if !follow { - // By specifying FILE_FLAG_OPEN_REPARSE_POINT, we force Windows to *not* dereference symlinks. - opts.custom_flags(Flags::FILE_FLAG_OPEN_REPARSE_POINT.bits()); - } - - let file = opts.read(true).open(path)?; - let stat = fd::filestat_get(&file)?; - Ok(stat) -} - -pub(crate) fn filestat_set_times_at( - dirfd: &OsDir, - path: &str, - atim: Timestamp, - mtim: Timestamp, - fst_flags: Fstflags, - follow: bool, -) -> Result<()> { - use winx::file::{AccessMode, Flags}; - let path = concatenate(dirfd, path)?; - let mut opts = OpenOptions::new(); - - if !follow { - // By specifying FILE_FLAG_OPEN_REPARSE_POINT, we force Windows to *not* dereference symlinks. - opts.custom_flags(Flags::FILE_FLAG_OPEN_REPARSE_POINT.bits()); - } - - let file = opts - .access_mode(AccessMode::FILE_WRITE_ATTRIBUTES.bits()) - .open(path)?; - fd::filestat_set_times(&file, atim, mtim, fst_flags)?; - Ok(()) -} diff --git a/crates/wasi-common/src/sys/windows/poll.rs b/crates/wasi-common/src/sys/windows/poll.rs deleted file mode 100644 index d7d2f3527c..0000000000 --- a/crates/wasi-common/src/sys/windows/poll.rs +++ /dev/null @@ -1,317 +0,0 @@ -use crate::handle::{Filetype, Handle}; -use crate::sched::{ - ClockEventData, Errno, Event, EventFdReadwrite, Eventrwflags, Eventtype, FdEventData, -}; -use crate::sys::osdir::OsDir; -use crate::sys::osfile::OsFile; -use crate::sys::osother::OsOther; -use crate::sys::stdio::{Stderr, Stdin, Stdout}; -use crate::sys::AsFile; -use crate::{Error, Result}; -use lazy_static::lazy_static; -use std::convert::{TryFrom, TryInto}; -use std::sync::mpsc::{self, Receiver, RecvTimeoutError, Sender, TryRecvError}; -use std::sync::Mutex; -use std::thread; -use std::time::Duration; -use tracing::{debug, error, trace, warn}; - -struct StdinPoll { - request_tx: Sender<()>, - notify_rx: Receiver, -} - -enum PollState { - Ready, - NotReady, // it's not ready, but we didn't wait - TimedOut, // it's not ready and a timeout has occurred - Error(Errno), -} - -enum WaitMode { - Timeout(Duration), - Infinite, - Immediate, -} - -impl StdinPoll { - // This function should not be used directly - // Correctness of this function crucially depends on the fact that - // mpsc::Receiver is !Sync. - fn poll(&self, wait_mode: WaitMode) -> PollState { - // Clean up possible unread result from the previous poll - match self.notify_rx.try_recv() { - Ok(_) | Err(TryRecvError::Empty) => {} - Err(TryRecvError::Disconnected) => panic!("notify_rx channel closed"), - } - - // Notify the worker thread that we want to poll stdin - self.request_tx.send(()).expect("request_tx channel closed"); - - // Wait for the worker thread to send a readiness notification - let pollret = match wait_mode { - WaitMode::Timeout(timeout) => { - self.notify_rx - .recv_timeout(timeout) - .unwrap_or_else(|e| match e { - RecvTimeoutError::Disconnected => panic!("notify_rx channel closed"), - RecvTimeoutError::Timeout => PollState::TimedOut, - }) - } - WaitMode::Infinite => self.notify_rx.recv().expect("notify_rx channel closed"), - WaitMode::Immediate => self.notify_rx.try_recv().unwrap_or_else(|e| match e { - TryRecvError::Disconnected => panic!("notify_rx channel closed"), - TryRecvError::Empty => PollState::NotReady, - }), - }; - - pollret - } - - fn event_loop(request_rx: Receiver<()>, notify_tx: Sender) -> ! { - use std::io::BufRead; - loop { - // Wait for the request to poll stdin - request_rx.recv().expect("request_rx channel closed"); - - // Wait for data to appear in stdin. - // If `fill_buf` returns any slice, then it means that either - // (a) there some data in stdin, if it's non-empty - // (b) EOF was received, if it's empty - // Linux returns `POLLIN` in both cases, and we imitate this behavior. - let resp = match std::io::stdin().lock().fill_buf() { - Ok(_) => PollState::Ready, - Err(e) => { - PollState::Error(Errno::try_from(Error::from(e)).expect("non-trapping error")) - } - }; - - // Notify the requestor about data in stdin. They may have already timed out, - // then the next requestor will have to clean the channel. - notify_tx.send(resp).expect("notify_tx channel closed"); - } - } -} - -lazy_static! { - static ref STDIN_POLL: Mutex = { - let (request_tx, request_rx) = mpsc::channel(); - let (notify_tx, notify_rx) = mpsc::channel(); - thread::spawn(move || StdinPoll::event_loop(request_rx, notify_tx)); - Mutex::new(StdinPoll { - request_tx, - notify_rx, - }) - }; -} - -fn make_rw_event(event: &FdEventData, nbytes: std::result::Result) -> Event { - let (nbytes, error) = match nbytes { - Ok(nbytes) => (nbytes, Errno::Success), - Err(e) => (u64::default(), e), - }; - Event { - userdata: event.userdata, - type_: event.r#type, - error, - fd_readwrite: EventFdReadwrite { - nbytes, - flags: Eventrwflags::empty(), - }, - } -} - -fn make_timeout_event(timeout: &ClockEventData) -> Event { - Event { - userdata: timeout.userdata, - type_: Eventtype::Clock, - error: Errno::Success, - fd_readwrite: EventFdReadwrite { - nbytes: 0, - flags: Eventrwflags::empty(), - }, - } -} - -fn handle_timeout(timeout_event: ClockEventData, timeout: Duration, events: &mut Vec) { - thread::sleep(timeout); - handle_timeout_event(timeout_event, events); -} - -fn handle_timeout_event(timeout_event: ClockEventData, events: &mut Vec) { - let new_event = make_timeout_event(&timeout_event); - events.push(new_event); -} - -fn handle_rw_event(event: FdEventData, out_events: &mut Vec) { - let handle = &event.handle; - let size = if let Some(_) = handle.as_any().downcast_ref::() { - // We return the only universally correct lower bound, see the comment later in the function. - Ok(1) - } else if let Some(_) = handle.as_any().downcast_ref::() { - // On Unix, ioctl(FIONREAD) will return 0 for stdout. Emulate the same behavior on Windows. - Ok(0) - } else if let Some(_) = handle.as_any().downcast_ref::() { - // On Unix, ioctl(FIONREAD) will return 0 for stdout/stderr. Emulate the same behavior on Windows. - Ok(0) - } else { - if event.r#type == Eventtype::FdRead { - handle - .as_file() - .and_then(|f| f.metadata()) - .map(|m| m.len()) - .map_err(|ioerror| { - Errno::try_from(Error::from(ioerror)).expect("non-trapping error") - }) - } else { - // The spec is unclear what nbytes should actually be for __WASI_EVENTTYPE_FD_WRITE and - // the implementation on Unix just returns 0 here, so it's probably fine - // to do the same on Windows for now. - // cf. https://github.com/WebAssembly/WASI/issues/148 - Ok(0) - } - }; - let new_event = make_rw_event(&event, size); - out_events.push(new_event); -} - -fn handle_error_event(event: FdEventData, error: Errno, out_events: &mut Vec) { - let new_event = make_rw_event(&event, Err(error)); - out_events.push(new_event); -} - -pub(crate) fn oneoff( - timeout: Option, - fd_events: Vec, - events: &mut Vec, -) -> Result<()> { - let timeout = timeout - .map(|event| { - event - .delay - .try_into() - .map(Duration::from_nanos) - .map(|dur| (event, dur)) - }) - .transpose()?; - - // With no events to listen, poll_oneoff just becomes a sleep. - if fd_events.is_empty() { - match timeout { - Some((event, dur)) => return Ok(handle_timeout(event, dur, events)), - // The implementation has to return Ok(()) in this case, - // cf. the comment in src/hostcalls_impl/misc.rs - None => return Ok(()), - } - } - - let mut stdin_events = vec![]; - let mut immediate_events = vec![]; - let mut pipe_events = vec![]; - - for event in fd_events { - let handle = &event.handle; - if let Some(_) = handle.as_any().downcast_ref::() { - immediate_events.push(event); - } else if let Some(_) = handle.as_any().downcast_ref::() { - immediate_events.push(event); - } else if let Some(_) = handle.as_any().downcast_ref::() { - stdin_events.push(event); - } else if let Some(_) = handle.as_any().downcast_ref::() { - // stdout are always considered ready to write because there seems to - // be no way of checking if a write to stdout would block. - // - // If stdin is polled for anything else then reading, then it is also - // considered immediately ready, following the behavior on Linux. - immediate_events.push(event); - } else if let Some(_) = handle.as_any().downcast_ref::() { - // stderr are always considered ready to write because there seems to - // be no way of checking if a write to stdout would block. - // - // If stdin is polled for anything else then reading, then it is also - // considered immediately ready, following the behavior on Linux. - immediate_events.push(event); - } else if let Some(other) = handle.as_any().downcast_ref::() { - if other.get_file_type() == Filetype::SocketStream { - // We map pipe to SocketStream - pipe_events.push(event); - } else { - debug!( - "poll_oneoff: unsupported file type: {}", - other.get_file_type() - ); - handle_error_event(event, Errno::Notsup, events); - } - } else { - tracing::error!("can poll FdEvent for OS resources only"); - return Err(Error::Badf); - } - } - - let immediate = !immediate_events.is_empty(); - // Process all the events that do not require waiting. - if immediate { - trace!(" | have immediate events, will return immediately"); - for event in immediate_events { - handle_rw_event(event, events); - } - } - if !stdin_events.is_empty() { - // waiting for data to arrive on stdin. This thread will not terminate. - // - // We'd like to do the following: - // (1) wait in a non-blocking way for data to be available in stdin, with timeout - // (2) find out, how many bytes are there available to be read. - // - // One issue is that we are currently relying on the Rust libstd for interaction - // with stdin. More precisely, `io::stdin` is used via the `BufRead` trait, - // in the `fd_read` function, which always does buffering on the libstd side. [1] - // This means that even if there's still some unread data in stdin, - // the lower-level Windows system calls may return false negatives, - // claiming that stdin is empty. - // - // Theoretically, one could use `WaitForSingleObject` on the stdin handle - // to achieve (1). Unfortunately, this function doesn't seem to honor the - // requested timeout and to misbehaves after the stdin is closed. - // - // There appears to be no way of achieving (2) on Windows. - // [1]: https://github.com/rust-lang/rust/pull/12422 - let waitmode = if immediate { - trace!(" | tentatively checking stdin"); - WaitMode::Immediate - } else { - trace!(" | passively waiting on stdin"); - match timeout { - Some((_event, dur)) => WaitMode::Timeout(dur), - None => WaitMode::Infinite, - } - }; - let state = STDIN_POLL.lock().unwrap().poll(waitmode); - for event in stdin_events { - match state { - PollState::Ready => handle_rw_event(event, events), - PollState::NotReady => {} // not immediately available, so just ignore - PollState::TimedOut => handle_timeout_event(timeout.unwrap().0, events), - PollState::Error(e) => handle_error_event(event, e, events), - } - } - } - - if !immediate && !pipe_events.is_empty() { - trace!(" | actively polling pipes"); - match timeout { - Some((event, dur)) => { - // In the tests stdin is replaced with a dummy pipe, so for now - // we just time out. Support for pipes will be decided later on. - warn!("Polling pipes not supported on Windows, will just time out."); - handle_timeout(event, dur, events); - } - None => { - error!("Polling only pipes with no timeout not supported on Windows."); - return Err(Error::Notsup); - } - } - } - - Ok(()) -} diff --git a/crates/wasi-common/src/sys/windows/stdio.rs b/crates/wasi-common/src/sys/windows/stdio.rs deleted file mode 100644 index 25140db90a..0000000000 --- a/crates/wasi-common/src/sys/windows/stdio.rs +++ /dev/null @@ -1,59 +0,0 @@ -use super::{get_file_type, get_rights}; -use crate::handle::Handle; -use crate::sys::stdio::{Stderr, StderrExt, Stdin, StdinExt, Stdout, StdoutExt}; -use std::cell::Cell; -use std::fs::File; -use std::io; -use std::mem::ManuallyDrop; -use std::os::windows::prelude::{AsRawHandle, FromRawHandle, RawHandle}; - -impl AsRawHandle for Stdin { - fn as_raw_handle(&self) -> RawHandle { - io::stdin().as_raw_handle() - } -} - -impl AsRawHandle for Stdout { - fn as_raw_handle(&self) -> RawHandle { - io::stdout().as_raw_handle() - } -} - -impl AsRawHandle for Stderr { - fn as_raw_handle(&self) -> RawHandle { - io::stderr().as_raw_handle() - } -} - -impl StdinExt for Stdin { - fn stdin() -> io::Result> { - let file = unsafe { File::from_raw_handle(io::stdin().as_raw_handle()) }; - let file = ManuallyDrop::new(file); - let file_type = get_file_type(&file)?; - let rights = get_rights(&file_type)?; - let rights = Cell::new(rights); - Ok(Box::new(Self { file_type, rights })) - } -} - -impl StdoutExt for Stdout { - fn stdout() -> io::Result> { - let file = unsafe { File::from_raw_handle(io::stdin().as_raw_handle()) }; - let file = ManuallyDrop::new(file); - let file_type = get_file_type(&file)?; - let rights = get_rights(&file_type)?; - let rights = Cell::new(rights); - Ok(Box::new(Self { file_type, rights })) - } -} - -impl StderrExt for Stderr { - fn stderr() -> io::Result> { - let file = unsafe { File::from_raw_handle(io::stdin().as_raw_handle()) }; - let file = ManuallyDrop::new(file); - let file_type = get_file_type(&file)?; - let rights = get_rights(&file_type)?; - let rights = Cell::new(rights); - Ok(Box::new(Self { file_type, rights })) - } -} diff --git a/crates/wasi-common/src/virtfs.rs b/crates/wasi-common/src/virtfs.rs deleted file mode 100644 index a219cff719..0000000000 --- a/crates/wasi-common/src/virtfs.rs +++ /dev/null @@ -1,781 +0,0 @@ -use crate::handle::{ - Advice, Dircookie, Dirent, Fdflags, Filesize, Filestat, Filetype, Fstflags, Handle, - HandleRights, Oflags, Rights, RightsExt, Size, DIRCOOKIE_START, -}; -use crate::sched::Timestamp; -use crate::{Error, Result}; -use std::any::Any; -use std::cell::{Cell, RefCell}; -use std::collections::hash_map::Entry; -use std::collections::HashMap; -use std::convert::TryInto; -use std::io; -use std::io::SeekFrom; -use std::path::{Path, PathBuf}; -use std::rc::Rc; -use tracing::trace; - -pub mod pipe; - -/// An entry in a virtual filesystem -pub enum VirtualDirEntry { - /// The contents of a child directory - Directory(HashMap), - /// A file - File(Box), -} - -impl VirtualDirEntry { - /// Construct an empty directory - pub fn empty_directory() -> Self { - Self::Directory(HashMap::new()) - } -} - -/// Files and directories may be moved, and for implementation reasons retain a reference to their -/// parent Handle, so files that can be moved must provide an interface to update their parent -/// reference. -pub(crate) trait MovableFile { - fn set_parent(&self, new_parent: Option>); -} - -pub trait FileContents { - /// The implementation-defined maximum size of the store corresponding to a `FileContents` - /// implementation. - fn max_size(&self) -> Filesize; - /// The current number of bytes this `FileContents` describes. - fn size(&self) -> Filesize; - /// Resize to hold `new_size` number of bytes, or error if this is not possible. - fn resize(&mut self, new_size: Filesize) -> Result<()>; - /// Write a list of `IoSlice` starting at `offset`. `offset` plus the total size of all `iovs` - /// is guaranteed to not exceed `max_size`. Implementations must not indicate more bytes have - /// been written than can be held by `iovs`. - fn pwritev(&mut self, iovs: &[io::IoSlice], offset: Filesize) -> Result; - /// Read from the file from `offset`, filling a list of `IoSlice`. The returend size must not - /// be more than the capactiy of `iovs`, and must not exceed the limit reported by - /// `self.max_size()`. - fn preadv(&self, iovs: &mut [io::IoSliceMut], offset: Filesize) -> Result; - /// Write contents from `buf` to this file starting at `offset`. `offset` plus the length of - /// `buf` is guaranteed to not exceed `max_size`. Implementations must not indicate more bytes - /// have been written than the size of `buf`. - fn pwrite(&mut self, buf: &[u8], offset: Filesize) -> Result; - /// Read from the file at `offset`, filling `buf`. The returned size must not be more than the - /// capacity of `buf`, and `offset` plus the returned size must not exceed `self.max_size()`. - fn pread(&self, buf: &mut [u8], offset: Filesize) -> Result; -} - -impl FileContents for VecFileContents { - fn max_size(&self) -> Filesize { - std::usize::MAX as Filesize - } - - fn size(&self) -> Filesize { - self.content.len() as Filesize - } - - fn resize(&mut self, new_size: Filesize) -> Result<()> { - let new_size: usize = new_size.try_into().map_err(|_| Error::Inval)?; - self.content.resize(new_size, 0); - Ok(()) - } - - fn preadv(&self, iovs: &mut [io::IoSliceMut], offset: Filesize) -> Result { - let mut read_total = 0usize; - for iov in iovs.iter_mut() { - let skip: u64 = read_total.try_into().map_err(|_| Error::Inval)?; - let read = self.pread(iov, offset + skip)?; - read_total = read_total.checked_add(read).expect("FileContents::preadv must not be called when reads could total to more bytes than the return value can hold"); - } - Ok(read_total) - } - - fn pwritev(&mut self, iovs: &[io::IoSlice], offset: Filesize) -> Result { - let mut write_total = 0usize; - for iov in iovs.iter() { - let skip: u64 = write_total.try_into().map_err(|_| Error::Inval)?; - let written = self.pwrite(iov, offset + skip)?; - write_total = write_total.checked_add(written).expect("FileContents::pwritev must not be called when writes could total to more bytes than the return value can hold"); - } - Ok(write_total) - } - - fn pread(&self, buf: &mut [u8], offset: Filesize) -> Result { - trace!(buffer_length = buf.len(), offset = offset, "pread"); - let offset: usize = offset.try_into().map_err(|_| Error::Inval)?; - - let data_remaining = self.content.len().saturating_sub(offset); - - let read_count = std::cmp::min(buf.len(), data_remaining); - - (&mut buf[..read_count]).copy_from_slice(&self.content[offset..][..read_count]); - - Ok(read_count) - } - - fn pwrite(&mut self, buf: &[u8], offset: Filesize) -> Result { - let offset: usize = offset.try_into().map_err(|_| Error::Inval)?; - - let write_end = offset.checked_add(buf.len()).ok_or(Error::Fbig)?; - - if write_end > self.content.len() { - self.content.resize(write_end, 0); - } - - (&mut self.content[offset..][..buf.len()]).copy_from_slice(buf); - - Ok(buf.len()) - } -} - -pub struct VecFileContents { - content: Vec, -} - -impl VecFileContents { - pub fn new() -> Self { - Self { - content: Vec::new(), - } - } - - pub fn with_content(content: Vec) -> Self { - Self { content } - } -} - -/// An `InMemoryFile` is a shared handle to some underlying data. The relationship is analagous to -/// a filesystem wherein a file descriptor is one view into a possibly-shared underlying collection -/// of data and permissions on a filesystem. -pub struct InMemoryFile { - rights: Cell, - cursor: Cell, - parent: Rc>>>, - fd_flags: Cell, - data: Rc>>, -} - -impl InMemoryFile { - pub fn memory_backed() -> Self { - Self::new(Box::new(VecFileContents::new())) - } - - pub fn new(contents: Box) -> Self { - let rights = HandleRights::new( - Rights::regular_file_base(), - Rights::regular_file_inheriting(), - ); - let rights = Cell::new(rights); - Self { - rights, - cursor: Cell::new(0), - fd_flags: Cell::new(Fdflags::empty()), - parent: Rc::new(RefCell::new(None)), - data: Rc::new(RefCell::new(contents)), - } - } -} - -impl MovableFile for InMemoryFile { - fn set_parent(&self, new_parent: Option>) { - *self.parent.borrow_mut() = new_parent; - } -} - -impl Handle for InMemoryFile { - fn as_any(&self) -> &dyn Any { - self - } - fn try_clone(&self) -> io::Result> { - Ok(Box::new(Self { - rights: self.rights.clone(), - cursor: Cell::new(0), - fd_flags: self.fd_flags.clone(), - parent: Rc::clone(&self.parent), - data: Rc::clone(&self.data), - })) - } - fn get_file_type(&self) -> Filetype { - Filetype::RegularFile - } - fn get_rights(&self) -> HandleRights { - self.rights.get() - } - fn set_rights(&self, rights: HandleRights) { - self.rights.set(rights) - } - // FdOps - fn advise(&self, _advice: Advice, _offset: Filesize, _len: Filesize) -> Result<()> { - // we'll just ignore advice for now, unless it's totally invalid - Ok(()) - } - fn allocate(&self, offset: Filesize, len: Filesize) -> Result<()> { - let new_limit = offset.checked_add(len).ok_or(Error::Fbig)?; - let mut data = self.data.borrow_mut(); - - if new_limit > data.max_size() { - return Err(Error::Fbig); - } - - if new_limit > data.size() { - data.resize(new_limit)?; - } - - Ok(()) - } - fn fdstat_get(&self) -> Result { - Ok(self.fd_flags.get()) - } - fn fdstat_set_flags(&self, fdflags: Fdflags) -> Result<()> { - self.fd_flags.set(fdflags); - Ok(()) - } - fn filestat_get(&self) -> Result { - let stat = Filestat { - dev: 0, - ino: 0, - nlink: 0, - size: self.data.borrow().size(), - atim: 0, - ctim: 0, - mtim: 0, - filetype: self.get_file_type(), - }; - Ok(stat) - } - fn filestat_set_size(&self, st_size: Filesize) -> Result<()> { - let mut data = self.data.borrow_mut(); - if st_size > data.max_size() { - return Err(Error::Fbig); - } - data.resize(st_size) - } - fn preadv(&self, buf: &mut [io::IoSliceMut], offset: Filesize) -> Result { - self.data.borrow_mut().preadv(buf, offset) - } - fn pwritev(&self, buf: &[io::IoSlice], offset: Filesize) -> Result { - self.data.borrow_mut().pwritev(buf, offset) - } - fn read_vectored(&self, iovs: &mut [io::IoSliceMut]) -> Result { - trace!("read_vectored(iovs={:?})", iovs); - trace!(" | *read_start={:?}", self.cursor.get()); - let read = self.data.borrow_mut().preadv(iovs, self.cursor.get())?; - let offset: u64 = read.try_into().map_err(|_| Error::Inval)?; - let update = self.cursor.get().checked_add(offset).ok_or(Error::Inval)?; - self.cursor.set(update); - Ok(read) - } - fn seek(&self, offset: SeekFrom) -> Result { - let content_len = self.data.borrow().size(); - match offset { - SeekFrom::Current(offset) => { - let new_cursor = if offset < 0 { - self.cursor - .get() - .checked_sub(offset.wrapping_neg() as u64) - .ok_or(Error::Inval)? - } else { - self.cursor - .get() - .checked_add(offset as u64) - .ok_or(Error::Inval)? - }; - self.cursor.set(std::cmp::min(content_len, new_cursor)); - } - SeekFrom::End(offset) => { - // A negative offset from the end would be past the end of the file, - let offset: u64 = offset.try_into().map_err(|_| Error::Inval)?; - self.cursor.set(content_len.saturating_sub(offset)); - } - SeekFrom::Start(offset) => { - // A negative offset from the end would be before the start of the file. - let offset: u64 = offset.try_into().map_err(|_| Error::Inval)?; - self.cursor.set(std::cmp::min(content_len, offset)); - } - } - - Ok(self.cursor.get()) - } - fn write_vectored(&self, iovs: &[io::IoSlice]) -> Result { - trace!("write_vectored(iovs={:?})", iovs); - let mut data = self.data.borrow_mut(); - - let append_mode = self.fd_flags.get().contains(Fdflags::APPEND); - trace!(" | fd_flags={}", self.fd_flags.get()); - - // If this file is in append mode, we write to the end. - let write_start = if append_mode { - data.size() - } else { - self.cursor.get() - }; - - let max_size = iovs - .iter() - .map(|iov| { - let cast_iovlen: Size = iov - .len() - .try_into() - .expect("iovec are bounded by wasi max sizes"); - cast_iovlen - }) - .fold(Some(0u32), |len, iov| len.and_then(|x| x.checked_add(iov))) - .expect("write_vectored will not be called with invalid iovs"); - - if let Some(end) = write_start.checked_add(max_size as Filesize) { - if end > data.max_size() { - return Err(Error::Fbig); - } - } else { - return Err(Error::Fbig); - } - - trace!(" | *write_start={:?}", write_start); - let written = data.pwritev(iovs, write_start)?; - - // If we are not appending, adjust the cursor appropriately for the write, too. This can't - // overflow, as we checked against that before writing any data. - if !append_mode { - let update = self.cursor.get() + written as u64; - self.cursor.set(update); - } - - Ok(written) - } - // PathOps - fn create_directory(&self, _path: &str) -> Result<()> { - Err(Error::Notdir) - } - fn openat( - &self, - path: &str, - _read: bool, - _write: bool, - oflags: Oflags, - _fd_flags: Fdflags, - ) -> Result> { - if oflags.contains(Oflags::DIRECTORY) { - tracing::trace!( - "InMemoryFile::openat was passed oflags DIRECTORY, but {:?} is a file.", - path - ); - return Err(Error::Notdir); - } - - if path == "." { - return self.try_clone().map_err(Into::into); - } else if path == ".." { - match &*self.parent.borrow() { - Some(file) => file.try_clone().map_err(Into::into), - None => self.try_clone().map_err(Into::into), - } - } else { - Err(Error::Acces) - } - } - fn link( - &self, - _old_path: &str, - _new_handle: Box, - _new_path: &str, - _follow: bool, - ) -> Result<()> { - Err(Error::Notdir) - } - fn readlink(&self, _path: &str, _buf: &mut [u8]) -> Result { - Err(Error::Notdir) - } - fn readlinkat(&self, _path: &str) -> Result { - Err(Error::Notdir) - } - fn rename(&self, _old_path: &str, _new_handle: Box, _new_path: &str) -> Result<()> { - Err(Error::Notdir) - } - fn remove_directory(&self, _path: &str) -> Result<()> { - Err(Error::Notdir) - } - fn symlink(&self, _old_path: &str, _new_path: &str) -> Result<()> { - Err(Error::Notdir) - } - fn unlink_file(&self, _path: &str) -> Result<()> { - Err(Error::Notdir) - } -} - -/// A clonable read/write directory. -pub struct VirtualDir { - rights: Cell, - writable: bool, - // All copies of this `VirtualDir` must share `parent`, and changes in one copy's `parent` - // must be reflected in all handles, so they share `Rc` of an underlying `parent`. - parent: Rc>>>, - entries: Rc>>>, -} - -impl VirtualDir { - pub fn new(writable: bool) -> Self { - let rights = HandleRights::new(Rights::directory_base(), Rights::directory_inheriting()); - let rights = Cell::new(rights); - Self { - rights, - writable, - parent: Rc::new(RefCell::new(None)), - entries: Rc::new(RefCell::new(HashMap::new())), - } - } - - #[allow(dead_code)] - pub fn with_dir>(mut self, dir: Self, path: P) -> Self { - self.add_dir(dir, path); - self - } - - #[allow(dead_code)] - pub fn add_dir>(&mut self, dir: Self, path: P) { - let entry = Box::new(dir); - entry.set_parent(Some(self.try_clone().expect("can clone self"))); - self.entries - .borrow_mut() - .insert(path.as_ref().to_owned(), entry); - } - - #[allow(dead_code)] - pub fn with_file>(mut self, content: Box, path: P) -> Self { - self.add_file(content, path); - self - } - - #[allow(dead_code)] - pub fn add_file>(&mut self, content: Box, path: P) { - let entry = Box::new(InMemoryFile::new(content)); - entry.set_parent(Some(self.try_clone().expect("can clone self"))); - self.entries - .borrow_mut() - .insert(path.as_ref().to_owned(), entry); - } -} - -impl MovableFile for VirtualDir { - fn set_parent(&self, new_parent: Option>) { - *self.parent.borrow_mut() = new_parent; - } -} - -const SELF_DIR_COOKIE: u32 = 0; -const PARENT_DIR_COOKIE: u32 = 1; - -// This MUST be the number of constants above. This limit is used to prevent allocation of files -// that would wrap and be mapped to the same dir cookies as `self` or `parent`. -const RESERVED_ENTRY_COUNT: u32 = 2; - -impl Handle for VirtualDir { - fn as_any(&self) -> &dyn Any { - self - } - fn try_clone(&self) -> io::Result> { - Ok(Box::new(Self { - rights: self.rights.clone(), - writable: self.writable, - parent: Rc::clone(&self.parent), - entries: Rc::clone(&self.entries), - })) - } - fn get_file_type(&self) -> Filetype { - Filetype::Directory - } - fn get_rights(&self) -> HandleRights { - self.rights.get() - } - fn set_rights(&self, rights: HandleRights) { - self.rights.set(rights) - } - // FdOps - fn filestat_get(&self) -> Result { - let stat = Filestat { - dev: 0, - ino: 0, - nlink: 0, - size: 0, - atim: 0, - ctim: 0, - mtim: 0, - filetype: self.get_file_type(), - }; - Ok(stat) - } - fn readdir( - &self, - cookie: Dircookie, - ) -> Result>>> { - struct VirtualDirIter { - start: u32, - entries: Rc>>>, - } - impl Iterator for VirtualDirIter { - type Item = Result<(Dirent, String)>; - - fn next(&mut self) -> Option { - tracing::trace!("VirtualDirIter::next continuing from {}", self.start); - if self.start == SELF_DIR_COOKIE { - self.start += 1; - let name = ".".to_owned(); - let dirent = Dirent { - d_next: self.start as u64, - d_ino: 0, - d_namlen: name.len() as _, - d_type: Filetype::Directory, - }; - return Some(Ok((dirent, name))); - } - if self.start == PARENT_DIR_COOKIE { - self.start += 1; - let name = "..".to_owned(); - let dirent = Dirent { - d_next: self.start as u64, - d_ino: 0, - d_namlen: name.len() as _, - d_type: Filetype::Directory, - }; - return Some(Ok((dirent, name))); - } - - let entries = self.entries.borrow(); - - // Adjust `start` to be an appropriate number of HashMap entries. - let start = self.start - RESERVED_ENTRY_COUNT; - if start as usize >= entries.len() { - return None; - } - - self.start += 1; - - let (path, file) = entries - .iter() - .skip(start as usize) - .next() - .expect("seeked less than the length of entries"); - - let name = path - .to_str() - .expect("wasi paths are valid utf8 strings") - .to_owned(); - let dirent = || -> Result { - let dirent = Dirent { - d_namlen: name.len().try_into()?, - d_type: file.get_file_type(), - d_ino: 0, - d_next: self.start as u64, - }; - Ok(dirent) - }; - Some(dirent().map(|dirent| (dirent, name))) - } - } - let cookie = match cookie.try_into() { - Ok(cookie) => cookie, - Err(_) => { - // Cookie is larger than u32. it doesn't seem like there's an explicit error - // condition in POSIX or WASI, so just start from the start? - 0 - } - }; - Ok(Box::new(VirtualDirIter { - start: cookie, - entries: Rc::clone(&self.entries), - })) - } - // PathOps - fn create_directory(&self, path: &str) -> Result<()> { - let mut entries = self.entries.borrow_mut(); - match entries.entry(PathBuf::from(path)) { - Entry::Occupied(_) => Err(Error::Exist), - Entry::Vacant(v) => { - if self.writable { - let new_dir = Box::new(Self::new(true)); - new_dir.set_parent(Some(self.try_clone()?)); - v.insert(new_dir); - Ok(()) - } else { - Err(Error::Acces) - } - } - } - } - fn filestat_get_at(&self, path: &str, _follow: bool) -> Result { - let stat = self - .openat(path, false, false, Oflags::empty(), Fdflags::empty())? - .filestat_get()?; - Ok(stat) - } - fn filestat_set_times_at( - &self, - path: &str, - atim: Timestamp, - mtim: Timestamp, - fst_flags: Fstflags, - _follow: bool, - ) -> Result<()> { - self.openat(path, false, false, Oflags::empty(), Fdflags::empty())? - .filestat_set_times(atim, mtim, fst_flags)?; - Ok(()) - } - fn openat( - &self, - path: &str, - _read: bool, - _write: bool, - oflags: Oflags, - fd_flags: Fdflags, - ) -> Result> { - if path == "." { - return self.try_clone().map_err(Into::into); - } else if path == ".." { - match &*self.parent.borrow() { - Some(file) => { - return file.try_clone().map_err(Into::into); - } - None => { - return self.try_clone().map_err(Into::into); - } - } - } - - // openat may have been passed a path with a trailing slash, but files are mapped to paths - // with trailing slashes normalized out. - let file_name = Path::new(path).file_name().ok_or(Error::Inval)?; - let mut entries = self.entries.borrow_mut(); - let entry_count = entries.len(); - match entries.entry(Path::new(file_name).to_path_buf()) { - Entry::Occupied(e) => { - let creat_excl_mask = Oflags::CREAT | Oflags::EXCL; - if (oflags & creat_excl_mask) == creat_excl_mask { - tracing::trace!("VirtualDir::openat was passed oflags CREAT|EXCL, but the file {:?} exists.", file_name); - return Err(Error::Exist); - } - - if oflags.contains(Oflags::DIRECTORY) - && e.get().get_file_type() != Filetype::Directory - { - tracing::trace!( - "VirtualDir::openat was passed oflags DIRECTORY, but {:?} is a file.", - file_name - ); - return Err(Error::Notdir); - } - - e.get().try_clone().map_err(Into::into) - } - Entry::Vacant(v) => { - if oflags.contains(Oflags::CREAT) { - if self.writable { - // Enforce a hard limit at `u32::MAX - 2` files. - // This is to have a constant limit (rather than target-dependent limit we - // would have with `usize`. The limit is the full `u32` range minus two so we - // can reserve "self" and "parent" cookie values. - if entry_count >= (std::u32::MAX - RESERVED_ENTRY_COUNT) as usize { - return Err(Error::Nospc); - } - - tracing::trace!( - "VirtualDir::openat creating an InMemoryFile named {}", - path - ); - - let file = Box::new(InMemoryFile::memory_backed()); - file.fd_flags.set(fd_flags); - file.set_parent(Some(self.try_clone().expect("can clone self"))); - v.insert(file).try_clone().map_err(Into::into) - } else { - Err(Error::Acces) - } - } else { - Err(Error::Noent) - } - } - } - } - fn readlinkat(&self, _path: &str) -> Result { - // Files are not symbolic links or directories, faithfully report Notdir. - Err(Error::Notdir) - } - fn remove_directory(&self, path: &str) -> Result<()> { - let trimmed_path = path.trim_end_matches('/'); - let mut entries = self.entries.borrow_mut(); - match entries.entry(Path::new(trimmed_path).to_path_buf()) { - Entry::Occupied(e) => { - // first, does this name a directory? - if e.get().get_file_type() != Filetype::Directory { - return Err(Error::Notdir); - } - - // Okay, but is the directory empty? - let iter = e.get().readdir(DIRCOOKIE_START)?; - if iter.skip(RESERVED_ENTRY_COUNT as usize).next().is_some() { - return Err(Error::Notempty); - } - - // Alright, it's an empty directory. We can remove it. - let removed = e.remove_entry(); - - // TODO refactor - // And sever the file's parent ref to avoid Rc cycles. - if let Some(dir) = removed.1.as_any().downcast_ref::() { - dir.set_parent(None); - } else if let Some(file) = removed.1.as_any().downcast_ref::() { - file.set_parent(None); - } else { - panic!("neither VirtualDir nor InMemoryFile"); - } - - Ok(()) - } - Entry::Vacant(_) => { - tracing::trace!( - "VirtualDir::remove_directory failed to remove {}, no such entry", - trimmed_path - ); - Err(Error::Noent) - } - } - } - fn unlink_file(&self, path: &str) -> Result<()> { - let trimmed_path = path.trim_end_matches('/'); - - // Special case: we may be unlinking this directory itself if path is `"."`. In that case, - // fail with Isdir, since this is a directory. Alternatively, we may be unlinking `".."`, - // which is bound the same way, as this is by definition contained in a directory. - if trimmed_path == "." || trimmed_path == ".." { - return Err(Error::Isdir); - } - - let mut entries = self.entries.borrow_mut(); - match entries.entry(Path::new(trimmed_path).to_path_buf()) { - Entry::Occupied(e) => { - // Directories must be removed through `remove_directory`, not `unlink_file`. - if e.get().get_file_type() == Filetype::Directory { - return Err(Error::Isdir); - } - - let removed = e.remove_entry(); - - // TODO refactor - // Sever the file's parent ref to avoid Rc cycles. - if let Some(dir) = removed.1.as_any().downcast_ref::() { - dir.set_parent(None); - } else if let Some(file) = removed.1.as_any().downcast_ref::() { - file.set_parent(None); - } else { - panic!("neither VirtualDir nor InMemoryFile"); - } - - Ok(()) - } - Entry::Vacant(_) => { - tracing::trace!( - "VirtualDir::unlink_file failed to remove {}, no such entry", - trimmed_path - ); - Err(Error::Noent) - } - } - } -} diff --git a/crates/wasi-common/src/virtfs/pipe.rs b/crates/wasi-common/src/virtfs/pipe.rs deleted file mode 100644 index f23e7556b4..0000000000 --- a/crates/wasi-common/src/virtfs/pipe.rs +++ /dev/null @@ -1,403 +0,0 @@ -//! Virtual pipes. -//! -//! These types provide easy implementations of `Handle` that mimic much of the behavior of Unix -//! pipes. These are particularly helpful for redirecting WASI stdio handles to destinations other -//! than OS files. -//! -//! Some convenience constructors are included for common backing types like `Vec` and `String`, -//! but the virtual pipes can be instantiated with any `Read` or `Write` type. -//! -//! Note that `poll_oneoff` is not supported for these types, so they do not match the behavior of -//! real pipes exactly. -use crate::handle::{ - Advice, Fdflags, Filesize, Filestat, Filetype, Handle, HandleRights, Oflags, Rights, -}; -use crate::{Error, Result}; -use std::any::Any; -use std::io::{self, Read, Write}; -use std::sync::{Arc, RwLock}; - -/// A virtual pipe read end. -/// -/// A variety of `From` impls are provided so that common pipe types are easy to create. For example: -/// -/// ``` -/// # use wasi_common::WasiCtxBuilder; -/// # use wasi_common::virtfs::pipe::ReadPipe; -/// let mut ctx = WasiCtxBuilder::new(); -/// let stdin = ReadPipe::from("hello from stdin!"); -/// ctx.stdin(stdin); -/// ``` -#[derive(Debug)] -pub struct ReadPipe { - rights: RwLock, - reader: Arc>, -} - -impl Clone for ReadPipe { - fn clone(&self) -> Self { - Self { - rights: RwLock::new(*self.rights.read().unwrap()), - reader: self.reader.clone(), - } - } -} - -impl ReadPipe { - /// Create a new pipe from a `Read` type. - /// - /// All `Handle` read operations delegate to reading from this underlying reader. - pub fn new(r: R) -> Self { - Self::from_shared(Arc::new(RwLock::new(r))) - } - - /// Create a new pipe from a shareable `Read` type. - /// - /// All `Handle` read operations delegate to reading from this underlying reader. - pub fn from_shared(reader: Arc>) -> Self { - Self { - rights: RwLock::new(HandleRights::from_base( - Rights::FD_DATASYNC - | Rights::FD_FDSTAT_SET_FLAGS - | Rights::FD_READ - | Rights::FD_SYNC - | Rights::FD_FILESTAT_GET - | Rights::POLL_FD_READWRITE, - )), - reader, - } - } - - /// Try to convert this `ReadPipe` back to the underlying `R` type. - /// - /// This will fail with `Err(self)` if multiple references to the underlying `R` exist. - pub fn try_into_inner(mut self) -> std::result::Result { - match Arc::try_unwrap(self.reader) { - Ok(rc) => Ok(RwLock::into_inner(rc).unwrap()), - Err(reader) => { - self.reader = reader; - Err(self) - } - } - } -} - -impl From> for ReadPipe>> { - fn from(r: Vec) -> Self { - Self::new(io::Cursor::new(r)) - } -} - -impl From<&[u8]> for ReadPipe>> { - fn from(r: &[u8]) -> Self { - Self::from(r.to_vec()) - } -} - -impl From for ReadPipe> { - fn from(r: String) -> Self { - Self::new(io::Cursor::new(r)) - } -} - -impl From<&str> for ReadPipe> { - fn from(r: &str) -> Self { - Self::from(r.to_string()) - } -} - -impl Handle for ReadPipe { - fn as_any(&self) -> &dyn Any { - self - } - - fn try_clone(&self) -> io::Result> { - Ok(Box::new(self.clone())) - } - - fn get_file_type(&self) -> Filetype { - Filetype::Unknown - } - - fn get_rights(&self) -> HandleRights { - *self.rights.read().unwrap() - } - - fn set_rights(&self, rights: HandleRights) { - *self.rights.write().unwrap() = rights; - } - - fn advise(&self, _advice: Advice, _offset: Filesize, _len: Filesize) -> Result<()> { - Err(Error::Spipe) - } - - fn allocate(&self, _offset: Filesize, _len: Filesize) -> Result<()> { - Err(Error::Spipe) - } - - fn fdstat_set_flags(&self, _fdflags: Fdflags) -> Result<()> { - // do nothing for now - Ok(()) - } - - fn filestat_get(&self) -> Result { - let stat = Filestat { - dev: 0, - ino: 0, - nlink: 0, - size: 0, - atim: 0, - ctim: 0, - mtim: 0, - filetype: self.get_file_type(), - }; - Ok(stat) - } - - fn filestat_set_size(&self, _st_size: Filesize) -> Result<()> { - Err(Error::Spipe) - } - - fn preadv(&self, buf: &mut [io::IoSliceMut], offset: Filesize) -> Result { - if offset != 0 { - return Err(Error::Spipe); - } - Ok(self.reader.write().unwrap().read_vectored(buf)?) - } - - fn seek(&self, _offset: io::SeekFrom) -> Result { - Err(Error::Spipe) - } - - fn read_vectored(&self, iovs: &mut [io::IoSliceMut]) -> Result { - Ok(self.reader.write().unwrap().read_vectored(iovs)?) - } - - fn create_directory(&self, _path: &str) -> Result<()> { - Err(Error::Notdir) - } - - fn openat( - &self, - _path: &str, - _read: bool, - _write: bool, - _oflags: Oflags, - _fd_flags: Fdflags, - ) -> Result> { - Err(Error::Notdir) - } - - fn link( - &self, - _old_path: &str, - _new_handle: Box, - _new_path: &str, - _follow: bool, - ) -> Result<()> { - Err(Error::Notdir) - } - - fn readlink(&self, _path: &str, _buf: &mut [u8]) -> Result { - Err(Error::Notdir) - } - - fn readlinkat(&self, _path: &str) -> Result { - Err(Error::Notdir) - } - - fn rename(&self, _old_path: &str, _new_handle: Box, _new_path: &str) -> Result<()> { - Err(Error::Notdir) - } - - fn remove_directory(&self, _path: &str) -> Result<()> { - Err(Error::Notdir) - } - - fn symlink(&self, _old_path: &str, _new_path: &str) -> Result<()> { - Err(Error::Notdir) - } - - fn unlink_file(&self, _path: &str) -> Result<()> { - Err(Error::Notdir) - } -} - -/// A virtual pipe write end. -#[derive(Debug)] -pub struct WritePipe { - rights: RwLock, - writer: Arc>, -} - -impl Clone for WritePipe { - fn clone(&self) -> Self { - Self { - rights: RwLock::new(*self.rights.read().unwrap()), - writer: self.writer.clone(), - } - } -} - -impl WritePipe { - /// Create a new pipe from a `Write` type. - /// - /// All `Handle` write operations delegate to writing to this underlying writer. - pub fn new(w: W) -> Self { - Self::from_shared(Arc::new(RwLock::new(w))) - } - - /// Create a new pipe from a shareable `Write` type. - /// - /// All `Handle` write operations delegate to writing to this underlying writer. - pub fn from_shared(writer: Arc>) -> Self { - Self { - rights: RwLock::new(HandleRights::from_base( - Rights::FD_DATASYNC - | Rights::FD_FDSTAT_SET_FLAGS - | Rights::FD_SYNC - | Rights::FD_WRITE - | Rights::FD_FILESTAT_GET - | Rights::POLL_FD_READWRITE, - )), - writer, - } - } - - /// Try to convert this `WritePipe` back to the underlying `W` type. - /// - /// This will fail with `Err(self)` if multiple references to the underlying `W` exist. - pub fn try_into_inner(mut self) -> std::result::Result { - match Arc::try_unwrap(self.writer) { - Ok(rc) => Ok(RwLock::into_inner(rc).unwrap()), - Err(writer) => { - self.writer = writer; - Err(self) - } - } - } -} - -impl WritePipe>> { - /// Create a new writable virtual pipe backed by a `Vec` buffer. - pub fn new_in_memory() -> Self { - Self::new(io::Cursor::new(vec![])) - } -} - -impl Handle for WritePipe { - fn as_any(&self) -> &dyn Any { - self - } - - fn try_clone(&self) -> io::Result> { - Ok(Box::new(self.clone())) - } - - fn get_file_type(&self) -> Filetype { - Filetype::Unknown - } - - fn get_rights(&self) -> HandleRights { - *self.rights.read().unwrap() - } - - fn set_rights(&self, rights: HandleRights) { - *self.rights.write().unwrap() = rights; - } - - fn advise(&self, _advice: Advice, _offset: Filesize, _len: Filesize) -> Result<()> { - Err(Error::Spipe) - } - - fn allocate(&self, _offset: Filesize, _len: Filesize) -> Result<()> { - Err(Error::Spipe) - } - - fn fdstat_set_flags(&self, _fdflags: Fdflags) -> Result<()> { - // do nothing for now - Ok(()) - } - - fn filestat_get(&self) -> Result { - let stat = Filestat { - dev: 0, - ino: 0, - nlink: 0, - size: 0, - atim: 0, - ctim: 0, - mtim: 0, - filetype: self.get_file_type(), - }; - Ok(stat) - } - - fn filestat_set_size(&self, _st_size: Filesize) -> Result<()> { - Err(Error::Spipe) - } - - fn pwritev(&self, buf: &[io::IoSlice], offset: Filesize) -> Result { - if offset != 0 { - return Err(Error::Spipe); - } - Ok(self.writer.write().unwrap().write_vectored(buf)?) - } - - fn seek(&self, _offset: io::SeekFrom) -> Result { - Err(Error::Spipe) - } - - fn write_vectored(&self, iovs: &[io::IoSlice]) -> Result { - Ok(self.writer.write().unwrap().write_vectored(iovs)?) - } - - fn create_directory(&self, _path: &str) -> Result<()> { - Err(Error::Notdir) - } - - fn openat( - &self, - _path: &str, - _read: bool, - _write: bool, - _oflags: Oflags, - _fd_flags: Fdflags, - ) -> Result> { - Err(Error::Notdir) - } - - fn link( - &self, - _old_path: &str, - _new_handle: Box, - _new_path: &str, - _follow: bool, - ) -> Result<()> { - Err(Error::Notdir) - } - - fn readlink(&self, _path: &str, _buf: &mut [u8]) -> Result { - Err(Error::Notdir) - } - - fn readlinkat(&self, _path: &str) -> Result { - Err(Error::Notdir) - } - - fn rename(&self, _old_path: &str, _new_handle: Box, _new_path: &str) -> Result<()> { - Err(Error::Notdir) - } - - fn remove_directory(&self, _path: &str) -> Result<()> { - Err(Error::Notdir) - } - - fn symlink(&self, _old_path: &str, _new_path: &str) -> Result<()> { - Err(Error::Notdir) - } - - fn unlink_file(&self, _path: &str) -> Result<()> { - Err(Error::Notdir) - } -} diff --git a/crates/wasi-common/src/wasi.rs b/crates/wasi-common/src/wasi.rs deleted file mode 100644 index 978d45d113..0000000000 --- a/crates/wasi-common/src/wasi.rs +++ /dev/null @@ -1,103 +0,0 @@ -use crate::{Error, WasiCtx}; -use std::convert::{TryFrom, TryInto}; -use tracing::debug; - -wiggle::from_witx!({ - witx: ["$WASI_ROOT/phases/snapshot/witx/wasi_snapshot_preview1.witx"], - ctx: WasiCtx, - errors: { errno => Error }, -}); - -use types::Errno; - -impl wiggle::GuestErrorType for Errno { - fn success() -> Self { - Self::Success - } -} - -impl types::GuestErrorConversion for WasiCtx { - fn into_errno(&self, e: wiggle::GuestError) -> Errno { - debug!("Guest error: {:?}", e); - e.into() - } -} - -impl types::UserErrorConversion for WasiCtx { - fn errno_from_error(&self, e: Error) -> Result { - debug!("Error: {:?}", e); - e.try_into() - } -} - -impl TryFrom for Errno { - type Error = wiggle::Trap; - fn try_from(e: Error) -> Result { - match e { - Error::Guest(e) => Ok(e.into()), - Error::TryFromInt(_) => Ok(Errno::Overflow), - Error::Utf8(_) => Ok(Errno::Ilseq), - Error::UnexpectedIo(_) => Ok(Errno::Io), - Error::GetRandom(_) => Ok(Errno::Io), - Error::TooBig => Ok(Errno::TooBig), - Error::Acces => Ok(Errno::Acces), - Error::Badf => Ok(Errno::Badf), - Error::Busy => Ok(Errno::Busy), - Error::Exist => Ok(Errno::Exist), - Error::Fault => Ok(Errno::Fault), - Error::Fbig => Ok(Errno::Fbig), - Error::Ilseq => Ok(Errno::Ilseq), - Error::Inval => Ok(Errno::Inval), - Error::Io => Ok(Errno::Io), - Error::Isdir => Ok(Errno::Isdir), - Error::Loop => Ok(Errno::Loop), - Error::Mfile => Ok(Errno::Mfile), - Error::Mlink => Ok(Errno::Mlink), - Error::Nametoolong => Ok(Errno::Nametoolong), - Error::Nfile => Ok(Errno::Nfile), - Error::Noent => Ok(Errno::Noent), - Error::Nomem => Ok(Errno::Nomem), - Error::Nospc => Ok(Errno::Nospc), - Error::Notdir => Ok(Errno::Notdir), - Error::Notempty => Ok(Errno::Notempty), - Error::Notsup => Ok(Errno::Notsup), - Error::Overflow => Ok(Errno::Overflow), - Error::Pipe => Ok(Errno::Pipe), - Error::Perm => Ok(Errno::Perm), - Error::Spipe => Ok(Errno::Spipe), - Error::Notcapable => Ok(Errno::Notcapable), - Error::Unsupported(feature) => { - Err(wiggle::Trap::String(format!("unsupported: {}", feature))) - } - } - } -} - -impl From for Errno { - fn from(err: wiggle::GuestError) -> Self { - use wiggle::GuestError::*; - match err { - InvalidFlagValue { .. } => Self::Inval, - InvalidEnumValue { .. } => Self::Inval, - PtrOverflow { .. } => Self::Fault, - PtrOutOfBounds { .. } => Self::Fault, - PtrNotAligned { .. } => Self::Inval, - PtrBorrowed { .. } => Self::Fault, - InvalidUtf8 { .. } => Self::Ilseq, - TryFromIntError { .. } => Self::Overflow, - InFunc { err, .. } => Errno::from(*err), - InDataField { err, .. } => Errno::from(*err), - SliceLengthsDiffer { .. } => Self::Fault, - BorrowCheckerOutOfHandles { .. } => Self::Fault, - } - } -} - -impl crate::fdpool::Fd for types::Fd { - fn as_raw(&self) -> u32 { - (*self).into() - } - fn from_raw(raw_fd: u32) -> Self { - Self::from(raw_fd) - } -} diff --git a/crates/wasi-common/winx/Cargo.toml b/crates/wasi-common/winx/Cargo.toml deleted file mode 100644 index 0d170b73d6..0000000000 --- a/crates/wasi-common/winx/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -name = "winx" -version = "0.22.0" -authors = ["Jakub Konka "] -description = "Windows API helper library" -documentation = "https://docs.rs/winx" -license = "Apache-2.0 WITH LLVM-exception" -repository = "https://github.com/bytecodealliance/wasmtime" -edition = "2018" - -[dependencies] -bitflags = "1.0" -cvt = "0.1" -winapi = { version = "^0.3", features = [ - "std", - "errhandlingapi", - "handleapi", - "processthreadsapi", - "profileapi", - "securitybaseapi", - "winbase", - "winerror", - "ws2def", - "fileapi", - "aclapi", -] } - -[badges] -maintenance = { status = "actively-developed" } diff --git a/crates/wasi-common/winx/LICENSE b/crates/wasi-common/winx/LICENSE deleted file mode 100644 index f9d81955f4..0000000000 --- a/crates/wasi-common/winx/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/winx/src/file.rs b/crates/wasi-common/winx/src/file.rs deleted file mode 100644 index d3639d9f97..0000000000 --- a/crates/wasi-common/winx/src/file.rs +++ /dev/null @@ -1,455 +0,0 @@ -#![allow(non_camel_case_types)] - -use crate::ntdll::{ - NtQueryInformationFile, RtlNtStatusToDosError, FILE_ACCESS_INFORMATION, FILE_INFORMATION_CLASS, - FILE_MODE_INFORMATION, IO_STATUS_BLOCK, -}; -use bitflags::bitflags; -use cvt::cvt; -use std::ffi::{c_void, OsString}; -use std::fs::File; -use std::io::{Error, Result}; -use std::os::windows::prelude::{AsRawHandle, OsStringExt, RawHandle}; -use winapi::shared::{ - minwindef::{self, DWORD}, - ntstatus, winerror, -}; -use winapi::um::{fileapi, fileapi::GetFileType, minwinbase, winbase, winnt}; - -/// Maximum total path length for Unicode in Windows. -/// [Maximum path length limitation]: https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation -pub const WIDE_MAX_PATH: DWORD = 0x7fff; - -#[derive(Debug, Copy, Clone)] -pub struct FileType(minwindef::DWORD); - -// possible types are: -// * FILE_TYPE_CHAR -// * FILE_TYPE_DISK -// * FILE_TYPE_PIPE -// * FILE_TYPE_REMOTE -// * FILE_TYPE_UNKNOWN -// -// FILE_TYPE_REMOTE is unused -// https://technet.microsoft.com/en-us/evalcenter/aa364960(v=vs.100) -impl FileType { - /// Returns true if character device such as LPT device or console - pub fn is_char(&self) -> bool { - self.0 == winbase::FILE_TYPE_CHAR - } - - /// Returns true if disk device such as file or dir - pub fn is_disk(&self) -> bool { - self.0 == winbase::FILE_TYPE_DISK - } - - /// Returns true if pipe device such as socket, named pipe or anonymous pipe - pub fn is_pipe(&self) -> bool { - self.0 == winbase::FILE_TYPE_PIPE - } - - /// Returns true if unknown device - pub fn is_unknown(&self) -> bool { - self.0 == winbase::FILE_TYPE_UNKNOWN - } -} - -pub unsafe fn get_file_type(handle: RawHandle) -> Result { - let file_type = FileType(GetFileType(handle)); - let err = Error::last_os_error(); - if file_type.is_unknown() && err.raw_os_error().unwrap() as u32 != winerror::ERROR_SUCCESS { - Err(err) - } else { - Ok(file_type) - } -} - -#[derive(Debug, Clone, Copy, Eq, PartialEq)] -#[repr(u32)] -pub enum CreationDisposition { - NO_DISPOSITION = 0, - /// Creates a new file, only if it does not already exist. - /// If the specified file exists, the function fails and the last-error code is - /// set to ERROR_FILE_EXISTS (80). - /// - /// If the specified file does not exist and is a valid path to a writable location, - /// a new file is created. - CREATE_NEW = fileapi::CREATE_NEW, - /// Creates a new file, always. - /// If the specified file exists and is writable, the function overwrites the file, - /// the function succeeds, and last-error code is set to ERROR_ALREADY_EXISTS (183). - /// - /// If the specified file does not exist and is a valid path, a new file is created, - /// the function succeeds, and the last-error code is set to zero. - CREATE_ALWAYS = fileapi::CREATE_ALWAYS, - /// Opens a file or device, only if it exists. - /// If the specified file or device does not exist, the function fails and the - /// last-error code is set to ERROR_FILE_NOT_FOUND (2). - OPEN_EXISTING = fileapi::OPEN_EXISTING, - /// Opens a file, always. - /// If the specified file exists, the function succeeds and the last-error code is - /// set to ERROR_ALREADY_EXISTS (183). - /// - /// If the specified file does not exist and is a valid path to a writable location, - /// the function creates a file and the last-error code is set to zero. - OPEN_ALWAYS = fileapi::OPEN_ALWAYS, - /// Opens a file and truncates it so that its size is zero bytes, only if it exists. - /// If the specified file does not exist, the function fails and the last-error code - /// is set to ERROR_FILE_NOT_FOUND (2). - /// - /// The calling process must open the file with the GENERIC_WRITE bit set as part - /// of the dwDesiredAccess parameter. - TRUNCATE_EXISTING = fileapi::TRUNCATE_EXISTING, -} - -impl CreationDisposition { - pub fn from_u32(disp: u32) -> Self { - use CreationDisposition::*; - match disp { - fileapi::CREATE_NEW => CREATE_NEW, - fileapi::CREATE_ALWAYS => CREATE_ALWAYS, - fileapi::OPEN_EXISTING => OPEN_EXISTING, - fileapi::OPEN_ALWAYS => OPEN_ALWAYS, - fileapi::TRUNCATE_EXISTING => TRUNCATE_EXISTING, - _ => NO_DISPOSITION, - } - } -} - -bitflags! { - pub struct Attributes: minwindef::DWORD { - /// A file or directory that is an archive file or directory. - /// Applications typically use this attribute to mark files for backup or removal. - const FILE_ATTRIBUTE_ARCHIVE = winnt::FILE_ATTRIBUTE_ARCHIVE; - /// A file or directory that is compressed. For a file, all of the data in the file is compressed. - /// For a directory, compression is the default for newly created files and subdirectories. - const FILE_ATTRIBUTE_COMPRESSED = winnt::FILE_ATTRIBUTE_COMPRESSED; - /// This value is reserved for system use. - const FILE_ATTRIBUTE_DEVICE = winnt::FILE_ATTRIBUTE_DEVICE; - /// The handle that identifies a directory. - const FILE_ATTRIBUTE_DIRECTORY = winnt::FILE_ATTRIBUTE_DIRECTORY; - /// A file or directory that is encrypted. For a file, all data streams in the file are encrypted. - /// For a directory, encryption is the default for newly created files and subdirectories. - const FILE_ATTRIBUTE_ENCRYPTED = winnt::FILE_ATTRIBUTE_ENCRYPTED; - /// The file or directory is hidden. It is not included in an ordinary directory listing. - const FILE_ATTRIBUTE_HIDDEN = winnt::FILE_ATTRIBUTE_HIDDEN; - /// The directory or user data stream is configured with integrity (only supported on ReFS volumes). - /// It is not included in an ordinary directory listing. The integrity setting persists with the file if it's renamed. - /// If a file is copied the destination file will have integrity set if either the source file or destination directory have integrity set. - const FILE_ATTRIBUTE_INTEGRITY_STREAM = winnt::FILE_ATTRIBUTE_INTEGRITY_STREAM; - /// A file that does not have other attributes set. This attribute is valid only when used alone. - const FILE_ATTRIBUTE_NORMAL = winnt::FILE_ATTRIBUTE_NORMAL; - /// The file or directory is not to be indexed by the content indexing service. - const FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = winnt::FILE_ATTRIBUTE_NOT_CONTENT_INDEXED; - /// The user data stream not to be read by the background data integrity scanner (AKA scrubber). - /// When set on a directory it only provides inheritance. This flag is only supported on Storage Spaces and ReFS volumes. - /// It is not included in an ordinary directory listing. - const FILE_ATTRIBUTE_NO_SCRUB_DATA = winnt::FILE_ATTRIBUTE_NO_SCRUB_DATA; - /// The data of a file is not available immediately. - /// This attribute indicates that the file data is physically moved to offline storage. - /// This attribute is used by Remote Storage, which is the hierarchical storage management software. - /// Applications should not arbitrarily change this attribute. - const FILE_ATTRIBUTE_OFFLINE = winnt::FILE_ATTRIBUTE_OFFLINE; - /// A file that is read-only. Applications can read the file, but cannot write to it or delete it. - /// This attribute is not honored on directories. - const FILE_ATTRIBUTE_READONLY = winnt::FILE_ATTRIBUTE_READONLY; - /// When this attribute is set, it means that the file or directory is not fully present locally. - /// For a file that means that not all of its data is on local storage (e.g. it may be sparse with some data still in remote storage). - /// For a directory it means that some of the directory contents are being virtualized from another location. - /// Reading the file / enumerating the directory will be more expensive than normal, e.g. it will cause at least some of the - /// file/directory content to be fetched from a remote store. Only kernel-mode callers can set this bit. - const FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS = winnt::FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS; - /// This attribute only appears in directory enumeration classes (FILE_DIRECTORY_INFORMATION, FILE_BOTH_DIR_INFORMATION, etc.). - /// When this attribute is set, it means that the file or directory has no physical representation on the local system; the item is virtual. - /// Opening the item will be more expensive than normal, e.g. it will cause at least some of it to be fetched from a remote store. - const FILE_ATTRIBUTE_RECALL_ON_OPEN = winnt::FILE_ATTRIBUTE_RECALL_ON_OPEN; - /// A file or directory that has an associated reparse point, or a file that is a symbolic link. - const FILE_ATTRIBUTE_REPARSE_POINT = winnt::FILE_ATTRIBUTE_REPARSE_POINT; - /// A file that is a sparse file. - const FILE_ATTRIBUTE_SPARSE_FILE = winnt::FILE_ATTRIBUTE_SPARSE_FILE; - /// A file or directory that the operating system uses a part of, or uses exclusively. - const FILE_ATTRIBUTE_SYSTEM = winnt::FILE_ATTRIBUTE_SYSTEM; - /// A file that is being used for temporary storage. - /// File systems avoid writing data back to mass storage if sufficient cache memory is available, because typically, - /// an application deletes a temporary file after the handle is closed. In that scenario, the system can entirely - /// avoid writing the data. Otherwise, the data is written after the handle is closed. - const FILE_ATTRIBUTE_TEMPORARY = winnt::FILE_ATTRIBUTE_TEMPORARY; - /// This value is reserved for system use. - const FILE_ATTRIBUTE_VIRTUAL = winnt::FILE_ATTRIBUTE_VIRTUAL; - } -} - -bitflags! { - pub struct Flags: minwindef::DWORD { - /// The file is being opened or created for a backup or restore operation. - /// The system ensures that the calling process overrides file security checks when the process has SE_BACKUP_NAME and SE_RESTORE_NAME privileges. - /// You must set this flag to obtain a handle to a directory. A directory handle can be passed to some functions instead of a file handle. - const FILE_FLAG_BACKUP_SEMANTICS = winbase::FILE_FLAG_BACKUP_SEMANTICS; - /// The file is to be deleted immediately after all of its handles are closed, which includes the specified handle and any other open or duplicated handles. - /// If there are existing open handles to a file, the call fails unless they were all opened with the FILE_SHARE_DELETE share mode. - /// Subsequent open requests for the file fail, unless the FILE_SHARE_DELETE share mode is specified. - const FILE_FLAG_DELETE_ON_CLOSE = winbase::FILE_FLAG_DELETE_ON_CLOSE; - /// The file or device is being opened with no system caching for data reads and writes. - /// This flag does not affect hard disk caching or memory mapped files. - /// There are strict requirements for successfully working with files opened with - /// CreateFile using the FILE_FLAG_NO_BUFFERING flag. - const FILE_FLAG_NO_BUFFERING = winbase::FILE_FLAG_NO_BUFFERING; - /// The file data is requested, but it should continue to be located in remote storage. - /// It should not be transported back to local storage. This flag is for use by remote storage systems. - const FILE_FLAG_OPEN_NO_RECALL = winbase::FILE_FLAG_OPEN_NO_RECALL; - /// Normal reparse point processing will not occur; CreateFile will attempt to open the reparse point. - /// When a file is opened, a file handle is returned, whether or not the filter that controls the reparse point is operational. - /// This flag cannot be used with the CREATE_ALWAYS flag. - /// If the file is not a reparse point, then this flag is ignored. - const FILE_FLAG_OPEN_REPARSE_POINT = winbase::FILE_FLAG_OPEN_REPARSE_POINT; - /// The file or device is being opened or created for asynchronous I/O. - /// When subsequent I/O operations are completed on this handle, the event specified in the OVERLAPPED structure will be set to the signaled state. - /// If this flag is specified, the file can be used for simultaneous read and write operations. - /// If this flag is not specified, then I/O operations are serialized, even if the calls to the read and write functions specify an OVERLAPPED structure. - const FILE_FLAG_OVERLAPPED = winbase::FILE_FLAG_OVERLAPPED; - /// Access will occur according to POSIX rules. This includes allowing multiple files with names, - /// differing only in case, for file systems that support that naming. Use care when using this option, - /// because files created with this flag may not be accessible by applications that are written for MS-DOS or 16-bit Windows. - const FILE_FLAG_POSIX_SEMANTICS = winbase::FILE_FLAG_POSIX_SEMANTICS; - /// Access is intended to be random. The system can use this as a hint to optimize file caching. - /// This flag has no effect if the file system does not support cached I/O and FILE_FLAG_NO_BUFFERING. - const FILE_FLAG_RANDOM_ACCESS = winbase::FILE_FLAG_RANDOM_ACCESS; - /// The file or device is being opened with session awareness. - /// If this flag is not specified, then per-session devices (such as a device using RemoteFX USB Redirection) - /// cannot be opened by processes running in session 0. This flag has no effect for callers not in session 0. - /// This flag is supported only on server editions of Windows. - const FILE_FLAG_SESSION_AWARE = winbase::FILE_FLAG_SESSION_AWARE; - /// Access is intended to be sequential from beginning to end. The system can use this as a hint to optimize file caching. - /// This flag should not be used if read-behind (that is, reverse scans) will be used. - /// This flag has no effect if the file system does not support cached I/O and FILE_FLAG_NO_BUFFERING. - const FILE_FLAG_SEQUENTIAL_SCAN = winbase::FILE_FLAG_SEQUENTIAL_SCAN; - /// Write operations will not go through any intermediate cache, they will go directly to disk. - const FILE_FLAG_WRITE_THROUGH = winbase::FILE_FLAG_WRITE_THROUGH; - } -} - -bitflags! { - /// [Access mask]: https://docs.microsoft.com/en-us/windows/desktop/SecAuthZ/access-mask - pub struct AccessMode: minwindef::DWORD { - /// For a file object, the right to read the corresponding file data. - /// For a directory object, the right to read the corresponding directory data. - const FILE_READ_DATA = winnt::FILE_READ_DATA; - const FILE_LIST_DIRECTORY = winnt::FILE_LIST_DIRECTORY; - /// For a file object, the right to write data to the file. - /// For a directory object, the right to create a file in the directory. - const FILE_WRITE_DATA = winnt::FILE_WRITE_DATA; - const FILE_ADD_FILE = winnt::FILE_ADD_FILE; - /// For a file object, the right to append data to the file. - /// (For local files, write operations will not overwrite existing data - /// if this flag is specified without FILE_WRITE_DATA.) - /// For a directory object, the right to create a subdirectory. - /// For a named pipe, the right to create a pipe. - const FILE_APPEND_DATA = winnt::FILE_APPEND_DATA; - const FILE_ADD_SUBDIRECTORY = winnt::FILE_ADD_SUBDIRECTORY; - const FILE_CREATE_PIPE_INSTANCE = winnt::FILE_CREATE_PIPE_INSTANCE; - /// The right to read extended file attributes. - const FILE_READ_EA = winnt::FILE_READ_EA; - /// The right to write extended file attributes. - const FILE_WRITE_EA = winnt::FILE_WRITE_EA; - /// For a file, the right to execute FILE_EXECUTE. - /// For a directory, the right to traverse the directory. - /// By default, users are assigned the BYPASS_TRAVERSE_CHECKING privilege, - /// which ignores the FILE_TRAVERSE access right. - const FILE_EXECUTE = winnt::FILE_EXECUTE; - const FILE_TRAVERSE = winnt::FILE_TRAVERSE; - /// For a directory, the right to delete a directory and all - /// the files it contains, including read-only files. - const FILE_DELETE_CHILD = winnt::FILE_DELETE_CHILD; - /// The right to read file attributes. - const FILE_READ_ATTRIBUTES = winnt::FILE_READ_ATTRIBUTES; - /// The right to write file attributes. - const FILE_WRITE_ATTRIBUTES = winnt::FILE_WRITE_ATTRIBUTES; - /// The right to delete the object. - const DELETE = winnt::DELETE; - /// The right to read the information in the object's security descriptor, - /// not including the information in the system access control list (SACL). - const READ_CONTROL = winnt::READ_CONTROL; - /// The right to use the object for synchronization. This enables a thread - /// to wait until the object is in the signaled state. Some object types - /// do not support this access right. - const SYNCHRONIZE = winnt::SYNCHRONIZE; - /// The right to modify the discretionary access control list (DACL) in - /// the object's security descriptor. - const WRITE_DAC = winnt::WRITE_DAC; - /// The right to change the owner in the object's security descriptor. - const WRITE_OWNER = winnt::WRITE_OWNER; - /// It is used to indicate access to a system access control list (SACL). - const ACCESS_SYSTEM_SECURITY = winnt::ACCESS_SYSTEM_SECURITY; - /// Maximum allowed. - const MAXIMUM_ALLOWED = winnt::MAXIMUM_ALLOWED; - /// Reserved - const RESERVED1 = 0x4000000; - /// Reserved - const RESERVED2 = 0x8000000; - /// Provides all possible access rights. - /// This is convenience flag which is translated by the OS into actual [`FILE_GENERIC_ALL`] union. - const GENERIC_ALL = winnt::GENERIC_ALL; - /// Provides execute access. - const GENERIC_EXECUTE = winnt::GENERIC_EXECUTE; - /// Provides write access. - /// This is convenience flag which is translated by the OS into actual [`FILE_GENERIC_WRITE`] union. - const GENERIC_WRITE = winnt::GENERIC_WRITE; - /// Provides read access. - /// This is convenience flag which is translated by the OS into actual [`FILE_GENERIC_READ`] union. - const GENERIC_READ = winnt::GENERIC_READ; - /// Provides read access. - const FILE_GENERIC_READ = winnt::FILE_GENERIC_READ; - /// Provides write access. - const FILE_GENERIC_WRITE = winnt::FILE_GENERIC_WRITE; - /// Provides execute access. - const FILE_GENERIC_EXECUTE = winnt::FILE_GENERIC_EXECUTE; - /// Provides all accesses. - const FILE_ALL_ACCESS = winnt::FILE_ALL_ACCESS; - } -} - -bitflags! { - // https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/52df7798-8330-474b-ac31-9afe8075640c - pub struct FileModeInformation: minwindef::DWORD { - /// When set, any system services, file system drivers (FSDs), and drivers that write data to - /// the file are required to actually transfer the data into the file before any requested write - /// operation is considered complete. - const FILE_WRITE_THROUGH = 0x2; - /// This is a hint that informs the cache that it SHOULD optimize for sequential access. - /// Non-sequential access of the file can result in performance degradation. - const FILE_SEQUENTIAL_ONLY = 0x4; - /// When set, the file cannot be cached or buffered in a driver's internal buffers. - const FILE_NO_INTERMEDIATE_BUFFERING = 0x8; - /// When set, all operations on the file are performed synchronously. - /// Any wait on behalf of the caller is subject to premature termination from alerts. - /// This flag also causes the I/O system to maintain the file position context. - const FILE_SYNCHRONOUS_IO_ALERT = 0x10; - /// When set, all operations on the file are performed synchronously. - /// Wait requests in the system to synchronize I/O queuing and completion are not subject to alerts. - /// This flag also causes the I/O system to maintain the file position context. - const FILE_SYNCHRONOUS_IO_NONALERT = 0x20; - /// This flag is not implemented and is always returned as not set. - const FILE_DELETE_ON_CLOSE = 0x1000; - } -} - -pub fn get_file_path(file: &File) -> Result { - use winapi::um::fileapi::GetFinalPathNameByHandleW; - - let mut raw_path: Vec = vec![0; WIDE_MAX_PATH as usize]; - - let handle = file.as_raw_handle(); - let read_len = - cvt(unsafe { GetFinalPathNameByHandleW(handle, raw_path.as_mut_ptr(), WIDE_MAX_PATH, 0) })?; - - // obtain a slice containing the written bytes, and check for it being too long - // (practically probably impossible) - let written_bytes = raw_path - .get(..read_len as usize) - .ok_or(Error::from_raw_os_error( - winerror::ERROR_BUFFER_OVERFLOW as i32, - ))?; - - Ok(OsString::from_wide(written_bytes)) -} - -pub fn get_fileinfo(file: &File) -> Result { - use fileapi::{GetFileInformationByHandle, BY_HANDLE_FILE_INFORMATION}; - use std::mem; - - let handle = file.as_raw_handle(); - let info = unsafe { - let mut info: BY_HANDLE_FILE_INFORMATION = mem::zeroed(); - cvt(GetFileInformationByHandle(handle, &mut info))?; - info - }; - - Ok(info) -} - -pub fn change_time(file: &File) -> Result { - use fileapi::FILE_BASIC_INFO; - use minwinbase::FileBasicInfo; - use std::mem; - use winbase::GetFileInformationByHandleEx; - - let handle = file.as_raw_handle(); - let tm = unsafe { - let mut info: FILE_BASIC_INFO = mem::zeroed(); - let infosize = mem::size_of_val(&info); - cvt(GetFileInformationByHandleEx( - handle, - FileBasicInfo, - &mut info as *mut FILE_BASIC_INFO as *mut c_void, - infosize as u32, - ))?; - *info.ChangeTime.QuadPart() - }; - - Ok(tm) -} - -pub fn query_access_information(handle: RawHandle) -> Result { - let mut io_status_block = IO_STATUS_BLOCK::default(); - let mut info = FILE_ACCESS_INFORMATION::default(); - - unsafe { - let status = NtQueryInformationFile( - handle, - &mut io_status_block, - &mut info as *mut _ as *mut c_void, - std::mem::size_of::() as u32, - FILE_INFORMATION_CLASS::FileAccessInformation, - ); - - if status != ntstatus::STATUS_SUCCESS { - return Err(Error::from_raw_os_error( - RtlNtStatusToDosError(status) as i32 - )); - } - } - - Ok(AccessMode::from_bits_truncate(info.AccessFlags)) -} - -pub fn query_mode_information(handle: RawHandle) -> Result { - let mut io_status_block = IO_STATUS_BLOCK::default(); - let mut info = FILE_MODE_INFORMATION::default(); - - unsafe { - let status = NtQueryInformationFile( - handle, - &mut io_status_block, - &mut info as *mut _ as *mut c_void, - std::mem::size_of::() as u32, - FILE_INFORMATION_CLASS::FileModeInformation, - ); - - if status != ntstatus::STATUS_SUCCESS { - return Err(Error::from_raw_os_error( - RtlNtStatusToDosError(status) as i32 - )); - } - } - - Ok(FileModeInformation::from_bits_truncate(info.Mode)) -} - -pub fn reopen_file(handle: RawHandle, access_mode: AccessMode, flags: Flags) -> Result { - // Files on Windows are opened with DELETE, READ, and WRITE share mode by default (see OpenOptions in stdlib) - // This keeps the same share mode when reopening the file handle - let new_handle = unsafe { - winbase::ReOpenFile( - handle, - access_mode.bits(), - winnt::FILE_SHARE_DELETE | winnt::FILE_SHARE_READ | winnt::FILE_SHARE_WRITE, - flags.bits(), - ) - }; - - if new_handle == winapi::um::handleapi::INVALID_HANDLE_VALUE { - return Err(Error::last_os_error()); - } - - Ok(new_handle) -} diff --git a/crates/wasi-common/winx/src/lib.rs b/crates/wasi-common/winx/src/lib.rs deleted file mode 100644 index 6130f28ff6..0000000000 --- a/crates/wasi-common/winx/src/lib.rs +++ /dev/null @@ -1,25 +0,0 @@ -#![deny( - // missing_docs, - trivial_numeric_casts, - unused_extern_crates, - unstable_features -)] -#![warn(unused_import_braces)] -#![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../clippy.toml")))] -#![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))] -#![cfg_attr( - feature = "cargo-clippy", - warn( - clippy::float_arithmetic, - clippy::mut_mut, - clippy::nonminimal_bool, - clippy::map_unwrap_or, - clippy::clippy::unicode_not_nfc, - clippy::use_self - ) -)] -#![cfg(windows)] - -pub mod file; -mod ntdll; -pub mod time; diff --git a/crates/wasi-common/winx/src/ntdll.rs b/crates/wasi-common/winx/src/ntdll.rs deleted file mode 100644 index 110c80fa33..0000000000 --- a/crates/wasi-common/winx/src/ntdll.rs +++ /dev/null @@ -1,90 +0,0 @@ -//! Module for importing functions from ntdll.dll. -//! The winapi crate does not expose these Windows API functions. - -#![allow(nonstandard_style)] - -use std::ffi::c_void; -use std::os::raw::c_ulong; -use std::os::windows::prelude::RawHandle; -use std::sync::atomic::{AtomicUsize, Ordering}; -use winapi::shared::ntdef::NTSTATUS; -use winapi::um::libloaderapi::{GetModuleHandleA, GetProcAddress}; -use winapi::um::winnt::ACCESS_MASK; - -#[repr(C)] -#[derive(Copy, Clone)] -pub(crate) enum FILE_INFORMATION_CLASS { - FileAccessInformation = 8, - FileModeInformation = 16, -} - -#[repr(C)] -#[derive(Copy, Clone)] -pub(crate) union IO_STATUS_BLOCK_u { - pub Status: NTSTATUS, - pub Pointer: *mut c_void, -} - -#[repr(C)] -#[derive(Copy, Clone)] -pub(crate) struct IO_STATUS_BLOCK { - pub u: IO_STATUS_BLOCK_u, - pub Information: *mut c_void, -} - -#[repr(C)] -#[derive(Copy, Clone, Default)] -pub(crate) struct FILE_ACCESS_INFORMATION { - pub AccessFlags: ACCESS_MASK, -} - -#[repr(C)] -#[derive(Copy, Clone, Default)] -pub(crate) struct FILE_MODE_INFORMATION { - pub Mode: c_ulong, -} - -impl Default for IO_STATUS_BLOCK { - #[inline] - fn default() -> Self { - unsafe { std::mem::zeroed() } - } -} - -macro_rules! ntdll_import { - { fn $name:ident($($arg:ident: $argty:ty),*) -> $retty:ty; $($tail:tt)* } => { - pub(crate) unsafe fn $name($($arg: $argty),*) -> $retty { - static ADDRESS: AtomicUsize = AtomicUsize::new(0); - let address = match ADDRESS.load(Ordering::Relaxed) { - 0 => { - let ntdll = GetModuleHandleA("ntdll\0".as_ptr() as *const i8); - let address = GetProcAddress( - ntdll, - concat!(stringify!($name), "\0").as_ptr() as *const i8, - ) as usize; - assert!(address != 0); - ADDRESS.store(address, Ordering::Relaxed); - address - } - address => address - }; - let func: unsafe fn($($argty),*) -> $retty = std::mem::transmute(address); - func($($arg),*) - } - ntdll_import! { $($tail)* } - }; - {} => {}; -} - -ntdll_import! { - // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-ntqueryinformationfile - fn NtQueryInformationFile( - FileHandle: RawHandle, - IoStatusBlock: *mut IO_STATUS_BLOCK, - FileInformation: *mut c_void, - Length: c_ulong, - FileInformationClass: FILE_INFORMATION_CLASS - ) -> NTSTATUS; - // https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-rtlntstatustodoserror - fn RtlNtStatusToDosError(status: NTSTATUS) -> c_ulong; -} diff --git a/crates/wasi-common/winx/src/time.rs b/crates/wasi-common/winx/src/time.rs deleted file mode 100644 index cb3e7918c8..0000000000 --- a/crates/wasi-common/winx/src/time.rs +++ /dev/null @@ -1,11 +0,0 @@ -use cvt::cvt; -use std::io::Result; -use winapi::um::{profileapi::QueryPerformanceFrequency, winnt::LARGE_INTEGER}; - -pub fn perf_counter_frequency() -> Result { - unsafe { - let mut frequency: LARGE_INTEGER = std::mem::zeroed(); - cvt(QueryPerformanceFrequency(&mut frequency))?; - Ok(*frequency.QuadPart() as u64) - } -} diff --git a/crates/wasi-common/yanix/Cargo.toml b/crates/wasi-common/yanix/Cargo.toml deleted file mode 100644 index e4c4941f62..0000000000 --- a/crates/wasi-common/yanix/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "yanix" -version = "0.22.0" -authors = ["The Wasmtime Project Developers"] -description = "Yet Another Nix crate: a Unix API helper library" -documentation = "https://docs.rs/yanix" -license = "Apache-2.0 WITH LLVM-exception" -repository = "https://github.com/bytecodealliance/wasmtime" -edition = "2018" - -[dependencies] -tracing = "0.1.15" -libc = { version = "0.2", features = ["extra_traits"] } -bitflags = "1.2" -cfg-if = "1.0" -filetime = "0.2.7" - -[badges] -maintenance = { status = "actively-developed" } diff --git a/crates/wasi-common/yanix/LICENSE b/crates/wasi-common/yanix/LICENSE deleted file mode 100644 index f9d81955f4..0000000000 --- a/crates/wasi-common/yanix/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/yanix/src/clock.rs b/crates/wasi-common/yanix/src/clock.rs deleted file mode 100644 index 34e0b9947b..0000000000 --- a/crates/wasi-common/yanix/src/clock.rs +++ /dev/null @@ -1,34 +0,0 @@ -use crate::from_success_code; -use std::io::Result; -use std::mem::MaybeUninit; - -#[derive(Debug, Copy, Clone)] -pub enum ClockId { - Realtime, - Monotonic, - ProcessCPUTime, - ThreadCPUTime, -} - -impl ClockId { - pub fn as_raw(&self) -> libc::clockid_t { - match self { - Self::Realtime => libc::CLOCK_REALTIME, - Self::Monotonic => libc::CLOCK_MONOTONIC, - Self::ProcessCPUTime => libc::CLOCK_PROCESS_CPUTIME_ID, - Self::ThreadCPUTime => libc::CLOCK_THREAD_CPUTIME_ID, - } - } -} - -pub fn clock_getres(clock_id: ClockId) -> Result { - let mut timespec = MaybeUninit::::uninit(); - from_success_code(unsafe { libc::clock_getres(clock_id.as_raw(), timespec.as_mut_ptr()) })?; - Ok(unsafe { timespec.assume_init() }) -} - -pub fn clock_gettime(clock_id: ClockId) -> Result { - let mut timespec = MaybeUninit::::uninit(); - from_success_code(unsafe { libc::clock_gettime(clock_id.as_raw(), timespec.as_mut_ptr()) })?; - Ok(unsafe { timespec.assume_init() }) -} diff --git a/crates/wasi-common/yanix/src/dir.rs b/crates/wasi-common/yanix/src/dir.rs deleted file mode 100644 index 8e6c70a40f..0000000000 --- a/crates/wasi-common/yanix/src/dir.rs +++ /dev/null @@ -1,150 +0,0 @@ -use crate::{ - file::FileType, - sys::dir::{iter_impl, EntryImpl}, -}; -use std::convert::TryInto; -use std::io::{Error, Result}; -#[cfg(unix)] -use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd}; -#[cfg(target_os = "wasi")] -use std::os::wasi::io::{AsRawFd, IntoRawFd, RawFd}; -use std::{ffi::CStr, io, ops::Deref, ptr}; - -pub use crate::sys::EntryExt; - -#[derive(Clone, Debug, Eq, Hash, PartialEq)] -pub struct Dir(ptr::NonNull); - -impl Dir { - /// Takes the ownership of the passed-in descriptor-based object, - /// and creates a new instance of `Dir`. - #[inline] - pub fn from(fd: F) -> Result { - let fd = fd.into_raw_fd(); - unsafe { Self::from_fd(fd) } - } - - unsafe fn from_fd(fd: RawFd) -> Result { - let d = libc::fdopendir(fd); - if let Some(d) = ptr::NonNull::new(d) { - Ok(Self(d)) - } else { - let e = io::Error::last_os_error(); - libc::close(fd); - Err(e.into()) - } - } - - /// Set the position of the directory stream, see `seekdir(3)`. - pub fn seek(&mut self, loc: SeekLoc) { - // https://github.com/rust-lang/libc/pull/1996 - #[cfg(not(target_os = "android"))] - use libc::seekdir; - #[cfg(target_os = "android")] - extern "C" { - fn seekdir(dirp: *mut libc::DIR, loc: libc::c_long); - } - unsafe { seekdir(self.0.as_ptr(), loc.0) } - } - - /// Reset directory stream, see `rewinddir(3)`. - pub fn rewind(&mut self) { - unsafe { libc::rewinddir(self.0.as_ptr()) } - } - - /// Get the current position in the directory stream. - /// - /// If this location is given to `Dir::seek`, the entries up to the previously returned - /// will be omitted and the iteration will start from the currently pending directory entry. - #[allow(dead_code)] - pub fn tell(&self) -> SeekLoc { - #[cfg(not(target_os = "android"))] - use libc::telldir; - #[cfg(target_os = "android")] - extern "C" { - fn telldir(dirp: *mut libc::DIR) -> libc::c_long; - } - // https://github.com/rust-lang/libc/pull/1996 - let loc = unsafe { telldir(self.0.as_ptr()) }; - SeekLoc(loc) - } - - /// For use by platform-specific implementation code. Returns the raw - /// underlying state. - pub(crate) fn as_raw(&self) -> ptr::NonNull { - self.0 - } -} - -unsafe impl Send for Dir {} - -impl AsRawFd for Dir { - fn as_raw_fd(&self) -> RawFd { - unsafe { libc::dirfd(self.0.as_ptr()) } - } -} - -impl Drop for Dir { - fn drop(&mut self) { - unsafe { libc::closedir(self.0.as_ptr()) }; - } -} - -#[derive(Debug, Copy, Clone)] -pub struct Entry(pub(crate) EntryImpl); - -impl Entry { - /// Returns the file name of this directory entry. - pub fn file_name(&self) -> &CStr { - unsafe { CStr::from_ptr(self.0.d_name.as_ptr()) } - } - - /// Returns the type of this directory entry. - pub fn file_type(&self) -> FileType { - FileType::from_dirent_d_type(self.0.d_type) - } -} - -#[derive(Clone, Copy, Debug)] -pub struct SeekLoc(pub(crate) libc::c_long); - -impl SeekLoc { - pub fn to_raw(&self) -> i64 { - self.0.into() - } - - pub unsafe fn from_raw(loc: i64) -> Result { - // The cookie (or `loc`) is an opaque value, and applications aren't supposed to do - // arithmetic on them or pick their own values or have any awareness of the numeric - // range of the values. They're just supposed to pass back in the values that we - // give them. And any value we give them will be convertable back to `long`, - // because that's the type the OS gives them to us in. So return an `EINVAL`. - let loc = loc - .try_into() - .map_err(|_| Error::from_raw_os_error(libc::EINVAL))?; - Ok(Self(loc)) - } -} - -#[derive(Debug)] -pub struct DirIter>(T); - -impl DirIter -where - T: Deref, -{ - pub fn new(dir: T) -> Self { - Self(dir) - } -} - -impl Iterator for DirIter -where - T: Deref, -{ - type Item = Result; - - fn next(&mut self) -> Option { - iter_impl(&self.0).map(|x| x.map(Entry)) - } -} diff --git a/crates/wasi-common/yanix/src/fcntl.rs b/crates/wasi-common/yanix/src/fcntl.rs deleted file mode 100644 index 824b997b9b..0000000000 --- a/crates/wasi-common/yanix/src/fcntl.rs +++ /dev/null @@ -1,37 +0,0 @@ -use crate::{ - file::{FdFlags, OFlags}, - from_result, from_success_code, -}; -use std::io::Result; -#[cfg(unix)] -use std::os::unix::prelude::*; -#[cfg(target_os = "wasi")] -use std::os::wasi::prelude::*; - -pub unsafe fn dup_fd(fd: RawFd, close_on_exec: bool) -> Result { - // Both fcntl commands expect a RawFd arg which will specify - // the minimum duplicated RawFd number. In our case, I don't - // think we have to worry about this that much, so passing in - // the RawFd descriptor we want duplicated - from_result(if close_on_exec { - libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, fd) - } else { - libc::fcntl(fd, libc::F_DUPFD, fd) - }) -} - -pub unsafe fn get_fd_flags(fd: RawFd) -> Result { - from_result(libc::fcntl(fd, libc::F_GETFD)).map(FdFlags::from_bits_truncate) -} - -pub unsafe fn set_fd_flags(fd: RawFd, flags: FdFlags) -> Result<()> { - from_success_code(libc::fcntl(fd, libc::F_SETFD, flags.bits())) -} - -pub unsafe fn get_status_flags(fd: RawFd) -> Result { - from_result(libc::fcntl(fd, libc::F_GETFL)).map(OFlags::from_bits_truncate) -} - -pub unsafe fn set_status_flags(fd: RawFd, flags: OFlags) -> Result<()> { - from_success_code(libc::fcntl(fd, libc::F_SETFL, flags.bits())) -} diff --git a/crates/wasi-common/yanix/src/file.rs b/crates/wasi-common/yanix/src/file.rs deleted file mode 100644 index 62af1c9d77..0000000000 --- a/crates/wasi-common/yanix/src/file.rs +++ /dev/null @@ -1,304 +0,0 @@ -use crate::{cstr, from_result, from_success_code}; -use bitflags::bitflags; -use cfg_if::cfg_if; -#[cfg(unix)] -use std::os::unix::prelude::*; -#[cfg(target_os = "wasi")] -use std::os::wasi::prelude::*; -use std::{ - convert::TryInto, - ffi::{OsStr, OsString}, - io::Result, - path::Path, -}; - -pub use crate::sys::file::*; - -bitflags! { - pub struct FdFlags: libc::c_int { - const CLOEXEC = libc::FD_CLOEXEC; - } -} - -bitflags! { - pub struct AtFlags: libc::c_int { - const REMOVEDIR = libc::AT_REMOVEDIR; - const SYMLINK_FOLLOW = libc::AT_SYMLINK_FOLLOW; - const SYMLINK_NOFOLLOW = libc::AT_SYMLINK_NOFOLLOW; - #[cfg(any(target_os = "linux", - target_os = "fuchsia"))] - const EMPTY_PATH = libc::AT_EMPTY_PATH; - } -} - -bitflags! { - pub struct Mode: libc::mode_t { - const IRWXU = libc::S_IRWXU; - const IRUSR = libc::S_IRUSR; - const IWUSR = libc::S_IWUSR; - const IXUSR = libc::S_IXUSR; - const IRWXG = libc::S_IRWXG; - const IRGRP = libc::S_IRGRP; - const IWGRP = libc::S_IWGRP; - const IXGRP = libc::S_IXGRP; - const IRWXO = libc::S_IRWXO; - const IROTH = libc::S_IROTH; - const IWOTH = libc::S_IWOTH; - const IXOTH = libc::S_IXOTH; - const ISUID = libc::S_ISUID as libc::mode_t; - const ISGID = libc::S_ISGID as libc::mode_t; - const ISVTX = libc::S_ISVTX as libc::mode_t; - } -} - -bitflags! { - pub struct OFlags: libc::c_int { - const ACCMODE = libc::O_ACCMODE; - const APPEND = libc::O_APPEND; - const CREAT = libc::O_CREAT; - const DIRECTORY = libc::O_DIRECTORY; - const DSYNC = { - // Have to use cfg_if: https://github.com/bitflags/bitflags/issues/137 - cfg_if! { - if #[cfg(any(target_os = "android", - target_os = "ios", - target_os = "linux", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - target_os = "wasi", - target_os = "emscripten"))] { - libc::O_DSYNC - } else if #[cfg(target_os = "freebsd")] { - // https://github.com/bytecodealliance/wasmtime/pull/756 - libc::O_SYNC - } - } - }; - const EXCL = libc::O_EXCL; - #[cfg(any(target_os = "dragonfly", - target_os = "freebsd", - target_os = "ios", - all(target_os = "linux", not(target_env = "musl")), - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd"))] - const FSYNC = libc::O_FSYNC; - const NOFOLLOW = libc::O_NOFOLLOW; - const NONBLOCK = libc::O_NONBLOCK; - const RDONLY = libc::O_RDONLY; - const WRONLY = libc::O_WRONLY; - const RDWR = libc::O_RDWR; - #[cfg(any(target_os = "linux", - target_os = "android", - target_os = "netbsd", - target_os = "openbsd", - target_os = "wasi", - target_os = "emscripten"))] - const RSYNC = { - // Have to use cfg_if: https://github.com/bitflags/bitflags/issues/137 - cfg_if! { - if #[cfg(any(target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "wasi", - target_os = "emscripten"))] { - libc::O_RSYNC - } else if #[cfg(target_os = "android")] { - // Android defines O_RSYNC as O_SYNC - libc::O_SYNC - } - } - }; - const SYNC = libc::O_SYNC; - const TRUNC = libc::O_TRUNC; - #[cfg(any(target_os = "linux", - target_os = "fuchsia", - target_os = "redox"))] - const PATH = libc::O_PATH; - #[cfg(any(target_os = "linux", - target_os = "fuchsia", - target_os = "hermit", - target_os = "solaris", - target_os = "haiku", - target_os = "netbsd", - target_os = "freebsd", - target_os = "openbsd", - target_os = "dragonfly", - target_os = "vxworks", - target_os = "macos", - target_os = "ios", - target_os = "redox"))] - const CLOEXEC = libc::O_CLOEXEC; - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum FileType { - CharacterDevice, - Directory, - BlockDevice, - RegularFile, - Symlink, - Fifo, - Socket, - Unknown, -} - -impl FileType { - pub fn from_stat_st_mode(st_mode: libc::mode_t) -> Self { - match st_mode & libc::S_IFMT { - libc::S_IFIFO => Self::Fifo, - libc::S_IFCHR => Self::CharacterDevice, - libc::S_IFDIR => Self::Directory, - libc::S_IFBLK => Self::BlockDevice, - libc::S_IFREG => Self::RegularFile, - libc::S_IFLNK => Self::Symlink, - libc::S_IFSOCK => Self::Socket, - _ => Self::Unknown, // Should we actually panic here since this one *should* never happen? - } - } - - pub fn from_dirent_d_type(d_type: u8) -> Self { - match d_type { - libc::DT_CHR => Self::CharacterDevice, - libc::DT_DIR => Self::Directory, - libc::DT_BLK => Self::BlockDevice, - libc::DT_REG => Self::RegularFile, - libc::DT_LNK => Self::Symlink, - libc::DT_SOCK => Self::Socket, - libc::DT_FIFO => Self::Fifo, - /* libc::DT_UNKNOWN */ _ => Self::Unknown, - } - } -} - -pub unsafe fn openat>( - dirfd: RawFd, - path: P, - oflag: OFlags, - mode: Mode, -) -> Result { - let path = cstr(path)?; - from_result(libc::openat( - dirfd, - path.as_ptr(), - oflag.bits(), - libc::c_uint::from(mode.bits()), - )) -} - -pub unsafe fn readlinkat>(dirfd: RawFd, path: P) -> Result { - let path = cstr(path)?; - let buffer = &mut [0u8; libc::PATH_MAX as usize + 1]; - let nread = from_result(libc::readlinkat( - dirfd, - path.as_ptr(), - buffer.as_mut_ptr() as *mut _, - buffer.len(), - ))?; - // We can just unwrap() this, because readlinkat returns an ssize_t which is either -1 - // (handled above) or non-negative and will fit in a size_t/usize, which is what we're - // converting it to here. - let nread = nread.try_into().unwrap(); - let link = OsStr::from_bytes(&buffer[0..nread]); - Ok(link.into()) -} - -pub unsafe fn mkdirat>(dirfd: RawFd, path: P, mode: Mode) -> Result<()> { - let path = cstr(path)?; - from_success_code(libc::mkdirat(dirfd, path.as_ptr(), mode.bits())) -} - -pub unsafe fn linkat, Q: AsRef>( - old_dirfd: RawFd, - old_path: P, - new_dirfd: RawFd, - new_path: Q, - flags: AtFlags, -) -> Result<()> { - let old_path = cstr(old_path)?; - let new_path = cstr(new_path)?; - from_success_code(libc::linkat( - old_dirfd, - old_path.as_ptr(), - new_dirfd, - new_path.as_ptr(), - flags.bits(), - )) -} - -pub unsafe fn unlinkat>(dirfd: RawFd, path: P, flags: AtFlags) -> Result<()> { - let path = cstr(path)?; - from_success_code(libc::unlinkat(dirfd, path.as_ptr(), flags.bits())) -} - -pub unsafe fn renameat, Q: AsRef>( - old_dirfd: RawFd, - old_path: P, - new_dirfd: RawFd, - new_path: Q, -) -> Result<()> { - let old_path = cstr(old_path)?; - let new_path = cstr(new_path)?; - from_success_code(libc::renameat( - old_dirfd, - old_path.as_ptr(), - new_dirfd, - new_path.as_ptr(), - )) -} - -pub unsafe fn symlinkat, Q: AsRef>( - old_path: P, - new_dirfd: RawFd, - new_path: Q, -) -> Result<()> { - let old_path = cstr(old_path)?; - let new_path = cstr(new_path)?; - from_success_code(libc::symlinkat( - old_path.as_ptr(), - new_dirfd, - new_path.as_ptr(), - )) -} - -pub unsafe fn fstatat>(dirfd: RawFd, path: P, flags: AtFlags) -> Result { - use std::mem::MaybeUninit; - let path = cstr(path)?; - let mut filestat = MaybeUninit::::uninit(); - from_result(libc::fstatat( - dirfd, - path.as_ptr(), - filestat.as_mut_ptr(), - flags.bits(), - ))?; - Ok(filestat.assume_init()) -} - -pub unsafe fn fstat(fd: RawFd) -> Result { - use std::mem::MaybeUninit; - let mut filestat = MaybeUninit::::uninit(); - from_result(libc::fstat(fd, filestat.as_mut_ptr()))?; - Ok(filestat.assume_init()) -} - -/// `fionread()` function, equivalent to `ioctl(fd, FIONREAD, *bytes)`. -pub unsafe fn fionread(fd: RawFd) -> Result { - let mut nread: libc::c_int = 0; - from_result(libc::ioctl(fd, libc::FIONREAD, &mut nread as *mut _))?; - // FIONREAD returns a non-negative int if it doesn't fail, or it'll fit in a u32 if it does. - // - // For the future, if we want to be super cautious and avoid assuming int is 32-bit, we could - // widen fionread's return type here, since the one place that calls it wants a u64 anyway. - Ok(nread.try_into().unwrap()) -} - -/// This function is unsafe because it operates on a raw file descriptor. -/// It's provided, because std::io::Seek requires a mutable borrow. -pub unsafe fn tell(fd: RawFd) -> Result { - let offset = from_result(libc::lseek(fd, 0, libc::SEEK_CUR))?; - // lseek returns an off_t, which we can assume is a non-negative i64 if it doesn't fail. - // So we can unwrap() this conversion. - Ok(offset.try_into().unwrap()) -} diff --git a/crates/wasi-common/yanix/src/filetime.rs b/crates/wasi-common/yanix/src/filetime.rs deleted file mode 100644 index cc6b80f3c5..0000000000 --- a/crates/wasi-common/yanix/src/filetime.rs +++ /dev/null @@ -1,78 +0,0 @@ -//! This module consists of helper types and functions for dealing -//! with setting the file times (mainly in `path_filestat_set_times` syscall for now). -//! -//! The vast majority of the code contained within and in platform-specific implementations -//! (`super::linux::filetime` and `super::bsd::filetime`) is based on the [filetime] crate. -//! Kudos @alexcrichton! -//! -//! [filetime]: https://github.com/alexcrichton/filetime -use std::io::Result; - -pub use super::sys::filetime::*; - -/// Internal trait which specialises `filetime::FileTime`'s -/// `seconds` and `nanoseconds` accessors for different -/// pointer widths (32 and 64bit currently). -pub(crate) trait FileTimeExt { - fn seconds_(&self) -> Result; - fn nanoseconds_(&self) -> libc::c_long; -} - -impl FileTimeExt for filetime::FileTime { - fn seconds_(&self) -> Result { - use std::convert::TryInto; - use std::io::Error; - let sec = match self.seconds().try_into() { - Ok(sec) => sec, - Err(_) => { - tracing::debug!("filetime_to_timespec failed converting seconds to required width"); - return Err(Error::from_raw_os_error(libc::EOVERFLOW)); - } - }; - Ok(sec) - } - fn nanoseconds_(&self) -> libc::c_long { - use std::convert::TryInto; - // According to [filetime] docs, since the nanoseconds value is always less than 1 billion, - // any value should be convertible to `libc::c_long`, hence we can `unwrap` outright. - // - // [filetime]: https://docs.rs/filetime/0.2.8/filetime/struct.FileTime.html#method.nanoseconds - self.nanoseconds().try_into().unwrap() - } -} - -/// A wrapper `enum` around `filetime::FileTime` struct, but unlike the original, this -/// type allows the possibility of specifying `FileTime::Now` as a valid enumeration which, -/// in turn, if `utimensat` is available on the host, will use a special const setting -/// `UTIME_NOW`. -#[derive(Debug, Copy, Clone)] -pub enum FileTime { - Now, - Omit, - FileTime(filetime::FileTime), -} - -/// Converts `FileTime` to `libc::timespec`. If `FileTime::Now` variant is specified, this -/// resolves to `UTIME_NOW` special const, `FileTime::Omit` variant resolves to `UTIME_OMIT`, and -/// `FileTime::FileTime(ft)` where `ft := filetime::FileTime` uses [filetime] crate's original -/// implementation which can be found here: [filetime::unix::to_timespec]. -/// -/// [filetime]: https://github.com/alexcrichton/filetime -/// [filetime::unix::to_timespec]: https://github.com/alexcrichton/filetime/blob/master/src/unix/mod.rs#L30 -pub(crate) fn to_timespec(ft: &FileTime) -> Result { - let ts = match ft { - FileTime::Now => libc::timespec { - tv_sec: 0, - tv_nsec: libc::UTIME_NOW, - }, - FileTime::Omit => libc::timespec { - tv_sec: 0, - tv_nsec: libc::UTIME_OMIT, - }, - FileTime::FileTime(ft) => libc::timespec { - tv_sec: ft.seconds_()?, - tv_nsec: ft.nanoseconds_(), - }, - }; - Ok(ts) -} diff --git a/crates/wasi-common/yanix/src/lib.rs b/crates/wasi-common/yanix/src/lib.rs deleted file mode 100644 index 141ac0281a..0000000000 --- a/crates/wasi-common/yanix/src/lib.rs +++ /dev/null @@ -1,89 +0,0 @@ -//! `yanix` stands for Yet Another Nix crate, and, well, it is simply -//! a yet another crate in the spirit of the [nix] crate. As such, -//! this crate is inspired by the original `nix` crate, however, -//! it takes a different approach, using lower-level interfaces with -//! less abstraction, so that it fits better with its main use case -//! which is our WASI implementation, [wasi-common]. -//! -//! [nix]: https://github.com/nix-rust/nix -//! [wasi-common]: https://github.com/bytecodealliance/wasmtime/tree/main/crates/wasi-common -#![cfg(any(unix, target_os = "wasi"))] - -#[cfg(not(target_os = "wasi"))] // not implemented for WASI in yanix yet -pub mod clock; -pub mod dir; -pub mod fcntl; -pub mod file; -pub mod filetime; -#[cfg(not(target_os = "wasi"))] // not implemented for WASI in yanix yet -pub mod poll; -#[cfg(not(target_os = "wasi"))] // not supported in WASI yet -pub mod socket; - -mod sys; - -pub mod fadvise { - pub use super::sys::fadvise::*; -} - -use std::ffi::CString; -use std::io::{Error, Result}; -use std::path::Path; - -fn from_success_code(t: T) -> Result<()> { - if t.is_zero() { - Ok(()) - } else { - Err(Error::last_os_error()) - } -} - -fn from_result(t: T) -> Result { - if t.is_minus_one() { - Err(Error::last_os_error()) - } else { - Ok(t) - } -} - -trait IsZero { - fn is_zero(&self) -> bool; -} - -macro_rules! impl_is_zero { - ($($t:ident)*) => ($(impl IsZero for $t { - fn is_zero(&self) -> bool { - *self == 0 - } - })*) -} - -impl_is_zero! { i32 i64 isize } - -trait IsMinusOne { - fn is_minus_one(&self) -> bool; -} - -macro_rules! impl_is_minus_one { - ($($t:ident)*) => ($(impl IsMinusOne for $t { - fn is_minus_one(&self) -> bool { - *self == -1 - } - })*) -} - -impl_is_minus_one! { i32 i64 isize } - -/// Convert an `AsRef` into a `CString`. -fn cstr>(path: P) -> Result { - #[cfg(target_os = "hermit")] - use std::os::hermit::ext::ffi::OsStrExt; - #[cfg(unix)] - use std::os::unix::ffi::OsStrExt; - #[cfg(target_os = "vxworks")] - use std::os::vxworks::ext::ffi::OsStrExt; - #[cfg(target_os = "wasi")] - use std::os::wasi::ffi::OsStrExt; - - Ok(CString::new(path.as_ref().as_os_str().as_bytes())?) -} diff --git a/crates/wasi-common/yanix/src/poll.rs b/crates/wasi-common/yanix/src/poll.rs deleted file mode 100644 index 838c7ad331..0000000000 --- a/crates/wasi-common/yanix/src/poll.rs +++ /dev/null @@ -1,49 +0,0 @@ -use crate::from_result; -use bitflags::bitflags; -use std::{convert::TryInto, io::Result, os::unix::prelude::*}; - -bitflags! { - pub struct PollFlags: libc::c_short { - const POLLIN = libc::POLLIN; - const POLLPRI = libc::POLLPRI; - const POLLOUT = libc::POLLOUT; - const POLLRDNORM = libc::POLLRDNORM; - const POLLWRNORM = libc::POLLWRNORM; - const POLLRDBAND = libc::POLLRDBAND; - const POLLWRBAND = libc::POLLWRBAND; - const POLLERR = libc::POLLERR; - const POLLHUP = libc::POLLHUP; - const POLLNVAL = libc::POLLNVAL; - } -} - -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -#[repr(C)] -pub struct PollFd(libc::pollfd); - -impl PollFd { - pub unsafe fn new(fd: RawFd, events: PollFlags) -> Self { - Self(libc::pollfd { - fd, - events: events.bits(), - revents: PollFlags::empty().bits(), - }) - } - - pub fn revents(self) -> Option { - PollFlags::from_bits(self.0.revents) - } -} - -pub fn poll(fds: &mut [PollFd], timeout: libc::c_int) -> Result { - let nready = from_result(unsafe { - libc::poll( - fds.as_mut_ptr() as *mut libc::pollfd, - fds.len() as libc::nfds_t, - timeout, - ) - })?; - // When poll doesn't fail, its return value is a non-negative int, which will - // always be convertable to usize, so we can unwrap() here. - Ok(nready.try_into().unwrap()) -} diff --git a/crates/wasi-common/yanix/src/socket.rs b/crates/wasi-common/yanix/src/socket.rs deleted file mode 100644 index f5de22decd..0000000000 --- a/crates/wasi-common/yanix/src/socket.rs +++ /dev/null @@ -1,32 +0,0 @@ -use crate::from_success_code; -use std::io::Result; -use std::os::unix::prelude::*; - -#[derive(Debug, Clone, Copy)] -#[repr(i32)] -pub enum SockType { - Stream = libc::SOCK_STREAM, - Datagram = libc::SOCK_DGRAM, - SeqPacket = libc::SOCK_SEQPACKET, - Raw = libc::SOCK_RAW, - Rdm = libc::SOCK_RDM, -} - -pub unsafe fn get_socket_type(fd: RawFd) -> Result { - use std::mem::{self, MaybeUninit}; - let mut buffer = MaybeUninit::::zeroed().assume_init(); - let mut out_len = mem::size_of::() as libc::socklen_t; - from_success_code(libc::getsockopt( - fd, - libc::SOL_SOCKET, - libc::SO_TYPE, - &mut buffer as *mut SockType as *mut _, - &mut out_len, - ))?; - assert_eq!( - out_len as usize, - mem::size_of::(), - "invalid SockType value" - ); - Ok(buffer) -} diff --git a/crates/wasi-common/yanix/src/sys/bsd/dir.rs b/crates/wasi-common/yanix/src/sys/bsd/dir.rs deleted file mode 100644 index 8a253a05f6..0000000000 --- a/crates/wasi-common/yanix/src/sys/bsd/dir.rs +++ /dev/null @@ -1,59 +0,0 @@ -use crate::dir::{Dir, Entry, EntryExt, SeekLoc}; -use std::{ - io::{Error, Result}, - ops::Deref, -}; - -#[derive(Copy, Clone, Debug)] -pub(crate) struct EntryImpl { - dirent: libc::dirent, - loc: SeekLoc, -} - -impl Deref for EntryImpl { - type Target = libc::dirent; - - fn deref(&self) -> &Self::Target { - &self.dirent - } -} - -pub(crate) fn iter_impl(dir: &Dir) -> Option> { - let errno = Error::last_os_error(); - let dirent = unsafe { libc::readdir(dir.as_raw().as_ptr()) }; - if dirent.is_null() { - let curr_errno = Error::last_os_error(); - if errno.raw_os_error() != curr_errno.raw_os_error() { - // TODO This should be verified on different BSD-flavours. - // - // According to 4.3BSD/POSIX.1-2001 man pages, there was an error - // if the errno value has changed at some point during the sequence - // of readdir calls. - Some(Err(curr_errno)) - } else { - // Not an error. We've simply reached the end of the stream. - None - } - } else { - Some(Ok(EntryImpl { - dirent: unsafe { *dirent }, - loc: dir.tell(), - })) - } -} - -impl EntryExt for Entry { - #[cfg(target_os = "freebsd")] - fn ino(&self) -> u64 { - self.0.d_fileno.into() - } - - #[cfg(not(target_os = "freebsd"))] - fn ino(&self) -> u64 { - self.0.d_ino.into() - } - - fn seek_loc(&self) -> Result { - Ok(self.0.loc) - } -} diff --git a/crates/wasi-common/yanix/src/sys/bsd/fadvise.rs b/crates/wasi-common/yanix/src/sys/bsd/fadvise.rs deleted file mode 100644 index 32d290a973..0000000000 --- a/crates/wasi-common/yanix/src/sys/bsd/fadvise.rs +++ /dev/null @@ -1,91 +0,0 @@ -use crate::from_success_code; -use std::{convert::TryInto, io::Result, os::unix::prelude::*}; - -#[cfg(not(any(target_os = "freebsd", target_os = "netbsd")))] -#[derive(Debug, Copy, Clone)] -#[repr(i32)] -pub enum PosixFadviseAdvice { - Normal, - Sequential, - Random, - NoReuse, - WillNeed, - DontNeed, -} - -#[cfg(any(target_os = "freebsd", target_os = "netbsd"))] -#[derive(Debug, Copy, Clone)] -#[repr(i32)] -pub enum PosixFadviseAdvice { - Normal = libc::POSIX_FADV_NORMAL, - Sequential = libc::POSIX_FADV_SEQUENTIAL, - Random = libc::POSIX_FADV_RANDOM, - NoReuse = libc::POSIX_FADV_NOREUSE, - WillNeed = libc::POSIX_FADV_WILLNEED, - DontNeed = libc::POSIX_FADV_DONTNEED, -} - -// There's no posix_fadvise on macOS but we can use fcntl with F_RDADVISE -// command instead to achieve the same -#[cfg(any(target_os = "macos", target_os = "ios"))] -pub unsafe fn posix_fadvise( - fd: RawFd, - offset: libc::off_t, - len: libc::off_t, - _advice: PosixFadviseAdvice, -) -> Result<()> { - // From macOS man pages: - // F_RDADVISE Issue an advisory read async with no copy to user. - // - // The F_RDADVISE command operates on the following structure which holds information passed from - // the user to the system: - // - // struct radvisory { - // off_t ra_offset; /* offset into the file */ - // int ra_count; /* size of the read */ - // }; - let ra_count = match len.try_into() { - Ok(ra_count) => ra_count, - Err(_) => { - // This conversion can fail, because it's converting into int. But in that case, the user - // is providing a dubiously large hint. This is not confirmed (no helpful info in the man - // pages), but offhand, a 2+ GiB advisory read async seems unlikely to help with any kind - // of performance, so we log and exit early with a no-op. - tracing::warn!( - "`len` too big to fit in the host's command. Returning early with no-op!" - ); - return Ok(()); - } - }; - let advisory = libc::radvisory { - ra_offset: offset, - ra_count, - }; - from_success_code(libc::fcntl(fd, libc::F_RDADVISE, &advisory)) -} - -#[cfg(any(target_os = "freebsd", target_os = "netbsd"))] -pub unsafe fn posix_fadvise( - fd: RawFd, - offset: libc::off_t, - len: libc::off_t, - advice: PosixFadviseAdvice, -) -> Result<()> { - from_success_code(libc::posix_fadvise(fd, offset, len, advice as libc::c_int)) -} - -// On BSDs without support we leave it as no-op -#[cfg(not(any( - target_os = "macos", - target_os = "ios", - target_os = "freebsd", - target_os = "netbsd" -)))] -pub unsafe fn posix_fadvise( - _fd: RawFd, - _offset: libc::off_t, - _len: libc::off_t, - _advice: PosixFadviseAdvice, -) -> Result<()> { - Ok(()) -} diff --git a/crates/wasi-common/yanix/src/sys/bsd/file.rs b/crates/wasi-common/yanix/src/sys/bsd/file.rs deleted file mode 100644 index 142c51aa1c..0000000000 --- a/crates/wasi-common/yanix/src/sys/bsd/file.rs +++ /dev/null @@ -1,20 +0,0 @@ -use std::{ - io::{Error, Result}, - os::unix::prelude::*, -}; - -pub unsafe fn isatty(fd: RawFd) -> Result { - let res = libc::isatty(fd); - if res == 1 { - // isatty() returns 1 if fd is an open file descriptor referring to a terminal... - Ok(true) - } else { - // ... otherwise 0 is returned, and errno is set to indicate the error. - let errno = Error::last_os_error(); - if errno.raw_os_error().unwrap() == libc::ENOTTY { - Ok(false) - } else { - Err(errno) - } - } -} diff --git a/crates/wasi-common/yanix/src/sys/bsd/filetime.rs b/crates/wasi-common/yanix/src/sys/bsd/filetime.rs deleted file mode 100644 index d0c62fc486..0000000000 --- a/crates/wasi-common/yanix/src/sys/bsd/filetime.rs +++ /dev/null @@ -1,77 +0,0 @@ -//! This module consists of helper types and functions for dealing -//! with setting the file times specific to BSD-style *nixes. -use crate::filetime::FileTime; -use crate::{cstr, from_success_code}; -use std::ffi::CStr; -use std::fs::File; -use std::io::Result; -use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; - -/// Wrapper for `utimensat` syscall, however, with an added twist such that `utimensat` symbol -/// is firstly resolved (i.e., we check whether it exists on the host), and only used if that is -/// the case. Otherwise, the syscall resorts to a less accurate `utimesat` emulated syscall. -/// The original implementation can be found here: [filetime::unix::macos::set_times] -/// -/// [filetime::unix::macos::set_times]: https://github.com/alexcrichton/filetime/blob/master/src/unix/macos.rs#L49 -pub fn utimensat( - dirfd: &File, - path: &str, - atime: FileTime, - mtime: FileTime, - symlink_nofollow: bool, -) -> Result<()> { - use crate::filetime::to_timespec; - use std::os::unix::prelude::*; - - // Attempt to use the `utimensat` syscall, but if it's not supported by the - // current kernel then fall back to an older syscall. - if let Some(func) = fetch_utimensat() { - let flags = if symlink_nofollow { - libc::AT_SYMLINK_NOFOLLOW - } else { - 0 - }; - - let path = cstr(path)?; - let times = [to_timespec(&atime)?, to_timespec(&mtime)?]; - return from_success_code(unsafe { - func(dirfd.as_raw_fd(), path.as_ptr(), times.as_ptr(), flags) - }); - } - - super::utimesat::utimesat(dirfd, path, atime, mtime, symlink_nofollow) -} - -/// Wraps `fetch` specifically targetting `utimensat` symbol. If the symbol exists -/// on the host, then returns an `Some(unsafe fn)`. -fn fetch_utimensat() -> Option< - unsafe extern "C" fn( - libc::c_int, - *const libc::c_char, - *const libc::timespec, - libc::c_int, - ) -> libc::c_int, -> { - static ADDR: AtomicUsize = AtomicUsize::new(0); - unsafe { - fetch(&ADDR, CStr::from_bytes_with_nul_unchecked(b"utimensat\0")) - .map(|sym| std::mem::transmute(sym)) - } -} - -/// Fetches a symbol by `name` and stores it in `cache`. -fn fetch(cache: &AtomicUsize, name: &CStr) -> Option { - match cache.load(SeqCst) { - 0 => {} - 1 => return None, - n => return Some(n), - } - let sym = unsafe { libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr() as *const _) }; - let (val, ret) = if sym.is_null() { - (1, None) - } else { - (sym as usize, Some(sym as usize)) - }; - cache.store(val, SeqCst); - return ret; -} diff --git a/crates/wasi-common/yanix/src/sys/bsd/mod.rs b/crates/wasi-common/yanix/src/sys/bsd/mod.rs deleted file mode 100644 index a8f4d43631..0000000000 --- a/crates/wasi-common/yanix/src/sys/bsd/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub(crate) mod dir; -pub(crate) mod fadvise; -pub(crate) mod file; -pub(crate) mod filetime; -#[path = "../linux/utimesat.rs"] -pub(crate) mod utimesat; diff --git a/crates/wasi-common/yanix/src/sys/emscripten/filetime.rs b/crates/wasi-common/yanix/src/sys/emscripten/filetime.rs deleted file mode 100644 index 771247896a..0000000000 --- a/crates/wasi-common/yanix/src/sys/emscripten/filetime.rs +++ /dev/null @@ -1,30 +0,0 @@ -//! This module consists of helper types and functions for dealing -//! with setting the file times specific to Emscripten. -use crate::filetime::FileTime; -use crate::{cstr, from_success_code}; -use std::fs::File; -use std::io::Result; - -/// Wrapper for `utimensat` syscall. In Emscripten, there is no point in dynamically resolving -/// if `utimensat` is available as it always was and will be. -pub fn utimensat( - dirfd: &File, - path: &str, - atime: FileTime, - mtime: FileTime, - symlink_nofollow: bool, -) -> Result<()> { - use crate::filetime::to_timespec; - use std::os::unix::prelude::*; - - let flags = if symlink_nofollow { - libc::AT_SYMLINK_NOFOLLOW - } else { - 0 - }; - let path = cstr(path)?; - let times = [to_timespec(&atime)?, to_timespec(&mtime)?]; - from_success_code(unsafe { - libc::utimensat(dirfd.as_raw_fd(), path.as_ptr(), times.as_ptr(), flags) - }) -} diff --git a/crates/wasi-common/yanix/src/sys/emscripten/mod.rs b/crates/wasi-common/yanix/src/sys/emscripten/mod.rs deleted file mode 100644 index a79cf82b9f..0000000000 --- a/crates/wasi-common/yanix/src/sys/emscripten/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -#[path = "../linux/dir.rs"] -pub(crate) mod dir; -#[path = "../linux/fadvise.rs"] -pub(crate) mod fadvise; -#[path = "../linux/file.rs"] -pub(crate) mod file; -pub(crate) mod filetime; diff --git a/crates/wasi-common/yanix/src/sys/linux/dir.rs b/crates/wasi-common/yanix/src/sys/linux/dir.rs deleted file mode 100644 index d7f25f43f0..0000000000 --- a/crates/wasi-common/yanix/src/sys/linux/dir.rs +++ /dev/null @@ -1,47 +0,0 @@ -use crate::dir::{Dir, Entry, EntryExt, SeekLoc}; -use std::{ - io::{Error, Result}, - ops::Deref, -}; - -#[derive(Copy, Clone, Debug)] -pub(crate) struct EntryImpl(libc::dirent64); - -impl Deref for EntryImpl { - type Target = libc::dirent64; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl EntryExt for Entry { - fn ino(&self) -> u64 { - self.0.d_ino.into() - } - - fn seek_loc(&self) -> Result { - unsafe { SeekLoc::from_raw(self.0.d_off) } - } -} - -pub(crate) fn iter_impl(dir: &Dir) -> Option> { - let errno = Error::last_os_error(); - let dirent = unsafe { libc::readdir64(dir.as_raw().as_ptr()) }; - if dirent.is_null() { - let curr_errno = Error::last_os_error(); - if errno.raw_os_error() != curr_errno.raw_os_error() { - // TODO This should be verified on different BSD-flavours. - // - // According to 4.3BSD/POSIX.1-2001 man pages, there was an error - // if the errno value has changed at some point during the sequence - // of readdir calls. - Some(Err(curr_errno)) - } else { - // Not an error. We've simply reached the end of the stream. - None - } - } else { - Some(Ok(EntryImpl(unsafe { *dirent }))) - } -} diff --git a/crates/wasi-common/yanix/src/sys/linux/fadvise.rs b/crates/wasi-common/yanix/src/sys/linux/fadvise.rs deleted file mode 100644 index c3e2354862..0000000000 --- a/crates/wasi-common/yanix/src/sys/linux/fadvise.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::from_success_code; -use std::io::Result; -use std::os::unix::prelude::*; - -#[derive(Debug, Copy, Clone)] -#[repr(i32)] -pub enum PosixFadviseAdvice { - Normal = libc::POSIX_FADV_NORMAL, - Sequential = libc::POSIX_FADV_SEQUENTIAL, - Random = libc::POSIX_FADV_RANDOM, - NoReuse = libc::POSIX_FADV_NOREUSE, - WillNeed = libc::POSIX_FADV_WILLNEED, - DontNeed = libc::POSIX_FADV_DONTNEED, -} - -pub unsafe fn posix_fadvise( - fd: RawFd, - offset: libc::off_t, - len: libc::off_t, - advice: PosixFadviseAdvice, -) -> Result<()> { - from_success_code(libc::posix_fadvise(fd, offset, len, advice as libc::c_int)) -} diff --git a/crates/wasi-common/yanix/src/sys/linux/file.rs b/crates/wasi-common/yanix/src/sys/linux/file.rs deleted file mode 100644 index e039f7bb84..0000000000 --- a/crates/wasi-common/yanix/src/sys/linux/file.rs +++ /dev/null @@ -1,26 +0,0 @@ -use std::{ - io::{Error, Result}, - os::unix::prelude::*, -}; - -pub unsafe fn isatty(fd: RawFd) -> Result { - let res = libc::isatty(fd); - if res == 1 { - // isatty() returns 1 if fd is an open file descriptor referring to a terminal... - Ok(true) - } else { - // ... otherwise 0 is returned, and errno is set to indicate the error. - let errno = Error::last_os_error(); - let raw_errno = errno.raw_os_error().unwrap(); - // While POSIX specifies ENOTTY if the passed - // fd is *not* a tty, on Linux, some implementations - // may return EINVAL instead. - // - // https://linux.die.net/man/3/isatty - if raw_errno == libc::ENOTTY || raw_errno == libc::EINVAL { - Ok(false) - } else { - Err(errno) - } - } -} diff --git a/crates/wasi-common/yanix/src/sys/linux/filetime.rs b/crates/wasi-common/yanix/src/sys/linux/filetime.rs deleted file mode 100644 index 34faefc6d6..0000000000 --- a/crates/wasi-common/yanix/src/sys/linux/filetime.rs +++ /dev/null @@ -1,60 +0,0 @@ -//! This module consists of helper types and functions for dealing -//! with setting the file times specific to Linux. -use crate::filetime::FileTime; -use crate::{cstr, from_success_code}; -use std::fs::File; -use std::io::Result; -use std::sync::atomic::{AtomicBool, Ordering::Relaxed}; - -/// Wrapper for `utimensat` syscall, however, with an added twist such that `utimensat` symbol -/// is firstly resolved (i.e., we check whether it exists on the host), and only used if that is -/// the case. Otherwise, the syscall resorts to a less accurate `utimesat` emulated syscall. -/// The original implementation can be found here: [filetime::unix::linux::set_times] -/// -/// [filetime::unix::linux::set_times]: https://github.com/alexcrichton/filetime/blob/master/src/unix/linux.rs#L64 -pub fn utimensat( - dirfd: &File, - path: &str, - atime: FileTime, - mtime: FileTime, - symlink_nofollow: bool, -) -> Result<()> { - use crate::filetime::to_timespec; - use std::os::unix::prelude::*; - - let flags = if symlink_nofollow { - libc::AT_SYMLINK_NOFOLLOW - } else { - 0 - }; - - // Attempt to use the `utimensat` syscall, but if it's not supported by the - // current kernel then fall back to an older syscall. - static INVALID: AtomicBool = AtomicBool::new(false); - if !INVALID.load(Relaxed) { - let path = cstr(path)?; - let times = [to_timespec(&atime)?, to_timespec(&mtime)?]; - let res = from_success_code(unsafe { - libc::syscall( - libc::SYS_utimensat, - dirfd.as_raw_fd(), - path.as_ptr(), - times.as_ptr(), - flags, - ) - }); - let err = match res { - Ok(()) => return Ok(()), - Err(e) => e, - }; - if err.raw_os_error().unwrap() == libc::ENOSYS { - INVALID.store(true, Relaxed); - } - return Err(err); - } - - #[cfg(not(target_os = "android"))] - return super::utimesat::utimesat(dirfd, path, atime, mtime, symlink_nofollow); - #[cfg(target_os = "android")] - unreachable!(); -} diff --git a/crates/wasi-common/yanix/src/sys/linux/mod.rs b/crates/wasi-common/yanix/src/sys/linux/mod.rs deleted file mode 100644 index 37d8a0a8af..0000000000 --- a/crates/wasi-common/yanix/src/sys/linux/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub(crate) mod dir; -pub(crate) mod fadvise; -pub(crate) mod file; -pub(crate) mod filetime; -#[cfg(not(target_os = "android"))] -pub(crate) mod utimesat; diff --git a/crates/wasi-common/yanix/src/sys/linux/utimesat.rs b/crates/wasi-common/yanix/src/sys/linux/utimesat.rs deleted file mode 100644 index 3ed9b5c68c..0000000000 --- a/crates/wasi-common/yanix/src/sys/linux/utimesat.rs +++ /dev/null @@ -1,81 +0,0 @@ -use crate::filetime::FileTime; -use crate::filetime::FileTimeExt; -use crate::{cstr, from_success_code}; -use std::fs; -use std::io::Result; - -/// Combines `openat` with `utimes` to emulate `utimensat` on platforms where it is -/// not available. The logic for setting file times is based on [filetime::unix::set_file_handles_times]. -/// -/// [filetime::unix::set_file_handles_times]: https://github.com/alexcrichton/filetime/blob/master/src/unix/utimes.rs#L24 -pub fn utimesat( - dirfd: &fs::File, - path: &str, - atime: FileTime, - mtime: FileTime, - symlink_nofollow: bool, -) -> Result<()> { - use std::os::unix::prelude::*; - // emulate *at syscall by reading the path from a combination of - // (fd, path) - let path = cstr(path)?; - let mut flags = libc::O_RDWR; - if symlink_nofollow { - flags |= libc::O_NOFOLLOW; - } - let fd = unsafe { libc::openat(dirfd.as_raw_fd(), path.as_ptr(), flags) }; - let f = unsafe { fs::File::from_raw_fd(fd) }; - let (atime, mtime) = get_times(atime, mtime, || f.metadata().map_err(Into::into))?; - let times = [to_timeval(atime)?, to_timeval(mtime)?]; - from_success_code(unsafe { libc::futimes(f.as_raw_fd(), times.as_ptr()) }) -} - -/// Converts `filetime::FileTime` to `libc::timeval`. -fn to_timeval(ft: filetime::FileTime) -> Result { - Ok(libc::timeval { - tv_sec: ft.seconds_()?, - tv_usec: (ft.nanoseconds_() / 1000) as libc::suseconds_t, - }) -} - -/// For a provided pair of access and modified `FileTime`s, converts the input to -/// `filetime::FileTime` used later in `utimensat` function. For variants `FileTime::Now` -/// and `FileTime::Omit`, this function will make two syscalls: either accessing current -/// system time, or accessing the file's metadata. -/// -/// The original implementation can be found here: [filetime::unix::get_times]. -/// -/// [filetime::unix::get_times]: https://github.com/alexcrichton/filetime/blob/master/src/unix/utimes.rs#L42 -fn get_times( - atime: FileTime, - mtime: FileTime, - current: impl Fn() -> Result, -) -> Result<(filetime::FileTime, filetime::FileTime)> { - use std::time::SystemTime; - - let atime = match atime { - FileTime::Now => { - let time = SystemTime::now(); - filetime::FileTime::from_system_time(time) - } - FileTime::Omit => { - let meta = current()?; - filetime::FileTime::from_last_access_time(&meta) - } - FileTime::FileTime(ft) => ft, - }; - - let mtime = match mtime { - FileTime::Now => { - let time = SystemTime::now(); - filetime::FileTime::from_system_time(time) - } - FileTime::Omit => { - let meta = current()?; - filetime::FileTime::from_last_modification_time(&meta) - } - FileTime::FileTime(ft) => ft, - }; - - Ok((atime, mtime)) -} diff --git a/crates/wasi-common/yanix/src/sys/mod.rs b/crates/wasi-common/yanix/src/sys/mod.rs deleted file mode 100644 index c189768532..0000000000 --- a/crates/wasi-common/yanix/src/sys/mod.rs +++ /dev/null @@ -1,32 +0,0 @@ -use crate::dir::SeekLoc; -use cfg_if::cfg_if; -use std::io::Result; - -cfg_if! { - if #[cfg(any(target_os = "linux", - target_os = "android"))] { - mod linux; - pub(crate) use linux::*; - } else if #[cfg(target_os = "emscripten")] { - mod emscripten; - pub(crate) use emscripten::*; - } else if #[cfg(any(target_os = "macos", - target_os = "ios", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - target_os = "dragonfly"))] { - mod bsd; - pub(crate) use bsd::*; - } else if #[cfg(target_os = "wasi")] { - mod wasi; - pub(crate) use wasi::*; - } else { - compile_error!("yanix doesn't compile for this platform yet"); - } -} - -pub trait EntryExt { - fn ino(&self) -> u64; - fn seek_loc(&self) -> Result; -} diff --git a/crates/wasi-common/yanix/src/sys/wasi/fadvise.rs b/crates/wasi-common/yanix/src/sys/wasi/fadvise.rs deleted file mode 100644 index 4c8d0fc4ad..0000000000 --- a/crates/wasi-common/yanix/src/sys/wasi/fadvise.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::from_success_code; -use std::io::Result; -use std::os::wasi::prelude::*; - -#[derive(Debug, Copy, Clone)] -#[repr(i32)] -pub enum PosixFadviseAdvice { - Normal = libc::POSIX_FADV_NORMAL, - Sequential = libc::POSIX_FADV_SEQUENTIAL, - Random = libc::POSIX_FADV_RANDOM, - NoReuse = libc::POSIX_FADV_NOREUSE, - WillNeed = libc::POSIX_FADV_WILLNEED, - DontNeed = libc::POSIX_FADV_DONTNEED, -} - -pub unsafe fn posix_fadvise( - fd: RawFd, - offset: libc::off_t, - len: libc::off_t, - advice: PosixFadviseAdvice, -) -> Result<()> { - from_success_code(libc::posix_fadvise(fd, offset, len, advice as libc::c_int)) -} diff --git a/crates/wasi-common/yanix/src/sys/wasi/file.rs b/crates/wasi-common/yanix/src/sys/wasi/file.rs deleted file mode 100644 index 4753d72f42..0000000000 --- a/crates/wasi-common/yanix/src/sys/wasi/file.rs +++ /dev/null @@ -1,21 +0,0 @@ -use std::{ - io::{Error, Result}, - os::wasi::prelude::*, -}; - -pub unsafe fn isatty(fd: RawFd) -> Result { - let res = libc::isatty(fd); - if res == 1 { - // isatty() returns 1 if fd is an open file descriptor referring to a terminal... - Ok(true) - } else { - // ... otherwise 0 is returned, and errno is set to indicate the error. - let errno = Error::last_os_error(); - let raw_errno = errno.raw_os_error().unwrap(); - if raw_errno == libc::ENOTTY { - Ok(false) - } else { - Err(errno) - } - } -} diff --git a/crates/wasi-common/yanix/src/sys/wasi/filetime.rs b/crates/wasi-common/yanix/src/sys/wasi/filetime.rs deleted file mode 100644 index 7cd8a4483c..0000000000 --- a/crates/wasi-common/yanix/src/sys/wasi/filetime.rs +++ /dev/null @@ -1,33 +0,0 @@ -use crate::filetime::FileTime; -use crate::from_success_code; -use std::fs::File; -use std::io; - -pub fn utimensat( - dirfd: &File, - path: &str, - atime: FileTime, - mtime: FileTime, - symlink_nofollow: bool, -) -> io::Result<()> { - use crate::filetime::to_timespec; - use std::ffi::CString; - use std::os::wasi::prelude::*; - - let p = CString::new(path.as_bytes())?; - let times = [to_timespec(&atime)?, to_timespec(&mtime)?]; - let flags = if symlink_nofollow { - libc::AT_SYMLINK_NOFOLLOW - } else { - 0 - }; - - from_success_code(unsafe { - libc::utimensat( - dirfd.as_raw_fd() as libc::c_int, - p.as_ptr(), - times.as_ptr(), - flags, - ) - }) -} diff --git a/crates/wasi-common/yanix/src/sys/wasi/mod.rs b/crates/wasi-common/yanix/src/sys/wasi/mod.rs deleted file mode 100644 index 104da5e592..0000000000 --- a/crates/wasi-common/yanix/src/sys/wasi/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub(crate) mod fadvise; -pub(crate) mod file; -pub(crate) mod filetime;