diff --git a/Cargo.lock b/Cargo.lock index 1a11542bcb9..f36ce7f52f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -246,6 +246,12 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "bytes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" + [[package]] name = "bytesize" version = "1.1.0" @@ -307,6 +313,20 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "js-sys", + "libc", + "num-integer", + "num-traits", + "wasm-bindgen", + "winapi", +] + [[package]] name = "clang-sys" version = "1.3.3" @@ -665,6 +685,17 @@ dependencies = [ "syn", ] +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "derive_arbitrary" version = "1.1.0" @@ -1387,6 +1418,16 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.15" @@ -3133,6 +3174,19 @@ dependencies = [ "thiserror", ] +[[package]] +name = "wasmer-vbus" +version = "2.3.0" +dependencies = [ + "libc", + "serde", + "slab", + "thiserror", + "tracing", + "typetag", + "wasmer-vfs", +] + [[package]] name = "wasmer-vfs" version = "2.3.0" @@ -3170,12 +3224,29 @@ dependencies = [ "winapi", ] +[[package]] +name = "wasmer-vnet" +version = "2.3.0" +dependencies = [ + "bytes", + "libc", + "serde", + "slab", + "thiserror", + "tracing", + "typetag", + "wasmer-vfs", +] + [[package]] name = "wasmer-wasi" version = "2.3.0" dependencies = [ "bincode", + "bytes", "cfg-if 1.0.0", + "chrono", + "derivative", "generational-arena", "getrandom", "libc", @@ -3187,7 +3258,10 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-test", "wasmer", + "wasmer-vbus", "wasmer-vfs", + "wasmer-vnet", + "wasmer-wasi-local-networking", "wasmer-wasi-types", "winapi", ] @@ -3205,6 +3279,16 @@ dependencies = [ "wasmer-wasi", ] +[[package]] +name = "wasmer-wasi-local-networking" +version = "2.3.0" +dependencies = [ + "bytes", + "tracing", + "wasmer-vfs", + "wasmer-vnet", +] + [[package]] name = "wasmer-wasi-types" version = "2.3.0" diff --git a/Cargo.toml b/Cargo.toml index 12e507d0967..d086b027bb7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,10 +47,13 @@ members = [ "lib/engine-staticlib", "lib/object", "lib/vfs", + "lib/vnet", + "lib/vbus", "lib/vm", "lib/wasi", "lib/wasi-types", "lib/wasi-experimental-io-devices", + "lib/wasi-local-networking", "lib/types", "tests/wasi-wast", "tests/lib/wast", diff --git a/benches/static_and_dynamic_functions.rs b/benches/static_and_dynamic_functions.rs index c809140dd52..f798d9a3e57 100644 --- a/benches/static_and_dynamic_functions.rs +++ b/benches/static_and_dynamic_functions.rs @@ -32,10 +32,10 @@ static BASIC_WAT: &str = r#"(module )"#; pub fn run_basic_static_function(store: &Store, compiler_name: &str, c: &mut Criterion) { - let module = Module::new(&store, BASIC_WAT).unwrap(); + let module = Module::new(store, BASIC_WAT).unwrap(); let import_object = imports! { "env" => { - "multiply" => Function::new_native(&store, |a: i32, b: i32| a * b), + "multiply" => Function::new_native(store, |a: i32, b: i32| a * b), }, }; let instance = Instance::new(&module, &import_object).unwrap(); @@ -93,10 +93,10 @@ pub fn run_basic_static_function(store: &Store, compiler_name: &str, c: &mut Cri } pub fn run_basic_dynamic_function(store: &Store, compiler_name: &str, c: &mut Criterion) { - let module = Module::new(&store, BASIC_WAT).unwrap(); + let module = Module::new(store, BASIC_WAT).unwrap(); let import_object = imports! { "env" => { - "multiply" => Function::new_native(&store, |a: i32, b: i32| a * b), + "multiply" => Function::new_native(store, |a: i32, b: i32| a * b), }, }; let instance = Instance::new(&module, &import_object).unwrap(); diff --git a/examples/wasi_pipes.rs b/examples/wasi_pipes.rs index b1d44e119b4..fa4cad6973b 100644 --- a/examples/wasi_pipes.rs +++ b/examples/wasi_pipes.rs @@ -11,6 +11,7 @@ //! //! Ready? +use std::io::{Read, Write}; use wasmer::{Instance, Module, Store}; use wasmer_compiler_cranelift::Cranelift; use wasmer_engine_universal::Universal; @@ -36,11 +37,11 @@ fn main() -> Result<(), Box> { println!("Creating `WasiEnv`..."); // First, we create the `WasiEnv` with the stdio pipes - let input = Pipe::new(); - let output = Pipe::new(); + let mut input = Pipe::new(); + let mut output = Pipe::new(); let mut wasi_env = WasiState::new("hello") - .stdin(Box::new(input)) - .stdout(Box::new(output)) + .stdin(Box::new(input.clone())) + .stdout(Box::new(output.clone())) .finalize()?; println!("Instantiating module with WASI imports..."); @@ -51,16 +52,8 @@ fn main() -> Result<(), Box> { let msg = "racecar go zoom"; println!("Writing \"{}\" to the WASI stdin...", msg); - // To write to the stdin, we need a mutable reference to the pipe - // - // We access WasiState in a nested scope to ensure we're not holding - // the mutex after we need it. - { - let mut state = wasi_env.state(); - let wasi_stdin = state.fs.stdin_mut()?.as_mut().unwrap(); - // Then we can write to it! - writeln!(wasi_stdin, "{}", msg)?; - } + // To write to the stdin + writeln!(input, "{}", msg)?; println!("Call WASI `_start` function..."); // And we just call the `_start` function! @@ -68,12 +61,10 @@ fn main() -> Result<(), Box> { start.call(&[])?; println!("Reading from the WASI stdout..."); - // To read from the stdout, we again need a mutable reference to the pipe - let mut state = wasi_env.state(); - let wasi_stdout = state.fs.stdout_mut()?.as_mut().unwrap(); - // Then we can read from it! + + // To read from the stdout let mut buf = String::new(); - wasi_stdout.read_to_string(&mut buf)?; + output.read_to_string(&mut buf)?; println!("Read \"{}\" from the WASI stdout!", buf.trim()); Ok(()) diff --git a/lib/api/src/js/externals/memory.rs b/lib/api/src/js/externals/memory.rs index d7c9460d3a1..2de3d044db4 100644 --- a/lib/api/src/js/externals/memory.rs +++ b/lib/api/src/js/externals/memory.rs @@ -240,7 +240,7 @@ impl Memory { /// Used by tests #[doc(hidden)] pub fn uint8view(&self) -> js_sys::Uint8Array { - self.view.clone() + js_sys::Uint8Array::new(&self.vm_memory.memory.buffer()) } pub(crate) fn from_vm_export(store: &Store, vm_memory: VMMemory) -> Self { @@ -276,16 +276,17 @@ impl Memory { /// This method is guaranteed to be safe (from the host side) in the face of /// concurrent writes. pub fn read(&self, offset: u64, buf: &mut [u8]) -> Result<(), MemoryAccessError> { + let view = self.uint8view(); let offset: u32 = offset.try_into().map_err(|_| MemoryAccessError::Overflow)?; let len: u32 = buf .len() .try_into() .map_err(|_| MemoryAccessError::Overflow)?; let end = offset.checked_add(len).ok_or(MemoryAccessError::Overflow)?; - if end > self.view.length() { + if end > view.length() { Err(MemoryAccessError::HeapOutOfBounds)?; } - self.view.subarray(offset, end).copy_to(buf); + view.subarray(offset, end).copy_to(buf); Ok(()) } @@ -304,13 +305,14 @@ impl Memory { offset: u64, buf: &'a mut [MaybeUninit], ) -> Result<&'a mut [u8], MemoryAccessError> { + let view = self.uint8view(); let offset: u32 = offset.try_into().map_err(|_| MemoryAccessError::Overflow)?; let len: u32 = buf .len() .try_into() .map_err(|_| MemoryAccessError::Overflow)?; let end = offset.checked_add(len).ok_or(MemoryAccessError::Overflow)?; - if end > self.view.length() { + if end > view.length() { Err(MemoryAccessError::HeapOutOfBounds)?; } @@ -321,7 +323,7 @@ impl Memory { } let buf = unsafe { slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut u8, buf.len()) }; - self.view.subarray(offset, end).copy_to(buf); + view.subarray(offset, end).copy_to(buf); Ok(buf) } @@ -333,16 +335,18 @@ impl Memory { /// This method is guaranteed to be safe (from the host side) in the face of /// concurrent reads/writes. pub fn write(&self, offset: u64, data: &[u8]) -> Result<(), MemoryAccessError> { + let view = self.uint8view(); let offset: u32 = offset.try_into().map_err(|_| MemoryAccessError::Overflow)?; let len: u32 = data .len() .try_into() .map_err(|_| MemoryAccessError::Overflow)?; + let view = self.uint8view(); let end = offset.checked_add(len).ok_or(MemoryAccessError::Overflow)?; - if end > self.view.length() { + if end > view.length() { Err(MemoryAccessError::HeapOutOfBounds)?; } - self.view.subarray(offset, end).copy_from(data); + view.subarray(offset, end).copy_from(data); Ok(()) } } diff --git a/lib/api/src/js/mem_access.rs b/lib/api/src/js/mem_access.rs index 98c98d2a2ba..9c7cbef967c 100644 --- a/lib/api/src/js/mem_access.rs +++ b/lib/api/src/js/mem_access.rs @@ -10,7 +10,7 @@ use std::{ string::FromUtf8Error, }; use thiserror::Error; -use wasmer_types::ValueType; +use wasmer_types::{MemorySize, ValueType}; /// Error for invalid [`Memory`] access. #[derive(Clone, Copy, Debug, Error)] @@ -85,6 +85,17 @@ impl<'a, T: ValueType> WasmRef<'a, T> { WasmPtr::new(self.offset) } + /// Get a `WasmPtr` fror this `WasmRef`. + #[inline] + pub fn as_ptr(self) -> WasmPtr { + let offset: M::Offset = self + .offset + .try_into() + .map_err(|_| "invalid offset into memory") + .unwrap(); + WasmPtr::::new(offset) + } + /// Get a reference to the Wasm memory backing this reference. #[inline] pub fn memory(self) -> &'a Memory { diff --git a/lib/api/src/js/native.rs b/lib/api/src/js/native.rs index 91342b5d229..4d56cb96c30 100644 --- a/lib/api/src/js/native.rs +++ b/lib/api/src/js/native.rs @@ -28,6 +28,7 @@ pub struct TypedFunction { } unsafe impl Send for TypedFunction {} +unsafe impl Sync for TypedFunction {} impl TypedFunction where diff --git a/lib/api/src/js/ptr.rs b/lib/api/src/js/ptr.rs index c3a4a177724..32fb98da9a0 100644 --- a/lib/api/src/js/ptr.rs +++ b/lib/api/src/js/ptr.rs @@ -4,53 +4,11 @@ use std::convert::TryFrom; use std::{fmt, marker::PhantomData, mem}; use wasmer_types::{NativeWasmType, ValueType}; -/// Trait for the `Memory32` and `Memory64` marker types. -/// -/// This allows code to be generic over 32-bit and 64-bit memories. -pub unsafe trait MemorySize { - /// Type used to represent an offset into a memory. This is `u32` or `u64`. - type Offset: Copy + Into + TryFrom; - - /// Type used to pass this value as an argument or return value for a Wasm function. - type Native: NativeWasmType; - - /// Zero value used for `WasmPtr::is_null`. - const ZERO: Self::Offset; - - /// Convert an `Offset` to a `Native`. - fn offset_to_native(offset: Self::Offset) -> Self::Native; - - /// Convert a `Native` to an `Offset`. - fn native_to_offset(native: Self::Native) -> Self::Offset; -} +pub use wasmer_types::MemorySize; -/// Marker trait for 32-bit memories. -pub struct Memory32; -unsafe impl MemorySize for Memory32 { - type Offset = u32; - type Native = i32; - const ZERO: Self::Offset = 0; - fn offset_to_native(offset: Self::Offset) -> Self::Native { - offset as Self::Native - } - fn native_to_offset(native: Self::Native) -> Self::Offset { - native as Self::Offset - } -} +pub use wasmer_types::Memory32; -/// Marker trait for 64-bit memories. -pub struct Memory64; -unsafe impl MemorySize for Memory64 { - type Offset = u64; - type Native = i64; - const ZERO: Self::Offset = 0; - fn offset_to_native(offset: Self::Offset) -> Self::Native { - offset as Self::Native - } - fn native_to_offset(native: Self::Native) -> Self::Offset { - native as Self::Offset - } -} +pub use wasmer_types::Memory64; /// Alias for `WasmPtr. pub type WasmPtr64 = WasmPtr; diff --git a/lib/api/src/sys/imports.rs b/lib/api/src/sys/imports.rs index 405b3985bfe..b1126836fcb 100644 --- a/lib/api/src/sys/imports.rs +++ b/lib/api/src/sys/imports.rs @@ -361,7 +361,7 @@ mod test { "small" => g.clone() }, "cat" => { - "small" => g.clone() + "small" => g } }; diff --git a/lib/api/src/sys/mem_access.rs b/lib/api/src/sys/mem_access.rs index 7a6ba392ebd..00d7f7144b4 100644 --- a/lib/api/src/sys/mem_access.rs +++ b/lib/api/src/sys/mem_access.rs @@ -1,5 +1,5 @@ use crate::RuntimeError; -use crate::{Memory, Memory32, Memory64, WasmPtr}; +use crate::{Memory, Memory32, Memory64, MemorySize, WasmPtr}; use std::{ convert::TryInto, fmt, @@ -85,6 +85,17 @@ impl<'a, T: ValueType> WasmRef<'a, T> { WasmPtr::new(self.offset) } + /// Get a `WasmPtr` fror this `WasmRef`. + #[inline] + pub fn as_ptr(self) -> WasmPtr { + let offset: M::Offset = self + .offset + .try_into() + .map_err(|_| "invalid offset into memory") + .unwrap(); + WasmPtr::::new(offset) + } + /// Get a reference to the Wasm memory backing this reference. #[inline] pub fn memory(self) -> &'a Memory { diff --git a/lib/api/src/sys/ptr.rs b/lib/api/src/sys/ptr.rs index e52216049c8..3ece04f551e 100644 --- a/lib/api/src/sys/ptr.rs +++ b/lib/api/src/sys/ptr.rs @@ -2,59 +2,13 @@ use crate::sys::{externals::Memory, FromToNativeWasmType}; use crate::{MemoryAccessError, WasmRef, WasmSlice}; use std::convert::TryFrom; use std::{fmt, marker::PhantomData, mem}; -use wasmer_types::{NativeWasmType, ValueType}; +use wasmer_types::ValueType; -/// Trait for the `Memory32` and `Memory64` marker types. -/// -/// This allows code to be generic over 32-bit and 64-bit memories. -/// -/// # Safety -/// -/// Used for raw memory access. -pub unsafe trait MemorySize { - /// Type used to represent an offset into a memory. This is `u32` or `u64`. - type Offset: Copy + Into + TryFrom; - - /// Type used to pass this value as an argument or return value for a Wasm function. - type Native: NativeWasmType; - - /// Zero value used for `WasmPtr::is_null`. - const ZERO: Self::Offset; - - /// Convert an `Offset` to a `Native`. - fn offset_to_native(offset: Self::Offset) -> Self::Native; - - /// Convert a `Native` to an `Offset`. - fn native_to_offset(native: Self::Native) -> Self::Offset; -} +pub use wasmer_types::MemorySize; -/// Marker trait for 32-bit memories. -pub struct Memory32; -unsafe impl MemorySize for Memory32 { - type Offset = u32; - type Native = i32; - const ZERO: Self::Offset = 0; - fn offset_to_native(offset: Self::Offset) -> Self::Native { - offset as Self::Native - } - fn native_to_offset(native: Self::Native) -> Self::Offset { - native as Self::Offset - } -} +pub use wasmer_types::Memory32; -/// Marker trait for 64-bit memories. -pub struct Memory64; -unsafe impl MemorySize for Memory64 { - type Offset = u64; - type Native = i64; - const ZERO: Self::Offset = 0; - fn offset_to_native(offset: Self::Offset) -> Self::Native { - offset as Self::Native - } - fn native_to_offset(native: Self::Native) -> Self::Offset { - native as Self::Offset - } -} +pub use wasmer_types::Memory64; /// Alias for `WasmPtr. pub type WasmPtr64 = WasmPtr; diff --git a/lib/api/tests/sys_export.rs b/lib/api/tests/sys_export.rs index 85473da296b..aed5c51f0b8 100644 --- a/lib/api/tests/sys_export.rs +++ b/lib/api/tests/sys_export.rs @@ -114,10 +114,10 @@ mod sys { let host_fn = |env: &MemEnv| { let mem = env.memory_ref().unwrap(); - assert_eq!(is_memory_instance_ref_strong(&mem), Some(false)); + assert_eq!(is_memory_instance_ref_strong(mem), Some(false)); let mem_clone = mem.clone(); assert_eq!(is_memory_instance_ref_strong(&mem_clone), Some(true)); - assert_eq!(is_memory_instance_ref_strong(&mem), Some(false)); + assert_eq!(is_memory_instance_ref_strong(mem), Some(false)); }; let f: TypedFunction<(), ()> = { @@ -136,7 +136,7 @@ mod sys { { let mem = instance.exports.get_memory("memory")?; - assert_eq!(is_memory_instance_ref_strong(&mem), Some(true)); + assert_eq!(is_memory_instance_ref_strong(mem), Some(true)); } let f: TypedFunction<(), ()> = instance.exports.get_native_function("call_host_fn")?; @@ -158,10 +158,10 @@ mod sys { let host_fn = |env: &GlobalEnv| { let global = env.global_ref().unwrap(); - assert_eq!(is_global_instance_ref_strong(&global), Some(false)); + assert_eq!(is_global_instance_ref_strong(global), Some(false)); let global_clone = global.clone(); assert_eq!(is_global_instance_ref_strong(&global_clone), Some(true)); - assert_eq!(is_global_instance_ref_strong(&global), Some(false)); + assert_eq!(is_global_instance_ref_strong(global), Some(false)); }; let f: TypedFunction<(), ()> = { @@ -180,7 +180,7 @@ mod sys { { let global = instance.exports.get_global("global")?; - assert_eq!(is_global_instance_ref_strong(&global), Some(true)); + assert_eq!(is_global_instance_ref_strong(global), Some(true)); } let f: TypedFunction<(), ()> = instance.exports.get_native_function("call_host_fn")?; @@ -202,10 +202,10 @@ mod sys { let host_fn = |env: &TableEnv| { let table = env.table_ref().unwrap(); - assert_eq!(is_table_instance_ref_strong(&table), Some(false)); + assert_eq!(is_table_instance_ref_strong(table), Some(false)); let table_clone = table.clone(); assert_eq!(is_table_instance_ref_strong(&table_clone), Some(true)); - assert_eq!(is_table_instance_ref_strong(&table), Some(false)); + assert_eq!(is_table_instance_ref_strong(table), Some(false)); }; let f: TypedFunction<(), ()> = { @@ -224,7 +224,7 @@ mod sys { { let table = instance.exports.get_table("table")?; - assert_eq!(is_table_instance_ref_strong(&table), Some(true)); + assert_eq!(is_table_instance_ref_strong(table), Some(true)); } let f: TypedFunction<(), ()> = instance.exports.get_native_function("call_host_fn")?; @@ -246,10 +246,10 @@ mod sys { let host_fn = |env: &FunctionEnv| { let function = env.call_host_fn_ref().unwrap(); - assert_eq!(is_function_instance_ref_strong(&function), Some(false)); + assert_eq!(is_function_instance_ref_strong(function), Some(false)); let function_clone = function.clone(); assert_eq!(is_function_instance_ref_strong(&function_clone), Some(true)); - assert_eq!(is_function_instance_ref_strong(&function), Some(false)); + assert_eq!(is_function_instance_ref_strong(function), Some(false)); }; let f: TypedFunction<(), ()> = { @@ -268,7 +268,7 @@ mod sys { { let function = instance.exports.get_function("call_host_fn")?; - assert_eq!(is_function_instance_ref_strong(&function), Some(true)); + assert_eq!(is_function_instance_ref_strong(function), Some(true)); } let f: TypedFunction<(), ()> = instance.exports.get_native_function("call_host_fn")?; @@ -291,7 +291,7 @@ mod sys { let host_fn = |env: &FunctionEnv| { let function = env.call_host_fn_ref().unwrap(); assert_eq!( - is_native_function_instance_ref_strong(&function), + is_native_function_instance_ref_strong(function), Some(false) ); let function_clone = function.clone(); @@ -300,7 +300,7 @@ mod sys { Some(true) ); assert_eq!( - is_native_function_instance_ref_strong(&function), + is_native_function_instance_ref_strong(function), Some(false) ); }; diff --git a/lib/api/tests/sys_externals.rs b/lib/api/tests/sys_externals.rs index 8c4b865ddeb..baad342f8cf 100644 --- a/lib/api/tests/sys_externals.rs +++ b/lib/api/tests/sys_externals.rs @@ -94,7 +94,7 @@ mod sys { maximum: Some(1), }; let f = Function::new_native(&store, |num: i32| num + 1); - let table = Table::new(&store, table_type, Value::FuncRef(Some(f.clone())))?; + let table = Table::new(&store, table_type, Value::FuncRef(Some(f)))?; assert_eq!(*table.ty(), table_type); let _elem = table.get(0).unwrap(); // assert_eq!(elem.funcref().unwrap(), f); @@ -123,7 +123,7 @@ mod sys { assert!(old_len.is_err()); // Growing to a bigger maximum should return None - let old_len = table.grow(5, Value::FuncRef(Some(f.clone())))?; + let old_len = table.grow(5, Value::FuncRef(Some(f)))?; assert_eq!(old_len, 0); Ok(()) @@ -238,11 +238,10 @@ mod sys { function.ty().clone(), FunctionType::new(vec![], vec![Type::I32]) ); - let function = Function::new_native_with_env( - &store, - my_env.clone(), - |_env: &MyEnv| -> (i32, i64, f32, f64) { (1, 2, 3.0, 4.0) }, - ); + let function = + Function::new_native_with_env(&store, my_env, |_env: &MyEnv| -> (i32, i64, f32, f64) { + (1, 2, 3.0, 4.0) + }); assert_eq!( function.ty().clone(), FunctionType::new(vec![], vec![Type::I32, Type::I64, Type::F32, Type::F64]) @@ -338,7 +337,7 @@ mod sys { let function = Function::new_with_env( &store, function_type, - my_env.clone(), + my_env, |_env: &MyEnv, _values: &[Value]| unimplemented!(), ); assert_eq!(function.ty().params(), [Type::V128]); diff --git a/lib/api/tests/sys_instance.rs b/lib/api/tests/sys_instance.rs index c1793cccf43..a89c5e6ceaf 100644 --- a/lib/api/tests/sys_instance.rs +++ b/lib/api/tests/sys_instance.rs @@ -50,7 +50,7 @@ mod sys { fn imported_fn(env: &Env, args: &[Val]) -> Result, RuntimeError> { let value = env.multiplier * args[0].unwrap_i32() as u32; - return Ok(vec![Val::I32(value as _)]); + Ok(vec![Val::I32(value as _)]) } let imported_signature = FunctionType::new(vec![Type::I32], vec![Type::I32]); diff --git a/lib/c-api/examples/early-exit.c b/lib/c-api/examples/early-exit.c index b4479fc6143..d2410ca8baf 100644 --- a/lib/c-api/examples/early-exit.c +++ b/lib/c-api/examples/early-exit.c @@ -10,10 +10,12 @@ // Use the last_error API to retrieve error messages void print_wasmer_error() { int error_len = wasmer_last_error_length(); - printf("Error len: `%d`\n", error_len); - char *error_str = malloc(error_len); - wasmer_last_error_message(error_str, error_len); - printf("Error str: `%s`\n", error_str); + if (error_len > 0) { + printf("Error len: `%d`\n", error_len); + char *error_str = malloc(error_len); + wasmer_last_error_message(error_str, error_len); + printf("Error str: `%s`\n", error_str); + } } void print_frame(wasm_frame_t* frame) { diff --git a/lib/c-api/examples/wasi.c b/lib/c-api/examples/wasi.c index e3047912dca..941a769135e 100644 --- a/lib/c-api/examples/wasi.c +++ b/lib/c-api/examples/wasi.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -12,13 +13,16 @@ void print_wasmer_error() { int error_len = wasmer_last_error_length(); - printf("Error len: `%d`\n", error_len); - char *error_str = malloc(error_len); - wasmer_last_error_message(error_str, error_len); - printf("Error str: `%s`\n", error_str); + if (error_len > 0) { + printf("Error len: `%d`\n", error_len); + char *error_str = malloc(error_len); + wasmer_last_error_message(error_str, error_len); + printf("Error str: `%s`\n", error_str); + } } int main(int argc, const char* argv[]) { + // Initialize. printf("Initializing...\n"); wasm_engine_t* engine = wasm_engine_new(); @@ -119,6 +123,7 @@ int main(int argc, const char* argv[]) { printf("> Error calling function!\n"); return 1; } + printf("Call completed\n"); { FILE *memory_stream; @@ -137,6 +142,11 @@ int main(int argc, const char* argv[]) { do { data_read_size = wasi_env_read_stdout(wasi_env, buffer, BUF_SIZE); + if (data_read_size == -1) { + printf("failed to read stdout: %s\n", strerror(errno)); + print_wasmer_error(); + return -1; + } if (data_read_size > 0) { stdout_size += data_read_size; diff --git a/lib/c-api/src/wasm_c_api/wasi/capture_files.rs b/lib/c-api/src/wasm_c_api/wasi/capture_files.rs deleted file mode 100644 index 85ae72dd1e3..00000000000 --- a/lib/c-api/src/wasm_c_api/wasi/capture_files.rs +++ /dev/null @@ -1,93 +0,0 @@ -//! Default implementations for capturing the stdout/stderr output of a WASI program. - -use std::collections::VecDeque; -use std::io::{self, Read, Seek, Write}; -use wasmer_wasi::{WasiFile, WasiFsError}; - -/// For capturing stdout/stderr. Stores all output in a string. -#[derive(Debug)] -pub struct OutputCapturer { - pub(crate) buffer: VecDeque, -} - -impl OutputCapturer { - pub fn new() -> Self { - Self { - buffer: VecDeque::new(), - } - } -} - -impl WasiFile for OutputCapturer { - fn last_accessed(&self) -> u64 { - 0 - } - fn last_modified(&self) -> u64 { - 0 - } - fn created_time(&self) -> u64 { - 0 - } - fn size(&self) -> u64 { - 0 - } - fn set_len(&mut self, _len: u64) -> Result<(), WasiFsError> { - Ok(()) - } - fn unlink(&mut self) -> Result<(), WasiFsError> { - Ok(()) - } - fn bytes_available(&self) -> Result { - // return an arbitrary amount - Ok(1024) - } -} - -// fail when reading or Seeking -impl Read for OutputCapturer { - fn read(&mut self, _buf: &mut [u8]) -> io::Result { - Err(io::Error::new( - io::ErrorKind::Other, - "can not read from capturing stdout", - )) - } - fn read_to_end(&mut self, _buf: &mut Vec) -> std::io::Result { - Err(std::io::Error::new( - std::io::ErrorKind::Other, - "can not read from capturing stdout", - )) - } - fn read_to_string(&mut self, _buf: &mut String) -> io::Result { - Err(io::Error::new( - io::ErrorKind::Other, - "can not read from capturing stdout", - )) - } - fn read_exact(&mut self, _buf: &mut [u8]) -> io::Result<()> { - Err(io::Error::new( - io::ErrorKind::Other, - "can not read from capturing stdout", - )) - } -} -impl Seek for OutputCapturer { - fn seek(&mut self, _pos: io::SeekFrom) -> io::Result { - Err(io::Error::new( - io::ErrorKind::Other, - "can not seek capturing stdout", - )) - } -} -impl Write for OutputCapturer { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.buffer.extend(buf); - Ok(buf.len()) - } - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - self.buffer.extend(buf); - Ok(()) - } -} diff --git a/lib/c-api/src/wasm_c_api/wasi/mod.rs b/lib/c-api/src/wasm_c_api/wasi/mod.rs index 63cda7d3d9d..a285c916c02 100644 --- a/lib/c-api/src/wasm_c_api/wasi/mod.rs +++ b/lib/c-api/src/wasm_c_api/wasi/mod.rs @@ -2,8 +2,6 @@ //! //! This API will be superseded by a standard WASI API when/if such a standard is created. -mod capture_files; - pub use super::unstable::wasi::wasi_get_unordered_imports; use super::{ externals::{wasm_extern_vec_t, wasm_func_t}, @@ -12,14 +10,13 @@ use super::{ store::wasm_store_t, }; use crate::error::update_last_error; -use std::cmp::min; use std::convert::TryFrom; use std::ffi::CStr; use std::os::raw::c_char; use std::slice; use wasmer_api::{Exportable, Extern}; use wasmer_wasi::{ - generate_import_object_from_env, get_wasi_version, WasiEnv, WasiFile, WasiState, + generate_import_object_from_env, get_wasi_version, Pipe, WasiEnv, WasiFile, WasiState, WasiStateBuilder, WasiVersion, }; @@ -175,15 +172,11 @@ pub struct wasi_env_t { #[no_mangle] pub extern "C" fn wasi_env_new(mut config: Box) -> Option> { if !config.inherit_stdout { - config - .state_builder - .stdout(Box::new(capture_files::OutputCapturer::new())); + config.state_builder.stdout(Box::new(Pipe::new())); } if !config.inherit_stderr { - config - .state_builder - .stderr(Box::new(capture_files::OutputCapturer::new())); + config.state_builder.stderr(Box::new(Pipe::new())); } // TODO: impl capturer for stdin @@ -206,19 +199,19 @@ pub unsafe extern "C" fn wasi_env_read_stdout( buffer_len: usize, ) -> isize { let inner_buffer = slice::from_raw_parts_mut(buffer as *mut _, buffer_len as usize); - let mut state = env.inner.state(); + let state = env.inner.state(); - let stdout = if let Ok(stdout) = state.fs.stdout_mut() { + if let Ok(mut stdout) = state.stdout() { if let Some(stdout) = stdout.as_mut() { - stdout + read_inner(stdout, inner_buffer) } else { update_last_error("could not find a file handle for `stdout`"); - return -1; + -1 } } else { - return -1; - }; - read_inner(stdout, inner_buffer) + update_last_error("could not find a file handle for `stdout`"); + -1 + } } #[no_mangle] @@ -228,35 +221,30 @@ pub unsafe extern "C" fn wasi_env_read_stderr( buffer_len: usize, ) -> isize { let inner_buffer = slice::from_raw_parts_mut(buffer as *mut _, buffer_len as usize); - let mut state = env.inner.state(); - let stderr = if let Ok(stderr) = state.fs.stderr_mut() { + let state = env.inner.state(); + if let Ok(mut stderr) = state.stderr() { if let Some(stderr) = stderr.as_mut() { - stderr + read_inner(stderr, inner_buffer) } else { update_last_error("could not find a file handle for `stderr`"); - return -1; + -1 } } else { update_last_error("could not find a file handle for `stderr`"); - return -1; - }; - read_inner(stderr, inner_buffer) + -1 + } } -fn read_inner(wasi_file: &mut Box, inner_buffer: &mut [u8]) -> isize { - if let Some(oc) = wasi_file.downcast_mut::() { - let total_to_read = min(inner_buffer.len(), oc.buffer.len()); - - for (address, value) in inner_buffer - .iter_mut() - .zip(oc.buffer.drain(..total_to_read)) - { - *address = value; +fn read_inner( + wasi_file: &mut Box, + inner_buffer: &mut [u8], +) -> isize { + match wasi_file.read(inner_buffer) { + Ok(a) => a as isize, + Err(err) => { + update_last_error(format!("failed to read wasi_file: {}", err)); + -1 } - - total_to_read as isize - } else { - -1 } } @@ -286,6 +274,12 @@ pub enum wasi_version_t { /// `wasi_snapshot_preview1`. SNAPSHOT1 = 2, + + /// `wasix_32v1`. + WASIX32V1 = 3, + + /// `wasix_64v1`. + WASIX64V1 = 4, } impl From for wasi_version_t { @@ -293,6 +287,8 @@ impl From for wasi_version_t { match other { WasiVersion::Snapshot0 => wasi_version_t::SNAPSHOT0, WasiVersion::Snapshot1 => wasi_version_t::SNAPSHOT1, + WasiVersion::Wasix32v1 => wasi_version_t::WASIX32V1, + WasiVersion::Wasix64v1 => wasi_version_t::WASIX64V1, WasiVersion::Latest => wasi_version_t::LATEST, } } @@ -306,6 +302,8 @@ impl TryFrom for WasiVersion { wasi_version_t::INVALID_VERSION => return Err("Invalid WASI version cannot be used"), wasi_version_t::SNAPSHOT0 => WasiVersion::Snapshot0, wasi_version_t::SNAPSHOT1 => WasiVersion::Snapshot1, + wasi_version_t::WASIX32V1 => WasiVersion::Wasix32v1, + wasi_version_t::WASIX64V1 => WasiVersion::Wasix64v1, wasi_version_t::LATEST => WasiVersion::Latest, }) } diff --git a/lib/cache/benches/bench_filesystem_cache.rs b/lib/cache/benches/bench_filesystem_cache.rs index f9adc74b3d6..b7b6a712b42 100644 --- a/lib/cache/benches/bench_filesystem_cache.rs +++ b/lib/cache/benches/bench_filesystem_cache.rs @@ -1,3 +1,4 @@ +#![allow(unused_imports)] use criterion::{black_box, criterion_group, criterion_main, Criterion}; use rand::distributions::Alphanumeric; use rand::{thread_rng, Rng}; @@ -46,7 +47,7 @@ pub fn load_cache_universal(c: &mut Criterion) { fs_cache.store(key, &module).unwrap(); c.bench_function("load universal module in filesystem cache", |b| { - b.iter(|| unsafe { fs_cache.load(&store, key.clone()).unwrap() }) + b.iter(|| unsafe { fs_cache.load(&store, key).unwrap() }) }); } @@ -54,7 +55,7 @@ pub fn store_cache_native(c: &mut Criterion) { let tmp_dir = TempDir::new().unwrap(); let mut fs_cache = FileSystemCache::new(tmp_dir.path()).unwrap(); let compiler = Singlepass::default(); - let store = Store::new(&Native::new(compiler).engine()); + let store = Store::new(&Universal::new(compiler).engine()); let module = Module::new( &store, std::fs::read("../../lib/c-api/examples/assets/qjs.wasm").unwrap(), @@ -73,7 +74,7 @@ pub fn load_cache_native(c: &mut Criterion) { let tmp_dir = TempDir::new().unwrap(); let mut fs_cache = FileSystemCache::new(tmp_dir.path()).unwrap(); let compiler = Singlepass::default(); - let store = Store::new(&Native::new(compiler).engine()); + let store = Store::new(&Universal::new(compiler).engine()); let module = Module::new( &store, std::fs::read("../../lib/c-api/examples/assets/qjs.wasm").unwrap(), @@ -83,7 +84,7 @@ pub fn load_cache_native(c: &mut Criterion) { fs_cache.store(key, &module).unwrap(); c.bench_function("load native module in filesystem cache", |b| { - b.iter(|| unsafe { fs_cache.load(&store, key.clone()).unwrap() }) + b.iter(|| unsafe { fs_cache.load(&store, key).unwrap() }) }); } diff --git a/lib/cli-compiler/src/store.rs b/lib/cli-compiler/src/store.rs index 16f8691ca7c..096e871bcba 100644 --- a/lib/cli-compiler/src/store.rs +++ b/lib/cli-compiler/src/store.rs @@ -107,13 +107,15 @@ pub struct CompilerOptions { llvm: bool, /// Enable compiler internal verification. + #[allow(unused)] #[structopt(long)] #[allow(dead_code)] enable_verifier: bool, /// LLVM debug directory, where IR and object files will be written to. + #[allow(unused)] #[cfg(feature = "llvm")] - #[structopt(long, parse(from_os_str))] + #[cfg_attr(feature = "llvm", structopt(long, parse(from_os_str)))] llvm_debug_dir: Option, #[structopt(flatten)] diff --git a/lib/cli/src/commands/run/wasi.rs b/lib/cli/src/commands/run/wasi.rs index b7adf3a091f..f7e19820791 100644 --- a/lib/cli/src/commands/run/wasi.rs +++ b/lib/cli/src/commands/run/wasi.rs @@ -3,7 +3,7 @@ use anyhow::Result; use std::collections::BTreeSet; use std::path::PathBuf; use wasmer::{Instance, Module, RuntimeError, Val}; -use wasmer_wasi::{get_wasi_versions, WasiError, WasiState, WasiVersion}; +use wasmer_wasi::{get_wasi_versions, is_wasix_module, WasiError, WasiState, WasiVersion}; use structopt::StructOpt; @@ -41,7 +41,10 @@ pub struct Wasi { /// Enable experimental IO devices #[cfg(feature = "experimental-io-devices")] - #[structopt(long = "enable-experimental-io-devices")] + #[cfg_attr( + feature = "experimental-io-devices", + structopt(long = "enable-experimental-io-devices") + )] enable_experimental_io_devices: bool, /// Allow WASI modules to import multiple versions of WASI without a warning. @@ -94,6 +97,11 @@ impl Wasi { } let mut wasi_env = wasi_state_builder.finalize()?; + wasi_env.state.fs.is_wasix.store( + is_wasix_module(module), + std::sync::atomic::Ordering::Release, + ); + let import_object = wasi_env.import_object_for_all_wasi_versions(module)?; let instance = Instance::new(module, &import_object)?; Ok(instance) diff --git a/lib/cli/src/commands/wasmer_create_exe_main.c b/lib/cli/src/commands/wasmer_create_exe_main.c index c77fcab9dd7..3661ffa9d29 100644 --- a/lib/cli/src/commands/wasmer_create_exe_main.c +++ b/lib/cli/src/commands/wasmer_create_exe_main.c @@ -12,11 +12,13 @@ static void print_wasmer_error() { int error_len = wasmer_last_error_length(); - printf("Error len: `%d`\n", error_len); - char *error_str = (char *)malloc(error_len); - wasmer_last_error_message(error_str, error_len); - printf("%s\n", error_str); - free(error_str); + if (error_len > 0) { + printf("Error len: `%d`\n", error_len); + char *error_str = (char *)malloc(error_len); + wasmer_last_error_message(error_str, error_len); + printf("%s\n", error_str); + free(error_str); + } } #ifdef WASI diff --git a/lib/engine-staticlib/README.md b/lib/engine-staticlib/README.md index 9c1cc0dbd74..e8566f0137b 100644 --- a/lib/engine-staticlib/README.md +++ b/lib/engine-staticlib/README.md @@ -40,11 +40,13 @@ Now let's create a program to link with this static object file. static void print_wasmer_error() { int error_len = wasmer_last_error_length(); - printf("Error len: `%d`\n", error_len); - char* error_str = (char*) malloc(error_len); - wasmer_last_error_message(error_str, error_len); - printf("Error str: `%s`\n", error_str); - free(error_str); + if (error_len > 0) { + printf("Error len: `%d`\n", error_len); + char* error_str = (char*) malloc(error_len); + wasmer_last_error_message(error_str, error_len); + printf("Error str: `%s`\n", error_str); + free(error_str); + } } int main() { diff --git a/lib/middlewares/src/metering.rs b/lib/middlewares/src/metering.rs index a8faf51c817..6ae7e6a639e 100644 --- a/lib/middlewares/src/metering.rs +++ b/lib/middlewares/src/metering.rs @@ -382,7 +382,7 @@ mod tests { fn get_remaining_points_works() { let metering = Arc::new(Metering::new(10, cost_function)); let mut compiler_config = Cranelift::default(); - compiler_config.push_middleware(metering.clone()); + compiler_config.push_middleware(metering); let store = Store::new(&Universal::new(compiler_config).engine()); let module = Module::new(&store, bytecode()).unwrap(); @@ -427,7 +427,7 @@ mod tests { fn set_remaining_points_works() { let metering = Arc::new(Metering::new(10, cost_function)); let mut compiler_config = Cranelift::default(); - compiler_config.push_middleware(metering.clone()); + compiler_config.push_middleware(metering); let store = Store::new(&Universal::new(compiler_config).engine()); let module = Module::new(&store, bytecode()).unwrap(); diff --git a/lib/types/src/lib.rs b/lib/types/src/lib.rs index da421966b15..b001ab0a17c 100644 --- a/lib/types/src/lib.rs +++ b/lib/types/src/lib.rs @@ -83,6 +83,7 @@ pub use crate::indexes::{ pub use crate::initializers::{ DataInitializer, DataInitializerLocation, OwnedDataInitializer, TableInitializer, }; +pub use crate::memory::{Memory32, Memory64, MemorySize}; pub use crate::module::{ExportsIterator, ImportsIterator, ModuleInfo}; pub use crate::native::{NativeWasmType, ValueType}; pub use crate::units::{ diff --git a/lib/types/src/memory.rs b/lib/types/src/memory.rs index 6c459cbb8c3..b49fb71a398 100644 --- a/lib/types/src/memory.rs +++ b/lib/types/src/memory.rs @@ -1,8 +1,11 @@ -use crate::Pages; +use crate::{Pages, ValueType}; #[cfg(feature = "enable-rkyv")] use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; #[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; +use std::convert::{TryFrom, TryInto}; +use std::iter::Sum; +use std::ops::{Add, AddAssign}; /// Implementation styles for WebAssembly linear memory. #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -43,3 +46,81 @@ impl MemoryStyle { } } } + +/// Trait for the `Memory32` and `Memory64` marker types. +/// +/// This allows code to be generic over 32-bit and 64-bit memories. +/// # Safety +/// Direct memory access is unsafe +pub unsafe trait MemorySize: Copy { + /// Type used to represent an offset into a memory. This is `u32` or `u64`. + type Offset: Default + + std::fmt::Debug + + std::fmt::Display + + Eq + + Ord + + PartialEq + + PartialOrd + + Clone + + Copy + + ValueType + + Into + + From + + From + + From + + TryFrom + + TryFrom + + TryFrom + + TryFrom + + TryInto + + TryInto + + TryInto + + TryInto + + TryInto + + TryFrom + + Add + + Sum + + AddAssign; + + /// Type used to pass this value as an argument or return value for a Wasm function. + type Native: super::NativeWasmType; + + /// Zero value used for `WasmPtr::is_null`. + const ZERO: Self::Offset; + + /// Convert an `Offset` to a `Native`. + fn offset_to_native(offset: Self::Offset) -> Self::Native; + + /// Convert a `Native` to an `Offset`. + fn native_to_offset(native: Self::Native) -> Self::Offset; +} + +/// Marker trait for 32-bit memories. +#[derive(Clone, Copy)] +pub struct Memory32; +unsafe impl MemorySize for Memory32 { + type Offset = u32; + type Native = i32; + const ZERO: Self::Offset = 0; + fn offset_to_native(offset: Self::Offset) -> Self::Native { + offset as Self::Native + } + fn native_to_offset(native: Self::Native) -> Self::Offset { + native as Self::Offset + } +} + +/// Marker trait for 64-bit memories. +#[derive(Clone, Copy)] +pub struct Memory64; +unsafe impl MemorySize for Memory64 { + type Offset = u64; + type Native = i64; + const ZERO: Self::Offset = 0; + fn offset_to_native(offset: Self::Offset) -> Self::Native { + offset as Self::Native + } + fn native_to_offset(native: Self::Native) -> Self::Offset { + native as Self::Offset + } +} diff --git a/lib/vbus/Cargo.toml b/lib/vbus/Cargo.toml new file mode 100644 index 00000000000..bf0400e2290 --- /dev/null +++ b/lib/vbus/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "wasmer-vbus" +version = "2.3.0" +description = "Wasmer Virtual Bus" +authors = ["Wasmer Engineering Team "] +license = "MIT" +edition = "2018" + +[dependencies] +libc = { version = "^0.2", default-features = false, optional = true } +thiserror = "1" +tracing = { version = "0.1" } +typetag = { version = "0.1", optional = true } +serde = { version = "1.0", default-features = false, features = ["derive"], optional = true } +slab = { version = "0.4", optional = true } +wasmer-vfs = { path = "../vfs", version = "=2.3.0", default-features = false } + +[features] +default = [] diff --git a/lib/vbus/src/lib.rs b/lib/vbus/src/lib.rs new file mode 100644 index 00000000000..e51ec11ffac --- /dev/null +++ b/lib/vbus/src/lib.rs @@ -0,0 +1,364 @@ +use std::fmt; +use std::pin::Pin; +use std::task::{Context, Poll}; +use thiserror::Error; + +pub use wasmer_vfs::FileDescriptor; +pub use wasmer_vfs::StdioMode; + +pub type Result = std::result::Result; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[repr(transparent)] +pub struct CallDescriptor(u32); + +impl CallDescriptor { + pub fn raw(&self) -> u32 { + self.0 + } +} + +impl From for CallDescriptor { + fn from(a: u32) -> Self { + Self(a) + } +} + +pub trait VirtualBus: fmt::Debug + Send + Sync + 'static { + /// Starts a new WAPM sub process + fn new_spawn(&self) -> SpawnOptions; + + /// Creates a listener thats used to receive BUS commands + fn listen(&self) -> Result>; +} + +pub trait VirtualBusSpawner { + /// Spawns a new WAPM process by its name + fn spawn(&mut self, name: &str, config: &SpawnOptionsConfig) -> Result; +} + +#[derive(Debug, Clone)] +pub struct SpawnOptionsConfig { + reuse: bool, + chroot: bool, + args: Vec, + preopen: Vec, + stdin_mode: StdioMode, + stdout_mode: StdioMode, + stderr_mode: StdioMode, + working_dir: String, + remote_instance: Option, + access_token: Option, +} + +impl SpawnOptionsConfig { + pub const fn reuse(&self) -> bool { + self.reuse + } + + pub const fn chroot(&self) -> bool { + self.chroot + } + + pub const fn args(&self) -> &Vec { + &self.args + } + + pub const fn preopen(&self) -> &Vec { + &self.preopen + } + + pub const fn stdin_mode(&self) -> StdioMode { + self.stdin_mode + } + + pub const fn stdout_mode(&self) -> StdioMode { + self.stdout_mode + } + + pub const fn stderr_mode(&self) -> StdioMode { + self.stderr_mode + } + + pub fn working_dir(&self) -> &str { + self.working_dir.as_str() + } + + pub fn remote_instance(&self) -> Option<&str> { + self.remote_instance.as_deref() + } + + pub fn access_token(&self) -> Option<&str> { + self.access_token.as_deref() + } +} + +pub struct SpawnOptions { + spawner: Box, + conf: SpawnOptionsConfig, +} + +impl SpawnOptions { + pub fn new(spawner: Box) -> Self { + Self { + spawner, + conf: SpawnOptionsConfig { + reuse: false, + chroot: false, + args: Vec::new(), + preopen: Vec::new(), + stdin_mode: StdioMode::Null, + stdout_mode: StdioMode::Null, + stderr_mode: StdioMode::Null, + working_dir: "/".to_string(), + remote_instance: None, + access_token: None, + }, + } + } + pub fn options(&mut self, options: SpawnOptionsConfig) -> &mut Self { + self.conf = options; + self + } + + pub fn reuse(&mut self, reuse: bool) -> &mut Self { + self.conf.reuse = reuse; + self + } + + pub fn chroot(&mut self, chroot: bool) -> &mut Self { + self.conf.chroot = chroot; + self + } + + pub fn args(&mut self, args: Vec) -> &mut Self { + self.conf.args = args; + self + } + + pub fn preopen(&mut self, preopen: Vec) -> &mut Self { + self.conf.preopen = preopen; + self + } + + pub fn stdin_mode(&mut self, stdin_mode: StdioMode) -> &mut Self { + self.conf.stdin_mode = stdin_mode; + self + } + + pub fn stdout_mode(&mut self, stdout_mode: StdioMode) -> &mut Self { + self.conf.stdout_mode = stdout_mode; + self + } + + pub fn stderr_mode(&mut self, stderr_mode: StdioMode) -> &mut Self { + self.conf.stderr_mode = stderr_mode; + self + } + + pub fn working_dir(&mut self, working_dir: String) -> &mut Self { + self.conf.working_dir = working_dir; + self + } + + pub fn remote_instance(&mut self, remote_instance: String) -> &mut Self { + self.conf.remote_instance = Some(remote_instance); + self + } + + pub fn access_token(&mut self, access_token: String) -> &mut Self { + self.conf.access_token = Some(access_token); + self + } + + /// Spawns a new bus instance by its reference name + pub fn spawn(&mut self, name: &str) -> Result { + self.spawner.spawn(name, &self.conf) + } +} + +#[derive(Debug)] +pub struct BusSpawnedProcess { + /// Reference to the spawned instance + pub inst: Box, +} + +pub trait VirtualBusScope: fmt::Debug + Send + Sync + 'static { + //// Returns true if the invokable target has finished + fn poll_finished(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()>; +} + +pub trait VirtualBusInvokable: fmt::Debug + Send + Sync + 'static { + /// Invokes a service within this instance + fn invoke( + &self, + topic: String, + format: BusDataFormat, + buf: &[u8], + ) -> Result>; +} + +pub trait VirtualBusProcess: + VirtualBusScope + VirtualBusInvokable + fmt::Debug + Send + Sync + 'static +{ + /// Returns the exit code if the instance has finished + fn exit_code(&self) -> Option; + + /// Returns a file descriptor used to read the STDIN + fn stdin_fd(&self) -> Option; + + /// Returns a file descriptor used to write to STDOUT + fn stdout_fd(&self) -> Option; + + /// Returns a file descriptor used to write to STDERR + fn stderr_fd(&self) -> Option; +} + +pub trait VirtualBusInvocation: + VirtualBusScope + VirtualBusInvokable + fmt::Debug + Send + Sync + 'static +{ + /// Polls for new listen events related to this context + fn poll_event(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll; +} + +#[derive(Debug)] +pub enum BusInvocationEvent { + /// The server has sent some out-of-band data to you + Callback { + /// Topic that this call relates to + topic: String, + /// Format of the data we received + format: BusDataFormat, + /// Data passed in the call + data: Vec, + }, + /// The service has a responded to your call + Response { + /// Format of the data we received + format: BusDataFormat, + /// Data returned by the call + data: Vec, + }, +} + +pub trait VirtualBusListener: fmt::Debug + Send + Sync + 'static { + /// Polls for new calls to this service + fn poll_call(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll; +} + +#[derive(Debug)] +pub struct BusCallEvent { + /// Topic that this call relates to + pub topic: String, + /// Reference to the call itself + pub called: Box, + /// Format of the data we received + pub format: BusDataFormat, + /// Data passed in the call + pub data: Vec, +} + +pub trait VirtualBusCalled: VirtualBusListener + fmt::Debug + Send + Sync + 'static { + /// Sends an out-of-band message back to the caller + fn callback(&self, topic: String, format: BusDataFormat, buf: &[u8]) -> Result<()>; + + /// Informs the caller that their call has failed + fn fault(self, fault: BusError) -> Result<()>; + + /// Finishes the call and returns a particular response + fn reply(self, format: BusDataFormat, buf: &[u8]) -> Result<()>; +} + +/// Format that the supplied data is in +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum BusDataFormat { + Raw, + Bincode, + MessagePack, + Json, + Yaml, + Xml, +} + +#[derive(Debug, Default)] +pub struct UnsupportedVirtualBus {} + +impl VirtualBus for UnsupportedVirtualBus { + fn new_spawn(&self) -> SpawnOptions { + SpawnOptions::new(Box::new(UnsupportedVirtualBusSpawner::default())) + } + + fn listen(&self) -> Result> { + Err(BusError::Unsupported) + } +} + +#[derive(Debug, Default)] +pub struct UnsupportedVirtualBusSpawner {} + +impl VirtualBusSpawner for UnsupportedVirtualBusSpawner { + fn spawn(&mut self, _name: &str, _config: &SpawnOptionsConfig) -> Result { + Err(BusError::Unsupported) + } +} + +#[derive(Error, Copy, Clone, Debug, PartialEq, Eq)] +pub enum BusError { + /// Failed during serialization + #[error("serialization failed")] + Serialization, + /// Failed during deserialization + #[error("deserialization failed")] + Deserialization, + /// Invalid WAPM process + #[error("invalid wapm")] + InvalidWapm, + /// Failed to fetch the WAPM process + #[error("fetch failed")] + FetchFailed, + /// Failed to compile the WAPM process + #[error("compile error")] + CompileError, + /// Invalid ABI + #[error("WAPM process has an invalid ABI")] + InvalidABI, + /// Call was aborted + #[error("call aborted")] + Aborted, + /// Bad handle + #[error("bad handle")] + BadHandle, + /// Invalid topic + #[error("invalid topic")] + InvalidTopic, + /// Invalid callback + #[error("invalid callback")] + BadCallback, + /// Call is unsupported + #[error("unsupported")] + Unsupported, + /// Bad request + #[error("bad request")] + BadRequest, + /// Access denied + #[error("access denied")] + AccessDenied, + /// Internal error has occured + #[error("internal error")] + InternalError, + /// Memory allocation failed + #[error("memory allocation failed")] + MemoryAllocationFailed, + /// Invocation has failed + #[error("invocation has failed")] + InvokeFailed, + /// Already consumed + #[error("already consumed")] + AlreadyConsumed, + /// Memory access violation + #[error("memory access violation")] + MemoryAccessViolation, + /// Some other unhandled error. If you see this, it's probably a bug. + #[error("unknown error found")] + UnknownError, +} diff --git a/lib/vfs/src/host_fs.rs b/lib/vfs/src/host_fs.rs index 961f6685be0..788d21b3b77 100644 --- a/lib/vfs/src/host_fs.rs +++ b/lib/vfs/src/host_fs.rs @@ -177,7 +177,11 @@ impl TryInto for fs::Metadata { pub struct FileOpener; impl crate::FileOpener for FileOpener { - fn open(&mut self, path: &Path, conf: &OpenOptionsConfig) -> Result> { + fn open( + &mut self, + path: &Path, + conf: &OpenOptionsConfig, + ) -> Result> { // TODO: handle create implying write, etc. let read = conf.read(); let write = conf.write(); @@ -193,7 +197,7 @@ impl crate::FileOpener for FileOpener { .map_err(Into::into) .map(|file| { Box::new(File::new(file, path.to_owned(), read, write, append)) - as Box + as Box }) } } diff --git a/lib/vfs/src/lib.rs b/lib/vfs/src/lib.rs index f1274a0f655..982b944b3ee 100644 --- a/lib/vfs/src/lib.rs +++ b/lib/vfs/src/lib.rs @@ -22,6 +22,18 @@ pub type Result = std::result::Result; #[repr(transparent)] pub struct FileDescriptor(usize); +impl From for FileDescriptor { + fn from(a: u32) -> Self { + Self(a as usize) + } +} + +impl From for u32 { + fn from(a: FileDescriptor) -> u32 { + a.0 as u32 + } +} + pub trait FileSystem: fmt::Debug + Send + Sync + 'static + Upcastable { fn read_dir(&self, path: &Path) -> Result; fn create_dir(&self, path: &Path) -> Result<()>; @@ -51,7 +63,11 @@ impl dyn FileSystem + 'static { } pub trait FileOpener { - fn open(&mut self, path: &Path, conf: &OpenOptionsConfig) -> Result>; + fn open( + &mut self, + path: &Path, + conf: &OpenOptionsConfig, + ) -> Result>; } #[derive(Debug, Clone)] @@ -111,6 +127,10 @@ impl OpenOptions { }, } } + pub fn options(&mut self, options: OpenOptionsConfig) -> &mut Self { + self.conf = options; + self + } pub fn read(&mut self, read: bool) -> &mut Self { self.conf.read = read; @@ -142,14 +162,17 @@ impl OpenOptions { self } - pub fn open>(&mut self, path: P) -> Result> { + pub fn open>( + &mut self, + path: P, + ) -> Result> { self.opener.open(path.as_ref(), &self.conf) } } /// This trait relies on your file closing when it goes out of scope via `Drop` #[cfg_attr(feature = "enable-serde", typetag::serde)] -pub trait VirtualFile: fmt::Debug + Send + Write + Read + Seek + 'static + Upcastable { +pub trait VirtualFile: fmt::Debug + Write + Read + Seek + Upcastable { /// the last time the file was accessed in nanoseconds as a UNIX timestamp fn last_accessed(&self) -> u64; @@ -177,7 +200,28 @@ pub trait VirtualFile: fmt::Debug + Send + Write + Read + Seek + 'static + Upcas } /// Returns the number of bytes available. This function must not block - fn bytes_available(&self) -> Result; + fn bytes_available(&self) -> Result { + Ok(self.bytes_available_read()?.unwrap_or(0usize) + + self.bytes_available_write()?.unwrap_or(0usize)) + } + + /// Returns the number of bytes available. This function must not block + /// Defaults to `None` which means the number of bytes is unknown + fn bytes_available_read(&self) -> Result> { + Ok(None) + } + + /// Returns the number of bytes available. This function must not block + /// Defaults to `None` which means the number of bytes is unknown + fn bytes_available_write(&self) -> Result> { + Ok(None) + } + + /// Indicates if the file is opened or closed. This function must not block + /// Defaults to a status of being constantly open + fn is_open(&self) -> bool { + true + } /// Used for polling. Default returns `None` because this method cannot be implemented for most types /// Returns the underlying host fd @@ -209,15 +253,17 @@ impl Upcastable for T { } } -impl dyn VirtualFile + 'static { - #[inline] - pub fn downcast_ref(&'_ self) -> Option<&'_ T> { - self.upcast_any_ref().downcast_ref::() - } - #[inline] - pub fn downcast_mut(&'_ mut self) -> Option<&'_ mut T> { - self.upcast_any_mut().downcast_mut::() - } +/// Determines the mode that stdio handlers will operate in +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum StdioMode { + /// Stdio will be piped to a file descriptor + Piped, + /// Stdio will inherit the file handlers of its parent + Inherit, + /// Stdio will be dropped + Null, + /// Stdio will be sent to the log handler + Log, } /// Error type for external users diff --git a/lib/vfs/src/mem_fs/file_opener.rs b/lib/vfs/src/mem_fs/file_opener.rs index 689feb5aafd..80fc9255b3d 100644 --- a/lib/vfs/src/mem_fs/file_opener.rs +++ b/lib/vfs/src/mem_fs/file_opener.rs @@ -10,7 +10,11 @@ pub struct FileOpener { } impl crate::FileOpener for FileOpener { - fn open(&mut self, path: &Path, conf: &OpenOptionsConfig) -> Result> { + fn open( + &mut self, + path: &Path, + conf: &OpenOptionsConfig, + ) -> Result> { let read = conf.read(); let mut write = conf.write(); let append = conf.append(); diff --git a/lib/vm/src/instance/mod.rs b/lib/vm/src/instance/mod.rs index c224b0ff4c7..f7671196694 100644 --- a/lib/vm/src/instance/mod.rs +++ b/lib/vm/src/instance/mod.rs @@ -859,7 +859,7 @@ impl Instance { /// /// This is more or less a public facade of the private `Instance`, /// providing useful higher-level API. -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub struct InstanceHandle { /// The [`InstanceRef`]. See its documentation to learn more. instance: InstanceRef, diff --git a/lib/vnet/Cargo.toml b/lib/vnet/Cargo.toml new file mode 100644 index 00000000000..a61e8175282 --- /dev/null +++ b/lib/vnet/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "wasmer-vnet" +version = "2.3.0" +description = "Wasmer Virtual Networking" +authors = ["Wasmer Engineering Team "] +license = "MIT" +edition = "2018" + +[dependencies] +libc = { version = "^0.2", default-features = false, optional = true } +thiserror = "1" +tracing = { version = "0.1" } +typetag = { version = "0.1", optional = true } +serde = { version = "1.0", default-features = false, features = ["derive"], optional = true } +slab = { version = "0.4", optional = true } +wasmer-vfs = { path = "../vfs", version = "=2.3.0", default-features = false } +bytes = "1" + +[features] +default = [] diff --git a/lib/vnet/src/lib.rs b/lib/vnet/src/lib.rs new file mode 100644 index 00000000000..499d709b7d5 --- /dev/null +++ b/lib/vnet/src/lib.rs @@ -0,0 +1,695 @@ +use std::fmt; +use std::net::IpAddr; +use std::net::Ipv4Addr; +use std::net::Ipv6Addr; +use std::net::Shutdown; +use std::net::SocketAddr; +use std::sync::mpsc; +use std::sync::Arc; +use std::sync::Mutex; +use std::time::Duration; +use thiserror::Error; + +pub use bytes::Bytes; +pub use bytes::BytesMut; + +pub type Result = std::result::Result; + +/// Socket descriptors are also file descriptors and so +/// all file operations can also be used on sockets +pub type SocketDescriptor = wasmer_vfs::FileDescriptor; + +/// Represents an IP address and its netmask +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct IpCidr { + pub ip: IpAddr, + pub prefix: u8, +} + +/// Represents a routing entry in the routing table of the interface +#[derive(Clone, Debug)] +pub struct IpRoute { + pub cidr: IpCidr, + pub via_router: IpAddr, + pub preferred_until: Option, + pub expires_at: Option, +} + +/// An implementation of virtual networking +pub trait VirtualNetworking: fmt::Debug + Send + Sync + 'static { + /// Establishes a web socket connection + /// (note: this does not use the virtual sockets and is standalone + /// functionality that works without the network being connected) + fn ws_connect(&self, url: &str) -> Result>; + + /// Makes a HTTP request to a remote web resource + /// The headers are separated by line breaks + /// (note: this does not use the virtual sockets and is standalone + /// functionality that works without the network being connected) + fn http_request( + &self, + url: &str, + method: &str, + headers: &str, + gzip: bool, + ) -> Result; + + /// Bridges this local network with a remote network, which is required in + /// order to make lower level networking calls (such as UDP/TCP) + fn bridge(&self, network: &str, access_token: &str, security: StreamSecurity) -> Result<()>; + + /// Disconnects from the remote network essentially unbridging it + fn unbridge(&self) -> Result<()>; + + /// Acquires an IP address on the network and configures the routing tables + fn dhcp_acquire(&self) -> Result>; + + /// Adds a static IP address to the interface with a netmask prefix + fn ip_add(&self, ip: IpAddr, prefix: u8) -> Result<()>; + + /// Removes a static (or dynamic) IP address from the interface + fn ip_remove(&self, ip: IpAddr) -> Result<()>; + + /// Clears all the assigned IP addresses for this interface + fn ip_clear(&self) -> Result<()>; + + /// Lists all the IP addresses currently assigned to this interface + fn ip_list(&self) -> Result>; + + /// Returns the hardware MAC address for this interface + fn mac(&self) -> Result<[u8; 6]>; + + /// Adds a default gateway to the routing table + fn gateway_set(&self, ip: IpAddr) -> Result<()>; + + /// Adds a specific route to the routing table + fn route_add( + &self, + cidr: IpCidr, + via_router: IpAddr, + preferred_until: Option, + expires_at: Option, + ) -> Result<()>; + + /// Removes a routing rule from the routing table + fn route_remove(&self, cidr: IpAddr) -> Result<()>; + + /// Clears the routing table for this interface + fn route_clear(&self) -> Result<()>; + + /// Lists all the routes defined in the routing table for this interface + fn route_list(&self) -> Result>; + + /// Creates a low level socket that can read and write Ethernet packets + /// directly to the interface + fn bind_raw(&self) -> Result>; + + /// Lists for TCP connections on a specific IP and Port combination + /// Multiple servers (processes or threads) can bind to the same port if they each set + /// the reuse-port and-or reuse-addr flags + fn listen_tcp( + &self, + addr: SocketAddr, + only_v6: bool, + reuse_port: bool, + reuse_addr: bool, + ) -> Result>; + + /// Opens a UDP socket that listens on a specific IP and Port combination + /// Multiple servers (processes or threads) can bind to the same port if they each set + /// the reuse-port and-or reuse-addr flags + fn bind_udp( + &self, + addr: SocketAddr, + reuse_port: bool, + reuse_addr: bool, + ) -> Result>; + + /// Creates a socket that can be used to send and receive ICMP packets + /// from a paritcular IP address + fn bind_icmp(&self, addr: IpAddr) -> Result>; + + /// Opens a TCP connection to a particular destination IP address and port + fn connect_tcp( + &self, + addr: SocketAddr, + peer: SocketAddr, + timeout: Option, + ) -> Result>; + + /// Performs DNS resolution for a specific hostname + fn resolve( + &self, + host: &str, + port: Option, + dns_server: Option, + ) -> Result>; +} + +/// Holds the interface used to work with a pending HTTP request +#[derive(Debug)] +pub struct SocketHttpRequest { + /// Used to send the request bytes to the HTTP server + /// (once all bytes are send the sender should be closed) + pub request: Option>>, + /// Used to receive the response bytes from the HTTP server + /// (once all the bytes have been received the receiver will be closed) + pub response: Option>>, + /// Used to receive all the headers from the HTTP server + /// (once all the headers have been received the receiver will be closed) + pub headers: Option>, + /// Used to watch for the status + pub status: Arc>>>, +} + +/// Represents the final result of a HTTP request +#[derive(Debug)] +pub struct HttpStatus { + /// Indicates if the HTTP request was redirected to another URL / server + pub redirected: bool, + /// Size of the data held in the response receiver + pub size: usize, + /// Status code returned by the server + pub status: u16, + /// Status text returned by the server + pub status_text: String, +} + +#[derive(Debug)] +pub struct SocketReceive { + /// Data that was received + pub data: Bytes, + /// Indicates if the data was truncated (e.g. UDP packet) + pub truncated: bool, +} + +#[derive(Debug)] +pub struct SocketReceiveFrom { + /// Data that was received + pub data: Bytes, + /// Indicates if the data was truncated (e.g. UDP packet) + pub truncated: bool, + /// Peer sender address of the data + pub addr: SocketAddr, +} + +pub trait VirtualTcpListener: fmt::Debug + Send + Sync + 'static { + /// Accepts an connection attempt that was made to this listener + fn accept(&self) -> Result<(Box, SocketAddr)>; + + /// Accepts an connection attempt that was made to this listener (or times out) + fn accept_timeout( + &self, + timeout: Duration, + ) -> Result<(Box, SocketAddr)>; + + /// Sets the accept timeout + fn set_timeout(&mut self, timeout: Option) -> Result<()>; + + /// Gets the accept timeout + fn timeout(&self) -> Result>; + + /// Returns the local address of this TCP listener + fn addr_local(&self) -> Result; + + /// Sets how many network hops the packets are permitted for new connections + fn set_ttl(&mut self, ttl: u8) -> Result<()>; + + /// Returns the maximum number of network hops before packets are dropped + fn ttl(&self) -> Result; +} + +pub trait VirtualSocket: fmt::Debug + Send + Sync + 'static { + /// Sets how many network hops the packets are permitted for new connections + fn set_ttl(&mut self, ttl: u32) -> Result<()>; + + /// Returns the maximum number of network hops before packets are dropped + fn ttl(&self) -> Result; + + /// Returns the local address for this socket + fn addr_local(&self) -> Result; + + /// Returns the status/state of the socket + fn status(&self) -> Result; +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum SocketStatus { + Opening, + Opened, + Closed, + Failed, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum StreamSecurity { + Unencrypted, + AnyEncyption, + ClassicEncryption, + DoubleEncryption, +} + +/// Interface used for sending and receiving data from a web socket +pub trait VirtualWebSocket: fmt::Debug + Send + Sync + 'static { + /// Sends out a datagram or stream of bytes on this socket + fn send(&mut self, data: Bytes) -> Result; + + /// FLushes all the datagrams + fn flush(&mut self) -> Result<()>; + + /// Recv a packet from the socket + fn recv(&mut self) -> Result; +} + +/// Connected sockets have a persistent connection to a remote peer +pub trait VirtualConnectedSocket: VirtualSocket + fmt::Debug + Send + Sync + 'static { + /// Determines how long the socket will remain in a TIME_WAIT + /// after it disconnects (only the one that initiates the close will + /// be in a TIME_WAIT state thus the clients should always do this rather + /// than the server) + fn set_linger(&mut self, linger: Option) -> Result<()>; + + /// Returns how long the socket will remain in a TIME_WAIT + /// after it disconnects + fn linger(&self) -> Result>; + + /// Sends out a datagram or stream of bytes on this socket + fn send(&mut self, data: Bytes) -> Result; + + /// FLushes all the datagrams + fn flush(&mut self) -> Result<()>; + + /// Recv a packet from the socket + fn recv(&mut self) -> Result; + + /// Peeks for a packet from the socket + fn peek(&mut self) -> Result; +} + +/// Connectionless sockets are able to send and receive datagrams and stream +/// bytes to multiple addresses at the same time (peer-to-peer) +pub trait VirtualConnectionlessSocket: VirtualSocket + fmt::Debug + Send + Sync + 'static { + /// Sends out a datagram or stream of bytes on this socket + /// to a specific address + fn send_to(&mut self, data: Bytes, addr: SocketAddr) -> Result; + + /// Recv a packet from the socket + fn recv_from(&mut self) -> Result; + + /// Peeks for a packet from the socket + fn peek_from(&mut self) -> Result; +} + +/// ICMP sockets are low level devices bound to a specific address +/// that can send and receive ICMP packets +pub trait VirtualIcmpSocket: + VirtualConnectionlessSocket + fmt::Debug + Send + Sync + 'static +{ +} + +pub trait VirtualRawSocket: VirtualSocket + fmt::Debug + Send + Sync + 'static { + /// Sends out a raw packet on this socket + fn send(&mut self, data: Bytes) -> Result; + + /// FLushes all the datagrams + fn flush(&mut self) -> Result<()>; + + /// Recv a packet from the socket + fn recv(&mut self) -> Result; + + /// Tells the raw socket and its backing switch that all packets + /// should be received by this socket even if they are not + /// destined for this device + fn set_promiscuous(&mut self, promiscuous: bool) -> Result<()>; + + /// Returns if the socket is running in promiscuous mode whereby it + /// will receive all packets even if they are not destined for the + /// local interface + fn promiscuous(&self) -> Result; +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum TimeType { + ReadTimeout, + WriteTimeout, + AcceptTimeout, + ConnectTimeout, + Linger, +} + +pub trait VirtualTcpSocket: VirtualConnectedSocket + fmt::Debug + Send + Sync + 'static { + /// Sets the timeout for a specific action on the socket + fn set_opt_time(&mut self, ty: TimeType, timeout: Option) -> Result<()>; + + /// Returns one of the previous set timeouts + fn opt_time(&self, ty: TimeType) -> Result>; + + /// Sets the receive buffer size which acts as a trottle for how + /// much data is buffered on this side of the pipe + fn set_recv_buf_size(&mut self, size: usize) -> Result<()>; + + /// Size of the receive buffer that holds all data that has not + /// yet been read + fn recv_buf_size(&self) -> Result; + + /// Sets the size of the send buffer which will hold the bytes of + /// data while they are being sent over to the peer + fn set_send_buf_size(&mut self, size: usize) -> Result<()>; + + /// Size of the send buffer that holds all data that is currently + /// being transmitted. + fn send_buf_size(&self) -> Result; + + /// When NO_DELAY is set the data that needs to be transmitted to + /// the peer is sent immediately rather than waiting for a bigger + /// batch of data, this reduces latency but increases encapsulation + /// overhead. + fn set_nodelay(&mut self, reuse: bool) -> Result<()>; + + /// Indicates if the NO_DELAY flag is set which means that data + /// is immediately sent to the peer without waiting. This reduces + /// latency but increases encapsulation overhead. + fn nodelay(&self) -> Result; + + /// Returns the address (IP and Port) of the peer socket that this + /// is conencted to + fn addr_peer(&self) -> Result; + + /// Causes all the data held in the send buffer to be immediately + /// flushed to the destination peer + fn flush(&mut self) -> Result<()>; + + /// Shuts down either the READER or WRITER sides of the socket + /// connection. + fn shutdown(&mut self, how: Shutdown) -> Result<()>; +} + +pub trait VirtualUdpSocket: + VirtualConnectedSocket + VirtualConnectionlessSocket + fmt::Debug + Send + Sync + 'static +{ + /// Connects to a destination peer so that the normal + /// send/recv operations can be used. + fn connect(&mut self, addr: SocketAddr) -> Result<()>; + + /// Sets a flag that means that the UDP socket is able + /// to receive and process broadcast packets. + fn set_broadcast(&mut self, broadcast: bool) -> Result<()>; + + /// Indicates if the SO_BROADCAST flag is set which means + /// that the UDP socket will receive and process broadcast + /// packets + fn broadcast(&self) -> Result; + + /// Sets a flag that indicates if multicast packets that + /// this socket is a member of will be looped back to + /// the sending socket. This applies to IPv4 addresses + fn set_multicast_loop_v4(&mut self, val: bool) -> Result<()>; + + /// Gets a flag that indicates if multicast packets that + /// this socket is a member of will be looped back to + /// the sending socket. This applies to IPv4 addresses + fn multicast_loop_v4(&self) -> Result; + + /// Sets a flag that indicates if multicast packets that + /// this socket is a member of will be looped back to + /// the sending socket. This applies to IPv6 addresses + fn set_multicast_loop_v6(&mut self, val: bool) -> Result<()>; + + /// Gets a flag that indicates if multicast packets that + /// this socket is a member of will be looped back to + /// the sending socket. This applies to IPv6 addresses + fn multicast_loop_v6(&self) -> Result; + + /// Sets the TTL for IPv4 multicast packets which is the + /// number of network hops before the packet is dropped + fn set_multicast_ttl_v4(&mut self, ttl: u32) -> Result<()>; + + /// Gets the TTL for IPv4 multicast packets which is the + /// number of network hops before the packet is dropped + fn multicast_ttl_v4(&self) -> Result; + + /// Tells this interface that it will subscribe to a + /// particular multicast address. This applies to IPv4 addresses + fn join_multicast_v4(&mut self, multiaddr: Ipv4Addr, iface: Ipv4Addr) -> Result<()>; + + /// Tells this interface that it will unsubscribe to a + /// particular multicast address. This applies to IPv4 addresses + fn leave_multicast_v4(&mut self, multiaddr: Ipv4Addr, iface: Ipv4Addr) -> Result<()>; + + /// Tells this interface that it will subscribe to a + /// particular multicast address. This applies to IPv6 addresses + fn join_multicast_v6(&mut self, multiaddr: Ipv6Addr, iface: u32) -> Result<()>; + + /// Tells this interface that it will unsubscribe to a + /// particular multicast address. This applies to IPv6 addresses + fn leave_multicast_v6(&mut self, multiaddr: Ipv6Addr, iface: u32) -> Result<()>; + + /// Returns the remote address of this UDP socket if it has been + /// connected to a specific target destination address + fn addr_peer(&self) -> Result>; +} + +#[derive(Debug, Default)] +pub struct UnsupportedVirtualNetworking {} + +impl VirtualNetworking for UnsupportedVirtualNetworking { + fn ws_connect(&self, _url: &str) -> Result> { + Err(NetworkError::Unsupported) + } + + fn http_request( + &self, + _url: &str, + _method: &str, + _headers: &str, + _gzip: bool, + ) -> Result { + Err(NetworkError::Unsupported) + } + + fn bridge(&self, _network: &str, _access_token: &str, _security: StreamSecurity) -> Result<()> { + Err(NetworkError::Unsupported) + } + + fn unbridge(&self) -> Result<()> { + Err(NetworkError::Unsupported) + } + + fn dhcp_acquire(&self) -> Result> { + Err(NetworkError::Unsupported) + } + + fn ip_add(&self, _ip: IpAddr, _prefix: u8) -> Result<()> { + Err(NetworkError::Unsupported) + } + + fn ip_remove(&self, _ip: IpAddr) -> Result<()> { + Err(NetworkError::Unsupported) + } + + fn ip_clear(&self) -> Result<()> { + Err(NetworkError::Unsupported) + } + + fn ip_list(&self) -> Result> { + Err(NetworkError::Unsupported) + } + + fn mac(&self) -> Result<[u8; 6]> { + Err(NetworkError::Unsupported) + } + + fn gateway_set(&self, _ip: IpAddr) -> Result<()> { + Err(NetworkError::Unsupported) + } + + fn route_add( + &self, + _cidr: IpCidr, + _via_router: IpAddr, + _preferred_until: Option, + _expires_at: Option, + ) -> Result<()> { + Err(NetworkError::Unsupported) + } + + fn route_remove(&self, _cidr: IpAddr) -> Result<()> { + Err(NetworkError::Unsupported) + } + + fn route_clear(&self) -> Result<()> { + Err(NetworkError::Unsupported) + } + + fn route_list(&self) -> Result> { + Err(NetworkError::Unsupported) + } + + fn bind_raw(&self) -> Result> { + Err(NetworkError::Unsupported) + } + + fn bind_icmp(&self, _addr: IpAddr) -> Result> { + Err(NetworkError::Unsupported) + } + + fn listen_tcp( + &self, + _addr: SocketAddr, + _only_v6: bool, + _reuse_port: bool, + _reuse_addr: bool, + ) -> Result> { + Err(NetworkError::Unsupported) + } + + fn connect_tcp( + &self, + _addr: SocketAddr, + _peer: SocketAddr, + _timeout: Option, + ) -> Result> { + Err(NetworkError::Unsupported) + } + + fn bind_udp( + &self, + _addr: SocketAddr, + _reuse_port: bool, + _reuse_addr: bool, + ) -> Result> { + Err(NetworkError::Unsupported) + } + + fn resolve( + &self, + _host: &str, + _port: Option, + _dns_server: Option, + ) -> Result> { + Err(NetworkError::Unsupported) + } +} + +#[derive(Error, Copy, Clone, Debug, PartialEq, Eq)] +pub enum NetworkError { + /// The handle given was not usable + #[error("invalid fd")] + InvalidFd, + /// File exists + #[error("file exists")] + AlreadyExists, + /// The filesystem has failed to lock a resource. + #[error("lock error")] + Lock, + /// Something failed when doing IO. These errors can generally not be handled. + /// It may work if tried again. + #[error("io error")] + IOError, + /// The address was in use + #[error("address is in use")] + AddressInUse, + /// The address could not be found + #[error("address could not be found")] + AddressNotAvailable, + /// A pipe was closed + #[error("broken pipe (was closed)")] + BrokenPipe, + /// The connection was aborted + #[error("connection aborted")] + ConnectionAborted, + /// The connection request was refused + #[error("connection refused")] + ConnectionRefused, + /// The connection was reset + #[error("connection reset")] + ConnectionReset, + /// The operation was interrupted before it could finish + #[error("operation interrupted")] + Interrupted, + /// Invalid internal data, if the argument data is invalid, use `InvalidInput` + #[error("invalid internal data")] + InvalidData, + /// The provided data is invalid + #[error("invalid input")] + InvalidInput, + /// Could not perform the operation because there was not an open connection + #[error("connection is not open")] + NotConnected, + /// The requested device couldn't be accessed + #[error("can't access device")] + NoDevice, + /// Caller was not allowed to perform this operation + #[error("permission denied")] + PermissionDenied, + /// The operation did not complete within the given amount of time + #[error("time out")] + TimedOut, + /// Found EOF when EOF was not expected + #[error("unexpected eof")] + UnexpectedEof, + /// Operation would block, this error lets the caller know that they can try again + #[error("blocking operation. try again")] + WouldBlock, + /// A call to write returned 0 + #[error("write returned 0")] + WriteZero, + /// The operation is not supported. + #[error("unsupported")] + Unsupported, + /// Some other unhandled error. If you see this, it's probably a bug. + #[error("unknown error found")] + UnknownError, +} + +pub fn net_error_into_io_err(net_error: NetworkError) -> std::io::Error { + use std::io::ErrorKind; + match net_error { + NetworkError::InvalidFd => ErrorKind::BrokenPipe.into(), + NetworkError::AlreadyExists => ErrorKind::AlreadyExists.into(), + NetworkError::Lock => ErrorKind::BrokenPipe.into(), + NetworkError::IOError => ErrorKind::BrokenPipe.into(), + NetworkError::AddressInUse => ErrorKind::AddrInUse.into(), + NetworkError::AddressNotAvailable => ErrorKind::AddrNotAvailable.into(), + NetworkError::BrokenPipe => ErrorKind::BrokenPipe.into(), + NetworkError::ConnectionAborted => ErrorKind::ConnectionAborted.into(), + NetworkError::ConnectionRefused => ErrorKind::ConnectionRefused.into(), + NetworkError::ConnectionReset => ErrorKind::ConnectionReset.into(), + NetworkError::Interrupted => ErrorKind::Interrupted.into(), + NetworkError::InvalidData => ErrorKind::InvalidData.into(), + NetworkError::InvalidInput => ErrorKind::InvalidInput.into(), + NetworkError::NotConnected => ErrorKind::NotConnected.into(), + NetworkError::NoDevice => ErrorKind::BrokenPipe.into(), + NetworkError::PermissionDenied => ErrorKind::PermissionDenied.into(), + NetworkError::TimedOut => ErrorKind::TimedOut.into(), + NetworkError::UnexpectedEof => ErrorKind::UnexpectedEof.into(), + NetworkError::WouldBlock => ErrorKind::WouldBlock.into(), + NetworkError::WriteZero => ErrorKind::WriteZero.into(), + NetworkError::Unsupported => ErrorKind::Unsupported.into(), + NetworkError::UnknownError => ErrorKind::BrokenPipe.into(), + } +} + +pub fn io_err_into_net_error(net_error: std::io::Error) -> NetworkError { + use std::io::ErrorKind; + match net_error.kind() { + ErrorKind::BrokenPipe => NetworkError::BrokenPipe, + ErrorKind::AlreadyExists => NetworkError::AlreadyExists, + ErrorKind::AddrInUse => NetworkError::AddressInUse, + ErrorKind::AddrNotAvailable => NetworkError::AddressNotAvailable, + ErrorKind::ConnectionAborted => NetworkError::ConnectionAborted, + ErrorKind::ConnectionRefused => NetworkError::ConnectionRefused, + ErrorKind::ConnectionReset => NetworkError::ConnectionReset, + ErrorKind::Interrupted => NetworkError::Interrupted, + ErrorKind::InvalidData => NetworkError::InvalidData, + ErrorKind::InvalidInput => NetworkError::InvalidInput, + ErrorKind::NotConnected => NetworkError::NotConnected, + ErrorKind::PermissionDenied => NetworkError::PermissionDenied, + ErrorKind::TimedOut => NetworkError::TimedOut, + ErrorKind::UnexpectedEof => NetworkError::UnexpectedEof, + ErrorKind::WouldBlock => NetworkError::WouldBlock, + ErrorKind::WriteZero => NetworkError::WriteZero, + ErrorKind::Unsupported => NetworkError::Unsupported, + _ => NetworkError::UnknownError, + } +} diff --git a/lib/wasi-experimental-io-devices/src/lib.rs b/lib/wasi-experimental-io-devices/src/lib.rs index 66b910bf7db..04008e359ce 100644 --- a/lib/wasi-experimental-io-devices/src/lib.rs +++ b/lib/wasi-experimental-io-devices/src/lib.rs @@ -3,7 +3,7 @@ use std::collections::{BTreeSet, VecDeque}; use std::convert::TryInto; use std::io::{Read, Seek, SeekFrom, Write}; use tracing::debug; -use wasmer_wasi::types::*; +use wasmer_wasi::{types::*, WasiInodes}; use wasmer_wasi::{Fd, VirtualFile, WasiFs, WasiFsError, ALL_RIGHTS, VIRTUAL_ROOT_FD}; use minifb::{Key, KeyRepeat, MouseButton, Scale, Window, WindowOptions}; @@ -426,7 +426,7 @@ impl VirtualFile for FrameBuffer { } } -pub fn initialize(fs: &mut WasiFs) -> Result<(), String> { +pub fn initialize(inodes: &mut WasiInodes, fs: &mut WasiFs) -> Result<(), String> { let frame_buffer_file = Box::new(FrameBuffer { fb_type: FrameBufferFileType::Buffer, cursor: 0, @@ -446,6 +446,7 @@ pub fn initialize(fs: &mut WasiFs) -> Result<(), String> { let base_dir_fd = unsafe { fs.open_dir_all( + inodes, VIRTUAL_ROOT_FD, "_wasmer/dev/fb0".to_string(), ALL_RIGHTS, @@ -457,6 +458,7 @@ pub fn initialize(fs: &mut WasiFs) -> Result<(), String> { let _fd = fs .open_file_at( + inodes, base_dir_fd, input_file, Fd::READ, @@ -471,6 +473,7 @@ pub fn initialize(fs: &mut WasiFs) -> Result<(), String> { let _fd = fs .open_file_at( + inodes, base_dir_fd, frame_buffer_file, Fd::READ | Fd::WRITE, @@ -485,6 +488,7 @@ pub fn initialize(fs: &mut WasiFs) -> Result<(), String> { let _fd = fs .open_file_at( + inodes, base_dir_fd, resolution_file, Fd::READ | Fd::WRITE, @@ -499,6 +503,7 @@ pub fn initialize(fs: &mut WasiFs) -> Result<(), String> { let _fd = fs .open_file_at( + inodes, base_dir_fd, draw_file, Fd::READ | Fd::WRITE, diff --git a/lib/wasi-local-networking/Cargo.toml b/lib/wasi-local-networking/Cargo.toml new file mode 100644 index 00000000000..ff18b704813 --- /dev/null +++ b/lib/wasi-local-networking/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "wasmer-wasi-local-networking" +version = "2.3.0" +description = "An WASIX extension for local networking" +categories = ["wasm"] +keywords = ["wasm", "webassembly", "types"] +authors = ["Wasmer Engineering Team "] +repository = "https://github.com/wasmerio/wasmer" +license = "MIT" +readme = "README.md" +edition = "2018" + +[badges] +maintenance = { status = "experimental" } + +[dependencies] +wasmer-vnet = { version = "=2.3.0", path = "../vnet", default-features = false } +wasmer-vfs = { path = "../vfs", version = "=2.3.0", default-features = false, features = [ "host-fs" ] } +tracing = "0.1" +bytes = "1.1" + +[features] +default = [ ] +wasix = [ ] diff --git a/lib/wasi-local-networking/README.md b/lib/wasi-local-networking/README.md new file mode 100644 index 00000000000..52a966a2046 --- /dev/null +++ b/lib/wasi-local-networking/README.md @@ -0,0 +1 @@ +This is experimental extension of WASIX for networking. diff --git a/lib/wasi-local-networking/src/lib.rs b/lib/wasi-local-networking/src/lib.rs new file mode 100644 index 00000000000..35bcb616f1e --- /dev/null +++ b/lib/wasi-local-networking/src/lib.rs @@ -0,0 +1,584 @@ +#![allow(unused_variables)] +use bytes::{Bytes, BytesMut}; +use std::io::{Read, Write}; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; +use std::time::Duration; +#[allow(unused_imports, dead_code)] +use tracing::{debug, error, info, trace, warn}; +use wasmer_vnet::{ + io_err_into_net_error, IpCidr, IpRoute, NetworkError, Result, SocketHttpRequest, SocketReceive, + SocketReceiveFrom, SocketStatus, StreamSecurity, TimeType, VirtualConnectedSocket, + VirtualConnectionlessSocket, VirtualIcmpSocket, VirtualNetworking, VirtualRawSocket, + VirtualSocket, VirtualTcpListener, VirtualTcpSocket, VirtualUdpSocket, VirtualWebSocket, +}; + +#[derive(Debug, Default)] +pub struct LocalNetworking {} + +#[allow(unused_variables)] +impl VirtualNetworking for LocalNetworking { + fn ws_connect(&self, url: &str) -> Result> { + Err(NetworkError::Unsupported) + } + + fn http_request( + &self, + url: &str, + method: &str, + headers: &str, + gzip: bool, + ) -> Result { + Err(NetworkError::Unsupported) + } + + fn bridge(&self, network: &str, access_token: &str, security: StreamSecurity) -> Result<()> { + Err(NetworkError::Unsupported) + } + + fn unbridge(&self) -> Result<()> { + Err(NetworkError::Unsupported) + } + + fn dhcp_acquire(&self) -> Result> { + Err(NetworkError::Unsupported) + } + + fn ip_add(&self, ip: IpAddr, prefix: u8) -> Result<()> { + Err(NetworkError::Unsupported) + } + + fn ip_remove(&self, ip: IpAddr) -> Result<()> { + Err(NetworkError::Unsupported) + } + + fn ip_clear(&self) -> Result<()> { + Err(NetworkError::Unsupported) + } + + fn ip_list(&self) -> Result> { + Err(NetworkError::Unsupported) + } + + fn mac(&self) -> Result<[u8; 6]> { + Err(NetworkError::Unsupported) + } + + fn gateway_set(&self, ip: IpAddr) -> Result<()> { + Err(NetworkError::Unsupported) + } + + fn route_add( + &self, + cidr: IpCidr, + via_router: IpAddr, + preferred_until: Option, + expires_at: Option, + ) -> Result<()> { + Err(NetworkError::Unsupported) + } + + fn route_remove(&self, cidr: IpAddr) -> Result<()> { + Err(NetworkError::Unsupported) + } + + fn route_clear(&self) -> Result<()> { + Err(NetworkError::Unsupported) + } + + fn route_list(&self) -> Result> { + Err(NetworkError::Unsupported) + } + + fn bind_raw(&self) -> Result> { + Err(NetworkError::Unsupported) + } + + fn listen_tcp( + &self, + addr: SocketAddr, + only_v6: bool, + reuse_port: bool, + reuse_addr: bool, + ) -> Result> { + let listener = std::net::TcpListener::bind(addr) + .map(|sock| { + Box::new(LocalTcpListener { + stream: sock, + timeout: None, + }) + }) + .map_err(io_err_into_net_error)?; + Ok(listener) + } + + fn bind_udp( + &self, + addr: SocketAddr, + _reuse_port: bool, + _reuse_addr: bool, + ) -> Result> { + let socket = std::net::UdpSocket::bind(addr).map_err(io_err_into_net_error)?; + Ok(Box::new(LocalUdpSocket(socket, addr))) + } + + fn bind_icmp(&self, addr: IpAddr) -> Result> { + Err(NetworkError::Unsupported) + } + + fn connect_tcp( + &self, + _addr: SocketAddr, + peer: SocketAddr, + timeout: Option, + ) -> Result> { + let stream = if let Some(timeout) = timeout { + std::net::TcpStream::connect_timeout(&peer, timeout) + } else { + std::net::TcpStream::connect(peer) + } + .map_err(io_err_into_net_error)?; + let peer = stream.peer_addr().map_err(io_err_into_net_error)?; + Ok(Box::new(LocalTcpStream { + stream, + addr: peer, + connect_timeout: None, + })) + } + + fn resolve( + &self, + host: &str, + port: Option, + dns_server: Option, + ) -> Result> { + use std::net::ToSocketAddrs; + Ok(if let Some(port) = port { + let host = format!("{}:{}", host, port); + host.to_socket_addrs() + .map(|a| a.map(|a| a.ip()).collect::>()) + .map_err(io_err_into_net_error)? + } else { + host.to_socket_addrs() + .map(|a| a.map(|a| a.ip()).collect::>()) + .map_err(io_err_into_net_error)? + }) + } +} + +#[derive(Debug)] +pub struct LocalTcpListener { + stream: std::net::TcpListener, + timeout: Option, +} + +impl VirtualTcpListener for LocalTcpListener { + fn accept(&self) -> Result<(Box, SocketAddr)> { + if let Some(timeout) = &self.timeout { + return self.accept_timeout(*timeout); + } + let (sock, addr) = self + .stream + .accept() + .map(|(sock, addr)| { + ( + Box::new(LocalTcpStream { + stream: sock, + addr, + connect_timeout: None, + }), + addr, + ) + }) + .map_err(io_err_into_net_error)?; + Ok((sock, addr)) + } + + #[cfg(feature = "wasix")] + fn accept_timeout( + &self, + timeout: Duration, + ) -> Result<(Box, SocketAddr)> { + let (sock, addr) = self + .stream + .accept_timeout(timeout) + .map(|(sock, addr)| { + ( + Box::new(LocalTcpStream { + stream: sock, + addr: addr.clone(), + connect_timeout: None, + }), + addr, + ) + }) + .map_err(io_err_into_net_error)?; + Ok((sock, addr)) + } + + #[cfg(not(feature = "wasix"))] + fn accept_timeout( + &self, + _timeout: Duration, + ) -> Result<(Box, SocketAddr)> { + self.accept() + } + + /// Sets the accept timeout + fn set_timeout(&mut self, timeout: Option) -> Result<()> { + self.timeout = timeout; + Ok(()) + } + + /// Gets the accept timeout + fn timeout(&self) -> Result> { + Ok(self.timeout) + } + + fn addr_local(&self) -> Result { + self.stream.local_addr().map_err(io_err_into_net_error) + } + + fn set_ttl(&mut self, ttl: u8) -> Result<()> { + self.stream + .set_ttl(ttl as u32) + .map_err(io_err_into_net_error) + } + + fn ttl(&self) -> Result { + self.stream + .ttl() + .map(|ttl| ttl as u8) + .map_err(io_err_into_net_error) + } +} + +#[derive(Debug)] +pub struct LocalTcpStream { + stream: std::net::TcpStream, + addr: SocketAddr, + connect_timeout: Option, +} + +impl VirtualTcpSocket for LocalTcpStream { + fn set_opt_time(&mut self, ty: TimeType, timeout: Option) -> Result<()> { + match ty { + TimeType::ReadTimeout => self + .stream + .set_read_timeout(timeout) + .map_err(io_err_into_net_error), + TimeType::WriteTimeout => self + .stream + .set_write_timeout(timeout) + .map_err(io_err_into_net_error), + TimeType::ConnectTimeout => { + self.connect_timeout = timeout; + Ok(()) + } + #[cfg(feature = "wasix")] + TimeType::Linger => self + .stream + .set_linger(timeout) + .map_err(io_err_into_net_error), + _ => Err(NetworkError::InvalidInput), + } + } + + fn opt_time(&self, ty: TimeType) -> Result> { + match ty { + TimeType::ReadTimeout => self.stream.read_timeout().map_err(io_err_into_net_error), + TimeType::WriteTimeout => self.stream.write_timeout().map_err(io_err_into_net_error), + TimeType::ConnectTimeout => Ok(self.connect_timeout), + #[cfg(feature = "wasix")] + TimeType::Linger => self.stream.linger().map_err(io_err_into_net_error), + _ => Err(NetworkError::InvalidInput), + } + } + + fn set_recv_buf_size(&mut self, size: usize) -> Result<()> { + Ok(()) + } + + fn recv_buf_size(&self) -> Result { + Err(NetworkError::Unsupported) + } + + fn set_send_buf_size(&mut self, size: usize) -> Result<()> { + Ok(()) + } + + fn send_buf_size(&self) -> Result { + Err(NetworkError::Unsupported) + } + + fn set_nodelay(&mut self, nodelay: bool) -> Result<()> { + self.stream + .set_nodelay(nodelay) + .map_err(io_err_into_net_error) + } + + fn nodelay(&self) -> Result { + self.stream.nodelay().map_err(io_err_into_net_error) + } + + fn addr_peer(&self) -> Result { + Ok(self.addr) + } + + fn flush(&mut self) -> Result<()> { + Ok(()) + } + + fn shutdown(&mut self, how: Shutdown) -> Result<()> { + self.stream.shutdown(how).map_err(io_err_into_net_error) + } +} + +impl VirtualConnectedSocket for LocalTcpStream { + fn set_linger(&mut self, linger: Option) -> Result<()> { + #[cfg(feature = "wasix")] + self.stream + .set_linger(linger) + .map_err(io_err_into_net_error)?; + Ok(()) + } + + #[cfg(feature = "wasix")] + fn linger(&self) -> Result> { + self.stream.linger().map_err(io_err_into_net_error) + } + + #[cfg(not(feature = "wasix"))] + fn linger(&self) -> Result> { + Ok(None) + } + + fn send(&mut self, data: Bytes) -> Result { + self.stream + .write_all(&data[..]) + .map(|_| data.len()) + .map_err(io_err_into_net_error) + } + + fn flush(&mut self) -> Result<()> { + self.stream.flush().map_err(io_err_into_net_error) + } + + fn recv(&mut self) -> Result { + let buf_size = 8192; + let mut buf = BytesMut::with_capacity(buf_size); + let read = self + .stream + .read(&mut buf[..]) + .map_err(io_err_into_net_error)?; + let buf = Bytes::from(buf).slice(..read); + Ok(SocketReceive { + data: buf, + truncated: read == buf_size, + }) + } + + fn peek(&mut self) -> Result { + let buf_size = 8192; + let mut buf = BytesMut::with_capacity(buf_size); + let read = self + .stream + .peek(&mut buf[..]) + .map_err(io_err_into_net_error)?; + let buf = Bytes::from(buf).slice(..read); + Ok(SocketReceive { + data: buf, + truncated: read == buf_size, + }) + } +} + +impl VirtualSocket for LocalTcpStream { + fn set_ttl(&mut self, ttl: u32) -> Result<()> { + self.stream.set_ttl(ttl).map_err(io_err_into_net_error) + } + + fn ttl(&self) -> Result { + self.stream.ttl().map_err(io_err_into_net_error) + } + + fn addr_local(&self) -> Result { + self.stream.local_addr().map_err(io_err_into_net_error) + } + + fn status(&self) -> Result { + Ok(SocketStatus::Opened) + } +} + +#[derive(Debug)] +pub struct LocalUdpSocket(std::net::UdpSocket, SocketAddr); + +impl VirtualUdpSocket for LocalUdpSocket { + fn connect(&mut self, addr: SocketAddr) -> Result<()> { + self.0.connect(addr).map_err(io_err_into_net_error) + } + + fn set_broadcast(&mut self, broadcast: bool) -> Result<()> { + self.0 + .set_broadcast(broadcast) + .map_err(io_err_into_net_error) + } + + fn broadcast(&self) -> Result { + self.0.broadcast().map_err(io_err_into_net_error) + } + + fn set_multicast_loop_v4(&mut self, val: bool) -> Result<()> { + self.0 + .set_multicast_loop_v4(val) + .map_err(io_err_into_net_error) + } + + fn multicast_loop_v4(&self) -> Result { + self.0.multicast_loop_v4().map_err(io_err_into_net_error) + } + + fn set_multicast_loop_v6(&mut self, val: bool) -> Result<()> { + self.0 + .set_multicast_loop_v6(val) + .map_err(io_err_into_net_error) + } + + fn multicast_loop_v6(&self) -> Result { + self.0.multicast_loop_v6().map_err(io_err_into_net_error) + } + + fn set_multicast_ttl_v4(&mut self, ttl: u32) -> Result<()> { + self.0 + .set_multicast_ttl_v4(ttl) + .map_err(io_err_into_net_error) + } + + fn multicast_ttl_v4(&self) -> Result { + self.0.multicast_ttl_v4().map_err(io_err_into_net_error) + } + + fn join_multicast_v4(&mut self, multiaddr: Ipv4Addr, iface: Ipv4Addr) -> Result<()> { + self.0 + .join_multicast_v4(&multiaddr, &iface) + .map_err(io_err_into_net_error) + } + + fn leave_multicast_v4(&mut self, multiaddr: Ipv4Addr, iface: Ipv4Addr) -> Result<()> { + self.0 + .leave_multicast_v4(&multiaddr, &iface) + .map_err(io_err_into_net_error) + } + + fn join_multicast_v6(&mut self, multiaddr: Ipv6Addr, iface: u32) -> Result<()> { + self.0 + .join_multicast_v6(&multiaddr, iface) + .map_err(io_err_into_net_error) + } + + fn leave_multicast_v6(&mut self, multiaddr: Ipv6Addr, iface: u32) -> Result<()> { + self.0 + .leave_multicast_v6(&multiaddr, iface) + .map_err(io_err_into_net_error) + } + + fn addr_peer(&self) -> Result> { + self.0.peer_addr().map(Some).map_err(io_err_into_net_error) + } +} + +impl VirtualConnectedSocket for LocalUdpSocket { + fn set_linger(&mut self, linger: Option) -> Result<()> { + Err(NetworkError::Unsupported) + } + + fn linger(&self) -> Result> { + Err(NetworkError::Unsupported) + } + + fn send(&mut self, data: Bytes) -> Result { + self.0.send(&data[..]).map_err(io_err_into_net_error) + } + + fn flush(&mut self) -> Result<()> { + Ok(()) + } + + fn recv(&mut self) -> Result { + let buf_size = 8192; + let mut buf = BytesMut::with_capacity(buf_size); + let read = self.0.recv(&mut buf[..]).map_err(io_err_into_net_error)?; + let buf = Bytes::from(buf).slice(..read); + Ok(SocketReceive { + data: buf, + truncated: read == buf_size, + }) + } + + fn peek(&mut self) -> Result { + let buf_size = 8192; + let mut buf = BytesMut::with_capacity(buf_size); + let read = self.0.peek(&mut buf[..]).map_err(io_err_into_net_error)?; + let buf = Bytes::from(buf).slice(..read); + Ok(SocketReceive { + data: buf, + truncated: read == buf_size, + }) + } +} + +impl VirtualConnectionlessSocket for LocalUdpSocket { + fn send_to(&mut self, data: Bytes, addr: SocketAddr) -> Result { + self.0 + .send_to(&data[..], addr) + .map_err(io_err_into_net_error) + } + + fn recv_from(&mut self) -> Result { + let buf_size = 8192; + let mut buf = BytesMut::with_capacity(buf_size); + let (read, peer) = self + .0 + .recv_from(&mut buf[..]) + .map_err(io_err_into_net_error)?; + let buf = Bytes::from(buf).slice(..read); + Ok(SocketReceiveFrom { + data: buf, + truncated: read == buf_size, + addr: peer, + }) + } + + fn peek_from(&mut self) -> Result { + let buf_size = 8192; + let mut buf = BytesMut::with_capacity(buf_size); + let (read, peer) = self + .0 + .peek_from(&mut buf[..]) + .map_err(io_err_into_net_error)?; + let buf = Bytes::from(buf).slice(..read); + Ok(SocketReceiveFrom { + data: buf, + truncated: read == buf_size, + addr: peer, + }) + } +} + +impl VirtualSocket for LocalUdpSocket { + fn set_ttl(&mut self, ttl: u32) -> Result<()> { + self.0.set_ttl(ttl).map_err(io_err_into_net_error) + } + + fn ttl(&self) -> Result { + self.0.ttl().map_err(io_err_into_net_error) + } + + fn addr_local(&self) -> Result { + self.0.local_addr().map_err(io_err_into_net_error) + } + + fn status(&self) -> Result { + Ok(SocketStatus::Opened) + } +} diff --git a/lib/wasi-types/src/bus.rs b/lib/wasi-types/src/bus.rs new file mode 100644 index 00000000000..ff8bea2ecb3 --- /dev/null +++ b/lib/wasi-types/src/bus.rs @@ -0,0 +1,113 @@ +use super::*; +use wasmer_derive::ValueType; +use wasmer_types::MemorySize; + +pub type __wasi_busdataformat_t = u8; +pub const __WASI_BUS_DATA_FORMAT_RAW: __wasi_busdataformat_t = 0; +pub const __WASI_BUS_DATA_FORMAT_BINCODE: __wasi_busdataformat_t = 1; +pub const __WASI_BUS_DATA_FORMAT_MESSAGE_PACK: __wasi_busdataformat_t = 2; +pub const __WASI_BUS_DATA_FORMAT_JSON: __wasi_busdataformat_t = 3; +pub const __WASI_BUS_DATA_FORMAT_YAML: __wasi_busdataformat_t = 4; +pub const __WASI_BUS_DATA_FORMAT_XML: __wasi_busdataformat_t = 5; +pub const __WASI_BUS_DATA_FORMAT_RKYV: __wasi_busdataformat_t = 6; + +pub type __wasi_buseventtype_t = u8; +pub const __WASI_BUS_EVENT_TYPE_NOOP: __wasi_buseventtype_t = 0; +pub const __WASI_BUS_EVENT_TYPE_EXIT: __wasi_buseventtype_t = 1; +pub const __WASI_BUS_EVENT_TYPE_CALL: __wasi_buseventtype_t = 2; +pub const __WASI_BUS_EVENT_TYPE_RESULT: __wasi_buseventtype_t = 3; +pub const __WASI_BUS_EVENT_TYPE_FAULT: __wasi_buseventtype_t = 4; +pub const __WASI_BUS_EVENT_TYPE_CLOSE: __wasi_buseventtype_t = 5; + +pub type __wasi_bid_t = u32; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueType)] +#[repr(C)] +pub struct __wasi_option_bid_t { + pub tag: __wasi_option_t, + pub bid: __wasi_bid_t, +} + +pub type __wasi_cid_t = u8; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueType)] +#[repr(C)] +pub struct __wasi_option_fd_t { + pub tag: __wasi_option_t, + pub fd: __wasi_fd_t, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueType)] +#[repr(C)] +pub struct __wasi_option_cid_t { + pub tag: __wasi_option_t, + pub cid: __wasi_cid_t, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueType)] +#[repr(C)] +pub struct __wasi_bus_handles_t { + pub bid: __wasi_bid_t, + pub stdin: __wasi_option_fd_t, + pub stdout: __wasi_option_fd_t, + pub stderr: __wasi_option_fd_t, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueType)] +#[repr(C)] +pub struct __wasi_busevent_exit_t { + pub bid: __wasi_bid_t, + pub rval: __wasi_exitcode_t, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueType)] +#[repr(C)] +pub struct __wasi_busevent_call_t { + pub parent: __wasi_option_cid_t, + pub cid: __wasi_cid_t, + pub format: __wasi_busdataformat_t, + pub topic_ptr: M::Offset, + pub topic_len: M::Offset, + pub buf_ptr: M::Offset, + pub buf_len: M::Offset, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueType)] +#[repr(C)] +pub struct __wasi_busevent_result_t { + pub format: __wasi_busdataformat_t, + pub cid: __wasi_cid_t, + pub buf_ptr: M::Offset, + pub buf_len: M::Offset, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueType)] +#[repr(C)] +pub struct __wasi_busevent_fault_t { + pub cid: __wasi_cid_t, + pub err: __bus_errno_t, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueType)] +#[repr(C)] +pub struct __wasi_busevent_close_t { + pub cid: __wasi_cid_t, +} + +#[derive(Copy, Clone)] +#[repr(C)] +pub union __wasi_busevent_u { + pub noop: u8, + pub exit: __wasi_busevent_exit_t, + pub call: __wasi_busevent_call_t, + pub result: __wasi_busevent_result_t, + pub fault: __wasi_busevent_fault_t, + pub close: __wasi_busevent_close_t, +} + +#[derive(Copy, Clone)] +#[repr(C)] +pub struct __wasi_busevent_t { + pub tag: __wasi_buseventtype_t, + pub u: __wasi_busevent_u, +} diff --git a/lib/wasi-types/src/error.rs b/lib/wasi-types/src/error.rs index 87d01bb0fac..fa1bf528126 100644 --- a/lib/wasi-types/src/error.rs +++ b/lib/wasi-types/src/error.rs @@ -76,3 +76,25 @@ pub const __WASI_ETIMEDOUT: u16 = 73; pub const __WASI_ETXTBSY: u16 = 74; pub const __WASI_EXDEV: u16 = 75; pub const __WASI_ENOTCAPABLE: u16 = 76; + +pub type __bus_errno_t = u32; +pub const __BUS_ESUCCESS: u32 = 0; +pub const __BUS_ESER: u32 = 1; +pub const __BUS_EDES: u32 = 2; +pub const __BUS_EWAPM: u32 = 3; +pub const __BUS_EFETCH: u32 = 4; +pub const __BUS_ECOMPILE: u32 = 5; +pub const __BUS_EABI: u32 = 6; +pub const __BUS_EABORTED: u32 = 7; +pub const __BUS_EBADHANDLE: u32 = 8; +pub const __BUS_ETOPIC: u32 = 9; +pub const __BUS_EBADCB: u32 = 10; +pub const __BUS_EUNSUPPORTED: u32 = 11; +pub const __BUS_EBADREQUEST: u32 = 12; +pub const __BUS_EDENIED: u32 = 13; +pub const __BUS_EINTERNAL: u32 = 14; +pub const __BUS_EALLOC: u32 = 15; +pub const __BUS_EINVOKE: u32 = 16; +pub const __BUS_ECONSUMED: u32 = 17; +pub const __BUS_EMEMVIOLATION: u32 = 18; +pub const __BUS_EUNKNOWN: u32 = 19; diff --git a/lib/wasi-types/src/file.rs b/lib/wasi-types/src/file.rs index 356a13398f8..0d58ba9bbe4 100644 --- a/lib/wasi-types/src/file.rs +++ b/lib/wasi-types/src/file.rs @@ -11,19 +11,25 @@ use wasmer_types::ValueType; pub type __wasi_device_t = u64; pub type __wasi_fd_t = u32; -pub const __WASI_STDIN_FILENO: u32 = 0; -pub const __WASI_STDOUT_FILENO: u32 = 1; -pub const __WASI_STDERR_FILENO: u32 = 2; +pub const __WASI_STDIN_FILENO: __wasi_fd_t = 0; +pub const __WASI_STDOUT_FILENO: __wasi_fd_t = 1; +pub const __WASI_STDERR_FILENO: __wasi_fd_t = 2; + +pub type __wasi_pid_t = u32; +pub type __wasi_tid_t = u32; pub type __wasi_fdflags_t = u16; -pub const __WASI_FDFLAG_APPEND: u16 = 1 << 0; -pub const __WASI_FDFLAG_DSYNC: u16 = 1 << 1; -pub const __WASI_FDFLAG_NONBLOCK: u16 = 1 << 2; -pub const __WASI_FDFLAG_RSYNC: u16 = 1 << 3; -pub const __WASI_FDFLAG_SYNC: u16 = 1 << 4; +pub const __WASI_FDFLAG_APPEND: __wasi_fdflags_t = 1 << 0; +pub const __WASI_FDFLAG_DSYNC: __wasi_fdflags_t = 1 << 1; +pub const __WASI_FDFLAG_NONBLOCK: __wasi_fdflags_t = 1 << 2; +pub const __WASI_FDFLAG_RSYNC: __wasi_fdflags_t = 1 << 3; +pub const __WASI_FDFLAG_SYNC: __wasi_fdflags_t = 1 << 4; + +pub type __wasi_eventfdflags = u16; +pub const __WASI_EVENTFDFLAGS_SEMAPHORE: __wasi_eventfdflags = 1 << 0; pub type __wasi_preopentype_t = u8; -pub const __WASI_PREOPENTYPE_DIR: u8 = 0; +pub const __WASI_PREOPENTYPE_DIR: __wasi_preopentype_t = 0; #[derive(Debug, Copy, Clone, PartialEq, Eq, ValueType)] #[repr(C)] @@ -204,64 +210,76 @@ pub fn wasi_filetype_to_name(ft: __wasi_filetype_t) -> &'static str { } pub type __wasi_filetype_t = u8; -pub const __WASI_FILETYPE_UNKNOWN: u8 = 0; -pub const __WASI_FILETYPE_BLOCK_DEVICE: u8 = 1; -pub const __WASI_FILETYPE_CHARACTER_DEVICE: u8 = 2; -pub const __WASI_FILETYPE_DIRECTORY: u8 = 3; -pub const __WASI_FILETYPE_REGULAR_FILE: u8 = 4; -pub const __WASI_FILETYPE_SOCKET_DGRAM: u8 = 5; -pub const __WASI_FILETYPE_SOCKET_STREAM: u8 = 6; -pub const __WASI_FILETYPE_SYMBOLIC_LINK: u8 = 7; +pub const __WASI_FILETYPE_UNKNOWN: __wasi_filetype_t = 0; +pub const __WASI_FILETYPE_BLOCK_DEVICE: __wasi_filetype_t = 1; +pub const __WASI_FILETYPE_CHARACTER_DEVICE: __wasi_filetype_t = 2; +pub const __WASI_FILETYPE_DIRECTORY: __wasi_filetype_t = 3; +pub const __WASI_FILETYPE_REGULAR_FILE: __wasi_filetype_t = 4; +pub const __WASI_FILETYPE_SOCKET_DGRAM: __wasi_filetype_t = 5; +pub const __WASI_FILETYPE_SOCKET_STREAM: __wasi_filetype_t = 6; +pub const __WASI_FILETYPE_SYMBOLIC_LINK: __wasi_filetype_t = 7; +pub const __WASI_FILETYPE_SOCKET_RAW: __wasi_filetype_t = 8; +pub const __WASI_FILETYPE_SOCKET_SEQPACKET: __wasi_filetype_t = 9; pub type __wasi_fstflags_t = u16; -pub const __WASI_FILESTAT_SET_ATIM: u16 = 1 << 0; -pub const __WASI_FILESTAT_SET_ATIM_NOW: u16 = 1 << 1; -pub const __WASI_FILESTAT_SET_MTIM: u16 = 1 << 2; -pub const __WASI_FILESTAT_SET_MTIM_NOW: u16 = 1 << 3; +pub const __WASI_FILESTAT_SET_ATIM: __wasi_fstflags_t = 1 << 0; +pub const __WASI_FILESTAT_SET_ATIM_NOW: __wasi_fstflags_t = 1 << 1; +pub const __WASI_FILESTAT_SET_MTIM: __wasi_fstflags_t = 1 << 2; +pub const __WASI_FILESTAT_SET_MTIM_NOW: __wasi_fstflags_t = 1 << 3; pub type __wasi_inode_t = u64; pub type __wasi_linkcount_t = u64; pub type __wasi_lookupflags_t = u32; -pub const __WASI_LOOKUP_SYMLINK_FOLLOW: u32 = 1 << 0; +pub const __WASI_LOOKUP_SYMLINK_FOLLOW: __wasi_lookupflags_t = 1 << 0; pub type __wasi_oflags_t = u16; -pub const __WASI_O_CREAT: u16 = 1 << 0; -pub const __WASI_O_DIRECTORY: u16 = 1 << 1; -pub const __WASI_O_EXCL: u16 = 1 << 2; -pub const __WASI_O_TRUNC: u16 = 1 << 3; +pub const __WASI_O_CREAT: __wasi_oflags_t = 1 << 0; +pub const __WASI_O_DIRECTORY: __wasi_oflags_t = 1 << 1; +pub const __WASI_O_EXCL: __wasi_oflags_t = 1 << 2; +pub const __WASI_O_TRUNC: __wasi_oflags_t = 1 << 3; pub type __wasi_rights_t = u64; -pub const __WASI_RIGHT_FD_DATASYNC: u64 = 1 << 0; -pub const __WASI_RIGHT_FD_READ: u64 = 1 << 1; -pub const __WASI_RIGHT_FD_SEEK: u64 = 1 << 2; -pub const __WASI_RIGHT_FD_FDSTAT_SET_FLAGS: u64 = 1 << 3; -pub const __WASI_RIGHT_FD_SYNC: u64 = 1 << 4; -pub const __WASI_RIGHT_FD_TELL: u64 = 1 << 5; -pub const __WASI_RIGHT_FD_WRITE: u64 = 1 << 6; -pub const __WASI_RIGHT_FD_ADVISE: u64 = 1 << 7; -pub const __WASI_RIGHT_FD_ALLOCATE: u64 = 1 << 8; -pub const __WASI_RIGHT_PATH_CREATE_DIRECTORY: u64 = 1 << 9; -pub const __WASI_RIGHT_PATH_CREATE_FILE: u64 = 1 << 10; -pub const __WASI_RIGHT_PATH_LINK_SOURCE: u64 = 1 << 11; -pub const __WASI_RIGHT_PATH_LINK_TARGET: u64 = 1 << 12; -pub const __WASI_RIGHT_PATH_OPEN: u64 = 1 << 13; -pub const __WASI_RIGHT_FD_READDIR: u64 = 1 << 14; -pub const __WASI_RIGHT_PATH_READLINK: u64 = 1 << 15; -pub const __WASI_RIGHT_PATH_RENAME_SOURCE: u64 = 1 << 16; -pub const __WASI_RIGHT_PATH_RENAME_TARGET: u64 = 1 << 17; -pub const __WASI_RIGHT_PATH_FILESTAT_GET: u64 = 1 << 18; -pub const __WASI_RIGHT_PATH_FILESTAT_SET_SIZE: u64 = 1 << 19; -pub const __WASI_RIGHT_PATH_FILESTAT_SET_TIMES: u64 = 1 << 20; -pub const __WASI_RIGHT_FD_FILESTAT_GET: u64 = 1 << 21; -pub const __WASI_RIGHT_FD_FILESTAT_SET_SIZE: u64 = 1 << 22; -pub const __WASI_RIGHT_FD_FILESTAT_SET_TIMES: u64 = 1 << 23; -pub const __WASI_RIGHT_PATH_SYMLINK: u64 = 1 << 24; -pub const __WASI_RIGHT_PATH_REMOVE_DIRECTORY: u64 = 1 << 25; -pub const __WASI_RIGHT_PATH_UNLINK_FILE: u64 = 1 << 26; -pub const __WASI_RIGHT_POLL_FD_READWRITE: u64 = 1 << 27; -pub const __WASI_RIGHT_SOCK_SHUTDOWN: u64 = 1 << 28; +pub const __WASI_RIGHT_FD_DATASYNC: __wasi_rights_t = 1 << 0; +pub const __WASI_RIGHT_FD_READ: __wasi_rights_t = 1 << 1; +pub const __WASI_RIGHT_FD_SEEK: __wasi_rights_t = 1 << 2; +pub const __WASI_RIGHT_FD_FDSTAT_SET_FLAGS: __wasi_rights_t = 1 << 3; +pub const __WASI_RIGHT_FD_SYNC: __wasi_rights_t = 1 << 4; +pub const __WASI_RIGHT_FD_TELL: __wasi_rights_t = 1 << 5; +pub const __WASI_RIGHT_FD_WRITE: __wasi_rights_t = 1 << 6; +pub const __WASI_RIGHT_FD_ADVISE: __wasi_rights_t = 1 << 7; +pub const __WASI_RIGHT_FD_ALLOCATE: __wasi_rights_t = 1 << 8; +pub const __WASI_RIGHT_PATH_CREATE_DIRECTORY: __wasi_rights_t = 1 << 9; +pub const __WASI_RIGHT_PATH_CREATE_FILE: __wasi_rights_t = 1 << 10; +pub const __WASI_RIGHT_PATH_LINK_SOURCE: __wasi_rights_t = 1 << 11; +pub const __WASI_RIGHT_PATH_LINK_TARGET: __wasi_rights_t = 1 << 12; +pub const __WASI_RIGHT_PATH_OPEN: __wasi_rights_t = 1 << 13; +pub const __WASI_RIGHT_FD_READDIR: __wasi_rights_t = 1 << 14; +pub const __WASI_RIGHT_PATH_READLINK: __wasi_rights_t = 1 << 15; +pub const __WASI_RIGHT_PATH_RENAME_SOURCE: __wasi_rights_t = 1 << 16; +pub const __WASI_RIGHT_PATH_RENAME_TARGET: __wasi_rights_t = 1 << 17; +pub const __WASI_RIGHT_PATH_FILESTAT_GET: __wasi_rights_t = 1 << 18; +pub const __WASI_RIGHT_PATH_FILESTAT_SET_SIZE: __wasi_rights_t = 1 << 19; +pub const __WASI_RIGHT_PATH_FILESTAT_SET_TIMES: __wasi_rights_t = 1 << 20; +pub const __WASI_RIGHT_FD_FILESTAT_GET: __wasi_rights_t = 1 << 21; +pub const __WASI_RIGHT_FD_FILESTAT_SET_SIZE: __wasi_rights_t = 1 << 22; +pub const __WASI_RIGHT_FD_FILESTAT_SET_TIMES: __wasi_rights_t = 1 << 23; +pub const __WASI_RIGHT_PATH_SYMLINK: __wasi_rights_t = 1 << 24; +pub const __WASI_RIGHT_PATH_REMOVE_DIRECTORY: __wasi_rights_t = 1 << 25; +pub const __WASI_RIGHT_PATH_UNLINK_FILE: __wasi_rights_t = 1 << 26; +pub const __WASI_RIGHT_POLL_FD_READWRITE: __wasi_rights_t = 1 << 27; +pub const __WASI_RIGHT_SOCK_SHUTDOWN: __wasi_rights_t = 1 << 28; +pub const __WASI_RIGHT_SOCK_ACCEPT: __wasi_rights_t = 1 << 29; +pub const __WASI_RIGHT_SOCK_CONNECT: __wasi_rights_t = 1 << 30; +pub const __WASI_RIGHT_SOCK_LISTEN: __wasi_rights_t = 1 << 31; +pub const __WASI_RIGHT_SOCK_BIND: __wasi_rights_t = 1 << 32; +pub const __WASI_RIGHT_SOCK_RECV: __wasi_rights_t = 1 << 33; +pub const __WASI_RIGHT_SOCK_SEND: __wasi_rights_t = 1 << 34; +pub const __WASI_RIGHT_SOCK_ADDR_LOCAL: __wasi_rights_t = 1 << 35; +pub const __WASI_RIGHT_SOCK_ADDR_REMOTE: __wasi_rights_t = 1 << 36; +pub const __WASI_RIGHT_SOCK_RECV_FROM: __wasi_rights_t = 1 << 37; +pub const __WASI_RIGHT_SOCK_SEND_TO: __wasi_rights_t = 1 << 38; /// function for debugging rights issues #[allow(dead_code)] @@ -313,20 +331,7 @@ pub fn right_to_string(right: __wasi_rights_t) -> Option<&'static str> { }) } -pub type __wasi_riflags_t = u16; -pub const __WASI_SOCK_RECV_PEEK: u16 = 1 << 0; -pub const __WASI_SOCK_RECV_WAITALL: u16 = 1 << 1; - -pub type __wasi_roflags_t = u16; -pub const __WASI_SOCK_RECV_DATA_TRUNCATED: u16 = 1 << 0; - pub type __wasi_whence_t = u8; -pub const __WASI_WHENCE_SET: u8 = 0; -pub const __WASI_WHENCE_CUR: u8 = 1; -pub const __WASI_WHENCE_END: u8 = 2; - -pub type __wasi_sdflags_t = u8; -pub const __WASI_SHUT_RD: u8 = 1 << 0; -pub const __WASI_SHUT_WR: u8 = 1 << 1; - -pub type __wasi_siflags_t = u16; +pub const __WASI_WHENCE_SET: __wasi_whence_t = 0; +pub const __WASI_WHENCE_CUR: __wasi_whence_t = 1; +pub const __WASI_WHENCE_END: __wasi_whence_t = 2; diff --git a/lib/wasi-types/src/io.rs b/lib/wasi-types/src/io.rs index 55d3fc5c1c6..f80ffcfb3eb 100644 --- a/lib/wasi-types/src/io.rs +++ b/lib/wasi-types/src/io.rs @@ -1,15 +1,55 @@ use wasmer_derive::ValueType; +use wasmer_types::MemorySize; + +use crate::__wasi_fd_t; + +pub type __wasi_count_t = u32; + +pub type __wasi_option_t = u8; +pub const __WASI_OPTION_NONE: __wasi_option_t = 0; +pub const __WASI_OPTION_SOME: __wasi_option_t = 1; + +pub type __wasi_bool_t = u8; +pub const __WASI_BOOL_FALSE: __wasi_bool_t = 0; +pub const __WASI_BOOL_TRUE: __wasi_bool_t = 1; #[derive(Debug, Copy, Clone, PartialEq, Eq, ValueType)] #[repr(C)] -pub struct __wasi_ciovec_t { - pub buf: u32, - pub buf_len: u32, +pub struct __wasi_ciovec_t { + pub buf: M::Offset, + pub buf_len: M::Offset, } #[derive(Debug, Copy, Clone, PartialEq, Eq, ValueType)] #[repr(C)] -pub struct __wasi_iovec_t { - pub buf: u32, - pub buf_len: u32, +pub struct __wasi_iovec_t { + pub buf: M::Offset, + pub buf_len: M::Offset, } + +#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueType)] +#[repr(C)] +pub struct __wasi_tty_t { + pub cols: u32, + pub rows: u32, + pub width: u32, + pub height: u32, + pub stdin_tty: __wasi_bool_t, + pub stdout_tty: __wasi_bool_t, + pub stderr_tty: __wasi_bool_t, + pub echo: __wasi_bool_t, + pub line_buffered: __wasi_bool_t, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueType)] +#[repr(C)] +pub struct __wasi_pipe_handles_t { + pub pipe: __wasi_fd_t, + pub other: __wasi_fd_t, +} + +pub type __wasi_stdiomode_t = u8; +pub const __WASI_STDIO_MODE_PIPED: __wasi_stdiomode_t = 1; +pub const __WASI_STDIO_MODE_INHERIT: __wasi_stdiomode_t = 2; +pub const __WASI_STDIO_MODE_NULL: __wasi_stdiomode_t = 3; +pub const __WASI_STDIO_MODE_LOG: __wasi_stdiomode_t = 4; diff --git a/lib/wasi-types/src/lib.rs b/lib/wasi-types/src/lib.rs index 5a0acab713e..14f9bff188b 100644 --- a/lib/wasi-types/src/lib.rs +++ b/lib/wasi-types/src/lib.rs @@ -12,11 +12,13 @@ extern crate wasmer_types as wasmer; mod advice; +mod bus; mod directory; mod error; mod event; mod file; mod io; +mod net; mod signal; mod subscription; mod time; @@ -24,11 +26,13 @@ mod versions; pub use crate::time::*; pub use advice::*; +pub use bus::*; pub use directory::*; pub use error::*; pub use event::*; pub use file::*; pub use io::*; +pub use net::*; pub use signal::*; pub use subscription::*; pub use versions::*; diff --git a/lib/wasi-types/src/net.rs b/lib/wasi-types/src/net.rs new file mode 100644 index 00000000000..7f0cbd79f64 --- /dev/null +++ b/lib/wasi-types/src/net.rs @@ -0,0 +1,494 @@ +use super::*; +use wasmer_derive::ValueType; + +use crate::__wasi_option_timestamp_t; + +pub type __wasi_socktype_t = u16; +pub const __WASI_SOCK_TYPE_DGRAM: __wasi_socktype_t = 0; +pub const __WASI_SOCK_TYPE_STREAM: __wasi_socktype_t = 1; +pub const __WASI_SOCK_TYPE_RAW: __wasi_socktype_t = 2; +pub const __WASI_SOCK_TYPE_SEQPACKET: __wasi_socktype_t = 3; + +pub type __wasi_sockstatus_t = u8; +pub const __WASI_SOCK_STATUS_OPENING: __wasi_sockstatus_t = 0; +pub const __WASI_SOCK_STATUS_OPENED: __wasi_sockstatus_t = 1; +pub const __WASI_SOCK_STATUS_CLOSED: __wasi_sockstatus_t = 2; +pub const __WASI_SOCK_STATUS_FAILED: __wasi_sockstatus_t = 3; + +pub type __wasi_sockoption_t = u8; +pub const __WASI_SOCK_OPTION_NOOP: __wasi_sockoption_t = 0; +pub const __WASI_SOCK_OPTION_REUSE_PORT: __wasi_sockoption_t = 1; +pub const __WASI_SOCK_OPTION_REUSE_ADDR: __wasi_sockoption_t = 2; +pub const __WASI_SOCK_OPTION_NO_DELAY: __wasi_sockoption_t = 3; +pub const __WASI_SOCK_OPTION_DONT_ROUTE: __wasi_sockoption_t = 4; +pub const __WASI_SOCK_OPTION_ONLY_V6: __wasi_sockoption_t = 5; +pub const __WASI_SOCK_OPTION_BROADCAST: __wasi_sockoption_t = 6; +pub const __WASI_SOCK_OPTION_MULTICAST_LOOP_V4: __wasi_sockoption_t = 7; +pub const __WASI_SOCK_OPTION_MULTICAST_LOOP_V6: __wasi_sockoption_t = 8; +pub const __WASI_SOCK_OPTION_PROMISCUOUS: __wasi_sockoption_t = 9; +pub const __WASI_SOCK_OPTION_LISTENING: __wasi_sockoption_t = 10; +pub const __WASI_SOCK_OPTION_LAST_ERROR: __wasi_sockoption_t = 11; +pub const __WASI_SOCK_OPTION_KEEP_ALIVE: __wasi_sockoption_t = 12; +pub const __WASI_SOCK_OPTION_LINGER: __wasi_sockoption_t = 13; +pub const __WASI_SOCK_OPTION_OOB_INLINE: __wasi_sockoption_t = 14; +pub const __WASI_SOCK_OPTION_RECV_BUF_SIZE: __wasi_sockoption_t = 15; +pub const __WASI_SOCK_OPTION_SEND_BUF_SIZE: __wasi_sockoption_t = 16; +pub const __WASI_SOCK_OPTION_RECV_LOWAT: __wasi_sockoption_t = 17; +pub const __WASI_SOCK_OPTION_SEND_LOWAT: __wasi_sockoption_t = 18; +pub const __WASI_SOCK_OPTION_RECV_TIMEOUT: __wasi_sockoption_t = 19; +pub const __WASI_SOCK_OPTION_SEND_TIMEOUT: __wasi_sockoption_t = 20; +pub const __WASI_SOCK_OPTION_CONNECT_TIMEOUT: __wasi_sockoption_t = 21; +pub const __WASI_SOCK_OPTION_ACCEPT_TIMEOUT: __wasi_sockoption_t = 22; +pub const __WASI_SOCK_OPTION_TTL: __wasi_sockoption_t = 23; +pub const __WASI_SOCK_OPTION_MULTICAST_TTL_V4: __wasi_sockoption_t = 24; +pub const __WASI_SOCK_OPTION_TYPE: __wasi_sockoption_t = 25; +pub const __WASI_SOCK_OPTION_PROTO: __wasi_sockoption_t = 26; + +pub type __wasi_streamsecurity_t = u8; +pub const __WASI_STREAM_SECURITY_UNENCRYPTED: __wasi_streamsecurity_t = 0; +pub const __WASI_STREAM_SECURITY_ANY_ENCRYPTION: __wasi_streamsecurity_t = 1; +pub const __WASI_STREAM_SECURITY_CLASSIC_ENCRYPTION: __wasi_streamsecurity_t = 2; +pub const __WASI_STREAM_SECURITY_DOUBLE_ENCRYPTION: __wasi_streamsecurity_t = 3; + +pub type __wasi_sockproto_t = u16; +pub const __WASI_SOCK_PROTO_IP: __wasi_sockproto_t = 0; +pub const __WASI_SOCK_PROTO_ICMP: __wasi_sockproto_t = 1; +pub const __WASI_SOCK_PROTO_IGMP: __wasi_sockproto_t = 2; +pub const __WASI_SOCK_PROTO_PROTO_3: __wasi_sockproto_t = 3; +pub const __WASI_SOCK_PROTO_IPIP: __wasi_sockproto_t = 4; +pub const __WASI_SOCK_PROTO_PROTO_5: __wasi_sockproto_t = 5; +pub const __WASI_SOCK_PROTO_TCP: __wasi_sockproto_t = 6; +pub const __WASI_SOCK_PROTO_PROTO_7: __wasi_sockproto_t = 7; +pub const __WASI_SOCK_PROTO_EGP: __wasi_sockproto_t = 8; +pub const __WASI_SOCK_PROTO_PROTO_9: __wasi_sockproto_t = 9; +pub const __WASI_SOCK_PROTO_PROTO_10: __wasi_sockproto_t = 10; +pub const __WASI_SOCK_PROTO_PROTO_11: __wasi_sockproto_t = 11; +pub const __WASI_SOCK_PROTO_PUP: __wasi_sockproto_t = 12; +pub const __WASI_SOCK_PROTO_PROTO_13: __wasi_sockproto_t = 13; +pub const __WASI_SOCK_PROTO_PROTO_14: __wasi_sockproto_t = 14; +pub const __WASI_SOCK_PROTO_PROTO_15: __wasi_sockproto_t = 15; +pub const __WASI_SOCK_PROTO_PROTO_16: __wasi_sockproto_t = 16; +pub const __WASI_SOCK_PROTO_UDP: __wasi_sockproto_t = 17; +pub const __WASI_SOCK_PROTO_PROTO_18: __wasi_sockproto_t = 18; +pub const __WASI_SOCK_PROTO_PROTO_19: __wasi_sockproto_t = 19; +pub const __WASI_SOCK_PROTO_PROTO_20: __wasi_sockproto_t = 20; +pub const __WASI_SOCK_PROTO_PROTO_21: __wasi_sockproto_t = 21; +pub const __WASI_SOCK_PROTO_IDP: __wasi_sockproto_t = 22; +pub const __WASI_SOCK_PROTO_PROTO_23: __wasi_sockproto_t = 23; +pub const __WASI_SOCK_PROTO_PROTO_24: __wasi_sockproto_t = 24; +pub const __WASI_SOCK_PROTO_PROTO_25: __wasi_sockproto_t = 25; +pub const __WASI_SOCK_PROTO_PROTO_26: __wasi_sockproto_t = 26; +pub const __WASI_SOCK_PROTO_PROTO_27: __wasi_sockproto_t = 27; +pub const __WASI_SOCK_PROTO_PROTO_28: __wasi_sockproto_t = 28; +pub const __WASI_SOCK_PROTO_PROTO_TP: __wasi_sockproto_t = 29; +pub const __WASI_SOCK_PROTO_PROTO_30: __wasi_sockproto_t = 30; +pub const __WASI_SOCK_PROTO_PROTO_31: __wasi_sockproto_t = 31; +pub const __WASI_SOCK_PROTO_PROTO_32: __wasi_sockproto_t = 32; +pub const __WASI_SOCK_PROTO_DCCP: __wasi_sockproto_t = 33; +pub const __WASI_SOCK_PROTO_PROTO_34: __wasi_sockproto_t = 34; +pub const __WASI_SOCK_PROTO_PROTO_35: __wasi_sockproto_t = 35; +pub const __WASI_SOCK_PROTO_PROTO_36: __wasi_sockproto_t = 36; +pub const __WASI_SOCK_PROTO_PROTO_37: __wasi_sockproto_t = 37; +pub const __WASI_SOCK_PROTO_PROTO_38: __wasi_sockproto_t = 38; +pub const __WASI_SOCK_PROTO_PROTO_39: __wasi_sockproto_t = 39; +pub const __WASI_SOCK_PROTO_PROTO_40: __wasi_sockproto_t = 40; +pub const __WASI_SOCK_PROTO_IPV6: __wasi_sockproto_t = 41; +pub const __WASI_SOCK_PROTO_PROTO_42: __wasi_sockproto_t = 42; +pub const __WASI_SOCK_PROTO_ROUTING: __wasi_sockproto_t = 43; +pub const __WASI_SOCK_PROTO_FRAGMENT: __wasi_sockproto_t = 44; +pub const __WASI_SOCK_PROTO_PROTO_45: __wasi_sockproto_t = 45; +pub const __WASI_SOCK_PROTO_RSVP: __wasi_sockproto_t = 46; +pub const __WASI_SOCK_PROTO_GRE: __wasi_sockproto_t = 47; +pub const __WASI_SOCK_PROTO_PROTO_48: __wasi_sockproto_t = 48; +pub const __WASI_SOCK_PROTO_PROTO_49: __wasi_sockproto_t = 49; +pub const __WASI_SOCK_PROTO_ESP: __wasi_sockproto_t = 50; +pub const __WASI_SOCK_PROTO_AH: __wasi_sockproto_t = 51; +pub const __WASI_SOCK_PROTO_PROTO_52: __wasi_sockproto_t = 52; +pub const __WASI_SOCK_PROTO_PROTO_53: __wasi_sockproto_t = 53; +pub const __WASI_SOCK_PROTO_PROTO_54: __wasi_sockproto_t = 54; +pub const __WASI_SOCK_PROTO_PROTO_55: __wasi_sockproto_t = 55; +pub const __WASI_SOCK_PROTO_PROTO_56: __wasi_sockproto_t = 56; +pub const __WASI_SOCK_PROTO_PROTO_57: __wasi_sockproto_t = 57; +pub const __WASI_SOCK_PROTO_ICMPV6: __wasi_sockproto_t = 58; +pub const __WASI_SOCK_PROTO_NONE: __wasi_sockproto_t = 59; +pub const __WASI_SOCK_PROTO_DSTOPTS: __wasi_sockproto_t = 60; +pub const __WASI_SOCK_PROTO_PROTO_61: __wasi_sockproto_t = 61; +pub const __WASI_SOCK_PROTO_PROTO_62: __wasi_sockproto_t = 62; +pub const __WASI_SOCK_PROTO_PROTO_63: __wasi_sockproto_t = 63; +pub const __WASI_SOCK_PROTO_PROTO_64: __wasi_sockproto_t = 64; +pub const __WASI_SOCK_PROTO_PROTO_65: __wasi_sockproto_t = 65; +pub const __WASI_SOCK_PROTO_PROTO_66: __wasi_sockproto_t = 66; +pub const __WASI_SOCK_PROTO_PROTO_67: __wasi_sockproto_t = 67; +pub const __WASI_SOCK_PROTO_PROTO_68: __wasi_sockproto_t = 68; +pub const __WASI_SOCK_PROTO_PROTO_69: __wasi_sockproto_t = 69; +pub const __WASI_SOCK_PROTO_PROTO_70: __wasi_sockproto_t = 70; +pub const __WASI_SOCK_PROTO_PROTO_71: __wasi_sockproto_t = 71; +pub const __WASI_SOCK_PROTO_PROTO_72: __wasi_sockproto_t = 72; +pub const __WASI_SOCK_PROTO_PROTO_73: __wasi_sockproto_t = 73; +pub const __WASI_SOCK_PROTO_PROTO_74: __wasi_sockproto_t = 74; +pub const __WASI_SOCK_PROTO_PROTO_75: __wasi_sockproto_t = 75; +pub const __WASI_SOCK_PROTO_PROTO_76: __wasi_sockproto_t = 76; +pub const __WASI_SOCK_PROTO_PROTO_77: __wasi_sockproto_t = 77; +pub const __WASI_SOCK_PROTO_PROTO_78: __wasi_sockproto_t = 78; +pub const __WASI_SOCK_PROTO_PROTO_79: __wasi_sockproto_t = 79; +pub const __WASI_SOCK_PROTO_PROTO_80: __wasi_sockproto_t = 80; +pub const __WASI_SOCK_PROTO_PROTO_81: __wasi_sockproto_t = 81; +pub const __WASI_SOCK_PROTO_PROTO_82: __wasi_sockproto_t = 82; +pub const __WASI_SOCK_PROTO_PROTO_83: __wasi_sockproto_t = 83; +pub const __WASI_SOCK_PROTO_PROTO_84: __wasi_sockproto_t = 84; +pub const __WASI_SOCK_PROTO_PROTO_85: __wasi_sockproto_t = 85; +pub const __WASI_SOCK_PROTO_PROTO_86: __wasi_sockproto_t = 86; +pub const __WASI_SOCK_PROTO_PROTO_87: __wasi_sockproto_t = 87; +pub const __WASI_SOCK_PROTO_PROTO_88: __wasi_sockproto_t = 88; +pub const __WASI_SOCK_PROTO_PROTO_89: __wasi_sockproto_t = 89; +pub const __WASI_SOCK_PROTO_PROTO_90: __wasi_sockproto_t = 90; +pub const __WASI_SOCK_PROTO_PROTO_91: __wasi_sockproto_t = 91; +pub const __WASI_SOCK_PROTO_MTP: __wasi_sockproto_t = 92; +pub const __WASI_SOCK_PROTO_PROTO_93: __wasi_sockproto_t = 93; +pub const __WASI_SOCK_PROTO_BEETPH: __wasi_sockproto_t = 94; +pub const __WASI_SOCK_PROTO_PROTO_95: __wasi_sockproto_t = 95; +pub const __WASI_SOCK_PROTO_PROTO_96: __wasi_sockproto_t = 96; +pub const __WASI_SOCK_PROTO_PROTO_97: __wasi_sockproto_t = 97; +pub const __WASI_SOCK_PROTO_ENCAP: __wasi_sockproto_t = 98; +pub const __WASI_SOCK_PROTO_PROTO_99: __wasi_sockproto_t = 99; +pub const __WASI_SOCK_PROTO_PROTO_100: __wasi_sockproto_t = 100; +pub const __WASI_SOCK_PROTO_PROTO_101: __wasi_sockproto_t = 101; +pub const __WASI_SOCK_PROTO_PROTO_102: __wasi_sockproto_t = 102; +pub const __WASI_SOCK_PROTO_PIM: __wasi_sockproto_t = 103; +pub const __WASI_SOCK_PROTO_PROTO_104: __wasi_sockproto_t = 104; +pub const __WASI_SOCK_PROTO_PROTO_105: __wasi_sockproto_t = 105; +pub const __WASI_SOCK_PROTO_PROTO_106: __wasi_sockproto_t = 106; +pub const __WASI_SOCK_PROTO_PROTO_107: __wasi_sockproto_t = 107; +pub const __WASI_SOCK_PROTO_COMP: __wasi_sockproto_t = 108; +pub const __WASI_SOCK_PROTO_PROTO_109: __wasi_sockproto_t = 109; +pub const __WASI_SOCK_PROTO_PROTO_110: __wasi_sockproto_t = 110; +pub const __WASI_SOCK_PROTO_PROTO_111: __wasi_sockproto_t = 111; +pub const __WASI_SOCK_PROTO_PROTO_112: __wasi_sockproto_t = 112; +pub const __WASI_SOCK_PROTO_PROTO_113: __wasi_sockproto_t = 113; +pub const __WASI_SOCK_PROTO_PROTO_114: __wasi_sockproto_t = 114; +pub const __WASI_SOCK_PROTO_PROTO_115: __wasi_sockproto_t = 115; +pub const __WASI_SOCK_PROTO_PROTO_116: __wasi_sockproto_t = 116; +pub const __WASI_SOCK_PROTO_PROTO_117: __wasi_sockproto_t = 117; +pub const __WASI_SOCK_PROTO_PROTO_118: __wasi_sockproto_t = 118; +pub const __WASI_SOCK_PROTO_PROTO_119: __wasi_sockproto_t = 119; +pub const __WASI_SOCK_PROTO_PROTO_120: __wasi_sockproto_t = 120; +pub const __WASI_SOCK_PROTO_PROTO_121: __wasi_sockproto_t = 121; +pub const __WASI_SOCK_PROTO_PROTO_122: __wasi_sockproto_t = 122; +pub const __WASI_SOCK_PROTO_PROTO_123: __wasi_sockproto_t = 123; +pub const __WASI_SOCK_PROTO_PROTO_124: __wasi_sockproto_t = 124; +pub const __WASI_SOCK_PROTO_PROTO_125: __wasi_sockproto_t = 125; +pub const __WASI_SOCK_PROTO_PROTO_126: __wasi_sockproto_t = 126; +pub const __WASI_SOCK_PROTO_PROTO_127: __wasi_sockproto_t = 127; +pub const __WASI_SOCK_PROTO_PROTO_128: __wasi_sockproto_t = 128; +pub const __WASI_SOCK_PROTO_PROTO_129: __wasi_sockproto_t = 129; +pub const __WASI_SOCK_PROTO_PROTO_130: __wasi_sockproto_t = 130; +pub const __WASI_SOCK_PROTO_PROTO_131: __wasi_sockproto_t = 131; +pub const __WASI_SOCK_PROTO_SCTP: __wasi_sockproto_t = 132; +pub const __WASI_SOCK_PROTO_PROTO_133: __wasi_sockproto_t = 133; +pub const __WASI_SOCK_PROTO_PROTO_134: __wasi_sockproto_t = 134; +pub const __WASI_SOCK_PROTO_MH: __wasi_sockproto_t = 135; +pub const __WASI_SOCK_PROTO_UDPLITE: __wasi_sockproto_t = 136; +pub const __WASI_SOCK_PROTO_MPLS: __wasi_sockproto_t = 137; +pub const __WASI_SOCK_PROTO_PROTO_138: __wasi_sockproto_t = 138; +pub const __WASI_SOCK_PROTO_PROTO_139: __wasi_sockproto_t = 139; +pub const __WASI_SOCK_PROTO_PROTO_140: __wasi_sockproto_t = 140; +pub const __WASI_SOCK_PROTO_PROTO_141: __wasi_sockproto_t = 141; +pub const __WASI_SOCK_PROTO_PROTO_142: __wasi_sockproto_t = 142; +pub const __WASI_SOCK_PROTO_ETHERNET: __wasi_sockproto_t = 143; +pub const __WASI_SOCK_PROTO_PROTO_144: __wasi_sockproto_t = 144; +pub const __WASI_SOCK_PROTO_PROTO_145: __wasi_sockproto_t = 145; +pub const __WASI_SOCK_PROTO_PROTO_146: __wasi_sockproto_t = 146; +pub const __WASI_SOCK_PROTO_PROTO_147: __wasi_sockproto_t = 147; +pub const __WASI_SOCK_PROTO_PROTO_148: __wasi_sockproto_t = 148; +pub const __WASI_SOCK_PROTO_PROTO_149: __wasi_sockproto_t = 149; +pub const __WASI_SOCK_PROTO_PROTO_150: __wasi_sockproto_t = 150; +pub const __WASI_SOCK_PROTO_PROTO_151: __wasi_sockproto_t = 151; +pub const __WASI_SOCK_PROTO_PROTO_152: __wasi_sockproto_t = 152; +pub const __WASI_SOCK_PROTO_PROTO_153: __wasi_sockproto_t = 153; +pub const __WASI_SOCK_PROTO_PROTO_154: __wasi_sockproto_t = 154; +pub const __WASI_SOCK_PROTO_PROTO_155: __wasi_sockproto_t = 155; +pub const __WASI_SOCK_PROTO_PROTO_156: __wasi_sockproto_t = 156; +pub const __WASI_SOCK_PROTO_PROTO_157: __wasi_sockproto_t = 157; +pub const __WASI_SOCK_PROTO_PROTO_158: __wasi_sockproto_t = 158; +pub const __WASI_SOCK_PROTO_PROTO_159: __wasi_sockproto_t = 159; +pub const __WASI_SOCK_PROTO_PROTO_160: __wasi_sockproto_t = 160; +pub const __WASI_SOCK_PROTO_PROTO_161: __wasi_sockproto_t = 161; +pub const __WASI_SOCK_PROTO_PROTO_162: __wasi_sockproto_t = 162; +pub const __WASI_SOCK_PROTO_PROTO_163: __wasi_sockproto_t = 163; +pub const __WASI_SOCK_PROTO_PROTO_164: __wasi_sockproto_t = 164; +pub const __WASI_SOCK_PROTO_PROTO_165: __wasi_sockproto_t = 165; +pub const __WASI_SOCK_PROTO_PROTO_166: __wasi_sockproto_t = 166; +pub const __WASI_SOCK_PROTO_PROTO_167: __wasi_sockproto_t = 167; +pub const __WASI_SOCK_PROTO_PROTO_168: __wasi_sockproto_t = 168; +pub const __WASI_SOCK_PROTO_PROTO_169: __wasi_sockproto_t = 169; +pub const __WASI_SOCK_PROTO_PROTO_170: __wasi_sockproto_t = 170; +pub const __WASI_SOCK_PROTO_PROTO_171: __wasi_sockproto_t = 171; +pub const __WASI_SOCK_PROTO_PROTO_172: __wasi_sockproto_t = 172; +pub const __WASI_SOCK_PROTO_PROTO_173: __wasi_sockproto_t = 173; +pub const __WASI_SOCK_PROTO_PROTO_174: __wasi_sockproto_t = 174; +pub const __WASI_SOCK_PROTO_PROTO_175: __wasi_sockproto_t = 175; +pub const __WASI_SOCK_PROTO_PROTO_176: __wasi_sockproto_t = 176; +pub const __WASI_SOCK_PROTO_PROTO_177: __wasi_sockproto_t = 177; +pub const __WASI_SOCK_PROTO_PROTO_178: __wasi_sockproto_t = 178; +pub const __WASI_SOCK_PROTO_PROTO_179: __wasi_sockproto_t = 179; +pub const __WASI_SOCK_PROTO_PROTO_180: __wasi_sockproto_t = 180; +pub const __WASI_SOCK_PROTO_PROTO_181: __wasi_sockproto_t = 181; +pub const __WASI_SOCK_PROTO_PROTO_182: __wasi_sockproto_t = 182; +pub const __WASI_SOCK_PROTO_PROTO_183: __wasi_sockproto_t = 183; +pub const __WASI_SOCK_PROTO_PROTO_184: __wasi_sockproto_t = 184; +pub const __WASI_SOCK_PROTO_PROTO_185: __wasi_sockproto_t = 185; +pub const __WASI_SOCK_PROTO_PROTO_186: __wasi_sockproto_t = 186; +pub const __WASI_SOCK_PROTO_PROTO_187: __wasi_sockproto_t = 187; +pub const __WASI_SOCK_PROTO_PROTO_188: __wasi_sockproto_t = 188; +pub const __WASI_SOCK_PROTO_PROTO_189: __wasi_sockproto_t = 189; +pub const __WASI_SOCK_PROTO_PROTO_190: __wasi_sockproto_t = 190; +pub const __WASI_SOCK_PROTO_PROTO_191: __wasi_sockproto_t = 191; +pub const __WASI_SOCK_PROTO_PROTO_192: __wasi_sockproto_t = 192; +pub const __WASI_SOCK_PROTO_PROTO_193: __wasi_sockproto_t = 193; +pub const __WASI_SOCK_PROTO_PROTO_194: __wasi_sockproto_t = 194; +pub const __WASI_SOCK_PROTO_PROTO_195: __wasi_sockproto_t = 195; +pub const __WASI_SOCK_PROTO_PROTO_196: __wasi_sockproto_t = 196; +pub const __WASI_SOCK_PROTO_PROTO_197: __wasi_sockproto_t = 197; +pub const __WASI_SOCK_PROTO_PROTO_198: __wasi_sockproto_t = 198; +pub const __WASI_SOCK_PROTO_PROTO_199: __wasi_sockproto_t = 199; +pub const __WASI_SOCK_PROTO_PROTO_200: __wasi_sockproto_t = 200; +pub const __WASI_SOCK_PROTO_PROTO_201: __wasi_sockproto_t = 201; +pub const __WASI_SOCK_PROTO_PROTO_202: __wasi_sockproto_t = 202; +pub const __WASI_SOCK_PROTO_PROTO_203: __wasi_sockproto_t = 203; +pub const __WASI_SOCK_PROTO_PROTO_204: __wasi_sockproto_t = 204; +pub const __WASI_SOCK_PROTO_PROTO_205: __wasi_sockproto_t = 205; +pub const __WASI_SOCK_PROTO_PROTO_206: __wasi_sockproto_t = 206; +pub const __WASI_SOCK_PROTO_PROTO_207: __wasi_sockproto_t = 207; +pub const __WASI_SOCK_PROTO_PROTO_208: __wasi_sockproto_t = 208; +pub const __WASI_SOCK_PROTO_PROTO_209: __wasi_sockproto_t = 209; +pub const __WASI_SOCK_PROTO_PROTO_210: __wasi_sockproto_t = 210; +pub const __WASI_SOCK_PROTO_PROTO_211: __wasi_sockproto_t = 211; +pub const __WASI_SOCK_PROTO_PROTO_212: __wasi_sockproto_t = 212; +pub const __WASI_SOCK_PROTO_PROTO_213: __wasi_sockproto_t = 213; +pub const __WASI_SOCK_PROTO_PROTO_214: __wasi_sockproto_t = 214; +pub const __WASI_SOCK_PROTO_PROTO_215: __wasi_sockproto_t = 215; +pub const __WASI_SOCK_PROTO_PROTO_216: __wasi_sockproto_t = 216; +pub const __WASI_SOCK_PROTO_PROTO_217: __wasi_sockproto_t = 217; +pub const __WASI_SOCK_PROTO_PROTO_218: __wasi_sockproto_t = 218; +pub const __WASI_SOCK_PROTO_PROTO_219: __wasi_sockproto_t = 219; +pub const __WASI_SOCK_PROTO_PROTO_220: __wasi_sockproto_t = 220; +pub const __WASI_SOCK_PROTO_PROTO_221: __wasi_sockproto_t = 221; +pub const __WASI_SOCK_PROTO_PROTO_222: __wasi_sockproto_t = 222; +pub const __WASI_SOCK_PROTO_PROTO_223: __wasi_sockproto_t = 223; +pub const __WASI_SOCK_PROTO_PROTO_224: __wasi_sockproto_t = 224; +pub const __WASI_SOCK_PROTO_PROTO_225: __wasi_sockproto_t = 225; +pub const __WASI_SOCK_PROTO_PROTO_226: __wasi_sockproto_t = 226; +pub const __WASI_SOCK_PROTO_PROTO_227: __wasi_sockproto_t = 227; +pub const __WASI_SOCK_PROTO_PROTO_228: __wasi_sockproto_t = 228; +pub const __WASI_SOCK_PROTO_PROTO_229: __wasi_sockproto_t = 229; +pub const __WASI_SOCK_PROTO_PROTO_230: __wasi_sockproto_t = 230; +pub const __WASI_SOCK_PROTO_PROTO_231: __wasi_sockproto_t = 231; +pub const __WASI_SOCK_PROTO_PROTO_232: __wasi_sockproto_t = 232; +pub const __WASI_SOCK_PROTO_PROTO_233: __wasi_sockproto_t = 233; +pub const __WASI_SOCK_PROTO_PROTO_234: __wasi_sockproto_t = 234; +pub const __WASI_SOCK_PROTO_PROTO_235: __wasi_sockproto_t = 235; +pub const __WASI_SOCK_PROTO_PROTO_236: __wasi_sockproto_t = 236; +pub const __WASI_SOCK_PROTO_PROTO_237: __wasi_sockproto_t = 237; +pub const __WASI_SOCK_PROTO_PROTO_238: __wasi_sockproto_t = 238; +pub const __WASI_SOCK_PROTO_PROTO_239: __wasi_sockproto_t = 239; +pub const __WASI_SOCK_PROTO_PROTO_240: __wasi_sockproto_t = 240; +pub const __WASI_SOCK_PROTO_PROTO_241: __wasi_sockproto_t = 241; +pub const __WASI_SOCK_PROTO_PROTO_242: __wasi_sockproto_t = 242; +pub const __WASI_SOCK_PROTO_PROTO_243: __wasi_sockproto_t = 243; +pub const __WASI_SOCK_PROTO_PROTO_244: __wasi_sockproto_t = 244; +pub const __WASI_SOCK_PROTO_PROTO_245: __wasi_sockproto_t = 245; +pub const __WASI_SOCK_PROTO_PROTO_246: __wasi_sockproto_t = 246; +pub const __WASI_SOCK_PROTO_PROTO_247: __wasi_sockproto_t = 247; +pub const __WASI_SOCK_PROTO_PROTO_248: __wasi_sockproto_t = 248; +pub const __WASI_SOCK_PROTO_PROTO_249: __wasi_sockproto_t = 249; +pub const __WASI_SOCK_PROTO_PROTO_250: __wasi_sockproto_t = 250; +pub const __WASI_SOCK_PROTO_PROTO_251: __wasi_sockproto_t = 251; +pub const __WASI_SOCK_PROTO_PROTO_252: __wasi_sockproto_t = 252; +pub const __WASI_SOCK_PROTO_PROTO_253: __wasi_sockproto_t = 253; +pub const __WASI_SOCK_PROTO_PROTO_254: __wasi_sockproto_t = 254; +pub const __WASI_SOCK_PROTO_PROTO_RAW: __wasi_sockproto_t = 255; +pub const __WASI_SOCK_PROTO_PROTO_256: __wasi_sockproto_t = 256; +pub const __WASI_SOCK_PROTO_PROTO_257: __wasi_sockproto_t = 257; +pub const __WASI_SOCK_PROTO_PROTO_258: __wasi_sockproto_t = 258; +pub const __WASI_SOCK_PROTO_PROTO_259: __wasi_sockproto_t = 259; +pub const __WASI_SOCK_PROTO_PROTO_260: __wasi_sockproto_t = 260; +pub const __WASI_SOCK_PROTO_PROTO_261: __wasi_sockproto_t = 261; +pub const __WASI_SOCK_PROTO_MPTCP: __wasi_sockproto_t = 262; +pub const __WASI_SOCK_PROTO_MAX: __wasi_sockproto_t = 263; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueType)] +#[repr(C)] +pub struct __wasi_hardwareaddress_t { + pub octs: [u8; 6], +} + +pub type __wasi_addressfamily_t = u16; +pub const __WASI_ADDRESS_FAMILY_UNSPEC: __wasi_addressfamily_t = 0; +pub const __WASI_ADDRESS_FAMILY_INET4: __wasi_addressfamily_t = 1; +pub const __WASI_ADDRESS_FAMILY_INET6: __wasi_addressfamily_t = 2; +pub const __WASI_ADDRESS_FAMILY_UNIX: __wasi_addressfamily_t = 3; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueType)] +#[repr(C)] +pub struct __wasi_addr_unspec_t { + pub n0: u8, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueType)] +#[repr(C)] +pub struct __wasi_addr_unspec_port_t { + pub port: u16, + pub addr: __wasi_addr_unspec_t, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueType)] +#[repr(C)] +pub struct __wasi_cidr_unspec_t { + pub addr: __wasi_addr_unspec_t, + pub prefix: u8, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueType)] +#[repr(C)] +pub struct __wasi_addr_ip4_t { + pub octs: [u8; 4], +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueType)] +#[repr(C)] +pub struct __wasi_addr_ip4_port_t { + pub port: u16, + pub ip: __wasi_addr_ip4_t, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueType)] +#[repr(C)] +pub struct __wasi_cidr_ip4_t { + pub ip: __wasi_addr_ip4_t, + pub prefix: u8, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueType)] +#[repr(C)] +pub struct __wasi_addr_unix_t { + pub octs: [u8; 16], +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueType)] +#[repr(C)] +pub struct __wasi_addr_unix_port_t { + pub port: u16, + pub unix: __wasi_addr_unix_t, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueType)] +#[repr(C)] +pub struct __wasi_cidr_unix_t { + pub unix: __wasi_addr_unix_t, + pub prefix: u8, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueType)] +#[repr(C)] +pub struct __wasi_addr_ip6_t { + pub segs: [u8; 16], +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueType)] +#[repr(C)] +pub struct __wasi_addr_ip6_port_t { + pub port: u16, + pub ip: __wasi_addr_ip6_t, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueType)] +#[repr(C)] +pub struct __wasi_cidr_ip6_t { + pub ip: __wasi_addr_ip6_t, + pub prefix: u8, +} + +#[derive(Debug, Copy, Clone, ValueType)] +#[repr(C)] +pub struct __wasi_addr_u { + pub octs: [u8; 16], +} + +#[derive(Debug, Copy, Clone, ValueType)] +#[repr(C)] +pub struct __wasi_addr_t { + pub tag: __wasi_addressfamily_t, + pub u: __wasi_addr_u, +} + +#[derive(Debug, Copy, Clone, ValueType)] +#[repr(C)] +pub struct __wasi_addr_port_u { + pub octs: [u8; 18], +} + +#[derive(Debug, Copy, Clone, ValueType)] +#[repr(C)] +pub struct __wasi_addr_port_t { + pub tag: __wasi_addressfamily_t, + pub u: __wasi_addr_port_u, +} + +#[derive(Debug, Copy, Clone, ValueType)] +#[repr(C)] +pub struct __wasi_cidr_u { + pub octs: [u8; 17], +} + +#[derive(Debug, Copy, Clone, ValueType)] +#[repr(C)] +pub struct __wasi_cidr_t { + pub tag: __wasi_addressfamily_t, + pub u: __wasi_cidr_u, +} + +#[derive(Debug, Copy, Clone, ValueType)] +#[repr(C)] +pub struct __wasi_route_t { + pub cidr: __wasi_cidr_t, + pub via_router: __wasi_addr_t, + pub preferred_until: __wasi_option_timestamp_t, + pub expires_at: __wasi_option_timestamp_t, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueType)] +#[repr(C)] +pub struct __wasi_http_handles_t { + pub req: __wasi_fd_t, + pub res: __wasi_fd_t, + pub hdr: __wasi_fd_t, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueType)] +#[repr(C)] +pub struct __wasi_http_status_t { + pub ok: __wasi_bool_t, + pub redirect: __wasi_bool_t, + pub size: __wasi_filesize_t, + pub status: u16, +} + +pub type __wasi_riflags_t = u16; +pub const __WASI_SOCK_RECV_INPUT_PEEK: __wasi_riflags_t = 1 << 0; +pub const __WASI_SOCK_RECV_INPUT_WAITALL: __wasi_riflags_t = 1 << 1; +pub const __WASI_SOCK_RECV_INPUT_DATA_TRUNCATED: __wasi_riflags_t = 1 << 2; + +pub type __wasi_roflags_t = u16; +pub const __WASI_SOCK_RECV_OUTPUT_DATA_TRUNCATED: __wasi_roflags_t = 1 << 0; + +pub type __wasi_sdflags_t = u8; +pub const __WASI_SHUT_RD: __wasi_sdflags_t = 1 << 0; +pub const __WASI_SHUT_WR: __wasi_sdflags_t = 1 << 1; + +pub type __wasi_siflags_t = u16; + +pub type __wasi_timeout_t = u8; +pub const __WASI_TIMEOUT_READ: __wasi_timeout_t = 0; +pub const __WASI_TIMEOUT_WRITE: __wasi_timeout_t = 1; +pub const __WASI_TIMEOUT_CONNECT: __wasi_timeout_t = 2; +pub const __WASI_TIMEOUT_ACCEPT: __wasi_timeout_t = 3; diff --git a/lib/wasi-types/src/time.rs b/lib/wasi-types/src/time.rs index 85b3ef05f12..de9a2b26b92 100644 --- a/lib/wasi-types/src/time.rs +++ b/lib/wasi-types/src/time.rs @@ -1,7 +1,17 @@ +use super::io::__wasi_option_t; +use wasmer_derive::ValueType; + pub type __wasi_clockid_t = u32; -pub const __WASI_CLOCK_REALTIME: u32 = 0; -pub const __WASI_CLOCK_MONOTONIC: u32 = 1; -pub const __WASI_CLOCK_PROCESS_CPUTIME_ID: u32 = 2; -pub const __WASI_CLOCK_THREAD_CPUTIME_ID: u32 = 3; +pub const __WASI_CLOCK_REALTIME: __wasi_clockid_t = 0; +pub const __WASI_CLOCK_MONOTONIC: __wasi_clockid_t = 1; +pub const __WASI_CLOCK_PROCESS_CPUTIME_ID: __wasi_clockid_t = 2; +pub const __WASI_CLOCK_THREAD_CPUTIME_ID: __wasi_clockid_t = 3; pub type __wasi_timestamp_t = u64; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueType)] +#[repr(C)] +pub struct __wasi_option_timestamp_t { + pub tag: __wasi_option_t, + pub u: __wasi_timestamp_t, +} diff --git a/lib/wasi/Cargo.toml b/lib/wasi/Cargo.toml index d6806c48eb2..2f0d97193fa 100644 --- a/lib/wasi/Cargo.toml +++ b/lib/wasi/Cargo.toml @@ -22,9 +22,15 @@ getrandom = "0.2" wasmer-wasi-types = { path = "../wasi-types", version = "=2.3.0" } wasmer = { path = "../api", version = "=2.3.0", default-features = false } wasmer-vfs = { path = "../vfs", version = "=2.3.0", default-features = false } +wasmer-vbus = { path = "../vbus", version = "=2.3.0", default-features = false } +wasmer-vnet = { path = "../vnet", version = "=2.3.0", default-features = false } +wasmer-wasi-local-networking = { path = "../wasi-local-networking", version = "=2.3.0", default-features = false, optional = true } typetag = { version = "0.1", optional = true } serde = { version = "1.0", default-features = false, features = ["derive"], optional = true } bincode = { version = "1.3", optional = true } +chrono = { version = "^0.4", default-features = false, features = [ "wasmbind", "std", "clock" ], optional = true } +derivative = { version = "^2" } +bytes = "1" [target.'cfg(unix)'.dependencies] libc = { version = "^0.2", default-features = false } @@ -43,12 +49,14 @@ tracing-wasm = "0.2" default = ["sys-default"] sys = ["wasmer/sys"] -sys-default = ["wasmer/sys-default", "sys", "logging", "host-fs"] +sys-default = ["wasmer/sys-default", "sys", "logging", "host-fs", "sys-poll", "host-vnet" ] +sys-poll = [] -js = ["wasmer/js", "mem-fs", "wasmer-vfs/no-time", "getrandom/js"] +js = ["wasmer/js", "mem-fs", "wasmer-vfs/no-time", "getrandom/js", "chrono"] js-default = ["js", "wasmer/js-default"] test-js = ["js", "wasmer/js-default", "wasmer/wat"] +host-vnet = [ "wasmer-wasi-local-networking" ] host-fs = ["wasmer-vfs/host-fs"] mem-fs = ["wasmer-vfs/mem-fs"] diff --git a/lib/wasi/README.md b/lib/wasi/README.md index 4d4d13529fc..f2b97bb105b 100644 --- a/lib/wasi/README.md +++ b/lib/wasi/README.md @@ -66,7 +66,8 @@ let wasi_env = WasiState::new("command-name") .finalize()?; // Generate an `ImportObject`. -let import_object = wasi_env.import_object(&module)?; +let mut wasi_thread = wasi_env.new_thread(); +let import_object = wasi_thread.import_object(&module)?; // Let's instantiate the module with the imports. let instance = Instance::new(&module, &import_object)?; diff --git a/lib/wasi/src/lib.rs b/lib/wasi/src/lib.rs index 6ffda3c848b..8d60b8e7526 100644 --- a/lib/wasi/src/lib.rs +++ b/lib/wasi/src/lib.rs @@ -35,6 +35,7 @@ compile_error!( #[macro_use] mod macros; +mod runtime; mod state; mod syscalls; mod utils; @@ -42,23 +43,35 @@ mod utils; use crate::syscalls::*; pub use crate::state::{ - Fd, Pipe, Stderr, Stdin, Stdout, WasiFs, WasiState, WasiStateBuilder, WasiStateCreationError, - ALL_RIGHTS, VIRTUAL_ROOT_FD, + Fd, Pipe, Stderr, Stdin, Stdout, WasiFs, WasiInodes, WasiState, WasiStateBuilder, + WasiStateCreationError, ALL_RIGHTS, VIRTUAL_ROOT_FD, }; pub use crate::syscalls::types; -pub use crate::utils::{get_wasi_version, get_wasi_versions, is_wasi_module, WasiVersion}; +pub use crate::utils::{ + get_wasi_version, get_wasi_versions, is_wasi_module, is_wasix_module, WasiVersion, +}; +pub use wasmer_vbus::{UnsupportedVirtualBus, VirtualBus}; #[deprecated(since = "2.1.0", note = "Please use `wasmer_vfs::FsError`")] pub use wasmer_vfs::FsError as WasiFsError; #[deprecated(since = "2.1.0", note = "Please use `wasmer_vfs::VirtualFile`")] pub use wasmer_vfs::VirtualFile as WasiFile; pub use wasmer_vfs::{FsError, VirtualFile}; +pub use wasmer_vnet::{UnsupportedVirtualNetworking, VirtualNetworking}; +use wasmer_wasi_types::__WASI_CLOCK_MONOTONIC; +use derivative::*; +use std::ops::Deref; use thiserror::Error; use wasmer::{ - imports, Function, Imports, LazyInit, Memory, MemoryAccessError, Module, Store, WasmerEnv, + imports, Function, Imports, LazyInit, Memory, Memory32, MemoryAccessError, MemorySize, Module, + Store, TypedFunction, WasmerEnv, }; -use std::sync::{Arc, Mutex, MutexGuard}; +pub use runtime::{ + PluggableRuntimeImplementation, WasiRuntimeImplementation, WasiThreadError, WasiTtyState, +}; +use std::sync::{mpsc, Arc, Mutex, RwLockReadGuard, RwLockWriteGuard}; +use std::time::Duration; /// This is returned in `RuntimeError`. /// Use `downcast` or `downcast_ref` to retrieve the `ExitCode`. @@ -70,28 +83,171 @@ pub enum WasiError { UnknownWasiVersion, } +/// Represents the ID of a WASI thread +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct WasiThreadId(u32); + +impl From for WasiThreadId { + fn from(id: u32) -> Self { + Self(id) + } +} +impl From for u32 { + fn from(t: WasiThreadId) -> u32 { + t.0 as u32 + } +} + +/// Represents the ID of a sub-process +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct WasiBusProcessId(u32); + +impl From for WasiBusProcessId { + fn from(id: u32) -> Self { + Self(id) + } +} +impl From for u32 { + fn from(id: WasiBusProcessId) -> u32 { + id.0 as u32 + } +} + +#[cfg(target_family = "wasm")] +#[link(wasm_import_module = "__wbindgen_thread_xform__")] +extern "C" { + fn __wbindgen_thread_id() -> u32; +} + +#[derive(Debug, Clone)] +pub struct WasiThread { + /// ID of this thread + #[allow(dead_code)] + id: WasiThreadId, + /// Signalers used to tell joiners that the thread has exited + exit: Arc>>>, + /// Event to wait on for the thread to join + join: Arc>>, +} + +impl WasiThread { + /// Waits for the thread to exit (false = timeout) + pub fn join(&self, timeout: Duration) -> bool { + let guard = self.join.lock().unwrap(); + match guard.recv_timeout(timeout) { + Ok(_) => true, + Err(mpsc::RecvTimeoutError::Disconnected) => true, + Err(mpsc::RecvTimeoutError::Timeout) => false, + } + } +} + /// The environment provided to the WASI imports. -#[derive(Debug, Clone, WasmerEnv)] +#[derive(Derivative, Clone, WasmerEnv)] +#[derivative(Debug)] pub struct WasiEnv { - /// Shared state of the WASI system. Manages all the data that the - /// executing WASI program can see. - /// - /// Be careful when using this in host functions that call into Wasm: - /// if the lock is held and the Wasm calls into a host function that tries - /// to lock this mutex, the program will deadlock. - pub state: Arc>, + /// ID of this thread (zero is the main thread) + id: WasiThreadId, + /// Represents a reference to the memory #[wasmer(export)] memory: LazyInit, + /// If the module has it then map the thread start + #[derivative(Debug = "ignore")] + #[wasmer(export(optional = true, name = "_thread_start"))] + thread_start: LazyInit>, + #[derivative(Debug = "ignore")] + #[wasmer(export(optional = true, name = "_reactor_work"))] + reactor_work: LazyInit>, + #[derivative(Debug = "ignore")] + #[wasmer(export(optional = true, name = "_reactor_finish"))] + reactor_finish: LazyInit>, + #[derivative(Debug = "ignore")] + #[wasmer(export(optional = true, name = "_malloc"))] + malloc: LazyInit>, + #[derivative(Debug = "ignore")] + #[wasmer(export(optional = true, name = "_free"))] + free: LazyInit>, + /// Shared state of the WASI system. Manages all the data that the + /// executing WASI program can see. + pub state: Arc, + /// Implementation of the WASI runtime. + pub(crate) runtime: Arc, } impl WasiEnv { pub fn new(state: WasiState) -> Self { Self { - state: Arc::new(Mutex::new(state)), + id: 0u32.into(), + state: Arc::new(state), memory: LazyInit::new(), + thread_start: LazyInit::new(), + reactor_work: LazyInit::new(), + reactor_finish: LazyInit::new(), + malloc: LazyInit::new(), + free: LazyInit::new(), + runtime: Arc::new(PluggableRuntimeImplementation::default()), } } + /// Returns a copy of the current runtime implementation for this environment + pub fn runtime(&self) -> &(dyn WasiRuntimeImplementation) { + self.runtime.deref() + } + + /// Overrides the runtime implementation for this environment + pub fn set_runtime(&mut self, runtime: R) + where + R: WasiRuntimeImplementation + Send + Sync + 'static, + { + self.runtime = Arc::new(runtime); + } + + /// Returns the current thread ID + pub fn current_thread_id(&self) -> WasiThreadId { + self.id + } + + /// Creates a new thread only this wasi environment + pub fn new_thread(&self) -> WasiThread { + let (tx, rx) = mpsc::channel(); + + let mut guard = self.state.threading.lock().unwrap(); + + guard.thread_seed += 1; + let next_id: WasiThreadId = guard.thread_seed.into(); + + let thread = WasiThread { + id: next_id, + exit: Arc::new(Mutex::new(Some(tx))), + join: Arc::new(Mutex::new(rx)), + }; + + guard.threads.insert(thread.id, thread.clone()); + thread + } + + /// Get the WASI state + /// + /// Be careful when using this in host functions that call into Wasm: + /// if the lock is held and the Wasm calls into a host function that tries + /// to lock this mutex, the program will deadlock. + pub fn state(&self) -> &WasiState { + self.state.deref() + } + + /// Get a reference to the memory + pub fn memory(&self) -> &Memory { + self.memory + .get_ref() + .expect("Memory should be set on `WasiEnv` first") + } + + /// Copy the lazy reference so that when it's initialized during the + /// export phase, all the other references get a copy of it + pub fn memory_clone(&self) -> LazyInit { + self.memory.clone() + } + /// Get an `Imports` for a specific version of WASI detected in the module. pub fn import_object(&mut self, module: &Module) -> Result { let wasi_version = get_wasi_version(module, false).ok_or(WasiError::UnknownWasiVersion)?; @@ -119,31 +275,84 @@ impl WasiEnv { resolver.define(&n, &m, e); } } + + if is_wasix_module(module) { + self.state + .fs + .is_wasix + .store(true, std::sync::atomic::Ordering::Release); + } + Ok(resolver) } - /// Get the WASI state - /// - /// Be careful when using this in host functions that call into Wasm: - /// if the lock is held and the Wasm calls into a host function that tries - /// to lock this mutex, the program will deadlock. - pub fn state(&self) -> MutexGuard { - self.state.lock().unwrap() + // Yields execution + pub fn yield_now(&self) -> Result<(), WasiError> { + self.runtime.yield_now(self.id)?; + Ok(()) } - /// Get a reference to the memory - pub fn memory(&self) -> &Memory { - self.memory_ref() - .expect("Memory should be set on `WasiEnv` first") + // Sleeps for a period of time + pub fn sleep(&self, duration: Duration) -> Result<(), WasiError> { + let duration = duration.as_nanos(); + let start = platform_clock_time_get(__WASI_CLOCK_MONOTONIC, 1_000_000).unwrap() as u128; + self.yield_now()?; + loop { + let now = platform_clock_time_get(__WASI_CLOCK_MONOTONIC, 1_000_000).unwrap() as u128; + let delta = match now.checked_sub(start) { + Some(a) => a, + None => { + break; + } + }; + if delta >= duration { + break; + } + let remaining = match duration.checked_sub(delta) { + Some(a) => Duration::from_nanos(a as u64), + None => { + break; + } + }; + std::thread::sleep(remaining.min(Duration::from_millis(10))); + self.yield_now()?; + } + Ok(()) + } + + /// Accesses the virtual networking implementation + pub fn net(&self) -> &(dyn VirtualNetworking) { + self.runtime.networking() + } + + /// Accesses the virtual bus implementation + pub fn bus(&self) -> &(dyn VirtualBus) { + self.runtime.bus() + } + pub(crate) fn get_memory_and_wasi_state(&self, _mem_index: u32) -> (&Memory, &WasiState) { + let memory = self.memory(); + let state = self.state.deref(); + (memory, state) } - pub(crate) fn get_memory_and_wasi_state( + pub(crate) fn get_memory_and_wasi_state_and_inodes( &self, _mem_index: u32, - ) -> (&Memory, MutexGuard) { + ) -> (&Memory, &WasiState, RwLockReadGuard) { let memory = self.memory(); - let state = self.state.lock().unwrap(); - (memory, state) + let state = self.state.deref(); + let inodes = state.inodes.read().unwrap(); + (memory, state, inodes) + } + + pub(crate) fn get_memory_and_wasi_state_and_inodes_mut( + &self, + _mem_index: u32, + ) -> (&Memory, &WasiState, RwLockWriteGuard) { + let memory = self.memory(); + let state = self.state.deref(); + let inodes = state.inodes.write().unwrap(); + (memory, state, inodes) } } @@ -152,19 +361,22 @@ impl WasiEnv { /// [`WasiStateBuilder`](state::WasiStateBuilder). pub fn generate_import_object_from_env( store: &Store, - wasi_env: WasiEnv, + env: WasiEnv, version: WasiVersion, ) -> Imports { match version { - WasiVersion::Snapshot0 => generate_import_object_snapshot0(store, wasi_env), + WasiVersion::Snapshot0 => generate_import_object_snapshot0(store, env), + WasiVersion::Wasix32v1 => generate_import_object_wasix32_v1(store, env), + WasiVersion::Wasix64v1 => generate_import_object_wasix64_v1(store, env), WasiVersion::Snapshot1 | WasiVersion::Latest => { - generate_import_object_snapshot1(store, wasi_env) + generate_import_object_snapshot1(store, env) } } } /// Combines a state generating function with the import list for legacy WASI fn generate_import_object_snapshot0(store: &Store, env: WasiEnv) -> Imports { + use self::wasi::*; imports! { "wasi_unstable" => { "args_get" => Function::new_native_with_env(store, env.clone(), args_get), @@ -218,6 +430,7 @@ fn generate_import_object_snapshot0(store: &Store, env: WasiEnv) -> Imports { /// Combines a state generating function with the import list for snapshot 1 fn generate_import_object_snapshot1(store: &Store, env: WasiEnv) -> Imports { + use self::wasi::*; imports! { "wasi_snapshot_preview1" => { "args_get" => Function::new_native_with_env(store, env.clone(), args_get), @@ -269,6 +482,237 @@ fn generate_import_object_snapshot1(store: &Store, env: WasiEnv) -> Imports { } } +/// Combines a state generating function with the import list for snapshot 1 +fn generate_import_object_wasix32_v1(store: &Store, env: WasiEnv) -> Imports { + use self::wasix32::*; + imports! { + "wasix_32v1" => { + "args_get" => Function::new_native_with_env(store, env.clone(), args_get), + "args_sizes_get" => Function::new_native_with_env(store, env.clone(), args_sizes_get), + "clock_res_get" => Function::new_native_with_env(store, env.clone(), clock_res_get), + "clock_time_get" => Function::new_native_with_env(store, env.clone(), clock_time_get), + "environ_get" => Function::new_native_with_env(store, env.clone(), environ_get), + "environ_sizes_get" => Function::new_native_with_env(store, env.clone(), environ_sizes_get), + "fd_advise" => Function::new_native_with_env(store, env.clone(), fd_advise), + "fd_allocate" => Function::new_native_with_env(store, env.clone(), fd_allocate), + "fd_close" => Function::new_native_with_env(store, env.clone(), fd_close), + "fd_datasync" => Function::new_native_with_env(store, env.clone(), fd_datasync), + "fd_fdstat_get" => Function::new_native_with_env(store, env.clone(), fd_fdstat_get), + "fd_fdstat_set_flags" => Function::new_native_with_env(store, env.clone(), fd_fdstat_set_flags), + "fd_fdstat_set_rights" => Function::new_native_with_env(store, env.clone(), fd_fdstat_set_rights), + "fd_filestat_get" => Function::new_native_with_env(store, env.clone(), fd_filestat_get), + "fd_filestat_set_size" => Function::new_native_with_env(store, env.clone(), fd_filestat_set_size), + "fd_filestat_set_times" => Function::new_native_with_env(store, env.clone(), fd_filestat_set_times), + "fd_pread" => Function::new_native_with_env(store, env.clone(), fd_pread), + "fd_prestat_get" => Function::new_native_with_env(store, env.clone(), fd_prestat_get), + "fd_prestat_dir_name" => Function::new_native_with_env(store, env.clone(), fd_prestat_dir_name), + "fd_pwrite" => Function::new_native_with_env(store, env.clone(), fd_pwrite), + "fd_read" => Function::new_native_with_env(store, env.clone(), fd_read), + "fd_readdir" => Function::new_native_with_env(store, env.clone(), fd_readdir), + "fd_renumber" => Function::new_native_with_env(store, env.clone(), fd_renumber), + "fd_dup" => Function::new_native_with_env(store, env.clone(), fd_dup), + "fd_event" => Function::new_native_with_env(store, env.clone(), fd_event), + "fd_seek" => Function::new_native_with_env(store, env.clone(), fd_seek), + "fd_sync" => Function::new_native_with_env(store, env.clone(), fd_sync), + "fd_tell" => Function::new_native_with_env(store, env.clone(), fd_tell), + "fd_write" => Function::new_native_with_env(store, env.clone(), fd_write), + "fd_pipe" => Function::new_native_with_env(store, env.clone(), fd_pipe), + "path_create_directory" => Function::new_native_with_env(store, env.clone(), path_create_directory), + "path_filestat_get" => Function::new_native_with_env(store, env.clone(), path_filestat_get), + "path_filestat_set_times" => Function::new_native_with_env(store, env.clone(), path_filestat_set_times), + "path_link" => Function::new_native_with_env(store, env.clone(), path_link), + "path_open" => Function::new_native_with_env(store, env.clone(), path_open), + "path_readlink" => Function::new_native_with_env(store, env.clone(), path_readlink), + "path_remove_directory" => Function::new_native_with_env(store, env.clone(), path_remove_directory), + "path_rename" => Function::new_native_with_env(store, env.clone(), path_rename), + "path_symlink" => Function::new_native_with_env(store, env.clone(), path_symlink), + "path_unlink_file" => Function::new_native_with_env(store, env.clone(), path_unlink_file), + "poll_oneoff" => Function::new_native_with_env(store, env.clone(), poll_oneoff), + "proc_exit" => Function::new_native_with_env(store, env.clone(), proc_exit), + "proc_raise" => Function::new_native_with_env(store, env.clone(), proc_raise), + "random_get" => Function::new_native_with_env(store, env.clone(), random_get), + "tty_get" => Function::new_native_with_env(store, env.clone(), tty_get), + "tty_set" => Function::new_native_with_env(store, env.clone(), tty_set), + "getcwd" => Function::new_native_with_env(store, env.clone(), getcwd), + "chdir" => Function::new_native_with_env(store, env.clone(), chdir), + "thread_spawn" => Function::new_native_with_env(store, env.clone(), thread_spawn), + "thread_sleep" => Function::new_native_with_env(store, env.clone(), thread_sleep), + "thread_id" => Function::new_native_with_env(store, env.clone(), thread_id), + "thread_join" => Function::new_native_with_env(store, env.clone(), thread_join), + "thread_parallelism" => Function::new_native_with_env(store, env.clone(), thread_parallelism), + "thread_exit" => Function::new_native_with_env(store, env.clone(), thread_exit), + "sched_yield" => Function::new_native_with_env(store, env.clone(), sched_yield), + "getpid" => Function::new_native_with_env(store, env.clone(), getpid), + "process_spawn" => Function::new_native_with_env(store, env.clone(), process_spawn), + "bus_open_local" => Function::new_native_with_env(store, env.clone(), bus_open_local), + "bus_open_remote" => Function::new_native_with_env(store, env.clone(), bus_open_remote), + "bus_close" => Function::new_native_with_env(store, env.clone(), bus_close), + "bus_call" => Function::new_native_with_env(store, env.clone(), bus_call), + "bus_subcall" => Function::new_native_with_env(store, env.clone(), bus_subcall), + "bus_poll" => Function::new_native_with_env(store, env.clone(), bus_poll), + "call_reply" => Function::new_native_with_env(store, env.clone(), call_reply), + "call_fault" => Function::new_native_with_env(store, env.clone(), call_fault), + "call_close" => Function::new_native_with_env(store, env.clone(), call_close), + "ws_connect" => Function::new_native_with_env(store, env.clone(), ws_connect), + "http_request" => Function::new_native_with_env(store, env.clone(), http_request), + "http_status" => Function::new_native_with_env(store, env.clone(), http_status), + "port_bridge" => Function::new_native_with_env(store, env.clone(), port_bridge), + "port_unbridge" => Function::new_native_with_env(store, env.clone(), port_unbridge), + "port_dhcp_acquire" => Function::new_native_with_env(store, env.clone(), port_dhcp_acquire), + "port_addr_add" => Function::new_native_with_env(store, env.clone(), port_addr_add), + "port_addr_remove" => Function::new_native_with_env(store, env.clone(), port_addr_remove), + "port_addr_clear" => Function::new_native_with_env(store, env.clone(), port_addr_clear), + "port_addr_list" => Function::new_native_with_env(store, env.clone(), port_addr_list), + "port_mac" => Function::new_native_with_env(store, env.clone(), port_mac), + "port_gateway_set" => Function::new_native_with_env(store, env.clone(), port_gateway_set), + "port_route_add" => Function::new_native_with_env(store, env.clone(), port_route_add), + "port_route_remove" => Function::new_native_with_env(store, env.clone(), port_route_remove), + "port_route_clear" => Function::new_native_with_env(store, env.clone(), port_route_clear), + "port_route_list" => Function::new_native_with_env(store, env.clone(), port_route_list), + "sock_status" => Function::new_native_with_env(store, env.clone(), sock_status), + "sock_addr_local" => Function::new_native_with_env(store, env.clone(), sock_addr_local), + "sock_addr_peer" => Function::new_native_with_env(store, env.clone(), sock_addr_peer), + "sock_open" => Function::new_native_with_env(store, env.clone(), sock_open), + "sock_set_opt_flag" => Function::new_native_with_env(store, env.clone(), sock_set_opt_flag), + "sock_get_opt_flag" => Function::new_native_with_env(store, env.clone(), sock_get_opt_flag), + "sock_set_opt_time" => Function::new_native_with_env(store, env.clone(), sock_set_opt_time), + "sock_get_opt_time" => Function::new_native_with_env(store, env.clone(), sock_get_opt_time), + "sock_set_opt_size" => Function::new_native_with_env(store, env.clone(), sock_set_opt_size), + "sock_get_opt_size" => Function::new_native_with_env(store, env.clone(), sock_get_opt_size), + "sock_join_multicast_v4" => Function::new_native_with_env(store, env.clone(), sock_join_multicast_v4), + "sock_leave_multicast_v4" => Function::new_native_with_env(store, env.clone(), sock_leave_multicast_v4), + "sock_join_multicast_v6" => Function::new_native_with_env(store, env.clone(), sock_join_multicast_v6), + "sock_leave_multicast_v6" => Function::new_native_with_env(store, env.clone(), sock_leave_multicast_v6), + "sock_bind" => Function::new_native_with_env(store, env.clone(), sock_bind), + "sock_listen" => Function::new_native_with_env(store, env.clone(), sock_listen), + "sock_accept" => Function::new_native_with_env(store, env.clone(), sock_accept), + "sock_connect" => Function::new_native_with_env(store, env.clone(), sock_connect), + "sock_recv" => Function::new_native_with_env(store, env.clone(), sock_recv), + "sock_recv_from" => Function::new_native_with_env(store, env.clone(), sock_recv_from), + "sock_send" => Function::new_native_with_env(store, env.clone(), sock_send), + "sock_send_to" => Function::new_native_with_env(store, env.clone(), sock_send_to), + "sock_send_file" => Function::new_native_with_env(store, env.clone(), sock_send_file), + "sock_shutdown" => Function::new_native_with_env(store, env.clone(), sock_shutdown), + "resolve" => Function::new_native_with_env(store, env, resolve), + } + } +} + +fn generate_import_object_wasix64_v1(store: &Store, env: WasiEnv) -> Imports { + use self::wasix64::*; + imports! { + "wasix_64v1" => { + "args_get" => Function::new_native_with_env(store, env.clone(), args_get), + "args_sizes_get" => Function::new_native_with_env(store, env.clone(), args_sizes_get), + "clock_res_get" => Function::new_native_with_env(store, env.clone(), clock_res_get), + "clock_time_get" => Function::new_native_with_env(store, env.clone(), clock_time_get), + "environ_get" => Function::new_native_with_env(store, env.clone(), environ_get), + "environ_sizes_get" => Function::new_native_with_env(store, env.clone(), environ_sizes_get), + "fd_advise" => Function::new_native_with_env(store, env.clone(), fd_advise), + "fd_allocate" => Function::new_native_with_env(store, env.clone(), fd_allocate), + "fd_close" => Function::new_native_with_env(store, env.clone(), fd_close), + "fd_datasync" => Function::new_native_with_env(store, env.clone(), fd_datasync), + "fd_fdstat_get" => Function::new_native_with_env(store, env.clone(), fd_fdstat_get), + "fd_fdstat_set_flags" => Function::new_native_with_env(store, env.clone(), fd_fdstat_set_flags), + "fd_fdstat_set_rights" => Function::new_native_with_env(store, env.clone(), fd_fdstat_set_rights), + "fd_filestat_get" => Function::new_native_with_env(store, env.clone(), fd_filestat_get), + "fd_filestat_set_size" => Function::new_native_with_env(store, env.clone(), fd_filestat_set_size), + "fd_filestat_set_times" => Function::new_native_with_env(store, env.clone(), fd_filestat_set_times), + "fd_pread" => Function::new_native_with_env(store, env.clone(), fd_pread), + "fd_prestat_get" => Function::new_native_with_env(store, env.clone(), fd_prestat_get), + "fd_prestat_dir_name" => Function::new_native_with_env(store, env.clone(), fd_prestat_dir_name), + "fd_pwrite" => Function::new_native_with_env(store, env.clone(), fd_pwrite), + "fd_read" => Function::new_native_with_env(store, env.clone(), fd_read), + "fd_readdir" => Function::new_native_with_env(store, env.clone(), fd_readdir), + "fd_renumber" => Function::new_native_with_env(store, env.clone(), fd_renumber), + "fd_dup" => Function::new_native_with_env(store, env.clone(), fd_dup), + "fd_event" => Function::new_native_with_env(store, env.clone(), fd_event), + "fd_seek" => Function::new_native_with_env(store, env.clone(), fd_seek), + "fd_sync" => Function::new_native_with_env(store, env.clone(), fd_sync), + "fd_tell" => Function::new_native_with_env(store, env.clone(), fd_tell), + "fd_write" => Function::new_native_with_env(store, env.clone(), fd_write), + "fd_pipe" => Function::new_native_with_env(store, env.clone(), fd_pipe), + "path_create_directory" => Function::new_native_with_env(store, env.clone(), path_create_directory), + "path_filestat_get" => Function::new_native_with_env(store, env.clone(), path_filestat_get), + "path_filestat_set_times" => Function::new_native_with_env(store, env.clone(), path_filestat_set_times), + "path_link" => Function::new_native_with_env(store, env.clone(), path_link), + "path_open" => Function::new_native_with_env(store, env.clone(), path_open), + "path_readlink" => Function::new_native_with_env(store, env.clone(), path_readlink), + "path_remove_directory" => Function::new_native_with_env(store, env.clone(), path_remove_directory), + "path_rename" => Function::new_native_with_env(store, env.clone(), path_rename), + "path_symlink" => Function::new_native_with_env(store, env.clone(), path_symlink), + "path_unlink_file" => Function::new_native_with_env(store, env.clone(), path_unlink_file), + "poll_oneoff" => Function::new_native_with_env(store, env.clone(), poll_oneoff), + "proc_exit" => Function::new_native_with_env(store, env.clone(), proc_exit), + "proc_raise" => Function::new_native_with_env(store, env.clone(), proc_raise), + "random_get" => Function::new_native_with_env(store, env.clone(), random_get), + "tty_get" => Function::new_native_with_env(store, env.clone(), tty_get), + "tty_set" => Function::new_native_with_env(store, env.clone(), tty_set), + "getcwd" => Function::new_native_with_env(store, env.clone(), getcwd), + "chdir" => Function::new_native_with_env(store, env.clone(), chdir), + "thread_spawn" => Function::new_native_with_env(store, env.clone(), thread_spawn), + "thread_sleep" => Function::new_native_with_env(store, env.clone(), thread_sleep), + "thread_id" => Function::new_native_with_env(store, env.clone(), thread_id), + "thread_join" => Function::new_native_with_env(store, env.clone(), thread_join), + "thread_parallelism" => Function::new_native_with_env(store, env.clone(), thread_parallelism), + "thread_exit" => Function::new_native_with_env(store, env.clone(), thread_exit), + "sched_yield" => Function::new_native_with_env(store, env.clone(), sched_yield), + "getpid" => Function::new_native_with_env(store, env.clone(), getpid), + "process_spawn" => Function::new_native_with_env(store, env.clone(), process_spawn), + "bus_open_local" => Function::new_native_with_env(store, env.clone(), bus_open_local), + "bus_open_remote" => Function::new_native_with_env(store, env.clone(), bus_open_remote), + "bus_close" => Function::new_native_with_env(store, env.clone(), bus_close), + "bus_call" => Function::new_native_with_env(store, env.clone(), bus_call), + "bus_subcall" => Function::new_native_with_env(store, env.clone(), bus_subcall), + "bus_poll" => Function::new_native_with_env(store, env.clone(), bus_poll), + "call_reply" => Function::new_native_with_env(store, env.clone(), call_reply), + "call_fault" => Function::new_native_with_env(store, env.clone(), call_fault), + "call_close" => Function::new_native_with_env(store, env.clone(), call_close), + "ws_connect" => Function::new_native_with_env(store, env.clone(), ws_connect), + "http_request" => Function::new_native_with_env(store, env.clone(), http_request), + "http_status" => Function::new_native_with_env(store, env.clone(), http_status), + "port_bridge" => Function::new_native_with_env(store, env.clone(), port_bridge), + "port_unbridge" => Function::new_native_with_env(store, env.clone(), port_unbridge), + "port_dhcp_acquire" => Function::new_native_with_env(store, env.clone(), port_dhcp_acquire), + "port_addr_add" => Function::new_native_with_env(store, env.clone(), port_addr_add), + "port_addr_remove" => Function::new_native_with_env(store, env.clone(), port_addr_remove), + "port_addr_clear" => Function::new_native_with_env(store, env.clone(), port_addr_clear), + "port_addr_list" => Function::new_native_with_env(store, env.clone(), port_addr_list), + "port_mac" => Function::new_native_with_env(store, env.clone(), port_mac), + "port_gateway_set" => Function::new_native_with_env(store, env.clone(), port_gateway_set), + "port_route_add" => Function::new_native_with_env(store, env.clone(), port_route_add), + "port_route_remove" => Function::new_native_with_env(store, env.clone(), port_route_remove), + "port_route_clear" => Function::new_native_with_env(store, env.clone(), port_route_clear), + "port_route_list" => Function::new_native_with_env(store, env.clone(), port_route_list), + "sock_status" => Function::new_native_with_env(store, env.clone(), sock_status), + "sock_addr_local" => Function::new_native_with_env(store, env.clone(), sock_addr_local), + "sock_addr_peer" => Function::new_native_with_env(store, env.clone(), sock_addr_peer), + "sock_open" => Function::new_native_with_env(store, env.clone(), sock_open), + "sock_set_opt_flag" => Function::new_native_with_env(store, env.clone(), sock_set_opt_flag), + "sock_get_opt_flag" => Function::new_native_with_env(store, env.clone(), sock_get_opt_flag), + "sock_set_opt_time" => Function::new_native_with_env(store, env.clone(), sock_set_opt_time), + "sock_get_opt_time" => Function::new_native_with_env(store, env.clone(), sock_get_opt_time), + "sock_set_opt_size" => Function::new_native_with_env(store, env.clone(), sock_set_opt_size), + "sock_get_opt_size" => Function::new_native_with_env(store, env.clone(), sock_get_opt_size), + "sock_join_multicast_v4" => Function::new_native_with_env(store, env.clone(), sock_join_multicast_v4), + "sock_leave_multicast_v4" => Function::new_native_with_env(store, env.clone(), sock_leave_multicast_v4), + "sock_join_multicast_v6" => Function::new_native_with_env(store, env.clone(), sock_join_multicast_v6), + "sock_leave_multicast_v6" => Function::new_native_with_env(store, env.clone(), sock_leave_multicast_v6), + "sock_bind" => Function::new_native_with_env(store, env.clone(), sock_bind), + "sock_listen" => Function::new_native_with_env(store, env.clone(), sock_listen), + "sock_accept" => Function::new_native_with_env(store, env.clone(), sock_accept), + "sock_connect" => Function::new_native_with_env(store, env.clone(), sock_connect), + "sock_recv" => Function::new_native_with_env(store, env.clone(), sock_recv), + "sock_recv_from" => Function::new_native_with_env(store, env.clone(), sock_recv_from), + "sock_send" => Function::new_native_with_env(store, env.clone(), sock_send), + "sock_send_to" => Function::new_native_with_env(store, env.clone(), sock_send_to), + "sock_send_file" => Function::new_native_with_env(store, env.clone(), sock_send_file), + "sock_shutdown" => Function::new_native_with_env(store, env.clone(), sock_shutdown), + "resolve" => Function::new_native_with_env(store, env, resolve), + } + } +} + fn mem_error_to_wasi(err: MemoryAccessError) -> types::__wasi_errno_t { match err { MemoryAccessError::HeapOutOfBounds => types::__WASI_EFAULT, @@ -277,3 +721,12 @@ fn mem_error_to_wasi(err: MemoryAccessError) -> types::__wasi_errno_t { _ => types::__WASI_EINVAL, } } + +fn mem_error_to_bus(err: MemoryAccessError) -> types::__bus_errno_t { + match err { + MemoryAccessError::HeapOutOfBounds => types::__BUS_EMEMVIOLATION, + MemoryAccessError::Overflow => types::__BUS_EMEMVIOLATION, + MemoryAccessError::NonUtf8String => types::__BUS_EBADREQUEST, + _ => types::__BUS_EUNKNOWN, + } +} diff --git a/lib/wasi/src/macros.rs b/lib/wasi/src/macros.rs index e2488668e08..b9ec4245597 100644 --- a/lib/wasi/src/macros.rs +++ b/lib/wasi/src/macros.rs @@ -11,14 +11,63 @@ macro_rules! wasi_try { val } Err(err) => { - tracing::trace!("wasi::wasi_try::err: {:?}", err); + tracing::debug!("wasi::wasi_try::err: {:?}", err); return err; } } }}; - ($expr:expr, $e:expr) => {{ - let opt: Option<_> = $expr; - wasi_try!(opt.ok_or($e)) +} + +/// Like the `try!` macro or `?` syntax: returns the value if the computation +/// succeeded or returns the error value. Results are wrapped in an Ok +macro_rules! wasi_try_ok { + ($expr:expr) => {{ + let res: Result<_, crate::syscalls::types::__wasi_errno_t> = $expr; + match res { + Ok(val) => { + tracing::trace!("wasi::wasi_try_ok::val: {:?}", val); + val + } + Err(err) => { + tracing::debug!("wasi::wasi_try_ok::err: {:?}", err); + return Ok(err); + } + } + }}; + + ($expr:expr, $thread:expr) => {{ + let res: Result<_, crate::syscalls::types::__wasi_errno_t> = $expr; + match res { + Ok(val) => { + tracing::trace!("wasi::wasi_try_ok::val: {:?}", val); + val + } + Err(err) => { + if err == __WASI_EINTR { + $thread.yield_now()?; + } + tracing::debug!("wasi::wasi_try_ok::err: {:?}", err); + return Ok(err); + } + } + }}; +} + +/// Like the `try!` macro or `?` syntax: returns the value if the computation +/// succeeded or returns the error value. +macro_rules! wasi_try_bus { + ($expr:expr) => {{ + let res: Result<_, crate::syscalls::types::__bus_errno_t> = $expr; + match res { + Ok(val) => { + tracing::trace!("wasi::wasi_try_bus::val: {:?}", val); + val + } + Err(err) => { + tracing::debug!("wasi::wasi_try_bus::err: {:?}", err); + return err; + } + } }}; } @@ -29,9 +78,33 @@ macro_rules! wasi_try_mem { }}; } +/// Like `wasi_try` but converts a `MemoryAccessError` to a __bus_errno_t`. +macro_rules! wasi_try_mem_bus { + ($expr:expr) => {{ + wasi_try_bus!($expr.map_err($crate::mem_error_to_bus)) + }}; +} + +/// Like `wasi_try` but converts a `MemoryAccessError` to a __wasi_errno_t`. +macro_rules! wasi_try_mem_ok { + ($expr:expr) => {{ + wasi_try_ok!($expr.map_err($crate::mem_error_to_wasi)) + }}; + + ($expr:expr, $thread:expr) => {{ + wasi_try_ok!($expr.map_err($crate::mem_error_to_wasi), $thread) + }}; +} + /// Reads a string from Wasm memory. macro_rules! get_input_str { ($memory:expr, $data:expr, $len:expr) => {{ wasi_try_mem!($data.read_utf8_string($memory, $len)) }}; } + +macro_rules! get_input_str_bus { + ($memory:expr, $data:expr, $len:expr) => {{ + wasi_try_mem_bus!($data.read_utf8_string($memory, $len)) + }}; +} diff --git a/lib/wasi/src/runtime.rs b/lib/wasi/src/runtime.rs new file mode 100644 index 00000000000..00c208bd166 --- /dev/null +++ b/lib/wasi/src/runtime.rs @@ -0,0 +1,151 @@ +use std::fmt; +use std::ops::Deref; +use std::sync::atomic::{AtomicU32, Ordering}; +use thiserror::Error; +use wasmer_vbus::{UnsupportedVirtualBus, VirtualBus}; +use wasmer_vnet::VirtualNetworking; + +use super::types::*; +use super::WasiError; +use super::WasiThreadId; + +#[derive(Error, Debug)] +pub enum WasiThreadError { + #[error("Multithreading is not supported")] + Unsupported, + #[error("The method named is not an exported function")] + MethodNotFound, +} + +impl From for __wasi_errno_t { + fn from(a: WasiThreadError) -> __wasi_errno_t { + match a { + WasiThreadError::Unsupported => __WASI_ENOTSUP, + WasiThreadError::MethodNotFound => __WASI_EINVAL, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct WasiTtyState { + pub cols: u32, + pub rows: u32, + pub width: u32, + pub height: u32, + pub stdin_tty: bool, + pub stdout_tty: bool, + pub stderr_tty: bool, + pub echo: bool, + pub line_buffered: bool, +} + +/// Represents an implementation of the WASI runtime - by default everything is +/// unimplemented. +pub trait WasiRuntimeImplementation: fmt::Debug + Sync { + /// For WASI runtimes that support it they can implement a message BUS implementation + /// which allows runtimes to pass serialized messages between each other similar to + /// RPC's. BUS implementation can be implemented that communicate across runtimes + /// thus creating a distributed computing architecture. + fn bus(&self) -> &(dyn VirtualBus); + + /// Provides access to all the networking related functions such as sockets. + /// By default networking is not implemented. + fn networking(&self) -> &(dyn VirtualNetworking); + + /// Generates a new thread ID + fn thread_generate_id(&self) -> WasiThreadId; + + /// Gets the TTY state + fn tty_get(&self) -> WasiTtyState { + WasiTtyState { + rows: 25, + cols: 80, + width: 800, + height: 600, + stdin_tty: false, + stdout_tty: false, + stderr_tty: false, + echo: true, + line_buffered: true, + } + } + + /// Sets the TTY state + fn tty_set(&self, _tty_state: WasiTtyState) {} + + /// Spawns a new thread by invoking the + fn thread_spawn( + &self, + _callback: Box, + ) -> Result<(), WasiThreadError> { + Err(WasiThreadError::Unsupported) + } + + /// Returns the amount of parallelism that is possible on this platform + fn thread_parallelism(&self) -> Result { + Err(WasiThreadError::Unsupported) + } + + /// Invokes whenever a WASM thread goes idle. In some runtimes (like singlethreaded + /// execution environments) they will need to do asynchronous work whenever the main + /// thread goes idle and this is the place to hook for that. + fn yield_now(&self, _id: WasiThreadId) -> Result<(), WasiError> { + std::thread::yield_now(); + Ok(()) + } + + /// Gets the current process ID + fn getpid(&self) -> Option { + None + } +} + +#[derive(Debug)] +pub struct PluggableRuntimeImplementation { + pub bus: Box, + pub networking: Box, + pub thread_id_seed: AtomicU32, +} + +impl PluggableRuntimeImplementation { + pub fn set_bus_implementation(&mut self, bus: I) + where + I: VirtualBus + Sync, + { + self.bus = Box::new(bus) + } + + pub fn set_networking_implementation(&mut self, net: I) + where + I: VirtualNetworking + Sync, + { + self.networking = Box::new(net) + } +} + +impl Default for PluggableRuntimeImplementation { + fn default() -> Self { + Self { + #[cfg(not(feature = "host-vnet"))] + networking: Box::new(wasmer_vnet::UnsupportedVirtualNetworking::default()), + #[cfg(feature = "host-vnet")] + networking: Box::new(wasmer_wasi_local_networking::LocalNetworking::default()), + bus: Box::new(UnsupportedVirtualBus::default()), + thread_id_seed: Default::default(), + } + } +} + +impl WasiRuntimeImplementation for PluggableRuntimeImplementation { + fn bus(&self) -> &(dyn VirtualBus) { + self.bus.deref() + } + + fn networking(&self) -> &(dyn VirtualNetworking) { + self.networking.deref() + } + + fn thread_generate_id(&self) -> WasiThreadId { + self.thread_id_seed.fetch_add(1, Ordering::Relaxed).into() + } +} diff --git a/lib/wasi/src/state/builder.rs b/lib/wasi/src/state/builder.rs index 37bf5c9d37b..541b0a4a923 100644 --- a/lib/wasi/src/state/builder.rs +++ b/lib/wasi/src/state/builder.rs @@ -2,8 +2,13 @@ use crate::state::{default_fs_backing, WasiFs, WasiState}; use crate::syscalls::types::{__WASI_STDERR_FILENO, __WASI_STDIN_FILENO, __WASI_STDOUT_FILENO}; -use crate::WasiEnv; +use crate::{WasiEnv, WasiInodes}; +use generational_arena::Arena; +use std::collections::HashMap; +use std::ops::{Deref, DerefMut}; use std::path::{Path, PathBuf}; +use std::sync::Arc; +use std::sync::RwLock; use thiserror::Error; use wasmer_vfs::{FsError, VirtualFile}; @@ -40,11 +45,12 @@ pub struct WasiStateBuilder { preopens: Vec, vfs_preopens: Vec, #[allow(clippy::type_complexity)] - setup_fs_fn: Option Result<(), String> + Send>>, - stdout_override: Option>, - stderr_override: Option>, - stdin_override: Option>, + setup_fs_fn: Option Result<(), String> + Send>>, + stdout_override: Option>, + stderr_override: Option>, + stdin_override: Option>, fs_override: Option>, + runtime_override: Option>, } impl std::fmt::Debug for WasiStateBuilder { @@ -58,6 +64,7 @@ impl std::fmt::Debug for WasiStateBuilder { .field("stdout_override exists", &self.stdout_override.is_some()) .field("stderr_override exists", &self.stderr_override.is_some()) .field("stdin_override exists", &self.stdin_override.is_some()) + .field("runtime_override_exists", &self.runtime_override.is_some()) .finish() } } @@ -277,7 +284,7 @@ impl WasiStateBuilder { /// Overwrite the default WASI `stdout`, if you want to hold on to the /// original `stdout` use [`WasiFs::swap_file`] after building. - pub fn stdout(&mut self, new_file: Box) -> &mut Self { + pub fn stdout(&mut self, new_file: Box) -> &mut Self { self.stdout_override = Some(new_file); self @@ -285,7 +292,7 @@ impl WasiStateBuilder { /// Overwrite the default WASI `stderr`, if you want to hold on to the /// original `stderr` use [`WasiFs::swap_file`] after building. - pub fn stderr(&mut self, new_file: Box) -> &mut Self { + pub fn stderr(&mut self, new_file: Box) -> &mut Self { self.stderr_override = Some(new_file); self @@ -293,7 +300,7 @@ impl WasiStateBuilder { /// Overwrite the default WASI `stdin`, if you want to hold on to the /// original `stdin` use [`WasiFs::swap_file`] after building. - pub fn stdin(&mut self, new_file: Box) -> &mut Self { + pub fn stdin(&mut self, new_file: Box) -> &mut Self { self.stdin_override = Some(new_file); self @@ -312,13 +319,23 @@ impl WasiStateBuilder { // TODO: improve ergonomics on this function pub fn setup_fs( &mut self, - setup_fs_fn: Box Result<(), String> + Send>, + setup_fs_fn: Box Result<(), String> + Send>, ) -> &mut Self { self.setup_fs_fn = Some(setup_fs_fn); self } + /// Sets the WASI runtime implementation and overrides the default + /// implementation + pub fn runtime(&mut self, runtime: R) -> &mut Self + where + R: crate::WasiRuntimeImplementation + Send + Sync + 'static, + { + self.runtime_override = Some(Arc::new(runtime)); + self + } + /// Consumes the [`WasiStateBuilder`] and produces a [`WasiState`] /// /// Returns the error from `WasiFs::new` if there's an error @@ -404,35 +421,53 @@ impl WasiStateBuilder { let fs_backing = self.fs_override.take().unwrap_or_else(default_fs_backing); // self.preopens are checked in [`PreopenDirBuilder::build`] - let mut wasi_fs = WasiFs::new_with_preopen(&self.preopens, &self.vfs_preopens, fs_backing) + let inodes = RwLock::new(crate::state::WasiInodes { + arena: Arena::new(), + orphan_fds: HashMap::new(), + }); + let wasi_fs = { + let mut inodes = inodes.write().unwrap(); + + // self.preopens are checked in [`PreopenDirBuilder::build`] + let mut wasi_fs = WasiFs::new_with_preopen( + inodes.deref_mut(), + &self.preopens, + &self.vfs_preopens, + fs_backing, + ) .map_err(WasiStateCreationError::WasiFsCreationError)?; - // set up the file system, overriding base files and calling the setup function - if let Some(stdin_override) = self.stdin_override.take() { - wasi_fs - .swap_file(__WASI_STDIN_FILENO, stdin_override) - .map_err(WasiStateCreationError::FileSystemError)?; - } + // set up the file system, overriding base files and calling the setup function + if let Some(stdin_override) = self.stdin_override.take() { + wasi_fs + .swap_file(inodes.deref(), __WASI_STDIN_FILENO, stdin_override) + .map_err(WasiStateCreationError::FileSystemError)?; + } - if let Some(stdout_override) = self.stdout_override.take() { - wasi_fs - .swap_file(__WASI_STDOUT_FILENO, stdout_override) - .map_err(WasiStateCreationError::FileSystemError)?; - } + if let Some(stdout_override) = self.stdout_override.take() { + wasi_fs + .swap_file(inodes.deref(), __WASI_STDOUT_FILENO, stdout_override) + .map_err(WasiStateCreationError::FileSystemError)?; + } - if let Some(stderr_override) = self.stderr_override.take() { - wasi_fs - .swap_file(__WASI_STDERR_FILENO, stderr_override) - .map_err(WasiStateCreationError::FileSystemError)?; - } + if let Some(stderr_override) = self.stderr_override.take() { + wasi_fs + .swap_file(inodes.deref(), __WASI_STDERR_FILENO, stderr_override) + .map_err(WasiStateCreationError::FileSystemError)?; + } - if let Some(f) = &self.setup_fs_fn { - f(&mut wasi_fs).map_err(WasiStateCreationError::WasiFsSetupError)?; - } + if let Some(f) = &self.setup_fs_fn { + f(inodes.deref_mut(), &mut wasi_fs) + .map_err(WasiStateCreationError::WasiFsSetupError)?; + } + wasi_fs + }; Ok(WasiState { fs: wasi_fs, + inodes: Arc::new(inodes), args: self.args.clone(), + threading: Default::default(), envs: self .envs .iter() @@ -461,7 +496,11 @@ impl WasiStateBuilder { pub fn finalize(&mut self) -> Result { let state = self.build()?; - Ok(WasiEnv::new(state)) + let mut env = WasiEnv::new(state); + if let Some(runtime) = self.runtime_override.as_ref() { + env.runtime = runtime.clone(); + } + Ok(env) } } diff --git a/lib/wasi/src/state/guard.rs b/lib/wasi/src/state/guard.rs new file mode 100644 index 00000000000..e37c9c97845 --- /dev/null +++ b/lib/wasi/src/state/guard.rs @@ -0,0 +1,285 @@ +use super::*; +use std::{ + io::{Read, Seek}, + sync::{RwLockReadGuard, RwLockWriteGuard}, +}; + +#[derive(Debug)] +pub(crate) struct InodeValFileReadGuard<'a> { + pub(crate) guard: RwLockReadGuard<'a, Kind>, +} + +impl<'a> Deref for InodeValFileReadGuard<'a> { + type Target = Option>; + fn deref(&self) -> &Self::Target { + if let Kind::File { handle, .. } = self.guard.deref() { + return handle; + } + unreachable!() + } +} + +#[derive(Debug)] +pub struct InodeValFileWriteGuard<'a> { + pub(crate) guard: RwLockWriteGuard<'a, Kind>, +} + +impl<'a> Deref for InodeValFileWriteGuard<'a> { + type Target = Option>; + fn deref(&self) -> &Self::Target { + if let Kind::File { handle, .. } = self.guard.deref() { + return handle; + } + unreachable!() + } +} + +impl<'a> DerefMut for InodeValFileWriteGuard<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + if let Kind::File { handle, .. } = self.guard.deref_mut() { + return handle; + } + unreachable!() + } +} + +#[derive(Debug)] +pub(crate) struct WasiStateFileGuard { + inodes: Arc>, + inode: generational_arena::Index, +} + +impl WasiStateFileGuard { + pub fn new(state: &WasiState, fd: __wasi_fd_t) -> Result, FsError> { + let inodes = state.inodes.read().unwrap(); + let fd_map = state.fs.fd_map.read().unwrap(); + if let Some(fd) = fd_map.get(&fd) { + let guard = inodes.arena[fd.inode].read(); + if let Kind::File { .. } = guard.deref() { + Ok(Some(Self { + inodes: state.inodes.clone(), + inode: fd.inode, + })) + } else { + // Our public API should ensure that this is not possible + Err(FsError::NotAFile) + } + } else { + Ok(None) + } + } + + pub fn lock_read<'a>( + &self, + inodes: &'a RwLockReadGuard, + ) -> InodeValFileReadGuard<'a> { + let guard = inodes.arena[self.inode].read(); + if let Kind::File { .. } = guard.deref() { + InodeValFileReadGuard { guard } + } else { + // Our public API should ensure that this is not possible + unreachable!("Non-file found in standard device location") + } + } + + pub fn lock_write<'a>( + &self, + inodes: &'a RwLockReadGuard, + ) -> InodeValFileWriteGuard<'a> { + let guard = inodes.arena[self.inode].write(); + if let Kind::File { .. } = guard.deref() { + InodeValFileWriteGuard { guard } + } else { + // Our public API should ensure that this is not possible + unreachable!("Non-file found in standard device location") + } + } +} + +impl VirtualFile for WasiStateFileGuard { + fn last_accessed(&self) -> u64 { + let inodes = self.inodes.read().unwrap(); + let guard = self.lock_read(&inodes); + if let Some(file) = guard.deref() { + file.last_accessed() + } else { + 0 + } + } + + fn last_modified(&self) -> u64 { + let inodes = self.inodes.read().unwrap(); + let guard = self.lock_read(&inodes); + if let Some(file) = guard.deref() { + file.last_modified() + } else { + 0 + } + } + + fn created_time(&self) -> u64 { + let inodes = self.inodes.read().unwrap(); + let guard = self.lock_read(&inodes); + if let Some(file) = guard.deref() { + file.created_time() + } else { + 0 + } + } + + fn size(&self) -> u64 { + let inodes = self.inodes.read().unwrap(); + let guard = self.lock_read(&inodes); + if let Some(file) = guard.deref() { + file.size() + } else { + 0 + } + } + + fn set_len(&mut self, new_size: u64) -> Result<(), FsError> { + let inodes = self.inodes.read().unwrap(); + let mut guard = self.lock_write(&inodes); + if let Some(file) = guard.deref_mut() { + file.set_len(new_size) + } else { + Err(FsError::IOError) + } + } + + fn unlink(&mut self) -> Result<(), FsError> { + let inodes = self.inodes.read().unwrap(); + let mut guard = self.lock_write(&inodes); + if let Some(file) = guard.deref_mut() { + file.unlink() + } else { + Err(FsError::IOError) + } + } + + fn sync_to_disk(&self) -> Result<(), FsError> { + let inodes = self.inodes.read().unwrap(); + let guard = self.lock_read(&inodes); + if let Some(file) = guard.deref() { + file.sync_to_disk() + } else { + Err(FsError::IOError) + } + } + + fn bytes_available(&self) -> Result { + let inodes = self.inodes.read().unwrap(); + let guard = self.lock_read(&inodes); + if let Some(file) = guard.deref() { + file.bytes_available() + } else { + Err(FsError::IOError) + } + } + + fn bytes_available_read(&self) -> Result, FsError> { + let inodes = self.inodes.read().unwrap(); + let guard = self.lock_read(&inodes); + if let Some(file) = guard.deref() { + file.bytes_available_read() + } else { + Err(FsError::IOError) + } + } + + fn bytes_available_write(&self) -> Result, FsError> { + let inodes = self.inodes.read().unwrap(); + let guard = self.lock_read(&inodes); + if let Some(file) = guard.deref() { + file.bytes_available_write() + } else { + Err(FsError::IOError) + } + } + + fn is_open(&self) -> bool { + let inodes = self.inodes.read().unwrap(); + let guard = self.lock_read(&inodes); + if let Some(file) = guard.deref() { + file.is_open() + } else { + false + } + } + + fn get_fd(&self) -> Option { + let inodes = self.inodes.read().unwrap(); + let guard = self.lock_read(&inodes); + if let Some(file) = guard.deref() { + file.get_fd() + } else { + None + } + } +} + +impl Write for WasiStateFileGuard { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + let inodes = self.inodes.read().unwrap(); + let mut guard = self.lock_write(&inodes); + if let Some(file) = guard.deref_mut() { + file.write(buf) + } else { + Err(std::io::ErrorKind::Unsupported.into()) + } + } + + fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result { + let inodes = self.inodes.read().unwrap(); + let mut guard = self.lock_write(&inodes); + if let Some(file) = guard.deref_mut() { + file.write_vectored(bufs) + } else { + Err(std::io::ErrorKind::Unsupported.into()) + } + } + + fn flush(&mut self) -> std::io::Result<()> { + let inodes = self.inodes.read().unwrap(); + let mut guard = self.lock_write(&inodes); + if let Some(file) = guard.deref_mut() { + file.flush() + } else { + Err(std::io::ErrorKind::Unsupported.into()) + } + } +} + +impl Read for WasiStateFileGuard { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + let inodes = self.inodes.read().unwrap(); + let mut guard = self.lock_write(&inodes); + if let Some(file) = guard.deref_mut() { + file.read(buf) + } else { + Err(std::io::ErrorKind::Unsupported.into()) + } + } + + fn read_vectored(&mut self, bufs: &mut [std::io::IoSliceMut<'_>]) -> std::io::Result { + let inodes = self.inodes.read().unwrap(); + let mut guard = self.lock_write(&inodes); + if let Some(file) = guard.deref_mut() { + file.read_vectored(bufs) + } else { + Err(std::io::ErrorKind::Unsupported.into()) + } + } +} + +impl Seek for WasiStateFileGuard { + fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result { + let inodes = self.inodes.read().unwrap(); + let mut guard = self.lock_write(&inodes); + if let Some(file) = guard.deref_mut() { + file.seek(pos) + } else { + Err(std::io::ErrorKind::Unsupported.into()) + } + } +} diff --git a/lib/wasi/src/state/mod.rs b/lib/wasi/src/state/mod.rs index 10212563715..1f34a3f92fa 100644 --- a/lib/wasi/src/state/mod.rs +++ b/lib/wasi/src/state/mod.rs @@ -16,23 +16,42 @@ #![allow(clippy::cognitive_complexity, clippy::too_many_arguments)] mod builder; +mod guard; +mod pipe; +mod socket; mod types; pub use self::builder::*; +pub use self::guard::*; +pub use self::pipe::*; +pub use self::socket::*; pub use self::types::*; use crate::syscalls::types::*; +use crate::utils::map_io_err; +use crate::WasiBusProcessId; +use crate::WasiThread; +use crate::WasiThreadId; use generational_arena::Arena; pub use generational_arena::Index as Inode; #[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; +use std::borrow::Cow; use std::collections::HashMap; +use std::collections::VecDeque; +use std::sync::mpsc; +use std::sync::Arc; use std::{ borrow::Borrow, - cell::Cell, io::Write, + ops::{Deref, DerefMut}, path::{Path, PathBuf}, + sync::{ + atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering}, + Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard, + }, }; -use tracing::debug; +use tracing::{debug, trace}; +use wasmer_vbus::BusSpawnedProcess; use wasmer_vfs::{FileSystem, FsError, OpenOptions, VirtualFile}; @@ -62,10 +81,20 @@ pub const MAX_SYMLINKS: u32 = 128; #[derive(Debug)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] pub struct InodeVal { - pub stat: __wasi_filestat_t, + pub stat: RwLock<__wasi_filestat_t>, pub is_preopened: bool, pub name: String, - pub kind: Kind, + pub kind: RwLock, +} + +impl InodeVal { + pub fn read(&self) -> RwLockReadGuard { + self.kind.read().unwrap() + } + + pub fn write(&self) -> RwLockWriteGuard { + self.kind.write().unwrap() + } } /// The core of the filesystem abstraction. Includes directories, @@ -75,7 +104,7 @@ pub struct InodeVal { pub enum Kind { File { /// The open file, if it's open - handle: Option>, + handle: Option>, /// The path on the host system where the file is located /// This is deprecated and will be removed soon path: PathBuf, @@ -85,6 +114,14 @@ pub enum Kind { /// TOOD: clarify here? fd: Option, }, + Socket { + /// Represents a networking socket + socket: InodeSocket, + }, + Pipe { + /// Reference to the pipe + pipe: WasiPipe, + }, Dir { /// Parent directory parent: Option, @@ -118,9 +155,17 @@ pub enum Kind { Buffer { buffer: Vec, }, + EventNotifications { + /// Used for event notifications by the user application or operating system + counter: Arc, + /// Flag that indicates if this is operating + is_semaphore: bool, + /// Receiver that wakes sleeping threads + wakers: Arc>>>, + }, } -#[derive(Debug)] +#[derive(Debug, Clone)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] pub struct Fd { pub rights: __wasi_rights_t, @@ -154,20 +199,138 @@ impl Fd { pub const CREATE: u16 = 16; } +#[derive(Debug)] +#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +pub struct WasiInodes { + pub arena: Arena, + pub orphan_fds: HashMap, +} + +impl WasiInodes { + /// gets either a normal inode or an orphaned inode + pub fn get_inodeval( + &self, + inode: generational_arena::Index, + ) -> Result<&InodeVal, __wasi_errno_t> { + if let Some(iv) = self.arena.get(inode) { + Ok(iv) + } else { + self.orphan_fds.get(&inode).ok_or(__WASI_EBADF) + } + } + + /// gets either a normal inode or an orphaned inode + pub fn get_inodeval_mut( + &mut self, + inode: generational_arena::Index, + ) -> Result<&mut InodeVal, __wasi_errno_t> { + if let Some(iv) = self.arena.get_mut(inode) { + Ok(iv) + } else { + self.orphan_fds.get_mut(&inode).ok_or(__WASI_EBADF) + } + } + + /// Get the `VirtualFile` object at stdout + pub(crate) fn stdout( + &self, + fd_map: &RwLock>, + ) -> Result { + self.std_dev_get(fd_map, __WASI_STDOUT_FILENO) + } + /// Get the `VirtualFile` object at stdout mutably + pub(crate) fn stdout_mut( + &self, + fd_map: &RwLock>, + ) -> Result { + self.std_dev_get_mut(fd_map, __WASI_STDOUT_FILENO) + } + + /// Get the `VirtualFile` object at stderr + pub(crate) fn stderr( + &self, + fd_map: &RwLock>, + ) -> Result { + self.std_dev_get(fd_map, __WASI_STDERR_FILENO) + } + /// Get the `VirtualFile` object at stderr mutably + pub(crate) fn stderr_mut( + &self, + fd_map: &RwLock>, + ) -> Result { + self.std_dev_get_mut(fd_map, __WASI_STDERR_FILENO) + } + + /// Get the `VirtualFile` object at stdin + pub(crate) fn stdin( + &self, + fd_map: &RwLock>, + ) -> Result { + self.std_dev_get(fd_map, __WASI_STDIN_FILENO) + } + /// Get the `VirtualFile` object at stdin mutably + pub(crate) fn stdin_mut( + &self, + fd_map: &RwLock>, + ) -> Result { + self.std_dev_get_mut(fd_map, __WASI_STDIN_FILENO) + } + + /// Internal helper function to get a standard device handle. + /// Expects one of `__WASI_STDIN_FILENO`, `__WASI_STDOUT_FILENO`, `__WASI_STDERR_FILENO`. + fn std_dev_get<'a>( + &'a self, + fd_map: &RwLock>, + fd: __wasi_fd_t, + ) -> Result, FsError> { + if let Some(fd) = fd_map.read().unwrap().get(&fd) { + let guard = self.arena[fd.inode].read(); + if let Kind::File { .. } = guard.deref() { + Ok(InodeValFileReadGuard { guard }) + } else { + // Our public API should ensure that this is not possible + Err(FsError::NotAFile) + } + } else { + // this should only trigger if we made a mistake in this crate + Err(FsError::NoDevice) + } + } + /// Internal helper function to mutably get a standard device handle. + /// Expects one of `__WASI_STDIN_FILENO`, `__WASI_STDOUT_FILENO`, `__WASI_STDERR_FILENO`. + fn std_dev_get_mut<'a>( + &'a self, + fd_map: &RwLock>, + fd: __wasi_fd_t, + ) -> Result, FsError> { + if let Some(fd) = fd_map.read().unwrap().get(&fd) { + let guard = self.arena[fd.inode].write(); + if let Kind::File { .. } = guard.deref() { + Ok(InodeValFileWriteGuard { guard }) + } else { + // Our public API should ensure that this is not possible + Err(FsError::NotAFile) + } + } else { + // this should only trigger if we made a mistake in this crate + Err(FsError::NoDevice) + } + } +} + /// Warning, modifying these fields directly may cause invariants to break and /// should be considered unsafe. These fields may be made private in a future release #[derive(Debug)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] pub struct WasiFs { //pub repo: Repo, - pub preopen_fds: Vec, + pub preopen_fds: RwLock>, pub name_map: HashMap, - pub inodes: Arena, - pub fd_map: HashMap, - pub next_fd: Cell, - inode_counter: Cell, - /// for fds still open after the file has been deleted - pub orphan_fds: HashMap, + pub fd_map: RwLock>, + pub next_fd: AtomicU32, + inode_counter: AtomicU64, + pub current_dir: Mutex, + pub is_wasix: AtomicBool, #[cfg_attr(feature = "enable-serde", serde(skip, default = "default_fs_backing"))] pub fs_backing: Box, } @@ -224,11 +387,12 @@ impl FileSystem for FallbackFileSystem { impl WasiFs { /// Created for the builder API. like `new` but with more information pub(crate) fn new_with_preopen( + inodes: &mut WasiInodes, preopens: &[PreopenedDir], vfs_preopens: &[String], fs_backing: Box, ) -> Result { - let (mut wasi_fs, root_inode) = Self::new_init(fs_backing)?; + let (wasi_fs, root_inode) = Self::new_init(fs_backing, inodes)?; for preopen_name in vfs_preopens { let kind = Kind::Dir { @@ -250,7 +414,7 @@ impl WasiFs { | __WASI_RIGHT_POLL_FD_READWRITE | __WASI_RIGHT_SOCK_SHUTDOWN; let inode = wasi_fs - .create_inode(kind, true, preopen_name.clone()) + .create_inode(inodes, kind, true, preopen_name.clone()) .map_err(|e| { format!( "Failed to create inode for preopened dir (name `{}`): WASI error code: {}", @@ -261,17 +425,20 @@ impl WasiFs { let fd = wasi_fs .create_fd(rights, rights, 0, fd_flags, inode) .map_err(|e| format!("Could not open fd for file {:?}: {}", preopen_name, e))?; - if let Kind::Root { entries } = &mut wasi_fs.inodes[root_inode].kind { - let existing_entry = entries.insert(preopen_name.clone(), inode); - if existing_entry.is_some() { - return Err(format!( - "Found duplicate entry for alias `{}`", - preopen_name - )); + { + let mut guard = inodes.arena[root_inode].write(); + if let Kind::Root { entries } = guard.deref_mut() { + let existing_entry = entries.insert(preopen_name.clone(), inode); + if existing_entry.is_some() { + return Err(format!( + "Found duplicate entry for alias `{}`", + preopen_name + )); + } + assert!(existing_entry.is_none()) } - assert!(existing_entry.is_none()) } - wasi_fs.preopen_fds.push(fd); + wasi_fs.preopen_fds.write().unwrap().push(fd); } for PreopenedDir { @@ -343,15 +510,16 @@ impl WasiFs { | __WASI_RIGHT_PATH_CREATE_FILE | __WASI_RIGHT_PATH_LINK_TARGET | __WASI_RIGHT_PATH_OPEN - | __WASI_RIGHT_PATH_RENAME_TARGET; + | __WASI_RIGHT_PATH_RENAME_TARGET + | __WASI_RIGHT_PATH_SYMLINK; } rights }; let inode = if let Some(alias) = &alias { - wasi_fs.create_inode(kind, true, alias.clone()) + wasi_fs.create_inode(inodes, kind, true, alias.clone()) } else { - wasi_fs.create_inode(kind, true, path.to_string_lossy().into_owned()) + wasi_fs.create_inode(inodes, kind, true, path.to_string_lossy().into_owned()) } .map_err(|e| { format!( @@ -376,19 +544,22 @@ impl WasiFs { let fd = wasi_fs .create_fd(rights, rights, 0, fd_flags, inode) .map_err(|e| format!("Could not open fd for file {:?}: {}", path, e))?; - if let Kind::Root { entries } = &mut wasi_fs.inodes[root_inode].kind { - let key = if let Some(alias) = &alias { - alias.clone() - } else { - path.to_string_lossy().into_owned() - }; - let existing_entry = entries.insert(key.clone(), inode); - if existing_entry.is_some() { - return Err(format!("Found duplicate entry for alias `{}`", key)); + { + let mut guard = inodes.arena[root_inode].write(); + if let Kind::Root { entries } = guard.deref_mut() { + let key = if let Some(alias) = &alias { + alias.clone() + } else { + path.to_string_lossy().into_owned() + }; + let existing_entry = entries.insert(key.clone(), inode); + if existing_entry.is_some() { + return Err(format!("Found duplicate entry for alias `{}`", key)); + } + assert!(existing_entry.is_none()) } - assert!(existing_entry.is_none()) } - wasi_fs.preopen_fds.push(fd); + wasi_fs.preopen_fds.write().unwrap().push(fd); } Ok(wasi_fs) @@ -396,22 +567,24 @@ impl WasiFs { /// Private helper function to init the filesystem, called in `new` and /// `new_with_preopen` - fn new_init(fs_backing: Box) -> Result<(Self, Inode), String> { + fn new_init( + fs_backing: Box, + inodes: &mut WasiInodes, + ) -> Result<(Self, Inode), String> { debug!("Initializing WASI filesystem"); - let inodes = Arena::new(); - let mut wasi_fs = Self { - preopen_fds: vec![], + let wasi_fs = Self { + preopen_fds: RwLock::new(vec![]), name_map: HashMap::new(), - inodes, - fd_map: HashMap::new(), - next_fd: Cell::new(3), - inode_counter: Cell::new(1024), - orphan_fds: HashMap::new(), + fd_map: RwLock::new(HashMap::new()), + next_fd: AtomicU32::new(3), + inode_counter: AtomicU64::new(1024), + current_dir: Mutex::new("/".to_string()), + is_wasix: AtomicBool::new(false), fs_backing, }; - wasi_fs.create_stdin(); - wasi_fs.create_stdout(); - wasi_fs.create_stderr(); + wasi_fs.create_stdin(inodes); + wasi_fs.create_stdout(inodes); + wasi_fs.create_stderr(inodes); // create virtual root let root_inode = { @@ -433,83 +606,20 @@ impl WasiFs { & (!__WASI_RIGHT_PATH_SYMLINK) & (!__WASI_RIGHT_PATH_UNLINK_FILE) & (!__WASI_RIGHT_PATH_REMOVE_DIRECTORY)*/; - let inode = wasi_fs.create_virtual_root(); + let inode = wasi_fs.create_virtual_root(inodes); let fd = wasi_fs .create_fd(root_rights, root_rights, 0, Fd::READ, inode) .map_err(|e| format!("Could not create root fd: {}", e))?; - wasi_fs.preopen_fds.push(fd); + wasi_fs.preopen_fds.write().unwrap().push(fd); inode }; Ok((wasi_fs, root_inode)) } - /// Get the `VirtualFile` object at stdout - pub fn stdout(&self) -> Result<&Option>, FsError> { - self.std_dev_get(__WASI_STDOUT_FILENO) - } - /// Get the `VirtualFile` object at stdout mutably - pub fn stdout_mut(&mut self) -> Result<&mut Option>, FsError> { - self.std_dev_get_mut(__WASI_STDOUT_FILENO) - } - - /// Get the `VirtualFile` object at stderr - pub fn stderr(&self) -> Result<&Option>, FsError> { - self.std_dev_get(__WASI_STDERR_FILENO) - } - /// Get the `VirtualFile` object at stderr mutably - pub fn stderr_mut(&mut self) -> Result<&mut Option>, FsError> { - self.std_dev_get_mut(__WASI_STDERR_FILENO) - } - - /// Get the `VirtualFile` object at stdin - pub fn stdin(&self) -> Result<&Option>, FsError> { - self.std_dev_get(__WASI_STDIN_FILENO) - } - /// Get the `VirtualFile` object at stdin mutably - pub fn stdin_mut(&mut self) -> Result<&mut Option>, FsError> { - self.std_dev_get_mut(__WASI_STDIN_FILENO) - } - - /// Internal helper function to get a standard device handle. - /// Expects one of `__WASI_STDIN_FILENO`, `__WASI_STDOUT_FILENO`, `__WASI_STDERR_FILENO`. - fn std_dev_get(&self, fd: __wasi_fd_t) -> Result<&Option>, FsError> { - if let Some(fd) = self.fd_map.get(&fd) { - if let Kind::File { ref handle, .. } = self.inodes[fd.inode].kind { - Ok(handle) - } else { - // Our public API should ensure that this is not possible - unreachable!("Non-file found in standard device location") - } - } else { - // this should only trigger if we made a mistake in this crate - Err(FsError::NoDevice) - } - } - /// Internal helper function to mutably get a standard device handle. - /// Expects one of `__WASI_STDIN_FILENO`, `__WASI_STDOUT_FILENO`, `__WASI_STDERR_FILENO`. - fn std_dev_get_mut( - &mut self, - fd: __wasi_fd_t, - ) -> Result<&mut Option>, FsError> { - if let Some(fd) = self.fd_map.get_mut(&fd) { - if let Kind::File { ref mut handle, .. } = self.inodes[fd.inode].kind { - Ok(handle) - } else { - // Our public API should ensure that this is not possible - unreachable!("Non-file found in standard device location") - } - } else { - // this should only trigger if we made a mistake in this crate - Err(FsError::NoDevice) - } - } - /// Returns the next available inode index for creating a new inode. - fn get_next_inode_index(&mut self) -> u64 { - let next = self.inode_counter.get(); - self.inode_counter.set(next + 1); - next + fn get_next_inode_index(&self) -> u64 { + self.inode_counter.fetch_add(1, Ordering::AcqRel) } /// This function is like create dir all, but it also opens it. @@ -524,22 +634,23 @@ impl WasiFs { #[allow(dead_code)] pub unsafe fn open_dir_all( &mut self, + inodes: &mut WasiInodes, base: __wasi_fd_t, name: String, rights: __wasi_rights_t, rights_inheriting: __wasi_rights_t, flags: __wasi_fdflags_t, ) -> Result<__wasi_fd_t, FsError> { - let base_fd = self.get_fd(base).map_err(fs_error_from_wasi_err)?; // TODO: check permissions here? probably not, but this should be // an explicit choice, so justify it in a comment when we remove this one - let mut cur_inode = base_fd.inode; + let mut cur_inode = self.get_fd_inode(base).map_err(fs_error_from_wasi_err)?; let path: &Path = Path::new(&name); //let n_components = path.components().count(); for c in path.components() { let segment_name = c.as_os_str().to_string_lossy().to_string(); - match &self.inodes[cur_inode].kind { + let guard = inodes.arena[cur_inode].read(); + match guard.deref() { Kind::Dir { ref entries, .. } | Kind::Root { ref entries } => { if let Some(_entry) = entries.get(&segment_name) { // TODO: this should be fixed @@ -552,19 +663,27 @@ impl WasiFs { entries: HashMap::new(), }; - let inode = - self.create_inode_with_default_stat(kind, false, segment_name.clone()); + drop(guard); + let inode = self.create_inode_with_default_stat( + inodes, + kind, + false, + segment_name.clone(), + ); + // reborrow to insert - match &mut self.inodes[cur_inode].kind { - Kind::Dir { - ref mut entries, .. - } - | Kind::Root { ref mut entries } => { - entries.insert(segment_name, inode); + { + let mut guard = inodes.arena[cur_inode].write(); + match guard.deref_mut() { + Kind::Dir { + ref mut entries, .. + } + | Kind::Root { ref mut entries } => { + entries.insert(segment_name, inode); + } + _ => unreachable!("Dir or Root became not Dir or Root"), } - _ => unreachable!("Dir or Root became not Dir or Root"), } - cur_inode = inode; } _ => return Err(FsError::BaseNotDirectory), @@ -588,20 +707,21 @@ impl WasiFs { #[allow(dead_code)] pub fn open_file_at( &mut self, + inodes: &mut WasiInodes, base: __wasi_fd_t, - file: Box, + file: Box, open_flags: u16, name: String, rights: __wasi_rights_t, rights_inheriting: __wasi_rights_t, flags: __wasi_fdflags_t, ) -> Result<__wasi_fd_t, FsError> { - let base_fd = self.get_fd(base).map_err(fs_error_from_wasi_err)?; // TODO: check permissions here? probably not, but this should be // an explicit choice, so justify it in a comment when we remove this one - let base_inode = base_fd.inode; + let base_inode = self.get_fd_inode(base).map_err(fs_error_from_wasi_err)?; - match &self.inodes[base_inode].kind { + let guard = inodes.arena[base_inode].read(); + match guard.deref() { Kind::Dir { ref entries, .. } | Kind::Root { ref entries } => { if let Some(_entry) = entries.get(&name) { // TODO: eventually change the logic here to allow overwrites @@ -611,21 +731,25 @@ impl WasiFs { let kind = Kind::File { handle: Some(file), path: PathBuf::from(""), - fd: Some(self.next_fd.get()), + fd: Some(self.next_fd.load(Ordering::Acquire)), }; + drop(guard); let inode = self - .create_inode(kind, false, name.clone()) + .create_inode(inodes, kind, false, name.clone()) .map_err(|_| FsError::IOError)?; - // reborrow to insert - match &mut self.inodes[base_inode].kind { - Kind::Dir { - ref mut entries, .. - } - | Kind::Root { ref mut entries } => { - entries.insert(name, inode); + + { + let mut guard = inodes.arena[base_inode].write(); + match guard.deref_mut() { + Kind::Dir { + ref mut entries, .. + } + | Kind::Root { ref mut entries } => { + entries.insert(name, inode); + } + _ => unreachable!("Dir or Root became not Dir or Root"), } - _ => unreachable!("Dir or Root became not Dir or Root"), } self.create_fd(rights, rights_inheriting, flags, open_flags, inode) @@ -640,26 +764,29 @@ impl WasiFs { /// TODO: add examples #[allow(dead_code)] pub fn swap_file( - &mut self, + &self, + inodes: &WasiInodes, fd: __wasi_fd_t, - file: Box, - ) -> Result>, FsError> { + file: Box, + ) -> Result>, FsError> { let mut ret = Some(file); match fd { __WASI_STDIN_FILENO => { - std::mem::swap(self.stdin_mut()?, &mut ret); + let mut target = inodes.stdin_mut(&self.fd_map)?; + std::mem::swap(target.deref_mut(), &mut ret); } __WASI_STDOUT_FILENO => { - std::mem::swap(self.stdout_mut()?, &mut ret); + let mut target = inodes.stdout_mut(&self.fd_map)?; + std::mem::swap(target.deref_mut(), &mut ret); } __WASI_STDERR_FILENO => { - std::mem::swap(self.stderr_mut()?, &mut ret); + let mut target = inodes.stderr_mut(&self.fd_map)?; + std::mem::swap(target.deref_mut(), &mut ret); } _ => { - let base_fd = self.get_fd(fd).map_err(fs_error_from_wasi_err)?; - let base_inode = base_fd.inode; - - match &mut self.inodes[base_inode].kind { + let base_inode = self.get_fd_inode(fd).map_err(fs_error_from_wasi_err)?; + let mut guard = inodes.arena[base_inode].write(); + match guard.deref_mut() { Kind::File { ref mut handle, .. } => { std::mem::swap(handle, &mut ret); } @@ -673,15 +800,19 @@ impl WasiFs { /// refresh size from filesystem pub(crate) fn filestat_resync_size( - &mut self, + &self, + inodes: &WasiInodes, fd: __wasi_fd_t, ) -> Result<__wasi_filesize_t, __wasi_errno_t> { - let fd = self.fd_map.get_mut(&fd).ok_or(__WASI_EBADF)?; - match &mut self.inodes[fd.inode].kind { + let inode = self.get_fd_inode(fd)?; + let mut guard = inodes.arena[inode].write(); + match guard.deref_mut() { Kind::File { handle, .. } => { if let Some(h) = handle { let new_size = h.size(); - self.inodes[fd.inode].stat.st_size = new_size; + drop(guard); + + inodes.arena[inode].stat.write().unwrap().st_size = new_size; Ok(new_size as __wasi_filesize_t) } else { Err(__WASI_EBADF) @@ -692,6 +823,42 @@ impl WasiFs { } } + /// Changes the current directory + pub fn set_current_dir(&self, path: &str) { + let mut guard = self.current_dir.lock().unwrap(); + *guard = path.to_string(); + } + + /// Gets the current directory + pub fn get_current_dir( + &self, + inodes: &mut WasiInodes, + base: __wasi_fd_t, + ) -> Result<(Inode, String), __wasi_errno_t> { + self.get_current_dir_inner(inodes, base, 0) + } + + pub(crate) fn get_current_dir_inner( + &self, + inodes: &mut WasiInodes, + base: __wasi_fd_t, + symlink_count: u32, + ) -> Result<(Inode, String), __wasi_errno_t> { + let current_dir = { + let guard = self.current_dir.lock().unwrap(); + guard.clone() + }; + let cur_inode = self.get_fd_inode(base)?; + let inode = self.get_inode_at_path_inner( + inodes, + cur_inode, + current_dir.as_str(), + symlink_count, + true, + )?; + Ok((inode, current_dir)) + } + /// Internal part of the core path resolution function which implements path /// traversal logic such as resolving relative path segments (such as /// `.` and `..`) and resolving symlinks (while preventing infinite @@ -706,8 +873,9 @@ impl WasiFs { /// /// TODO: write more tests for this code fn get_inode_at_path_inner( - &mut self, - base: __wasi_fd_t, + &self, + inodes: &mut WasiInodes, + mut cur_inode: generational_arena::Index, path: &str, mut symlink_count: u32, follow_symlinks: bool, @@ -716,11 +884,9 @@ impl WasiFs { return Err(__WASI_EMLINK); } - let base_dir = self.get_fd(base)?; let path: &Path = Path::new(path); - - let mut cur_inode = base_dir.inode; let n_components = path.components().count(); + // TODO: rights checks 'path_iter: for (i, component) in path.components().enumerate() { // used to terminate symlink resolution properly @@ -728,7 +894,8 @@ impl WasiFs { // for each component traverse file structure // loading inodes as necessary 'symlink_resolution: while symlink_count < MAX_SYMLINKS { - match &mut self.inodes[cur_inode].kind { + let mut guard = inodes.arena[cur_inode].write(); + match guard.deref_mut() { Kind::Buffer { .. } => unimplemented!("state::get_inode_at_path for buffers"), Kind::Dir { ref mut entries, @@ -788,11 +955,11 @@ impl WasiFs { } } else if file_type.is_symlink() { should_insert = false; - let link_value = file.read_link().ok().ok_or(__WASI_EIO)?; + let link_value = file.read_link().map_err(map_io_err)?; debug!("attempting to decompose path {:?}", link_value); let (pre_open_dir_fd, relative_path) = if link_value.is_relative() { - self.path_into_pre_open_and_relative_path(&file)? + self.path_into_pre_open_and_relative_path(inodes, &file)? } else { unimplemented!("Absolute symlinks are not yet supported"); }; @@ -828,7 +995,9 @@ impl WasiFs { path: file.clone(), fd: None, }; + drop(guard); let new_inode = self.create_inode_with_stat( + inodes, kind, false, file.to_string_lossy().to_string(), @@ -837,9 +1006,11 @@ impl WasiFs { ..__wasi_filestat_t::default() }, ); + + let mut guard = inodes.arena[cur_inode].write(); if let Kind::Dir { ref mut entries, .. - } = &mut self.inodes[cur_inode].kind + } = guard.deref_mut() { entries.insert( component.as_os_str().to_string_lossy().to_string(), @@ -857,12 +1028,18 @@ impl WasiFs { unimplemented!("state::get_inode_at_path unknown file type: not file, directory, or symlink"); }; - let new_inode = - self.create_inode(kind, false, file.to_string_lossy().to_string())?; + drop(guard); + let new_inode = self.create_inode( + inodes, + kind, + false, + file.to_string_lossy().to_string(), + )?; if should_insert { + let mut guard = inodes.arena[cur_inode].write(); if let Kind::Dir { ref mut entries, .. - } = &mut self.inodes[cur_inode].kind + } = guard.deref_mut() { entries.insert( component.as_os_str().to_string_lossy().to_string(), @@ -895,7 +1072,10 @@ impl WasiFs { return Err(__WASI_ENOENT); } } - Kind::File { .. } => { + Kind::File { .. } + | Kind::Socket { .. } + | Kind::Pipe { .. } + | Kind::EventNotifications { .. } => { return Err(__WASI_ENOTDIR); } Kind::Symlink { @@ -904,6 +1084,8 @@ impl WasiFs { relative_path, } => { let new_base_dir = *base_po_dir; + let new_base_inode = self.get_fd_inode(new_base_dir)?; + // allocate to reborrow mutabily to recur let new_path = { /*if let Kind::Root { .. } = self.inodes[base_po_dir].kind { @@ -917,8 +1099,10 @@ impl WasiFs { base.to_string_lossy().to_string() }; debug!("Following symlink recursively"); + drop(guard); let symlink_inode = self.get_inode_at_path_inner( - new_base_dir, + inodes, + new_base_inode, &new_path, symlink_count + 1, follow_symlinks, @@ -926,7 +1110,8 @@ impl WasiFs { cur_inode = symlink_inode; // if we're at the very end and we found a file, then we're done // TODO: figure out if this should also happen for directories? - if let Kind::File { .. } = &self.inodes[cur_inode].kind { + let guard = inodes.arena[cur_inode].read(); + if let Kind::File { .. } = guard.deref() { // check if on last step if last_component { break 'symlink_resolution; @@ -953,6 +1138,7 @@ impl WasiFs { /// In the case of a tie, the later preopened fd is preferred. fn path_into_pre_open_and_relative_path<'path>( &self, + inodes: &WasiInodes, path: &'path Path, ) -> Result<(__wasi_fd_t, &'path Path), __wasi_errno_t> { enum BaseFdAndRelPath<'a> { @@ -974,9 +1160,11 @@ impl WasiFs { } let mut res = BaseFdAndRelPath::None; // for each preopened directory - for po_fd in &self.preopen_fds { - let po_inode = self.fd_map[po_fd].inode; - let po_path = match &self.inodes[po_inode].kind { + let preopen_fds = self.preopen_fds.read().unwrap(); + for po_fd in preopen_fds.deref() { + let po_inode = self.fd_map.read().unwrap()[po_fd].inode; + let guard = inodes.arena[po_inode].read(); + let po_path = match guard.deref() { Kind::Dir { path, .. } => &**path, Kind::Root { .. } => Path::new("/"), _ => unreachable!("Preopened FD that's not a directory or the root"), @@ -1007,17 +1195,18 @@ impl WasiFs { /// expects inode to point to a directory pub(crate) fn path_depth_from_fd( &self, + inodes: &WasiInodes, fd: __wasi_fd_t, inode: Inode, ) -> Result { let mut counter = 0; - let base_fd = self.get_fd(fd)?; - let base_inode = base_fd.inode; + let base_inode = self.get_fd_inode(fd)?; let mut cur_inode = inode; while cur_inode != base_inode { counter += 1; - match &self.inodes[cur_inode].kind { + let guard = inodes.arena[cur_inode].read(); + match guard.deref() { Kind::Dir { parent, .. } => { if let Some(p) = parent { cur_inode = *p; @@ -1037,18 +1226,27 @@ impl WasiFs { // symlink so // This will be resolved when we have tests asserting the correct behavior pub(crate) fn get_inode_at_path( - &mut self, + &self, + inodes: &mut WasiInodes, base: __wasi_fd_t, path: &str, follow_symlinks: bool, ) -> Result { - self.get_inode_at_path_inner(base, path, 0, follow_symlinks) + let start_inode = if !path.starts_with('/') && self.is_wasix.load(Ordering::Acquire) { + let (cur_inode, _) = self.get_current_dir(inodes, base)?; + cur_inode + } else { + self.get_fd_inode(base)? + }; + + self.get_inode_at_path_inner(inodes, start_inode, path, 0, follow_symlinks) } /// Returns the parent Dir or Root that the file at a given path is in and the file name /// stripped off pub(crate) fn get_parent_inode_at_path( - &mut self, + &self, + inodes: &mut WasiInodes, base: __wasi_fd_t, path: &Path, follow_symlinks: bool, @@ -1064,31 +1262,45 @@ impl WasiFs { for comp in components.rev() { parent_dir.push(comp); } - self.get_inode_at_path(base, &parent_dir.to_string_lossy(), follow_symlinks) + self.get_inode_at_path(inodes, base, &parent_dir.to_string_lossy(), follow_symlinks) .map(|v| (v, new_entity_name)) } - pub fn get_fd(&self, fd: __wasi_fd_t) -> Result<&Fd, __wasi_errno_t> { - self.fd_map.get(&fd).ok_or(__WASI_EBADF) + pub fn get_fd(&self, fd: __wasi_fd_t) -> Result { + self.fd_map + .read() + .unwrap() + .get(&fd) + .ok_or(__WASI_EBADF) + .map(|a| a.clone()) } - /// gets either a normal inode or an orphaned inode - pub fn get_inodeval_mut(&mut self, fd: __wasi_fd_t) -> Result<&mut InodeVal, __wasi_errno_t> { - let inode = self.get_fd(fd)?.inode; - if let Some(iv) = self.inodes.get_mut(inode) { - Ok(iv) - } else { - self.orphan_fds.get_mut(&inode).ok_or(__WASI_EBADF) - } + pub fn get_fd_inode( + &self, + fd: __wasi_fd_t, + ) -> Result { + self.fd_map + .read() + .unwrap() + .get(&fd) + .ok_or(__WASI_EBADF) + .map(|a| a.inode) } - pub fn filestat_fd(&self, fd: __wasi_fd_t) -> Result<__wasi_filestat_t, __wasi_errno_t> { - let fd = self.get_fd(fd)?; - - Ok(self.inodes[fd.inode].stat) + pub fn filestat_fd( + &self, + inodes: &WasiInodes, + fd: __wasi_fd_t, + ) -> Result<__wasi_filestat_t, __wasi_errno_t> { + let inode = self.get_fd_inode(fd)?; + Ok(*inodes.arena[inode].stat.read().unwrap().deref()) } - pub fn fdstat(&self, fd: __wasi_fd_t) -> Result<__wasi_fdstat_t, __wasi_errno_t> { + pub fn fdstat( + &self, + inodes: &WasiInodes, + fd: __wasi_fd_t, + ) -> Result<__wasi_fdstat_t, __wasi_errno_t> { match fd { __WASI_STDIN_FILENO => { return Ok(__wasi_fdstat_t { @@ -1126,11 +1338,11 @@ impl WasiFs { _ => (), } let fd = self.get_fd(fd)?; - debug!("fdstat: {:?}", fd); + let guard = inodes.arena[fd.inode].read(); Ok(__wasi_fdstat_t { - fs_filetype: match self.inodes[fd.inode].kind { + fs_filetype: match guard.deref() { Kind::File { .. } => __WASI_FILETYPE_REGULAR_FILE, Kind::Dir { .. } => __WASI_FILETYPE_DIRECTORY, Kind::Symlink { .. } => __WASI_FILETYPE_SYMBOLIC_LINK, @@ -1142,56 +1354,60 @@ impl WasiFs { }) } - pub fn prestat_fd(&self, fd: __wasi_fd_t) -> Result<__wasi_prestat_t, __wasi_errno_t> { - let fd = self.fd_map.get(&fd).ok_or(__WASI_EBADF)?; + pub fn prestat_fd( + &self, + inodes: &WasiInodes, + fd: __wasi_fd_t, + ) -> Result<__wasi_prestat_t, __wasi_errno_t> { + let inode = self.get_fd_inode(fd)?; + trace!("in prestat_fd {:?}", self.get_fd(fd)?); - debug!("in prestat_fd {:?}", fd); - let inode_val = &self.inodes[fd.inode]; + let inode_val = &inodes.arena[inode]; if inode_val.is_preopened { - Ok(__wasi_prestat_t { - pr_type: __WASI_PREOPENTYPE_DIR, - u: PrestatEnum::Dir { - // REVIEW: - pr_name_len: inode_val.name.len() as u32 + 1, - } - .untagged(), - }) + Ok(self.prestat_fd_inner(inode_val)) } else { Err(__WASI_EBADF) } } - pub fn flush(&mut self, fd: __wasi_fd_t) -> Result<(), __wasi_errno_t> { + pub(crate) fn prestat_fd_inner(&self, inode_val: &InodeVal) -> __wasi_prestat_t { + __wasi_prestat_t { + pr_type: __WASI_PREOPENTYPE_DIR, + u: PrestatEnum::Dir { + // REVIEW: + pr_name_len: inode_val.name.len() as u32 + 1, + } + .untagged(), + } + } + + pub fn flush(&self, inodes: &WasiInodes, fd: __wasi_fd_t) -> Result<(), __wasi_errno_t> { match fd { __WASI_STDIN_FILENO => (), - __WASI_STDOUT_FILENO => self - .stdout_mut() + __WASI_STDOUT_FILENO => inodes + .stdout_mut(&self.fd_map) .map_err(fs_error_into_wasi_err)? .as_mut() - .and_then(|f| f.flush().ok()) - .ok_or(__WASI_EIO)?, - __WASI_STDERR_FILENO => self - .stderr_mut() + .map(|f| f.flush().map_err(map_io_err)) + .unwrap_or_else(|| Err(__WASI_EIO))?, + __WASI_STDERR_FILENO => inodes + .stderr_mut(&self.fd_map) .map_err(fs_error_into_wasi_err)? .as_mut() .and_then(|f| f.flush().ok()) .ok_or(__WASI_EIO)?, _ => { - let fd = self.fd_map.get(&fd).ok_or(__WASI_EBADF)?; + let fd = self.get_fd(fd)?; if fd.rights & __WASI_RIGHT_FD_DATASYNC == 0 { return Err(__WASI_EACCES); } - let inode = &mut self.inodes[fd.inode]; - - match &mut inode.kind { + let mut guard = inodes.arena[fd.inode].write(); + match guard.deref_mut() { Kind::File { handle: Some(file), .. } => file.flush().map_err(|_| __WASI_EIO)?, - Kind::File { handle: None, .. } => { - return Err(__WASI_EIO); - } // TODO: verify this behavior Kind::Dir { .. } => return Err(__WASI_EISDIR), Kind::Symlink { .. } => unimplemented!("WasiFs::flush Kind::Symlink"), @@ -1205,29 +1421,32 @@ impl WasiFs { /// Creates an inode and inserts it given a Kind and some extra data pub(crate) fn create_inode( - &mut self, + &self, + inodes: &mut WasiInodes, kind: Kind, is_preopened: bool, name: String, ) -> Result { - let stat = self.get_stat_for_kind(&kind).ok_or(__WASI_EIO)?; - Ok(self.create_inode_with_stat(kind, is_preopened, name, stat)) + let stat = self.get_stat_for_kind(inodes, &kind)?; + Ok(self.create_inode_with_stat(inodes, kind, is_preopened, name, stat)) } /// Creates an inode and inserts it given a Kind, does not assume the file exists. pub(crate) fn create_inode_with_default_stat( - &mut self, + &self, + inodes: &mut WasiInodes, kind: Kind, is_preopened: bool, name: String, ) -> Inode { let stat = __wasi_filestat_t::default(); - self.create_inode_with_stat(kind, is_preopened, name, stat) + self.create_inode_with_stat(inodes, kind, is_preopened, name, stat) } /// Creates an inode with the given filestat and inserts it. pub(crate) fn create_inode_with_stat( - &mut self, + &self, + inodes: &mut WasiInodes, kind: Kind, is_preopened: bool, name: String, @@ -1235,25 +1454,24 @@ impl WasiFs { ) -> Inode { stat.st_ino = self.get_next_inode_index(); - self.inodes.insert(InodeVal { - stat, + inodes.arena.insert(InodeVal { + stat: RwLock::new(stat), is_preopened, name, - kind, + kind: RwLock::new(kind), }) } pub fn create_fd( - &mut self, + &self, rights: __wasi_rights_t, rights_inheriting: __wasi_rights_t, flags: __wasi_fdflags_t, open_flags: u16, inode: Inode, ) -> Result<__wasi_fd_t, __wasi_errno_t> { - let idx = self.next_fd.get(); - self.next_fd.set(idx + 1); - self.fd_map.insert( + let idx = self.next_fd.fetch_add(1, Ordering::AcqRel); + self.fd_map.write().unwrap().insert( idx, Fd { rights, @@ -1267,6 +1485,23 @@ impl WasiFs { Ok(idx) } + pub fn clone_fd(&self, fd: __wasi_fd_t) -> Result<__wasi_fd_t, __wasi_errno_t> { + let fd = self.get_fd(fd)?; + let idx = self.next_fd.fetch_add(1, Ordering::AcqRel); + self.fd_map.write().unwrap().insert( + idx, + Fd { + rights: fd.rights, + rights_inheriting: fd.rights_inheriting, + flags: fd.flags, + offset: fd.offset, + open_flags: fd.open_flags, + inode: fd.inode, + }, + ); + Ok(idx) + } + /// Low level function to remove an inode, that is it deletes the WASI FS's /// knowledge of a file. /// @@ -1275,11 +1510,11 @@ impl WasiFs { /// # Safety /// - The caller must ensure that all references to the specified inode have /// been removed from the filesystem. - pub unsafe fn remove_inode(&mut self, inode: Inode) -> Option { - self.inodes.remove(inode) + pub unsafe fn remove_inode(&self, inodes: &mut WasiInodes, inode: Inode) -> Option { + inodes.arena.remove(inode) } - fn create_virtual_root(&mut self) -> Inode { + fn create_virtual_root(&self, inodes: &mut WasiInodes) -> Inode { let stat = __wasi_filestat_t { st_filetype: __WASI_FILETYPE_DIRECTORY, st_ino: self.get_next_inode_index(), @@ -1289,16 +1524,17 @@ impl WasiFs { entries: HashMap::new(), }; - self.inodes.insert(InodeVal { - stat, + inodes.arena.insert(InodeVal { + stat: RwLock::new(stat), is_preopened: true, name: "/".to_string(), - kind: root_kind, + kind: RwLock::new(root_kind), }) } - fn create_stdout(&mut self) { + fn create_stdout(&self, inodes: &mut WasiInodes) { self.create_std_dev_inner( + inodes, Box::new(Stdout::default()), "stdout", __WASI_STDOUT_FILENO, @@ -1306,8 +1542,9 @@ impl WasiFs { __WASI_FDFLAG_APPEND, ); } - fn create_stdin(&mut self) { + fn create_stdin(&self, inodes: &mut WasiInodes) { self.create_std_dev_inner( + inodes, Box::new(Stdin::default()), "stdin", __WASI_STDIN_FILENO, @@ -1315,8 +1552,9 @@ impl WasiFs { 0, ); } - fn create_stderr(&mut self) { + fn create_stderr(&self, inodes: &mut WasiInodes) { self.create_std_dev_inner( + inodes, Box::new(Stderr::default()), "stderr", __WASI_STDERR_FILENO, @@ -1326,8 +1564,9 @@ impl WasiFs { } fn create_std_dev_inner( - &mut self, - handle: Box, + &self, + inodes: &mut WasiInodes, + handle: Box, name: &'static str, raw_fd: __wasi_fd_t, rights: __wasi_rights_t, @@ -1343,13 +1582,15 @@ impl WasiFs { handle: Some(handle), path: "".into(), }; - let inode = self.inodes.insert(InodeVal { - stat, - is_preopened: true, - name: name.to_string(), - kind, - }); - self.fd_map.insert( + let inode = { + inodes.arena.insert(InodeVal { + stat: RwLock::new(stat), + is_preopened: true, + name: name.to_string(), + kind: RwLock::new(kind), + }) + }; + self.fd_map.write().unwrap().insert( raw_fd, Fd { rights, @@ -1363,11 +1604,15 @@ impl WasiFs { ); } - pub fn get_stat_for_kind(&self, kind: &Kind) -> Option<__wasi_filestat_t> { + pub fn get_stat_for_kind( + &self, + inodes: &WasiInodes, + kind: &Kind, + ) -> Result<__wasi_filestat_t, __wasi_errno_t> { let md = match kind { Kind::File { handle, path, .. } => match handle { Some(wf) => { - return Some(__wasi_filestat_t { + return Ok(__wasi_filestat_t { st_filetype: __WASI_FILETYPE_REGULAR_FILE, st_size: wf.size(), st_atim: wf.last_accessed(), @@ -1377,19 +1622,26 @@ impl WasiFs { ..__wasi_filestat_t::default() }) } - None => self.fs_backing.metadata(path).ok()?, + None => self + .fs_backing + .metadata(path) + .map_err(fs_error_into_wasi_err)?, }, - Kind::Dir { path, .. } => self.fs_backing.metadata(path).ok()?, + Kind::Dir { path, .. } => self + .fs_backing + .metadata(path) + .map_err(fs_error_into_wasi_err)?, Kind::Symlink { base_po_dir, path_to_symlink, .. } => { - let base_po_inode = &self.fd_map[base_po_dir].inode; - let base_po_inode_v = &self.inodes[*base_po_inode]; - match &base_po_inode_v.kind { + let base_po_inode = &self.fd_map.read().unwrap()[base_po_dir].inode; + let base_po_inode_v = &inodes.arena[*base_po_inode]; + let guard = base_po_inode_v.read(); + match guard.deref() { Kind::Root { .. } => { - self.fs_backing.symlink_metadata(path_to_symlink).ok()? + self.fs_backing.symlink_metadata(path_to_symlink).map_err(fs_error_into_wasi_err)? } Kind::Dir { path, .. } => { let mut real_path = path.clone(); @@ -1400,15 +1652,15 @@ impl WasiFs { // TODO: adjust size of symlink, too // for all paths adjusted think about this real_path.push(path_to_symlink); - self.fs_backing.symlink_metadata(&real_path).ok()? + self.fs_backing.symlink_metadata(&real_path).map_err(fs_error_into_wasi_err)? } // if this triggers, there's a bug in the symlink code _ => unreachable!("Symlink pointing to something that's not a directory as its base preopened directory"), } } - _ => return None, + _ => return Err(__WASI_EIO), }; - Some(__wasi_filestat_t { + Ok(__wasi_filestat_t { st_filetype: virtual_file_type_to_wasi_file_type(md.file_type()), st_size: md.len(), st_atim: md.accessed(), @@ -1419,15 +1671,28 @@ impl WasiFs { } /// Closes an open FD, handling all details such as FD being preopen - pub(crate) fn close_fd(&mut self, fd: __wasi_fd_t) -> Result<(), __wasi_errno_t> { - let inodeval_mut = self.get_inodeval_mut(fd)?; - let is_preopened = inodeval_mut.is_preopened; + pub(crate) fn close_fd( + &self, + inodes: &WasiInodes, + fd: __wasi_fd_t, + ) -> Result<(), __wasi_errno_t> { + let inode = self.get_fd_inode(fd)?; + let inodeval = inodes.get_inodeval(inode)?; + let is_preopened = inodeval.is_preopened; - match &mut inodeval_mut.kind { + let mut guard = inodeval.write(); + match guard.deref_mut() { Kind::File { ref mut handle, .. } => { let mut empty_handle = None; std::mem::swap(handle, &mut empty_handle); } + Kind::Socket { ref mut socket, .. } => { + let mut closed_socket = InodeSocket::new(InodeSocketKind::Closed); + std::mem::swap(socket, &mut closed_socket); + } + Kind::Pipe { ref mut pipe } => { + pipe.close(); + } Kind::Dir { parent, path, .. } => { debug!("Closing dir {:?}", &path); let key = path @@ -1436,22 +1701,27 @@ impl WasiFs { .to_string_lossy() .to_string(); if let Some(p) = *parent { - match &mut self.inodes[p].kind { + drop(guard); + let mut guard = inodes.arena[p].write(); + match guard.deref_mut() { Kind::Dir { entries, .. } | Kind::Root { entries } => { - self.fd_map.remove(&fd).unwrap(); + self.fd_map.write().unwrap().remove(&fd).unwrap(); if is_preopened { let mut idx = None; - for (i, po_fd) in self.preopen_fds.iter().enumerate() { - if *po_fd == fd { - idx = Some(i); - break; + { + let preopen_fds = self.preopen_fds.read().unwrap(); + for (i, po_fd) in preopen_fds.iter().enumerate() { + if *po_fd == fd { + idx = Some(i); + break; + } } } if let Some(i) = idx { // only remove entry properly if this is the original preopen FD // calling `path_open` can give you an fd to the same inode as a preopen fd entries.remove(&key); - self.preopen_fds.remove(i); + self.preopen_fds.write().unwrap().remove(i); // Maybe recursively closes fds if original preopen? } } @@ -1466,6 +1736,7 @@ impl WasiFs { return Err(__WASI_EINVAL); } } + Kind::EventNotifications { .. } => {} Kind::Root { .. } => return Err(__WASI_EACCES), Kind::Symlink { .. } | Kind::Buffer { .. } => return Err(__WASI_EINVAL), } @@ -1523,6 +1794,20 @@ impl WasiState { } } +/// Structures used for the threading and sub-processes +/// +/// These internal implementation details are hidden away from the +/// consumer who should instead implement the vbus trait on the runtime +#[derive(Debug, Default)] +#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +pub(crate) struct WasiStateThreading { + pub threads: HashMap, + pub thread_seed: u32, + pub processes: HashMap, + pub process_reuse: HashMap, WasiBusProcessId>, + pub process_seed: u32, +} + /// Top level data type containing all* the state with which WASI can /// interact. /// @@ -1555,6 +1840,8 @@ impl WasiState { #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] pub struct WasiState { pub fs: WasiFs, + pub inodes: Arc>, + pub(crate) threading: Mutex, pub args: Vec>, pub envs: Vec>, } @@ -1578,6 +1865,65 @@ impl WasiState { pub fn unfreeze(bytes: &[u8]) -> Option { bincode::deserialize(bytes).ok() } + + /// Get the `VirtualFile` object at stdout + pub fn stdout(&self) -> Result>, FsError> { + self.std_dev_get(__WASI_STDOUT_FILENO) + } + + #[deprecated( + since = "3.0.0", + note = "stdout_mut() is no longer needed - just use stdout() instead" + )] + pub fn stdout_mut( + &self, + ) -> Result>, FsError> { + self.stdout() + } + + /// Get the `VirtualFile` object at stderr + pub fn stderr(&self) -> Result>, FsError> { + self.std_dev_get(__WASI_STDERR_FILENO) + } + + #[deprecated( + since = "3.0.0", + note = "stderr_mut() is no longer needed - just use stderr() instead" + )] + pub fn stderr_mut( + &self, + ) -> Result>, FsError> { + self.stderr() + } + + /// Get the `VirtualFile` object at stdin + pub fn stdin(&self) -> Result>, FsError> { + self.std_dev_get(__WASI_STDIN_FILENO) + } + + #[deprecated( + since = "3.0.0", + note = "stdin_mut() is no longer needed - just use stdin() instead" + )] + pub fn stdin_mut( + &self, + ) -> Result>, FsError> { + self.stdin() + } + + /// Internal helper function to get a standard device handle. + /// Expects one of `__WASI_STDIN_FILENO`, `__WASI_STDOUT_FILENO`, `__WASI_STDERR_FILENO`. + fn std_dev_get( + &self, + fd: __wasi_fd_t, + ) -> Result>, FsError> { + let ret = WasiStateFileGuard::new(self, fd)?.map(|a| { + let ret = Box::new(a); + let ret: Box = ret; + ret + }); + Ok(ret) + } } pub fn virtual_file_type_to_wasi_file_type(file_type: wasmer_vfs::FileType) -> __wasi_filetype_t { diff --git a/lib/wasi/src/state/pipe.rs b/lib/wasi/src/state/pipe.rs new file mode 100644 index 00000000000..b49e4040812 --- /dev/null +++ b/lib/wasi/src/state/pipe.rs @@ -0,0 +1,118 @@ +use crate::syscalls::types::*; +use crate::syscalls::{read_bytes, write_bytes}; +use bytes::{Buf, Bytes}; +use std::convert::TryInto; +use std::io::{self, Read}; +use std::ops::DerefMut; +use std::sync::mpsc; +use std::sync::Mutex; +use wasmer::MemorySize; +use wasmer::{Memory, WasmSlice}; + +#[derive(Debug)] +pub struct WasiPipe { + /// Sends bytes down the pipe + tx: Mutex>>, + /// Receives bytes from the pipe + rx: Mutex>>, + /// Buffers the last read message from the pipe while its being consumed + read_buffer: Option, +} + +impl WasiPipe { + pub fn new() -> (WasiPipe, WasiPipe) { + let (tx1, rx1) = mpsc::channel(); + let (tx2, rx2) = mpsc::channel(); + + let pipe1 = WasiPipe { + tx: Mutex::new(tx1), + rx: Mutex::new(rx2), + read_buffer: None, + }; + + let pipe2 = WasiPipe { + tx: Mutex::new(tx2), + rx: Mutex::new(rx1), + read_buffer: None, + }; + + (pipe1, pipe2) + } + + pub fn recv( + &mut self, + memory: &Memory, + iov: WasmSlice<__wasi_iovec_t>, + ) -> Result { + loop { + if let Some(buf) = self.read_buffer.as_mut() { + let buf_len = buf.len(); + if buf_len > 0 { + let reader = buf.as_ref(); + let read = read_bytes(reader, memory, iov).map(|_| buf_len as usize)?; + buf.advance(read); + return Ok(read); + } + } + let rx = self.rx.lock().unwrap(); + let data = rx.recv().map_err(|_| __WASI_EIO)?; + self.read_buffer.replace(Bytes::from(data)); + } + } + + pub fn send( + &mut self, + memory: &Memory, + iov: WasmSlice<__wasi_ciovec_t>, + ) -> Result { + let buf_len: M::Offset = iov + .iter() + .filter_map(|a| a.read().ok()) + .map(|a| a.buf_len) + .sum(); + let buf_len: usize = buf_len.try_into().map_err(|_| __WASI_EINVAL)?; + let mut buf = Vec::with_capacity(buf_len); + write_bytes(&mut buf, memory, iov)?; + let tx = self.tx.lock().unwrap(); + tx.send(buf).map_err(|_| __WASI_EIO)?; + Ok(buf_len) + } + + pub fn close(&mut self) { + let (mut null_tx, _) = mpsc::channel(); + let (_, mut null_rx) = mpsc::channel(); + { + let mut guard = self.rx.lock().unwrap(); + std::mem::swap(guard.deref_mut(), &mut null_rx); + } + { + let mut guard = self.tx.lock().unwrap(); + std::mem::swap(guard.deref_mut(), &mut null_tx); + } + self.read_buffer.take(); + } +} + +impl Read for WasiPipe { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + loop { + if let Some(inner_buf) = self.read_buffer.as_mut() { + let buf_len = inner_buf.len(); + if buf_len > 0 { + let mut reader = inner_buf.as_ref(); + let read = reader.read(buf).map(|_| buf_len as usize)?; + inner_buf.advance(read); + return Ok(read); + } + } + let rx = self.rx.lock().unwrap(); + let data = rx.recv().map_err(|_| { + io::Error::new( + io::ErrorKind::BrokenPipe, + "the wasi pipe is not connected".to_string(), + ) + })?; + self.read_buffer.replace(Bytes::from(data)); + } + } +} diff --git a/lib/wasi/src/state/socket.rs b/lib/wasi/src/state/socket.rs new file mode 100644 index 00000000000..290b154b766 --- /dev/null +++ b/lib/wasi/src/state/socket.rs @@ -0,0 +1,1496 @@ +use super::types::net_error_into_wasi_err; +use crate::syscalls::types::*; +use crate::syscalls::{read_bytes, write_bytes}; +use bytes::{Buf, Bytes}; +use std::convert::TryInto; +use std::io::{self, Read}; +use std::mem::transmute; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; +use std::sync::Mutex; +use std::time::Duration; +#[allow(unused_imports)] +use tracing::{debug, error, info, warn}; +use wasmer::{Memory, MemorySize, WasmPtr, WasmSlice}; +use wasmer_vnet::{net_error_into_io_err, TimeType}; +use wasmer_vnet::{ + IpCidr, IpRoute, SocketHttpRequest, VirtualIcmpSocket, VirtualNetworking, VirtualRawSocket, + VirtualTcpListener, VirtualTcpSocket, VirtualUdpSocket, VirtualWebSocket, +}; + +#[cfg(feature = "enable-serde")] +use serde::{Deserialize, Serialize}; + +#[derive(Debug)] +#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +pub enum InodeHttpSocketType { + /// Used to feed the bytes into the request itself + Request, + /// Used to receive the bytes from the HTTP server + Response, + /// Used to read the headers from the HTTP server + Headers, +} + +#[derive(Debug)] +#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +pub enum InodeSocketKind { + PreSocket { + family: __wasi_addressfamily_t, + ty: __wasi_socktype_t, + pt: __wasi_sockproto_t, + addr: Option, + only_v6: bool, + reuse_port: bool, + reuse_addr: bool, + send_buf_size: Option, + recv_buf_size: Option, + send_timeout: Option, + recv_timeout: Option, + connect_timeout: Option, + accept_timeout: Option, + }, + HttpRequest(Mutex, InodeHttpSocketType), + WebSocket(Box), + Icmp(Box), + Raw(Box), + TcpListener(Box), + TcpStream(Box), + UdpSocket(Box), + Closed, +} + +pub enum WasiSocketOption { + Noop, + ReusePort, + ReuseAddr, + NoDelay, + DontRoute, + OnlyV6, + Broadcast, + MulticastLoopV4, + MulticastLoopV6, + Promiscuous, + Listening, + LastError, + KeepAlive, + Linger, + OobInline, + RecvBufSize, + SendBufSize, + RecvLowat, + SendLowat, + RecvTimeout, + SendTimeout, + ConnectTimeout, + AcceptTimeout, + Ttl, + MulticastTtlV4, + Type, + Proto, +} + +impl From<__wasi_sockoption_t> for WasiSocketOption { + fn from(opt: __wasi_sockoption_t) -> Self { + use WasiSocketOption::*; + match opt { + __WASI_SOCK_OPTION_NOOP => Noop, + __WASI_SOCK_OPTION_REUSE_PORT => ReusePort, + __WASI_SOCK_OPTION_REUSE_ADDR => ReuseAddr, + __WASI_SOCK_OPTION_NO_DELAY => NoDelay, + __WASI_SOCK_OPTION_DONT_ROUTE => DontRoute, + __WASI_SOCK_OPTION_ONLY_V6 => OnlyV6, + __WASI_SOCK_OPTION_BROADCAST => Broadcast, + __WASI_SOCK_OPTION_MULTICAST_LOOP_V4 => MulticastLoopV4, + __WASI_SOCK_OPTION_MULTICAST_LOOP_V6 => MulticastLoopV6, + __WASI_SOCK_OPTION_PROMISCUOUS => Promiscuous, + __WASI_SOCK_OPTION_LISTENING => Listening, + __WASI_SOCK_OPTION_LAST_ERROR => LastError, + __WASI_SOCK_OPTION_KEEP_ALIVE => KeepAlive, + __WASI_SOCK_OPTION_LINGER => Linger, + __WASI_SOCK_OPTION_OOB_INLINE => OobInline, + __WASI_SOCK_OPTION_RECV_BUF_SIZE => RecvBufSize, + __WASI_SOCK_OPTION_SEND_BUF_SIZE => SendBufSize, + __WASI_SOCK_OPTION_RECV_LOWAT => RecvLowat, + __WASI_SOCK_OPTION_SEND_LOWAT => SendLowat, + __WASI_SOCK_OPTION_RECV_TIMEOUT => RecvTimeout, + __WASI_SOCK_OPTION_SEND_TIMEOUT => SendTimeout, + __WASI_SOCK_OPTION_CONNECT_TIMEOUT => ConnectTimeout, + __WASI_SOCK_OPTION_ACCEPT_TIMEOUT => AcceptTimeout, + __WASI_SOCK_OPTION_TTL => Ttl, + __WASI_SOCK_OPTION_MULTICAST_TTL_V4 => MulticastTtlV4, + __WASI_SOCK_OPTION_TYPE => Type, + __WASI_SOCK_OPTION_PROTO => Proto, + _ => Noop, + } + } +} + +#[derive(Debug)] +pub enum WasiSocketStatus { + Opening, + Opened, + Closed, + Failed, +} + +#[derive(Debug)] +pub struct WasiHttpStatus { + pub ok: bool, + pub redirected: bool, + pub size: u64, + pub status: u16, +} + +#[derive(Debug)] +#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +pub struct InodeSocket { + kind: InodeSocketKind, + read_buffer: Option, + read_addr: Option, +} + +impl InodeSocket { + pub fn new(kind: InodeSocketKind) -> InodeSocket { + InodeSocket { + kind, + read_buffer: None, + read_addr: None, + } + } + + pub fn bind( + &mut self, + net: &(dyn VirtualNetworking), + set_addr: SocketAddr, + ) -> Result, __wasi_errno_t> { + match &mut self.kind { + InodeSocketKind::PreSocket { + family, + ty, + addr, + reuse_port, + reuse_addr, + .. + } => { + match *family { + __WASI_ADDRESS_FAMILY_INET4 => { + if !set_addr.is_ipv4() { + return Err(__WASI_EINVAL); + } + } + __WASI_ADDRESS_FAMILY_INET6 => { + if !set_addr.is_ipv6() { + return Err(__WASI_EINVAL); + } + } + _ => { + return Err(__WASI_ENOTSUP); + } + } + + addr.replace(set_addr); + let addr = (*addr).unwrap(); + + Ok(match *ty { + __WASI_SOCK_TYPE_STREAM => { + // we already set the socket address - next we need a bind or connect so nothing + // more to do at this time + None + } + __WASI_SOCK_TYPE_DGRAM => { + let socket = net + .bind_udp(addr, *reuse_port, *reuse_addr) + .map_err(net_error_into_wasi_err)?; + Some(InodeSocket::new(InodeSocketKind::UdpSocket(socket))) + } + _ => return Err(__WASI_EINVAL), + }) + } + _ => Err(__WASI_ENOTSUP), + } + } + + pub fn listen( + &mut self, + net: &(dyn VirtualNetworking), + _backlog: usize, + ) -> Result, __wasi_errno_t> { + match &self.kind { + InodeSocketKind::PreSocket { + ty, + addr, + only_v6, + reuse_port, + reuse_addr, + accept_timeout, + .. + } => Ok(match *ty { + __WASI_SOCK_TYPE_STREAM => { + if addr.is_none() { + return Err(__WASI_EINVAL); + } + let addr = *addr.as_ref().unwrap(); + let mut socket = net + .listen_tcp(addr, *only_v6, *reuse_port, *reuse_addr) + .map_err(net_error_into_wasi_err)?; + if let Some(accept_timeout) = accept_timeout { + socket + .set_timeout(Some(*accept_timeout)) + .map_err(net_error_into_wasi_err)?; + } + Some(InodeSocket::new(InodeSocketKind::TcpListener(socket))) + } + _ => return Err(__WASI_ENOTSUP), + }), + InodeSocketKind::Closed => Err(__WASI_EIO), + _ => Err(__WASI_ENOTSUP), + } + } + + pub fn accept( + &self, + _fd_flags: __wasi_fdflags_t, + ) -> Result<(Box, SocketAddr), __wasi_errno_t> { + let (sock, addr) = match &self.kind { + InodeSocketKind::TcpListener(sock) => sock.accept().map_err(net_error_into_wasi_err), + InodeSocketKind::PreSocket { .. } => Err(__WASI_ENOTCONN), + InodeSocketKind::Closed => Err(__WASI_EIO), + _ => Err(__WASI_ENOTSUP), + }?; + Ok((sock, addr)) + } + + pub fn accept_timeout( + &self, + _fd_flags: __wasi_fdflags_t, + timeout: Duration, + ) -> Result<(Box, SocketAddr), __wasi_errno_t> { + let (sock, addr) = match &self.kind { + InodeSocketKind::TcpListener(sock) => sock + .accept_timeout(timeout) + .map_err(net_error_into_wasi_err), + InodeSocketKind::PreSocket { .. } => Err(__WASI_ENOTCONN), + InodeSocketKind::Closed => Err(__WASI_EIO), + _ => Err(__WASI_ENOTSUP), + }?; + Ok((sock, addr)) + } + + pub fn connect( + &mut self, + net: &(dyn VirtualNetworking), + peer: SocketAddr, + ) -> Result, __wasi_errno_t> { + match &mut self.kind { + InodeSocketKind::PreSocket { + ty, + addr, + send_timeout, + recv_timeout, + connect_timeout, + .. + } => Ok(match *ty { + __WASI_SOCK_TYPE_STREAM => { + let addr = match addr { + Some(a) => *a, + None => { + let ip = match peer.is_ipv4() { + true => IpAddr::V4(Ipv4Addr::UNSPECIFIED), + false => IpAddr::V6(Ipv6Addr::UNSPECIFIED), + }; + SocketAddr::new(ip, 0) + } + }; + let mut socket = net + .connect_tcp(addr, peer, *connect_timeout) + .map_err(net_error_into_wasi_err)?; + if let Some(timeout) = send_timeout { + socket + .set_opt_time(TimeType::WriteTimeout, Some(*timeout)) + .map_err(net_error_into_wasi_err)?; + } + if let Some(timeout) = recv_timeout { + socket + .set_opt_time(TimeType::ReadTimeout, Some(*timeout)) + .map_err(net_error_into_wasi_err)?; + } + Some(InodeSocket::new(InodeSocketKind::TcpStream(socket))) + } + __WASI_SOCK_TYPE_DGRAM => return Err(__WASI_EINVAL), + _ => return Err(__WASI_ENOTSUP), + }), + InodeSocketKind::UdpSocket(sock) => { + sock.connect(peer).map_err(net_error_into_wasi_err)?; + Ok(None) + } + InodeSocketKind::Closed => Err(__WASI_EIO), + _ => Err(__WASI_ENOTSUP), + } + } + + pub fn status(&self) -> Result { + Ok(match &self.kind { + InodeSocketKind::PreSocket { .. } => WasiSocketStatus::Opening, + InodeSocketKind::WebSocket(_) => WasiSocketStatus::Opened, + InodeSocketKind::HttpRequest(..) => WasiSocketStatus::Opened, + InodeSocketKind::TcpListener(_) => WasiSocketStatus::Opened, + InodeSocketKind::TcpStream(_) => WasiSocketStatus::Opened, + InodeSocketKind::UdpSocket(_) => WasiSocketStatus::Opened, + InodeSocketKind::Closed => WasiSocketStatus::Closed, + _ => WasiSocketStatus::Failed, + }) + } + + pub fn http_status(&self) -> Result { + Ok(match &self.kind { + InodeSocketKind::HttpRequest(http, ..) => { + let http = http.lock().unwrap(); + let guard = http.status.lock().unwrap(); + let status = guard + .recv() + .map_err(|_| __WASI_EIO)? + .map_err(net_error_into_wasi_err)?; + WasiHttpStatus { + ok: true, + redirected: status.redirected, + status: status.status, + size: status.size as u64, + } + } + InodeSocketKind::Closed => return Err(__WASI_EIO), + _ => return Err(__WASI_ENOTSUP), + }) + } + + pub fn addr_local(&self) -> Result { + Ok(match &self.kind { + InodeSocketKind::PreSocket { family, addr, .. } => { + if let Some(addr) = addr { + *addr + } else { + SocketAddr::new( + match *family { + __WASI_ADDRESS_FAMILY_INET4 => IpAddr::V4(Ipv4Addr::UNSPECIFIED), + __WASI_ADDRESS_FAMILY_INET6 => IpAddr::V6(Ipv6Addr::UNSPECIFIED), + _ => return Err(__WASI_EINVAL), + }, + 0, + ) + } + } + InodeSocketKind::Icmp(sock) => sock.addr_local().map_err(net_error_into_wasi_err)?, + InodeSocketKind::TcpListener(sock) => { + sock.addr_local().map_err(net_error_into_wasi_err)? + } + InodeSocketKind::TcpStream(sock) => { + sock.addr_local().map_err(net_error_into_wasi_err)? + } + InodeSocketKind::UdpSocket(sock) => { + sock.addr_local().map_err(net_error_into_wasi_err)? + } + InodeSocketKind::Closed => return Err(__WASI_EIO), + _ => return Err(__WASI_ENOTSUP), + }) + } + + pub fn addr_peer(&self) -> Result { + Ok(match &self.kind { + InodeSocketKind::PreSocket { family, .. } => SocketAddr::new( + match *family { + __WASI_ADDRESS_FAMILY_INET4 => IpAddr::V4(Ipv4Addr::UNSPECIFIED), + __WASI_ADDRESS_FAMILY_INET6 => IpAddr::V6(Ipv6Addr::UNSPECIFIED), + _ => return Err(__WASI_EINVAL), + }, + 0, + ), + InodeSocketKind::TcpStream(sock) => { + sock.addr_peer().map_err(net_error_into_wasi_err)? + } + InodeSocketKind::UdpSocket(sock) => sock + .addr_peer() + .map_err(net_error_into_wasi_err)? + .map(Ok) + .unwrap_or_else(|| { + sock.addr_local() + .map_err(net_error_into_wasi_err) + .map(|addr| { + SocketAddr::new( + match addr { + SocketAddr::V4(_) => IpAddr::V4(Ipv4Addr::UNSPECIFIED), + SocketAddr::V6(_) => IpAddr::V6(Ipv6Addr::UNSPECIFIED), + }, + 0, + ) + }) + })?, + InodeSocketKind::Closed => return Err(__WASI_EIO), + _ => return Err(__WASI_ENOTSUP), + }) + } + + pub fn set_opt_flag( + &mut self, + option: WasiSocketOption, + val: bool, + ) -> Result<(), __wasi_errno_t> { + match &mut self.kind { + InodeSocketKind::PreSocket { + only_v6, + reuse_port, + reuse_addr, + .. + } => { + match option { + WasiSocketOption::OnlyV6 => *only_v6 = val, + WasiSocketOption::ReusePort => *reuse_port = val, + WasiSocketOption::ReuseAddr => *reuse_addr = val, + _ => return Err(__WASI_EINVAL), + }; + } + InodeSocketKind::Raw(sock) => match option { + WasiSocketOption::Promiscuous => { + sock.set_promiscuous(val).map_err(net_error_into_wasi_err)? + } + _ => return Err(__WASI_EINVAL), + }, + InodeSocketKind::TcpStream(sock) => match option { + WasiSocketOption::NoDelay => { + sock.set_nodelay(val).map_err(net_error_into_wasi_err)? + } + _ => return Err(__WASI_EINVAL), + }, + InodeSocketKind::UdpSocket(sock) => match option { + WasiSocketOption::Broadcast => { + sock.set_broadcast(val).map_err(net_error_into_wasi_err)? + } + WasiSocketOption::MulticastLoopV4 => sock + .set_multicast_loop_v4(val) + .map_err(net_error_into_wasi_err)?, + WasiSocketOption::MulticastLoopV6 => sock + .set_multicast_loop_v6(val) + .map_err(net_error_into_wasi_err)?, + _ => return Err(__WASI_EINVAL), + }, + InodeSocketKind::Closed => return Err(__WASI_EIO), + _ => return Err(__WASI_ENOTSUP), + } + Ok(()) + } + + pub fn get_opt_flag(&self, option: WasiSocketOption) -> Result { + Ok(match &self.kind { + InodeSocketKind::PreSocket { + only_v6, + reuse_port, + reuse_addr, + .. + } => match option { + WasiSocketOption::OnlyV6 => *only_v6, + WasiSocketOption::ReusePort => *reuse_port, + WasiSocketOption::ReuseAddr => *reuse_addr, + _ => return Err(__WASI_EINVAL), + }, + InodeSocketKind::Raw(sock) => match option { + WasiSocketOption::Promiscuous => { + sock.promiscuous().map_err(net_error_into_wasi_err)? + } + _ => return Err(__WASI_EINVAL), + }, + InodeSocketKind::TcpStream(sock) => match option { + WasiSocketOption::NoDelay => sock.nodelay().map_err(net_error_into_wasi_err)?, + _ => return Err(__WASI_EINVAL), + }, + InodeSocketKind::UdpSocket(sock) => match option { + WasiSocketOption::Broadcast => sock.broadcast().map_err(net_error_into_wasi_err)?, + WasiSocketOption::MulticastLoopV4 => { + sock.multicast_loop_v4().map_err(net_error_into_wasi_err)? + } + WasiSocketOption::MulticastLoopV6 => { + sock.multicast_loop_v6().map_err(net_error_into_wasi_err)? + } + _ => return Err(__WASI_EINVAL), + }, + InodeSocketKind::Closed => return Err(__WASI_EIO), + _ => return Err(__WASI_ENOTSUP), + }) + } + + pub fn set_send_buf_size(&mut self, size: usize) -> Result<(), __wasi_errno_t> { + match &mut self.kind { + InodeSocketKind::PreSocket { send_buf_size, .. } => { + *send_buf_size = Some(size); + } + InodeSocketKind::TcpStream(sock) => { + sock.set_send_buf_size(size) + .map_err(net_error_into_wasi_err)?; + } + InodeSocketKind::Closed => return Err(__WASI_EIO), + _ => return Err(__WASI_ENOTSUP), + } + Ok(()) + } + + pub fn send_buf_size(&self) -> Result { + match &self.kind { + InodeSocketKind::PreSocket { send_buf_size, .. } => { + Ok((*send_buf_size).unwrap_or_default()) + } + InodeSocketKind::TcpStream(sock) => { + sock.send_buf_size().map_err(net_error_into_wasi_err) + } + InodeSocketKind::Closed => Err(__WASI_EIO), + _ => Err(__WASI_ENOTSUP), + } + } + + pub fn set_recv_buf_size(&mut self, size: usize) -> Result<(), __wasi_errno_t> { + match &mut self.kind { + InodeSocketKind::PreSocket { recv_buf_size, .. } => { + *recv_buf_size = Some(size); + } + InodeSocketKind::TcpStream(sock) => { + sock.set_recv_buf_size(size) + .map_err(net_error_into_wasi_err)?; + } + InodeSocketKind::Closed => return Err(__WASI_EIO), + _ => return Err(__WASI_ENOTSUP), + } + Ok(()) + } + + pub fn recv_buf_size(&self) -> Result { + match &self.kind { + InodeSocketKind::PreSocket { recv_buf_size, .. } => { + Ok((*recv_buf_size).unwrap_or_default()) + } + InodeSocketKind::TcpStream(sock) => { + sock.recv_buf_size().map_err(net_error_into_wasi_err) + } + InodeSocketKind::Closed => Err(__WASI_EIO), + _ => Err(__WASI_ENOTSUP), + } + } + + pub fn set_linger( + &mut self, + linger: Option, + ) -> Result<(), __wasi_errno_t> { + match &mut self.kind { + InodeSocketKind::TcpStream(sock) => { + sock.set_linger(linger).map_err(net_error_into_wasi_err) + } + InodeSocketKind::PreSocket { .. } => Err(__WASI_EIO), + InodeSocketKind::Closed => Err(__WASI_EIO), + _ => Err(__WASI_ENOTSUP), + } + } + + pub fn linger(&self) -> Result, __wasi_errno_t> { + match &self.kind { + InodeSocketKind::TcpStream(sock) => sock.linger().map_err(net_error_into_wasi_err), + InodeSocketKind::PreSocket { .. } => Err(__WASI_EIO), + InodeSocketKind::Closed => Err(__WASI_EIO), + _ => Err(__WASI_ENOTSUP), + } + } + + pub fn set_opt_time( + &mut self, + ty: TimeType, + timeout: Option, + ) -> Result<(), __wasi_errno_t> { + match &mut self.kind { + InodeSocketKind::TcpStream(sock) => sock + .set_opt_time(ty, timeout) + .map_err(net_error_into_wasi_err), + InodeSocketKind::TcpListener(sock) => match ty { + TimeType::AcceptTimeout => { + sock.set_timeout(timeout).map_err(net_error_into_wasi_err) + } + _ => Err(__WASI_EINVAL), + }, + InodeSocketKind::PreSocket { + recv_timeout, + send_timeout, + connect_timeout, + accept_timeout, + .. + } => match ty { + TimeType::ConnectTimeout => { + *connect_timeout = timeout; + Ok(()) + } + TimeType::AcceptTimeout => { + *accept_timeout = timeout; + Ok(()) + } + TimeType::ReadTimeout => { + *recv_timeout = timeout; + Ok(()) + } + TimeType::WriteTimeout => { + *send_timeout = timeout; + Ok(()) + } + _ => Err(__WASI_EIO), + }, + InodeSocketKind::Closed => Err(__WASI_EIO), + _ => Err(__WASI_ENOTSUP), + } + } + + pub fn opt_time(&self, ty: TimeType) -> Result, __wasi_errno_t> { + match &self.kind { + InodeSocketKind::TcpStream(sock) => sock.opt_time(ty).map_err(net_error_into_wasi_err), + InodeSocketKind::TcpListener(sock) => match ty { + TimeType::AcceptTimeout => sock.timeout().map_err(net_error_into_wasi_err), + _ => Err(__WASI_EINVAL), + }, + InodeSocketKind::PreSocket { + recv_timeout, + send_timeout, + connect_timeout, + accept_timeout, + .. + } => match ty { + TimeType::ConnectTimeout => Ok(*connect_timeout), + TimeType::AcceptTimeout => Ok(*accept_timeout), + TimeType::ReadTimeout => Ok(*recv_timeout), + TimeType::WriteTimeout => Ok(*send_timeout), + _ => Err(__WASI_EINVAL), + }, + InodeSocketKind::Closed => Err(__WASI_EIO), + _ => Err(__WASI_ENOTSUP), + } + } + + pub fn set_ttl(&mut self, ttl: u32) -> Result<(), __wasi_errno_t> { + match &mut self.kind { + InodeSocketKind::TcpStream(sock) => sock.set_ttl(ttl).map_err(net_error_into_wasi_err), + InodeSocketKind::UdpSocket(sock) => sock.set_ttl(ttl).map_err(net_error_into_wasi_err), + InodeSocketKind::PreSocket { .. } => Err(__WASI_EIO), + InodeSocketKind::Closed => Err(__WASI_EIO), + _ => Err(__WASI_ENOTSUP), + } + } + + pub fn ttl(&self) -> Result { + match &self.kind { + InodeSocketKind::TcpStream(sock) => sock.ttl().map_err(net_error_into_wasi_err), + InodeSocketKind::UdpSocket(sock) => sock.ttl().map_err(net_error_into_wasi_err), + InodeSocketKind::PreSocket { .. } => Err(__WASI_EIO), + InodeSocketKind::Closed => Err(__WASI_EIO), + _ => Err(__WASI_ENOTSUP), + } + } + + pub fn set_multicast_ttl_v4(&mut self, ttl: u32) -> Result<(), __wasi_errno_t> { + match &mut self.kind { + InodeSocketKind::UdpSocket(sock) => sock + .set_multicast_ttl_v4(ttl) + .map_err(net_error_into_wasi_err), + InodeSocketKind::PreSocket { .. } => Err(__WASI_EIO), + InodeSocketKind::Closed => Err(__WASI_EIO), + _ => Err(__WASI_ENOTSUP), + } + } + + pub fn multicast_ttl_v4(&self) -> Result { + match &self.kind { + InodeSocketKind::UdpSocket(sock) => { + sock.multicast_ttl_v4().map_err(net_error_into_wasi_err) + } + InodeSocketKind::PreSocket { .. } => Err(__WASI_EIO), + InodeSocketKind::Closed => Err(__WASI_EIO), + _ => Err(__WASI_ENOTSUP), + } + } + + pub fn join_multicast_v4( + &mut self, + multiaddr: Ipv4Addr, + iface: Ipv4Addr, + ) -> Result<(), __wasi_errno_t> { + match &mut self.kind { + InodeSocketKind::UdpSocket(sock) => sock + .join_multicast_v4(multiaddr, iface) + .map_err(net_error_into_wasi_err), + InodeSocketKind::PreSocket { .. } => Err(__WASI_EIO), + InodeSocketKind::Closed => Err(__WASI_EIO), + _ => Err(__WASI_ENOTSUP), + } + } + + pub fn leave_multicast_v4( + &mut self, + multiaddr: Ipv4Addr, + iface: Ipv4Addr, + ) -> Result<(), __wasi_errno_t> { + match &mut self.kind { + InodeSocketKind::UdpSocket(sock) => sock + .leave_multicast_v4(multiaddr, iface) + .map_err(net_error_into_wasi_err), + InodeSocketKind::PreSocket { .. } => Err(__WASI_EIO), + InodeSocketKind::Closed => Err(__WASI_EIO), + _ => Err(__WASI_ENOTSUP), + } + } + + pub fn join_multicast_v6( + &mut self, + multiaddr: Ipv6Addr, + iface: u32, + ) -> Result<(), __wasi_errno_t> { + match &mut self.kind { + InodeSocketKind::UdpSocket(sock) => sock + .join_multicast_v6(multiaddr, iface) + .map_err(net_error_into_wasi_err), + InodeSocketKind::PreSocket { .. } => Err(__WASI_EIO), + InodeSocketKind::Closed => Err(__WASI_EIO), + _ => Err(__WASI_ENOTSUP), + } + } + + pub fn leave_multicast_v6( + &mut self, + multiaddr: Ipv6Addr, + iface: u32, + ) -> Result<(), __wasi_errno_t> { + match &mut self.kind { + InodeSocketKind::UdpSocket(sock) => sock + .leave_multicast_v6(multiaddr, iface) + .map_err(net_error_into_wasi_err), + InodeSocketKind::PreSocket { .. } => Err(__WASI_EIO), + InodeSocketKind::Closed => Err(__WASI_EIO), + _ => Err(__WASI_ENOTSUP), + } + } + + pub fn send( + &mut self, + memory: &Memory, + iov: WasmSlice<__wasi_ciovec_t>, + ) -> Result { + let buf_len: M::Offset = iov + .iter() + .filter_map(|a| a.read().ok()) + .map(|a| a.buf_len) + .sum(); + let buf_len: usize = buf_len.try_into().map_err(|_| __WASI_EINVAL)?; + let mut buf = Vec::with_capacity(buf_len); + write_bytes(&mut buf, memory, iov)?; + match &mut self.kind { + InodeSocketKind::HttpRequest(sock, ty) => { + let sock = sock.get_mut().unwrap(); + match ty { + InodeHttpSocketType::Request => { + if sock.request.is_none() { + return Err(__WASI_EIO); + } + let request = sock.request.as_ref().unwrap(); + request.send(buf).map(|_| buf_len).map_err(|_| __WASI_EIO) + } + _ => { + return Err(__WASI_EIO); + } + } + } + InodeSocketKind::WebSocket(sock) => sock + .send(Bytes::from(buf)) + .map(|_| buf_len) + .map_err(net_error_into_wasi_err), + InodeSocketKind::Raw(sock) => { + sock.send(Bytes::from(buf)).map_err(net_error_into_wasi_err) + } + InodeSocketKind::TcpStream(sock) => { + sock.send(Bytes::from(buf)).map_err(net_error_into_wasi_err) + } + InodeSocketKind::UdpSocket(sock) => { + sock.send(Bytes::from(buf)).map_err(net_error_into_wasi_err) + } + InodeSocketKind::PreSocket { .. } => Err(__WASI_ENOTCONN), + InodeSocketKind::Closed => Err(__WASI_EIO), + _ => Err(__WASI_ENOTSUP), + } + .map(|_| buf_len) + } + + pub fn send_bytes(&mut self, buf: Bytes) -> Result { + let buf_len = buf.len(); + match &mut self.kind { + InodeSocketKind::HttpRequest(sock, ty) => { + let sock = sock.get_mut().unwrap(); + match ty { + InodeHttpSocketType::Request => { + if sock.request.is_none() { + return Err(__WASI_EIO); + } + let request = sock.request.as_ref().unwrap(); + request + .send(buf.to_vec()) + .map(|_| buf_len) + .map_err(|_| __WASI_EIO) + } + _ => { + return Err(__WASI_EIO); + } + } + } + InodeSocketKind::WebSocket(sock) => sock + .send(buf) + .map(|_| buf_len) + .map_err(net_error_into_wasi_err), + InodeSocketKind::Raw(sock) => sock.send(buf).map_err(net_error_into_wasi_err), + InodeSocketKind::TcpStream(sock) => sock.send(buf).map_err(net_error_into_wasi_err), + InodeSocketKind::UdpSocket(sock) => sock.send(buf).map_err(net_error_into_wasi_err), + InodeSocketKind::PreSocket { .. } => Err(__WASI_ENOTCONN), + InodeSocketKind::Closed => Err(__WASI_EIO), + _ => Err(__WASI_ENOTSUP), + } + .map(|_| buf_len) + } + + pub fn send_to( + &mut self, + memory: &Memory, + iov: WasmSlice<__wasi_ciovec_t>, + addr: WasmPtr<__wasi_addr_port_t, M>, + ) -> Result { + let (addr_ip, addr_port) = read_ip_port(memory, addr)?; + let addr = SocketAddr::new(addr_ip, addr_port); + let buf_len: M::Offset = iov + .iter() + .filter_map(|a| a.read().ok()) + .map(|a| a.buf_len) + .sum(); + let buf_len: usize = buf_len.try_into().map_err(|_| __WASI_EINVAL)?; + let mut buf = Vec::with_capacity(buf_len); + write_bytes(&mut buf, memory, iov)?; + match &mut self.kind { + InodeSocketKind::Icmp(sock) => sock + .send_to(Bytes::from(buf), addr) + .map_err(net_error_into_wasi_err), + InodeSocketKind::UdpSocket(sock) => sock + .send_to(Bytes::from(buf), addr) + .map_err(net_error_into_wasi_err), + InodeSocketKind::PreSocket { .. } => Err(__WASI_ENOTCONN), + InodeSocketKind::Closed => Err(__WASI_EIO), + _ => Err(__WASI_ENOTSUP), + } + .map(|_| buf_len) + } + + pub fn recv( + &mut self, + memory: &Memory, + iov: WasmSlice<__wasi_iovec_t>, + ) -> Result { + loop { + if let Some(buf) = self.read_buffer.as_mut() { + let buf_len = buf.len(); + if buf_len > 0 { + let reader = buf.as_ref(); + let read = read_bytes(reader, memory, iov).map(|_| buf_len)?; + if let InodeSocketKind::TcpStream(..) = &self.kind { + buf.advance(read); + } else { + buf.clear(); + } + return Ok(read); + } + } + let data = match &mut self.kind { + InodeSocketKind::HttpRequest(sock, ty) => { + let sock = sock.get_mut().unwrap(); + match ty { + InodeHttpSocketType::Response => { + if sock.response.is_none() { + return Err(__WASI_EIO); + } + let response = sock.response.as_ref().unwrap(); + Bytes::from(response.recv().map_err(|_| __WASI_EIO)?) + } + InodeHttpSocketType::Headers => { + if sock.headers.is_none() { + return Err(__WASI_EIO); + } + let headers = sock.headers.as_ref().unwrap(); + let headers = headers.recv().map_err(|_| __WASI_EIO)?; + let headers = format!("{}: {}", headers.0, headers.1); + Bytes::from(headers.as_bytes().to_vec()) + } + _ => { + return Err(__WASI_EIO); + } + } + } + InodeSocketKind::WebSocket(sock) => { + let read = sock.recv().map_err(net_error_into_wasi_err)?; + read.data + } + InodeSocketKind::Raw(sock) => { + let read = sock.recv().map_err(net_error_into_wasi_err)?; + read.data + } + InodeSocketKind::TcpStream(sock) => { + let read = sock.recv().map_err(net_error_into_wasi_err)?; + read.data + } + InodeSocketKind::UdpSocket(sock) => { + let read = sock.recv().map_err(net_error_into_wasi_err)?; + read.data + } + InodeSocketKind::PreSocket { .. } => return Err(__WASI_ENOTCONN), + InodeSocketKind::Closed => return Err(__WASI_EIO), + _ => return Err(__WASI_ENOTSUP), + }; + self.read_buffer.replace(data); + self.read_addr.take(); + } + } + + pub fn recv_from( + &mut self, + memory: &Memory, + iov: WasmSlice<__wasi_iovec_t>, + addr: WasmPtr<__wasi_addr_port_t, M>, + ) -> Result { + loop { + if let Some(buf) = self.read_buffer.as_mut() { + if !buf.is_empty() { + let reader = buf.as_ref(); + let ret = read_bytes(reader, memory, iov)?; + let peer = self + .read_addr + .unwrap_or_else(|| SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0)); + write_ip_port(memory, addr, peer.ip(), peer.port())?; + return Ok(ret); + } + } + let rcv = match &mut self.kind { + InodeSocketKind::Icmp(sock) => sock.recv_from().map_err(net_error_into_wasi_err)?, + InodeSocketKind::UdpSocket(sock) => { + sock.recv_from().map_err(net_error_into_wasi_err)? + } + InodeSocketKind::PreSocket { .. } => return Err(__WASI_ENOTCONN), + InodeSocketKind::Closed => return Err(__WASI_EIO), + _ => return Err(__WASI_ENOTSUP), + }; + self.read_buffer.replace(rcv.data); + self.read_addr.replace(rcv.addr); + } + } + + pub fn shutdown(&mut self, how: std::net::Shutdown) -> Result<(), __wasi_errno_t> { + use std::net::Shutdown; + match &mut self.kind { + InodeSocketKind::TcpStream(sock) => { + sock.shutdown(how).map_err(net_error_into_wasi_err)?; + } + InodeSocketKind::HttpRequest(http, ..) => { + let http = http.get_mut().unwrap(); + match how { + Shutdown::Read => { + http.response.take(); + http.headers.take(); + } + Shutdown::Write => { + http.request.take(); + } + Shutdown::Both => { + http.request.take(); + http.response.take(); + http.headers.take(); + } + }; + } + InodeSocketKind::PreSocket { .. } => return Err(__WASI_ENOTCONN), + InodeSocketKind::Closed => return Err(__WASI_EIO), + _ => return Err(__WASI_ENOTSUP), + } + Ok(()) + } +} + +impl Read for InodeSocket { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + loop { + if let Some(read_buf) = self.read_buffer.as_mut() { + let buf_len = read_buf.len(); + if buf_len > 0 { + let mut reader = read_buf.as_ref(); + let read = reader.read(buf)?; + read_buf.advance(read); + return Ok(read); + } + } + let data = match &mut self.kind { + InodeSocketKind::HttpRequest(sock, ty) => { + let sock = sock.get_mut().unwrap(); + match ty { + InodeHttpSocketType::Response => { + if sock.response.is_none() { + return Err(io::Error::new( + io::ErrorKind::BrokenPipe, + "the socket is not connected".to_string(), + )); + } + let response = sock.response.as_ref().unwrap(); + Bytes::from(response.recv().map_err(|_| { + io::Error::new( + io::ErrorKind::BrokenPipe, + "the wasi pipe is not connected".to_string(), + ) + })?) + } + InodeHttpSocketType::Headers => { + if sock.headers.is_none() { + return Err(io::Error::new( + io::ErrorKind::BrokenPipe, + "the socket is not connected".to_string(), + )); + } + let headers = sock.headers.as_ref().unwrap(); + let headers = headers.recv().map_err(|_| { + io::Error::new( + io::ErrorKind::BrokenPipe, + "the wasi pipe is not connected".to_string(), + ) + })?; + let headers = format!("{}: {}", headers.0, headers.1); + Bytes::from(headers.as_bytes().to_vec()) + } + _ => { + return Err(io::Error::new( + io::ErrorKind::Unsupported, + "the socket is of an unsupported type".to_string(), + )); + } + } + } + InodeSocketKind::WebSocket(sock) => { + let read = sock.recv().map_err(net_error_into_io_err)?; + read.data + } + InodeSocketKind::Raw(sock) => { + let read = sock.recv().map_err(net_error_into_io_err)?; + read.data + } + InodeSocketKind::TcpStream(sock) => { + let read = sock.recv().map_err(net_error_into_io_err)?; + read.data + } + InodeSocketKind::UdpSocket(sock) => { + let read = sock.recv().map_err(net_error_into_io_err)?; + read.data + } + InodeSocketKind::PreSocket { .. } => { + return Err(io::Error::new( + io::ErrorKind::NotConnected, + "the socket is not connected".to_string(), + )) + } + InodeSocketKind::Closed => { + return Err(io::Error::new( + io::ErrorKind::BrokenPipe, + "the socket has been closed".to_string(), + )) + } + _ => { + return Err(io::Error::new( + io::ErrorKind::Unsupported, + "the socket type is not supported".to_string(), + )) + } + }; + self.read_buffer.replace(data); + self.read_addr.take(); + } + } +} + +impl Drop for InodeSocket { + fn drop(&mut self) { + if let InodeSocketKind::HttpRequest(http, ty) = &self.kind { + let mut guard = http.lock().unwrap(); + match ty { + InodeHttpSocketType::Request => { + guard.request.take(); + } + InodeHttpSocketType::Response => { + guard.response.take(); + } + InodeHttpSocketType::Headers => { + guard.headers.take(); + } + } + } + } +} + +#[allow(dead_code)] +pub(crate) fn read_ip( + memory: &Memory, + ptr: WasmPtr<__wasi_addr_t, M>, +) -> Result { + let addr_ptr = ptr.deref(memory); + let addr = addr_ptr.read().map_err(crate::mem_error_to_wasi)?; + + let o = addr.u.octs; + Ok(match addr.tag { + __WASI_ADDRESS_FAMILY_INET4 => IpAddr::V4(Ipv4Addr::new(o[0], o[1], o[2], o[3])), + __WASI_ADDRESS_FAMILY_INET6 => { + let [a, b, c, d, e, f, g, h] = unsafe { transmute::<_, [u16; 8]>(o) }; + IpAddr::V6(Ipv6Addr::new(a, b, c, d, e, f, g, h)) + } + _ => return Err(__WASI_EINVAL), + }) +} + +pub(crate) fn read_ip_v4( + memory: &Memory, + ptr: WasmPtr<__wasi_addr_ip4_t, M>, +) -> Result { + let addr_ptr = ptr.deref(memory); + let addr = addr_ptr.read().map_err(crate::mem_error_to_wasi)?; + + let o = addr.octs; + Ok(Ipv4Addr::new(o[0], o[1], o[2], o[3])) +} + +pub(crate) fn read_ip_v6( + memory: &Memory, + ptr: WasmPtr<__wasi_addr_ip6_t, M>, +) -> Result { + let addr_ptr = ptr.deref(memory); + let addr = addr_ptr.read().map_err(crate::mem_error_to_wasi)?; + + let [a, b, c, d, e, f, g, h] = unsafe { transmute::<_, [u16; 8]>(addr.segs) }; + Ok(Ipv6Addr::new(a, b, c, d, e, f, g, h)) +} + +pub(crate) fn write_ip( + memory: &Memory, + ptr: WasmPtr<__wasi_addr_t, M>, + ip: IpAddr, +) -> Result<(), __wasi_errno_t> { + let ip = match ip { + IpAddr::V4(ip) => { + let o = ip.octets(); + __wasi_addr_t { + tag: __WASI_ADDRESS_FAMILY_INET4, + u: __wasi_addr_u { + octs: [o[0], o[1], o[2], o[3], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + }, + } + } + IpAddr::V6(ip) => { + let o = ip.octets(); + __wasi_addr_t { + tag: __WASI_ADDRESS_FAMILY_INET6, + u: __wasi_addr_u { octs: o }, + } + } + }; + + let addr_ptr = ptr.deref(memory); + addr_ptr.write(ip).map_err(crate::mem_error_to_wasi)?; + Ok(()) +} + +#[allow(dead_code)] +pub(crate) fn read_cidr( + memory: &Memory, + ptr: WasmPtr<__wasi_cidr_t, M>, +) -> Result { + let addr_ptr = ptr.deref(memory); + let addr = addr_ptr.read().map_err(crate::mem_error_to_wasi)?; + + let o = addr.u.octs; + Ok(match addr.tag { + __WASI_ADDRESS_FAMILY_INET4 => IpCidr { + ip: IpAddr::V4(Ipv4Addr::new(o[0], o[1], o[2], o[3])), + prefix: o[4], + }, + __WASI_ADDRESS_FAMILY_INET6 => { + let [a, b, c, d, e, f, g, h] = { + let o = [ + o[0], o[1], o[2], o[3], o[4], o[5], o[6], o[7], o[8], o[9], o[10], o[11], + o[12], o[13], o[14], o[15], + ]; + unsafe { transmute::<_, [u16; 8]>(o) } + }; + IpCidr { + ip: IpAddr::V6(Ipv6Addr::new(a, b, c, d, e, f, g, h)), + prefix: o[16], + } + } + _ => return Err(__WASI_EINVAL), + }) +} + +#[allow(dead_code)] +pub(crate) fn write_cidr( + memory: &Memory, + ptr: WasmPtr<__wasi_cidr_t, M>, + cidr: IpCidr, +) -> Result<(), __wasi_errno_t> { + let p = cidr.prefix; + let cidr = match cidr.ip { + IpAddr::V4(ip) => { + let o = ip.octets(); + __wasi_cidr_t { + tag: __WASI_ADDRESS_FAMILY_INET4, + u: __wasi_cidr_u { + octs: [ + o[0], o[1], o[2], o[3], p, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ], + }, + } + } + IpAddr::V6(ip) => { + let o = ip.octets(); + __wasi_cidr_t { + tag: __WASI_ADDRESS_FAMILY_INET6, + u: __wasi_cidr_u { + octs: [ + o[0], o[1], o[2], o[3], o[4], o[5], o[6], o[7], o[8], o[9], o[10], o[11], + o[12], o[13], o[14], o[15], p, + ], + }, + } + } + }; + + let addr_ptr = ptr.deref(memory); + addr_ptr.write(cidr).map_err(crate::mem_error_to_wasi)?; + Ok(()) +} + +pub(crate) fn read_ip_port( + memory: &Memory, + ptr: WasmPtr<__wasi_addr_port_t, M>, +) -> Result<(IpAddr, u16), __wasi_errno_t> { + let addr_ptr = ptr.deref(memory); + let addr = addr_ptr.read().map_err(crate::mem_error_to_wasi)?; + + let o = addr.u.octs; + Ok(match addr.tag { + __WASI_ADDRESS_FAMILY_INET4 => { + let port = u16::from_ne_bytes([o[0], o[1]]); + (IpAddr::V4(Ipv4Addr::new(o[2], o[3], o[4], o[5])), port) + } + __WASI_ADDRESS_FAMILY_INET6 => { + let [a, b, c, d, e, f, g, h] = { + let o = [ + o[2], o[3], o[4], o[5], o[6], o[7], o[8], o[9], o[10], o[11], o[12], o[13], + o[14], o[15], o[16], o[17], + ]; + unsafe { transmute::<_, [u16; 8]>(o) } + }; + ( + IpAddr::V6(Ipv6Addr::new(a, b, c, d, e, f, g, h)), + u16::from_ne_bytes([o[0], o[1]]), + ) + } + _ => return Err(__WASI_EINVAL), + }) +} + +#[allow(dead_code)] +pub(crate) fn write_ip_port( + memory: &Memory, + ptr: WasmPtr<__wasi_addr_port_t, M>, + ip: IpAddr, + port: u16, +) -> Result<(), __wasi_errno_t> { + let p = port.to_be_bytes(); + let ipport = match ip { + IpAddr::V4(ip) => { + let o = ip.octets(); + __wasi_addr_port_t { + tag: __WASI_ADDRESS_FAMILY_INET4, + u: __wasi_addr_port_u { + octs: [ + p[0], p[1], o[0], o[1], o[2], o[3], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ], + }, + } + } + IpAddr::V6(ip) => { + let o = ip.octets(); + __wasi_addr_port_t { + tag: __WASI_ADDRESS_FAMILY_INET6, + u: __wasi_addr_port_u { + octs: [ + p[0], p[1], o[0], o[1], o[2], o[3], o[4], o[5], o[6], o[7], o[8], o[9], + o[10], o[11], o[12], o[13], o[14], o[15], + ], + }, + } + } + }; + + let addr_ptr = ptr.deref(memory); + addr_ptr.write(ipport).map_err(crate::mem_error_to_wasi)?; + Ok(()) +} + +#[allow(dead_code)] +pub(crate) fn read_route( + memory: &Memory, + ptr: WasmPtr<__wasi_route_t, M>, +) -> Result { + let route_ptr = ptr.deref(memory); + let route = route_ptr.read().map_err(crate::mem_error_to_wasi)?; + + Ok(IpRoute { + cidr: { + let o = route.cidr.u.octs; + match route.cidr.tag { + __WASI_ADDRESS_FAMILY_INET4 => IpCidr { + ip: IpAddr::V4(Ipv4Addr::new(o[0], o[1], o[2], o[3])), + prefix: o[4], + }, + __WASI_ADDRESS_FAMILY_INET6 => { + let [a, b, c, d, e, f, g, h] = { + let o = [ + o[0], o[1], o[2], o[3], o[4], o[5], o[6], o[7], o[8], o[9], o[10], + o[11], o[12], o[13], o[14], o[15], + ]; + unsafe { transmute::<_, [u16; 8]>(o) } + }; + IpCidr { + ip: IpAddr::V6(Ipv6Addr::new(a, b, c, d, e, f, g, h)), + prefix: o[16], + } + } + _ => return Err(__WASI_EINVAL), + } + }, + via_router: { + let o = route.via_router.u.octs; + match route.via_router.tag { + __WASI_ADDRESS_FAMILY_INET4 => IpAddr::V4(Ipv4Addr::new(o[0], o[1], o[2], o[3])), + __WASI_ADDRESS_FAMILY_INET6 => { + let [a, b, c, d, e, f, g, h] = unsafe { transmute::<_, [u16; 8]>(o) }; + IpAddr::V6(Ipv6Addr::new(a, b, c, d, e, f, g, h)) + } + _ => return Err(__WASI_EINVAL), + } + }, + preferred_until: match route.preferred_until.tag { + __WASI_OPTION_NONE => None, + __WASI_OPTION_SOME => Some(Duration::from_nanos(route.preferred_until.u)), + _ => return Err(__WASI_EINVAL), + }, + expires_at: match route.expires_at.tag { + __WASI_OPTION_NONE => None, + __WASI_OPTION_SOME => Some(Duration::from_nanos(route.expires_at.u)), + _ => return Err(__WASI_EINVAL), + }, + }) +} + +pub(crate) fn write_route( + memory: &Memory, + ptr: WasmPtr<__wasi_route_t, M>, + route: IpRoute, +) -> Result<(), __wasi_errno_t> { + let cidr = { + let p = route.cidr.prefix; + match route.cidr.ip { + IpAddr::V4(ip) => { + let o = ip.octets(); + __wasi_cidr_t { + tag: __WASI_ADDRESS_FAMILY_INET4, + u: __wasi_cidr_u { + octs: [ + o[0], o[1], o[2], o[3], p, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ], + }, + } + } + IpAddr::V6(ip) => { + let o = ip.octets(); + __wasi_cidr_t { + tag: __WASI_ADDRESS_FAMILY_INET6, + u: __wasi_cidr_u { + octs: [ + o[0], o[1], o[2], o[3], o[4], o[5], o[6], o[7], o[8], o[9], o[10], + o[11], o[12], o[13], o[14], o[15], p, + ], + }, + } + } + } + }; + let via_router = match route.via_router { + IpAddr::V4(ip) => { + let o = ip.octets(); + __wasi_addr_t { + tag: __WASI_ADDRESS_FAMILY_INET4, + u: __wasi_addr_u { + octs: [o[0], o[1], o[2], o[3], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + }, + } + } + IpAddr::V6(ip) => { + let o = ip.octets(); + __wasi_addr_t { + tag: __WASI_ADDRESS_FAMILY_INET6, + u: __wasi_addr_u { octs: o }, + } + } + }; + let preferred_until = match route.preferred_until { + None => __wasi_option_timestamp_t { + tag: __WASI_OPTION_NONE, + u: 0, + }, + Some(u) => __wasi_option_timestamp_t { + tag: __WASI_OPTION_SOME, + u: u.as_nanos() as u64, + }, + }; + let expires_at = match route.expires_at { + None => __wasi_option_timestamp_t { + tag: __WASI_OPTION_NONE, + u: 0, + }, + Some(u) => __wasi_option_timestamp_t { + tag: __WASI_OPTION_SOME, + u: u.as_nanos() as u64, + }, + }; + + let route = __wasi_route_t { + cidr, + via_router, + preferred_until, + expires_at, + }; + + let route_ptr = ptr.deref(memory); + route_ptr.write(route).map_err(crate::mem_error_to_wasi)?; + Ok(()) +} + +pub(crate) fn all_socket_rights() -> __wasi_rights_t { + __WASI_RIGHT_FD_FDSTAT_SET_FLAGS + | __WASI_RIGHT_FD_FILESTAT_GET + | __WASI_RIGHT_FD_READ + | __WASI_RIGHT_FD_WRITE + | __WASI_RIGHT_POLL_FD_READWRITE + | __WASI_RIGHT_SOCK_SHUTDOWN + | __WASI_RIGHT_SOCK_CONNECT + | __WASI_RIGHT_SOCK_LISTEN + | __WASI_RIGHT_SOCK_BIND + | __WASI_RIGHT_SOCK_ACCEPT + | __WASI_RIGHT_SOCK_RECV + | __WASI_RIGHT_SOCK_SEND + | __WASI_RIGHT_SOCK_ADDR_LOCAL + | __WASI_RIGHT_SOCK_ADDR_REMOTE + | __WASI_RIGHT_SOCK_RECV_FROM + | __WASI_RIGHT_SOCK_SEND_TO +} diff --git a/lib/wasi/src/state/types.rs b/lib/wasi/src/state/types.rs index f20e92efcd7..7842f68bc5d 100644 --- a/lib/wasi/src/state/types.rs +++ b/lib/wasi/src/state/types.rs @@ -2,12 +2,15 @@ use crate::syscalls::types::*; #[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; -#[cfg(unix)] +#[cfg(all(unix, feature = "sys-poll"))] use std::convert::TryInto; use std::{ collections::VecDeque, io::{self, Read, Seek, Write}, + sync::{Arc, Mutex}, + time::Duration, }; +use wasmer_vbus::BusError; #[cfg(feature = "host-fs")] pub use wasmer_vfs::host_fs::{Stderr, Stdin, Stdout}; @@ -15,6 +18,7 @@ pub use wasmer_vfs::host_fs::{Stderr, Stdin, Stdout}; pub use wasmer_vfs::mem_fs::{Stderr, Stdin, Stdout}; use wasmer_vfs::{FsError, VirtualFile}; +use wasmer_vnet::NetworkError; pub fn fs_error_from_wasi_err(err: __wasi_errno_t) -> FsError { match err { @@ -71,6 +75,83 @@ pub fn fs_error_into_wasi_err(fs_error: FsError) -> __wasi_errno_t { } } +pub fn net_error_into_wasi_err(net_error: NetworkError) -> __wasi_errno_t { + match net_error { + NetworkError::InvalidFd => __WASI_EBADF, + NetworkError::AlreadyExists => __WASI_EEXIST, + NetworkError::Lock => __WASI_EIO, + NetworkError::IOError => __WASI_EIO, + NetworkError::AddressInUse => __WASI_EADDRINUSE, + NetworkError::AddressNotAvailable => __WASI_EADDRNOTAVAIL, + NetworkError::BrokenPipe => __WASI_EPIPE, + NetworkError::ConnectionAborted => __WASI_ECONNABORTED, + NetworkError::ConnectionRefused => __WASI_ECONNREFUSED, + NetworkError::ConnectionReset => __WASI_ECONNRESET, + NetworkError::Interrupted => __WASI_EINTR, + NetworkError::InvalidData => __WASI_EIO, + NetworkError::InvalidInput => __WASI_EINVAL, + NetworkError::NotConnected => __WASI_ENOTCONN, + NetworkError::NoDevice => __WASI_ENODEV, + NetworkError::PermissionDenied => __WASI_EPERM, + NetworkError::TimedOut => __WASI_ETIMEDOUT, + NetworkError::UnexpectedEof => __WASI_EPROTO, + NetworkError::WouldBlock => __WASI_EAGAIN, + NetworkError::WriteZero => __WASI_ENOSPC, + NetworkError::Unsupported => __WASI_ENOTSUP, + NetworkError::UnknownError => __WASI_EIO, + } +} + +pub fn bus_error_into_wasi_err(bus_error: BusError) -> __bus_errno_t { + use BusError::*; + match bus_error { + Serialization => __BUS_ESER, + Deserialization => __BUS_EDES, + InvalidWapm => __BUS_EWAPM, + FetchFailed => __BUS_EFETCH, + CompileError => __BUS_ECOMPILE, + InvalidABI => __BUS_EABI, + Aborted => __BUS_EABORTED, + BadHandle => __BUS_EBADHANDLE, + InvalidTopic => __BUS_ETOPIC, + BadCallback => __BUS_EBADCB, + Unsupported => __BUS_EUNSUPPORTED, + BadRequest => __BUS_EBADREQUEST, + AccessDenied => __BUS_EDENIED, + InternalError => __BUS_EINTERNAL, + MemoryAllocationFailed => __BUS_EALLOC, + InvokeFailed => __BUS_EINVOKE, + AlreadyConsumed => __BUS_ECONSUMED, + MemoryAccessViolation => __BUS_EMEMVIOLATION, + UnknownError => __BUS_EUNKNOWN, + } +} + +pub fn wasi_error_into_bus_err(bus_error: __bus_errno_t) -> BusError { + use BusError::*; + match bus_error { + __BUS_ESER => Serialization, + __BUS_EDES => Deserialization, + __BUS_EWAPM => InvalidWapm, + __BUS_EFETCH => FetchFailed, + __BUS_ECOMPILE => CompileError, + __BUS_EABI => InvalidABI, + __BUS_EABORTED => Aborted, + __BUS_EBADHANDLE => BadHandle, + __BUS_ETOPIC => InvalidTopic, + __BUS_EBADCB => BadCallback, + __BUS_EUNSUPPORTED => Unsupported, + __BUS_EBADREQUEST => BadRequest, + __BUS_EDENIED => AccessDenied, + __BUS_EINTERNAL => InternalError, + __BUS_EALLOC => MemoryAllocationFailed, + __BUS_EINVOKE => InvokeFailed, + __BUS_ECONSUMED => AlreadyConsumed, + __BUS_EMEMVIOLATION => MemoryAccessViolation, + /*__BUS_EUNKNOWN |*/ _ => UnknownError, + } +} + #[derive(Debug, Clone)] #[allow(clippy::enum_variant_names)] pub enum PollEvent { @@ -137,7 +218,7 @@ pub fn iterate_poll_events(pes: PollEventSet) -> PollEventIter { PollEventIter { pes, i: 0 } } -#[cfg(unix)] +#[cfg(all(unix, feature = "sys-poll"))] fn poll_event_set_to_platform_poll_events(mut pes: PollEventSet) -> i16 { let mut out = 0; for i in 0..16 { @@ -154,7 +235,7 @@ fn poll_event_set_to_platform_poll_events(mut pes: PollEventSet) -> i16 { out } -#[cfg(unix)] +#[cfg(all(unix, feature = "sys-poll"))] fn platform_poll_events_to_pollevent_set(mut num: i16) -> PollEventSet { let mut peb = PollEventBuilder::new(); for i in 0..16 { @@ -171,6 +252,7 @@ fn platform_poll_events_to_pollevent_set(mut num: i16) -> PollEventSet { peb.build() } +#[allow(dead_code)] impl PollEventBuilder { pub fn new() -> PollEventBuilder { PollEventBuilder { inner: 0 } @@ -186,11 +268,12 @@ impl PollEventBuilder { } } -#[cfg(unix)] +#[cfg(all(unix, feature = "sys-poll"))] pub(crate) fn poll( - selfs: &[&dyn VirtualFile], + selfs: &[&(dyn VirtualFile + Send + Sync + 'static)], events: &[PollEventSet], seen_events: &mut [PollEventSet], + timeout: Duration, ) -> Result { if !(selfs.len() == events.len() && events.len() == seen_events.len()) { return Err(FsError::InvalidInput); @@ -205,7 +288,13 @@ pub(crate) fn poll( revents: 0, }) .collect::>(); - let result = unsafe { libc::poll(fds.as_mut_ptr(), selfs.len() as _, 1) }; + let result = unsafe { + libc::poll( + fds.as_mut_ptr(), + selfs.len() as _, + timeout.as_millis() as i32, + ) + }; if result < 0 { // TODO: check errno and return value @@ -219,22 +308,78 @@ pub(crate) fn poll( Ok(result.try_into().unwrap()) } -#[cfg(not(unix))] +#[cfg(any(not(unix), not(feature = "sys-poll")))] pub(crate) fn poll( - _selfs: &[&dyn VirtualFile], - _events: &[PollEventSet], - _seen_events: &mut [PollEventSet], -) -> Result<(), FsError> { - unimplemented!("VirtualFile::poll is not implemented for non-Unix-like targets yet"); + files: &[&(dyn VirtualFile + Send + Sync + 'static)], + events: &[PollEventSet], + seen_events: &mut [PollEventSet], + timeout: Duration, +) -> Result { + if !(files.len() == events.len() && events.len() == seen_events.len()) { + tracing::debug!("the slice length of 'files', 'events' and 'seen_events' must be the same (files={}, events={}, seen_events={})", files.len(), events.len(), seen_events.len()); + return Err(FsError::InvalidInput); + } + + let mut ret = 0; + for n in 0..files.len() { + let mut builder = PollEventBuilder::new(); + + let file = files[n]; + let can_read = file.bytes_available_read()?.map(|_| true).unwrap_or(false); + let can_write = file + .bytes_available_write()? + .map(|s| s > 0) + .unwrap_or(false); + let is_closed = file.is_open() == false; + + tracing::debug!( + "poll_evt can_read={} can_write={} is_closed={}", + can_read, + can_write, + is_closed + ); + + for event in iterate_poll_events(events[n]) { + match event { + PollEvent::PollIn if can_read => { + builder = builder.add(PollEvent::PollIn); + } + PollEvent::PollOut if can_write => { + builder = builder.add(PollEvent::PollOut); + } + PollEvent::PollHangUp if is_closed => { + builder = builder.add(PollEvent::PollHangUp); + } + PollEvent::PollInvalid if is_closed => { + builder = builder.add(PollEvent::PollInvalid); + } + PollEvent::PollError if is_closed => { + builder = builder.add(PollEvent::PollError); + } + _ => {} + } + } + let revents = builder.build(); + if revents != 0 { + ret += 1; + } + seen_events[n] = revents; + } + + if ret == 0 && timeout > Duration::ZERO { + return Err(FsError::WouldBlock); + } + + Ok(ret) } pub trait WasiPath {} /// For piping stdio. Stores all output / input in a byte-vector. -#[derive(Debug, Default)] +#[derive(Debug, Clone, Default)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] pub struct Pipe { - buffer: VecDeque, + buffer: Arc>>, } impl Pipe { @@ -245,8 +390,9 @@ impl Pipe { impl Read for Pipe { fn read(&mut self, buf: &mut [u8]) -> io::Result { - let amt = std::cmp::min(buf.len(), self.buffer.len()); - for (i, byte) in self.buffer.drain(..amt).enumerate() { + let mut buffer = self.buffer.lock().unwrap(); + let amt = std::cmp::min(buf.len(), buffer.len()); + for (i, byte) in buffer.drain(..amt).enumerate() { buf[i] = byte; } Ok(amt) @@ -255,7 +401,8 @@ impl Read for Pipe { impl Write for Pipe { fn write(&mut self, buf: &[u8]) -> io::Result { - self.buffer.extend(buf); + let mut buffer = self.buffer.lock().unwrap(); + buffer.extend(buf); Ok(buf.len()) } fn flush(&mut self) -> io::Result<()> { @@ -284,17 +431,20 @@ impl VirtualFile for Pipe { 0 } fn size(&self) -> u64 { - self.buffer.len() as u64 + let buffer = self.buffer.lock().unwrap(); + buffer.len() as u64 } fn set_len(&mut self, len: u64) -> Result<(), FsError> { - self.buffer.resize(len as usize, 0); + let mut buffer = self.buffer.lock().unwrap(); + buffer.resize(len as usize, 0); Ok(()) } fn unlink(&mut self) -> Result<(), FsError> { Ok(()) } - fn bytes_available(&self) -> Result { - Ok(self.buffer.len()) + fn bytes_available_read(&self) -> Result, FsError> { + let buffer = self.buffer.lock().unwrap(); + Ok(Some(buffer.len())) } } diff --git a/lib/wasi/src/syscalls/legacy/snapshot0.rs b/lib/wasi/src/syscalls/legacy/snapshot0.rs index 1ed33a2989e..bf15b211966 100644 --- a/lib/wasi/src/syscalls/legacy/snapshot0.rs +++ b/lib/wasi/src/syscalls/legacy/snapshot0.rs @@ -1,6 +1,6 @@ use crate::syscalls; use crate::syscalls::types::{self, snapshot0}; -use crate::{mem_error_to_wasi, WasiEnv}; +use crate::{mem_error_to_wasi, Memory32, MemorySize, WasiEnv, WasiError, WasiThread}; use wasmer::WasmPtr; /// Wrapper around `syscalls::fd_filestat_get` with extra logic to handle the size @@ -12,21 +12,21 @@ use wasmer::WasmPtr; pub fn fd_filestat_get( env: &WasiEnv, fd: types::__wasi_fd_t, - buf: WasmPtr, + buf: WasmPtr, ) -> types::__wasi_errno_t { let memory = env.memory(); // transmute the WasmPtr into a WasmPtr where T2 > T1, this will read extra memory. // The edge case of this cenv.mausing an OOB is not handled, if the new field is OOB, then the entire // memory access will fail. - let new_buf: WasmPtr = buf.cast(); + let new_buf: WasmPtr = buf.cast(); // Copy the data including the extra data let new_filestat_setup: types::__wasi_filestat_t = wasi_try_mem!(new_buf.read(memory)); // Set up complete, make the call with the pointer that will write to the // struct and some unrelated memory after the struct. - let result = syscalls::fd_filestat_get(env, fd, new_buf); + let result = syscalls::fd_filestat_get::(env, fd, new_buf); // reborrow memory let memory = env.memory(); @@ -62,17 +62,17 @@ pub fn path_filestat_get( env: &WasiEnv, fd: types::__wasi_fd_t, flags: types::__wasi_lookupflags_t, - path: WasmPtr, + path: WasmPtr, path_len: u32, - buf: WasmPtr, + buf: WasmPtr, ) -> types::__wasi_errno_t { // see `fd_filestat_get` in this file for an explanation of this strange behavior let memory = env.memory(); - let new_buf: WasmPtr = buf.cast(); + let new_buf: WasmPtr = buf.cast(); let new_filestat_setup: types::__wasi_filestat_t = wasi_try_mem!(new_buf.read(memory)); - let result = syscalls::path_filestat_get(env, fd, flags, path, path_len, new_buf); + let result = syscalls::path_filestat_get::(env, fd, flags, path, path_len, new_buf); let memory = env.memory(); let new_filestat = wasi_try_mem!(new_buf.deref(memory).read()); @@ -100,8 +100,8 @@ pub fn fd_seek( fd: types::__wasi_fd_t, offset: types::__wasi_filedelta_t, whence: snapshot0::__wasi_whence_t, - newoffset: WasmPtr, -) -> types::__wasi_errno_t { + newoffset: WasmPtr, +) -> Result { let new_whence = match whence { snapshot0::__WASI_WHENCE_CUR => types::__WASI_WHENCE_CUR, snapshot0::__WASI_WHENCE_END => types::__WASI_WHENCE_END, @@ -109,34 +109,35 @@ pub fn fd_seek( // if it's invalid, let the new fd_seek handle it _ => whence, }; - syscalls::fd_seek(env, fd, offset, new_whence, newoffset) + syscalls::fd_seek::(env, fd, offset, new_whence, newoffset) } /// Wrapper around `syscalls::poll_oneoff` with extra logic to add the removed /// userdata field back pub fn poll_oneoff( env: &WasiEnv, - in_: WasmPtr, - out_: WasmPtr, + in_: WasmPtr, + out_: WasmPtr, nsubscriptions: u32, - nevents: WasmPtr, -) -> types::__wasi_errno_t { + nevents: WasmPtr, +) -> Result { // in this case the new type is smaller than the old type, so it all fits into memory, // we just need to readjust and copy it // we start by adjusting `in_` into a format that the new code can understand let memory = env.memory(); - let in_origs = wasi_try_mem!(in_.slice(memory, nsubscriptions)); - let in_origs = wasi_try_mem!(in_origs.read_to_vec()); + let nsubscriptions_offset: u32 = nsubscriptions; + let in_origs = wasi_try_mem_ok!(in_.slice(memory, nsubscriptions_offset)); + let in_origs = wasi_try_mem_ok!(in_origs.read_to_vec()); // get a pointer to the smaller new type - let in_new_type_ptr: WasmPtr = in_.cast(); + let in_new_type_ptr: WasmPtr = in_.cast(); - for (in_sub_new, orig) in wasi_try_mem!(in_new_type_ptr.slice(memory, nsubscriptions)) + for (in_sub_new, orig) in wasi_try_mem_ok!(in_new_type_ptr.slice(memory, nsubscriptions_offset)) .iter() .zip(in_origs.iter()) { - wasi_try_mem!(in_sub_new.write(types::__wasi_subscription_t { + wasi_try_mem_ok!(in_sub_new.write(types::__wasi_subscription_t { userdata: orig.userdata, type_: orig.type_, u: if orig.type_ == types::__WASI_EVENTTYPE_CLOCK { @@ -157,16 +158,17 @@ pub fn poll_oneoff( } // make the call - let result = syscalls::poll_oneoff(env, in_new_type_ptr, out_, nsubscriptions, nevents); + let result = + syscalls::poll_oneoff::(env, in_new_type_ptr, out_, nsubscriptions, nevents); // replace the old values of in, in case the calling code reuses the memory let memory = env.memory(); - for (in_sub, orig) in wasi_try_mem!(in_.slice(memory, nsubscriptions)) + for (in_sub, orig) in wasi_try_mem_ok!(in_.slice(memory, nsubscriptions_offset)) .iter() .zip(in_origs.into_iter()) { - wasi_try_mem!(in_sub.write(orig)); + wasi_try_mem_ok!(in_sub.write(orig)); } result diff --git a/lib/wasi/src/syscalls/mod.rs b/lib/wasi/src/syscalls/mod.rs index fa87402614f..54812ad292b 100644 --- a/lib/wasi/src/syscalls/mod.rs +++ b/lib/wasi/src/syscalls/mod.rs @@ -17,23 +17,39 @@ pub mod wasm32; pub mod windows; pub mod legacy; +pub mod wasi; +pub mod wasix32; +pub mod wasix64; use self::types::*; +use crate::state::{bus_error_into_wasi_err, wasi_error_into_bus_err, InodeHttpSocketType}; +use crate::utils::map_io_err; +use crate::WasiBusProcessId; use crate::{ mem_error_to_wasi, state::{ - self, fs_error_into_wasi_err, iterate_poll_events, poll, - virtual_file_type_to_wasi_file_type, Fd, Inode, InodeVal, Kind, PollEvent, - PollEventBuilder, WasiState, MAX_SYMLINKS, + self, fs_error_into_wasi_err, iterate_poll_events, net_error_into_wasi_err, poll, + virtual_file_type_to_wasi_file_type, Fd, Inode, InodeSocket, InodeSocketKind, InodeVal, + Kind, PollEvent, PollEventBuilder, WasiPipe, WasiState, MAX_SYMLINKS, }, - WasiEnv, WasiError, + WasiEnv, WasiError, WasiThread, WasiThreadId, }; -use std::borrow::Borrow; +use bytes::Bytes; +use std::borrow::{Borrow, Cow}; use std::convert::{Infallible, TryInto}; use std::io::{self, Read, Seek, Write}; -use tracing::{debug, trace}; -use wasmer::{Memory, RuntimeError, Value, WasmPtr, WasmSlice}; +use std::mem::transmute; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; +use std::ops::{Deref, DerefMut}; +use std::sync::atomic::AtomicU64; +use std::sync::{atomic::Ordering, Mutex}; +use std::sync::{mpsc, Arc}; +use std::time::Duration; +use tracing::{debug, error, trace, warn}; +use wasmer::{Memory, Memory32, Memory64, MemorySize, RuntimeError, Value, WasmPtr, WasmSlice}; +use wasmer_vbus::{FileDescriptor, StdioMode}; use wasmer_vfs::{FsError, VirtualFile}; +use wasmer_vnet::{SocketHttpRequest, StreamSecurity}; #[cfg(any( target_os = "freebsd", @@ -49,42 +65,51 @@ pub use windows::*; #[cfg(any(target_arch = "wasm32"))] pub use wasm32::*; -fn write_bytes_inner( +fn to_offset(offset: usize) -> Result { + let ret: M::Offset = offset.try_into().map_err(|_| __WASI_EINVAL)?; + Ok(ret) +} + +fn from_offset(offset: M::Offset) -> Result { + let ret: usize = offset.try_into().map_err(|_| __WASI_EINVAL)?; + Ok(ret) +} + +fn write_bytes_inner( mut write_loc: T, memory: &Memory, - iovs_arr_cell: WasmSlice<__wasi_ciovec_t>, -) -> Result { - let mut bytes_written = 0; + iovs_arr_cell: WasmSlice<__wasi_ciovec_t>, +) -> Result { + let mut bytes_written = 0usize; for iov in iovs_arr_cell.iter() { let iov_inner = iov.read().map_err(mem_error_to_wasi)?; - let bytes = WasmPtr::::new(iov_inner.buf) + let bytes = WasmPtr::::new(iov_inner.buf) .slice(memory, iov_inner.buf_len) .map_err(mem_error_to_wasi)?; let bytes = bytes.read_to_vec().map_err(mem_error_to_wasi)?; - write_loc.write_all(&bytes).map_err(|_| __WASI_EIO)?; + write_loc.write_all(&bytes).map_err(map_io_err)?; - // TODO: handle failure more accurately - bytes_written += iov_inner.buf_len; + bytes_written += from_offset::(iov_inner.buf_len)?; } Ok(bytes_written) } -fn write_bytes( +pub(crate) fn write_bytes( mut write_loc: T, memory: &Memory, - iovs_arr: WasmSlice<__wasi_ciovec_t>, -) -> Result { - let result = write_bytes_inner(&mut write_loc, memory, iovs_arr); + iovs_arr: WasmSlice<__wasi_ciovec_t>, +) -> Result { + let result = write_bytes_inner::<_, M>(&mut write_loc, memory, iovs_arr); write_loc.flush(); result } -fn read_bytes( +pub(crate) fn read_bytes( mut reader: T, memory: &Memory, - iovs_arr: WasmSlice<__wasi_iovec_t>, -) -> Result { - let mut bytes_read = 0; + iovs_arr: WasmSlice<__wasi_iovec_t>, +) -> Result { + let mut bytes_read = 0usize; // We allocate the raw_bytes first once instead of // N times in the loop. @@ -93,10 +118,10 @@ fn read_bytes( for iov in iovs_arr.iter() { let iov_inner = iov.read().map_err(mem_error_to_wasi)?; raw_bytes.clear(); - raw_bytes.resize(iov_inner.buf_len as usize, 0); - bytes_read += reader.read(&mut raw_bytes).map_err(|_| __WASI_EIO)? as u32; + raw_bytes.resize(from_offset::(iov_inner.buf_len)?, 0); + bytes_read += reader.read(&mut raw_bytes).map_err(map_io_err)?; - let buf = WasmPtr::::new(iov_inner.buf) + let buf = WasmPtr::::new(iov_inner.buf) .slice(memory, iov_inner.buf_len) .map_err(mem_error_to_wasi)?; buf.write_slice(&raw_bytes).map_err(mem_error_to_wasi)?; @@ -109,26 +134,134 @@ fn has_rights(rights_set: __wasi_rights_t, rights_check_set: __wasi_rights_t) -> rights_set | rights_check_set == rights_set } +fn __sock_actor( + env: &WasiEnv, + sock: __wasi_fd_t, + rights: __wasi_rights_t, + actor: F, +) -> Result +where + F: FnOnce(&crate::state::InodeSocket) -> Result, +{ + let (_, state, inodes) = env.get_memory_and_wasi_state_and_inodes(0); + + let fd_entry = state.fs.get_fd(sock)?; + let ret = { + if rights != 0 && !has_rights(fd_entry.rights, rights) { + return Err(__WASI_EACCES); + } + + let inode_idx = fd_entry.inode; + let inode = &inodes.arena[inode_idx]; + + let mut guard = inode.read(); + match guard.deref() { + Kind::Socket { socket } => actor(socket)?, + _ => { + return Err(__WASI_ENOTSOCK); + } + } + }; + + Ok(ret) +} + +fn __sock_actor_mut( + env: &WasiEnv, + sock: __wasi_fd_t, + rights: __wasi_rights_t, + actor: F, +) -> Result +where + F: FnOnce(&mut crate::state::InodeSocket) -> Result, +{ + let (_, state, inodes) = env.get_memory_and_wasi_state_and_inodes(0); + + let fd_entry = state.fs.get_fd(sock)?; + let ret = { + if rights != 0 && !has_rights(fd_entry.rights, rights) { + return Err(__WASI_EACCES); + } + + let inode_idx = fd_entry.inode; + let inode = &inodes.arena[inode_idx]; + + let mut guard = inode.write(); + match guard.deref_mut() { + Kind::Socket { socket } => actor(socket)?, + _ => { + return Err(__WASI_ENOTSOCK); + } + } + }; + + Ok(ret) +} + +fn __sock_upgrade( + env: &WasiEnv, + sock: __wasi_fd_t, + rights: __wasi_rights_t, + actor: F, +) -> Result<(), __wasi_errno_t> +where + F: FnOnce( + &mut crate::state::InodeSocket, + ) -> Result, __wasi_errno_t>, +{ + let (_, state, inodes) = env.get_memory_and_wasi_state_and_inodes(0); + + let fd_entry = state.fs.get_fd(sock)?; + if rights != 0 && !has_rights(fd_entry.rights, rights) { + return Err(__WASI_EACCES); + } + + let inode_idx = fd_entry.inode; + let inode = &inodes.arena[inode_idx]; + + let mut guard = inode.write(); + match guard.deref_mut() { + Kind::Socket { socket } => { + let new_socket = actor(socket)?; + + if let Some(mut new_socket) = new_socket { + std::mem::swap(socket, &mut new_socket); + } + } + _ => { + return Err(__WASI_ENOTSOCK); + } + } + + Ok(()) +} + #[must_use] -fn write_buffer_array( +fn write_buffer_array( memory: &Memory, from: &[Vec], - ptr_buffer: WasmPtr>, - buffer: WasmPtr, + ptr_buffer: WasmPtr, M>, + buffer: WasmPtr, ) -> __wasi_errno_t { - let ptrs = wasi_try_mem!(ptr_buffer.slice(memory, from.len() as u32)); + let ptrs = wasi_try_mem!(ptr_buffer.slice(memory, wasi_try!(to_offset::(from.len())))); - let mut current_buffer_offset = 0; + let mut current_buffer_offset = 0usize; for ((i, sub_buffer), ptr) in from.iter().enumerate().zip(ptrs.iter()) { - debug!("ptr: {:?}, subbuffer: {:?}", ptr, sub_buffer); - let new_ptr = WasmPtr::new(buffer.offset() + current_buffer_offset); + trace!("ptr: {:?}, subbuffer: {:?}", ptr, sub_buffer); + let mut buf_offset = buffer.offset(); + buf_offset += wasi_try!(to_offset::(current_buffer_offset)); + let new_ptr = WasmPtr::new(buf_offset); wasi_try_mem!(ptr.write(new_ptr)); - let data = wasi_try_mem!(new_ptr.slice(memory, sub_buffer.len() as u32)); + let data = + wasi_try_mem!(new_ptr.slice(memory, wasi_try!(to_offset::(sub_buffer.len())))); wasi_try_mem!(data.write_slice(sub_buffer)); - wasi_try_mem!(wasi_try_mem!(new_ptr.add_offset(sub_buffer.len() as u32)).write(memory, 0)); + wasi_try_mem!(wasi_try_mem!( + new_ptr.add_offset(wasi_try!(to_offset::(sub_buffer.len()))) + ) + .write(memory, 0)); - current_buffer_offset += sub_buffer.len() as u32 + 1; + current_buffer_offset += sub_buffer.len() + 1; } __WASI_ESUCCESS @@ -151,13 +284,13 @@ fn get_current_time_in_nanos() -> Result<__wasi_timestamp_t, __wasi_errno_t> { /// - `char *argv_buf` /// A pointer to a buffer to write the argument string data. /// -pub fn args_get( +pub fn args_get( env: &WasiEnv, - argv: WasmPtr>, - argv_buf: WasmPtr, + argv: WasmPtr, M>, + argv_buf: WasmPtr, ) -> __wasi_errno_t { debug!("wasi::args_get"); - let (memory, mut state) = env.get_memory_and_wasi_state(0); + let (memory, state) = env.get_memory_and_wasi_state(0); let result = write_buffer_array(memory, &*state.args, argv, argv_buf); @@ -182,19 +315,21 @@ pub fn args_get( /// The number of arguments. /// - `size_t *argv_buf_size` /// The size of the argument string data. -pub fn args_sizes_get( +pub fn args_sizes_get( env: &WasiEnv, - argc: WasmPtr, - argv_buf_size: WasmPtr, + argc: WasmPtr, + argv_buf_size: WasmPtr, ) -> __wasi_errno_t { debug!("wasi::args_sizes_get"); - let (memory, mut state) = env.get_memory_and_wasi_state(0); + let (memory, state) = env.get_memory_and_wasi_state(0); let argc = argc.deref(memory); let argv_buf_size = argv_buf_size.deref(memory); - let argc_val = state.args.len() as u32; - let argv_buf_size_val = state.args.iter().map(|v| v.len() as u32 + 1).sum(); + let argc_val: M::Offset = wasi_try!(state.args.len().try_into().map_err(|_| __WASI_EOVERFLOW)); + let argv_buf_size_val: usize = state.args.iter().map(|v| v.len() + 1).sum(); + let argv_buf_size_val: M::Offset = + wasi_try!(argv_buf_size_val.try_into().map_err(|_| __WASI_EOVERFLOW)); wasi_try_mem!(argc.write(argc_val)); wasi_try_mem!(argv_buf_size.write(argv_buf_size_val)); @@ -211,16 +346,18 @@ pub fn args_sizes_get( /// Output: /// - `__wasi_timestamp_t *resolution` /// The resolution of the clock in nanoseconds -pub fn clock_res_get( +pub fn clock_res_get( env: &WasiEnv, clock_id: __wasi_clockid_t, - resolution: WasmPtr<__wasi_timestamp_t>, + resolution: WasmPtr<__wasi_timestamp_t, M>, ) -> __wasi_errno_t { - debug!("wasi::clock_res_get"); + trace!("wasi::clock_res_get"); let memory = env.memory(); let out_addr = resolution.deref(memory); - platform_clock_res_get(clock_id, out_addr) + let t_out = wasi_try!(platform_clock_res_get(clock_id, out_addr)); + wasi_try_mem!(resolution.write(memory, t_out as __wasi_timestamp_t)); + __WASI_ESUCCESS } /// ### `clock_time_get()` @@ -233,11 +370,11 @@ pub fn clock_res_get( /// Output: /// - `__wasi_timestamp_t *time` /// The value of the clock in nanoseconds -pub fn clock_time_get( +pub fn clock_time_get( env: &WasiEnv, clock_id: __wasi_clockid_t, precision: __wasi_timestamp_t, - time: WasmPtr<__wasi_timestamp_t>, + time: WasmPtr<__wasi_timestamp_t, M>, ) -> __wasi_errno_t { debug!( "wasi::clock_time_get clock_id: {}, precision: {}", @@ -245,9 +382,11 @@ pub fn clock_time_get( ); let memory = env.memory(); - let out_addr = time.deref(memory); - let result = platform_clock_time_get(clock_id, precision, out_addr); - debug!( + let t_out = wasi_try!(platform_clock_time_get(clock_id, precision)); + wasi_try_mem!(time.write(memory, t_out as __wasi_timestamp_t)); + + let result = __WASI_ESUCCESS; + trace!( "time: {} => {}", wasi_try_mem!(time.deref(memory).read()), result @@ -263,17 +402,17 @@ pub fn clock_time_get( /// A pointer to a buffer to write the environment variable pointers. /// - `char *environ_buf` /// A pointer to a buffer to write the environment variable string data. -pub fn environ_get( +pub fn environ_get( env: &WasiEnv, - environ: WasmPtr>, - environ_buf: WasmPtr, + environ: WasmPtr, M>, + environ_buf: WasmPtr, ) -> __wasi_errno_t { debug!( "wasi::environ_get. Environ: {:?}, environ_buf: {:?}", environ, environ_buf ); - let (memory, mut state) = env.get_memory_and_wasi_state(0); - debug!(" -> State envs: {:?}", state.envs); + let (memory, state) = env.get_memory_and_wasi_state(0); + trace!(" -> State envs: {:?}", state.envs); write_buffer_array(memory, &*state.envs, environ, environ_buf) } @@ -285,25 +424,28 @@ pub fn environ_get( /// The number of environment variables. /// - `size_t *environ_buf_size` /// The size of the environment variable string data. -pub fn environ_sizes_get( +pub fn environ_sizes_get( env: &WasiEnv, - environ_count: WasmPtr, - environ_buf_size: WasmPtr, + environ_count: WasmPtr, + environ_buf_size: WasmPtr, ) -> __wasi_errno_t { - debug!("wasi::environ_sizes_get"); - let (memory, mut state) = env.get_memory_and_wasi_state(0); + trace!("wasi::environ_sizes_get"); + let (memory, state) = env.get_memory_and_wasi_state(0); let environ_count = environ_count.deref(memory); let environ_buf_size = environ_buf_size.deref(memory); - let env_var_count = state.envs.len() as u32; - let env_buf_size = state.envs.iter().map(|v| v.len() as u32 + 1).sum(); + let env_var_count: M::Offset = + wasi_try!(state.envs.len().try_into().map_err(|_| __WASI_EOVERFLOW)); + let env_buf_size: usize = state.envs.iter().map(|v| v.len() + 1).sum(); + let env_buf_size: M::Offset = wasi_try!(env_buf_size.try_into().map_err(|_| __WASI_EOVERFLOW)); wasi_try_mem!(environ_count.write(env_var_count)); wasi_try_mem!(environ_buf_size.write(env_buf_size)); - debug!( + trace!( "env_var_count: {}, env_buf_size: {}", - env_var_count, env_buf_size + env_var_count, + env_buf_size ); __WASI_ESUCCESS @@ -350,30 +492,35 @@ pub fn fd_allocate( len: __wasi_filesize_t, ) -> __wasi_errno_t { debug!("wasi::fd_allocate"); - let (memory, mut state) = env.get_memory_and_wasi_state(0); + let (_, state, inodes) = env.get_memory_and_wasi_state_and_inodes(0); let fd_entry = wasi_try!(state.fs.get_fd(fd)); let inode = fd_entry.inode; if !has_rights(fd_entry.rights, __WASI_RIGHT_FD_ALLOCATE) { return __WASI_EACCES; } - let new_size = wasi_try!(offset.checked_add(len), __WASI_EINVAL); - - match &mut state.fs.inodes[inode].kind { - Kind::File { handle, .. } => { - if let Some(handle) = handle { - wasi_try!(handle.set_len(new_size).map_err(fs_error_into_wasi_err)); - } else { - return __WASI_EBADF; + let new_size = wasi_try!(offset.checked_add(len).ok_or(__WASI_EINVAL)); + { + let mut guard = inodes.arena[inode].write(); + match guard.deref_mut() { + Kind::File { handle, .. } => { + if let Some(handle) = handle { + wasi_try!(handle.set_len(new_size).map_err(fs_error_into_wasi_err)); + } else { + return __WASI_EBADF; + } } + Kind::Socket { .. } => return __WASI_EBADF, + Kind::Pipe { .. } => return __WASI_EBADF, + Kind::Buffer { buffer } => { + buffer.resize(new_size as usize, 0); + } + Kind::Symlink { .. } => return __WASI_EBADF, + Kind::EventNotifications { .. } => return __WASI_EBADF, + Kind::Dir { .. } | Kind::Root { .. } => return __WASI_EISDIR, } - Kind::Buffer { buffer } => { - buffer.resize(new_size as usize, 0); - } - Kind::Symlink { .. } => return __WASI_EBADF, - Kind::Dir { .. } | Kind::Root { .. } => return __WASI_EISDIR, } - state.fs.inodes[inode].stat.st_size = new_size; + inodes.arena[inode].stat.write().unwrap().st_size = new_size; debug!("New file size: {}", new_size); __WASI_ESUCCESS @@ -391,11 +538,11 @@ pub fn fd_allocate( /// If `fd` is invalid or not open pub fn fd_close(env: &WasiEnv, fd: __wasi_fd_t) -> __wasi_errno_t { debug!("wasi::fd_close: fd={}", fd); - let (memory, mut state) = env.get_memory_and_wasi_state(0); + let (_, state, inodes) = env.get_memory_and_wasi_state_and_inodes(0); let fd_entry = wasi_try!(state.fs.get_fd(fd)); - wasi_try!(state.fs.close_fd(fd)); + wasi_try!(state.fs.close_fd(inodes.deref(), fd)); __WASI_ESUCCESS } @@ -407,13 +554,13 @@ pub fn fd_close(env: &WasiEnv, fd: __wasi_fd_t) -> __wasi_errno_t { /// The file descriptor to sync pub fn fd_datasync(env: &WasiEnv, fd: __wasi_fd_t) -> __wasi_errno_t { debug!("wasi::fd_datasync"); - let (memory, mut state) = env.get_memory_and_wasi_state(0); + let (_, state, inodes) = env.get_memory_and_wasi_state_and_inodes(0); let fd_entry = wasi_try!(state.fs.get_fd(fd)); if !has_rights(fd_entry.rights, __WASI_RIGHT_FD_DATASYNC) { return __WASI_EACCES; } - if let Err(e) = state.fs.flush(fd) { + if let Err(e) = state.fs.flush(inodes.deref(), fd) { e } else { __WASI_ESUCCESS @@ -428,23 +575,20 @@ pub fn fd_datasync(env: &WasiEnv, fd: __wasi_fd_t) -> __wasi_errno_t { /// Output: /// - `__wasi_fdstat_t *buf` /// The location where the metadata will be written -pub fn fd_fdstat_get( +pub fn fd_fdstat_get( env: &WasiEnv, fd: __wasi_fd_t, - buf_ptr: WasmPtr<__wasi_fdstat_t>, + buf_ptr: WasmPtr<__wasi_fdstat_t, M>, ) -> __wasi_errno_t { debug!( "wasi::fd_fdstat_get: fd={}, buf_ptr={}", fd, buf_ptr.offset() ); - let (memory, mut state) = env.get_memory_and_wasi_state(0); - let fd_entry = wasi_try!(state.fs.get_fd(fd)); + let (memory, state, inodes) = env.get_memory_and_wasi_state_and_inodes(0); + let stat = wasi_try!(state.fs.fdstat(inodes.deref(), fd)); - let stat = wasi_try!(state.fs.fdstat(fd)); - let buf = buf_ptr.deref(memory); - - wasi_try_mem!(buf.write(stat)); + wasi_try_mem!(buf_ptr.write(memory, stat)); __WASI_ESUCCESS } @@ -462,8 +606,9 @@ pub fn fd_fdstat_set_flags( flags: __wasi_fdflags_t, ) -> __wasi_errno_t { debug!("wasi::fd_fdstat_set_flags"); - let (memory, mut state) = env.get_memory_and_wasi_state(0); - let fd_entry = wasi_try!(state.fs.fd_map.get_mut(&fd).ok_or(__WASI_EBADF)); + let (_, state) = env.get_memory_and_wasi_state(0); + let mut fd_map = state.fs.fd_map.write().unwrap(); + let fd_entry = wasi_try!(fd_map.get_mut(&fd).ok_or(__WASI_EBADF)); if !has_rights(fd_entry.rights, __WASI_RIGHT_FD_FDSTAT_SET_FLAGS) { return __WASI_EACCES; @@ -489,8 +634,9 @@ pub fn fd_fdstat_set_rights( fs_rights_inheriting: __wasi_rights_t, ) -> __wasi_errno_t { debug!("wasi::fd_fdstat_set_rights"); - let (memory, mut state) = env.get_memory_and_wasi_state(0); - let fd_entry = wasi_try!(state.fs.fd_map.get_mut(&fd).ok_or(__WASI_EBADF)); + let (_, state) = env.get_memory_and_wasi_state(0); + let mut fd_map = state.fs.fd_map.write().unwrap(); + let fd_entry = wasi_try!(fd_map.get_mut(&fd).ok_or(__WASI_EBADF)); // ensure new rights are a subset of current rights if fd_entry.rights | fs_rights_base != fd_entry.rights @@ -513,22 +659,21 @@ pub fn fd_fdstat_set_rights( /// Output: /// - `__wasi_filestat_t *buf` /// Where the metadata from `fd` will be written -pub fn fd_filestat_get( +pub fn fd_filestat_get( env: &WasiEnv, fd: __wasi_fd_t, - buf: WasmPtr<__wasi_filestat_t>, + buf: WasmPtr<__wasi_filestat_t, M>, ) -> __wasi_errno_t { debug!("wasi::fd_filestat_get"); - let (memory, mut state) = env.get_memory_and_wasi_state(0); + let (memory, state, inodes) = env.get_memory_and_wasi_state_and_inodes(0); let fd_entry = wasi_try!(state.fs.get_fd(fd)); if !has_rights(fd_entry.rights, __WASI_RIGHT_FD_FILESTAT_GET) { return __WASI_EACCES; } - let stat = wasi_try!(state.fs.filestat_fd(fd)); + let stat = wasi_try!(state.fs.filestat_fd(inodes.deref(), fd)); - let buf = buf.deref(memory); - wasi_try_mem!(buf.write(stat)); + wasi_try_mem!(buf.write(memory, stat)); __WASI_ESUCCESS } @@ -546,7 +691,7 @@ pub fn fd_filestat_set_size( st_size: __wasi_filesize_t, ) -> __wasi_errno_t { debug!("wasi::fd_filestat_set_size"); - let (memory, mut state) = env.get_memory_and_wasi_state(0); + let (_, state, inodes) = env.get_memory_and_wasi_state_and_inodes(0); let fd_entry = wasi_try!(state.fs.get_fd(fd)); let inode = fd_entry.inode; @@ -554,21 +699,27 @@ pub fn fd_filestat_set_size( return __WASI_EACCES; } - match &mut state.fs.inodes[inode].kind { - Kind::File { handle, .. } => { - if let Some(handle) = handle { - wasi_try!(handle.set_len(st_size).map_err(fs_error_into_wasi_err)); - } else { - return __WASI_EBADF; + { + let mut guard = inodes.arena[inode].write(); + match guard.deref_mut() { + Kind::File { handle, .. } => { + if let Some(handle) = handle { + wasi_try!(handle.set_len(st_size).map_err(fs_error_into_wasi_err)); + } else { + return __WASI_EBADF; + } } + Kind::Buffer { buffer } => { + buffer.resize(st_size as usize, 0); + } + Kind::Socket { .. } => return __WASI_EBADF, + Kind::Pipe { .. } => return __WASI_EBADF, + Kind::Symlink { .. } => return __WASI_EBADF, + Kind::EventNotifications { .. } => return __WASI_EBADF, + Kind::Dir { .. } | Kind::Root { .. } => return __WASI_EISDIR, } - Kind::Buffer { buffer } => { - buffer.resize(st_size as usize, 0); - } - Kind::Symlink { .. } => return __WASI_EBADF, - Kind::Dir { .. } | Kind::Root { .. } => return __WASI_EISDIR, } - state.fs.inodes[inode].stat.st_size = st_size; + inodes.arena[inode].stat.write().unwrap().st_size = st_size; __WASI_ESUCCESS } @@ -590,8 +741,8 @@ pub fn fd_filestat_set_times( fst_flags: __wasi_fstflags_t, ) -> __wasi_errno_t { debug!("wasi::fd_filestat_set_times"); - let (memory, mut state) = env.get_memory_and_wasi_state(0); - let fd_entry = wasi_try!(state.fs.fd_map.get_mut(&fd).ok_or(__WASI_EBADF)); + let (_, state, inodes) = env.get_memory_and_wasi_state_and_inodes(0); + let fd_entry = wasi_try!(state.fs.get_fd(fd)); if !has_rights(fd_entry.rights, __WASI_RIGHT_FD_FILESTAT_SET_TIMES) { return __WASI_EACCES; @@ -605,7 +756,7 @@ pub fn fd_filestat_set_times( } let inode_idx = fd_entry.inode; - let inode = &mut state.fs.inodes[inode_idx]; + let inode = &inodes.arena[inode_idx]; if fst_flags & __WASI_FILESTAT_SET_ATIM != 0 || fst_flags & __WASI_FILESTAT_SET_ATIM_NOW != 0 { let time_to_set = if fst_flags & __WASI_FILESTAT_SET_ATIM != 0 { @@ -613,7 +764,7 @@ pub fn fd_filestat_set_times( } else { wasi_try!(get_current_time_in_nanos()) }; - inode.stat.st_atim = time_to_set; + inode.stat.write().unwrap().st_atim = time_to_set; } if fst_flags & __WASI_FILESTAT_SET_MTIM != 0 || fst_flags & __WASI_FILESTAT_SET_MTIM_NOW != 0 { @@ -622,7 +773,7 @@ pub fn fd_filestat_set_times( } else { wasi_try!(get_current_time_in_nanos()) }; - inode.stat.st_mtim = time_to_set; + inode.stat.write().unwrap().st_mtim = time_to_set; } __WASI_ESUCCESS @@ -643,34 +794,38 @@ pub fn fd_filestat_set_times( /// Output: /// - `size_t nread` /// The number of bytes read -pub fn fd_pread( +pub fn fd_pread( env: &WasiEnv, fd: __wasi_fd_t, - iovs: WasmPtr<__wasi_iovec_t>, - iovs_len: u32, + iovs: WasmPtr<__wasi_iovec_t, M>, + iovs_len: M::Offset, offset: __wasi_filesize_t, - nread: WasmPtr, -) -> __wasi_errno_t { - debug!("wasi::fd_pread: fd={}, offset={}", fd, offset); - let (memory, mut state) = env.get_memory_and_wasi_state(0); + nread: WasmPtr, +) -> Result<__wasi_errno_t, WasiError> { + trace!("wasi::fd_pread: fd={}, offset={}", fd, offset); + let (memory, state, inodes) = env.get_memory_and_wasi_state_and_inodes(0); - let iovs = wasi_try_mem!(iovs.slice(memory, iovs_len)); + let iovs = wasi_try_mem_ok!(iovs.slice(memory, iovs_len)); let nread_ref = nread.deref(memory); + let fd_entry = wasi_try_ok!(state.fs.get_fd(fd)); let bytes_read = match fd { __WASI_STDIN_FILENO => { - if let Some(ref mut stdin) = - wasi_try!(state.fs.stdin_mut().map_err(fs_error_into_wasi_err)) - { - wasi_try!(read_bytes(stdin, memory, iovs)) + let mut guard = wasi_try_ok!( + inodes + .stdin_mut(&state.fs.fd_map) + .map_err(fs_error_into_wasi_err), + env + ); + if let Some(ref mut stdin) = guard.deref_mut() { + wasi_try_ok!(read_bytes(stdin, memory, iovs), env) } else { - return __WASI_EBADF; + return Ok(__WASI_EBADF); } } - __WASI_STDOUT_FILENO => return __WASI_EINVAL, - __WASI_STDERR_FILENO => return __WASI_EINVAL, + __WASI_STDOUT_FILENO => return Ok(__WASI_EINVAL), + __WASI_STDERR_FILENO => return Ok(__WASI_EINVAL), _ => { - let fd_entry = wasi_try!(state.fs.fd_map.get_mut(&fd).ok_or(__WASI_EBADF)); let inode = fd_entry.inode; if !(has_rights(fd_entry.rights, __WASI_RIGHT_FD_READ) @@ -680,32 +835,42 @@ pub fn fd_pread( "Invalid rights on {:X}: expected READ and SEEK", fd_entry.rights ); - return __WASI_EACCES; + return Ok(__WASI_EACCES); } - match &mut state.fs.inodes[inode].kind { + let mut guard = inodes.arena[inode].write(); + match guard.deref_mut() { Kind::File { handle, .. } => { if let Some(h) = handle { - wasi_try!( - h.seek(std::io::SeekFrom::Start(offset as u64)).ok(), - __WASI_EIO + wasi_try_ok!( + h.seek(std::io::SeekFrom::Start(offset as u64)) + .map_err(map_io_err), + env ); - wasi_try!(read_bytes(h, memory, iovs)) + wasi_try_ok!(read_bytes(h, memory, iovs), env) } else { - return __WASI_EINVAL; + return Ok(__WASI_EINVAL); } } - Kind::Dir { .. } | Kind::Root { .. } => return __WASI_EISDIR, + Kind::Socket { socket } => { + wasi_try_ok!(socket.recv(memory, iovs), env) + } + Kind::Pipe { pipe } => { + wasi_try_ok!(pipe.recv(memory, iovs), env) + } + Kind::EventNotifications { .. } => return Ok(__WASI_EINVAL), + Kind::Dir { .. } | Kind::Root { .. } => return Ok(__WASI_EISDIR), Kind::Symlink { .. } => unimplemented!("Symlinks in wasi::fd_pread"), Kind::Buffer { buffer } => { - wasi_try!(read_bytes(&buffer[(offset as usize)..], memory, iovs)) + wasi_try_ok!(read_bytes(&buffer[(offset as usize)..], memory, iovs), env) } } } }; - wasi_try_mem!(nread_ref.write(bytes_read)); + let bytes_read: M::Offset = wasi_try_ok!(bytes_read.try_into().map_err(|_| __WASI_EOVERFLOW)); + wasi_try_mem_ok!(nread_ref.write(bytes_read)); debug!("Success: {} bytes read", bytes_read); - __WASI_ESUCCESS + Ok(__WASI_ESUCCESS) } /// ### `fd_prestat_get()` @@ -716,57 +881,63 @@ pub fn fd_pread( /// Output: /// - `__wasi_prestat *buf` /// Where the metadata will be written -pub fn fd_prestat_get( +pub fn fd_prestat_get( env: &WasiEnv, fd: __wasi_fd_t, - buf: WasmPtr<__wasi_prestat_t>, + buf: WasmPtr<__wasi_prestat_t, M>, ) -> __wasi_errno_t { - debug!("wasi::fd_prestat_get: fd={}", fd); - let (memory, mut state) = env.get_memory_and_wasi_state(0); + trace!("wasi::fd_prestat_get: fd={}", fd); + let (memory, state, inodes) = env.get_memory_and_wasi_state_and_inodes(0); - let prestat_ptr = buf.deref(memory); - - wasi_try_mem!(prestat_ptr.write(wasi_try!(state.fs.prestat_fd(fd)))); + wasi_try_mem!(buf.write(memory, wasi_try!(state.fs.prestat_fd(inodes.deref(), fd)))); __WASI_ESUCCESS } -pub fn fd_prestat_dir_name( +pub fn fd_prestat_dir_name( env: &WasiEnv, fd: __wasi_fd_t, - path: WasmPtr, - path_len: u32, + path: WasmPtr, + path_len: M::Offset, ) -> __wasi_errno_t { - debug!( + trace!( "wasi::fd_prestat_dir_name: fd={}, path_len={}", - fd, path_len + fd, + path_len ); - let (memory, mut state) = env.get_memory_and_wasi_state(0); + let (memory, state, inodes) = env.get_memory_and_wasi_state_and_inodes(0); let path_chars = wasi_try_mem!(path.slice(memory, path_len)); - let real_fd = wasi_try!(state.fs.fd_map.get(&fd).ok_or(__WASI_EBADF)); - let inode_val = &state.fs.inodes[real_fd.inode]; + let real_inode = wasi_try!(state.fs.get_fd_inode(fd)); + let inode_val = &inodes.arena[real_inode]; // check inode-val.is_preopened? - debug!("=> inode: {:?}", inode_val); - match inode_val.kind { + trace!("=> inode: {:?}", inode_val); + let guard = inode_val.read(); + match guard.deref() { Kind::Dir { .. } | Kind::Root { .. } => { // TODO: verify this: null termination, etc - if inode_val.name.len() < path_len as usize { + let path_len: u64 = path_len.into(); + if (inode_val.name.len() as u64) < path_len { wasi_try_mem!(path_chars .subslice(0..inode_val.name.len() as u64) .write_slice(inode_val.name.as_bytes())); wasi_try_mem!(path_chars.index(inode_val.name.len() as u64).write(0)); - debug!("=> result: \"{}\"", inode_val.name); + trace!("=> result: \"{}\"", inode_val.name); __WASI_ESUCCESS } else { __WASI_EOVERFLOW } } - Kind::Symlink { .. } | Kind::Buffer { .. } | Kind::File { .. } => __WASI_ENOTDIR, + Kind::Symlink { .. } + | Kind::Buffer { .. } + | Kind::File { .. } + | Kind::Socket { .. } + | Kind::Pipe { .. } + | Kind::EventNotifications { .. } => __WASI_ENOTDIR, } } @@ -784,80 +955,101 @@ pub fn fd_prestat_dir_name( /// Output: /// - `u32 *nwritten` /// Number of bytes written -pub fn fd_pwrite( +pub fn fd_pwrite( env: &WasiEnv, fd: __wasi_fd_t, - iovs: WasmPtr<__wasi_ciovec_t>, - iovs_len: u32, + iovs: WasmPtr<__wasi_ciovec_t, M>, + iovs_len: M::Offset, offset: __wasi_filesize_t, - nwritten: WasmPtr, -) -> __wasi_errno_t { - debug!("wasi::fd_pwrite"); + nwritten: WasmPtr, +) -> Result<__wasi_errno_t, WasiError> { + trace!("wasi::fd_pwrite"); // TODO: refactor, this is just copied from `fd_write`... - let (memory, mut state) = env.get_memory_and_wasi_state(0); - let iovs_arr = wasi_try_mem!(iovs.slice(memory, iovs_len)); + let (memory, state, inodes) = env.get_memory_and_wasi_state_and_inodes(0); + let iovs_arr = wasi_try_mem_ok!(iovs.slice(memory, iovs_len)); let nwritten_ref = nwritten.deref(memory); + let fd_entry = wasi_try_ok!(state.fs.get_fd(fd)); let bytes_written = match fd { - __WASI_STDIN_FILENO => return __WASI_EINVAL, + __WASI_STDIN_FILENO => return Ok(__WASI_EINVAL), __WASI_STDOUT_FILENO => { - if let Some(ref mut stdout) = - wasi_try!(state.fs.stdout_mut().map_err(fs_error_into_wasi_err)) - { - wasi_try!(write_bytes(stdout, memory, iovs_arr)) + let mut guard = wasi_try_ok!( + inodes + .stdout_mut(&state.fs.fd_map) + .map_err(fs_error_into_wasi_err), + env + ); + if let Some(ref mut stdout) = guard.deref_mut() { + wasi_try_ok!(write_bytes(stdout, memory, iovs_arr), env) } else { - return __WASI_EBADF; + return Ok(__WASI_EBADF); } } __WASI_STDERR_FILENO => { - if let Some(ref mut stderr) = - wasi_try!(state.fs.stderr_mut().map_err(fs_error_into_wasi_err)) - { - wasi_try!(write_bytes(stderr, memory, iovs_arr)) + let mut guard = wasi_try_ok!( + inodes + .stderr_mut(&state.fs.fd_map) + .map_err(fs_error_into_wasi_err), + env + ); + if let Some(ref mut stderr) = guard.deref_mut() { + wasi_try_ok!(write_bytes(stderr, memory, iovs_arr), env) } else { - return __WASI_EBADF; + return Ok(__WASI_EBADF); } } _ => { - let fd_entry = wasi_try!(state.fs.fd_map.get_mut(&fd).ok_or(__WASI_EBADF)); - if !(has_rights(fd_entry.rights, __WASI_RIGHT_FD_WRITE) && has_rights(fd_entry.rights, __WASI_RIGHT_FD_SEEK)) { - return __WASI_EACCES; + return Ok(__WASI_EACCES); } let inode_idx = fd_entry.inode; - let inode = &mut state.fs.inodes[inode_idx]; + let inode = &inodes.arena[inode_idx]; - match &mut inode.kind { + let mut guard = inode.write(); + match guard.deref_mut() { Kind::File { handle, .. } => { if let Some(handle) = handle { - handle.seek(std::io::SeekFrom::Start(offset as u64)); - wasi_try!(write_bytes(handle, memory, iovs_arr)) + wasi_try_ok!( + handle + .seek(std::io::SeekFrom::Start(offset as u64)) + .map_err(map_io_err), + env + ); + wasi_try_ok!(write_bytes(handle, memory, iovs_arr), env) } else { - return __WASI_EINVAL; + return Ok(__WASI_EINVAL); } } + Kind::Socket { socket } => { + wasi_try_ok!(socket.send(memory, iovs_arr), env) + } + Kind::Pipe { pipe } => { + wasi_try_ok!(pipe.send(memory, iovs_arr), env) + } Kind::Dir { .. } | Kind::Root { .. } => { // TODO: verify - return __WASI_EISDIR; + return Ok(__WASI_EISDIR); } + Kind::EventNotifications { .. } => return Ok(__WASI_EINVAL), Kind::Symlink { .. } => unimplemented!("Symlinks in wasi::fd_pwrite"), Kind::Buffer { buffer } => { - wasi_try!(write_bytes( - &mut buffer[(offset as usize)..], - memory, - iovs_arr - )) + wasi_try_ok!( + write_bytes(&mut buffer[(offset as usize)..], memory, iovs_arr), + env + ) } } } }; - wasi_try_mem!(nwritten_ref.write(bytes_written)); + let bytes_written: M::Offset = + wasi_try_ok!(bytes_written.try_into().map_err(|_| __WASI_EOVERFLOW)); + wasi_try_mem_ok!(nwritten_ref.write(bytes_written)); - __WASI_ESUCCESS + Ok(__WASI_ESUCCESS) } /// ### `fd_read()` @@ -872,72 +1064,148 @@ pub fn fd_pwrite( /// Output: /// - `u32 *nread` /// Number of bytes read -pub fn fd_read( +/// +pub fn fd_read( env: &WasiEnv, fd: __wasi_fd_t, - iovs: WasmPtr<__wasi_iovec_t>, - iovs_len: u32, - nread: WasmPtr, -) -> __wasi_errno_t { - debug!("wasi::fd_read: fd={}", fd); - let (memory, mut state) = env.get_memory_and_wasi_state(0); - - let iovs_arr = wasi_try_mem!(iovs.slice(memory, iovs_len)); + iovs: WasmPtr<__wasi_iovec_t, M>, + iovs_len: M::Offset, + nread: WasmPtr, +) -> Result<__wasi_errno_t, WasiError> { + trace!("wasi::fd_read: fd={}", fd); + let (memory, state, inodes) = env.get_memory_and_wasi_state_and_inodes(0); + + let iovs_arr = wasi_try_mem_ok!(iovs.slice(memory, iovs_len)); let nread_ref = nread.deref(memory); + let fd_entry = wasi_try_ok!(state.fs.get_fd(fd)); let bytes_read = match fd { __WASI_STDIN_FILENO => { - if let Some(ref mut stdin) = - wasi_try!(state.fs.stdin_mut().map_err(fs_error_into_wasi_err)) - { - wasi_try!(read_bytes(stdin, memory, iovs_arr)) + let mut guard = wasi_try_ok!( + inodes + .stdin_mut(&state.fs.fd_map) + .map_err(fs_error_into_wasi_err), + env + ); + if let Some(ref mut stdin) = guard.deref_mut() { + wasi_try_ok!(read_bytes(stdin, memory, iovs_arr), env) } else { - return __WASI_EBADF; + return Ok(__WASI_EBADF); } } - __WASI_STDOUT_FILENO | __WASI_STDERR_FILENO => return __WASI_EINVAL, + __WASI_STDOUT_FILENO | __WASI_STDERR_FILENO => return Ok(__WASI_EINVAL), _ => { - let fd_entry = wasi_try!(state.fs.fd_map.get_mut(&fd).ok_or(__WASI_EBADF)); - if !has_rights(fd_entry.rights, __WASI_RIGHT_FD_READ) { // TODO: figure out the error to return when lacking rights - return __WASI_EACCES; + return Ok(__WASI_EACCES); } + let is_non_blocking = fd_entry.flags & __WASI_FDFLAG_NONBLOCK != 0; let offset = fd_entry.offset as usize; let inode_idx = fd_entry.inode; - let inode = &mut state.fs.inodes[inode_idx]; + let inode = &inodes.arena[inode_idx]; + + let bytes_read = { + let mut guard = inode.write(); + match guard.deref_mut() { + Kind::File { handle, .. } => { + if let Some(handle) = handle { + wasi_try_ok!( + handle + .seek(std::io::SeekFrom::Start(offset as u64)) + .map_err(map_io_err), + env + ); + wasi_try_ok!(read_bytes(handle, memory, iovs_arr), env) + } else { + return Ok(__WASI_EINVAL); + } + } + Kind::Socket { socket } => { + wasi_try_ok!(socket.recv(memory, iovs_arr), env) + } + Kind::Pipe { pipe } => { + wasi_try_ok!(pipe.recv(memory, iovs_arr), env) + } + Kind::Dir { .. } | Kind::Root { .. } => { + // TODO: verify + return Ok(__WASI_EISDIR); + } + Kind::EventNotifications { + counter, + is_semaphore, + wakers, + } => { + let counter = Arc::clone(counter); + let is_semaphore: bool = *is_semaphore; + let wakers = Arc::clone(wakers); + drop(guard); + drop(inodes); + + let (tx, rx) = mpsc::channel(); + { + let mut guard = wakers.lock().unwrap(); + guard.push_front(tx); + } - let bytes_read = match &mut inode.kind { - Kind::File { handle, .. } => { - if let Some(handle) = handle { - handle.seek(std::io::SeekFrom::Start(offset as u64)); - wasi_try!(read_bytes(handle, memory, iovs_arr)) - } else { - return __WASI_EINVAL; + let ret; + loop { + let val = counter.load(Ordering::Acquire); + if val > 0 { + let new_val = if is_semaphore { val - 1 } else { 0 }; + if counter + .compare_exchange( + val, + new_val, + Ordering::AcqRel, + Ordering::Acquire, + ) + .is_ok() + { + let reader = val.to_ne_bytes(); + ret = wasi_try_ok!( + read_bytes(&reader[..], memory, iovs_arr), + env + ); + break; + } else { + continue; + } + } + + // If its none blocking then exit + if is_non_blocking { + return Ok(__WASI_EAGAIN); + } + + // Yield for a fixed period of time and then check again + env.yield_now()?; + if rx.recv_timeout(Duration::from_millis(5)).is_err() { + env.sleep(Duration::from_millis(5))?; + } + } + ret + } + Kind::Symlink { .. } => unimplemented!("Symlinks in wasi::fd_read"), + Kind::Buffer { buffer } => { + wasi_try_ok!(read_bytes(&buffer[offset..], memory, iovs_arr), env) } - } - Kind::Dir { .. } | Kind::Root { .. } => { - // TODO: verify - return __WASI_EISDIR; - } - Kind::Symlink { .. } => unimplemented!("Symlinks in wasi::fd_read"), - Kind::Buffer { buffer } => { - wasi_try!(read_bytes(&buffer[offset..], memory, iovs_arr)) } }; // reborrow - let fd_entry = wasi_try!(state.fs.fd_map.get_mut(&fd).ok_or(__WASI_EBADF)); + let mut fd_map = state.fs.fd_map.write().unwrap(); + let fd_entry = wasi_try_ok!(fd_map.get_mut(&fd).ok_or(__WASI_EBADF)); fd_entry.offset += bytes_read as u64; bytes_read } }; - wasi_try_mem!(nread_ref.write(bytes_read)); + let bytes_read: M::Offset = wasi_try_ok!(bytes_read.try_into().map_err(|_| __WASI_EOVERFLOW)); + wasi_try_mem_ok!(nread_ref.write(bytes_read)); - __WASI_ESUCCESS + Ok(__WASI_ESUCCESS) } /// ### `fd_readdir()` @@ -955,85 +1223,87 @@ pub fn fd_read( /// - `u32 *bufused` /// The Number of bytes stored in `buf`; if less than `buf_len` then entire /// directory has been read -pub fn fd_readdir( +pub fn fd_readdir( env: &WasiEnv, fd: __wasi_fd_t, - buf: WasmPtr, - buf_len: u32, + buf: WasmPtr, + buf_len: M::Offset, cookie: __wasi_dircookie_t, - bufused: WasmPtr, + bufused: WasmPtr, ) -> __wasi_errno_t { - debug!("wasi::fd_readdir"); - let (memory, mut state) = env.get_memory_and_wasi_state(0); + trace!("wasi::fd_readdir"); + let (memory, state, inodes) = env.get_memory_and_wasi_state_and_inodes(0); // TODO: figure out how this is supposed to work; // is it supposed to pack the buffer full every time until it can't? or do one at a time? let buf_arr = wasi_try_mem!(buf.slice(memory, buf_len)); let bufused_ref = bufused.deref(memory); - let working_dir = wasi_try!(state.fs.fd_map.get(&fd).ok_or(__WASI_EBADF)); + let working_dir = wasi_try!(state.fs.get_fd(fd)); let mut cur_cookie = cookie; - let mut buf_idx = 0; - - let entries: Vec<(String, u8, u64)> = match &state.fs.inodes[working_dir.inode].kind { - Kind::Dir { path, entries, .. } => { - debug!("Reading dir {:?}", path); - // TODO: refactor this code - // we need to support multiple calls, - // simple and obviously correct implementation for now: - // maintain consistent order via lexacographic sorting - let fs_info = wasi_try!(wasi_try!(state.fs_read_dir(path)) - .collect::, _>>() - .map_err(|_| __WASI_EIO)); - let mut entry_vec = wasi_try!(fs_info - .into_iter() - .map(|entry| { - let filename = entry.file_name().to_string_lossy().to_string(); - debug!("Getting file: {:?}", filename); - let filetype = virtual_file_type_to_wasi_file_type( - entry.file_type().map_err(|_| __WASI_EIO)?, - ); - Ok(( - filename, filetype, 0, // TODO: inode - )) - }) - .collect::, _>>()); - entry_vec.extend( - entries - .iter() - .filter(|(_, inode)| state.fs.inodes[**inode].is_preopened) - .map(|(name, inode)| { - let entry = &state.fs.inodes[*inode]; - ( - entry.name.to_string(), - entry.stat.st_filetype, - entry.stat.st_ino, - ) - }), - ); - entry_vec.sort_by(|a, b| a.0.cmp(&b.0)); - entry_vec - } - Kind::Root { entries } => { - debug!("Reading root"); - let sorted_entries = { - let mut entry_vec: Vec<(String, Inode)> = - entries.iter().map(|(a, b)| (a.clone(), *b)).collect(); + let mut buf_idx = 0usize; + + let entries: Vec<(String, u8, u64)> = { + let guard = inodes.arena[working_dir.inode].read(); + match guard.deref() { + Kind::Dir { path, entries, .. } => { + debug!("Reading dir {:?}", path); + // TODO: refactor this code + // we need to support multiple calls, + // simple and obviously correct implementation for now: + // maintain consistent order via lexacographic sorting + let fs_info = wasi_try!(wasi_try!(state.fs_read_dir(path)) + .collect::, _>>() + .map_err(fs_error_into_wasi_err)); + let mut entry_vec = wasi_try!(fs_info + .into_iter() + .map(|entry| { + let filename = entry.file_name().to_string_lossy().to_string(); + debug!("Getting file: {:?}", filename); + let filetype = virtual_file_type_to_wasi_file_type( + entry.file_type().map_err(fs_error_into_wasi_err)?, + ); + Ok(( + filename, filetype, 0, // TODO: inode + )) + }) + .collect::, _>>()); + entry_vec.extend( + entries + .iter() + .filter(|(_, inode)| inodes.arena[**inode].is_preopened) + .map(|(name, inode)| { + let entry = &inodes.arena[*inode]; + let stat = entry.stat.read().unwrap(); + (entry.name.to_string(), stat.st_filetype, stat.st_ino) + }), + ); entry_vec.sort_by(|a, b| a.0.cmp(&b.0)); entry_vec - }; - sorted_entries - .into_iter() - .map(|(name, inode)| { - let entry = &state.fs.inodes[inode]; - ( - format!("/{}", entry.name), - entry.stat.st_filetype, - entry.stat.st_ino, - ) - }) - .collect() + } + Kind::Root { entries } => { + debug!("Reading root"); + let sorted_entries = { + let mut entry_vec: Vec<(String, Inode)> = + entries.iter().map(|(a, b)| (a.clone(), *b)).collect(); + entry_vec.sort_by(|a, b| a.0.cmp(&b.0)); + entry_vec + }; + sorted_entries + .into_iter() + .map(|(name, inode)| { + let entry = &inodes.arena[inode]; + let stat = entry.stat.read().unwrap(); + (format!("/{}", entry.name), stat.st_filetype, stat.st_ino) + }) + .collect() + } + Kind::File { .. } + | Kind::Symlink { .. } + | Kind::Buffer { .. } + | Kind::Socket { .. } + | Kind::Pipe { .. } + | Kind::EventNotifications { .. } => return __WASI_ENOTDIR, } - Kind::File { .. } | Kind::Symlink { .. } | Kind::Buffer { .. } => return __WASI_ENOTDIR, }; for (entry_path_str, wasi_file_type, ino) in entries.iter().skip(cookie as usize) { @@ -1047,8 +1317,9 @@ pub fn fd_readdir( d_type: *wasi_file_type, }; let dirent_bytes = dirent_to_le_bytes(&dirent); + let buf_len: u64 = buf_len.into(); let upper_limit = std::cmp::min( - buf_len as usize - buf_idx, + (buf_len - buf_idx as u64) as usize, std::mem::size_of::<__wasi_dirent_t>(), ); for (i, b) in dirent_bytes.iter().enumerate().take(upper_limit) { @@ -1058,7 +1329,7 @@ pub fn fd_readdir( if upper_limit != std::mem::size_of::<__wasi_dirent_t>() { break; } - let upper_limit = std::cmp::min(buf_len as usize - buf_idx, namlen); + let upper_limit = std::cmp::min((buf_len - buf_idx as u64) as usize, namlen); for (i, b) in entry_path_str.bytes().take(upper_limit).enumerate() { wasi_try_mem!(buf_arr.index((i + buf_idx) as u64).write(b)); } @@ -1068,7 +1339,8 @@ pub fn fd_readdir( } } - wasi_try_mem!(bufused_ref.write(buf_idx as u32)); + let buf_idx: M::Offset = wasi_try!(buf_idx.try_into().map_err(|_| __WASI_EOVERFLOW)); + wasi_try_mem!(bufused_ref.write(buf_idx)); __WASI_ESUCCESS } @@ -1081,16 +1353,74 @@ pub fn fd_readdir( /// Location to copy file descriptor to pub fn fd_renumber(env: &WasiEnv, from: __wasi_fd_t, to: __wasi_fd_t) -> __wasi_errno_t { debug!("wasi::fd_renumber: from={}, to={}", from, to); - let (memory, mut state) = env.get_memory_and_wasi_state(0); - let fd_entry = wasi_try!(state.fs.fd_map.get(&from).ok_or(__WASI_EBADF)); + let (_, state) = env.get_memory_and_wasi_state(0); + + let mut fd_map = state.fs.fd_map.write().unwrap(); + let fd_entry = wasi_try!(fd_map.get_mut(&from).ok_or(__WASI_EBADF)); + let new_fd_entry = Fd { // TODO: verify this is correct rights: fd_entry.rights_inheriting, ..*fd_entry }; - state.fs.fd_map.insert(to, new_fd_entry); - state.fs.fd_map.remove(&from); + fd_map.insert(to, new_fd_entry); + fd_map.remove(&from); + __WASI_ESUCCESS +} + +/// ### `fd_dup()` +/// Duplicates the file handle +/// Inputs: +/// - `__wasi_fd_t fd` +/// File handle to be cloned +/// Outputs: +/// - `__wasi_fd_t fd` +/// The new file handle that is a duplicate of the original +pub fn fd_dup( + env: &WasiEnv, + fd: __wasi_fd_t, + ret_fd: WasmPtr<__wasi_fd_t, M>, +) -> __wasi_errno_t { + debug!("wasi::fd_dup"); + + let (memory, state) = env.get_memory_and_wasi_state(0); + let fd = wasi_try!(state.fs.clone_fd(fd)); + + wasi_try_mem!(ret_fd.write(memory, fd)); + + __WASI_ESUCCESS +} + +/// ### `fd_event()` +/// Creates a file handle for event notifications +pub fn fd_event( + env: &WasiEnv, + initial_val: u64, + flags: __wasi_eventfdflags, + ret_fd: WasmPtr<__wasi_fd_t, M>, +) -> __wasi_errno_t { + debug!("wasi::fd_event"); + + let (memory, state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(0); + + let kind = Kind::EventNotifications { + counter: Arc::new(AtomicU64::new(initial_val)), + is_semaphore: flags & __WASI_EVENTFDFLAGS_SEMAPHORE != 0, + wakers: Default::default(), + }; + + let inode = state.fs.create_inode_with_default_stat( + inodes.deref_mut(), + kind, + false, + "event".to_string(), + ); + let rights = __WASI_RIGHT_FD_READ | __WASI_RIGHT_FD_WRITE | __WASI_RIGHT_POLL_FD_READWRITE; + let fd = wasi_try!(state.fs.create_fd(rights, rights, 0, 0, inode)); + + wasi_try_mem!(ret_fd.write(memory, fd)); + __WASI_ESUCCESS } @@ -1106,64 +1436,78 @@ pub fn fd_renumber(env: &WasiEnv, from: __wasi_fd_t, to: __wasi_fd_t) -> __wasi_ /// Output: /// - `__wasi_filesize_t *fd` /// The new offset relative to the start of the file -pub fn fd_seek( +pub fn fd_seek( env: &WasiEnv, fd: __wasi_fd_t, offset: __wasi_filedelta_t, whence: __wasi_whence_t, - newoffset: WasmPtr<__wasi_filesize_t>, -) -> __wasi_errno_t { - debug!("wasi::fd_seek: fd={}, offset={}", fd, offset); - let (memory, mut state) = env.get_memory_and_wasi_state(0); + newoffset: WasmPtr<__wasi_filesize_t, M>, +) -> Result<__wasi_errno_t, WasiError> { + trace!("wasi::fd_seek: fd={}, offset={}", fd, offset); + let (memory, state, inodes) = env.get_memory_and_wasi_state_and_inodes(0); let new_offset_ref = newoffset.deref(memory); - - let fd_entry = wasi_try!(state.fs.fd_map.get_mut(&fd).ok_or(__WASI_EBADF)); + let fd_entry = wasi_try_ok!(state.fs.get_fd(fd)); if !has_rights(fd_entry.rights, __WASI_RIGHT_FD_SEEK) { - return __WASI_EACCES; + return Ok(__WASI_EACCES); } // TODO: handle case if fd is a dir? match whence { - __WASI_WHENCE_CUR => fd_entry.offset = (fd_entry.offset as i64 + offset) as u64, + __WASI_WHENCE_CUR => { + let mut fd_map = state.fs.fd_map.write().unwrap(); + let fd_entry = wasi_try_ok!(fd_map.get_mut(&fd).ok_or(__WASI_EBADF)); + fd_entry.offset = (fd_entry.offset as i64 + offset) as u64 + } __WASI_WHENCE_END => { use std::io::SeekFrom; let inode_idx = fd_entry.inode; - match state.fs.inodes[inode_idx].kind { + let mut guard = inodes.arena[inode_idx].write(); + match guard.deref_mut() { Kind::File { ref mut handle, .. } => { if let Some(handle) = handle { - let end = wasi_try!(handle.seek(SeekFrom::End(0)).ok().ok_or(__WASI_EIO)); - // TODO: handle case if fd_entry.offset uses 64 bits of a u64 + let end = + wasi_try_ok!(handle.seek(SeekFrom::End(0)).map_err(map_io_err), env); - // reborrow - let fd_entry = wasi_try!(state.fs.fd_map.get_mut(&fd).ok_or(__WASI_EBADF)); + // TODO: handle case if fd_entry.offset uses 64 bits of a u64 + drop(guard); + let mut fd_map = state.fs.fd_map.write().unwrap(); + let fd_entry = wasi_try_ok!(fd_map.get_mut(&fd).ok_or(__WASI_EBADF)); fd_entry.offset = (end as i64 + offset) as u64; } else { - return __WASI_EINVAL; + return Ok(__WASI_EINVAL); } } Kind::Symlink { .. } => { unimplemented!("wasi::fd_seek not implemented for symlinks") } - Kind::Dir { .. } | Kind::Root { .. } => { + Kind::Dir { .. } + | Kind::Root { .. } + | Kind::Socket { .. } + | Kind::Pipe { .. } + | Kind::EventNotifications { .. } => { // TODO: check this - return __WASI_EINVAL; + return Ok(__WASI_EINVAL); } Kind::Buffer { .. } => { // seeking buffers probably makes sense // TODO: implement this - return __WASI_EINVAL; + return Ok(__WASI_EINVAL); } } } - __WASI_WHENCE_SET => fd_entry.offset = offset as u64, - _ => return __WASI_EINVAL, + __WASI_WHENCE_SET => { + let mut fd_map = state.fs.fd_map.write().unwrap(); + let fd_entry = wasi_try_ok!(fd_map.get_mut(&fd).ok_or(__WASI_EBADF)); + fd_entry.offset = offset as u64 + } + _ => return Ok(__WASI_EINVAL), } // reborrow - let fd_entry = wasi_try!(state.fs.fd_map.get_mut(&fd).ok_or(__WASI_EBADF)); - wasi_try_mem!(new_offset_ref.write(fd_entry.offset)); + let fd_entry = wasi_try_ok!(state.fs.get_fd(fd)); + wasi_try_mem_ok!(new_offset_ref.write(fd_entry.offset)); - __WASI_ESUCCESS + Ok(__WASI_ESUCCESS) } /// ### `fd_sync()` @@ -1178,7 +1522,7 @@ pub fn fd_seek( pub fn fd_sync(env: &WasiEnv, fd: __wasi_fd_t) -> __wasi_errno_t { debug!("wasi::fd_sync"); debug!("=> fd={}", fd); - let (memory, mut state) = env.get_memory_and_wasi_state(0); + let (_, state, inodes) = env.get_memory_and_wasi_state_and_inodes(0); let fd_entry = wasi_try!(state.fs.get_fd(fd)); if !has_rights(fd_entry.rights, __WASI_RIGHT_FD_SYNC) { return __WASI_EACCES; @@ -1186,16 +1530,23 @@ pub fn fd_sync(env: &WasiEnv, fd: __wasi_fd_t) -> __wasi_errno_t { let inode = fd_entry.inode; // TODO: implement this for more than files - match &mut state.fs.inodes[inode].kind { - Kind::File { handle, .. } => { - if let Some(h) = handle { - wasi_try!(h.sync_to_disk().map_err(fs_error_into_wasi_err)); - } else { - return __WASI_EINVAL; + { + let mut guard = inodes.arena[inode].write(); + match guard.deref_mut() { + Kind::File { handle, .. } => { + if let Some(h) = handle { + wasi_try!(h.sync_to_disk().map_err(fs_error_into_wasi_err)); + } else { + return __WASI_EINVAL; + } } + Kind::Root { .. } | Kind::Dir { .. } => return __WASI_EISDIR, + Kind::Buffer { .. } + | Kind::Symlink { .. } + | Kind::Socket { .. } + | Kind::Pipe { .. } + | Kind::EventNotifications { .. } => return __WASI_EINVAL, } - Kind::Root { .. } | Kind::Dir { .. } => return __WASI_EISDIR, - Kind::Buffer { .. } | Kind::Symlink { .. } => return __WASI_EINVAL, } __WASI_ESUCCESS @@ -1209,16 +1560,16 @@ pub fn fd_sync(env: &WasiEnv, fd: __wasi_fd_t) -> __wasi_errno_t { /// Output: /// - `__wasi_filesize_t *offset` /// The offset of `fd` relative to the start of the file -pub fn fd_tell( +pub fn fd_tell( env: &WasiEnv, fd: __wasi_fd_t, - offset: WasmPtr<__wasi_filesize_t>, + offset: WasmPtr<__wasi_filesize_t, M>, ) -> __wasi_errno_t { debug!("wasi::fd_tell"); - let (memory, mut state) = env.get_memory_and_wasi_state(0); + let (memory, state) = env.get_memory_and_wasi_state(0); let offset_ref = offset.deref(memory); - let fd_entry = wasi_try!(state.fs.fd_map.get_mut(&fd).ok_or(__WASI_EBADF)); + let fd_entry = wasi_try!(state.fs.get_fd(fd)); if !has_rights(fd_entry.rights, __WASI_RIGHT_FD_TELL) { return __WASI_EACCES; @@ -1243,85 +1594,167 @@ pub fn fd_tell( /// Number of bytes written /// Errors: /// -pub fn fd_write( +pub fn fd_write( env: &WasiEnv, fd: __wasi_fd_t, - iovs: WasmPtr<__wasi_ciovec_t>, - iovs_len: u32, - nwritten: WasmPtr, -) -> __wasi_errno_t { - // If we are writing to stdout or stderr - // we skip debug to not pollute the stdout/err - // and do debugging happily after :) - if fd != __WASI_STDOUT_FILENO && fd != __WASI_STDERR_FILENO { - debug!("wasi::fd_write: fd={}", fd); - } else { - trace!("wasi::fd_write: fd={}", fd); - } - let (memory, mut state) = env.get_memory_and_wasi_state(0); - let iovs_arr = wasi_try_mem!(iovs.slice(memory, iovs_len)); + iovs: WasmPtr<__wasi_ciovec_t, M>, + iovs_len: M::Offset, + nwritten: WasmPtr, +) -> Result<__wasi_errno_t, WasiError> { + trace!("wasi::fd_write: fd={}", fd); + let (memory, state, inodes) = env.get_memory_and_wasi_state_and_inodes(0); + let iovs_arr = wasi_try_mem_ok!(iovs.slice(memory, iovs_len)); let nwritten_ref = nwritten.deref(memory); + let fd_entry = wasi_try_ok!(state.fs.get_fd(fd)); let bytes_written = match fd { - __WASI_STDIN_FILENO => return __WASI_EINVAL, + __WASI_STDIN_FILENO => return Ok(__WASI_EINVAL), __WASI_STDOUT_FILENO => { - if let Some(ref mut stdout) = - wasi_try!(state.fs.stdout_mut().map_err(fs_error_into_wasi_err)) - { - wasi_try!(write_bytes(stdout, memory, iovs_arr)) + let mut guard = wasi_try_ok!( + inodes + .stdout_mut(&state.fs.fd_map) + .map_err(fs_error_into_wasi_err), + env + ); + if let Some(ref mut stdout) = guard.deref_mut() { + wasi_try_ok!(write_bytes(stdout, memory, iovs_arr), env) } else { - return __WASI_EBADF; + return Ok(__WASI_EBADF); } } __WASI_STDERR_FILENO => { - if let Some(ref mut stderr) = - wasi_try!(state.fs.stderr_mut().map_err(fs_error_into_wasi_err)) - { - wasi_try!(write_bytes(stderr, memory, iovs_arr)) + let mut guard = wasi_try_ok!( + inodes + .stderr_mut(&state.fs.fd_map) + .map_err(fs_error_into_wasi_err), + env + ); + if let Some(ref mut stderr) = guard.deref_mut() { + wasi_try_ok!(write_bytes(stderr, memory, iovs_arr), env) } else { - return __WASI_EBADF; + return Ok(__WASI_EBADF); } } _ => { - let fd_entry = wasi_try!(state.fs.fd_map.get_mut(&fd).ok_or(__WASI_EBADF)); - if !has_rights(fd_entry.rights, __WASI_RIGHT_FD_WRITE) { - return __WASI_EACCES; + return Ok(__WASI_EACCES); } let offset = fd_entry.offset as usize; let inode_idx = fd_entry.inode; - let inode = &mut state.fs.inodes[inode_idx]; + let inode = &inodes.arena[inode_idx]; + + let bytes_written = { + let mut guard = inode.write(); + match guard.deref_mut() { + Kind::File { handle, .. } => { + if let Some(handle) = handle { + wasi_try_ok!( + handle + .seek(std::io::SeekFrom::Start(offset as u64)) + .map_err(map_io_err), + env + ); + wasi_try_ok!(write_bytes(handle, memory, iovs_arr), env) + } else { + return Ok(__WASI_EINVAL); + } + } + Kind::Socket { socket } => { + wasi_try_ok!(socket.send(memory, iovs_arr), env) + } + Kind::Pipe { pipe } => { + wasi_try_ok!(pipe.send(memory, iovs_arr), env) + } + Kind::Dir { .. } | Kind::Root { .. } => { + // TODO: verify + return Ok(__WASI_EISDIR); + } + Kind::EventNotifications { + counter, wakers, .. + } => { + let mut val = 0u64.to_ne_bytes(); + let written = wasi_try_ok!(write_bytes(&mut val[..], memory, iovs_arr)); + if written != val.len() { + return Ok(__WASI_EINVAL); + } + let val = u64::from_ne_bytes(val); + + counter.fetch_add(val, Ordering::AcqRel); + { + let mut guard = wakers.lock().unwrap(); + while let Some(wake) = guard.pop_back() { + if wake.send(()).is_ok() { + break; + } + } + } - let bytes_written = match &mut inode.kind { - Kind::File { handle, .. } => { - if let Some(handle) = handle { - handle.seek(std::io::SeekFrom::Start(offset as u64)); - wasi_try!(write_bytes(handle, memory, iovs_arr)) - } else { - return __WASI_EINVAL; + written + } + Kind::Symlink { .. } => unimplemented!("Symlinks in wasi::fd_write"), + Kind::Buffer { buffer } => { + wasi_try_ok!(write_bytes(&mut buffer[offset..], memory, iovs_arr), env) } - } - Kind::Dir { .. } | Kind::Root { .. } => { - // TODO: verify - return __WASI_EISDIR; - } - Kind::Symlink { .. } => unimplemented!("Symlinks in wasi::fd_write"), - Kind::Buffer { buffer } => { - wasi_try!(write_bytes(&mut buffer[offset..], memory, iovs_arr)) } }; // reborrow - let fd_entry = wasi_try!(state.fs.fd_map.get_mut(&fd).ok_or(__WASI_EBADF)); - fd_entry.offset += bytes_written as u64; - wasi_try!(state.fs.filestat_resync_size(fd)); + { + let mut fd_map = state.fs.fd_map.write().unwrap(); + let fd_entry = wasi_try_ok!(fd_map.get_mut(&fd).ok_or(__WASI_EBADF)); + fd_entry.offset += bytes_written as u64; + } + wasi_try_ok!(state.fs.filestat_resync_size(inodes.deref(), fd), env); bytes_written } }; - wasi_try_mem!(nwritten_ref.write(bytes_written)); + let bytes_written: M::Offset = + wasi_try_ok!(bytes_written.try_into().map_err(|_| __WASI_EOVERFLOW)); + wasi_try_mem_ok!(nwritten_ref.write(bytes_written)); + + Ok(__WASI_ESUCCESS) +} + +/// ### `fd_pipe()` +/// Creates ta pipe that feeds data between two file handles +/// Output: +/// - `__wasi_fd_t` +/// First file handle that represents one end of the pipe +/// - `__wasi_fd_t` +/// Second file handle that represents the other end of the pipe +pub fn fd_pipe( + env: &WasiEnv, + ro_fd1: WasmPtr<__wasi_fd_t, M>, + ro_fd2: WasmPtr<__wasi_fd_t, M>, +) -> __wasi_errno_t { + trace!("wasi::fd_pipe"); + + let (memory, state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(0); + + let (pipe1, pipe2) = WasiPipe::new(); + + let inode1 = state.fs.create_inode_with_default_stat( + inodes.deref_mut(), + Kind::Pipe { pipe: pipe1 }, + false, + "pipe".to_string(), + ); + let inode2 = state.fs.create_inode_with_default_stat( + inodes.deref_mut(), + Kind::Pipe { pipe: pipe2 }, + false, + "pipe".to_string(), + ); + + let rights = super::state::all_socket_rights(); + let fd1 = wasi_try!(state.fs.create_fd(rights, rights, 0, 0, inode1)); + let fd2 = wasi_try!(state.fs.create_fd(rights, rights, 0, 0, inode2)); + + wasi_try_mem!(ro_fd1.write(memory, fd1)); + wasi_try_mem!(ro_fd2.write(memory, fd2)); __WASI_ESUCCESS } @@ -1339,18 +1772,21 @@ pub fn fd_write( /// Required Rights: /// - __WASI_RIGHT_PATH_CREATE_DIRECTORY /// This right must be set on the directory that the file is created in (TODO: verify that this is true) -pub fn path_create_directory( +pub fn path_create_directory( env: &WasiEnv, fd: __wasi_fd_t, - path: WasmPtr, - path_len: u32, + path: WasmPtr, + path_len: M::Offset, ) -> __wasi_errno_t { debug!("wasi::path_create_directory"); - let (memory, mut state) = env.get_memory_and_wasi_state(0); + let (memory, state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(0); let working_dir = wasi_try!(state.fs.get_fd(fd)); - if let Kind::Root { .. } = &state.fs.inodes[working_dir.inode].kind { - return __WASI_EACCES; + { + let guard = inodes.arena[working_dir.inode].read(); + if let Kind::Root { .. } = guard.deref() { + return __WASI_EACCES; + } } if !has_rights(working_dir.rights, __WASI_RIGHT_PATH_CREATE_DIRECTORY) { return __WASI_EACCES; @@ -1377,7 +1813,8 @@ pub fn path_create_directory( let mut cur_dir_inode = working_dir.inode; for comp in &path_vec { debug!("Creating dir {}", comp); - match &mut state.fs.inodes[cur_dir_inode].kind { + let mut guard = inodes.arena[cur_dir_inode].write(); + match guard.deref_mut() { Kind::Dir { ref mut entries, path, @@ -1397,11 +1834,22 @@ pub fn path_create_directory( cur_dir_inode = *child; } else { let mut adjusted_path = path.clone(); + drop(guard); + // TODO: double check this doesn't risk breaking the sandbox adjusted_path.push(comp); - if adjusted_path.exists() && !adjusted_path.is_dir() { - return __WASI_ENOTDIR; - } else if !adjusted_path.exists() { + if let Ok(adjusted_path_stat) = path_filestat_get_internal( + memory, + state, + inodes.deref_mut(), + fd, + 0, + &adjusted_path.to_string_lossy(), + ) { + if adjusted_path_stat.st_filetype != __WASI_FILETYPE_DIRECTORY { + return __WASI_ENOTDIR; + } + } else { wasi_try!(state.fs_create_dir(&adjusted_path)); } let kind = Kind::Dir { @@ -1409,13 +1857,22 @@ pub fn path_create_directory( path: adjusted_path, entries: Default::default(), }; - let new_inode = wasi_try!(state.fs.create_inode(kind, false, comp.to_string())); + let new_inode = wasi_try!(state.fs.create_inode( + inodes.deref_mut(), + kind, + false, + comp.to_string() + )); + // reborrow to insert - if let Kind::Dir { - ref mut entries, .. - } = &mut state.fs.inodes[cur_dir_inode].kind { - entries.insert(comp.to_string(), new_inode); + let mut guard = inodes.arena[cur_dir_inode].write(); + if let Kind::Dir { + ref mut entries, .. + } = guard.deref_mut() + { + entries.insert(comp.to_string(), new_inode); + } } cur_dir_inode = new_inode; } @@ -1442,74 +1899,105 @@ pub fn path_create_directory( /// Output: /// - `__wasi_file_stat_t *buf` /// The location where the metadata will be stored -pub fn path_filestat_get( +pub fn path_filestat_get( env: &WasiEnv, fd: __wasi_fd_t, flags: __wasi_lookupflags_t, - path: WasmPtr, - path_len: u32, - buf: WasmPtr<__wasi_filestat_t>, + path: WasmPtr, + path_len: M::Offset, + buf: WasmPtr<__wasi_filestat_t, M>, ) -> __wasi_errno_t { - debug!("wasi::path_filestat_get"); - let (memory, mut state) = env.get_memory_and_wasi_state(0); + debug!("wasi::path_filestat_get (fd={})", fd); + let (memory, state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(0); - let root_dir = wasi_try!(state.fs.get_fd(fd)); - - if !has_rights(root_dir.rights, __WASI_RIGHT_PATH_FILESTAT_GET) { - return __WASI_EACCES; - } let path_string = unsafe { get_input_str!(memory, path, path_len) }; - debug!("=> base_fd: {}, path: {}", fd, &path_string); - - let file_inode = wasi_try!(state.fs.get_inode_at_path( + let stat = wasi_try!(path_filestat_get_internal( + memory, + state, + inodes.deref_mut(), fd, - &path_string, - flags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0, + flags, + &path_string )); - let stat = if state.fs.inodes[file_inode].is_preopened { - state.fs.inodes[file_inode].stat - } else { - wasi_try!(state - .fs - .get_stat_for_kind(&state.fs.inodes[file_inode].kind) - .ok_or(__WASI_EIO)) - }; wasi_try_mem!(buf.deref(memory).write(stat)); __WASI_ESUCCESS } -/// ### `path_filestat_set_times()` -/// Update time metadata on a file or directory +/// ### `path_filestat_get()` +/// Access metadata about a file or directory /// Inputs: /// - `__wasi_fd_t fd` -/// The directory relative to which the path is resolved +/// The directory that `path` is relative to /// - `__wasi_lookupflags_t flags` -/// Flags to control how the path is understood +/// Flags to control how `path` is understood /// - `const char *path` /// String containing the file path /// - `u32 path_len` /// The length of the `path` string -/// - `__wasi_timestamp_t st_atim` -/// The timestamp that the last accessed time attribute is set to -/// - `__wasi_timestamp_t st_mtim` -/// The timestamp that the last modified time attribute is set to -/// - `__wasi_fstflags_t fst_flags` -/// A bitmask controlling which attributes are set -pub fn path_filestat_set_times( - env: &WasiEnv, +/// Output: +/// - `__wasi_file_stat_t *buf` +/// The location where the metadata will be stored +pub fn path_filestat_get_internal( + memory: &Memory, + state: &WasiState, + inodes: &mut crate::WasiInodes, fd: __wasi_fd_t, flags: __wasi_lookupflags_t, - path: WasmPtr, - path_len: u32, + path_string: &str, +) -> Result<__wasi_filestat_t, __wasi_errno_t> { + let root_dir = state.fs.get_fd(fd)?; + + if !has_rights(root_dir.rights, __WASI_RIGHT_PATH_FILESTAT_GET) { + return Err(__WASI_EACCES); + } + debug!("=> base_fd: {}, path: {}", fd, path_string); + + let file_inode = state.fs.get_inode_at_path( + inodes, + fd, + path_string, + flags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0, + )?; + if inodes.arena[file_inode].is_preopened { + Ok(*inodes.arena[file_inode].stat.read().unwrap().deref()) + } else { + let guard = inodes.arena[file_inode].read(); + state.fs.get_stat_for_kind(inodes.deref(), guard.deref()) + } +} + +/// ### `path_filestat_set_times()` +/// Update time metadata on a file or directory +/// Inputs: +/// - `__wasi_fd_t fd` +/// The directory relative to which the path is resolved +/// - `__wasi_lookupflags_t flags` +/// Flags to control how the path is understood +/// - `const char *path` +/// String containing the file path +/// - `u32 path_len` +/// The length of the `path` string +/// - `__wasi_timestamp_t st_atim` +/// The timestamp that the last accessed time attribute is set to +/// - `__wasi_timestamp_t st_mtim` +/// The timestamp that the last modified time attribute is set to +/// - `__wasi_fstflags_t fst_flags` +/// A bitmask controlling which attributes are set +pub fn path_filestat_set_times( + env: &WasiEnv, + fd: __wasi_fd_t, + flags: __wasi_lookupflags_t, + path: WasmPtr, + path_len: M::Offset, st_atim: __wasi_timestamp_t, st_mtim: __wasi_timestamp_t, fst_flags: __wasi_fstflags_t, ) -> __wasi_errno_t { debug!("wasi::path_filestat_set_times"); - let (memory, mut state) = env.get_memory_and_wasi_state(0); + let (memory, state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(0); let fd_entry = wasi_try!(state.fs.get_fd(fd)); let fd_inode = fd_entry.inode; if !has_rights(fd_entry.rights, __WASI_RIGHT_PATH_FILESTAT_SET_TIMES) { @@ -1526,16 +2014,17 @@ pub fn path_filestat_set_times( debug!("=> base_fd: {}, path: {}", fd, &path_string); let file_inode = wasi_try!(state.fs.get_inode_at_path( + inodes.deref_mut(), fd, &path_string, flags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0, )); - let stat = wasi_try!(state - .fs - .get_stat_for_kind(&state.fs.inodes[file_inode].kind) - .ok_or(__WASI_EIO)); + let stat = { + let guard = inodes.arena[file_inode].read(); + wasi_try!(state.fs.get_stat_for_kind(inodes.deref(), guard.deref())) + }; - let inode = &mut state.fs.inodes[fd_inode]; + let inode = &inodes.arena[fd_inode]; if fst_flags & __WASI_FILESTAT_SET_ATIM != 0 || fst_flags & __WASI_FILESTAT_SET_ATIM_NOW != 0 { let time_to_set = if fst_flags & __WASI_FILESTAT_SET_ATIM != 0 { @@ -1543,7 +2032,7 @@ pub fn path_filestat_set_times( } else { wasi_try!(get_current_time_in_nanos()) }; - inode.stat.st_atim = time_to_set; + inode.stat.write().unwrap().st_atim = time_to_set; } if fst_flags & __WASI_FILESTAT_SET_MTIM != 0 || fst_flags & __WASI_FILESTAT_SET_MTIM_NOW != 0 { let time_to_set = if fst_flags & __WASI_FILESTAT_SET_MTIM != 0 { @@ -1551,7 +2040,7 @@ pub fn path_filestat_set_times( } else { wasi_try!(get_current_time_in_nanos()) }; - inode.stat.st_mtim = time_to_set; + inode.stat.write().unwrap().st_mtim = time_to_set; } __WASI_ESUCCESS @@ -1574,21 +2063,21 @@ pub fn path_filestat_set_times( /// String containing the new file path /// - `u32 old_path_len` /// Length of the `new_path` string -pub fn path_link( +pub fn path_link( env: &WasiEnv, old_fd: __wasi_fd_t, old_flags: __wasi_lookupflags_t, - old_path: WasmPtr, - old_path_len: u32, + old_path: WasmPtr, + old_path_len: M::Offset, new_fd: __wasi_fd_t, - new_path: WasmPtr, - new_path_len: u32, + new_path: WasmPtr, + new_path_len: M::Offset, ) -> __wasi_errno_t { debug!("wasi::path_link"); if old_flags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0 { debug!(" - will follow symlinks when opening path"); } - let (memory, mut state) = env.get_memory_and_wasi_state(0); + let (memory, state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(0); let old_path_str = unsafe { get_input_str!(memory, old_path, old_path_len) }; let new_path_str = unsafe { get_input_str!(memory, new_path, new_path_len) }; let source_fd = wasi_try!(state.fs.get_fd(old_fd)); @@ -1605,30 +2094,42 @@ pub fn path_link( } let source_inode = wasi_try!(state.fs.get_inode_at_path( + inodes.deref_mut(), old_fd, &old_path_str, old_flags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0, )); let target_path_arg = std::path::PathBuf::from(&new_path_str); - let (target_parent_inode, new_entry_name) = - wasi_try!(state - .fs - .get_parent_inode_at_path(new_fd, &target_path_arg, false)); + let (target_parent_inode, new_entry_name) = wasi_try!(state.fs.get_parent_inode_at_path( + inodes.deref_mut(), + new_fd, + &target_path_arg, + false + )); - if state.fs.inodes[source_inode].stat.st_nlink == __wasi_linkcount_t::max_value() { + if inodes.arena[source_inode].stat.write().unwrap().st_nlink == __wasi_linkcount_t::max_value() + { return __WASI_EMLINK; } - match &mut state.fs.inodes[target_parent_inode].kind { - Kind::Dir { entries, .. } => { - if entries.contains_key(&new_entry_name) { - return __WASI_EEXIST; + { + let mut guard = inodes.arena[target_parent_inode].write(); + match guard.deref_mut() { + Kind::Dir { entries, .. } => { + if entries.contains_key(&new_entry_name) { + return __WASI_EEXIST; + } + entries.insert(new_entry_name, source_inode); } - entries.insert(new_entry_name, source_inode); + Kind::Root { .. } => return __WASI_EINVAL, + Kind::File { .. } + | Kind::Symlink { .. } + | Kind::Buffer { .. } + | Kind::Socket { .. } + | Kind::Pipe { .. } + | Kind::EventNotifications { .. } => return __WASI_ENOTDIR, } - Kind::Root { .. } => return __WASI_EINVAL, - Kind::File { .. } | Kind::Symlink { .. } | Kind::Buffer { .. } => return __WASI_ENOTDIR, } - state.fs.inodes[source_inode].stat.st_nlink += 1; + inodes.arena[source_inode].stat.write().unwrap().st_nlink += 1; __WASI_ESUCCESS } @@ -1657,25 +2158,26 @@ pub fn path_link( /// The new file descriptor /// Possible Errors: /// - `__WASI_EACCES`, `__WASI_EBADF`, `__WASI_EFAULT`, `__WASI_EFBIG?`, `__WASI_EINVAL`, `__WASI_EIO`, `__WASI_ELOOP`, `__WASI_EMFILE`, `__WASI_ENAMETOOLONG?`, `__WASI_ENFILE`, `__WASI_ENOENT`, `__WASI_ENOTDIR`, `__WASI_EROFS`, and `__WASI_ENOTCAPABLE` -pub fn path_open( +pub fn path_open( env: &WasiEnv, dirfd: __wasi_fd_t, dirflags: __wasi_lookupflags_t, - path: WasmPtr, - path_len: u32, + path: WasmPtr, + path_len: M::Offset, o_flags: __wasi_oflags_t, fs_rights_base: __wasi_rights_t, fs_rights_inheriting: __wasi_rights_t, fs_flags: __wasi_fdflags_t, - fd: WasmPtr<__wasi_fd_t>, + fd: WasmPtr<__wasi_fd_t, M>, ) -> __wasi_errno_t { debug!("wasi::path_open"); if dirflags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0 { debug!(" - will follow symlinks when opening path"); } - let (memory, mut state) = env.get_memory_and_wasi_state(0); + let (memory, state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(0); /* TODO: find actual upper bound on name size (also this is a path, not a name :think-fish:) */ - if path_len > 1024 * 1024 { + let path_len64: u64 = path_len.into(); + if path_len64 > 1024u64 * 1024u64 { return __WASI_ENAMETOOLONG; } @@ -1700,6 +2202,7 @@ pub fn path_open( let path_arg = std::path::PathBuf::from(&path_string); let maybe_inode = state.fs.get_inode_at_path( + inodes.deref_mut(), dirfd, &path_string, dirflags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0, @@ -1713,7 +2216,8 @@ pub fn path_open( let mut open_options = state.fs_new_open_options(); let inode = if let Ok(inode) = maybe_inode { // Happy path, we found the file we're trying to open - match &mut state.fs.inodes[inode].kind { + let mut guard = inodes.arena[inode].write(); + match guard.deref_mut() { Kind::File { ref mut handle, path, @@ -1728,9 +2232,10 @@ pub fn path_open( if o_flags & __WASI_O_DIRECTORY != 0 { return __WASI_ENOTDIR; } - if o_flags & __WASI_O_EXCL != 0 && path.exists() { + if o_flags & __WASI_O_EXCL != 0 { return __WASI_EEXIST; } + let write_permission = adjusted_rights & __WASI_RIGHT_FD_WRITE != 0; // append, truncate, and create all require the permission to write let (append_permission, truncate_permission, create_permission) = @@ -1765,12 +2270,11 @@ pub fn path_open( .map_err(fs_error_into_wasi_err))); } Kind::Buffer { .. } => unimplemented!("wasi::path_open for Buffer type files"), - Kind::Dir { .. } | Kind::Root { .. } => { - // TODO: adjust these to be correct - if o_flags & __WASI_O_EXCL != 0 && path_arg.exists() { - return __WASI_EEXIST; - } - } + Kind::Dir { .. } + | Kind::Root { .. } + | Kind::Socket { .. } + | Kind::Pipe { .. } + | Kind::EventNotifications { .. } => {} Kind::Symlink { base_po_dir, path_to_symlink, @@ -1793,18 +2297,22 @@ pub fn path_open( // strip end file name let (parent_inode, new_entity_name) = wasi_try!(state.fs.get_parent_inode_at_path( + inodes.deref_mut(), dirfd, &path_arg, dirflags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0 )); - let new_file_host_path = match &state.fs.inodes[parent_inode].kind { - Kind::Dir { path, .. } => { - let mut new_path = path.clone(); - new_path.push(&new_entity_name); - new_path + let new_file_host_path = { + let guard = inodes.arena[parent_inode].read(); + match guard.deref() { + Kind::Dir { path, .. } => { + let mut new_path = path.clone(); + new_path.push(&new_entity_name); + new_path + } + Kind::Root { .. } => return __WASI_EACCES, + _ => return __WASI_EINVAL, } - Kind::Root { .. } => return __WASI_EACCES, - _ => return __WASI_EINVAL, }; // once we got the data we need from the parent, we lookup the host file // todo: extra check that opening with write access is okay @@ -1832,14 +2340,22 @@ pub fn path_open( path: new_file_host_path, fd: None, }; - wasi_try!(state.fs.create_inode(kind, false, new_entity_name.clone())) + wasi_try!(state.fs.create_inode( + inodes.deref_mut(), + kind, + false, + new_entity_name.clone() + )) }; - if let Kind::Dir { - ref mut entries, .. - } = &mut state.fs.inodes[parent_inode].kind { - entries.insert(new_entity_name, new_inode); + let mut guard = inodes.arena[parent_inode].write(); + if let Kind::Dir { + ref mut entries, .. + } = guard.deref_mut() + { + entries.insert(new_entity_name, new_inode); + } } new_inode @@ -1848,10 +2364,9 @@ pub fn path_open( } }; - debug!( - "inode {:?} value {:#?} found!", - inode, state.fs.inodes[inode] - ); + { + debug!("inode {:?} value {:#?} found!", inode, inodes.arena[inode]); + } // TODO: check and reduce these // TODO: ensure a mutable fd to root can never be opened @@ -1885,100 +2400,119 @@ pub fn path_open( /// Pointer to characters containing the path that the symlink points to /// - `u32 buf_used` /// The number of bytes written to `buf` -pub fn path_readlink( +pub fn path_readlink( env: &WasiEnv, dir_fd: __wasi_fd_t, - path: WasmPtr, - path_len: u32, - buf: WasmPtr, - buf_len: u32, - buf_used: WasmPtr, + path: WasmPtr, + path_len: M::Offset, + buf: WasmPtr, + buf_len: M::Offset, + buf_used: WasmPtr, ) -> __wasi_errno_t { debug!("wasi::path_readlink"); - let (memory, mut state) = env.get_memory_and_wasi_state(0); + let (memory, state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(0); - let base_dir = wasi_try!(state.fs.fd_map.get(&dir_fd).ok_or(__WASI_EBADF)); + let base_dir = wasi_try!(state.fs.get_fd(dir_fd)); if !has_rights(base_dir.rights, __WASI_RIGHT_PATH_READLINK) { return __WASI_EACCES; } let path_str = unsafe { get_input_str!(memory, path, path_len) }; - let inode = wasi_try!(state.fs.get_inode_at_path(dir_fd, &path_str, false)); + let inode = wasi_try!(state + .fs + .get_inode_at_path(inodes.deref_mut(), dir_fd, &path_str, false)); - if let Kind::Symlink { relative_path, .. } = &state.fs.inodes[inode].kind { - let rel_path_str = relative_path.to_string_lossy(); - debug!("Result => {:?}", rel_path_str); - let bytes = rel_path_str.as_bytes(); - if bytes.len() >= buf_len as usize { - return __WASI_EOVERFLOW; - } + { + let guard = inodes.arena[inode].read(); + if let Kind::Symlink { relative_path, .. } = guard.deref() { + let rel_path_str = relative_path.to_string_lossy(); + debug!("Result => {:?}", rel_path_str); + let buf_len: u64 = buf_len.into(); + let bytes = rel_path_str.bytes(); + if bytes.len() as u64 >= buf_len { + return __WASI_EOVERFLOW; + } + let bytes: Vec<_> = bytes.collect(); - let out = wasi_try_mem!(buf.slice(memory, bytes.len() as u32)); - wasi_try_mem!(out.write_slice(bytes)); - // should we null terminate this? + let out = wasi_try_mem!(buf.slice(memory, wasi_try!(to_offset::(bytes.len())))); + wasi_try_mem!(out.write_slice(&bytes[..])); + // should we null terminate this? - wasi_try_mem!(buf_used.deref(memory).write(bytes.len() as u32)); - } else { - return __WASI_EINVAL; + let bytes_len: M::Offset = + wasi_try!(bytes.len().try_into().map_err(|_| __WASI_EOVERFLOW)); + wasi_try_mem!(buf_used.deref(memory).write(bytes_len)); + } else { + return __WASI_EINVAL; + } } __WASI_ESUCCESS } /// Returns __WASI_ENOTEMTPY if directory is not empty -pub fn path_remove_directory( +pub fn path_remove_directory( env: &WasiEnv, fd: __wasi_fd_t, - path: WasmPtr, - path_len: u32, + path: WasmPtr, + path_len: M::Offset, ) -> __wasi_errno_t { // TODO check if fd is a dir, ensure it's within sandbox, etc. debug!("wasi::path_remove_directory"); - let (memory, mut state) = env.get_memory_and_wasi_state(0); + let (memory, state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(0); - let base_dir = wasi_try!(state.fs.fd_map.get(&fd), __WASI_EBADF); + let base_dir = wasi_try!(state.fs.get_fd(fd)); let path_str = unsafe { get_input_str!(memory, path, path_len) }; - let inode = wasi_try!(state.fs.get_inode_at_path(fd, &path_str, false)); - let (parent_inode, childs_name) = - wasi_try!(state - .fs - .get_parent_inode_at_path(fd, std::path::Path::new(&path_str), false)); + let inode = wasi_try!(state + .fs + .get_inode_at_path(inodes.deref_mut(), fd, &path_str, false)); + let (parent_inode, childs_name) = wasi_try!(state.fs.get_parent_inode_at_path( + inodes.deref_mut(), + fd, + std::path::Path::new(&path_str), + false + )); - let host_path_to_remove = match &state.fs.inodes[inode].kind { - Kind::Dir { entries, path, .. } => { - if !entries.is_empty() || wasi_try!(state.fs_read_dir(path)).count() != 0 { - return __WASI_ENOTEMPTY; + let host_path_to_remove = { + let guard = inodes.arena[inode].read(); + match guard.deref() { + Kind::Dir { entries, path, .. } => { + if !entries.is_empty() || wasi_try!(state.fs_read_dir(path)).count() != 0 { + return __WASI_ENOTEMPTY; + } + path.clone() } - path.clone() + Kind::Root { .. } => return __WASI_EACCES, + _ => return __WASI_ENOTDIR, } - Kind::Root { .. } => return __WASI_EACCES, - _ => return __WASI_ENOTDIR, }; - match &mut state.fs.inodes[parent_inode].kind { - Kind::Dir { - ref mut entries, .. - } => { - let removed_inode = wasi_try!(entries.remove(&childs_name).ok_or(__WASI_EINVAL)); - // TODO: make this a debug assert in the future - assert!(inode == removed_inode); + { + let mut guard = inodes.arena[parent_inode].write(); + match guard.deref_mut() { + Kind::Dir { + ref mut entries, .. + } => { + let removed_inode = wasi_try!(entries.remove(&childs_name).ok_or(__WASI_EINVAL)); + // TODO: make this a debug assert in the future + assert!(inode == removed_inode); + } + Kind::Root { .. } => return __WASI_EACCES, + _ => unreachable!( + "Internal logic error in wasi::path_remove_directory, parent is not a directory" + ), } - Kind::Root { .. } => return __WASI_EACCES, - _ => unreachable!( - "Internal logic error in wasi::path_remove_directory, parent is not a directory" - ), } - if state.fs_remove_dir(host_path_to_remove).is_err() { + if let Err(err) = state.fs_remove_dir(host_path_to_remove) { // reinsert to prevent FS from being in bad state + let mut guard = inodes.arena[parent_inode].write(); if let Kind::Dir { ref mut entries, .. - } = &mut state.fs.inodes[parent_inode].kind + } = guard.deref_mut() { entries.insert(childs_name, inode); } - // TODO: more intelligently return error value by inspecting returned error value - return __WASI_EIO; + return err; } __WASI_ESUCCESS @@ -1999,20 +2533,20 @@ pub fn path_remove_directory( /// Pointer to UTF8 bytes, the new file name /// - `u32 new_path_len` /// The number of bytes to read from `new_path` -pub fn path_rename( +pub fn path_rename( env: &WasiEnv, old_fd: __wasi_fd_t, - old_path: WasmPtr, - old_path_len: u32, + old_path: WasmPtr, + old_path_len: M::Offset, new_fd: __wasi_fd_t, - new_path: WasmPtr, - new_path_len: u32, + new_path: WasmPtr, + new_path_len: M::Offset, ) -> __wasi_errno_t { debug!( "wasi::path_rename: old_fd = {}, new_fd = {}", old_fd, new_fd ); - let (memory, mut state) = env.get_memory_and_wasi_state(0); + let (memory, state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(0); let source_str = unsafe { get_input_str!(memory, old_path, old_path_len) }; let source_path = std::path::Path::new(&source_str); let target_str = unsafe { get_input_str!(memory, new_path, new_path_len) }; @@ -2031,84 +2565,119 @@ pub fn path_rename( } let (source_parent_inode, source_entry_name) = - wasi_try!(state.fs.get_parent_inode_at_path(old_fd, source_path, true)); + wasi_try!(state + .fs + .get_parent_inode_at_path(inodes.deref_mut(), old_fd, source_path, true)); let (target_parent_inode, target_entry_name) = - wasi_try!(state.fs.get_parent_inode_at_path(new_fd, target_path, true)); + wasi_try!(state + .fs + .get_parent_inode_at_path(inodes.deref_mut(), new_fd, target_path, true)); - let host_adjusted_target_path = match &state.fs.inodes[target_parent_inode].kind { - Kind::Dir { entries, path, .. } => { - if entries.contains_key(&target_entry_name) { - return __WASI_EEXIST; + let host_adjusted_target_path = { + let guard = inodes.arena[target_parent_inode].read(); + match guard.deref() { + Kind::Dir { entries, path, .. } => { + if entries.contains_key(&target_entry_name) { + return __WASI_EEXIST; + } + let mut out_path = path.clone(); + out_path.push(std::path::Path::new(&target_entry_name)); + out_path + } + Kind::Root { .. } => return __WASI_ENOTCAPABLE, + Kind::Socket { .. } | Kind::Pipe { .. } | Kind::EventNotifications { .. } => { + return __WASI_EINVAL + } + Kind::Symlink { .. } | Kind::File { .. } | Kind::Buffer { .. } => { + unreachable!("Fatal internal logic error: parent of inode is not a directory") } - let mut out_path = path.clone(); - out_path.push(std::path::Path::new(&target_entry_name)); - out_path - } - Kind::Root { .. } => return __WASI_ENOTCAPABLE, - Kind::Symlink { .. } | Kind::File { .. } | Kind::Buffer { .. } => { - unreachable!("Fatal internal logic error: parent of inode is not a directory") } }; - let source_entry = match &mut state.fs.inodes[source_parent_inode].kind { - Kind::Dir { entries, .. } => { - wasi_try!(entries.remove(&source_entry_name), __WASI_ENOENT) - } - Kind::Root { .. } => return __WASI_ENOTCAPABLE, - Kind::Symlink { .. } | Kind::File { .. } | Kind::Buffer { .. } => { - unreachable!("Fatal internal logic error: parent of inode is not a directory") + let source_entry = { + let mut guard = inodes.arena[source_parent_inode].write(); + match guard.deref_mut() { + Kind::Dir { entries, .. } => { + wasi_try!(entries.remove(&source_entry_name).ok_or(__WASI_ENOENT)) + } + Kind::Root { .. } => return __WASI_ENOTCAPABLE, + Kind::Socket { .. } | Kind::Pipe { .. } | Kind::EventNotifications { .. } => { + return __WASI_EINVAL + } + Kind::Symlink { .. } | Kind::File { .. } | Kind::Buffer { .. } => { + unreachable!("Fatal internal logic error: parent of inode is not a directory") + } } }; - match &mut state.fs.inodes[source_entry].kind { - Kind::File { - handle, ref path, .. - } => { - // TODO: investigate why handle is not always there, it probably should be. - // My best guess is the fact that a handle means currently open and a path - // just means reference to host file on disk. But ideally those concepts - // could just be unified even if there's a `Box` which just - // implements the logic of "I'm not actually a file, I'll try to be as needed". - let result = if let Some(h) = handle { - state.fs_rename(&source_path, &host_adjusted_target_path) - } else { - let path_clone = path.clone(); - let out = state.fs_rename(&path_clone, &host_adjusted_target_path); - if let Kind::File { ref mut path, .. } = &mut state.fs.inodes[source_entry].kind { - *path = host_adjusted_target_path; + { + let mut guard = inodes.arena[source_entry].write(); + match guard.deref_mut() { + Kind::File { + handle, ref path, .. + } => { + // TODO: investigate why handle is not always there, it probably should be. + // My best guess is the fact that a handle means currently open and a path + // just means reference to host file on disk. But ideally those concepts + // could just be unified even if there's a `Box` which just + // implements the logic of "I'm not actually a file, I'll try to be as needed". + let result = if let Some(h) = handle { + drop(guard); + state.fs_rename(&source_path, &host_adjusted_target_path) } else { - unreachable!() + let path_clone = path.clone(); + drop(guard); + let out = state.fs_rename(&path_clone, &host_adjusted_target_path); + { + let mut guard = inodes.arena[source_entry].write(); + if let Kind::File { ref mut path, .. } = guard.deref_mut() { + *path = host_adjusted_target_path; + } else { + unreachable!() + } + } + out + }; + // if the above operation failed we have to revert the previous change and then fail + if let Err(e) = result { + let mut guard = inodes.arena[source_parent_inode].write(); + if let Kind::Dir { entries, .. } = guard.deref_mut() { + entries.insert(source_entry_name, source_entry); + return e; + } } - out - }; - // if the above operation failed we have to revert the previous change and then fail - if let Err(e) = result { - if let Kind::Dir { entries, .. } = &mut state.fs.inodes[source_parent_inode].kind { - entries.insert(source_entry_name, source_entry); + } + Kind::Dir { ref path, .. } => { + let cloned_path = path.clone(); + if let Err(e) = state.fs_rename(cloned_path, &host_adjusted_target_path) { return e; } + { + drop(guard); + let mut guard = inodes.arena[source_entry].write(); + if let Kind::Dir { path, .. } = guard.deref_mut() { + *path = host_adjusted_target_path; + } + } } + Kind::Buffer { .. } => {} + Kind::Symlink { .. } => {} + Kind::Socket { .. } => {} + Kind::Pipe { .. } => {} + Kind::EventNotifications { .. } => {} + Kind::Root { .. } => unreachable!("The root can not be moved"), } - Kind::Dir { ref path, .. } => { - let cloned_path = path.clone(); - if let Err(e) = state.fs_rename(cloned_path, &host_adjusted_target_path) { - return e; - } - if let Kind::Dir { path, .. } = &mut state.fs.inodes[source_entry].kind { - *path = host_adjusted_target_path; - } - } - Kind::Buffer { .. } => {} - Kind::Symlink { .. } => {} - Kind::Root { .. } => unreachable!("The root can not be moved"), } - if let Kind::Dir { entries, .. } = &mut state.fs.inodes[target_parent_inode].kind { - let result = entries.insert(target_entry_name, source_entry); - assert!( - result.is_none(), - "Fatal error: race condition on filesystem detected or internal logic error" - ); + { + let mut guard = inodes.arena[target_parent_inode].write(); + if let Kind::Dir { entries, .. } = guard.deref_mut() { + let result = entries.insert(target_entry_name, source_entry); + assert!( + result.is_none(), + "Fatal error: race condition on filesystem detected or internal logic error" + ); + } } __WASI_ESUCCESS @@ -2127,16 +2696,16 @@ pub fn path_rename( /// Array of UTF-8 bytes representing the target path /// - `u32 new_path_len` /// The number of bytes to read from `new_path` -pub fn path_symlink( +pub fn path_symlink( env: &WasiEnv, - old_path: WasmPtr, - old_path_len: u32, + old_path: WasmPtr, + old_path_len: M::Offset, fd: __wasi_fd_t, - new_path: WasmPtr, - new_path_len: u32, + new_path: WasmPtr, + new_path_len: M::Offset, ) -> __wasi_errno_t { debug!("wasi::path_symlink"); - let (memory, mut state) = env.get_memory_and_wasi_state(0); + let (memory, state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(0); let old_path_str = unsafe { get_input_str!(memory, old_path, old_path_len) }; let new_path_str = unsafe { get_input_str!(memory, new_path, new_path_len) }; let base_fd = wasi_try!(state.fs.get_fd(fd)); @@ -2146,23 +2715,37 @@ pub fn path_symlink( // get the depth of the parent + 1 (UNDER INVESTIGATION HMMMMMMMM THINK FISH ^ THINK FISH) let old_path_path = std::path::Path::new(&old_path_str); - let (source_inode, _) = wasi_try!(state.fs.get_parent_inode_at_path(fd, old_path_path, true)); - let depth = wasi_try!(state.fs.path_depth_from_fd(fd, source_inode)) - 1; + let (source_inode, _) = + wasi_try!(state + .fs + .get_parent_inode_at_path(inodes.deref_mut(), fd, old_path_path, true)); + let depth = wasi_try!(state + .fs + .path_depth_from_fd(inodes.deref(), fd, source_inode)) + - 1; let new_path_path = std::path::Path::new(&new_path_str); let (target_parent_inode, entry_name) = - wasi_try!(state.fs.get_parent_inode_at_path(fd, new_path_path, true)); + wasi_try!(state + .fs + .get_parent_inode_at_path(inodes.deref_mut(), fd, new_path_path, true)); // short circuit if anything is wrong, before we create an inode - match &state.fs.inodes[target_parent_inode].kind { - Kind::Dir { entries, .. } => { - if entries.contains_key(&entry_name) { - return __WASI_EEXIST; + { + let guard = inodes.arena[target_parent_inode].read(); + match guard.deref() { + Kind::Dir { entries, .. } => { + if entries.contains_key(&entry_name) { + return __WASI_EEXIST; + } + } + Kind::Root { .. } => return __WASI_ENOTCAPABLE, + Kind::Socket { .. } | Kind::Pipe { .. } | Kind::EventNotifications { .. } => { + return __WASI_EINVAL + } + Kind::File { .. } | Kind::Symlink { .. } | Kind::Buffer { .. } => { + unreachable!("get_parent_inode_at_path returned something other than a Dir or Root") } - } - Kind::Root { .. } => return __WASI_ENOTCAPABLE, - Kind::File { .. } | Kind::Symlink { .. } | Kind::Buffer { .. } => { - unreachable!("get_parent_inode_at_path returned something other than a Dir or Root") } } @@ -2183,15 +2766,21 @@ pub fn path_symlink( path_to_symlink: std::path::PathBuf::from(new_path_str), relative_path, }; - let new_inode = state - .fs - .create_inode_with_default_stat(kind, false, entry_name.clone()); + let new_inode = state.fs.create_inode_with_default_stat( + inodes.deref_mut(), + kind, + false, + entry_name.clone(), + ); - if let Kind::Dir { - ref mut entries, .. - } = &mut state.fs.inodes[target_parent_inode].kind { - entries.insert(entry_name, new_inode); + let mut guard = inodes.arena[target_parent_inode].write(); + if let Kind::Dir { + ref mut entries, .. + } = guard.deref_mut() + { + entries.insert(entry_name, new_inode); + } } __WASI_ESUCCESS @@ -2206,81 +2795,96 @@ pub fn path_symlink( /// Array of UTF-8 bytes representing the path /// - `u32 path_len` /// The number of bytes in the `path` array -pub fn path_unlink_file( +pub fn path_unlink_file( env: &WasiEnv, fd: __wasi_fd_t, - path: WasmPtr, - path_len: u32, + path: WasmPtr, + path_len: M::Offset, ) -> __wasi_errno_t { debug!("wasi::path_unlink_file"); - let (memory, mut state) = env.get_memory_and_wasi_state(0); + let (memory, state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(0); - let base_dir = wasi_try!(state.fs.fd_map.get(&fd).ok_or(__WASI_EBADF)); + let base_dir = wasi_try!(state.fs.get_fd(fd)); if !has_rights(base_dir.rights, __WASI_RIGHT_PATH_UNLINK_FILE) { return __WASI_EACCES; } let path_str = unsafe { get_input_str!(memory, path, path_len) }; debug!("Requested file: {}", path_str); - let inode = wasi_try!(state.fs.get_inode_at_path(fd, &path_str, false)); - let (parent_inode, childs_name) = - wasi_try!(state - .fs - .get_parent_inode_at_path(fd, std::path::Path::new(&path_str), false)); + let inode = wasi_try!(state + .fs + .get_inode_at_path(inodes.deref_mut(), fd, &path_str, false)); + let (parent_inode, childs_name) = wasi_try!(state.fs.get_parent_inode_at_path( + inodes.deref_mut(), + fd, + std::path::Path::new(&path_str), + false + )); - let removed_inode = match &mut state.fs.inodes[parent_inode].kind { - Kind::Dir { - ref mut entries, .. - } => { - let removed_inode = wasi_try!(entries.remove(&childs_name).ok_or(__WASI_EINVAL)); - // TODO: make this a debug assert in the future - assert!(inode == removed_inode); - debug_assert!(state.fs.inodes[inode].stat.st_nlink > 0); - removed_inode + let removed_inode = { + let mut guard = inodes.arena[parent_inode].write(); + match guard.deref_mut() { + Kind::Dir { + ref mut entries, .. + } => { + let removed_inode = wasi_try!(entries.remove(&childs_name).ok_or(__WASI_EINVAL)); + // TODO: make this a debug assert in the future + assert!(inode == removed_inode); + debug_assert!(inodes.arena[inode].stat.read().unwrap().st_nlink > 0); + removed_inode + } + Kind::Root { .. } => return __WASI_EACCES, + _ => unreachable!( + "Internal logic error in wasi::path_unlink_file, parent is not a directory" + ), } - Kind::Root { .. } => return __WASI_EACCES, - _ => unreachable!( - "Internal logic error in wasi::path_unlink_file, parent is not a directory" - ), }; - state.fs.inodes[removed_inode].stat.st_nlink -= 1; - if state.fs.inodes[removed_inode].stat.st_nlink == 0 { - match &mut state.fs.inodes[removed_inode].kind { - Kind::File { handle, path, .. } => { - if let Some(h) = handle { - wasi_try!(h.unlink().map_err(fs_error_into_wasi_err)); - } else { - // File is closed - // problem with the abstraction, we can't call unlink because there's no handle - // drop mutable borrow on `path` - let path = path.clone(); - wasi_try!(state.fs_remove_file(path)); + let st_nlink = { + let mut guard = inodes.arena[removed_inode].stat.write().unwrap(); + guard.st_nlink -= 1; + guard.st_nlink + }; + if st_nlink == 0 { + { + let mut guard = inodes.arena[removed_inode].write(); + match guard.deref_mut() { + Kind::File { handle, path, .. } => { + if let Some(h) = handle { + wasi_try!(h.unlink().map_err(fs_error_into_wasi_err)); + } else { + // File is closed + // problem with the abstraction, we can't call unlink because there's no handle + // drop mutable borrow on `path` + let path = path.clone(); + wasi_try!(state.fs_remove_file(path)); + } } + Kind::Dir { .. } | Kind::Root { .. } => return __WASI_EISDIR, + Kind::Symlink { .. } => { + // TODO: actually delete real symlinks and do nothing for virtual symlinks + } + _ => unimplemented!("wasi::path_unlink_file for Buffer"), } - Kind::Dir { .. } | Kind::Root { .. } => return __WASI_EISDIR, - Kind::Symlink { .. } => { - // TODO: actually delete real symlinks and do nothing for virtual symlinks - } - _ => unimplemented!("wasi::path_unlink_file for Buffer"), } // TODO: test this on Windows and actually make it portable // make the file an orphan fd if the fd is still open - let fd_is_orphaned = if let Kind::File { handle, .. } = &state.fs.inodes[removed_inode].kind - { - handle.is_some() - } else { - false + let fd_is_orphaned = { + let guard = inodes.arena[removed_inode].read(); + if let Kind::File { handle, .. } = guard.deref() { + handle.is_some() + } else { + false + } }; - let removed_inode_val = unsafe { state.fs.remove_inode(removed_inode) }; + let removed_inode_val = unsafe { state.fs.remove_inode(inodes.deref_mut(), removed_inode) }; assert!( removed_inode_val.is_some(), "Inode could not be removed because it doesn't exist" ); if fd_is_orphaned { - state - .fs + inodes .orphan_fds .insert(removed_inode, removed_inode_val.unwrap()); } @@ -2301,41 +2905,39 @@ pub fn path_unlink_file( /// Output: /// - `u32 nevents` /// The number of events seen -#[cfg(not(feature = "js"))] -pub fn poll_oneoff( +pub fn poll_oneoff( env: &WasiEnv, - in_: WasmPtr<__wasi_subscription_t>, - out_: WasmPtr<__wasi_event_t>, - nsubscriptions: u32, - nevents: WasmPtr, -) -> __wasi_errno_t { - debug!("wasi::poll_oneoff"); - debug!(" => nsubscriptions = {}", nsubscriptions); - let (memory, mut state) = env.get_memory_and_wasi_state(0); - - let subscription_array = wasi_try_mem!(in_.slice(memory, nsubscriptions)); - let event_array = wasi_try_mem!(out_.slice(memory, nsubscriptions)); - let mut events_seen = 0; + in_: WasmPtr<__wasi_subscription_t, M>, + out_: WasmPtr<__wasi_event_t, M>, + nsubscriptions: M::Offset, + nevents: WasmPtr, +) -> Result<__wasi_errno_t, WasiError> { + trace!("wasi::poll_oneoff"); + trace!(" => nsubscriptions = {}", nsubscriptions); + let (memory, state, inodes) = env.get_memory_and_wasi_state_and_inodes(0); + + let subscription_array = wasi_try_mem_ok!(in_.slice(memory, nsubscriptions)); + let event_array = wasi_try_mem_ok!(out_.slice(memory, nsubscriptions)); + let mut events_seen: u32 = 0; let out_ptr = nevents.deref(memory); - let mut fds = vec![]; + let mut fd_guards = vec![]; let mut clock_subs = vec![]; let mut in_events = vec![]; - let mut total_ns_slept = 0; + let mut time_to_sleep = Duration::from_millis(5); for sub in subscription_array.iter() { - let s: WasiSubscription = wasi_try!(wasi_try_mem!(sub.read()).try_into()); + let s: WasiSubscription = wasi_try_ok!(wasi_try_mem_ok!(sub.read()).try_into()); let mut peb = PollEventBuilder::new(); - let mut ns_to_sleep = 0; let fd = match s.event_type { EventType::Read(__wasi_subscription_fs_readwrite_t { fd }) => { match fd { __WASI_STDIN_FILENO | __WASI_STDOUT_FILENO | __WASI_STDERR_FILENO => (), _ => { - let fd_entry = wasi_try!(state.fs.get_fd(fd)); + let fd_entry = wasi_try_ok!(state.fs.get_fd(fd), env); if !has_rights(fd_entry.rights, __WASI_RIGHT_FD_READ) { - return __WASI_EACCES; + return Ok(__WASI_EACCES); } } } @@ -2346,10 +2948,9 @@ pub fn poll_oneoff( match fd { __WASI_STDIN_FILENO | __WASI_STDOUT_FILENO | __WASI_STDERR_FILENO => (), _ => { - let fd_entry = wasi_try!(state.fs.get_fd(fd)); - + let fd_entry = wasi_try_ok!(state.fs.get_fd(fd), env); if !has_rights(fd_entry.rights, __WASI_RIGHT_FD_WRITE) { - return __WASI_EACCES; + return Ok(__WASI_EACCES); } } } @@ -2357,11 +2958,13 @@ pub fn poll_oneoff( Some(fd) } EventType::Clock(clock_info) => { - if clock_info.clock_id == __WASI_CLOCK_REALTIME { + if clock_info.clock_id == __WASI_CLOCK_REALTIME + || clock_info.clock_id == __WASI_CLOCK_MONOTONIC + { // this is a hack // TODO: do this properly - ns_to_sleep = clock_info.timeout; - clock_subs.push(clock_info); + time_to_sleep = Duration::from_nanos(clock_info.timeout); + clock_subs.push((clock_info, s.user_data)); None } else { unimplemented!("Polling not implemented for clocks yet"); @@ -2370,64 +2973,108 @@ pub fn poll_oneoff( }; if let Some(fd) = fd { - let wasi_file_ref: &dyn VirtualFile = match fd { - __WASI_STDERR_FILENO => wasi_try!( - wasi_try!(state.fs.stderr().map_err(fs_error_into_wasi_err)).as_ref(), - __WASI_EBADF - ) - .as_ref(), - __WASI_STDIN_FILENO => wasi_try!( - wasi_try!(state.fs.stdin().map_err(fs_error_into_wasi_err)).as_ref(), - __WASI_EBADF - ) - .as_ref(), - __WASI_STDOUT_FILENO => wasi_try!( - wasi_try!(state.fs.stdout().map_err(fs_error_into_wasi_err)).as_ref(), - __WASI_EBADF - ) - .as_ref(), + let wasi_file_ref = match fd { + __WASI_STDERR_FILENO => { + wasi_try_ok!( + inodes + .stderr(&state.fs.fd_map) + .map_err(fs_error_into_wasi_err), + env + ) + } + __WASI_STDIN_FILENO => { + wasi_try_ok!( + inodes + .stdin(&state.fs.fd_map) + .map_err(fs_error_into_wasi_err), + env + ) + } + __WASI_STDOUT_FILENO => { + wasi_try_ok!( + inodes + .stdout(&state.fs.fd_map) + .map_err(fs_error_into_wasi_err), + env + ) + } _ => { - let fd_entry = wasi_try!(state.fs.get_fd(fd)); + let fd_entry = wasi_try_ok!(state.fs.get_fd(fd), env); let inode = fd_entry.inode; if !has_rights(fd_entry.rights, __WASI_RIGHT_POLL_FD_READWRITE) { - return __WASI_EACCES; + return Ok(__WASI_EACCES); } - match &state.fs.inodes[inode].kind { - Kind::File { handle, .. } => { - if let Some(h) = handle { - h.as_ref() - } else { - return __WASI_EBADF; + { + let guard = inodes.arena[inode].read(); + match guard.deref() { + Kind::File { handle, .. } => { + if let Some(h) = handle { + crate::state::InodeValFileReadGuard { guard } + } else { + return Ok(__WASI_EBADF); + } + } + Kind::Socket { .. } + | Kind::Pipe { .. } + | Kind::EventNotifications { .. } => { + return Ok(__WASI_EBADF); + } + Kind::Dir { .. } + | Kind::Root { .. } + | Kind::Buffer { .. } + | Kind::Symlink { .. } => { + unimplemented!("polling read on non-files not yet supported") } - } - Kind::Dir { .. } - | Kind::Root { .. } - | Kind::Buffer { .. } - | Kind::Symlink { .. } => { - unimplemented!("polling read on non-files not yet supported") } } } }; - fds.push(wasi_file_ref); - } else { - let remaining_ns = ns_to_sleep as i64 - total_ns_slept as i64; - if remaining_ns > 0 { - debug!("Sleeping for {} nanoseconds", remaining_ns); - let duration = std::time::Duration::from_nanos(remaining_ns as u64); - std::thread::sleep(duration); - total_ns_slept += remaining_ns; - } + fd_guards.push(wasi_file_ref); } } + + let fds = { + let mut f = vec![]; + for fd in fd_guards.iter() { + f.push(wasi_try_ok!(fd.as_ref().ok_or(__WASI_EBADF)).deref()); + } + f + }; + let mut seen_events = vec![Default::default(); in_events.len()]; - wasi_try!(poll( - fds.as_slice(), - in_events.as_slice(), - seen_events.as_mut_slice() - ) - .map_err(fs_error_into_wasi_err)); + + let start = platform_clock_time_get(__WASI_CLOCK_MONOTONIC, 1_000_000).unwrap() as u128; + let mut triggered = 0; + while triggered == 0 { + let now = platform_clock_time_get(__WASI_CLOCK_MONOTONIC, 1_000_000).unwrap() as u128; + let delta = match now.checked_sub(start) { + Some(a) => Duration::from_nanos(a as u64), + None => Duration::ZERO, + }; + match poll( + fds.as_slice(), + in_events.as_slice(), + seen_events.as_mut_slice(), + Duration::from_millis(1), + ) { + Ok(0) => { + env.yield_now()?; + } + Ok(a) => { + triggered = a; + } + Err(FsError::WouldBlock) => { + env.sleep(Duration::from_millis(1))?; + } + Err(err) => { + return Ok(fs_error_into_wasi_err(err)); + } + }; + if delta > time_to_sleep { + break; + } + } for (i, seen_event) in seen_events.into_iter().enumerate() { let mut flags = 0; @@ -2440,21 +3087,31 @@ pub fn poll_oneoff( PollEvent::PollHangUp => flags = __WASI_EVENT_FD_READWRITE_HANGUP, PollEvent::PollInvalid => error = __WASI_EINVAL, PollEvent::PollIn => { - bytes_available = - wasi_try!(fds[i].bytes_available().map_err(fs_error_into_wasi_err)); + bytes_available = wasi_try_ok!( + fds[i] + .bytes_available_read() + .map_err(fs_error_into_wasi_err), + env + ) + .unwrap_or(0usize); error = __WASI_ESUCCESS; } PollEvent::PollOut => { - bytes_available = - wasi_try!(fds[i].bytes_available().map_err(fs_error_into_wasi_err)); + bytes_available = wasi_try_ok!( + fds[i] + .bytes_available_write() + .map_err(fs_error_into_wasi_err), + env + ) + .unwrap_or(0usize); error = __WASI_ESUCCESS; } } } let event = __wasi_event_t { - userdata: wasi_try_mem!(subscription_array.index(i as u64).read()).userdata, + userdata: wasi_try_mem_ok!(subscription_array.index(i as u64).read()).userdata, error, - type_: wasi_try_mem!(subscription_array.index(i as u64).read()).type_, + type_: wasi_try_mem_ok!(subscription_array.index(i as u64).read()).type_, u: unsafe { __wasi_event_u { fd_readwrite: __wasi_event_fd_readwrite_t { @@ -2464,52 +3121,64 @@ pub fn poll_oneoff( } }, }; - wasi_try_mem!(event_array.index(events_seen as u64).write(event)); + wasi_try_mem_ok!(event_array.index(events_seen as u64).write(event)); events_seen += 1; } - for clock_info in clock_subs { - let event = __wasi_event_t { - // TOOD: review userdata value - userdata: 0, - error: __WASI_ESUCCESS, - type_: __WASI_EVENTTYPE_CLOCK, - u: unsafe { - __wasi_event_u { - fd_readwrite: __wasi_event_fd_readwrite_t { - nbytes: 0, - flags: 0, - }, - } - }, - }; - wasi_try_mem!(event_array.index(events_seen as u64).write(event)); - events_seen += 1; + if triggered == 0 { + for (clock_info, userdata) in clock_subs { + let event = __wasi_event_t { + userdata, + error: __WASI_ESUCCESS, + type_: __WASI_EVENTTYPE_CLOCK, + u: unsafe { + __wasi_event_u { + fd_readwrite: __wasi_event_fd_readwrite_t { + nbytes: 0, + flags: 0, + }, + } + }, + }; + wasi_try_mem_ok!(event_array.index(events_seen as u64).write(event)); + events_seen += 1; + } } - wasi_try_mem!(out_ptr.write(events_seen as u32)); - __WASI_ESUCCESS -} - -#[cfg(feature = "js")] -pub fn poll_oneoff( - env: &WasiEnv, - in_: WasmPtr<__wasi_subscription_t>, - out_: WasmPtr<__wasi_event_t>, - nsubscriptions: u32, - nevents: WasmPtr, -) -> __wasi_errno_t { - unimplemented!(); + let events_seen: M::Offset = wasi_try_ok!(events_seen.try_into().map_err(|_| __WASI_EOVERFLOW)); + wasi_try_mem_ok!(out_ptr.write(events_seen)); + Ok(__WASI_ESUCCESS) } +/// ### `proc_exit()` +/// Terminate the process normally. An exit code of 0 indicates successful +/// termination of the program. The meanings of other values is dependent on +/// the environment. +/// Inputs: +/// - `__wasi_exitcode_t` +/// Exit code to return to the operating system pub fn proc_exit(env: &WasiEnv, code: __wasi_exitcode_t) -> Result<(), WasiError> { debug!("wasi::proc_exit, {}", code); Err(WasiError::Exit(code)) } +/// ### `proc_raise()` +/// Send a signal to the process of the calling thread. +/// Note: This is similar to `raise` in POSIX. +/// Inputs: +/// - `__wasi_signal_t` +/// Signal to be raised for this process pub fn proc_raise(env: &WasiEnv, sig: __wasi_signal_t) -> __wasi_errno_t { debug!("wasi::proc_raise"); unimplemented!("wasi::proc_raise") } +/// ### `sched_yield()` +/// Yields execution of the thread +pub fn sched_yield(env: &WasiEnv) -> Result<__wasi_errno_t, WasiError> { + trace!("wasi::sched_yield"); + env.yield_now()?; + Ok(__WASI_ESUCCESS) +} + /// ### `random_get()` /// Fill buffer with high-quality random data. This function may be slow and block /// Inputs: @@ -2517,14 +3186,19 @@ pub fn proc_raise(env: &WasiEnv, sig: __wasi_signal_t) -> __wasi_errno_t { /// A pointer to a buffer where the random bytes will be written /// - `size_t buf_len` /// The number of bytes that will be written -pub fn random_get(env: &WasiEnv, buf: u32, buf_len: u32) -> __wasi_errno_t { - debug!("wasi::random_get buf_len: {}", buf_len); +pub fn random_get( + env: &WasiEnv, + buf: WasmPtr, + buf_len: M::Offset, +) -> __wasi_errno_t { + trace!("wasi::random_get buf_len: {}", buf_len); let memory = env.memory(); - let mut u8_buffer = vec![0; buf_len as usize]; + let buf_len64: u64 = buf_len.into(); + let mut u8_buffer = vec![0; buf_len64 as usize]; let res = getrandom::getrandom(&mut u8_buffer); match res { Ok(()) => { - let buf = wasi_try_mem!(WasmPtr::::new(buf).slice(memory, buf_len)); + let buf = wasi_try_mem!(buf.slice(memory, buf_len)); wasi_try_mem!(buf.write_slice(&u8_buffer)); __WASI_ESUCCESS } @@ -2532,38 +3206,2206 @@ pub fn random_get(env: &WasiEnv, buf: u32, buf_len: u32) -> __wasi_errno_t { } } -/// ### `sched_yield()` -/// Yields execution of the thread -pub fn sched_yield(env: &WasiEnv) -> __wasi_errno_t { - debug!("wasi::sched_yield"); - ::std::thread::yield_now(); +/// ### `tty_get()` +/// Retrieves the current state of the TTY +pub fn tty_get( + env: &WasiEnv, + tty_state: WasmPtr<__wasi_tty_t, M>, +) -> __wasi_errno_t { + debug!("wasi::tty_stdin"); + + let state = env.runtime.tty_get(); + let state = __wasi_tty_t { + cols: state.cols, + rows: state.rows, + width: state.width, + height: state.height, + stdin_tty: match state.stdin_tty { + false => __WASI_BOOL_FALSE, + true => __WASI_BOOL_TRUE, + }, + stdout_tty: match state.stdout_tty { + false => __WASI_BOOL_FALSE, + true => __WASI_BOOL_TRUE, + }, + stderr_tty: match state.stderr_tty { + false => __WASI_BOOL_FALSE, + true => __WASI_BOOL_TRUE, + }, + echo: match state.echo { + false => __WASI_BOOL_FALSE, + true => __WASI_BOOL_TRUE, + }, + line_buffered: match state.line_buffered { + false => __WASI_BOOL_FALSE, + true => __WASI_BOOL_TRUE, + }, + }; + + let memory = env.memory(); + wasi_try_mem!(tty_state.write(memory, state)); + __WASI_ESUCCESS } -pub fn sock_recv( +/// ### `tty_set()` +/// Updates the properties of the rect +pub fn tty_set( env: &WasiEnv, - sock: __wasi_fd_t, - ri_data: WasmPtr<__wasi_iovec_t>, - ri_data_len: u32, - ri_flags: __wasi_riflags_t, - ro_datalen: WasmPtr, - ro_flags: WasmPtr<__wasi_roflags_t>, + tty_state: WasmPtr<__wasi_tty_t, M>, ) -> __wasi_errno_t { - debug!("wasi::sock_recv"); - unimplemented!("wasi::sock_recv") + debug!("wasi::tty_stdout"); + + let memory = env.memory(); + let state = wasi_try_mem!(tty_state.read(memory)); + let state = super::runtime::WasiTtyState { + cols: state.cols, + rows: state.rows, + width: state.width, + height: state.height, + stdin_tty: match state.stdin_tty { + __WASI_BOOL_FALSE => false, + __WASI_BOOL_TRUE => true, + _ => return __WASI_EINVAL, + }, + stdout_tty: match state.stdout_tty { + __WASI_BOOL_FALSE => false, + __WASI_BOOL_TRUE => true, + _ => return __WASI_EINVAL, + }, + stderr_tty: match state.stderr_tty { + __WASI_BOOL_FALSE => false, + __WASI_BOOL_TRUE => true, + _ => return __WASI_EINVAL, + }, + echo: match state.echo { + __WASI_BOOL_FALSE => false, + __WASI_BOOL_TRUE => true, + _ => return __WASI_EINVAL, + }, + line_buffered: match state.line_buffered { + __WASI_BOOL_FALSE => false, + __WASI_BOOL_TRUE => true, + _ => return __WASI_EINVAL, + }, + }; + + env.runtime.tty_set(state); + + __WASI_ESUCCESS } -pub fn sock_send( + +/// ### `getcwd()` +/// Returns the current working directory +/// If the path exceeds the size of the buffer then this function +/// will fill the path_len with the needed size and return EOVERFLOW +pub fn getcwd( env: &WasiEnv, - sock: __wasi_fd_t, - si_data: WasmPtr<__wasi_ciovec_t>, - si_data_len: u32, - si_flags: __wasi_siflags_t, - so_datalen: WasmPtr, + path: WasmPtr, + path_len: WasmPtr, ) -> __wasi_errno_t { - debug!("wasi::sock_send"); - unimplemented!("wasi::sock_send") + debug!("wasi::getpwd"); + let (memory, state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(0); + + let (_, cur_dir) = wasi_try!(state + .fs + .get_current_dir(inodes.deref_mut(), crate::VIRTUAL_ROOT_FD,)); + + let max_path_len = wasi_try_mem!(path_len.read(memory)); + let path_slice = wasi_try_mem!(path.slice(memory, max_path_len)); + let max_path_len: u64 = max_path_len.into(); + + let cur_dir = cur_dir.as_bytes(); + wasi_try_mem!(path_len.write(memory, wasi_try!(to_offset::(cur_dir.len())))); + if cur_dir.len() as u64 >= max_path_len { + return __WASI_EOVERFLOW; + } + + let cur_dir = { + let mut u8_buffer = vec![0; max_path_len as usize]; + let cur_dir_len = cur_dir.len(); + if (cur_dir_len as u64) < max_path_len { + u8_buffer[..cur_dir_len].clone_from_slice(cur_dir); + u8_buffer[cur_dir_len] = 0; + } else { + return __WASI_EOVERFLOW; + } + u8_buffer + }; + + wasi_try_mem!(path_slice.write_slice(&cur_dir[..])); + __WASI_ESUCCESS } -pub fn sock_shutdown(env: &WasiEnv, sock: __wasi_fd_t, how: __wasi_sdflags_t) -> __wasi_errno_t { - debug!("wasi::sock_shutdown"); - unimplemented!("wasi::sock_shutdown") + +/// ### `chdir()` +/// Sets the current working directory +pub fn chdir( + env: &WasiEnv, + path: WasmPtr, + path_len: M::Offset, +) -> __wasi_errno_t { + debug!("wasi::chdir"); + + let (memory, state) = env.get_memory_and_wasi_state(0); + let path = unsafe { get_input_str!(memory, path, path_len) }; + + state.fs.set_current_dir(path.as_str()); + __WASI_ESUCCESS +} + +/// ### `thread_spawn()` +/// Creates a new thread by spawning that shares the same +/// memory address space, file handles and main event loops. +/// The function referenced by the fork call must be +/// exported by the web assembly process. +/// +/// ## Parameters +/// +/// * `name` - Name of the function that will be invoked as a new thread +/// * `user_data` - User data that will be supplied to the function when its called +/// * `reactor` - Indicates if the function will operate as a reactor or +/// as a normal thread. Reactors will be repeatable called +/// whenever IO work is available to be processed. +/// +/// ## Return +/// +/// Returns the thread index of the newly created thread +/// (indices always start from zero) +pub fn thread_spawn( + env: &WasiEnv, + method: WasmPtr, + method_len: M::Offset, + user_data: u64, + reactor: __wasi_bool_t, + ret_tid: WasmPtr<__wasi_tid_t, M>, +) -> __wasi_errno_t { + debug!("wasi::thread_spawn"); + let memory = env.memory(); + let method = unsafe { get_input_str!(memory, method, method_len) }; + + // Load the callback function + if method.as_str() != "_thread_start" { + return __WASI_ENOTCAPABLE; + }; + let funct = unsafe { + if env.thread_start_ref().is_none() { + return __WASI_EADDRNOTAVAIL; + } + env.thread_start_ref_unchecked() + }; + + let reactor = match reactor { + __WASI_BOOL_FALSE => false, + __WASI_BOOL_TRUE => true, + _ => return __WASI_EINVAL, + }; + + // Create the sub-thread + let mut sub_env = env.clone(); + let mut sub_thread = env.new_thread(); + sub_env.id = sub_thread.id; + + let child = { + let id = sub_thread.id; + wasi_try!(env + .runtime + .thread_spawn(Box::new(move || { + if let Some(funct) = sub_env.thread_start_ref() { + if let Err(err) = funct.call(user_data) { + warn!("thread failed: {}", err); + std::mem::forget(sub_thread); + return; + } + } else { + warn!("failed to start thread: missing callback '__wasix_thread_start'"); + std::mem::forget(sub_thread); + return; + } + + let thread = { + let mut guard = sub_env.state.threading.lock().unwrap(); + let thread = guard.threads.remove(&id); + drop(guard); + thread + }; + + if let Some(thread) = thread { + let mut thread_guard = thread.exit.lock().unwrap(); + thread_guard.take(); + } + drop(sub_thread); + })) + .map_err(|err| { + let err: __wasi_errno_t = err.into(); + err + })); + id + }; + let child: __wasi_tid_t = child.into(); + + wasi_try_mem!(ret_tid.write(memory, child)); + __WASI_ESUCCESS +} + +/// ### `thread_sleep()` +/// Sends the current thread to sleep for a period of time +/// +/// ## Parameters +/// +/// * `duration` - Amount of time that the thread should sleep +pub fn thread_sleep( + env: &WasiEnv, + duration: __wasi_timestamp_t, +) -> Result<__wasi_errno_t, WasiError> { + debug!("wasi::thread_sleep"); + + let duration = Duration::from_nanos(duration as u64); + env.sleep(duration)?; + Ok(__WASI_ESUCCESS) +} + +/// ### `thread_id()` +/// Returns the index of the current thread +/// (threads indices are sequencial from zero) +pub fn thread_id( + env: &WasiEnv, + ret_tid: WasmPtr<__wasi_tid_t, M>, +) -> __wasi_errno_t { + debug!("wasi::thread_id"); + + let tid: __wasi_tid_t = env.id.into(); + wasi_try_mem!(ret_tid.write(env.memory(), tid)); + __WASI_ESUCCESS +} + +/// ### `thread_join()` +/// Joins this thread with another thread, blocking this +/// one until the other finishes +/// +/// ## Parameters +/// +/// * `tid` - Handle of the thread to wait on +pub fn thread_join(env: &WasiEnv, tid: __wasi_tid_t) -> Result<__wasi_errno_t, WasiError> { + debug!("wasi::thread_join"); + + let tid: WasiThreadId = tid.into(); + let other_thread = { + let guard = env.state.threading.lock().unwrap(); + guard.threads.get(&tid).cloned() + }; + if let Some(other_thread) = other_thread { + loop { + if other_thread.join(Duration::from_millis(5)) { + break; + } + env.yield_now()?; + } + Ok(__WASI_ESUCCESS) + } else { + Ok(__WASI_ESUCCESS) + } +} + +/// ### `thread_parallelism()` +/// Returns the available parallelism which is normally the +/// number of available cores that can run concurrently +pub fn thread_parallelism( + env: &WasiEnv, + ret_parallelism: WasmPtr, +) -> __wasi_errno_t { + debug!("wasi::thread_parallelism"); + + let parallelism = wasi_try!(env.runtime().thread_parallelism().map_err(|err| { + let err: __wasi_errno_t = err.into(); + err + })); + let parallelism: M::Offset = wasi_try!(parallelism.try_into().map_err(|_| __WASI_EOVERFLOW)); + wasi_try_mem!(ret_parallelism.write(env.memory(), parallelism)); + __WASI_ESUCCESS +} + +/// ### `getpid()` +/// Returns the handle of the current process +pub fn getpid(env: &WasiEnv, ret_pid: WasmPtr<__wasi_pid_t, M>) -> __wasi_errno_t { + debug!("wasi::getpid"); + + let pid = env.runtime().getpid(); + if let Some(pid) = pid { + wasi_try_mem!(ret_pid.write(env.memory(), pid as __wasi_pid_t)); + __WASI_ESUCCESS + } else { + __WASI_ENOTSUP + } +} + +/// ### `thread_exit()` +/// Terminates the current running thread, if this is the last thread then +/// the process will also exit with the specified exit code. An exit code +/// of 0 indicates successful termination of the thread. The meanings of +/// other values is dependent on the environment. +/// +/// ## Parameters +/// +/// * `rval` - The exit code returned by the process. +pub fn thread_exit( + env: &WasiEnv, + exitcode: __wasi_exitcode_t, +) -> Result<__wasi_errno_t, WasiError> { + debug!("wasi::thread_exit"); + Err(WasiError::Exit(exitcode)) +} + +/// Spawns a new process within the context of this machine +/// +/// ## Parameters +/// +/// * `name` - Name of the process to be spawned +/// * `chroot` - Indicates if the process will chroot or not +/// * `args` - List of the arguments to pass the process +/// (entries are separated by line feeds) +/// * `preopen` - List of the preopens for this process +/// (entries are separated by line feeds) +/// * `stdin` - How will stdin be handled +/// * `stdout` - How will stdout be handled +/// * `stderr` - How will stderr be handled +/// * `working_dir` - Working directory where this process should run +/// (passing '.' will use the current directory) +/// +/// ## Return +/// +/// Returns a bus process id that can be used to invoke calls +pub fn process_spawn( + env: &WasiEnv, + name: WasmPtr, + name_len: M::Offset, + chroot: __wasi_bool_t, + args: WasmPtr, + args_len: M::Offset, + preopen: WasmPtr, + preopen_len: M::Offset, + stdin: __wasi_stdiomode_t, + stdout: __wasi_stdiomode_t, + stderr: __wasi_stdiomode_t, + working_dir: WasmPtr, + working_dir_len: M::Offset, + ret_handles: WasmPtr<__wasi_bus_handles_t, M>, +) -> __bus_errno_t { + let bus = env.runtime.bus(); + let memory = env.memory(); + let name = unsafe { get_input_str_bus!(memory, name, name_len) }; + let args = unsafe { get_input_str_bus!(memory, args, args_len) }; + let preopen = unsafe { get_input_str_bus!(memory, preopen, preopen_len) }; + let working_dir = unsafe { get_input_str_bus!(memory, working_dir, working_dir_len) }; + let chroot = chroot == __WASI_BOOL_TRUE; + debug!("wasi::process_spawn (name={})", name); + + let args: Vec<_> = args.split(&['\n', '\r']).map(|a| a.to_string()).collect(); + + let preopen: Vec<_> = preopen + .split(&['\n', '\r']) + .map(|a| a.to_string()) + .collect(); + + let conv_stdio_mode = |mode: __wasi_stdiomode_t| match mode { + __WASI_STDIO_MODE_PIPED => StdioMode::Piped, + __WASI_STDIO_MODE_INHERIT => StdioMode::Inherit, + __WASI_STDIO_MODE_LOG => StdioMode::Log, + /*__WASI_STDIO_MODE_NULL |*/ _ => StdioMode::Null, + }; + + let process = wasi_try_bus!(bus + .new_spawn() + .chroot(chroot) + .args(args) + .preopen(preopen) + .stdin_mode(conv_stdio_mode(stdin)) + .stdout_mode(conv_stdio_mode(stdout)) + .stderr_mode(conv_stdio_mode(stderr)) + .working_dir(working_dir) + .spawn(name.as_str()) + .map_err(bus_error_into_wasi_err)); + + let conv_stdio_fd = |a: Option| match a { + Some(fd) => __wasi_option_fd_t { + tag: __WASI_OPTION_SOME, + fd: fd.into(), + }, + None => __wasi_option_fd_t { + tag: __WASI_OPTION_NONE, + fd: 0, + }, + }; + + // Convert the stdio + let stdin = conv_stdio_fd(process.inst.stdin_fd()); + let stdout = conv_stdio_fd(process.inst.stdout_fd()); + let stderr = conv_stdio_fd(process.inst.stderr_fd()); + + // Add the process to the environment state + let bid = { + let mut guard = env.state.threading.lock().unwrap(); + guard.process_seed += 1; + let bid = guard.process_seed; + guard.processes.insert(bid.into(), process); + bid + }; + + let handles = __wasi_bus_handles_t { + bid, + stdin, + stdout, + stderr, + }; + + wasi_try_mem_bus!(ret_handles.write(memory, handles)); + + __BUS_ESUCCESS +} + +/// Spawns a new bus process for a particular web WebAssembly +/// binary that is referenced by its process name. +/// +/// ## Parameters +/// +/// * `name` - Name of the process to be spawned +/// * `reuse` - Indicates if the existing processes should be reused +/// if they are already running +/// +/// ## Return +/// +/// Returns a bus process id that can be used to invoke calls +pub fn bus_open_local( + env: &WasiEnv, + name: WasmPtr, + name_len: M::Offset, + reuse: __wasi_bool_t, + ret_bid: WasmPtr<__wasi_bid_t, M>, +) -> __bus_errno_t { + let bus = env.runtime.bus(); + let memory = env.memory(); + let name = unsafe { get_input_str_bus!(memory, name, name_len) }; + let reuse = reuse == __WASI_BOOL_TRUE; + debug!("wasi::bus_open_local (name={}, reuse={})", name, reuse); + + bus_open_local_internal(env, name, reuse, None, None, ret_bid) +} + +/// Spawns a new bus process for a particular web WebAssembly +/// binary that is referenced by its process name on a remote instance. +/// +/// ## Parameters +/// +/// * `name` - Name of the process to be spawned +/// * `reuse` - Indicates if the existing processes should be reused +/// if they are already running +/// * `instance` - Instance identifier where this process will be spawned +/// * `token` - Acceess token used to authenticate with the instance +/// +/// ## Return +/// +/// Returns a bus process id that can be used to invoke calls +pub fn bus_open_remote( + env: &WasiEnv, + name: WasmPtr, + name_len: M::Offset, + reuse: __wasi_bool_t, + instance: WasmPtr, + instance_len: M::Offset, + token: WasmPtr, + token_len: M::Offset, + ret_bid: WasmPtr<__wasi_bid_t, M>, +) -> __bus_errno_t { + let bus = env.runtime.bus(); + let memory = env.memory(); + let name = unsafe { get_input_str_bus!(memory, name, name_len) }; + let instance = unsafe { get_input_str_bus!(memory, instance, instance_len) }; + let token = unsafe { get_input_str_bus!(memory, token, token_len) }; + let reuse = reuse == __WASI_BOOL_TRUE; + debug!( + "wasi::bus_open_remote (name={}, reuse={}, instance={})", + name, reuse, instance + ); + + bus_open_local_internal(env, name, reuse, Some(instance), Some(token), ret_bid) +} + +fn bus_open_local_internal( + env: &WasiEnv, + name: String, + reuse: bool, + instance: Option, + token: Option, + ret_bid: WasmPtr<__wasi_bid_t, M>, +) -> __bus_errno_t { + let bus = env.runtime.bus(); + let memory = env.memory(); + let name: Cow<'static, str> = name.into(); + + // Check if it already exists + if reuse { + let guard = env.state.threading.lock().unwrap(); + if let Some(bid) = guard.process_reuse.get(&name) { + if guard.processes.contains_key(bid) { + wasi_try_mem_bus!(ret_bid.write(memory, (*bid).into())); + return __BUS_ESUCCESS; + } + } + } + + let mut process = bus.new_spawn(); + process + .reuse(reuse) + .stdin_mode(StdioMode::Null) + .stdout_mode(StdioMode::Null) + .stderr_mode(StdioMode::Log); + + if let Some(instance) = instance { + process.remote_instance(instance); + } + + if let Some(token) = token { + process.access_token(token); + } + + let process = wasi_try_bus!(process + .spawn(name.as_ref()) + .map_err(bus_error_into_wasi_err)); + + // Add the process to the environment state + let bid = { + let mut guard = env.state.threading.lock().unwrap(); + guard.process_seed += 1; + let bid: WasiBusProcessId = guard.process_seed.into(); + guard.processes.insert(bid, process); + guard.process_reuse.insert(name, bid); + bid + }; + + wasi_try_mem_bus!(ret_bid.write(memory, bid.into())); + + __BUS_ESUCCESS +} + +/// Closes a bus process and releases all associated resources +/// +/// ## Parameters +/// +/// * `bid` - Handle of the bus process handle to be closed +pub fn bus_close(env: &WasiEnv, bid: __wasi_bid_t) -> __bus_errno_t { + trace!("wasi::bus_close (bid={})", bid); + let bid: WasiBusProcessId = bid.into(); + + let mut guard = env.state.threading.lock().unwrap(); + guard.processes.remove(&bid); + + __BUS_EUNSUPPORTED +} + +/// Invokes a call within a running bus process. +/// +/// ## Parameters +/// +/// * `bid` - Handle of the bus process to invoke the call within +/// * `keep_alive` - Causes the call handle to remain open even when A +/// reply is received. It is then the callers responsibility +/// to invoke 'bus_drop' when they are finished with the call +/// * `topic` - Topic that describes the type of call to made +/// * `format` - Format of the data pushed onto the bus +/// * `buf` - The buffer where data to be transmitted is stored +pub fn bus_call( + env: &WasiEnv, + bid: __wasi_bid_t, + keep_alive: __wasi_bool_t, + topic: WasmPtr, + topic_len: M::Offset, + format: __wasi_busdataformat_t, + buf: WasmPtr, + buf_len: M::Offset, + ret_cid: WasmPtr<__wasi_cid_t, M>, +) -> __bus_errno_t { + let bus = env.runtime.bus(); + let memory = env.memory(); + let topic = unsafe { get_input_str_bus!(memory, topic, topic_len) }; + let keep_alive = keep_alive == __WASI_BOOL_TRUE; + trace!( + "wasi::bus_call (bid={}, topic={}, buf_len={})", + bid, + topic, + buf_len + ); + + __BUS_EUNSUPPORTED +} + +/// Invokes a call within the context of another call +/// +/// ## Parameters +/// +/// * `parent` - Parent bus call that this is related to +/// * `keep_alive` - Causes the call handle to remain open even when A +/// reply is received. It is then the callers responsibility +/// to invoke 'bus_drop' when they are finished with the call +/// * `topic` - Topic that describes the type of call to made +/// * `format` - Format of the data pushed onto the bus +/// * `buf` - The buffer where data to be transmitted is stored +pub fn bus_subcall( + env: &WasiEnv, + parent: __wasi_cid_t, + keep_alive: __wasi_bool_t, + topic: WasmPtr, + topic_len: M::Offset, + format: __wasi_busdataformat_t, + buf: WasmPtr, + buf_len: M::Offset, + ret_cid: WasmPtr<__wasi_cid_t, M>, +) -> __bus_errno_t { + let bus = env.runtime.bus(); + let memory = env.memory(); + let topic = unsafe { get_input_str_bus!(memory, topic, topic_len) }; + let keep_alive = keep_alive == __WASI_BOOL_TRUE; + trace!( + "wasi::bus_subcall (parent={}, topic={}, buf_len={})", + parent, + topic, + buf_len + ); + + __BUS_EUNSUPPORTED +} + +/// Polls for any outstanding events from a particular +/// bus process by its handle +/// +/// ## Parameters +/// +/// * `timeout` - Timeout before the poll returns, if one passed 0 +/// as the timeout then this call is non blocking. +/// * `events` - An events buffer that will hold any received bus events +/// * `malloc` - Name of the function that will be invoked to allocate memory +/// Function signature fn(u64) -> u64 +/// +/// ## Return +/// +/// Returns the number of events that have occured +pub fn bus_poll( + env: &WasiEnv, + timeout: __wasi_timestamp_t, + events: WasmPtr, + nevents: M::Offset, + malloc: WasmPtr, + malloc_len: M::Offset, + ret_nevents: WasmPtr, +) -> __bus_errno_t { + let bus = env.runtime.bus(); + let memory = env.memory(); + let malloc = unsafe { get_input_str_bus!(memory, malloc, malloc_len) }; + trace!("wasi::bus_poll (timeout={}, malloc={})", timeout, malloc); + + __BUS_EUNSUPPORTED +} + +/// Replies to a call that was made to this process +/// from another process; where 'cid' is the call context. +/// This will may also drop the handle and release any +/// associated resources (if keepalive is not set) +/// +/// ## Parameters +/// +/// * `cid` - Handle of the call to send a reply on +/// * `format` - Format of the data pushed onto the bus +/// * `buf` - The buffer where data to be transmitted is stored +pub fn call_reply( + env: &WasiEnv, + cid: __wasi_cid_t, + format: __wasi_busdataformat_t, + buf: WasmPtr, + buf_len: M::Offset, +) -> __bus_errno_t { + let bus = env.runtime.bus(); + trace!( + "wasi::call_reply (cid={}, format={}, data_len={})", + cid, + format, + buf_len + ); + + __BUS_EUNSUPPORTED +} + +/// Causes a fault on a particular call that was made +/// to this process from another process; where 'bid' +/// is the callering process context. +/// +/// ## Parameters +/// +/// * `cid` - Handle of the call to raise a fault on +/// * `fault` - Fault to be raised on the bus +pub fn call_fault(env: &WasiEnv, cid: __wasi_cid_t, fault: __bus_errno_t) -> __bus_errno_t { + let bus = env.runtime.bus(); + debug!("wasi::call_fault (cid={}, fault={})", cid, fault); + + __BUS_EUNSUPPORTED +} + +/// Closes a bus call based on its bus call handle +/// +/// ## Parameters +/// +/// * `cid` - Handle of the bus call handle to be dropped +pub fn call_close(env: &WasiEnv, cid: __wasi_cid_t) -> __bus_errno_t { + let bus = env.runtime.bus(); + trace!("wasi::call_close (cid={})", cid); + + __BUS_EUNSUPPORTED +} + +/// ### `ws_connect()` +/// Connects to a websocket at a particular network URL +/// +/// ## Parameters +/// +/// * `url` - URL of the web socket destination to connect to +/// +/// ## Return +/// +/// Returns a socket handle which is used to send and receive data +pub fn ws_connect( + env: &WasiEnv, + url: WasmPtr, + url_len: M::Offset, + ret_sock: WasmPtr<__wasi_fd_t, M>, +) -> __wasi_errno_t { + debug!("wasi::ws_connect"); + let memory = env.memory(); + let url = unsafe { get_input_str!(memory, url, url_len) }; + + let socket = wasi_try!(env + .net() + .ws_connect(url.as_str()) + .map_err(net_error_into_wasi_err)); + + let (memory, state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(0); + + let kind = Kind::Socket { + socket: InodeSocket::new(InodeSocketKind::WebSocket(socket)), + }; + + let inode = state.fs.create_inode_with_default_stat( + inodes.deref_mut(), + kind, + false, + "socket".to_string(), + ); + let rights = super::state::all_socket_rights(); + let fd = wasi_try!(state.fs.create_fd(rights, rights, 0, 0, inode)); + + wasi_try_mem!(ret_sock.write(memory, fd)); + + __WASI_ESUCCESS +} + +/// ### `http_request()` +/// Makes a HTTP request to a remote web resource and +/// returns a socket handles that are used to send and receive data +/// +/// ## Parameters +/// +/// * `url` - URL of the HTTP resource to connect to +/// * `method` - HTTP method to be invoked +/// * `headers` - HTTP headers to attach to the request +/// (headers seperated by lines) +/// * `gzip` - Should the request body be compressed +/// +/// ## Return +/// +/// The body of the response can be streamed from the returned +/// file handle +pub fn http_request( + env: &WasiEnv, + url: WasmPtr, + url_len: M::Offset, + method: WasmPtr, + method_len: M::Offset, + headers: WasmPtr, + headers_len: M::Offset, + gzip: __wasi_bool_t, + ret_handles: WasmPtr<__wasi_http_handles_t, M>, +) -> __wasi_errno_t { + debug!("wasi::http_request"); + let memory = env.memory(); + let url = unsafe { get_input_str!(memory, url, url_len) }; + let method = unsafe { get_input_str!(memory, method, method_len) }; + let headers = unsafe { get_input_str!(memory, headers, headers_len) }; + + let gzip = match gzip { + __WASI_BOOL_FALSE => false, + __WASI_BOOL_TRUE => true, + _ => return __WASI_EINVAL, + }; + + let socket = wasi_try!(env + .net() + .http_request(url.as_str(), method.as_str(), headers.as_str(), gzip) + .map_err(net_error_into_wasi_err)); + let socket_req = SocketHttpRequest { + request: socket.request, + response: None, + headers: None, + status: socket.status.clone(), + }; + let socket_res = SocketHttpRequest { + request: None, + response: socket.response, + headers: None, + status: socket.status.clone(), + }; + let socket_hdr = SocketHttpRequest { + request: None, + response: None, + headers: socket.headers, + status: socket.status, + }; + + let (memory, state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(0); + + let kind_req = Kind::Socket { + socket: InodeSocket::new(InodeSocketKind::HttpRequest( + Mutex::new(socket_req), + InodeHttpSocketType::Request, + )), + }; + let kind_res = Kind::Socket { + socket: InodeSocket::new(InodeSocketKind::HttpRequest( + Mutex::new(socket_res), + InodeHttpSocketType::Response, + )), + }; + let kind_hdr = Kind::Socket { + socket: InodeSocket::new(InodeSocketKind::HttpRequest( + Mutex::new(socket_hdr), + InodeHttpSocketType::Headers, + )), + }; + + let inode_req = state.fs.create_inode_with_default_stat( + inodes.deref_mut(), + kind_req, + false, + "http_request".to_string(), + ); + let inode_res = state.fs.create_inode_with_default_stat( + inodes.deref_mut(), + kind_res, + false, + "http_response".to_string(), + ); + let inode_hdr = state.fs.create_inode_with_default_stat( + inodes.deref_mut(), + kind_hdr, + false, + "http_headers".to_string(), + ); + let rights = super::state::all_socket_rights(); + + let handles = __wasi_http_handles_t { + req: wasi_try!(state.fs.create_fd(rights, rights, 0, 0, inode_req)), + res: wasi_try!(state.fs.create_fd(rights, rights, 0, 0, inode_res)), + hdr: wasi_try!(state.fs.create_fd(rights, rights, 0, 0, inode_hdr)), + }; + + wasi_try_mem!(ret_handles.write(memory, handles)); + + __WASI_ESUCCESS +} + +/// ### `http_status()` +/// Retrieves the status of a HTTP request +/// +/// ## Parameters +/// +/// * `fd` - Handle of the HTTP request +/// * `status` - Pointer to a buffer that will be filled with the current +/// status of this HTTP request +pub fn http_status( + env: &WasiEnv, + sock: __wasi_fd_t, + status: WasmPtr<__wasi_http_status_t, M>, +) -> __wasi_errno_t { + debug!("wasi::http_status"); + + let memory = env.memory(); + let ref_status = status.deref(memory); + + let http_status = wasi_try!(__sock_actor(env, sock, 0, |socket| { + socket.http_status() + })); + + // Write everything else and return the status to the caller + let status = __wasi_http_status_t { + ok: __WASI_BOOL_TRUE, + redirect: match http_status.redirected { + true => __WASI_BOOL_TRUE, + false => __WASI_BOOL_FALSE, + }, + size: wasi_try!(Ok(http_status.size)), + status: http_status.status, + }; + + wasi_try_mem!(ref_status.write(status)); + + __WASI_ESUCCESS +} + +/// ### `port_bridge()` +/// Securely connects to a particular remote network +/// +/// ## Parameters +/// +/// * `network` - Fully qualified identifier for the network +/// * `token` - Access token used to authenticate with the network +/// * `security` - Level of encryption to encapsulate the network connection with +pub fn port_bridge( + env: &WasiEnv, + network: WasmPtr, + network_len: M::Offset, + token: WasmPtr, + token_len: M::Offset, + security: __wasi_streamsecurity_t, +) -> __wasi_errno_t { + debug!("wasi::port_bridge"); + let memory = env.memory(); + let network = unsafe { get_input_str!(memory, network, network_len) }; + let token = unsafe { get_input_str!(memory, token, token_len) }; + let security = match security { + __WASI_STREAM_SECURITY_UNENCRYPTED => StreamSecurity::Unencrypted, + __WASI_STREAM_SECURITY_ANY_ENCRYPTION => StreamSecurity::AnyEncyption, + __WASI_STREAM_SECURITY_CLASSIC_ENCRYPTION => StreamSecurity::ClassicEncryption, + __WASI_STREAM_SECURITY_DOUBLE_ENCRYPTION => StreamSecurity::DoubleEncryption, + _ => return __WASI_EINVAL, + }; + + wasi_try!(env + .net() + .bridge(network.as_str(), token.as_str(), security) + .map_err(net_error_into_wasi_err)); + __WASI_ESUCCESS +} + +/// ### `port_unbridge()` +/// Disconnects from a remote network +pub fn port_unbridge(env: &WasiEnv) -> __wasi_errno_t { + debug!("wasi::port_unbridge"); + wasi_try!(env.net().unbridge().map_err(net_error_into_wasi_err)); + __WASI_ESUCCESS +} + +/// ### `port_dhcp_acquire()` +/// Acquires a set of IP addresses using DHCP +pub fn port_dhcp_acquire(env: &WasiEnv) -> __wasi_errno_t { + debug!("wasi::port_dhcp_acquire"); + wasi_try!(env.net().dhcp_acquire().map_err(net_error_into_wasi_err)); + __WASI_ESUCCESS +} + +/// ### `port_addr_add()` +/// Adds another static address to the local port +/// +/// ## Parameters +/// +/// * `addr` - Address to be added +pub fn port_addr_add( + env: &WasiEnv, + ip: WasmPtr<__wasi_cidr_t, M>, +) -> __wasi_errno_t { + debug!("wasi::port_addr_add"); + let memory = env.memory(); + let cidr = wasi_try!(super::state::read_cidr(memory, ip)); + wasi_try!(env + .net() + .ip_add(cidr.ip, cidr.prefix) + .map_err(net_error_into_wasi_err)); + __WASI_ESUCCESS +} + +/// ### `port_addr_remove()` +/// Removes an address from the local port +/// +/// ## Parameters +/// +/// * `addr` - Address to be removed +pub fn port_addr_remove( + env: &WasiEnv, + ip: WasmPtr<__wasi_addr_t, M>, +) -> __wasi_errno_t { + debug!("wasi::port_addr_remove"); + let memory = env.memory(); + let ip = wasi_try!(super::state::read_ip(memory, ip)); + wasi_try!(env.net().ip_remove(ip).map_err(net_error_into_wasi_err)); + __WASI_ESUCCESS +} + +/// ### `port_addr_clear()` +/// Clears all the addresses on the local port +pub fn port_addr_clear(env: &WasiEnv) -> __wasi_errno_t { + debug!("wasi::port_addr_clear"); + wasi_try!(env.net().ip_clear().map_err(net_error_into_wasi_err)); + __WASI_ESUCCESS +} + +/// ### `port_mac()` +/// Returns the MAC address of the local port +pub fn port_mac( + env: &WasiEnv, + ret_mac: WasmPtr<__wasi_hardwareaddress_t, M>, +) -> __wasi_errno_t { + debug!("wasi::port_mac"); + let memory = env.memory(); + let mac = wasi_try!(env.net().mac().map_err(net_error_into_wasi_err)); + let mac = __wasi_hardwareaddress_t { octs: mac }; + wasi_try_mem!(ret_mac.write(memory, mac)); + __WASI_ESUCCESS +} + +/// ### `port_ip_list()` +/// Returns a list of all the addresses owned by the local port +/// This function fills the output buffer as much as possible. +/// If the buffer is not big enough then the naddrs address will be +/// filled with the buffer size needed and the EOVERFLOW will be returned +/// +/// ## Parameters +/// +/// * `addrs` - The buffer where addresses will be stored +/// +/// ## Return +/// +/// The number of addresses returned. +pub fn port_addr_list( + env: &WasiEnv, + addrs: WasmPtr<__wasi_cidr_t, M>, + naddrs: WasmPtr, +) -> __wasi_errno_t { + debug!("wasi::port_addr_list"); + let memory = env.memory(); + let max_addrs = wasi_try_mem!(naddrs.read(memory)); + let max_addrs: u64 = wasi_try!(max_addrs.try_into().map_err(|_| __WASI_EOVERFLOW)); + let ref_addrs = + wasi_try_mem!(addrs.slice(memory, wasi_try!(to_offset::(max_addrs as usize)))); + + let addrs = wasi_try!(env.net().ip_list().map_err(net_error_into_wasi_err)); + + let addrs_len: M::Offset = wasi_try!(addrs.len().try_into().map_err(|_| __WASI_EOVERFLOW)); + wasi_try_mem!(naddrs.write(memory, addrs_len)); + if addrs.len() as u64 > max_addrs { + return __WASI_EOVERFLOW; + } + + for n in 0..addrs.len() { + let nip = ref_addrs.index(n as u64); + super::state::write_cidr(memory, nip.as_ptr::(), *addrs.get(n).unwrap()); + } + + __WASI_ESUCCESS +} + +/// ### `port_gateway_set()` +/// Adds a default gateway to the port +/// +/// ## Parameters +/// +/// * `addr` - Address of the default gateway +pub fn port_gateway_set( + env: &WasiEnv, + ip: WasmPtr<__wasi_addr_t, M>, +) -> __wasi_errno_t { + debug!("wasi::port_gateway_set"); + let memory = env.memory(); + let ip = wasi_try!(super::state::read_ip(memory, ip)); + + wasi_try!(env.net().gateway_set(ip).map_err(net_error_into_wasi_err)); + __WASI_ESUCCESS +} + +/// ### `port_route_add()` +/// Adds a new route to the local port +pub fn port_route_add( + env: &WasiEnv, + cidr: WasmPtr<__wasi_cidr_t, M>, + via_router: WasmPtr<__wasi_addr_t, M>, + preferred_until: WasmPtr<__wasi_option_timestamp_t, M>, + expires_at: WasmPtr<__wasi_option_timestamp_t, M>, +) -> __wasi_errno_t { + debug!("wasi::port_route_add"); + let memory = env.memory(); + let cidr = wasi_try!(super::state::read_cidr(memory, cidr)); + let via_router = wasi_try!(super::state::read_ip(memory, via_router)); + let preferred_until = wasi_try_mem!(preferred_until.read(memory)); + let preferred_until = match preferred_until.tag { + __WASI_OPTION_NONE => None, + __WASI_OPTION_SOME => Some(Duration::from_nanos(preferred_until.u)), + _ => return __WASI_EINVAL, + }; + let expires_at = wasi_try_mem!(expires_at.read(memory)); + let expires_at = match expires_at.tag { + __WASI_OPTION_NONE => None, + __WASI_OPTION_SOME => Some(Duration::from_nanos(expires_at.u)), + _ => return __WASI_EINVAL, + }; + + wasi_try!(env + .net() + .route_add(cidr, via_router, preferred_until, expires_at) + .map_err(net_error_into_wasi_err)); + __WASI_ESUCCESS +} + +/// ### `port_route_remove()` +/// Removes an existing route from the local port +pub fn port_route_remove( + env: &WasiEnv, + ip: WasmPtr<__wasi_addr_t, M>, +) -> __wasi_errno_t { + debug!("wasi::port_route_remove"); + let memory = env.memory(); + let ip = wasi_try!(super::state::read_ip(memory, ip)); + wasi_try!(env.net().route_remove(ip).map_err(net_error_into_wasi_err)); + __WASI_ESUCCESS +} + +/// ### `port_route_clear()` +/// Clears all the routes in the local port +pub fn port_route_clear(env: &WasiEnv) -> __wasi_errno_t { + debug!("wasi::port_route_clear"); + wasi_try!(env.net().route_clear().map_err(net_error_into_wasi_err)); + __WASI_ESUCCESS +} + +/// ### `port_route_list()` +/// Returns a list of all the routes owned by the local port +/// This function fills the output buffer as much as possible. +/// If the buffer is too small this will return EOVERFLOW and +/// fill nroutes with the size of the buffer needed. +/// +/// ## Parameters +/// +/// * `routes` - The buffer where routes will be stored +pub fn port_route_list( + env: &WasiEnv, + routes: WasmPtr<__wasi_route_t, M>, + nroutes: WasmPtr, +) -> __wasi_errno_t { + debug!("wasi::port_route_list"); + let memory = env.memory(); + let nroutes = nroutes.deref(memory); + let max_routes: usize = wasi_try!(wasi_try_mem!(nroutes.read()) + .try_into() + .map_err(|_| __WASI_EINVAL)); + let ref_routes = wasi_try_mem!(routes.slice(memory, wasi_try!(to_offset::(max_routes)))); + + let routes = wasi_try!(env.net().route_list().map_err(net_error_into_wasi_err)); + + let routes_len: M::Offset = wasi_try!(routes.len().try_into().map_err(|_| __WASI_EINVAL)); + wasi_try_mem!(nroutes.write(routes_len)); + if routes.len() > max_routes { + return __WASI_EOVERFLOW; + } + + for n in 0..routes.len() { + let nroute = ref_routes.index(n as u64); + super::state::write_route(memory, nroute.as_ptr::(), routes.get(n).unwrap().clone()); + } + + __WASI_ESUCCESS +} + +/// ### `sock_shutdown()` +/// Shut down socket send and receive channels. +/// Note: This is similar to `shutdown` in POSIX. +/// +/// ## Parameters +/// +/// * `how` - Which channels on the socket to shut down. +pub fn sock_shutdown(env: &WasiEnv, sock: __wasi_fd_t, how: __wasi_sdflags_t) -> __wasi_errno_t { + debug!("wasi::sock_shutdown"); + + let both = __WASI_SHUT_RD | __WASI_SHUT_WR; + let how = match how { + __WASI_SHUT_RD => std::net::Shutdown::Read, + __WASI_SHUT_WR => std::net::Shutdown::Write, + a if a == both => std::net::Shutdown::Both, + _ => return __WASI_EINVAL, + }; + + wasi_try!(__sock_actor_mut( + env, + sock, + __WASI_RIGHT_SOCK_SHUTDOWN, + |socket| { socket.shutdown(how) } + )); + + __WASI_ESUCCESS +} + +/// ### `sock_status()` +/// Returns the current status of a socket +pub fn sock_status( + env: &WasiEnv, + sock: __wasi_fd_t, + ret_status: WasmPtr<__wasi_sockstatus_t, M>, +) -> __wasi_errno_t { + debug!("wasi::sock_status"); + + let status = wasi_try!(__sock_actor(env, sock, 0, |socket| { socket.status() })); + + use super::state::WasiSocketStatus; + let status = match status { + WasiSocketStatus::Opening => __WASI_SOCK_STATUS_OPENING, + WasiSocketStatus::Opened => __WASI_SOCK_STATUS_OPENED, + WasiSocketStatus::Closed => __WASI_SOCK_STATUS_CLOSED, + WasiSocketStatus::Failed => __WASI_SOCK_STATUS_FAILED, + }; + + wasi_try_mem!(ret_status.write(env.memory(), status)); + + __WASI_ESUCCESS +} + +/// ### `sock_addr_local()` +/// Returns the local address to which the socket is bound. +/// +/// Note: This is similar to `getsockname` in POSIX +/// +/// When successful, the contents of the output buffer consist of an IP address, +/// either IP4 or IP6. +/// +/// ## Parameters +/// +/// * `fd` - Socket that the address is bound to +pub fn sock_addr_local( + env: &WasiEnv, + sock: __wasi_fd_t, + ret_addr: WasmPtr<__wasi_addr_port_t, M>, +) -> __wasi_errno_t { + debug!("wasi::sock_addr_local"); + + let addr = wasi_try!(__sock_actor(env, sock, 0, |socket| { socket.addr_local() })); + wasi_try!(super::state::write_ip_port( + env.memory(), + ret_addr, + addr.ip(), + addr.port() + )); + __WASI_ESUCCESS +} + +/// ### `sock_addr_peer()` +/// Returns the remote address to which the socket is connected to. +/// +/// Note: This is similar to `getpeername` in POSIX +/// +/// When successful, the contents of the output buffer consist of an IP address, +/// either IP4 or IP6. +/// +/// ## Parameters +/// +/// * `fd` - Socket that the address is bound to +pub fn sock_addr_peer( + env: &WasiEnv, + sock: __wasi_fd_t, + ro_addr: WasmPtr<__wasi_addr_port_t, M>, +) -> __wasi_errno_t { + debug!("wasi::sock_addr_peer"); + + let addr = wasi_try!(__sock_actor(env, sock, 0, |socket| { socket.addr_peer() })); + wasi_try!(super::state::write_ip_port( + env.memory(), + ro_addr, + addr.ip(), + addr.port() + )); + __WASI_ESUCCESS +} + +/// ### `sock_open()` +/// Create an endpoint for communication. +/// +/// creates an endpoint for communication and returns a file descriptor +/// tor that refers to that endpoint. The file descriptor returned by a successful +/// call will be the lowest-numbered file descriptor not currently open +/// for the process. +/// +/// Note: This is similar to `socket` in POSIX using PF_INET +/// +/// ## Parameters +/// +/// * `af` - Address family +/// * `socktype` - Socket type, either datagram or stream +/// * `sock_proto` - Socket protocol +/// +/// ## Return +/// +/// The file descriptor of the socket that has been opened. +pub fn sock_open( + env: &WasiEnv, + af: __wasi_addressfamily_t, + ty: __wasi_socktype_t, + pt: __wasi_sockproto_t, + ro_sock: WasmPtr<__wasi_fd_t, M>, +) -> __wasi_errno_t { + debug!("wasi::sock_open"); + + let (memory, state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(0); + + let kind = match ty { + __WASI_SOCK_TYPE_STREAM | __WASI_SOCK_TYPE_DGRAM => Kind::Socket { + socket: InodeSocket::new(InodeSocketKind::PreSocket { + family: af, + ty, + pt, + addr: None, + only_v6: false, + reuse_port: false, + reuse_addr: false, + send_buf_size: None, + recv_buf_size: None, + send_timeout: None, + recv_timeout: None, + connect_timeout: None, + accept_timeout: None, + }), + }, + _ => return __WASI_ENOTSUP, + }; + + let inode = state.fs.create_inode_with_default_stat( + inodes.deref_mut(), + kind, + false, + "socket".to_string(), + ); + let rights = super::state::all_socket_rights(); + let fd = wasi_try!(state.fs.create_fd(rights, rights, 0, 0, inode)); + + wasi_try_mem!(ro_sock.write(memory, fd)); + + __WASI_ESUCCESS +} + +/// ### `sock_set_opt_flag()` +/// Sets a particular socket setting +/// Note: This is similar to `setsockopt` in POSIX for SO_REUSEADDR +/// +/// ## Parameters +/// +/// * `fd` - Socket descriptor +/// * `sockopt` - Socket option to be set +/// * `flag` - Value to set the option to +pub fn sock_set_opt_flag( + env: &WasiEnv, + sock: __wasi_fd_t, + opt: __wasi_sockoption_t, + flag: __wasi_bool_t, +) -> __wasi_errno_t { + debug!("wasi::sock_set_opt_flag(ty={})", opt); + + let flag = match flag { + __WASI_BOOL_FALSE => false, + __WASI_BOOL_TRUE => true, + _ => return __WASI_EINVAL, + }; + + let option: super::state::WasiSocketOption = opt.into(); + wasi_try!(__sock_actor_mut(env, sock, 0, |socket| { + socket.set_opt_flag(option, flag) + })); + __WASI_ESUCCESS +} + +/// ### `sock_get_opt_flag()` +/// Retrieve status of particular socket seting +/// Note: This is similar to `getsockopt` in POSIX for SO_REUSEADDR +/// +/// ## Parameters +/// +/// * `fd` - Socket descriptor +/// * `sockopt` - Socket option to be retrieved +pub fn sock_get_opt_flag( + env: &WasiEnv, + sock: __wasi_fd_t, + opt: __wasi_sockoption_t, + ret_flag: WasmPtr<__wasi_bool_t, M>, +) -> __wasi_errno_t { + debug!("wasi::sock_get_opt_flag(ty={})", opt); + let memory = env.memory(); + + let option: super::state::WasiSocketOption = opt.into(); + let flag = wasi_try!(__sock_actor(env, sock, 0, |socket| { + socket.get_opt_flag(option) + })); + let flag = match flag { + false => __WASI_BOOL_FALSE, + true => __WASI_BOOL_TRUE, + }; + + wasi_try_mem!(ret_flag.write(memory, flag)); + + __WASI_ESUCCESS +} + +/// ### `sock_set_opt_time()` +/// Sets one of the times the socket +/// +/// ## Parameters +/// +/// * `fd` - Socket descriptor +/// * `sockopt` - Socket option to be set +/// * `time` - Value to set the time to +pub fn sock_set_opt_time( + env: &WasiEnv, + sock: __wasi_fd_t, + opt: __wasi_sockoption_t, + time: WasmPtr<__wasi_option_timestamp_t, M>, +) -> __wasi_errno_t { + debug!("wasi::sock_set_opt_time(ty={})", opt); + + let memory = env.memory(); + let time = wasi_try_mem!(time.read(memory)); + let time = match time.tag { + __WASI_OPTION_NONE => None, + __WASI_OPTION_SOME => Some(Duration::from_nanos(time.u)), + _ => return __WASI_EINVAL, + }; + + let ty = match opt { + __WASI_SOCK_OPTION_RECV_TIMEOUT => wasmer_vnet::TimeType::ReadTimeout, + __WASI_SOCK_OPTION_SEND_TIMEOUT => wasmer_vnet::TimeType::WriteTimeout, + __WASI_SOCK_OPTION_CONNECT_TIMEOUT => wasmer_vnet::TimeType::ConnectTimeout, + __WASI_SOCK_OPTION_ACCEPT_TIMEOUT => wasmer_vnet::TimeType::AcceptTimeout, + __WASI_SOCK_OPTION_LINGER => wasmer_vnet::TimeType::Linger, + _ => return __WASI_EINVAL, + }; + + let option: super::state::WasiSocketOption = opt.into(); + wasi_try!(__sock_actor_mut(env, sock, 0, |socket| { + socket.set_opt_time(ty, time) + })); + __WASI_ESUCCESS +} + +/// ### `sock_get_opt_time()` +/// Retrieve one of the times on the socket +/// +/// ## Parameters +/// +/// * `fd` - Socket descriptor +/// * `sockopt` - Socket option to be retrieved +pub fn sock_get_opt_time( + env: &WasiEnv, + sock: __wasi_fd_t, + opt: __wasi_sockoption_t, + ret_time: WasmPtr<__wasi_option_timestamp_t, M>, +) -> __wasi_errno_t { + debug!("wasi::sock_get_opt_time(ty={})", opt); + let memory = env.memory(); + + let ty = match opt { + __WASI_SOCK_OPTION_RECV_TIMEOUT => wasmer_vnet::TimeType::ReadTimeout, + __WASI_SOCK_OPTION_SEND_TIMEOUT => wasmer_vnet::TimeType::WriteTimeout, + __WASI_SOCK_OPTION_CONNECT_TIMEOUT => wasmer_vnet::TimeType::ConnectTimeout, + __WASI_SOCK_OPTION_ACCEPT_TIMEOUT => wasmer_vnet::TimeType::AcceptTimeout, + __WASI_SOCK_OPTION_LINGER => wasmer_vnet::TimeType::Linger, + _ => return __WASI_EINVAL, + }; + + let time = wasi_try!(__sock_actor(env, sock, 0, |socket| { socket.opt_time(ty) })); + let time = match time { + None => __wasi_option_timestamp_t { + tag: __WASI_OPTION_NONE, + u: 0, + }, + Some(timeout) => __wasi_option_timestamp_t { + tag: __WASI_OPTION_SOME, + u: timeout.as_nanos() as __wasi_timestamp_t, + }, + }; + + wasi_try_mem!(ret_time.write(memory, time)); + + __WASI_ESUCCESS +} + +/// ### `sock_set_opt_size() +/// Set size of particular option for this socket +/// Note: This is similar to `setsockopt` in POSIX for SO_RCVBUF +/// +/// ## Parameters +/// +/// * `fd` - Socket descriptor +/// * `opt` - Socket option to be set +/// * `size` - Buffer size +pub fn sock_set_opt_size( + env: &WasiEnv, + sock: __wasi_fd_t, + opt: __wasi_sockoption_t, + size: __wasi_filesize_t, +) -> __wasi_errno_t { + debug!("wasi::sock_set_opt_size(ty={})", opt); + + let ty = match opt { + __WASI_SOCK_OPTION_RECV_TIMEOUT => wasmer_vnet::TimeType::ReadTimeout, + __WASI_SOCK_OPTION_SEND_TIMEOUT => wasmer_vnet::TimeType::WriteTimeout, + __WASI_SOCK_OPTION_CONNECT_TIMEOUT => wasmer_vnet::TimeType::ConnectTimeout, + __WASI_SOCK_OPTION_ACCEPT_TIMEOUT => wasmer_vnet::TimeType::AcceptTimeout, + __WASI_SOCK_OPTION_LINGER => wasmer_vnet::TimeType::Linger, + _ => return __WASI_EINVAL, + }; + + let option: super::state::WasiSocketOption = opt.into(); + wasi_try!(__sock_actor_mut(env, sock, 0, |socket| { + match opt { + __WASI_SOCK_OPTION_RECV_BUF_SIZE => socket.set_recv_buf_size(size as usize), + __WASI_SOCK_OPTION_SEND_BUF_SIZE => socket.set_send_buf_size(size as usize), + __WASI_SOCK_OPTION_TTL => socket.set_ttl(size as u32), + __WASI_SOCK_OPTION_MULTICAST_TTL_V4 => socket.set_multicast_ttl_v4(size as u32), + _ => Err(__WASI_EINVAL), + } + })); + __WASI_ESUCCESS +} + +/// ### `sock_get_opt_size()` +/// Retrieve the size of particular option for this socket +/// Note: This is similar to `getsockopt` in POSIX for SO_RCVBUF +/// +/// ## Parameters +/// +/// * `fd` - Socket descriptor +/// * `sockopt` - Socket option to be retrieved +pub fn sock_get_opt_size( + env: &WasiEnv, + sock: __wasi_fd_t, + opt: __wasi_sockoption_t, + ret_size: WasmPtr<__wasi_filesize_t, M>, +) -> __wasi_errno_t { + debug!("wasi::sock_get_opt_size(ty={})", opt); + let memory = env.memory(); + + let size = wasi_try!(__sock_actor(env, sock, 0, |socket| { + match opt { + __WASI_SOCK_OPTION_RECV_BUF_SIZE => { + socket.recv_buf_size().map(|a| a as __wasi_filesize_t) + } + __WASI_SOCK_OPTION_SEND_BUF_SIZE => { + socket.send_buf_size().map(|a| a as __wasi_filesize_t) + } + __WASI_SOCK_OPTION_TTL => socket.ttl().map(|a| a as __wasi_filesize_t), + __WASI_SOCK_OPTION_MULTICAST_TTL_V4 => { + socket.multicast_ttl_v4().map(|a| a as __wasi_filesize_t) + } + _ => Err(__WASI_EINVAL), + } + })); + wasi_try_mem!(ret_size.write(memory, size)); + + __WASI_ESUCCESS +} + +/// ### `sock_join_multicast_v4()` +/// Joins a particular multicast IPv4 group +/// +/// ## Parameters +/// +/// * `fd` - Socket descriptor +/// * `multiaddr` - Multicast group to joined +/// * `interface` - Interface that will join +pub fn sock_join_multicast_v4( + env: &WasiEnv, + sock: __wasi_fd_t, + multiaddr: WasmPtr<__wasi_addr_ip4_t, M>, + iface: WasmPtr<__wasi_addr_ip4_t, M>, +) -> __wasi_errno_t { + debug!("wasi::sock_join_multicast_v4"); + + let memory = env.memory(); + let multiaddr = wasi_try!(super::state::read_ip_v4(memory, multiaddr)); + let iface = wasi_try!(super::state::read_ip_v4(memory, iface)); + wasi_try!(__sock_actor_mut(env, sock, 0, |socket| { + socket.join_multicast_v4(multiaddr, iface) + })); + __WASI_ESUCCESS +} + +/// ### `sock_leave_multicast_v4()` +/// Leaves a particular multicast IPv4 group +/// +/// ## Parameters +/// +/// * `fd` - Socket descriptor +/// * `multiaddr` - Multicast group to leave +/// * `interface` - Interface that will left +pub fn sock_leave_multicast_v4( + env: &WasiEnv, + sock: __wasi_fd_t, + multiaddr: WasmPtr<__wasi_addr_ip4_t, M>, + iface: WasmPtr<__wasi_addr_ip4_t, M>, +) -> __wasi_errno_t { + debug!("wasi::sock_leave_multicast_v4"); + + let memory = env.memory(); + let multiaddr = wasi_try!(super::state::read_ip_v4(memory, multiaddr)); + let iface = wasi_try!(super::state::read_ip_v4(memory, iface)); + wasi_try!(__sock_actor_mut(env, sock, 0, |socket| { + socket.leave_multicast_v4(multiaddr, iface) + })); + __WASI_ESUCCESS +} + +/// ### `sock_join_multicast_v6()` +/// Joins a particular multicast IPv6 group +/// +/// ## Parameters +/// +/// * `fd` - Socket descriptor +/// * `multiaddr` - Multicast group to joined +/// * `interface` - Interface that will join +pub fn sock_join_multicast_v6( + env: &WasiEnv, + sock: __wasi_fd_t, + multiaddr: WasmPtr<__wasi_addr_ip6_t, M>, + iface: u32, +) -> __wasi_errno_t { + debug!("wasi::sock_join_multicast_v6"); + + let memory = env.memory(); + let multiaddr = wasi_try!(super::state::read_ip_v6(memory, multiaddr)); + wasi_try!(__sock_actor_mut(env, sock, 0, |socket| { + socket.join_multicast_v6(multiaddr, iface) + })); + __WASI_ESUCCESS +} + +/// ### `sock_leave_multicast_v6()` +/// Leaves a particular multicast IPv6 group +/// +/// ## Parameters +/// +/// * `fd` - Socket descriptor +/// * `multiaddr` - Multicast group to leave +/// * `interface` - Interface that will left +pub fn sock_leave_multicast_v6( + env: &WasiEnv, + sock: __wasi_fd_t, + multiaddr: WasmPtr<__wasi_addr_ip6_t, M>, + iface: u32, +) -> __wasi_errno_t { + debug!("wasi::sock_leave_multicast_v6"); + + let memory = env.memory(); + let multiaddr = wasi_try!(super::state::read_ip_v6(memory, multiaddr)); + wasi_try!(__sock_actor_mut(env, sock, 0, |socket| { + socket.leave_multicast_v6(multiaddr, iface) + })); + __WASI_ESUCCESS +} + +/// ### `sock_bind()` +/// Bind a socket +/// Note: This is similar to `bind` in POSIX using PF_INET +/// +/// ## Parameters +/// +/// * `fd` - File descriptor of the socket to be bind +/// * `addr` - Address to bind the socket to +pub fn sock_bind( + env: &WasiEnv, + sock: __wasi_fd_t, + addr: WasmPtr<__wasi_addr_port_t, M>, +) -> __wasi_errno_t { + debug!("wasi::sock_bind"); + + let addr = wasi_try!(super::state::read_ip_port(env.memory(), addr)); + let addr = SocketAddr::new(addr.0, addr.1); + wasi_try!(__sock_upgrade( + env, + sock, + __WASI_RIGHT_SOCK_BIND, + |socket| { socket.bind(env.net(), addr) } + )); + __WASI_ESUCCESS +} + +/// ### `sock_listen()` +/// Listen for connections on a socket +/// +/// Polling the socket handle will wait until a connection +/// attempt is made +/// +/// Note: This is similar to `listen` +/// +/// ## Parameters +/// +/// * `fd` - File descriptor of the socket to be bind +/// * `backlog` - Maximum size of the queue for pending connections +pub fn sock_listen( + env: &WasiEnv, + sock: __wasi_fd_t, + backlog: M::Offset, +) -> __wasi_errno_t { + debug!("wasi::sock_listen"); + + let backlog: usize = wasi_try!(backlog.try_into().map_err(|_| __WASI_EINVAL)); + wasi_try!(__sock_upgrade( + env, + sock, + __WASI_RIGHT_SOCK_BIND, + |socket| { socket.listen(env.net(), backlog) } + )); + __WASI_ESUCCESS +} + +/// ### `sock_accept()` +/// Accept a new incoming connection. +/// Note: This is similar to `accept` in POSIX. +/// +/// ## Parameters +/// +/// * `fd` - The listening socket. +/// * `flags` - The desired values of the file descriptor flags. +/// +/// ## Return +/// +/// New socket connection +pub fn sock_accept( + env: &WasiEnv, + sock: __wasi_fd_t, + fd_flags: __wasi_fdflags_t, + ro_fd: WasmPtr<__wasi_fd_t, M>, + ro_addr: WasmPtr<__wasi_addr_port_t, M>, +) -> Result<__wasi_errno_t, WasiError> { + debug!("wasi::sock_accept"); + + let (child, addr) = { + let mut ret; + let (_, state) = env.get_memory_and_wasi_state(0); + loop { + wasi_try_ok!( + match __sock_actor(env, sock, __WASI_RIGHT_SOCK_ACCEPT, |socket| socket + .accept_timeout(fd_flags, Duration::from_millis(5))) + { + Ok(a) => { + ret = a; + break; + } + Err(__WASI_ETIMEDOUT) => { + env.yield_now()?; + continue; + } + Err(__WASI_EAGAIN) => { + env.sleep(Duration::from_millis(5))?; + continue; + } + Err(err) => Err(err), + } + ); + } + ret + }; + + let (memory, state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(0); + + let kind = Kind::Socket { + socket: InodeSocket::new(InodeSocketKind::TcpStream(child)), + }; + let inode = state.fs.create_inode_with_default_stat( + inodes.deref_mut(), + kind, + false, + "socket".to_string(), + ); + + let rights = super::state::all_socket_rights(); + let fd = wasi_try_ok!(state.fs.create_fd(rights, rights, 0, 0, inode)); + + wasi_try_mem_ok!(ro_fd.write(memory, fd)); + wasi_try_ok!(super::state::write_ip_port( + memory, + ro_addr, + addr.ip(), + addr.port() + )); + + Ok(__WASI_ESUCCESS) +} + +/// ### `sock_connect()` +/// Initiate a connection on a socket to the specified address +/// +/// Polling the socket handle will wait for data to arrive or for +/// the socket status to change which can be queried via 'sock_status' +/// +/// Note: This is similar to `connect` in POSIX +/// +/// ## Parameters +/// +/// * `fd` - Socket descriptor +/// * `addr` - Address of the socket to connect to +pub fn sock_connect( + env: &WasiEnv, + sock: __wasi_fd_t, + addr: WasmPtr<__wasi_addr_port_t, M>, +) -> __wasi_errno_t { + debug!("wasi::sock_connect"); + + let addr = wasi_try!(super::state::read_ip_port(env.memory(), addr)); + let addr = SocketAddr::new(addr.0, addr.1); + wasi_try!(__sock_upgrade( + env, + sock, + __WASI_RIGHT_SOCK_CONNECT, + |socket| { socket.connect(env.net(), addr) } + )); + __WASI_ESUCCESS +} + +/// ### `sock_recv()` +/// Receive a message from a socket. +/// Note: This is similar to `recv` in POSIX, though it also supports reading +/// the data into multiple buffers in the manner of `readv`. +/// +/// ## Parameters +/// +/// * `ri_data` - List of scatter/gather vectors to which to store data. +/// * `ri_flags` - Message flags. +/// +/// ## Return +/// +/// Number of bytes stored in ri_data and message flags. +pub fn sock_recv( + env: &WasiEnv, + sock: __wasi_fd_t, + ri_data: WasmPtr<__wasi_iovec_t, M>, + ri_data_len: M::Offset, + _ri_flags: __wasi_riflags_t, + ro_data_len: WasmPtr, + ro_flags: WasmPtr<__wasi_roflags_t, M>, +) -> Result<__wasi_errno_t, WasiError> { + debug!("wasi::sock_recv"); + + let memory = env.memory(); + let iovs_arr = wasi_try_mem_ok!(ri_data.slice(memory, ri_data_len)); + + let bytes_read = wasi_try_ok!(__sock_actor_mut( + env, + sock, + __WASI_RIGHT_SOCK_RECV, + |socket| { socket.recv(memory, iovs_arr) } + )); + let bytes_read: M::Offset = wasi_try_ok!(bytes_read.try_into().map_err(|_| __WASI_EOVERFLOW)); + + wasi_try_mem_ok!(ro_flags.write(memory, 0)); + wasi_try_mem_ok!(ro_data_len.write(memory, bytes_read)); + + Ok(__WASI_ESUCCESS) +} + +/// ### `sock_recv_from()` +/// Receive a message and its peer address from a socket. +/// Note: This is similar to `recvfrom` in POSIX, though it also supports reading +/// the data into multiple buffers in the manner of `readv`. +/// +/// ## Parameters +/// +/// * `ri_data` - List of scatter/gather vectors to which to store data. +/// * `ri_flags` - Message flags. +/// +/// ## Return +/// +/// Number of bytes stored in ri_data and message flags. +pub fn sock_recv_from( + env: &WasiEnv, + sock: __wasi_fd_t, + ri_data: WasmPtr<__wasi_iovec_t, M>, + ri_data_len: M::Offset, + _ri_flags: __wasi_riflags_t, + ro_data_len: WasmPtr, + ro_flags: WasmPtr<__wasi_roflags_t, M>, + ro_addr: WasmPtr<__wasi_addr_port_t, M>, +) -> Result<__wasi_errno_t, WasiError> { + debug!("wasi::sock_recv_from"); + + let memory = env.memory(); + let iovs_arr = wasi_try_mem_ok!(ri_data.slice(memory, ri_data_len)); + + let bytes_read = wasi_try_ok!(__sock_actor_mut( + env, + sock, + __WASI_RIGHT_SOCK_RECV_FROM, + |socket| { socket.recv_from(memory, iovs_arr, ro_addr) } + )); + let bytes_read: M::Offset = wasi_try_ok!(bytes_read.try_into().map_err(|_| __WASI_EOVERFLOW)); + + wasi_try_mem_ok!(ro_flags.write(memory, 0)); + wasi_try_mem_ok!(ro_data_len.write(memory, bytes_read)); + + Ok(__WASI_ESUCCESS) +} + +/// ### `sock_send()` +/// Send a message on a socket. +/// Note: This is similar to `send` in POSIX, though it also supports writing +/// the data from multiple buffers in the manner of `writev`. +/// +/// ## Parameters +/// +/// * `si_data` - List of scatter/gather vectors to which to retrieve data +/// * `si_flags` - Message flags. +/// +/// ## Return +/// +/// Number of bytes transmitted. +pub fn sock_send( + env: &WasiEnv, + sock: __wasi_fd_t, + si_data: WasmPtr<__wasi_ciovec_t, M>, + si_data_len: M::Offset, + _si_flags: __wasi_siflags_t, + ret_data_len: WasmPtr, +) -> Result<__wasi_errno_t, WasiError> { + debug!("wasi::sock_send"); + + let memory = env.memory(); + let iovs_arr = wasi_try_mem_ok!(si_data.slice(memory, si_data_len)); + + let bytes_written = wasi_try_ok!(__sock_actor_mut( + env, + sock, + __WASI_RIGHT_SOCK_SEND, + |socket| { socket.send(memory, iovs_arr) } + )); + + let bytes_written: M::Offset = + wasi_try_ok!(bytes_written.try_into().map_err(|_| __WASI_EOVERFLOW)); + wasi_try_mem_ok!(ret_data_len.write(memory, bytes_written)); + + Ok(__WASI_ESUCCESS) +} + +/// ### `sock_send_to()` +/// Send a message on a socket to a specific address. +/// Note: This is similar to `sendto` in POSIX, though it also supports writing +/// the data from multiple buffers in the manner of `writev`. +/// +/// ## Parameters +/// +/// * `si_data` - List of scatter/gather vectors to which to retrieve data +/// * `si_flags` - Message flags. +/// * `addr` - Address of the socket to send message to +/// +/// ## Return +/// +/// Number of bytes transmitted. +pub fn sock_send_to( + env: &WasiEnv, + sock: __wasi_fd_t, + si_data: WasmPtr<__wasi_ciovec_t, M>, + si_data_len: M::Offset, + _si_flags: __wasi_siflags_t, + addr: WasmPtr<__wasi_addr_port_t, M>, + ret_data_len: WasmPtr, +) -> Result<__wasi_errno_t, WasiError> { + debug!("wasi::sock_send_to"); + + let memory = env.memory(); + let iovs_arr = wasi_try_mem_ok!(si_data.slice(memory, si_data_len)); + + let bytes_written = wasi_try_ok!(__sock_actor_mut( + env, + sock, + __WASI_RIGHT_SOCK_SEND_TO, + |socket| { socket.send_to::(memory, iovs_arr, addr) } + )); + + let bytes_written: M::Offset = + wasi_try_ok!(bytes_written.try_into().map_err(|_| __WASI_EOVERFLOW)); + wasi_try_mem_ok!(ret_data_len.write(memory, bytes_written as M::Offset)); + + Ok(__WASI_ESUCCESS) +} + +/// ### `sock_send_file()` +/// Sends the entire contents of a file down a socket +/// +/// ## Parameters +/// +/// * `in_fd` - Open file that has the data to be transmitted +/// * `offset` - Offset into the file to start reading at +/// * `count` - Number of bytes to be sent +/// +/// ## Return +/// +/// Number of bytes transmitted. +pub unsafe fn sock_send_file( + env: &WasiEnv, + sock: __wasi_fd_t, + in_fd: __wasi_fd_t, + offset: __wasi_filesize_t, + mut count: __wasi_filesize_t, + ret_sent: WasmPtr<__wasi_filesize_t, M>, +) -> Result<__wasi_errno_t, WasiError> { + debug!("wasi::send_file"); + let (memory, state, inodes) = env.get_memory_and_wasi_state_and_inodes(0); + + // Set the offset of the file + { + let mut fd_map = state.fs.fd_map.write().unwrap(); + let fd_entry = wasi_try_ok!(fd_map.get_mut(&in_fd).ok_or(__WASI_EBADF)); + fd_entry.offset = offset as u64; + } + + // Enter a loop that will process all the data + let mut total_written: __wasi_filesize_t = 0; + while (count > 0) { + let mut buf = [0; 4096]; + let sub_count = count.min(4096); + count -= sub_count; + + let fd_entry = wasi_try_ok!(state.fs.get_fd(in_fd)); + let bytes_read = match in_fd { + __WASI_STDIN_FILENO => { + let mut guard = wasi_try_ok!( + inodes + .stdin_mut(&state.fs.fd_map) + .map_err(fs_error_into_wasi_err), + env + ); + if let Some(ref mut stdin) = guard.deref_mut() { + wasi_try_ok!(stdin.read(&mut buf).map_err(map_io_err)) + } else { + return Ok(__WASI_EBADF); + } + } + __WASI_STDOUT_FILENO | __WASI_STDERR_FILENO => return Ok(__WASI_EINVAL), + _ => { + if !has_rights(fd_entry.rights, __WASI_RIGHT_FD_READ) { + // TODO: figure out the error to return when lacking rights + return Ok(__WASI_EACCES); + } + + let offset = fd_entry.offset as usize; + let inode_idx = fd_entry.inode; + let inode = &inodes.arena[inode_idx]; + + let bytes_read = { + let mut guard = inode.write(); + match guard.deref_mut() { + Kind::File { handle, .. } => { + if let Some(handle) = handle { + wasi_try_ok!( + handle + .seek(std::io::SeekFrom::Start(offset as u64)) + .map_err(map_io_err), + env + ); + wasi_try_ok!(handle.read(&mut buf).map_err(map_io_err)) + } else { + return Ok(__WASI_EINVAL); + } + } + Kind::Socket { socket } => { + wasi_try_ok!(socket.read(&mut buf).map_err(map_io_err)) + } + Kind::Pipe { pipe } => { + wasi_try_ok!(pipe.read(&mut buf).map_err(map_io_err)) + } + Kind::Dir { .. } | Kind::Root { .. } => { + return Ok(__WASI_EISDIR); + } + Kind::EventNotifications { .. } => { + return Ok(__WASI_EINVAL); + } + Kind::Symlink { .. } => unimplemented!("Symlinks in wasi::fd_read"), + Kind::Buffer { buffer } => { + let mut buf_read = &buffer[offset..]; + wasi_try_ok!(buf_read.read(&mut buf).map_err(map_io_err)) + } + } + }; + + // reborrow + let mut fd_map = state.fs.fd_map.write().unwrap(); + let fd_entry = wasi_try_ok!(fd_map.get_mut(&in_fd).ok_or(__WASI_EBADF)); + fd_entry.offset += bytes_read as u64; + + bytes_read + } + }; + + // Write it down to the socket + let bytes_written = wasi_try_ok!(__sock_actor_mut( + env, + sock, + __WASI_RIGHT_SOCK_SEND, + |socket| { + let buf = (&buf[..]).to_vec(); + socket.send_bytes::(Bytes::from(buf)) + } + )); + total_written += bytes_written as u64; + } + + wasi_try_mem_ok!(ret_sent.write(memory, total_written as __wasi_filesize_t)); + + Ok(__WASI_ESUCCESS) +} + +/// ### `resolve()` +/// Resolves a hostname and a port to one or more IP addresses. +/// +/// Note: This is similar to `getaddrinfo` in POSIX +/// +/// When successful, the contents of the output buffer consist of a sequence of +/// IPv4 and/or IPv6 addresses. Each address entry consists of a addr_t object. +/// This function fills the output buffer as much as possible. +/// +/// ## Parameters +/// +/// * `host` - Host to resolve +/// * `port` - Port hint (zero if no hint is supplied) +/// * `addrs` - The buffer where addresses will be stored +/// +/// ## Return +/// +/// The number of IP addresses returned during the DNS resolution. +pub fn resolve( + env: &WasiEnv, + host: WasmPtr, + host_len: M::Offset, + port: u16, + addrs: WasmPtr<__wasi_addr_t, M>, + naddrs: M::Offset, + ret_naddrs: WasmPtr, +) -> __wasi_errno_t { + debug!("wasi::resolve"); + + let naddrs: usize = wasi_try!(naddrs.try_into().map_err(|_| __WASI_EINVAL)); + let memory = env.memory(); + let host_str = unsafe { get_input_str!(memory, host, host_len) }; + let addrs = wasi_try_mem!(addrs.slice(memory, wasi_try!(to_offset::(naddrs)))); + + let port = if port > 0 { Some(port) } else { None }; + + let found_ips = wasi_try!(env + .net() + .resolve(host_str.as_str(), port, None) + .map_err(net_error_into_wasi_err)); + + let mut idx = 0; + for found_ip in found_ips.iter().take(naddrs) { + super::state::write_ip(memory, addrs.index(idx).as_ptr::(), *found_ip); + idx += 1; + } + + let idx: M::Offset = wasi_try!(idx.try_into().map_err(|_| __WASI_EOVERFLOW)); + wasi_try_mem!(ret_naddrs.write(memory, idx)); + + __WASI_ESUCCESS } diff --git a/lib/wasi/src/syscalls/unix/mod.rs b/lib/wasi/src/syscalls/unix/mod.rs index 2b494e589d5..9e3a5e35d16 100644 --- a/lib/wasi/src/syscalls/unix/mod.rs +++ b/lib/wasi/src/syscalls/unix/mod.rs @@ -9,13 +9,13 @@ use wasmer::WasmRef; pub fn platform_clock_res_get( clock_id: __wasi_clockid_t, resolution: WasmRef<__wasi_timestamp_t>, -) -> __wasi_errno_t { +) -> Result { let unix_clock_id = match clock_id { __WASI_CLOCK_MONOTONIC => CLOCK_MONOTONIC, __WASI_CLOCK_PROCESS_CPUTIME_ID => CLOCK_PROCESS_CPUTIME_ID, __WASI_CLOCK_REALTIME => CLOCK_REALTIME, __WASI_CLOCK_THREAD_CPUTIME_ID => CLOCK_THREAD_CPUTIME_ID, - _ => return __WASI_EINVAL, + _ => return Err(__WASI_EINVAL), }; let (output, timespec_out) = unsafe { @@ -27,23 +27,19 @@ pub fn platform_clock_res_get( }; let t_out = (timespec_out.tv_sec * 1_000_000_000).wrapping_add(timespec_out.tv_nsec); - wasi_try_mem!(resolution.write(t_out as __wasi_timestamp_t)); - - // TODO: map output of clock_getres to __wasi_errno_t - __WASI_ESUCCESS + Ok(t_out) } pub fn platform_clock_time_get( clock_id: __wasi_clockid_t, precision: __wasi_timestamp_t, - time: WasmRef<__wasi_timestamp_t>, -) -> __wasi_errno_t { +) -> Result { let unix_clock_id = match clock_id { __WASI_CLOCK_MONOTONIC => CLOCK_MONOTONIC, __WASI_CLOCK_PROCESS_CPUTIME_ID => CLOCK_PROCESS_CPUTIME_ID, __WASI_CLOCK_REALTIME => CLOCK_REALTIME, __WASI_CLOCK_THREAD_CPUTIME_ID => CLOCK_THREAD_CPUTIME_ID, - _ => return __WASI_EINVAL, + _ => return Err(__WASI_EINVAL), }; let (output, timespec_out) = unsafe { @@ -58,8 +54,5 @@ pub fn platform_clock_time_get( }; let t_out = (timespec_out.tv_sec * 1_000_000_000).wrapping_add(timespec_out.tv_nsec); - wasi_try_mem!(time.write(t_out as __wasi_timestamp_t)); - - // TODO: map output of clock_gettime to __wasi_errno_t - __WASI_ESUCCESS + Ok(t_out) } diff --git a/lib/wasi/src/syscalls/wasi.rs b/lib/wasi/src/syscalls/wasi.rs new file mode 100644 index 00000000000..b7d1c28edc4 --- /dev/null +++ b/lib/wasi/src/syscalls/wasi.rs @@ -0,0 +1,443 @@ +#![deny(dead_code)] +use crate::{WasiEnv, WasiError, WasiState, WasiThread}; +use wasmer::{Memory, Memory32, MemorySize, WasmPtr, WasmSlice}; +use wasmer_wasi_types::*; + +type MemoryType = Memory32; +type MemoryOffset = u32; + +pub(crate) fn args_get( + env: &WasiEnv, + argv: WasmPtr, MemoryType>, + argv_buf: WasmPtr, +) -> __wasi_errno_t { + super::args_get::(env, argv, argv_buf) +} + +pub(crate) fn args_sizes_get( + env: &WasiEnv, + argc: WasmPtr, + argv_buf_size: WasmPtr, +) -> __wasi_errno_t { + super::args_sizes_get::(env, argc, argv_buf_size) +} + +pub(crate) fn clock_res_get( + env: &WasiEnv, + clock_id: __wasi_clockid_t, + resolution: WasmPtr<__wasi_timestamp_t, MemoryType>, +) -> __wasi_errno_t { + super::clock_res_get::(env, clock_id, resolution) +} + +pub(crate) fn clock_time_get( + env: &WasiEnv, + clock_id: __wasi_clockid_t, + precision: __wasi_timestamp_t, + time: WasmPtr<__wasi_timestamp_t, MemoryType>, +) -> __wasi_errno_t { + super::clock_time_get::(env, clock_id, precision, time) +} + +pub(crate) fn environ_get( + env: &WasiEnv, + environ: WasmPtr, MemoryType>, + environ_buf: WasmPtr, +) -> __wasi_errno_t { + super::environ_get::(env, environ, environ_buf) +} + +pub(crate) fn environ_sizes_get( + env: &WasiEnv, + environ_count: WasmPtr, + environ_buf_size: WasmPtr, +) -> __wasi_errno_t { + super::environ_sizes_get::(env, environ_count, environ_buf_size) +} + +pub(crate) fn fd_advise( + env: &WasiEnv, + fd: __wasi_fd_t, + offset: __wasi_filesize_t, + len: __wasi_filesize_t, + advice: __wasi_advice_t, +) -> __wasi_errno_t { + super::fd_advise(env, fd, offset, len, advice) +} + +pub(crate) fn fd_allocate( + env: &WasiEnv, + fd: __wasi_fd_t, + offset: __wasi_filesize_t, + len: __wasi_filesize_t, +) -> __wasi_errno_t { + super::fd_allocate(env, fd, offset, len) +} + +pub(crate) fn fd_close(env: &WasiEnv, fd: __wasi_fd_t) -> __wasi_errno_t { + super::fd_close(env, fd) +} + +pub(crate) fn fd_datasync(env: &WasiEnv, fd: __wasi_fd_t) -> __wasi_errno_t { + super::fd_datasync(env, fd) +} + +pub(crate) fn fd_fdstat_get( + env: &WasiEnv, + fd: __wasi_fd_t, + buf_ptr: WasmPtr<__wasi_fdstat_t, MemoryType>, +) -> __wasi_errno_t { + super::fd_fdstat_get::(env, fd, buf_ptr) +} + +pub(crate) fn fd_fdstat_set_flags( + env: &WasiEnv, + fd: __wasi_fd_t, + flags: __wasi_fdflags_t, +) -> __wasi_errno_t { + super::fd_fdstat_set_flags(env, fd, flags) +} + +pub(crate) fn fd_fdstat_set_rights( + env: &WasiEnv, + fd: __wasi_fd_t, + fs_rights_base: __wasi_rights_t, + fs_rights_inheriting: __wasi_rights_t, +) -> __wasi_errno_t { + super::fd_fdstat_set_rights(env, fd, fs_rights_base, fs_rights_inheriting) +} + +pub(crate) fn fd_filestat_get( + env: &WasiEnv, + fd: __wasi_fd_t, + buf: WasmPtr<__wasi_filestat_t, MemoryType>, +) -> __wasi_errno_t { + super::fd_filestat_get::(env, fd, buf) +} + +pub(crate) fn fd_filestat_set_size( + env: &WasiEnv, + fd: __wasi_fd_t, + st_size: __wasi_filesize_t, +) -> __wasi_errno_t { + super::fd_filestat_set_size(env, fd, st_size) +} + +pub(crate) fn fd_filestat_set_times( + env: &WasiEnv, + fd: __wasi_fd_t, + st_atim: __wasi_timestamp_t, + st_mtim: __wasi_timestamp_t, + fst_flags: __wasi_fstflags_t, +) -> __wasi_errno_t { + super::fd_filestat_set_times(env, fd, st_atim, st_mtim, fst_flags) +} + +pub(crate) fn fd_pread( + env: &WasiEnv, + fd: __wasi_fd_t, + iovs: WasmPtr<__wasi_iovec_t, MemoryType>, + iovs_len: MemoryOffset, + offset: __wasi_filesize_t, + nread: WasmPtr, +) -> Result<__wasi_errno_t, WasiError> { + super::fd_pread::(env, fd, iovs, iovs_len, offset, nread) +} + +pub(crate) fn fd_prestat_get( + env: &WasiEnv, + fd: __wasi_fd_t, + buf: WasmPtr<__wasi_prestat_t, MemoryType>, +) -> __wasi_errno_t { + super::fd_prestat_get::(env, fd, buf) +} + +pub(crate) fn fd_prestat_dir_name( + env: &WasiEnv, + fd: __wasi_fd_t, + path: WasmPtr, + path_len: MemoryOffset, +) -> __wasi_errno_t { + super::fd_prestat_dir_name::(env, fd, path, path_len) +} + +pub(crate) fn fd_pwrite( + env: &WasiEnv, + fd: __wasi_fd_t, + iovs: WasmPtr<__wasi_ciovec_t, MemoryType>, + iovs_len: MemoryOffset, + offset: __wasi_filesize_t, + nwritten: WasmPtr, +) -> Result<__wasi_errno_t, WasiError> { + super::fd_pwrite::(env, fd, iovs, iovs_len, offset, nwritten) +} + +pub(crate) fn fd_read( + env: &WasiEnv, + fd: __wasi_fd_t, + iovs: WasmPtr<__wasi_iovec_t, MemoryType>, + iovs_len: MemoryOffset, + nread: WasmPtr, +) -> Result<__wasi_errno_t, WasiError> { + super::fd_read::(env, fd, iovs, iovs_len, nread) +} + +pub(crate) fn fd_readdir( + env: &WasiEnv, + fd: __wasi_fd_t, + buf: WasmPtr, + buf_len: MemoryOffset, + cookie: __wasi_dircookie_t, + bufused: WasmPtr, +) -> __wasi_errno_t { + super::fd_readdir::(env, fd, buf, buf_len, cookie, bufused) +} + +pub(crate) fn fd_renumber(env: &WasiEnv, from: __wasi_fd_t, to: __wasi_fd_t) -> __wasi_errno_t { + super::fd_renumber(env, from, to) +} + +pub(crate) fn fd_seek( + env: &WasiEnv, + fd: __wasi_fd_t, + offset: __wasi_filedelta_t, + whence: __wasi_whence_t, + newoffset: WasmPtr<__wasi_filesize_t, MemoryType>, +) -> Result<__wasi_errno_t, WasiError> { + super::fd_seek::(env, fd, offset, whence, newoffset) +} + +pub(crate) fn fd_sync(env: &WasiEnv, fd: __wasi_fd_t) -> __wasi_errno_t { + super::fd_sync(env, fd) +} + +pub(crate) fn fd_tell( + env: &WasiEnv, + fd: __wasi_fd_t, + offset: WasmPtr<__wasi_filesize_t, MemoryType>, +) -> __wasi_errno_t { + super::fd_tell::(env, fd, offset) +} + +pub(crate) fn fd_write( + env: &WasiEnv, + fd: __wasi_fd_t, + iovs: WasmPtr<__wasi_ciovec_t, MemoryType>, + iovs_len: MemoryOffset, + nwritten: WasmPtr, +) -> Result<__wasi_errno_t, WasiError> { + super::fd_write::(env, fd, iovs, iovs_len, nwritten) +} + +pub(crate) fn path_create_directory( + env: &WasiEnv, + fd: __wasi_fd_t, + path: WasmPtr, + path_len: MemoryOffset, +) -> __wasi_errno_t { + super::path_create_directory::(env, fd, path, path_len) +} + +pub(crate) fn path_filestat_get( + env: &WasiEnv, + fd: __wasi_fd_t, + flags: __wasi_lookupflags_t, + path: WasmPtr, + path_len: MemoryOffset, + buf: WasmPtr<__wasi_filestat_t, MemoryType>, +) -> __wasi_errno_t { + super::path_filestat_get::(env, fd, flags, path, path_len, buf) +} + +pub(crate) fn path_filestat_set_times( + env: &WasiEnv, + fd: __wasi_fd_t, + flags: __wasi_lookupflags_t, + path: WasmPtr, + path_len: MemoryOffset, + st_atim: __wasi_timestamp_t, + st_mtim: __wasi_timestamp_t, + fst_flags: __wasi_fstflags_t, +) -> __wasi_errno_t { + super::path_filestat_set_times::( + env, fd, flags, path, path_len, st_atim, st_mtim, fst_flags, + ) +} + +pub(crate) fn path_link( + env: &WasiEnv, + old_fd: __wasi_fd_t, + old_flags: __wasi_lookupflags_t, + old_path: WasmPtr, + old_path_len: MemoryOffset, + new_fd: __wasi_fd_t, + new_path: WasmPtr, + new_path_len: MemoryOffset, +) -> __wasi_errno_t { + super::path_link::( + env, + old_fd, + old_flags, + old_path, + old_path_len, + new_fd, + new_path, + new_path_len, + ) +} + +pub(crate) fn path_open( + env: &WasiEnv, + dirfd: __wasi_fd_t, + dirflags: __wasi_lookupflags_t, + path: WasmPtr, + path_len: MemoryOffset, + o_flags: __wasi_oflags_t, + fs_rights_base: __wasi_rights_t, + fs_rights_inheriting: __wasi_rights_t, + fs_flags: __wasi_fdflags_t, + fd: WasmPtr<__wasi_fd_t, MemoryType>, +) -> __wasi_errno_t { + super::path_open::( + env, + dirfd, + dirflags, + path, + path_len, + o_flags, + fs_rights_base, + fs_rights_inheriting, + fs_flags, + fd, + ) +} + +pub(crate) fn path_readlink( + env: &WasiEnv, + dir_fd: __wasi_fd_t, + path: WasmPtr, + path_len: MemoryOffset, + buf: WasmPtr, + buf_len: MemoryOffset, + buf_used: WasmPtr, +) -> __wasi_errno_t { + super::path_readlink::(env, dir_fd, path, path_len, buf, buf_len, buf_used) +} + +pub(crate) fn path_remove_directory( + env: &WasiEnv, + fd: __wasi_fd_t, + path: WasmPtr, + path_len: MemoryOffset, +) -> __wasi_errno_t { + super::path_remove_directory::(env, fd, path, path_len) +} + +pub(crate) fn path_rename( + env: &WasiEnv, + old_fd: __wasi_fd_t, + old_path: WasmPtr, + old_path_len: MemoryOffset, + new_fd: __wasi_fd_t, + new_path: WasmPtr, + new_path_len: MemoryOffset, +) -> __wasi_errno_t { + super::path_rename::( + env, + old_fd, + old_path, + old_path_len, + new_fd, + new_path, + new_path_len, + ) +} + +pub(crate) fn path_symlink( + env: &WasiEnv, + old_path: WasmPtr, + old_path_len: MemoryOffset, + fd: __wasi_fd_t, + new_path: WasmPtr, + new_path_len: MemoryOffset, +) -> __wasi_errno_t { + super::path_symlink::(env, old_path, old_path_len, fd, new_path, new_path_len) +} + +pub(crate) fn path_unlink_file( + env: &WasiEnv, + fd: __wasi_fd_t, + path: WasmPtr, + path_len: MemoryOffset, +) -> __wasi_errno_t { + super::path_unlink_file::(env, fd, path, path_len) +} + +pub(crate) fn poll_oneoff( + env: &WasiEnv, + in_: WasmPtr<__wasi_subscription_t, MemoryType>, + out_: WasmPtr<__wasi_event_t, MemoryType>, + nsubscriptions: MemoryOffset, + nevents: WasmPtr, +) -> Result<__wasi_errno_t, WasiError> { + super::poll_oneoff::(env, in_, out_, nsubscriptions, nevents) +} + +pub(crate) fn proc_exit(env: &WasiEnv, code: __wasi_exitcode_t) -> Result<(), WasiError> { + super::proc_exit(env, code) +} + +pub(crate) fn proc_raise(env: &WasiEnv, sig: __wasi_signal_t) -> __wasi_errno_t { + super::proc_raise(env, sig) +} + +pub(crate) fn random_get( + env: &WasiEnv, + buf: WasmPtr, + buf_len: MemoryOffset, +) -> __wasi_errno_t { + super::random_get::(env, buf, buf_len) +} + +pub(crate) fn sched_yield(env: &WasiEnv) -> Result<__wasi_errno_t, WasiError> { + super::sched_yield(env) +} + +pub(crate) fn sock_recv( + env: &WasiEnv, + sock: __wasi_fd_t, + ri_data: WasmPtr<__wasi_iovec_t, MemoryType>, + ri_data_len: MemoryOffset, + ri_flags: __wasi_riflags_t, + ro_data_len: WasmPtr, + ro_flags: WasmPtr<__wasi_roflags_t, MemoryType>, +) -> Result<__wasi_errno_t, WasiError> { + super::sock_recv::( + env, + sock, + ri_data, + ri_data_len, + ri_flags, + ro_data_len, + ro_flags, + ) +} + +pub(crate) fn sock_send( + env: &WasiEnv, + sock: __wasi_fd_t, + si_data: WasmPtr<__wasi_ciovec_t, MemoryType>, + si_data_len: MemoryOffset, + si_flags: __wasi_siflags_t, + ret_data_len: WasmPtr, +) -> Result<__wasi_errno_t, WasiError> { + super::sock_send::(env, sock, si_data, si_data_len, si_flags, ret_data_len) +} + +pub(crate) fn sock_shutdown( + env: &WasiEnv, + sock: __wasi_fd_t, + how: __wasi_sdflags_t, +) -> __wasi_errno_t { + super::sock_shutdown(env, sock, how) +} diff --git a/lib/wasi/src/syscalls/wasix32.rs b/lib/wasi/src/syscalls/wasix32.rs new file mode 100644 index 00000000000..557e5c2089a --- /dev/null +++ b/lib/wasi/src/syscalls/wasix32.rs @@ -0,0 +1,1043 @@ +#![deny(dead_code)] +use crate::{WasiEnv, WasiError, WasiState, WasiThread}; +use wasmer::{Memory, Memory32, MemorySize, WasmPtr, WasmSlice}; +use wasmer_wasi_types::*; + +type MemoryType = Memory32; +type MemoryOffset = u32; + +pub(crate) fn args_get( + env: &WasiEnv, + argv: WasmPtr, MemoryType>, + argv_buf: WasmPtr, +) -> __wasi_errno_t { + super::args_get::(env, argv, argv_buf) +} + +pub(crate) fn args_sizes_get( + env: &WasiEnv, + argc: WasmPtr, + argv_buf_size: WasmPtr, +) -> __wasi_errno_t { + super::args_sizes_get::(env, argc, argv_buf_size) +} + +pub(crate) fn clock_res_get( + env: &WasiEnv, + clock_id: __wasi_clockid_t, + resolution: WasmPtr<__wasi_timestamp_t, MemoryType>, +) -> __wasi_errno_t { + super::clock_res_get::(env, clock_id, resolution) +} + +pub(crate) fn clock_time_get( + env: &WasiEnv, + clock_id: __wasi_clockid_t, + precision: __wasi_timestamp_t, + time: WasmPtr<__wasi_timestamp_t, MemoryType>, +) -> __wasi_errno_t { + super::clock_time_get::(env, clock_id, precision, time) +} + +pub(crate) fn environ_get( + env: &WasiEnv, + environ: WasmPtr, MemoryType>, + environ_buf: WasmPtr, +) -> __wasi_errno_t { + super::environ_get::(env, environ, environ_buf) +} + +pub(crate) fn environ_sizes_get( + env: &WasiEnv, + environ_count: WasmPtr, + environ_buf_size: WasmPtr, +) -> __wasi_errno_t { + super::environ_sizes_get::(env, environ_count, environ_buf_size) +} + +pub(crate) fn fd_advise( + env: &WasiEnv, + fd: __wasi_fd_t, + offset: __wasi_filesize_t, + len: __wasi_filesize_t, + advice: __wasi_advice_t, +) -> __wasi_errno_t { + super::fd_advise(env, fd, offset, len, advice) +} + +pub(crate) fn fd_allocate( + env: &WasiEnv, + fd: __wasi_fd_t, + offset: __wasi_filesize_t, + len: __wasi_filesize_t, +) -> __wasi_errno_t { + super::fd_allocate(env, fd, offset, len) +} + +pub(crate) fn fd_close(env: &WasiEnv, fd: __wasi_fd_t) -> __wasi_errno_t { + super::fd_close(env, fd) +} + +pub(crate) fn fd_datasync(env: &WasiEnv, fd: __wasi_fd_t) -> __wasi_errno_t { + super::fd_datasync(env, fd) +} + +pub(crate) fn fd_fdstat_get( + env: &WasiEnv, + fd: __wasi_fd_t, + buf_ptr: WasmPtr<__wasi_fdstat_t, MemoryType>, +) -> __wasi_errno_t { + super::fd_fdstat_get::(env, fd, buf_ptr) +} + +pub(crate) fn fd_fdstat_set_flags( + env: &WasiEnv, + fd: __wasi_fd_t, + flags: __wasi_fdflags_t, +) -> __wasi_errno_t { + super::fd_fdstat_set_flags(env, fd, flags) +} + +pub(crate) fn fd_fdstat_set_rights( + env: &WasiEnv, + fd: __wasi_fd_t, + fs_rights_base: __wasi_rights_t, + fs_rights_inheriting: __wasi_rights_t, +) -> __wasi_errno_t { + super::fd_fdstat_set_rights(env, fd, fs_rights_base, fs_rights_inheriting) +} + +pub(crate) fn fd_filestat_get( + env: &WasiEnv, + fd: __wasi_fd_t, + buf: WasmPtr<__wasi_filestat_t, MemoryType>, +) -> __wasi_errno_t { + super::fd_filestat_get::(env, fd, buf) +} + +pub(crate) fn fd_filestat_set_size( + env: &WasiEnv, + fd: __wasi_fd_t, + st_size: __wasi_filesize_t, +) -> __wasi_errno_t { + super::fd_filestat_set_size(env, fd, st_size) +} + +pub(crate) fn fd_filestat_set_times( + env: &WasiEnv, + fd: __wasi_fd_t, + st_atim: __wasi_timestamp_t, + st_mtim: __wasi_timestamp_t, + fst_flags: __wasi_fstflags_t, +) -> __wasi_errno_t { + super::fd_filestat_set_times(env, fd, st_atim, st_mtim, fst_flags) +} + +pub(crate) fn fd_pread( + env: &WasiEnv, + fd: __wasi_fd_t, + iovs: WasmPtr<__wasi_iovec_t, MemoryType>, + iovs_len: MemoryOffset, + offset: __wasi_filesize_t, + nread: WasmPtr, +) -> Result<__wasi_errno_t, WasiError> { + super::fd_pread::(env, fd, iovs, iovs_len, offset, nread) +} + +pub(crate) fn fd_prestat_get( + env: &WasiEnv, + fd: __wasi_fd_t, + buf: WasmPtr<__wasi_prestat_t, MemoryType>, +) -> __wasi_errno_t { + super::fd_prestat_get::(env, fd, buf) +} + +pub(crate) fn fd_prestat_dir_name( + env: &WasiEnv, + fd: __wasi_fd_t, + path: WasmPtr, + path_len: MemoryOffset, +) -> __wasi_errno_t { + super::fd_prestat_dir_name::(env, fd, path, path_len) +} + +pub(crate) fn fd_pwrite( + env: &WasiEnv, + fd: __wasi_fd_t, + iovs: WasmPtr<__wasi_ciovec_t, MemoryType>, + iovs_len: MemoryOffset, + offset: __wasi_filesize_t, + nwritten: WasmPtr, +) -> Result<__wasi_errno_t, WasiError> { + super::fd_pwrite::(env, fd, iovs, iovs_len, offset, nwritten) +} + +pub(crate) fn fd_read( + env: &WasiEnv, + fd: __wasi_fd_t, + iovs: WasmPtr<__wasi_iovec_t, MemoryType>, + iovs_len: MemoryOffset, + nread: WasmPtr, +) -> Result<__wasi_errno_t, WasiError> { + super::fd_read::(env, fd, iovs, iovs_len, nread) +} + +pub(crate) fn fd_readdir( + env: &WasiEnv, + fd: __wasi_fd_t, + buf: WasmPtr, + buf_len: MemoryOffset, + cookie: __wasi_dircookie_t, + bufused: WasmPtr, +) -> __wasi_errno_t { + super::fd_readdir::(env, fd, buf, buf_len, cookie, bufused) +} + +pub(crate) fn fd_renumber(env: &WasiEnv, from: __wasi_fd_t, to: __wasi_fd_t) -> __wasi_errno_t { + super::fd_renumber(env, from, to) +} + +pub(crate) fn fd_seek( + env: &WasiEnv, + fd: __wasi_fd_t, + offset: __wasi_filedelta_t, + whence: __wasi_whence_t, + newoffset: WasmPtr<__wasi_filesize_t, MemoryType>, +) -> Result<__wasi_errno_t, WasiError> { + super::fd_seek::(env, fd, offset, whence, newoffset) +} + +pub(crate) fn fd_sync(env: &WasiEnv, fd: __wasi_fd_t) -> __wasi_errno_t { + super::fd_sync(env, fd) +} + +pub(crate) fn fd_tell( + env: &WasiEnv, + fd: __wasi_fd_t, + offset: WasmPtr<__wasi_filesize_t, MemoryType>, +) -> __wasi_errno_t { + super::fd_tell::(env, fd, offset) +} + +pub(crate) fn fd_write( + env: &WasiEnv, + fd: __wasi_fd_t, + iovs: WasmPtr<__wasi_ciovec_t, MemoryType>, + iovs_len: MemoryOffset, + nwritten: WasmPtr, +) -> Result<__wasi_errno_t, WasiError> { + super::fd_write::(env, fd, iovs, iovs_len, nwritten) +} + +pub(crate) fn path_create_directory( + env: &WasiEnv, + fd: __wasi_fd_t, + path: WasmPtr, + path_len: MemoryOffset, +) -> __wasi_errno_t { + super::path_create_directory::(env, fd, path, path_len) +} + +pub(crate) fn path_filestat_get( + env: &WasiEnv, + fd: __wasi_fd_t, + flags: __wasi_lookupflags_t, + path: WasmPtr, + path_len: MemoryOffset, + buf: WasmPtr<__wasi_filestat_t, MemoryType>, +) -> __wasi_errno_t { + super::path_filestat_get::(env, fd, flags, path, path_len, buf) +} + +pub(crate) fn path_filestat_set_times( + env: &WasiEnv, + fd: __wasi_fd_t, + flags: __wasi_lookupflags_t, + path: WasmPtr, + path_len: MemoryOffset, + st_atim: __wasi_timestamp_t, + st_mtim: __wasi_timestamp_t, + fst_flags: __wasi_fstflags_t, +) -> __wasi_errno_t { + super::path_filestat_set_times::( + env, fd, flags, path, path_len, st_atim, st_mtim, fst_flags, + ) +} + +pub(crate) fn path_link( + env: &WasiEnv, + old_fd: __wasi_fd_t, + old_flags: __wasi_lookupflags_t, + old_path: WasmPtr, + old_path_len: MemoryOffset, + new_fd: __wasi_fd_t, + new_path: WasmPtr, + new_path_len: MemoryOffset, +) -> __wasi_errno_t { + super::path_link::( + env, + old_fd, + old_flags, + old_path, + old_path_len, + new_fd, + new_path, + new_path_len, + ) +} + +pub(crate) fn path_open( + env: &WasiEnv, + dirfd: __wasi_fd_t, + dirflags: __wasi_lookupflags_t, + path: WasmPtr, + path_len: MemoryOffset, + o_flags: __wasi_oflags_t, + fs_rights_base: __wasi_rights_t, + fs_rights_inheriting: __wasi_rights_t, + fs_flags: __wasi_fdflags_t, + fd: WasmPtr<__wasi_fd_t, MemoryType>, +) -> __wasi_errno_t { + super::path_open::( + env, + dirfd, + dirflags, + path, + path_len, + o_flags, + fs_rights_base, + fs_rights_inheriting, + fs_flags, + fd, + ) +} + +pub(crate) fn path_readlink( + env: &WasiEnv, + dir_fd: __wasi_fd_t, + path: WasmPtr, + path_len: MemoryOffset, + buf: WasmPtr, + buf_len: MemoryOffset, + buf_used: WasmPtr, +) -> __wasi_errno_t { + super::path_readlink::(env, dir_fd, path, path_len, buf, buf_len, buf_used) +} + +pub(crate) fn path_remove_directory( + env: &WasiEnv, + fd: __wasi_fd_t, + path: WasmPtr, + path_len: MemoryOffset, +) -> __wasi_errno_t { + super::path_remove_directory::(env, fd, path, path_len) +} + +pub(crate) fn path_rename( + env: &WasiEnv, + old_fd: __wasi_fd_t, + old_path: WasmPtr, + old_path_len: MemoryOffset, + new_fd: __wasi_fd_t, + new_path: WasmPtr, + new_path_len: MemoryOffset, +) -> __wasi_errno_t { + super::path_rename::( + env, + old_fd, + old_path, + old_path_len, + new_fd, + new_path, + new_path_len, + ) +} + +pub(crate) fn path_symlink( + env: &WasiEnv, + old_path: WasmPtr, + old_path_len: MemoryOffset, + fd: __wasi_fd_t, + new_path: WasmPtr, + new_path_len: MemoryOffset, +) -> __wasi_errno_t { + super::path_symlink::(env, old_path, old_path_len, fd, new_path, new_path_len) +} + +pub(crate) fn path_unlink_file( + env: &WasiEnv, + fd: __wasi_fd_t, + path: WasmPtr, + path_len: MemoryOffset, +) -> __wasi_errno_t { + super::path_unlink_file::(env, fd, path, path_len) +} + +pub(crate) fn poll_oneoff( + env: &WasiEnv, + in_: WasmPtr<__wasi_subscription_t, MemoryType>, + out_: WasmPtr<__wasi_event_t, MemoryType>, + nsubscriptions: MemoryOffset, + nevents: WasmPtr, +) -> Result<__wasi_errno_t, WasiError> { + super::poll_oneoff::(env, in_, out_, nsubscriptions, nevents) +} + +pub(crate) fn proc_exit(env: &WasiEnv, code: __wasi_exitcode_t) -> Result<(), WasiError> { + super::proc_exit(env, code) +} + +pub(crate) fn proc_raise(env: &WasiEnv, sig: __wasi_signal_t) -> __wasi_errno_t { + super::proc_raise(env, sig) +} + +pub(crate) fn random_get( + env: &WasiEnv, + buf: WasmPtr, + buf_len: MemoryOffset, +) -> __wasi_errno_t { + super::random_get::(env, buf, buf_len) +} + +pub(crate) fn fd_dup( + env: &WasiEnv, + fd: __wasi_fd_t, + ret_fd: WasmPtr<__wasi_fd_t, MemoryType>, +) -> __wasi_errno_t { + super::fd_dup::(env, fd, ret_fd) +} + +pub(crate) fn fd_event( + env: &WasiEnv, + initial_val: u64, + flags: __wasi_eventfdflags, + ret_fd: WasmPtr<__wasi_fd_t, MemoryType>, +) -> __wasi_errno_t { + super::fd_event(env, initial_val, flags, ret_fd) +} + +pub(crate) fn fd_pipe( + env: &WasiEnv, + ro_fd1: WasmPtr<__wasi_fd_t, MemoryType>, + ro_fd2: WasmPtr<__wasi_fd_t, MemoryType>, +) -> __wasi_errno_t { + super::fd_pipe::(env, ro_fd1, ro_fd2) +} + +pub(crate) fn tty_get( + env: &WasiEnv, + tty_state: WasmPtr<__wasi_tty_t, MemoryType>, +) -> __wasi_errno_t { + super::tty_get::(env, tty_state) +} + +pub(crate) fn tty_set( + env: &WasiEnv, + tty_state: WasmPtr<__wasi_tty_t, MemoryType>, +) -> __wasi_errno_t { + super::tty_set::(env, tty_state) +} + +pub(crate) fn getcwd( + env: &WasiEnv, + path: WasmPtr, + path_len: WasmPtr, +) -> __wasi_errno_t { + super::getcwd::(env, path, path_len) +} + +pub(crate) fn chdir( + env: &WasiEnv, + path: WasmPtr, + path_len: MemoryOffset, +) -> __wasi_errno_t { + super::chdir::(env, path, path_len) +} + +pub(crate) fn thread_spawn( + env: &WasiEnv, + method: WasmPtr, + method_len: MemoryOffset, + user_data: u64, + reactor: __wasi_bool_t, + ret_tid: WasmPtr<__wasi_tid_t, MemoryType>, +) -> __wasi_errno_t { + super::thread_spawn::(env, method, method_len, user_data, reactor, ret_tid) +} + +pub(crate) fn thread_sleep( + env: &WasiEnv, + duration: __wasi_timestamp_t, +) -> Result<__wasi_errno_t, WasiError> { + super::thread_sleep(env, duration) +} + +pub(crate) fn thread_id( + env: &WasiEnv, + ret_tid: WasmPtr<__wasi_tid_t, MemoryType>, +) -> __wasi_errno_t { + super::thread_id::(env, ret_tid) +} + +pub(crate) fn thread_join(env: &WasiEnv, tid: __wasi_tid_t) -> Result<__wasi_errno_t, WasiError> { + super::thread_join(env, tid) +} + +pub(crate) fn thread_parallelism( + env: &WasiEnv, + ret_parallelism: WasmPtr, +) -> __wasi_errno_t { + super::thread_parallelism::(env, ret_parallelism) +} + +pub(crate) fn thread_exit( + env: &WasiEnv, + exitcode: __wasi_exitcode_t, +) -> Result<__wasi_errno_t, WasiError> { + super::thread_exit(env, exitcode) +} + +pub(crate) fn sched_yield(env: &WasiEnv) -> Result<__wasi_errno_t, WasiError> { + super::sched_yield(env) +} + +pub(crate) fn getpid(env: &WasiEnv, ret_pid: WasmPtr<__wasi_pid_t, MemoryType>) -> __wasi_errno_t { + super::getpid::(env, ret_pid) +} + +pub(crate) fn process_spawn( + env: &WasiEnv, + name: WasmPtr, + name_len: MemoryOffset, + chroot: __wasi_bool_t, + args: WasmPtr, + args_len: MemoryOffset, + preopen: WasmPtr, + preopen_len: MemoryOffset, + stdin: __wasi_stdiomode_t, + stdout: __wasi_stdiomode_t, + stderr: __wasi_stdiomode_t, + working_dir: WasmPtr, + working_dir_len: MemoryOffset, + ret_handles: WasmPtr<__wasi_bus_handles_t, MemoryType>, +) -> __bus_errno_t { + super::process_spawn::( + env, + name, + name_len, + chroot, + args, + args_len, + preopen, + preopen_len, + stdin, + stdout, + stderr, + working_dir, + working_dir_len, + ret_handles, + ) +} + +pub(crate) fn bus_open_local( + env: &WasiEnv, + name: WasmPtr, + name_len: MemoryOffset, + reuse: __wasi_bool_t, + ret_bid: WasmPtr<__wasi_bid_t, MemoryType>, +) -> __bus_errno_t { + super::bus_open_local::(env, name, name_len, reuse, ret_bid) +} + +pub(crate) fn bus_open_remote( + env: &WasiEnv, + name: WasmPtr, + name_len: MemoryOffset, + reuse: __wasi_bool_t, + instance: WasmPtr, + instance_len: MemoryOffset, + token: WasmPtr, + token_len: MemoryOffset, + ret_bid: WasmPtr<__wasi_bid_t, MemoryType>, +) -> __bus_errno_t { + super::bus_open_remote::( + env, + name, + name_len, + reuse, + instance, + instance_len, + token, + token_len, + ret_bid, + ) +} + +pub(crate) fn bus_close(env: &WasiEnv, bid: __wasi_bid_t) -> __bus_errno_t { + super::bus_close(env, bid) +} + +pub(crate) fn bus_call( + env: &WasiEnv, + bid: __wasi_bid_t, + keep_alive: __wasi_bool_t, + topic: WasmPtr, + topic_len: MemoryOffset, + format: __wasi_busdataformat_t, + buf: WasmPtr, + buf_len: MemoryOffset, + ret_cid: WasmPtr<__wasi_cid_t, MemoryType>, +) -> __bus_errno_t { + super::bus_call::( + env, bid, keep_alive, topic, topic_len, format, buf, buf_len, ret_cid, + ) +} + +pub(crate) fn bus_subcall( + env: &WasiEnv, + parent: __wasi_cid_t, + keep_alive: __wasi_bool_t, + topic: WasmPtr, + topic_len: MemoryOffset, + format: __wasi_busdataformat_t, + buf: WasmPtr, + buf_len: MemoryOffset, + ret_cid: WasmPtr<__wasi_cid_t, MemoryType>, +) -> __bus_errno_t { + super::bus_subcall::( + env, parent, keep_alive, topic, topic_len, format, buf, buf_len, ret_cid, + ) +} + +pub(crate) fn bus_poll( + env: &WasiEnv, + timeout: __wasi_timestamp_t, + events: WasmPtr, + nevents: MemoryOffset, + malloc: WasmPtr, + malloc_len: MemoryOffset, + ret_nevents: WasmPtr, +) -> __bus_errno_t { + super::bus_poll::( + env, + timeout, + events, + nevents, + malloc, + malloc_len, + ret_nevents, + ) +} + +pub(crate) fn call_reply( + env: &WasiEnv, + cid: __wasi_cid_t, + format: __wasi_busdataformat_t, + buf: WasmPtr, + buf_len: MemoryOffset, +) -> __bus_errno_t { + super::call_reply::(env, cid, format, buf, buf_len) +} + +pub(crate) fn call_fault(env: &WasiEnv, cid: __wasi_cid_t, fault: __bus_errno_t) -> __bus_errno_t { + super::call_fault(env, cid, fault) +} + +pub(crate) fn call_close(env: &WasiEnv, cid: __wasi_cid_t) -> __bus_errno_t { + super::call_close(env, cid) +} + +pub(crate) fn port_bridge( + env: &WasiEnv, + network: WasmPtr, + network_len: MemoryOffset, + token: WasmPtr, + token_len: MemoryOffset, + security: __wasi_streamsecurity_t, +) -> __wasi_errno_t { + super::port_bridge::(env, network, network_len, token, token_len, security) +} + +pub(crate) fn port_unbridge(env: &WasiEnv) -> __wasi_errno_t { + super::port_unbridge(env) +} + +pub(crate) fn port_dhcp_acquire(env: &WasiEnv) -> __wasi_errno_t { + super::port_dhcp_acquire(env) +} + +pub(crate) fn port_addr_add( + env: &WasiEnv, + addr: WasmPtr<__wasi_cidr_t, MemoryType>, +) -> __wasi_errno_t { + super::port_addr_add::(env, addr) +} + +pub(crate) fn port_addr_remove( + env: &WasiEnv, + addr: WasmPtr<__wasi_addr_t, MemoryType>, +) -> __wasi_errno_t { + super::port_addr_remove::(env, addr) +} + +pub(crate) fn port_addr_clear(env: &WasiEnv) -> __wasi_errno_t { + super::port_addr_clear(env) +} + +pub(crate) fn port_addr_list( + env: &WasiEnv, + addrs: WasmPtr<__wasi_cidr_t, MemoryType>, + naddrs: WasmPtr, +) -> __wasi_errno_t { + super::port_addr_list::(env, addrs, naddrs) +} + +pub(crate) fn port_mac( + env: &WasiEnv, + ret_mac: WasmPtr<__wasi_hardwareaddress_t, MemoryType>, +) -> __wasi_errno_t { + super::port_mac::(env, ret_mac) +} + +pub(crate) fn port_gateway_set( + env: &WasiEnv, + ip: WasmPtr<__wasi_addr_t, MemoryType>, +) -> __wasi_errno_t { + super::port_gateway_set::(env, ip) +} + +pub(crate) fn port_route_add( + env: &WasiEnv, + cidr: WasmPtr<__wasi_cidr_t, MemoryType>, + via_router: WasmPtr<__wasi_addr_t, MemoryType>, + preferred_until: WasmPtr<__wasi_option_timestamp_t, MemoryType>, + expires_at: WasmPtr<__wasi_option_timestamp_t, MemoryType>, +) -> __wasi_errno_t { + super::port_route_add::(env, cidr, via_router, preferred_until, expires_at) +} + +pub(crate) fn port_route_remove( + env: &WasiEnv, + ip: WasmPtr<__wasi_addr_t, MemoryType>, +) -> __wasi_errno_t { + super::port_route_remove::(env, ip) +} + +pub(crate) fn port_route_clear(env: &WasiEnv) -> __wasi_errno_t { + super::port_route_clear(env) +} + +pub(crate) fn port_route_list( + env: &WasiEnv, + routes: WasmPtr<__wasi_route_t, MemoryType>, + nroutes: WasmPtr, +) -> __wasi_errno_t { + super::port_route_list::(env, routes, nroutes) +} + +pub(crate) fn ws_connect( + env: &WasiEnv, + url: WasmPtr, + url_len: MemoryOffset, + ret_sock: WasmPtr<__wasi_fd_t, MemoryType>, +) -> __wasi_errno_t { + super::ws_connect::(env, url, url_len, ret_sock) +} + +pub(crate) fn http_request( + env: &WasiEnv, + url: WasmPtr, + url_len: MemoryOffset, + method: WasmPtr, + method_len: MemoryOffset, + headers: WasmPtr, + headers_len: MemoryOffset, + gzip: __wasi_bool_t, + ret_handles: WasmPtr<__wasi_http_handles_t, MemoryType>, +) -> __wasi_errno_t { + super::http_request::( + env, + url, + url_len, + method, + method_len, + headers, + headers_len, + gzip, + ret_handles, + ) +} + +pub(crate) fn http_status( + env: &WasiEnv, + sock: __wasi_fd_t, + status: WasmPtr<__wasi_http_status_t, MemoryType>, + status_text: WasmPtr, + status_text_len: WasmPtr, + headers: WasmPtr, + headers_len: WasmPtr, +) -> __wasi_errno_t { + super::http_status::(env, sock, status) +} + +pub(crate) fn sock_status( + env: &WasiEnv, + sock: __wasi_fd_t, + ret_status: WasmPtr<__wasi_sockstatus_t, MemoryType>, +) -> __wasi_errno_t { + super::sock_status::(env, sock, ret_status) +} + +pub(crate) fn sock_addr_local( + env: &WasiEnv, + sock: __wasi_fd_t, + ret_addr: WasmPtr<__wasi_addr_port_t, MemoryType>, +) -> __wasi_errno_t { + super::sock_addr_local::(env, sock, ret_addr) +} + +pub(crate) fn sock_addr_peer( + env: &WasiEnv, + sock: __wasi_fd_t, + ro_addr: WasmPtr<__wasi_addr_port_t, MemoryType>, +) -> __wasi_errno_t { + super::sock_addr_peer::(env, sock, ro_addr) +} + +pub(crate) fn sock_open( + env: &WasiEnv, + af: __wasi_addressfamily_t, + ty: __wasi_socktype_t, + pt: __wasi_sockproto_t, + ro_sock: WasmPtr<__wasi_fd_t, MemoryType>, +) -> __wasi_errno_t { + super::sock_open::(env, af, ty, pt, ro_sock) +} + +pub(crate) fn sock_set_opt_flag( + env: &WasiEnv, + sock: __wasi_fd_t, + opt: __wasi_sockoption_t, + flag: __wasi_bool_t, +) -> __wasi_errno_t { + super::sock_set_opt_flag(env, sock, opt, flag) +} + +pub(crate) fn sock_get_opt_flag( + env: &WasiEnv, + sock: __wasi_fd_t, + opt: __wasi_sockoption_t, + ret_flag: WasmPtr<__wasi_bool_t, MemoryType>, +) -> __wasi_errno_t { + super::sock_get_opt_flag::(env, sock, opt, ret_flag) +} + +pub fn sock_set_opt_time( + env: &WasiEnv, + sock: __wasi_fd_t, + opt: __wasi_sockoption_t, + time: WasmPtr<__wasi_option_timestamp_t, MemoryType>, +) -> __wasi_errno_t { + super::sock_set_opt_time(env, sock, opt, time) +} + +pub fn sock_get_opt_time( + env: &WasiEnv, + sock: __wasi_fd_t, + opt: __wasi_sockoption_t, + ret_time: WasmPtr<__wasi_option_timestamp_t, MemoryType>, +) -> __wasi_errno_t { + super::sock_get_opt_time(env, sock, opt, ret_time) +} + +pub fn sock_set_opt_size( + env: &WasiEnv, + sock: __wasi_fd_t, + opt: __wasi_sockoption_t, + size: __wasi_filesize_t, +) -> __wasi_errno_t { + super::sock_set_opt_size(env, sock, opt, size) +} + +pub fn sock_get_opt_size( + env: &WasiEnv, + sock: __wasi_fd_t, + opt: __wasi_sockoption_t, + ret_size: WasmPtr<__wasi_filesize_t, MemoryType>, +) -> __wasi_errno_t { + super::sock_get_opt_size(env, sock, opt, ret_size) +} + +pub(crate) fn sock_join_multicast_v4( + env: &WasiEnv, + sock: __wasi_fd_t, + multiaddr: WasmPtr<__wasi_addr_ip4_t, MemoryType>, + iface: WasmPtr<__wasi_addr_ip4_t, MemoryType>, +) -> __wasi_errno_t { + super::sock_join_multicast_v4::(env, sock, multiaddr, iface) +} + +pub(crate) fn sock_leave_multicast_v4( + env: &WasiEnv, + sock: __wasi_fd_t, + multiaddr: WasmPtr<__wasi_addr_ip4_t, MemoryType>, + iface: WasmPtr<__wasi_addr_ip4_t, MemoryType>, +) -> __wasi_errno_t { + super::sock_leave_multicast_v4::(env, sock, multiaddr, iface) +} + +pub(crate) fn sock_join_multicast_v6( + env: &WasiEnv, + sock: __wasi_fd_t, + multiaddr: WasmPtr<__wasi_addr_ip6_t, MemoryType>, + iface: u32, +) -> __wasi_errno_t { + super::sock_join_multicast_v6::(env, sock, multiaddr, iface) +} + +pub(crate) fn sock_leave_multicast_v6( + env: &WasiEnv, + sock: __wasi_fd_t, + multiaddr: WasmPtr<__wasi_addr_ip6_t, MemoryType>, + iface: u32, +) -> __wasi_errno_t { + super::sock_leave_multicast_v6::(env, sock, multiaddr, iface) +} + +pub(crate) fn sock_bind( + env: &WasiEnv, + sock: __wasi_fd_t, + addr: WasmPtr<__wasi_addr_port_t, MemoryType>, +) -> __wasi_errno_t { + super::sock_bind::(env, sock, addr) +} + +pub(crate) fn sock_listen( + env: &WasiEnv, + sock: __wasi_fd_t, + backlog: MemoryOffset, +) -> __wasi_errno_t { + super::sock_listen::(env, sock, backlog) +} + +pub(crate) fn sock_accept( + env: &WasiEnv, + sock: __wasi_fd_t, + fd_flags: __wasi_fdflags_t, + ro_fd: WasmPtr<__wasi_fd_t, MemoryType>, + ro_addr: WasmPtr<__wasi_addr_port_t, MemoryType>, +) -> Result<__wasi_errno_t, WasiError> { + super::sock_accept::(env, sock, fd_flags, ro_fd, ro_addr) +} + +pub(crate) fn sock_connect( + env: &WasiEnv, + sock: __wasi_fd_t, + addr: WasmPtr<__wasi_addr_port_t, MemoryType>, +) -> __wasi_errno_t { + super::sock_connect::(env, sock, addr) +} + +pub(crate) fn sock_recv( + env: &WasiEnv, + sock: __wasi_fd_t, + ri_data: WasmPtr<__wasi_iovec_t, MemoryType>, + ri_data_len: MemoryOffset, + ri_flags: __wasi_riflags_t, + ro_data_len: WasmPtr, + ro_flags: WasmPtr<__wasi_roflags_t, MemoryType>, +) -> Result<__wasi_errno_t, WasiError> { + super::sock_recv::( + env, + sock, + ri_data, + ri_data_len, + ri_flags, + ro_data_len, + ro_flags, + ) +} + +pub(crate) fn sock_recv_from( + env: &WasiEnv, + sock: __wasi_fd_t, + ri_data: WasmPtr<__wasi_iovec_t, MemoryType>, + ri_data_len: MemoryOffset, + ri_flags: __wasi_riflags_t, + ro_data_len: WasmPtr, + ro_flags: WasmPtr<__wasi_roflags_t, MemoryType>, + ro_addr: WasmPtr<__wasi_addr_port_t, MemoryType>, +) -> Result<__wasi_errno_t, WasiError> { + super::sock_recv_from::( + env, + sock, + ri_data, + ri_data_len, + ri_flags, + ro_data_len, + ro_flags, + ro_addr, + ) +} + +pub(crate) fn sock_send( + env: &WasiEnv, + sock: __wasi_fd_t, + si_data: WasmPtr<__wasi_ciovec_t, MemoryType>, + si_data_len: MemoryOffset, + si_flags: __wasi_siflags_t, + ret_data_len: WasmPtr, +) -> Result<__wasi_errno_t, WasiError> { + super::sock_send::(env, sock, si_data, si_data_len, si_flags, ret_data_len) +} + +pub(crate) fn sock_send_to( + env: &WasiEnv, + sock: __wasi_fd_t, + si_data: WasmPtr<__wasi_ciovec_t, MemoryType>, + si_data_len: MemoryOffset, + si_flags: __wasi_siflags_t, + addr: WasmPtr<__wasi_addr_port_t, MemoryType>, + ret_data_len: WasmPtr, +) -> Result<__wasi_errno_t, WasiError> { + super::sock_send_to::( + env, + sock, + si_data, + si_data_len, + si_flags, + addr, + ret_data_len, + ) +} + +pub(crate) fn sock_send_file( + env: &WasiEnv, + out_fd: __wasi_fd_t, + in_fd: __wasi_fd_t, + offset: __wasi_filesize_t, + count: __wasi_filesize_t, + ret_sent: WasmPtr<__wasi_filesize_t, MemoryType>, +) -> Result<__wasi_errno_t, WasiError> { + unsafe { super::sock_send_file::(env, out_fd, in_fd, offset, count, ret_sent) } +} + +pub(crate) fn sock_shutdown( + env: &WasiEnv, + sock: __wasi_fd_t, + how: __wasi_sdflags_t, +) -> __wasi_errno_t { + super::sock_shutdown(env, sock, how) +} + +pub(crate) fn resolve( + env: &WasiEnv, + host: WasmPtr, + host_len: MemoryOffset, + port: u16, + ips: WasmPtr<__wasi_addr_t, MemoryType>, + nips: MemoryOffset, + ret_nips: WasmPtr, +) -> __wasi_errno_t { + super::resolve::(env, host, host_len, port, ips, nips, ret_nips) +} diff --git a/lib/wasi/src/syscalls/wasix64.rs b/lib/wasi/src/syscalls/wasix64.rs new file mode 100644 index 00000000000..e3ece1fb80d --- /dev/null +++ b/lib/wasi/src/syscalls/wasix64.rs @@ -0,0 +1,1043 @@ +#![deny(dead_code)] +use crate::{WasiEnv, WasiError, WasiState, WasiThread}; +use wasmer::{Memory, Memory64, MemorySize, WasmPtr, WasmSlice}; +use wasmer_wasi_types::*; + +type MemoryType = Memory64; +type MemoryOffset = u64; + +pub(crate) fn args_get( + env: &WasiEnv, + argv: WasmPtr, MemoryType>, + argv_buf: WasmPtr, +) -> __wasi_errno_t { + super::args_get::(env, argv, argv_buf) +} + +pub(crate) fn args_sizes_get( + env: &WasiEnv, + argc: WasmPtr, + argv_buf_size: WasmPtr, +) -> __wasi_errno_t { + super::args_sizes_get::(env, argc, argv_buf_size) +} + +pub(crate) fn clock_res_get( + env: &WasiEnv, + clock_id: __wasi_clockid_t, + resolution: WasmPtr<__wasi_timestamp_t, MemoryType>, +) -> __wasi_errno_t { + super::clock_res_get::(env, clock_id, resolution) +} + +pub(crate) fn clock_time_get( + env: &WasiEnv, + clock_id: __wasi_clockid_t, + precision: __wasi_timestamp_t, + time: WasmPtr<__wasi_timestamp_t, MemoryType>, +) -> __wasi_errno_t { + super::clock_time_get::(env, clock_id, precision, time) +} + +pub(crate) fn environ_get( + env: &WasiEnv, + environ: WasmPtr, MemoryType>, + environ_buf: WasmPtr, +) -> __wasi_errno_t { + super::environ_get::(env, environ, environ_buf) +} + +pub(crate) fn environ_sizes_get( + env: &WasiEnv, + environ_count: WasmPtr, + environ_buf_size: WasmPtr, +) -> __wasi_errno_t { + super::environ_sizes_get::(env, environ_count, environ_buf_size) +} + +pub(crate) fn fd_advise( + env: &WasiEnv, + fd: __wasi_fd_t, + offset: __wasi_filesize_t, + len: __wasi_filesize_t, + advice: __wasi_advice_t, +) -> __wasi_errno_t { + super::fd_advise(env, fd, offset, len, advice) +} + +pub(crate) fn fd_allocate( + env: &WasiEnv, + fd: __wasi_fd_t, + offset: __wasi_filesize_t, + len: __wasi_filesize_t, +) -> __wasi_errno_t { + super::fd_allocate(env, fd, offset, len) +} + +pub(crate) fn fd_close(env: &WasiEnv, fd: __wasi_fd_t) -> __wasi_errno_t { + super::fd_close(env, fd) +} + +pub(crate) fn fd_datasync(env: &WasiEnv, fd: __wasi_fd_t) -> __wasi_errno_t { + super::fd_datasync(env, fd) +} + +pub(crate) fn fd_fdstat_get( + env: &WasiEnv, + fd: __wasi_fd_t, + buf_ptr: WasmPtr<__wasi_fdstat_t, MemoryType>, +) -> __wasi_errno_t { + super::fd_fdstat_get::(env, fd, buf_ptr) +} + +pub(crate) fn fd_fdstat_set_flags( + env: &WasiEnv, + fd: __wasi_fd_t, + flags: __wasi_fdflags_t, +) -> __wasi_errno_t { + super::fd_fdstat_set_flags(env, fd, flags) +} + +pub(crate) fn fd_fdstat_set_rights( + env: &WasiEnv, + fd: __wasi_fd_t, + fs_rights_base: __wasi_rights_t, + fs_rights_inheriting: __wasi_rights_t, +) -> __wasi_errno_t { + super::fd_fdstat_set_rights(env, fd, fs_rights_base, fs_rights_inheriting) +} + +pub(crate) fn fd_filestat_get( + env: &WasiEnv, + fd: __wasi_fd_t, + buf: WasmPtr<__wasi_filestat_t, MemoryType>, +) -> __wasi_errno_t { + super::fd_filestat_get::(env, fd, buf) +} + +pub(crate) fn fd_filestat_set_size( + env: &WasiEnv, + fd: __wasi_fd_t, + st_size: __wasi_filesize_t, +) -> __wasi_errno_t { + super::fd_filestat_set_size(env, fd, st_size) +} + +pub(crate) fn fd_filestat_set_times( + env: &WasiEnv, + fd: __wasi_fd_t, + st_atim: __wasi_timestamp_t, + st_mtim: __wasi_timestamp_t, + fst_flags: __wasi_fstflags_t, +) -> __wasi_errno_t { + super::fd_filestat_set_times(env, fd, st_atim, st_mtim, fst_flags) +} + +pub(crate) fn fd_pread( + env: &WasiEnv, + fd: __wasi_fd_t, + iovs: WasmPtr<__wasi_iovec_t, MemoryType>, + iovs_len: MemoryOffset, + offset: __wasi_filesize_t, + nread: WasmPtr, +) -> Result<__wasi_errno_t, WasiError> { + super::fd_pread::(env, fd, iovs, iovs_len, offset, nread) +} + +pub(crate) fn fd_prestat_get( + env: &WasiEnv, + fd: __wasi_fd_t, + buf: WasmPtr<__wasi_prestat_t, MemoryType>, +) -> __wasi_errno_t { + super::fd_prestat_get::(env, fd, buf) +} + +pub(crate) fn fd_prestat_dir_name( + env: &WasiEnv, + fd: __wasi_fd_t, + path: WasmPtr, + path_len: MemoryOffset, +) -> __wasi_errno_t { + super::fd_prestat_dir_name::(env, fd, path, path_len) +} + +pub(crate) fn fd_pwrite( + env: &WasiEnv, + fd: __wasi_fd_t, + iovs: WasmPtr<__wasi_ciovec_t, MemoryType>, + iovs_len: MemoryOffset, + offset: __wasi_filesize_t, + nwritten: WasmPtr, +) -> Result<__wasi_errno_t, WasiError> { + super::fd_pwrite::(env, fd, iovs, iovs_len, offset, nwritten) +} + +pub(crate) fn fd_read( + env: &WasiEnv, + fd: __wasi_fd_t, + iovs: WasmPtr<__wasi_iovec_t, MemoryType>, + iovs_len: MemoryOffset, + nread: WasmPtr, +) -> Result<__wasi_errno_t, WasiError> { + super::fd_read::(env, fd, iovs, iovs_len, nread) +} + +pub(crate) fn fd_readdir( + env: &WasiEnv, + fd: __wasi_fd_t, + buf: WasmPtr, + buf_len: MemoryOffset, + cookie: __wasi_dircookie_t, + bufused: WasmPtr, +) -> __wasi_errno_t { + super::fd_readdir::(env, fd, buf, buf_len, cookie, bufused) +} + +pub(crate) fn fd_renumber(env: &WasiEnv, from: __wasi_fd_t, to: __wasi_fd_t) -> __wasi_errno_t { + super::fd_renumber(env, from, to) +} + +pub(crate) fn fd_seek( + env: &WasiEnv, + fd: __wasi_fd_t, + offset: __wasi_filedelta_t, + whence: __wasi_whence_t, + newoffset: WasmPtr<__wasi_filesize_t, MemoryType>, +) -> Result<__wasi_errno_t, WasiError> { + super::fd_seek::(env, fd, offset, whence, newoffset) +} + +pub(crate) fn fd_sync(env: &WasiEnv, fd: __wasi_fd_t) -> __wasi_errno_t { + super::fd_sync(env, fd) +} + +pub(crate) fn fd_tell( + env: &WasiEnv, + fd: __wasi_fd_t, + offset: WasmPtr<__wasi_filesize_t, MemoryType>, +) -> __wasi_errno_t { + super::fd_tell::(env, fd, offset) +} + +pub(crate) fn fd_write( + env: &WasiEnv, + fd: __wasi_fd_t, + iovs: WasmPtr<__wasi_ciovec_t, MemoryType>, + iovs_len: MemoryOffset, + nwritten: WasmPtr, +) -> Result<__wasi_errno_t, WasiError> { + super::fd_write::(env, fd, iovs, iovs_len, nwritten) +} + +pub(crate) fn path_create_directory( + env: &WasiEnv, + fd: __wasi_fd_t, + path: WasmPtr, + path_len: MemoryOffset, +) -> __wasi_errno_t { + super::path_create_directory::(env, fd, path, path_len) +} + +pub(crate) fn path_filestat_get( + env: &WasiEnv, + fd: __wasi_fd_t, + flags: __wasi_lookupflags_t, + path: WasmPtr, + path_len: MemoryOffset, + buf: WasmPtr<__wasi_filestat_t, MemoryType>, +) -> __wasi_errno_t { + super::path_filestat_get::(env, fd, flags, path, path_len, buf) +} + +pub(crate) fn path_filestat_set_times( + env: &WasiEnv, + fd: __wasi_fd_t, + flags: __wasi_lookupflags_t, + path: WasmPtr, + path_len: MemoryOffset, + st_atim: __wasi_timestamp_t, + st_mtim: __wasi_timestamp_t, + fst_flags: __wasi_fstflags_t, +) -> __wasi_errno_t { + super::path_filestat_set_times::( + env, fd, flags, path, path_len, st_atim, st_mtim, fst_flags, + ) +} + +pub(crate) fn path_link( + env: &WasiEnv, + old_fd: __wasi_fd_t, + old_flags: __wasi_lookupflags_t, + old_path: WasmPtr, + old_path_len: MemoryOffset, + new_fd: __wasi_fd_t, + new_path: WasmPtr, + new_path_len: MemoryOffset, +) -> __wasi_errno_t { + super::path_link::( + env, + old_fd, + old_flags, + old_path, + old_path_len, + new_fd, + new_path, + new_path_len, + ) +} + +pub(crate) fn path_open( + env: &WasiEnv, + dirfd: __wasi_fd_t, + dirflags: __wasi_lookupflags_t, + path: WasmPtr, + path_len: MemoryOffset, + o_flags: __wasi_oflags_t, + fs_rights_base: __wasi_rights_t, + fs_rights_inheriting: __wasi_rights_t, + fs_flags: __wasi_fdflags_t, + fd: WasmPtr<__wasi_fd_t, MemoryType>, +) -> __wasi_errno_t { + super::path_open::( + env, + dirfd, + dirflags, + path, + path_len, + o_flags, + fs_rights_base, + fs_rights_inheriting, + fs_flags, + fd, + ) +} + +pub(crate) fn path_readlink( + env: &WasiEnv, + dir_fd: __wasi_fd_t, + path: WasmPtr, + path_len: MemoryOffset, + buf: WasmPtr, + buf_len: MemoryOffset, + buf_used: WasmPtr, +) -> __wasi_errno_t { + super::path_readlink::(env, dir_fd, path, path_len, buf, buf_len, buf_used) +} + +pub(crate) fn path_remove_directory( + env: &WasiEnv, + fd: __wasi_fd_t, + path: WasmPtr, + path_len: MemoryOffset, +) -> __wasi_errno_t { + super::path_remove_directory::(env, fd, path, path_len) +} + +pub(crate) fn path_rename( + env: &WasiEnv, + old_fd: __wasi_fd_t, + old_path: WasmPtr, + old_path_len: MemoryOffset, + new_fd: __wasi_fd_t, + new_path: WasmPtr, + new_path_len: MemoryOffset, +) -> __wasi_errno_t { + super::path_rename::( + env, + old_fd, + old_path, + old_path_len, + new_fd, + new_path, + new_path_len, + ) +} + +pub(crate) fn path_symlink( + env: &WasiEnv, + old_path: WasmPtr, + old_path_len: MemoryOffset, + fd: __wasi_fd_t, + new_path: WasmPtr, + new_path_len: MemoryOffset, +) -> __wasi_errno_t { + super::path_symlink::(env, old_path, old_path_len, fd, new_path, new_path_len) +} + +pub(crate) fn path_unlink_file( + env: &WasiEnv, + fd: __wasi_fd_t, + path: WasmPtr, + path_len: MemoryOffset, +) -> __wasi_errno_t { + super::path_unlink_file::(env, fd, path, path_len) +} + +pub(crate) fn poll_oneoff( + env: &WasiEnv, + in_: WasmPtr<__wasi_subscription_t, MemoryType>, + out_: WasmPtr<__wasi_event_t, MemoryType>, + nsubscriptions: MemoryOffset, + nevents: WasmPtr, +) -> Result<__wasi_errno_t, WasiError> { + super::poll_oneoff::(env, in_, out_, nsubscriptions, nevents) +} + +pub(crate) fn proc_exit(env: &WasiEnv, code: __wasi_exitcode_t) -> Result<(), WasiError> { + super::proc_exit(env, code) +} + +pub(crate) fn proc_raise(env: &WasiEnv, sig: __wasi_signal_t) -> __wasi_errno_t { + super::proc_raise(env, sig) +} + +pub(crate) fn random_get( + env: &WasiEnv, + buf: WasmPtr, + buf_len: MemoryOffset, +) -> __wasi_errno_t { + super::random_get::(env, buf, buf_len) +} + +pub(crate) fn fd_dup( + env: &WasiEnv, + fd: __wasi_fd_t, + ret_fd: WasmPtr<__wasi_fd_t, MemoryType>, +) -> __wasi_errno_t { + super::fd_dup::(env, fd, ret_fd) +} + +pub(crate) fn fd_event( + env: &WasiEnv, + initial_val: u64, + flags: __wasi_eventfdflags, + ret_fd: WasmPtr<__wasi_fd_t, MemoryType>, +) -> __wasi_errno_t { + super::fd_event(env, initial_val, flags, ret_fd) +} + +pub(crate) fn fd_pipe( + env: &WasiEnv, + ro_fd1: WasmPtr<__wasi_fd_t, MemoryType>, + ro_fd2: WasmPtr<__wasi_fd_t, MemoryType>, +) -> __wasi_errno_t { + super::fd_pipe::(env, ro_fd1, ro_fd2) +} + +pub(crate) fn tty_get( + env: &WasiEnv, + tty_state: WasmPtr<__wasi_tty_t, MemoryType>, +) -> __wasi_errno_t { + super::tty_get::(env, tty_state) +} + +pub(crate) fn tty_set( + env: &WasiEnv, + tty_state: WasmPtr<__wasi_tty_t, MemoryType>, +) -> __wasi_errno_t { + super::tty_set::(env, tty_state) +} + +pub(crate) fn getcwd( + env: &WasiEnv, + path: WasmPtr, + path_len: WasmPtr, +) -> __wasi_errno_t { + super::getcwd::(env, path, path_len) +} + +pub(crate) fn chdir( + env: &WasiEnv, + path: WasmPtr, + path_len: MemoryOffset, +) -> __wasi_errno_t { + super::chdir::(env, path, path_len) +} + +pub(crate) fn thread_spawn( + env: &WasiEnv, + method: WasmPtr, + method_len: MemoryOffset, + user_data: u64, + reactor: __wasi_bool_t, + ret_tid: WasmPtr<__wasi_tid_t, MemoryType>, +) -> __wasi_errno_t { + super::thread_spawn::(env, method, method_len, user_data, reactor, ret_tid) +} + +pub(crate) fn thread_sleep( + env: &WasiEnv, + duration: __wasi_timestamp_t, +) -> Result<__wasi_errno_t, WasiError> { + super::thread_sleep(env, duration) +} + +pub(crate) fn thread_id( + env: &WasiEnv, + ret_tid: WasmPtr<__wasi_tid_t, MemoryType>, +) -> __wasi_errno_t { + super::thread_id::(env, ret_tid) +} + +pub(crate) fn thread_join(env: &WasiEnv, tid: __wasi_tid_t) -> Result<__wasi_errno_t, WasiError> { + super::thread_join(env, tid) +} + +pub(crate) fn thread_parallelism( + env: &WasiEnv, + ret_parallelism: WasmPtr, +) -> __wasi_errno_t { + super::thread_parallelism::(env, ret_parallelism) +} + +pub(crate) fn thread_exit( + env: &WasiEnv, + exitcode: __wasi_exitcode_t, +) -> Result<__wasi_errno_t, WasiError> { + super::thread_exit(env, exitcode) +} + +pub(crate) fn sched_yield(env: &WasiEnv) -> Result<__wasi_errno_t, WasiError> { + super::sched_yield(env) +} + +pub(crate) fn getpid(env: &WasiEnv, ret_pid: WasmPtr<__wasi_pid_t, MemoryType>) -> __wasi_errno_t { + super::getpid::(env, ret_pid) +} + +pub(crate) fn process_spawn( + env: &WasiEnv, + name: WasmPtr, + name_len: MemoryOffset, + chroot: __wasi_bool_t, + args: WasmPtr, + args_len: MemoryOffset, + preopen: WasmPtr, + preopen_len: MemoryOffset, + stdin: __wasi_stdiomode_t, + stdout: __wasi_stdiomode_t, + stderr: __wasi_stdiomode_t, + working_dir: WasmPtr, + working_dir_len: MemoryOffset, + ret_handles: WasmPtr<__wasi_bus_handles_t, MemoryType>, +) -> __bus_errno_t { + super::process_spawn::( + env, + name, + name_len, + chroot, + args, + args_len, + preopen, + preopen_len, + stdin, + stdout, + stderr, + working_dir, + working_dir_len, + ret_handles, + ) +} + +pub(crate) fn bus_open_local( + env: &WasiEnv, + name: WasmPtr, + name_len: MemoryOffset, + reuse: __wasi_bool_t, + ret_bid: WasmPtr<__wasi_bid_t, MemoryType>, +) -> __bus_errno_t { + super::bus_open_local::(env, name, name_len, reuse, ret_bid) +} + +pub(crate) fn bus_open_remote( + env: &WasiEnv, + name: WasmPtr, + name_len: MemoryOffset, + reuse: __wasi_bool_t, + instance: WasmPtr, + instance_len: MemoryOffset, + token: WasmPtr, + token_len: MemoryOffset, + ret_bid: WasmPtr<__wasi_bid_t, MemoryType>, +) -> __bus_errno_t { + super::bus_open_remote::( + env, + name, + name_len, + reuse, + instance, + instance_len, + token, + token_len, + ret_bid, + ) +} + +pub(crate) fn bus_close(env: &WasiEnv, bid: __wasi_bid_t) -> __bus_errno_t { + super::bus_close(env, bid) +} + +pub(crate) fn bus_call( + env: &WasiEnv, + bid: __wasi_bid_t, + keep_alive: __wasi_bool_t, + topic: WasmPtr, + topic_len: MemoryOffset, + format: __wasi_busdataformat_t, + buf: WasmPtr, + buf_len: MemoryOffset, + ret_cid: WasmPtr<__wasi_cid_t, MemoryType>, +) -> __bus_errno_t { + super::bus_call::( + env, bid, keep_alive, topic, topic_len, format, buf, buf_len, ret_cid, + ) +} + +pub(crate) fn bus_subcall( + env: &WasiEnv, + parent: __wasi_cid_t, + keep_alive: __wasi_bool_t, + topic: WasmPtr, + topic_len: MemoryOffset, + format: __wasi_busdataformat_t, + buf: WasmPtr, + buf_len: MemoryOffset, + ret_cid: WasmPtr<__wasi_cid_t, MemoryType>, +) -> __bus_errno_t { + super::bus_subcall::( + env, parent, keep_alive, topic, topic_len, format, buf, buf_len, ret_cid, + ) +} + +pub(crate) fn bus_poll( + env: &WasiEnv, + timeout: __wasi_timestamp_t, + events: WasmPtr, + nevents: MemoryOffset, + malloc: WasmPtr, + malloc_len: MemoryOffset, + ret_nevents: WasmPtr, +) -> __bus_errno_t { + super::bus_poll::( + env, + timeout, + events, + nevents, + malloc, + malloc_len, + ret_nevents, + ) +} + +pub(crate) fn call_reply( + env: &WasiEnv, + cid: __wasi_cid_t, + format: __wasi_busdataformat_t, + buf: WasmPtr, + buf_len: MemoryOffset, +) -> __bus_errno_t { + super::call_reply::(env, cid, format, buf, buf_len) +} + +pub(crate) fn call_fault(env: &WasiEnv, cid: __wasi_cid_t, fault: __bus_errno_t) -> __bus_errno_t { + super::call_fault(env, cid, fault) +} + +pub(crate) fn call_close(env: &WasiEnv, cid: __wasi_cid_t) -> __bus_errno_t { + super::call_close(env, cid) +} + +pub(crate) fn port_bridge( + env: &WasiEnv, + network: WasmPtr, + network_len: MemoryOffset, + token: WasmPtr, + token_len: MemoryOffset, + security: __wasi_streamsecurity_t, +) -> __wasi_errno_t { + super::port_bridge::(env, network, network_len, token, token_len, security) +} + +pub(crate) fn port_unbridge(env: &WasiEnv) -> __wasi_errno_t { + super::port_unbridge(env) +} + +pub(crate) fn port_dhcp_acquire(env: &WasiEnv) -> __wasi_errno_t { + super::port_dhcp_acquire(env) +} + +pub(crate) fn port_addr_add( + env: &WasiEnv, + addr: WasmPtr<__wasi_cidr_t, MemoryType>, +) -> __wasi_errno_t { + super::port_addr_add::(env, addr) +} + +pub(crate) fn port_addr_remove( + env: &WasiEnv, + addr: WasmPtr<__wasi_addr_t, MemoryType>, +) -> __wasi_errno_t { + super::port_addr_remove::(env, addr) +} + +pub(crate) fn port_addr_clear(env: &WasiEnv) -> __wasi_errno_t { + super::port_addr_clear(env) +} + +pub(crate) fn port_addr_list( + env: &WasiEnv, + addrs: WasmPtr<__wasi_cidr_t, MemoryType>, + naddrs: WasmPtr, +) -> __wasi_errno_t { + super::port_addr_list::(env, addrs, naddrs) +} + +pub(crate) fn port_mac( + env: &WasiEnv, + ret_mac: WasmPtr<__wasi_hardwareaddress_t, MemoryType>, +) -> __wasi_errno_t { + super::port_mac::(env, ret_mac) +} + +pub(crate) fn port_gateway_set( + env: &WasiEnv, + ip: WasmPtr<__wasi_addr_t, MemoryType>, +) -> __wasi_errno_t { + super::port_gateway_set::(env, ip) +} + +pub(crate) fn port_route_add( + env: &WasiEnv, + cidr: WasmPtr<__wasi_cidr_t, MemoryType>, + via_router: WasmPtr<__wasi_addr_t, MemoryType>, + preferred_until: WasmPtr<__wasi_option_timestamp_t, MemoryType>, + expires_at: WasmPtr<__wasi_option_timestamp_t, MemoryType>, +) -> __wasi_errno_t { + super::port_route_add::(env, cidr, via_router, preferred_until, expires_at) +} + +pub(crate) fn port_route_remove( + env: &WasiEnv, + ip: WasmPtr<__wasi_addr_t, MemoryType>, +) -> __wasi_errno_t { + super::port_route_remove::(env, ip) +} + +pub(crate) fn port_route_clear(env: &WasiEnv) -> __wasi_errno_t { + super::port_route_clear(env) +} + +pub(crate) fn port_route_list( + env: &WasiEnv, + routes: WasmPtr<__wasi_route_t, MemoryType>, + nroutes: WasmPtr, +) -> __wasi_errno_t { + super::port_route_list::(env, routes, nroutes) +} + +pub(crate) fn ws_connect( + env: &WasiEnv, + url: WasmPtr, + url_len: MemoryOffset, + ret_sock: WasmPtr<__wasi_fd_t, MemoryType>, +) -> __wasi_errno_t { + super::ws_connect::(env, url, url_len, ret_sock) +} + +pub(crate) fn http_request( + env: &WasiEnv, + url: WasmPtr, + url_len: MemoryOffset, + method: WasmPtr, + method_len: MemoryOffset, + headers: WasmPtr, + headers_len: MemoryOffset, + gzip: __wasi_bool_t, + ret_handles: WasmPtr<__wasi_http_handles_t, MemoryType>, +) -> __wasi_errno_t { + super::http_request::( + env, + url, + url_len, + method, + method_len, + headers, + headers_len, + gzip, + ret_handles, + ) +} + +pub(crate) fn http_status( + env: &WasiEnv, + sock: __wasi_fd_t, + status: WasmPtr<__wasi_http_status_t, MemoryType>, + status_text: WasmPtr, + status_text_len: WasmPtr, + headers: WasmPtr, + headers_len: WasmPtr, +) -> __wasi_errno_t { + super::http_status::(env, sock, status) +} + +pub(crate) fn sock_status( + env: &WasiEnv, + sock: __wasi_fd_t, + ret_status: WasmPtr<__wasi_sockstatus_t, MemoryType>, +) -> __wasi_errno_t { + super::sock_status::(env, sock, ret_status) +} + +pub(crate) fn sock_addr_local( + env: &WasiEnv, + sock: __wasi_fd_t, + ret_addr: WasmPtr<__wasi_addr_port_t, MemoryType>, +) -> __wasi_errno_t { + super::sock_addr_local::(env, sock, ret_addr) +} + +pub(crate) fn sock_addr_peer( + env: &WasiEnv, + sock: __wasi_fd_t, + ro_addr: WasmPtr<__wasi_addr_port_t, MemoryType>, +) -> __wasi_errno_t { + super::sock_addr_peer::(env, sock, ro_addr) +} + +pub(crate) fn sock_open( + env: &WasiEnv, + af: __wasi_addressfamily_t, + ty: __wasi_socktype_t, + pt: __wasi_sockproto_t, + ro_sock: WasmPtr<__wasi_fd_t, MemoryType>, +) -> __wasi_errno_t { + super::sock_open::(env, af, ty, pt, ro_sock) +} + +pub(crate) fn sock_set_opt_flag( + env: &WasiEnv, + sock: __wasi_fd_t, + opt: __wasi_sockoption_t, + flag: __wasi_bool_t, +) -> __wasi_errno_t { + super::sock_set_opt_flag(env, sock, opt, flag) +} + +pub(crate) fn sock_get_opt_flag( + env: &WasiEnv, + sock: __wasi_fd_t, + opt: __wasi_sockoption_t, + ret_flag: WasmPtr<__wasi_bool_t, MemoryType>, +) -> __wasi_errno_t { + super::sock_get_opt_flag::(env, sock, opt, ret_flag) +} + +pub fn sock_set_opt_time( + env: &WasiEnv, + sock: __wasi_fd_t, + opt: __wasi_sockoption_t, + time: WasmPtr<__wasi_option_timestamp_t, MemoryType>, +) -> __wasi_errno_t { + super::sock_set_opt_time(env, sock, opt, time) +} + +pub fn sock_get_opt_time( + env: &WasiEnv, + sock: __wasi_fd_t, + opt: __wasi_sockoption_t, + ret_time: WasmPtr<__wasi_option_timestamp_t, MemoryType>, +) -> __wasi_errno_t { + super::sock_get_opt_time(env, sock, opt, ret_time) +} + +pub fn sock_set_opt_size( + env: &WasiEnv, + sock: __wasi_fd_t, + opt: __wasi_sockoption_t, + size: __wasi_filesize_t, +) -> __wasi_errno_t { + super::sock_set_opt_size(env, sock, opt, size) +} + +pub fn sock_get_opt_size( + env: &WasiEnv, + sock: __wasi_fd_t, + opt: __wasi_sockoption_t, + ret_size: WasmPtr<__wasi_filesize_t, MemoryType>, +) -> __wasi_errno_t { + super::sock_get_opt_size(env, sock, opt, ret_size) +} + +pub(crate) fn sock_join_multicast_v4( + env: &WasiEnv, + sock: __wasi_fd_t, + multiaddr: WasmPtr<__wasi_addr_ip4_t, MemoryType>, + iface: WasmPtr<__wasi_addr_ip4_t, MemoryType>, +) -> __wasi_errno_t { + super::sock_join_multicast_v4::(env, sock, multiaddr, iface) +} + +pub(crate) fn sock_leave_multicast_v4( + env: &WasiEnv, + sock: __wasi_fd_t, + multiaddr: WasmPtr<__wasi_addr_ip4_t, MemoryType>, + iface: WasmPtr<__wasi_addr_ip4_t, MemoryType>, +) -> __wasi_errno_t { + super::sock_leave_multicast_v4::(env, sock, multiaddr, iface) +} + +pub(crate) fn sock_join_multicast_v6( + env: &WasiEnv, + sock: __wasi_fd_t, + multiaddr: WasmPtr<__wasi_addr_ip6_t, MemoryType>, + iface: u32, +) -> __wasi_errno_t { + super::sock_join_multicast_v6::(env, sock, multiaddr, iface) +} + +pub(crate) fn sock_leave_multicast_v6( + env: &WasiEnv, + sock: __wasi_fd_t, + multiaddr: WasmPtr<__wasi_addr_ip6_t, MemoryType>, + iface: u32, +) -> __wasi_errno_t { + super::sock_leave_multicast_v6::(env, sock, multiaddr, iface) +} + +pub(crate) fn sock_bind( + env: &WasiEnv, + sock: __wasi_fd_t, + addr: WasmPtr<__wasi_addr_port_t, MemoryType>, +) -> __wasi_errno_t { + super::sock_bind::(env, sock, addr) +} + +pub(crate) fn sock_listen( + env: &WasiEnv, + sock: __wasi_fd_t, + backlog: MemoryOffset, +) -> __wasi_errno_t { + super::sock_listen::(env, sock, backlog) +} + +pub(crate) fn sock_accept( + env: &WasiEnv, + sock: __wasi_fd_t, + fd_flags: __wasi_fdflags_t, + ro_fd: WasmPtr<__wasi_fd_t, MemoryType>, + ro_addr: WasmPtr<__wasi_addr_port_t, MemoryType>, +) -> Result<__wasi_errno_t, WasiError> { + super::sock_accept::(env, sock, fd_flags, ro_fd, ro_addr) +} + +pub(crate) fn sock_connect( + env: &WasiEnv, + sock: __wasi_fd_t, + addr: WasmPtr<__wasi_addr_port_t, MemoryType>, +) -> __wasi_errno_t { + super::sock_connect::(env, sock, addr) +} + +pub(crate) fn sock_recv( + env: &WasiEnv, + sock: __wasi_fd_t, + ri_data: WasmPtr<__wasi_iovec_t, MemoryType>, + ri_data_len: MemoryOffset, + ri_flags: __wasi_riflags_t, + ro_data_len: WasmPtr, + ro_flags: WasmPtr<__wasi_roflags_t, MemoryType>, +) -> Result<__wasi_errno_t, WasiError> { + super::sock_recv::( + env, + sock, + ri_data, + ri_data_len, + ri_flags, + ro_data_len, + ro_flags, + ) +} + +pub(crate) fn sock_recv_from( + env: &WasiEnv, + sock: __wasi_fd_t, + ri_data: WasmPtr<__wasi_iovec_t, MemoryType>, + ri_data_len: MemoryOffset, + ri_flags: __wasi_riflags_t, + ro_data_len: WasmPtr, + ro_flags: WasmPtr<__wasi_roflags_t, MemoryType>, + ro_addr: WasmPtr<__wasi_addr_port_t, MemoryType>, +) -> Result<__wasi_errno_t, WasiError> { + super::sock_recv_from::( + env, + sock, + ri_data, + ri_data_len, + ri_flags, + ro_data_len, + ro_flags, + ro_addr, + ) +} + +pub(crate) fn sock_send( + env: &WasiEnv, + sock: __wasi_fd_t, + si_data: WasmPtr<__wasi_ciovec_t, MemoryType>, + si_data_len: MemoryOffset, + si_flags: __wasi_siflags_t, + ret_data_len: WasmPtr, +) -> Result<__wasi_errno_t, WasiError> { + super::sock_send::(env, sock, si_data, si_data_len, si_flags, ret_data_len) +} + +pub(crate) fn sock_send_to( + env: &WasiEnv, + sock: __wasi_fd_t, + si_data: WasmPtr<__wasi_ciovec_t, MemoryType>, + si_data_len: MemoryOffset, + si_flags: __wasi_siflags_t, + addr: WasmPtr<__wasi_addr_port_t, MemoryType>, + ret_data_len: WasmPtr, +) -> Result<__wasi_errno_t, WasiError> { + super::sock_send_to::( + env, + sock, + si_data, + si_data_len, + si_flags, + addr, + ret_data_len, + ) +} + +pub(crate) fn sock_send_file( + env: &WasiEnv, + out_fd: __wasi_fd_t, + in_fd: __wasi_fd_t, + offset: __wasi_filesize_t, + count: __wasi_filesize_t, + ret_sent: WasmPtr<__wasi_filesize_t, MemoryType>, +) -> Result<__wasi_errno_t, WasiError> { + unsafe { super::sock_send_file::(env, out_fd, in_fd, offset, count, ret_sent) } +} + +pub(crate) fn sock_shutdown( + env: &WasiEnv, + sock: __wasi_fd_t, + how: __wasi_sdflags_t, +) -> __wasi_errno_t { + super::sock_shutdown(env, sock, how) +} + +pub(crate) fn resolve( + env: &WasiEnv, + host: WasmPtr, + host_len: MemoryOffset, + port: u16, + ips: WasmPtr<__wasi_addr_t, MemoryType>, + nips: MemoryOffset, + ret_nips: WasmPtr, +) -> __wasi_errno_t { + super::resolve::(env, host, host_len, port, ips, nips, ret_nips) +} diff --git a/lib/wasi/src/syscalls/wasm32.rs b/lib/wasi/src/syscalls/wasm32.rs index 73cadb1b0e6..d9f56ac85c1 100644 --- a/lib/wasi/src/syscalls/wasm32.rs +++ b/lib/wasi/src/syscalls/wasm32.rs @@ -1,25 +1,26 @@ use crate::syscalls::types::*; +use chrono::prelude::*; use std::mem; use wasmer::WasmRef; pub fn platform_clock_res_get( clock_id: __wasi_clockid_t, resolution: WasmRef<__wasi_timestamp_t>, -) -> __wasi_errno_t { - let t_out = 1 * 1_000_000_000; - wasi_try_mem!(resolution.write(t_out as __wasi_timestamp_t)); - - // TODO: map output of clock_getres to __wasi_errno_t - __WASI_ESUCCESS +) -> Result { + let t_out = match clock_id { + __WASI_CLOCK_MONOTONIC => 10_000_000, + __WASI_CLOCK_REALTIME => 1, + __WASI_CLOCK_PROCESS_CPUTIME_ID => 1, + __WASI_CLOCK_THREAD_CPUTIME_ID => 1, + _ => return Err(__WASI_EINVAL), + }; + Ok(t_out) } pub fn platform_clock_time_get( clock_id: __wasi_clockid_t, precision: __wasi_timestamp_t, - time: WasmRef<__wasi_timestamp_t>, -) -> __wasi_errno_t { - let t_out = 1 * 1_000_000_000; - wasi_try_mem!(time.write(t_out as __wasi_timestamp_t)); - - __WASI_ESUCCESS +) -> Result { + let new_time: DateTime = Local::now(); + Ok(new_time.timestamp_nanos() as i64) } diff --git a/lib/wasi/src/syscalls/windows.rs b/lib/wasi/src/syscalls/windows.rs index 31fd8f42a1b..c461fa44ca8 100644 --- a/lib/wasi/src/syscalls/windows.rs +++ b/lib/wasi/src/syscalls/windows.rs @@ -5,7 +5,7 @@ use wasmer::WasmRef; pub fn platform_clock_res_get( clock_id: __wasi_clockid_t, resolution: WasmRef<__wasi_timestamp_t>, -) -> __wasi_errno_t { +) -> Result { let resolution_val = match clock_id { // resolution of monotonic clock at 10ms, from: // https://docs.microsoft.com/en-us/windows/desktop/api/sysinfoapi/nf-sysinfoapi-gettickcount64 @@ -13,34 +13,32 @@ pub fn platform_clock_res_get( // TODO: verify or compute this __WASI_CLOCK_REALTIME => 1, __WASI_CLOCK_PROCESS_CPUTIME_ID => { - return __WASI_EINVAL; + return Err(__WASI_EINVAL); } __WASI_CLOCK_THREAD_CPUTIME_ID => { - return __WASI_EINVAL; + return Err(__WASI_EINVAL); } - _ => return __WASI_EINVAL, + _ => return Err(__WASI_EINVAL), }; - wasi_try_mem!(resolution.write(resolution_val)); - __WASI_ESUCCESS + Ok(resolution_val) } pub fn platform_clock_time_get( clock_id: __wasi_clockid_t, precision: __wasi_timestamp_t, - time: WasmRef<__wasi_timestamp_t>, -) -> __wasi_errno_t { +) -> Result { let nanos = match clock_id { __WASI_CLOCK_MONOTONIC => { let tick_ms = unsafe { winapi::um::sysinfoapi::GetTickCount64() }; tick_ms * 1_000_000 } __WASI_CLOCK_REALTIME => { - let duration = wasi_try!(std::time::SystemTime::now() + let duration = std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .map_err(|e| { debug!("Error in wasi::platform_clock_time_get: {:?}", e); __WASI_EIO - })); + })?; duration.as_nanos() as u64 } __WASI_CLOCK_PROCESS_CPUTIME_ID => { @@ -49,8 +47,7 @@ pub fn platform_clock_time_get( __WASI_CLOCK_THREAD_CPUTIME_ID => { unimplemented!("wasi::platform_clock_time_get(__WASI_CLOCK_THREAD_CPUTIME_ID, ..)") } - _ => return __WASI_EINVAL, + _ => return Err(__WASI_EINVAL), }; - wasi_try_mem!(time.write(nanos)); - __WASI_ESUCCESS + Ok(nanos as i64) } diff --git a/lib/wasi/src/utils.rs b/lib/wasi/src/utils.rs index b46ac0852ba..77222889179 100644 --- a/lib/wasi/src/utils.rs +++ b/lib/wasi/src/utils.rs @@ -1,3 +1,4 @@ +use super::types::*; use std::collections::BTreeSet; use wasmer::Module; @@ -8,6 +9,44 @@ pub fn is_wasi_module(module: &Module) -> bool { get_wasi_version(module, false).is_some() } +#[allow(dead_code)] +/// Returns if the module is WASIX or not +pub fn is_wasix_module(module: &Module) -> bool { + match get_wasi_versions(module, false).ok_or(false) { + Ok(wasi_versions) => { + wasi_versions.contains(&WasiVersion::Wasix32v1) + || wasi_versions.contains(&WasiVersion::Wasix64v1) + } + Err(_) => false, + } +} + +pub fn map_io_err(err: std::io::Error) -> __wasi_errno_t { + use std::io::ErrorKind; + match err.kind() { + ErrorKind::NotFound => __WASI_ENOENT, + ErrorKind::PermissionDenied => __WASI_EPERM, + ErrorKind::ConnectionRefused => __WASI_ECONNREFUSED, + ErrorKind::ConnectionReset => __WASI_ECONNRESET, + ErrorKind::ConnectionAborted => __WASI_ECONNABORTED, + ErrorKind::NotConnected => __WASI_ENOTCONN, + ErrorKind::AddrInUse => __WASI_EADDRINUSE, + ErrorKind::AddrNotAvailable => __WASI_EADDRNOTAVAIL, + ErrorKind::BrokenPipe => __WASI_EPIPE, + ErrorKind::AlreadyExists => __WASI_EEXIST, + ErrorKind::WouldBlock => __WASI_EAGAIN, + ErrorKind::InvalidInput => __WASI_EIO, + ErrorKind::InvalidData => __WASI_EIO, + ErrorKind::TimedOut => __WASI_ETIMEDOUT, + ErrorKind::WriteZero => __WASI_EIO, + ErrorKind::Interrupted => __WASI_EINTR, + ErrorKind::Other => __WASI_EIO, + ErrorKind::UnexpectedEof => __WASI_EIO, + ErrorKind::Unsupported => __WASI_ENOTSUP, + _ => __WASI_EIO, + } +} + /// The version of WASI. This is determined by the imports namespace /// string. #[derive(Debug, Clone, Copy, Eq)] @@ -18,6 +57,12 @@ pub enum WasiVersion { /// `wasi_snapshot_preview1`. Snapshot1, + /// `wasix_32v1`. + Wasix32v1, + + /// `wasix_64v1`. + Wasix64v1, + /// Latest version. /// /// It's a “floating” version, i.e. it's an alias to the latest @@ -37,6 +82,8 @@ impl WasiVersion { match *self { WasiVersion::Snapshot0 => SNAPSHOT0_NAMESPACE, WasiVersion::Snapshot1 => SNAPSHOT1_NAMESPACE, + WasiVersion::Wasix32v1 => WASIX_32V1_NAMESPACE, + WasiVersion::Wasix64v1 => WASIX_64V1_NAMESPACE, WasiVersion::Latest => SNAPSHOT1_NAMESPACE, } } @@ -51,6 +98,8 @@ impl PartialEq for WasiVersion { | (Self::Latest, Self::Latest) | (Self::Snapshot0, Self::Snapshot0) | (Self::Snapshot1, Self::Snapshot1) + | (Self::Wasix32v1, Self::Wasix32v1) + | (Self::Wasix64v1, Self::Wasix64v1) ) } } @@ -67,12 +116,19 @@ impl Ord for WasiVersion { return std::cmp::Ordering::Equal; } match (*self, *other) { - // if snapshot0 is not equal, it must be less - (Self::Snapshot0, _) => std::cmp::Ordering::Less, - (Self::Snapshot1, Self::Snapshot0) | (Self::Latest, Self::Snapshot0) => { + (Self::Snapshot1, Self::Snapshot0) => std::cmp::Ordering::Greater, + (Self::Wasix32v1, Self::Snapshot1) | (Self::Wasix32v1, Self::Snapshot0) => { std::cmp::Ordering::Greater } - _ => unreachable!("Missing info about ordering of WasiVerison"), + (Self::Wasix64v1, Self::Wasix32v1) + | (Self::Wasix64v1, Self::Snapshot1) + | (Self::Wasix64v1, Self::Snapshot0) => std::cmp::Ordering::Greater, + (Self::Latest, Self::Wasix64v1) + | (Self::Latest, Self::Wasix32v1) + | (Self::Latest, Self::Snapshot1) + | (Self::Latest, Self::Snapshot0) => std::cmp::Ordering::Greater, + // they are not equal and not greater so they must be less + (_, _) => std::cmp::Ordering::Less, } } } @@ -83,6 +139,12 @@ const SNAPSHOT0_NAMESPACE: &str = "wasi_unstable"; /// Namespace for the `Snapshot1` version. const SNAPSHOT1_NAMESPACE: &str = "wasi_snapshot_preview1"; +/// Namespace for the `wasix` version. +const WASIX_32V1_NAMESPACE: &str = "wasix_32v1"; + +/// Namespace for the `wasix` version. +const WASIX_64V1_NAMESPACE: &str = "wasix_64v1"; + /// Detect the version of WASI being used based on the import /// namespaces. /// @@ -99,6 +161,8 @@ pub fn get_wasi_version(module: &Module, strict: bool) -> Option { match first_module.as_str() { SNAPSHOT0_NAMESPACE => Some(WasiVersion::Snapshot0), SNAPSHOT1_NAMESPACE => Some(WasiVersion::Snapshot1), + WASIX_32V1_NAMESPACE => Some(WasiVersion::Wasix32v1), + WASIX_64V1_NAMESPACE => Some(WasiVersion::Wasix64v1), _ => None, } } else { @@ -110,6 +174,8 @@ pub fn get_wasi_version(module: &Module, strict: bool) -> Option { imports.find_map(|module| match module.as_str() { SNAPSHOT0_NAMESPACE => Some(WasiVersion::Snapshot0), SNAPSHOT1_NAMESPACE => Some(WasiVersion::Snapshot1), + WASIX_32V1_NAMESPACE => Some(WasiVersion::Wasix32v1), + WASIX_64V1_NAMESPACE => Some(WasiVersion::Wasix64v1), _ => None, }) } @@ -132,6 +198,12 @@ pub fn get_wasi_versions(module: &Module, strict: bool) -> Option { out.insert(WasiVersion::Snapshot1); } + WASIX_32V1_NAMESPACE => { + out.insert(WasiVersion::Wasix32v1); + } + WASIX_64V1_NAMESPACE => { + out.insert(WasiVersion::Wasix64v1); + } _ => { non_wasi_seen = true; } @@ -151,31 +223,65 @@ mod test { #[test] fn wasi_version_equality() { assert_eq!(WasiVersion::Snapshot0, WasiVersion::Snapshot0); + assert_eq!(WasiVersion::Wasix64v1, WasiVersion::Wasix64v1); + assert_eq!(WasiVersion::Wasix32v1, WasiVersion::Wasix32v1); assert_eq!(WasiVersion::Snapshot1, WasiVersion::Snapshot1); assert_eq!(WasiVersion::Snapshot1, WasiVersion::Latest); assert_eq!(WasiVersion::Latest, WasiVersion::Snapshot1); assert_eq!(WasiVersion::Latest, WasiVersion::Latest); + assert!(WasiVersion::Wasix32v1 != WasiVersion::Wasix64v1); + assert!(WasiVersion::Wasix64v1 != WasiVersion::Wasix32v1); + assert!(WasiVersion::Snapshot1 != WasiVersion::Wasix64v1); + assert!(WasiVersion::Wasix64v1 != WasiVersion::Snapshot1); + assert!(WasiVersion::Snapshot1 != WasiVersion::Wasix32v1); + assert!(WasiVersion::Wasix32v1 != WasiVersion::Snapshot1); assert!(WasiVersion::Snapshot0 != WasiVersion::Snapshot1); assert!(WasiVersion::Snapshot1 != WasiVersion::Snapshot0); assert!(WasiVersion::Snapshot0 != WasiVersion::Latest); assert!(WasiVersion::Latest != WasiVersion::Snapshot0); + assert!(WasiVersion::Snapshot0 != WasiVersion::Latest); + assert!(WasiVersion::Latest != WasiVersion::Snapshot0); + assert!(WasiVersion::Wasix32v1 != WasiVersion::Latest); + assert!(WasiVersion::Wasix64v1 != WasiVersion::Latest); } #[test] fn wasi_version_ordering() { assert!(WasiVersion::Snapshot0 <= WasiVersion::Snapshot0); assert!(WasiVersion::Snapshot1 <= WasiVersion::Snapshot1); + assert!(WasiVersion::Wasix32v1 <= WasiVersion::Wasix32v1); + assert!(WasiVersion::Wasix64v1 <= WasiVersion::Wasix64v1); assert!(WasiVersion::Latest <= WasiVersion::Latest); assert!(WasiVersion::Snapshot0 >= WasiVersion::Snapshot0); assert!(WasiVersion::Snapshot1 >= WasiVersion::Snapshot1); + assert!(WasiVersion::Wasix32v1 >= WasiVersion::Wasix32v1); + assert!(WasiVersion::Wasix64v1 >= WasiVersion::Wasix64v1); assert!(WasiVersion::Latest >= WasiVersion::Latest); - assert!(WasiVersion::Snapshot0 < WasiVersion::Snapshot1); - assert!(WasiVersion::Snapshot1 > WasiVersion::Snapshot0); assert!(WasiVersion::Snapshot0 < WasiVersion::Latest); + assert!(WasiVersion::Snapshot0 < WasiVersion::Wasix32v1); + assert!(WasiVersion::Snapshot0 < WasiVersion::Wasix64v1); + assert!(WasiVersion::Snapshot0 < WasiVersion::Snapshot1); assert!(WasiVersion::Latest > WasiVersion::Snapshot0); + assert!(WasiVersion::Wasix32v1 > WasiVersion::Snapshot0); + assert!(WasiVersion::Wasix64v1 > WasiVersion::Snapshot0); + assert!(WasiVersion::Snapshot1 > WasiVersion::Snapshot0); + + assert!(WasiVersion::Snapshot1 < WasiVersion::Wasix32v1); + assert!(WasiVersion::Snapshot1 < WasiVersion::Wasix64v1); + assert!(WasiVersion::Wasix32v1 > WasiVersion::Snapshot1); + assert!(WasiVersion::Wasix64v1 > WasiVersion::Snapshot1); + + assert!(WasiVersion::Wasix32v1 < WasiVersion::Latest); + assert!(WasiVersion::Wasix32v1 > WasiVersion::Snapshot1); + assert!(WasiVersion::Wasix64v1 < WasiVersion::Latest); + assert!(WasiVersion::Wasix64v1 > WasiVersion::Snapshot1); + assert!(WasiVersion::Latest > WasiVersion::Wasix32v1); + assert!(WasiVersion::Snapshot1 < WasiVersion::Wasix32v1); + assert!(WasiVersion::Latest > WasiVersion::Wasix64v1); + assert!(WasiVersion::Snapshot1 < WasiVersion::Wasix32v1); - assert!(!(WasiVersion::Snapshot1 < WasiVersion::Latest)); - assert!(!(WasiVersion::Latest > WasiVersion::Snapshot1)); + assert!(WasiVersion::Wasix32v1 < WasiVersion::Wasix64v1); + assert!(WasiVersion::Wasix64v1 > WasiVersion::Wasix32v1); } } diff --git a/lib/wasi/tests/js.rs b/lib/wasi/tests/stdio.rs similarity index 75% rename from lib/wasi/tests/js.rs rename to lib/wasi/tests/stdio.rs index 909e050fde4..45dcd57e4de 100644 --- a/lib/wasi/tests/js.rs +++ b/lib/wasi/tests/stdio.rs @@ -1,10 +1,45 @@ -#![cfg(feature = "js")] +use std::io::{Read, Write}; -use wasm_bindgen_test::*; use wasmer::{Instance, Module, Store}; -use wasmer_wasi::{Stdin, Stdout, WasiState}; +use wasmer_wasi::{Pipe, WasiState}; + +mod sys { + #[test] + fn test_stdout() { + super::test_stdout() + } + + #[test] + fn test_stdin() { + super::test_stdin() + } + + #[test] + fn test_env() { + super::test_env() + } +} + +#[cfg(feature = "js")] +mod js { + use wasm_bindgen_test::*; + + #[wasm_bindgen_test] + fn test_stdout() { + super::test_stdout() + } + + #[wasm_bindgen_test] + fn test_stdin() { + super::test_stdin() + } + + #[wasm_bindgen_test] + fn test_env() { + super::test_env() + } +} -#[wasm_bindgen_test] fn test_stdout() { let store = Store::default(); let module = Module::new(&store, br#" @@ -38,10 +73,10 @@ fn test_stdout() { "#).unwrap(); // Create the `WasiEnv`. - // let stdout = Stdout::default(); + let mut stdout = Pipe::default(); let mut wasi_env = WasiState::new("command-name") .args(&["Gordon"]) - // .stdout(Box::new(stdout)) + .stdout(Box::new(stdout.clone())) .finalize() .unwrap(); @@ -55,17 +90,17 @@ fn test_stdout() { let start = instance.exports.get_function("_start").unwrap(); start.call(&[]).unwrap(); - let state = wasi_env.state(); - let stdout = state.fs.stdout().unwrap().as_ref().unwrap(); - let stdout = stdout.downcast_ref::().unwrap(); - let stdout_as_str = std::str::from_utf8(&stdout.buf).unwrap(); + let mut stdout_str = String::new(); + stdout.read_to_string(&mut stdout_str).unwrap(); + let stdout_as_str = stdout_str.as_str(); assert_eq!(stdout_as_str, "hello world\n"); } -#[wasm_bindgen_test] + fn test_env() { let store = Store::default(); let module = Module::new(&store, include_bytes!("envvar.wasm")).unwrap(); + #[cfg(feature = "js")] tracing_wasm::set_as_global_default_with_config({ let mut builder = tracing_wasm::WASMLayerConfigBuilder::new(); builder.set_console_config(tracing_wasm::ConsoleConfig::ReportWithoutConsoleColor); @@ -73,7 +108,7 @@ fn test_env() { }); // Create the `WasiEnv`. - // let stdout = Stdout::default(); + let mut stdout = Pipe::new(); let mut wasi_state_builder = WasiState::new("command-name"); wasi_state_builder .args(&["Gordon"]) @@ -82,7 +117,7 @@ fn test_env() { .env("TEST2", "VALUE2"); // panic!("envs: {:?}", wasi_state_builder.envs); let mut wasi_env = wasi_state_builder - // .stdout(Box::new(stdout)) + .stdout(Box::new(stdout.clone())) .finalize() .unwrap(); @@ -96,26 +131,27 @@ fn test_env() { let start = instance.exports.get_function("_start").unwrap(); start.call(&[]).unwrap(); - let state = wasi_env.state(); - let stdout = state.fs.stdout().unwrap().as_ref().unwrap(); - let stdout = stdout.downcast_ref::().unwrap(); - let stdout_as_str = std::str::from_utf8(&stdout.buf).unwrap(); + let mut stdout_str = String::new(); + stdout.read_to_string(&mut stdout_str).unwrap(); + let stdout_as_str = stdout_str.as_str(); assert_eq!(stdout_as_str, "Env vars:\nDOG=X\nTEST2=VALUE2\nTEST=VALUE\nDOG Ok(\"X\")\nDOG_TYPE Err(NotPresent)\nSET VAR Ok(\"HELLO\")\n"); } -#[wasm_bindgen_test] fn test_stdin() { let store = Store::default(); let module = Module::new(&store, include_bytes!("stdin-hello.wasm")).unwrap(); // Create the `WasiEnv`. - let mut stdin = Stdin::default(); - stdin.buf = "Hello, stdin!".as_bytes().to_owned(); + let mut stdin = Pipe::new(); let mut wasi_env = WasiState::new("command-name") - .stdin(Box::new(stdin)) + .stdin(Box::new(stdin.clone())) .finalize() .unwrap(); + // Write to STDIN + let buf = "Hello, stdin!\n".as_bytes().to_owned(); + stdin.write(&buf[..]).unwrap(); + // Generate an `ImportObject`. let import_object = wasi_env.import_object(&module).unwrap(); @@ -125,11 +161,10 @@ fn test_stdin() { // Let's call the `_start` function, which is our `main` function in Rust. let start = instance.exports.get_function("_start").unwrap(); let result = start.call(&[]); - assert!(result.is_err()); - // let status = result.unwrap_err().downcast::().unwrap(); - let state = wasi_env.state(); - let stdin = state.fs.stdin().unwrap().as_ref().unwrap(); - let stdin = stdin.downcast_ref::().unwrap(); + assert!(!result.is_err()); + // We assure stdin is now empty - assert_eq!(stdin.buf.len(), 0); + let mut buf = Vec::new(); + stdin.read_to_end(&mut buf).unwrap(); + assert_eq!(buf.len(), 0); } diff --git a/tests/compilers/config.rs b/tests/compilers/config.rs index 8274ed20044..28c35b7cbf8 100644 --- a/tests/compilers/config.rs +++ b/tests/compilers/config.rs @@ -99,7 +99,10 @@ impl Config { } } - pub fn compiler_config(&self, canonicalize_nans: bool) -> Box { + pub fn compiler_config( + &self, + #[allow(unused_variables)] canonicalize_nans: bool, + ) -> Box { match &self.compiler { #[cfg(feature = "cranelift")] Compiler::Cranelift => { @@ -135,6 +138,7 @@ impl Config { } } + #[allow(dead_code)] fn add_middlewares(&self, config: &mut dyn CompilerConfig) { for middleware in self.middlewares.iter() { config.push_middleware(middleware.clone()); diff --git a/tests/compilers/imports.rs b/tests/compilers/imports.rs index b94c22caeea..869e330c6c5 100644 --- a/tests/compilers/imports.rs +++ b/tests/compilers/imports.rs @@ -38,7 +38,7 @@ fn get_module(store: &Store) -> Result { (start $foo) "#; - let module = Module::new(&store, &wat)?; + let module = Module::new(store, &wat)?; Ok(module) } @@ -311,7 +311,7 @@ fn get_module2(store: &Store) -> Result { (call 0)) "#; - let module = Module::new(&store, &wat)?; + let module = Module::new(store, &wat)?; Ok(module) } @@ -334,7 +334,7 @@ fn dynamic_function_with_env_wasmer_env_init_works(config: crate::Config) -> Res &module, &imports! { "host" => { - "fn" => Function::new_with_env(&store, FunctionType::new(vec![], vec![]), env.clone(), |env, _values| { + "fn" => Function::new_with_env(&store, FunctionType::new(vec![], vec![]), env, |env, _values| { assert!(env.memory_ref().is_some()); Ok(vec![]) }), @@ -375,7 +375,7 @@ fn multi_use_host_fn_manages_memory_correctly(config: crate::Config) -> Result<( let imports = imports! { "host" => { - "fn" => Function::new_native_with_env(&store, env.clone(), host_fn), + "fn" => Function::new_native_with_env(&store, env, host_fn), }, }; let instance1 = Instance::new(&module, &imports)?; diff --git a/tests/compilers/issues.rs b/tests/compilers/issues.rs index eddd6d250f2..dcb63c6c207 100644 --- a/tests/compilers/issues.rs +++ b/tests/compilers/issues.rs @@ -67,7 +67,7 @@ fn issue_2329(mut config: crate::Config) -> Result<()> { "env" => { "__read_memory" => Function::new_native_with_env( &store, - env.clone(), + env, read_memory ), } @@ -210,10 +210,7 @@ fn call_with_static_data_pointers(mut config: crate::Config) -> Result<()> { "mango", Function::new_native_with_env(&store, env.clone(), mango), ); - exports.insert( - "gas", - Function::new_native_with_env(&store, env.clone(), gas), - ); + exports.insert("gas", Function::new_native_with_env(&store, env, gas)); let mut imports = Imports::new(); imports.register_namespace("env", exports); let instance = Instance::new(&module, &imports)?; diff --git a/tests/compilers/metering.rs b/tests/compilers/metering.rs index d9ee5e35cd2..6732aa4fb33 100644 --- a/tests/compilers/metering.rs +++ b/tests/compilers/metering.rs @@ -101,7 +101,7 @@ fn complex_loop(mut config: crate::Config) -> Result<()> { // } // return y; // } - static WAT: &'static str = r#" + static WAT: &str = r#" (module (type $t0 (func (param i32 i32) (result i32))) (type $t1 (func)) diff --git a/tests/integration/cli/tests/compile.rs b/tests/integration/cli/tests/compile.rs index 17d50d081a6..8cba88de268 100644 --- a/tests/integration/cli/tests/compile.rs +++ b/tests/integration/cli/tests/compile.rs @@ -126,7 +126,7 @@ fn staticlib_engine_works() -> anyhow::Result<()> { WasmerCompile { current_dir: operating_dir.clone(), - wasm_path: wasm_path, + wasm_path, wasm_object_path: wasm_object_path.clone(), header_output_path, compiler: Compiler::Cranelift, diff --git a/tests/integration/cli/tests/create_exe.rs b/tests/integration/cli/tests/create_exe.rs index d1c2952b433..abb213c58b6 100644 --- a/tests/integration/cli/tests/create_exe.rs +++ b/tests/integration/cli/tests/create_exe.rs @@ -81,7 +81,7 @@ fn create_exe_works() -> anyhow::Result<()> { WasmerCreateExe { current_dir: operating_dir.clone(), - wasm_path: wasm_path, + wasm_path, native_executable_path: executable_path.clone(), compiler: Compiler::Cranelift, ..Default::default() @@ -114,7 +114,7 @@ fn create_exe_works_with_file() -> anyhow::Result<()> { WasmerCreateExe { current_dir: operating_dir.clone(), - wasm_path: wasm_path, + wasm_path, native_executable_path: executable_path.clone(), compiler: Compiler::Cranelift, ..Default::default() diff --git a/tests/integration/cli/tests/staticlib_engine_test_c_source.c b/tests/integration/cli/tests/staticlib_engine_test_c_source.c index 68a79f1a49d..0a3bc08878c 100644 --- a/tests/integration/cli/tests/staticlib_engine_test_c_source.c +++ b/tests/integration/cli/tests/staticlib_engine_test_c_source.c @@ -8,11 +8,13 @@ static void print_wasmer_error() { int error_len = wasmer_last_error_length(); - printf("Error len: `%d`\n", error_len); - char *error_str = (char *)malloc(error_len); - wasmer_last_error_message(error_str, error_len); - printf("Error str: `%s`\n", error_str); - free(error_str); + if (error_len > 0) { + printf("Error len: `%d`\n", error_len); + char *error_str = (char *)malloc(error_len); + wasmer_last_error_message(error_str, error_len); + printf("Error str: `%s`\n", error_str); + free(error_str); + } } int main() { diff --git a/tests/lib/wast/src/wasi_wast.rs b/tests/lib/wast/src/wasi_wast.rs index b01bc70672a..f4e3aff5306 100644 --- a/tests/lib/wast/src/wasi_wast.rs +++ b/tests/lib/wast/src/wasi_wast.rs @@ -2,6 +2,7 @@ use anyhow::Context; use std::fs::{read_dir, File, OpenOptions, ReadDir}; use std::io::{self, Read, Seek, Write}; use std::path::{Path, PathBuf}; +use std::sync::{mpsc, Arc, Mutex}; use wasmer::{Imports, Instance, Module, Store}; use wasmer_vfs::{host_fs, mem_fs, FileSystem}; use wasmer_wasi::types::{__wasi_filesize_t, __wasi_timestamp_t}; @@ -39,12 +40,12 @@ pub struct WasiTest<'a> { // TODO: add `test_fs` here to sandbox better const BASE_TEST_DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../../wasi-wast/wasi/"); -fn get_stdout_output(wasi_state: &WasiState) -> anyhow::Result { - let stdout_boxed = wasi_state.fs.stdout()?.as_ref().unwrap(); - let stdout = (&**stdout_boxed) - .downcast_ref::() - .unwrap(); - let stdout_str = std::str::from_utf8(&stdout.output)?; +fn get_stdio_output(rx: &mpsc::Receiver>) -> anyhow::Result { + let mut stdio = Vec::new(); + while let Ok(mut buf) = rx.try_recv() { + stdio.append(&mut buf); + } + let stdout_str = std::str::from_utf8(&stdio[..])?; #[cfg(target_os = "windows")] // normalize line endings return Ok(stdout_str.replace("\r\n", "\n")); @@ -53,21 +54,6 @@ fn get_stdout_output(wasi_state: &WasiState) -> anyhow::Result { return Ok(stdout_str.to_string()); } -fn get_stderr_output(wasi_state: &WasiState) -> anyhow::Result { - let stderr_boxed = wasi_state.fs.stderr()?.as_ref().unwrap(); - let stderr = (&**stderr_boxed) - .downcast_ref::() - .unwrap(); - let stderr_str = std::str::from_utf8(&stderr.output)?; - - #[cfg(target_os = "windows")] - // normalize line endings - return Ok(stderr_str.replace("\r\n", "\n")); - - #[cfg(not(target_os = "windows"))] - return Ok(stderr_str.to_string()); -} - #[allow(dead_code)] impl<'a> WasiTest<'a> { /// Turn a WASI WAST string into a list of tokens. @@ -96,15 +82,15 @@ impl<'a> WasiTest<'a> { out }; let module = Module::new(store, &wasm_bytes)?; - let (env, _tempdirs) = self.create_wasi_env(filesystem_kind)?; + let (env, _tempdirs, stdout_rx, stderr_rx) = self.create_wasi_env(filesystem_kind)?; let imports = self.get_imports(store, &module, env.clone())?; let instance = Instance::new(&module, &imports)?; let start = instance.exports.get_function("_start")?; if let Some(stdin) = &self.stdin { - let mut state = env.state(); - let wasi_stdin = state.fs.stdin_mut()?.as_mut().unwrap(); + let state = env.state(); + let mut wasi_stdin = state.stdin().unwrap().unwrap(); // Then we can write to it! write!(wasi_stdin, "{}", stdin.stream)?; } @@ -113,9 +99,8 @@ impl<'a> WasiTest<'a> { match start.call(&[]) { Ok(_) => {} Err(e) => { - let wasi_state = env.state(); - let stdout_str = get_stdout_output(&wasi_state)?; - let stderr_str = get_stderr_output(&wasi_state)?; + let stdout_str = get_stdio_output(&stdout_rx)?; + let stderr_str = get_stdio_output(&stderr_rx)?; Err(e).with_context(|| { format!( "failed to run WASI `_start` function: failed with stdout: \"{}\"\nstderr: \"{}\"", @@ -126,15 +111,13 @@ impl<'a> WasiTest<'a> { } } - let wasi_state = env.state(); - if let Some(expected_stdout) = &self.assert_stdout { - let stdout_str = get_stdout_output(&wasi_state)?; + let stdout_str = get_stdio_output(&stdout_rx)?; assert_eq!(stdout_str, expected_stdout.expected); } if let Some(expected_stderr) = &self.assert_stderr { - let stderr_str = get_stderr_output(&wasi_state)?; + let stderr_str = get_stdio_output(&stderr_rx)?; assert_eq!(stderr_str, expected_stderr.expected); } @@ -142,10 +125,16 @@ impl<'a> WasiTest<'a> { } /// Create the wasi env with the given metadata. + #[allow(clippy::type_complexity)] fn create_wasi_env( &self, filesystem_kind: WasiFileSystemKind, - ) -> anyhow::Result<(WasiEnv, Vec)> { + ) -> anyhow::Result<( + WasiEnv, + Vec, + mpsc::Receiver>, + mpsc::Receiver>, + )> { let mut builder = WasiState::new(self.wasm_path); let stdin_pipe = Pipe::new(); @@ -216,15 +205,17 @@ impl<'a> WasiTest<'a> { } } + let (stdout, stdout_rx) = OutputCapturerer::new(); + let (stderr, stderr_rx) = OutputCapturerer::new(); let out = builder .args(&self.args) // adding this causes some tests to fail. TODO: investigate this //.env("RUST_BACKTRACE", "1") - .stdout(Box::new(OutputCapturerer::new())) - .stderr(Box::new(OutputCapturerer::new())) + .stdout(Box::new(stdout)) + .stderr(Box::new(stderr)) .finalize()?; - Ok((out, host_temp_dirs_to_not_drop)) + Ok((out, host_temp_dirs_to_not_drop, stdout_rx, stderr_rx)) } /// Get the correct [`WasiVersion`] from the Wasm [`Module`]. @@ -528,14 +519,20 @@ mod test { } } -#[derive(Debug)] +#[derive(Debug, Clone)] struct OutputCapturerer { - output: Vec, + output: Arc>>>, } impl OutputCapturerer { - fn new() -> Self { - Self { output: vec![] } + fn new() -> (Self, mpsc::Receiver>) { + let (tx, rx) = mpsc::channel(); + ( + Self { + output: Arc::new(Mutex::new(tx)), + }, + rx, + ) } } @@ -575,18 +572,33 @@ impl Seek for OutputCapturerer { } impl Write for OutputCapturerer { fn write(&mut self, buf: &[u8]) -> io::Result { - self.output.extend_from_slice(buf); + self.output + .lock() + .unwrap() + .send(buf.to_vec()) + .map_err(|err| io::Error::new(io::ErrorKind::BrokenPipe, err.to_string()))?; Ok(buf.len()) } fn flush(&mut self) -> io::Result<()> { Ok(()) } fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - self.output.extend_from_slice(buf); + self.output + .lock() + .unwrap() + .send(buf.to_vec()) + .map_err(|err| io::Error::new(io::ErrorKind::BrokenPipe, err.to_string()))?; Ok(()) } fn write_fmt(&mut self, fmt: std::fmt::Arguments) -> io::Result<()> { - self.output.write_fmt(fmt) + let mut buf = Vec::::new(); + buf.write_fmt(fmt)?; + self.output + .lock() + .unwrap() + .send(buf) + .map_err(|err| io::Error::new(io::ErrorKind::BrokenPipe, err.to_string()))?; + Ok(()) } }