diff --git a/.github/actions/install-rust/main.js b/.github/actions/install-rust/main.js index fe01702bb7..b2355abfa1 100644 --- a/.github/actions/install-rust/main.js +++ b/.github/actions/install-rust/main.js @@ -1,17 +1,13 @@ const child_process = require('child_process'); const toolchain = process.env.INPUT_TOOLCHAIN; -for (var i = 0, keys = Object.keys(process.env), ii = keys.length; i < ii; i++) { - console.log(keys[i] + '=' + process.env[keys[i]]); -} - if (process.platform === 'darwin') { child_process.execSync(`curl https://sh.rustup.rs | sh -s -- -y --default-toolchain=none --profile=minimal`); const bindir = `${process.env.HOME}/.cargo/bin`; console.log(`::add-path::${bindir}`); process.env.PATH = `${process.env.PATH}:${bindir}`; - child_process.execFileSync('rustup', ['set', 'profile', 'minimal']); } +child_process.execFileSync('rustup', ['set', 'profile', 'minimal']); child_process.execFileSync('rustup', ['update', toolchain, '--no-self-update']); child_process.execFileSync('rustup', ['default', toolchain]); diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a03f0e55a9..d00d9f7507 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -92,7 +92,6 @@ jobs: name: Test runs-on: ${{ matrix.os }} strategy: - fail-fast: false matrix: build: [stable, beta, nightly, windows, macos] include: @@ -102,9 +101,11 @@ jobs: - build: beta os: ubuntu-latest rust: beta + # FIXME need to wait for rust-lang/rust#67065 to get into nightlies, + # and then this can be updated to `nightly` again. - build: nightly os: ubuntu-latest - rust: nightly + rust: nightly-2019-12-03 - build: macos os: macos-latest rust: stable @@ -163,7 +164,6 @@ jobs: name: Python Wheel runs-on: ${{ matrix.os }} strategy: - fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] steps: @@ -260,7 +260,6 @@ jobs: name: Build wasmtime runs-on: ${{ matrix.os }} strategy: - fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] steps: @@ -328,7 +327,26 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, macos-latest, windows-latest] + build: [linux-debug, linux-release, macos-debug, macos-release, windows-debug, windows-release] + include: + - build: linux-debug + os: ubuntu-latest + config: debug + - build: linux-release + os: ubuntu-latest + config: release + - build: macos-debug + os: macos-latest + config: debug + - build: macos-release + os: macos-latest + config: release + - build: windows-debug + os: windows-latest + config: debug + - build: windows-release + os: windows-latest + config: release steps: - uses: actions/checkout@v1 with: @@ -339,12 +357,14 @@ jobs: - uses: actions/setup-dotnet@v1 with: dotnet-version: '3.0.101' - - run: | + - name: Test + run: | cd crates/misc/dotnet/tests - dotnet test - - run: | + dotnet test -c ${{ matrix.config }} + - name: Create package + run: | cd crates/misc/dotnet/src - dotnet pack + dotnet pack -c ${{ matrix.config }} if: matrix.os == 'macos-latest' # Currently the pack target only supports macOS # Consumes all published artifacts from all the previous build steps, creates diff --git a/Cargo.toml b/Cargo.toml index da4c511759..f49eea6671 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,10 +15,6 @@ publish = false [dependencies] # Enable all supported architectures by default. -cranelift-codegen = { version = "0.50.0", features = ["enable-serde", "all-arch"] } -cranelift-entity = { version = "0.50.0", features = ["enable-serde"] } -cranelift-wasm = { version = "0.50.0", features = ["enable-serde"] } -cranelift-native = "0.50.0" wasmtime = { path = "crates/api" } wasmtime-debug = { path = "crates/debug" } wasmtime-environ = { path = "crates/environ" } diff --git a/crates/api/Cargo.toml b/crates/api/Cargo.toml index 9e4689616c..f0359a97e4 100644 --- a/crates/api/Cargo.toml +++ b/crates/api/Cargo.toml @@ -13,15 +13,10 @@ name = "wasmtime" crate-type = ["lib", "staticlib", "cdylib"] [dependencies] -cranelift-codegen = { version = "0.50.0", features = ["enable-serde"] } -cranelift-native = "0.50.0" -cranelift-entity = { version = "0.50.0", features = ["enable-serde"] } -cranelift-wasm = { version = "0.50.0", features = ["enable-serde"] } -cranelift-frontend = "0.50.0" wasmtime-runtime = { path = "../runtime" } wasmtime-environ = { path = "../environ" } wasmtime-jit = { path = "../jit" } -wasmparser = { version = "0.39.2", default-features = false } +wasmparser = { version = "0.44.0", default-features = false } target-lexicon = { version = "0.9.0", default-features = false } anyhow = "1.0.19" thiserror = "1.0.4" diff --git a/crates/api/examples/memory.rs b/crates/api/examples/memory.rs index 3c72d87bbb..7214a95e8c 100644 --- a/crates/api/examples/memory.rs +++ b/crates/api/examples/memory.rs @@ -51,7 +51,7 @@ macro_rules! call { ($func:expr, $($p:expr),*) => { match $func.borrow().call(&[$($p.into()),*]) { Ok(result) => { - let result: i32 = result[0].clone().into(); + let result: i32 = result[0].unwrap_i32(); result } Err(_) => { bail!("> Error on result, expected return"); } @@ -151,7 +151,7 @@ fn main() -> Result<(), Error> { // Create stand-alone memory. // TODO(wasm+): Once Wasm allows multiple memories, turn this into import. println!("Creating stand-alone memory..."); - let memorytype = MemoryType::new(Limits::new(5, 5)); + let memorytype = MemoryType::new(Limits::new(5, Some(5))); let mut memory2 = Memory::new(&store, memorytype); check!(memory2.size(), 5u32); check!(memory2.grow(1), false); diff --git a/crates/api/examples/multi.rs b/crates/api/examples/multi.rs index 9fb4d4118f..d02e56cbc6 100644 --- a/crates/api/examples/multi.rs +++ b/crates/api/examples/multi.rs @@ -9,10 +9,10 @@ struct Callback; impl Callable for Callback { fn call(&self, args: &[Val], results: &mut [Val]) -> Result<(), HostRef> { println!("Calling back..."); - println!("> {} {}", args[0].i32(), args[1].i64()); + println!("> {} {}", args[0].unwrap_i32(), args[1].unwrap_i64()); - results[0] = Val::I64(args[1].i64() + 1); - results[1] = Val::I32(args[0].i32() + 1); + results[0] = Val::I64(args[1].unwrap_i64() + 1); + results[1] = Val::I32(args[0].unwrap_i32() + 1); Ok(()) } } @@ -88,10 +88,10 @@ fn main() -> Result<()> { .map_err(|e| format_err!("> Error calling g! {:?}", e))?; println!("Printing result..."); - println!("> {} {}", results[0].i64(), results[1].i32()); + println!("> {} {}", results[0].unwrap_i64(), results[1].unwrap_i32()); - debug_assert_eq!(results[0].i64(), 4); - debug_assert_eq!(results[1].i32(), 2); + debug_assert_eq!(results[0].unwrap_i64(), 4); + debug_assert_eq!(results[1].unwrap_i32(), 2); // Call `$round_trip_many`. println!("Calling export \"round_trip_many\"..."); @@ -115,7 +115,7 @@ fn main() -> Result<()> { println!("Printing result..."); print!(">"); for r in results.iter() { - print!(" {}", r.i64()); + print!(" {}", r.unwrap_i64()); } println!(); diff --git a/crates/api/src/callable.rs b/crates/api/src/callable.rs index 2659ce06a7..84026ca3d5 100644 --- a/crates/api/src/callable.rs +++ b/crates/api/src/callable.rs @@ -1,4 +1,3 @@ -use crate::data_structures::ir; use crate::r#ref::HostRef; use crate::runtime::Store; use crate::trampoline::{generate_func_export, take_api_trap}; @@ -6,6 +5,7 @@ use crate::trap::Trap; use crate::types::FuncType; use crate::values::Val; use std::rc::Rc; +use wasmtime_environ::ir; use wasmtime_jit::InstanceHandle; use wasmtime_runtime::Export; diff --git a/crates/api/src/context.rs b/crates/api/src/context.rs index 42b98a0d1f..29cea2e98d 100644 --- a/crates/api/src/context.rs +++ b/crates/api/src/context.rs @@ -1,9 +1,8 @@ -use crate::data_structures::native_isa_builder; use crate::Config; use std::cell::{RefCell, RefMut}; use std::hash::{Hash, Hasher}; use std::rc::Rc; -use wasmtime_jit::{Compiler, Features}; +use wasmtime_jit::{native, Compiler, Features}; #[derive(Clone)] pub struct Context { @@ -14,8 +13,7 @@ pub struct Context { impl Context { pub fn new(config: &Config) -> Context { - let isa_builder = native_isa_builder(); - let isa = isa_builder.finish(config.flags.clone()); + let isa = native::builder().finish(config.flags.clone()); Context::new_with_compiler(config, Compiler::new(isa, config.strategy)) } diff --git a/crates/api/src/data_structures.rs b/crates/api/src/data_structures.rs deleted file mode 100644 index df8e14c86a..0000000000 --- a/crates/api/src/data_structures.rs +++ /dev/null @@ -1,26 +0,0 @@ -pub(crate) mod ir { - pub(crate) use cranelift_codegen::ir::{types, AbiParam, ArgumentPurpose, Signature, Type}; -} - -pub(crate) mod settings { - pub(crate) use cranelift_codegen::settings::{builder, Flags}; -} - -pub(crate) use cranelift_codegen::isa::CallConv; -pub(crate) use cranelift_entity::{EntityRef, PrimaryMap}; - -pub(crate) mod wasm { - pub(crate) use cranelift_wasm::{ - DefinedFuncIndex, DefinedTableIndex, FuncIndex, Global, GlobalInit, Memory, Table, - TableElementType, - }; -} - -pub(crate) fn native_isa_builder() -> cranelift_codegen::isa::Builder { - cranelift_native::builder().expect("host machine is not a supported target") -} - -pub(crate) fn native_isa_call_conv() -> CallConv { - use target_lexicon::HOST; - CallConv::triple_default(&HOST) -} diff --git a/crates/api/src/externals.rs b/crates/api/src/externals.rs index 7853755a46..6e1a00bbce 100644 --- a/crates/api/src/externals.rs +++ b/crates/api/src/externals.rs @@ -1,5 +1,4 @@ use crate::callable::{Callable, NativeCallable, WasmtimeFn, WrappedCallable}; -use crate::data_structures::wasm; use crate::r#ref::{AnyRef, HostRef}; use crate::runtime::Store; use crate::trampoline::{generate_global_export, generate_memory_export, generate_table_export}; @@ -9,6 +8,7 @@ use crate::values::{from_checked_anyfunc, into_checked_anyfunc, Val}; use std::fmt; use std::rc::Rc; use std::slice; +use wasmtime_environ::wasm; use wasmtime_runtime::InstanceHandle; // Externals @@ -49,10 +49,10 @@ impl Extern { pub fn r#type(&self) -> ExternType { match self { - Extern::Func(ft) => ExternType::ExternFunc(ft.borrow().r#type().clone()), - Extern::Memory(ft) => ExternType::ExternMemory(ft.borrow().r#type().clone()), - Extern::Table(tt) => ExternType::ExternTable(tt.borrow().r#type().clone()), - Extern::Global(gt) => ExternType::ExternGlobal(gt.borrow().r#type().clone()), + Extern::Func(ft) => ExternType::Func(ft.borrow().r#type().clone()), + Extern::Memory(ft) => ExternType::Memory(ft.borrow().r#type().clone()), + Extern::Table(tt) => ExternType::Table(tt.borrow().r#type().clone()), + Extern::Global(gt) => ExternType::Global(gt.borrow().r#type().clone()), } } @@ -148,7 +148,7 @@ impl Func { } pub fn call(&self, params: &[Val]) -> Result, HostRef> { - let mut results = vec![Val::default(); self.result_arity()]; + let mut results = vec![Val::null(); self.result_arity()]; self.callable.call(params, &mut results)?; Ok(results.into_boxed_slice()) } @@ -215,8 +215,8 @@ impl Global { match self.r#type().content() { ValType::I32 => Val::from(*definition.as_i32()), ValType::I64 => Val::from(*definition.as_i64()), - ValType::F32 => Val::from_f32_bits(*definition.as_u32()), - ValType::F64 => Val::from_f64_bits(*definition.as_u64()), + ValType::F32 => Val::F32(*definition.as_u32()), + ValType::F64 => Val::F64(*definition.as_u64()), _ => unimplemented!("Global::get for {:?}", self.r#type().content()), } } diff --git a/crates/api/src/instance.rs b/crates/api/src/instance.rs index e5935a8408..49eecce2ed 100644 --- a/crates/api/src/instance.rs +++ b/crates/api/src/instance.rs @@ -4,7 +4,7 @@ use crate::module::Module; use crate::r#ref::HostRef; use crate::runtime::Store; use crate::trampoline::take_api_trap; -use crate::types::{ExportType, ExternType, Name}; +use crate::types::{ExportType, ExternType}; use anyhow::Result; use std::cell::RefCell; use std::collections::{HashMap, HashSet}; @@ -122,7 +122,7 @@ impl Instance { .exports() .iter() .enumerate() - .find(|(_, e)| e.name().as_str() == name)?; + .find(|(_, e)| e.name() == name)?; Some(&self.exports()[i]) } @@ -141,7 +141,7 @@ impl Instance { let _ = store.borrow_mut().register_wasmtime_signature(signature); } let extern_type = ExternType::from_wasmtime_export(&export); - exports_types.push(ExportType::new(Name::new(name), extern_type)); + exports_types.push(ExportType::new(name, extern_type)); exports.push(Extern::from_wasmtime_export( store, instance_handle.clone(), diff --git a/crates/api/src/lib.rs b/crates/api/src/lib.rs index 7f2a20354c..cde6b0a336 100644 --- a/crates/api/src/lib.rs +++ b/crates/api/src/lib.rs @@ -1,10 +1,15 @@ -//! Wasmtime embed API. Based on wasm-c-api. +//! Wasmtime's embedding API +//! +//! This crate contains a high-level API used to interact with WebAssembly +//! modules. The API here is intended to mirror the proposed [WebAssembly C +//! API](https://github.com/WebAssembly/wasm-c-api), with small extensions here +//! and there to implement Rust idioms. This crate also defines the actual C API +//! itself for consumption from other languages. #![allow(improper_ctypes)] mod callable; mod context; -mod data_structures; mod externals; mod instance; mod module; diff --git a/crates/api/src/module.rs b/crates/api/src/module.rs index e3d2f8bfbe..f92aee937b 100644 --- a/crates/api/src/module.rs +++ b/crates/api/src/module.rs @@ -12,10 +12,7 @@ use wasmparser::{ fn into_memory_type(mt: wasmparser::MemoryType) -> MemoryType { assert!(!mt.shared); - MemoryType::new(Limits::new( - mt.limits.initial, - mt.limits.maximum.unwrap_or(std::u32::MAX), - )) + MemoryType::new(Limits::new(mt.limits.initial, mt.limits.maximum)) } fn into_global_type(gt: &wasmparser::GlobalType) -> GlobalType { @@ -53,10 +50,7 @@ fn into_table_type(tt: wasmparser::TableType) -> TableType { tt.element_type == wasmparser::Type::AnyFunc || tt.element_type == wasmparser::Type::AnyRef ); let ty = into_valtype(&tt.element_type); - let limits = Limits::new( - tt.limits.initial, - tt.limits.maximum.unwrap_or(std::u32::MAX), - ); + let limits = Limits::new(tt.limits.initial, tt.limits.maximum); TableType::new(ty, limits) } @@ -112,31 +106,29 @@ fn read_imports_and_exports(binary: &[u8]) -> Result<(Box<[ImportType]>, Box<[Ex imports.reserve_exact(section.get_count() as usize); for entry in section { let entry = entry?; - let module = String::from(entry.module).into(); - let name = String::from(entry.field).into(); let r#type = match entry.ty { ImportSectionEntryType::Function(index) => { func_sig.push(index); let sig = &sigs[index as usize]; - ExternType::ExternFunc(sig.clone()) + ExternType::Func(sig.clone()) } ImportSectionEntryType::Table(tt) => { let table = into_table_type(tt); tables.push(table.clone()); - ExternType::ExternTable(table) + ExternType::Table(table) } ImportSectionEntryType::Memory(mt) => { let memory = into_memory_type(mt); memories.push(memory.clone()); - ExternType::ExternMemory(memory) + ExternType::Memory(memory) } ImportSectionEntryType::Global(gt) => { let global = into_global_type(>); globals.push(global.clone()); - ExternType::ExternGlobal(global) + ExternType::Global(global) } }; - imports.push(ImportType::new(module, name, r#type)); + imports.push(ImportType::new(entry.module, entry.field, r#type)); } } SectionCode::Export => { @@ -144,24 +136,23 @@ fn read_imports_and_exports(binary: &[u8]) -> Result<(Box<[ImportType]>, Box<[Ex exports.reserve_exact(section.get_count() as usize); for entry in section { let entry = entry?; - let name = String::from(entry.field).into(); let r#type = match entry.kind { ExternalKind::Function => { let sig_index = func_sig[entry.index as usize] as usize; let sig = &sigs[sig_index]; - ExternType::ExternFunc(sig.clone()) + ExternType::Func(sig.clone()) } ExternalKind::Table => { - ExternType::ExternTable(tables[entry.index as usize].clone()) + ExternType::Table(tables[entry.index as usize].clone()) } ExternalKind::Memory => { - ExternType::ExternMemory(memories[entry.index as usize].clone()) + ExternType::Memory(memories[entry.index as usize].clone()) } ExternalKind::Global => { - ExternType::ExternGlobal(globals[entry.index as usize].clone()) + ExternType::Global(globals[entry.index as usize].clone()) } }; - exports.push(ExportType::new(name, r#type)); + exports.push(ExportType::new(entry.field, r#type)); } } _ => { diff --git a/crates/api/src/runtime.rs b/crates/api/src/runtime.rs index 8fc1ac9b61..26c827036e 100644 --- a/crates/api/src/runtime.rs +++ b/crates/api/src/runtime.rs @@ -1,9 +1,9 @@ use crate::context::Context; -use crate::data_structures::{ir, settings}; use crate::r#ref::HostRef; use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; +use wasmtime_environ::{ir, settings}; use wasmtime_jit::{CompilationStrategy, Features}; // Runtime Environment diff --git a/crates/api/src/trampoline/create_handle.rs b/crates/api/src/trampoline/create_handle.rs index bca41712df..ec8ba85973 100644 --- a/crates/api/src/trampoline/create_handle.rs +++ b/crates/api/src/trampoline/create_handle.rs @@ -1,13 +1,13 @@ //! Support for a calling of an imported function. -use crate::data_structures::wasm::DefinedFuncIndex; -use crate::data_structures::PrimaryMap; use crate::runtime::Store; use anyhow::Result; use std::any::Any; use std::cell::{RefCell, RefMut}; use std::collections::{HashMap, HashSet}; use std::rc::Rc; +use wasmtime_environ::entity::PrimaryMap; +use wasmtime_environ::wasm::DefinedFuncIndex; use wasmtime_environ::Module; use wasmtime_runtime::{Imports, InstanceHandle, VMFunctionBody}; diff --git a/crates/api/src/trampoline/func.rs b/crates/api/src/trampoline/func.rs index 14ffca3e72..d283370b44 100644 --- a/crates/api/src/trampoline/func.rs +++ b/crates/api/src/trampoline/func.rs @@ -1,21 +1,25 @@ //! Support for a calling of an imported function. use super::create_handle::create_handle; -use super::ir::{ExternalName, Function, InstBuilder, MemFlags, StackSlotData, StackSlotKind}; use super::trap::{record_api_trap, TrapSink, API_TRAP_CODE}; -use super::{binemit, pretty_error, TargetIsa}; -use super::{Context, FunctionBuilder, FunctionBuilderContext}; -use crate::data_structures::ir::{self, types}; -use crate::data_structures::wasm::{DefinedFuncIndex, FuncIndex}; -use crate::data_structures::{native_isa_builder, settings, EntityRef, PrimaryMap}; use crate::r#ref::HostRef; use crate::{Callable, FuncType, Store, Val}; use anyhow::Result; use std::cmp; use std::convert::TryFrom; use std::rc::Rc; -use wasmtime_environ::{CompiledFunction, Export, Module, TrapInformation}; -use wasmtime_jit::CodeMemory; +use wasmtime_environ::entity::{EntityRef, PrimaryMap}; +use wasmtime_environ::ir::types; +use wasmtime_environ::isa::TargetIsa; +use wasmtime_environ::wasm::{DefinedFuncIndex, FuncIndex}; +use wasmtime_environ::{ir, settings, CompiledFunction, Export, Module, TrapInformation}; +use wasmtime_jit::trampoline::ir::{ + ExternalName, Function, InstBuilder, MemFlags, StackSlotData, StackSlotKind, +}; +use wasmtime_jit::trampoline::{ + binemit, pretty_error, Context, FunctionBuilder, FunctionBuilderContext, +}; +use wasmtime_jit::{native, CodeMemory}; use wasmtime_runtime::{ get_mut_trap_registry, InstanceHandle, TrapRegistrationGuard, VMContext, VMFunctionBody, }; @@ -76,7 +80,7 @@ unsafe extern "C" fn stub_fn(vmctx: *mut VMContext, call_id: u32, values_vec: *m (args, signature.returns.len()) }; - let mut returns = vec![Val::default(); returns_len]; + let mut returns = vec![Val::null(); returns_len]; let func = &instance .host_state() .downcast_mut::() @@ -232,7 +236,7 @@ pub fn create_handle_with_function( let sig = ft.get_wasmtime_signature().clone(); let isa = { - let isa_builder = native_isa_builder(); + let isa_builder = native::builder(); let flag_builder = settings::builder(); isa_builder.finish(settings::Flags::new(flag_builder)) }; diff --git a/crates/api/src/trampoline/global.rs b/crates/api/src/trampoline/global.rs index 53bb8eac72..f1b610559d 100644 --- a/crates/api/src/trampoline/global.rs +++ b/crates/api/src/trampoline/global.rs @@ -1,8 +1,8 @@ use super::create_handle::create_handle; -use crate::data_structures::{wasm, PrimaryMap}; use crate::{GlobalType, Mutability, Val}; use anyhow::Result; -use wasmtime_environ::Module; +use wasmtime_environ::entity::PrimaryMap; +use wasmtime_environ::{wasm, Module}; use wasmtime_runtime::{InstanceHandle, VMGlobalDefinition}; #[allow(dead_code)] diff --git a/crates/api/src/trampoline/memory.rs b/crates/api/src/trampoline/memory.rs index 511a2bd117..9437876219 100644 --- a/crates/api/src/trampoline/memory.rs +++ b/crates/api/src/trampoline/memory.rs @@ -1,8 +1,8 @@ use super::create_handle::create_handle; -use crate::data_structures::{wasm, PrimaryMap}; use crate::MemoryType; use anyhow::Result; -use wasmtime_environ::Module; +use wasmtime_environ::entity::PrimaryMap; +use wasmtime_environ::{wasm, Module}; use wasmtime_runtime::InstanceHandle; #[allow(dead_code)] @@ -12,11 +12,7 @@ pub fn create_handle_with_memory(memory: &MemoryType) -> Result let memory = wasm::Memory { minimum: memory.limits().min(), - maximum: if memory.limits().max() == std::u32::MAX { - None - } else { - Some(memory.limits().max()) - }, + maximum: memory.limits().max(), shared: false, // TODO }; let tunable = Default::default(); diff --git a/crates/api/src/trampoline/mod.rs b/crates/api/src/trampoline/mod.rs index 95bfcf9767..50a76293ca 100644 --- a/crates/api/src/trampoline/mod.rs +++ b/crates/api/src/trampoline/mod.rs @@ -51,61 +51,3 @@ pub fn generate_table_export( let export = instance.lookup("table").expect("table export"); Ok((instance, export)) } - -pub(crate) use cranelift_codegen::print_errors::pretty_error; - -pub(crate) mod binemit { - pub(crate) use cranelift_codegen::binemit::{CodeOffset, NullStackmapSink, TrapSink}; - - pub use cranelift_codegen::{binemit, ir}; - - /// We don't expect trampoline compilation to produce any relocations, so - /// this `RelocSink` just asserts that it doesn't recieve any. - pub(crate) struct TrampolineRelocSink {} - - impl binemit::RelocSink for TrampolineRelocSink { - fn reloc_ebb( - &mut self, - _offset: binemit::CodeOffset, - _reloc: binemit::Reloc, - _ebb_offset: binemit::CodeOffset, - ) { - panic!("trampoline compilation should not produce ebb relocs"); - } - fn reloc_external( - &mut self, - _offset: binemit::CodeOffset, - _reloc: binemit::Reloc, - _name: &ir::ExternalName, - _addend: binemit::Addend, - ) { - panic!("trampoline compilation should not produce external symbol relocs"); - } - fn reloc_constant( - &mut self, - _code_offset: binemit::CodeOffset, - _reloc: binemit::Reloc, - _constant_offset: ir::ConstantOffset, - ) { - panic!("trampoline compilation should not produce constant relocs"); - } - fn reloc_jt( - &mut self, - _offset: binemit::CodeOffset, - _reloc: binemit::Reloc, - _jt: ir::JumpTable, - ) { - panic!("trampoline compilation should not produce jump table relocs"); - } - } -} - -pub(crate) mod ir { - pub(crate) use cranelift_codegen::ir::{ - ExternalName, Function, InstBuilder, MemFlags, SourceLoc, StackSlotData, StackSlotKind, - TrapCode, - }; -} -pub(crate) use cranelift_codegen::isa::TargetIsa; -pub(crate) use cranelift_codegen::Context; -pub(crate) use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; diff --git a/crates/api/src/trampoline/table.rs b/crates/api/src/trampoline/table.rs index 64138f2600..5d2441bf92 100644 --- a/crates/api/src/trampoline/table.rs +++ b/crates/api/src/trampoline/table.rs @@ -1,8 +1,8 @@ use super::create_handle::create_handle; -use crate::data_structures::{wasm, PrimaryMap}; use crate::{TableType, ValType}; use anyhow::Result; -use wasmtime_environ::Module; +use wasmtime_environ::entity::PrimaryMap; +use wasmtime_environ::{wasm, Module}; use wasmtime_runtime::InstanceHandle; pub fn create_handle_with_table(table: &TableType) -> Result { @@ -10,11 +10,7 @@ pub fn create_handle_with_table(table: &TableType) -> Result { let table = wasm::Table { minimum: table.limits().min(), - maximum: if table.limits().max() == std::u32::MAX { - None - } else { - Some(table.limits().max()) - }, + maximum: table.limits().max(), ty: match table.element() { ValType::FuncRef => wasm::TableElementType::Func, _ => wasm::TableElementType::Val(table.element().get_wasmtime_type()), diff --git a/crates/api/src/trampoline/trap.rs b/crates/api/src/trampoline/trap.rs index 5b231f86cf..bb8df5f567 100644 --- a/crates/api/src/trampoline/trap.rs +++ b/crates/api/src/trampoline/trap.rs @@ -1,10 +1,10 @@ use std::cell::Cell; -use super::binemit; -use super::ir::{SourceLoc, TrapCode}; use crate::r#ref::HostRef; use crate::Trap; +use wasmtime_environ::ir::{SourceLoc, TrapCode}; use wasmtime_environ::TrapInformation; +use wasmtime_jit::trampoline::binemit; // Randomly selected user TrapCode magic number 13. pub const API_TRAP_CODE: TrapCode = TrapCode::User(13); diff --git a/crates/api/src/types.rs b/crates/api/src/types.rs index 3740e2756a..2084985f87 100644 --- a/crates/api/src/types.rs +++ b/crates/api/src/types.rs @@ -1,38 +1,46 @@ -use crate::data_structures::{ir, wasm}; +use wasmtime_environ::{ir, wasm}; // Type Representations // Type attributes +/// Indicator of whether a global is mutable or not #[derive(Debug, Clone, Copy)] pub enum Mutability { + /// The global is constant and its value does not change Const, + /// The value of the global can change over time Var, } +/// Limits of tables/memories where the units of the limits are defined by the +/// table/memory types. +/// +/// A minimum is always available but the maximum may not be present. #[derive(Debug, Clone)] pub struct Limits { min: u32, - max: u32, + max: Option, } impl Limits { - pub fn new(min: u32, max: u32) -> Limits { + /// Creates a new set of limits with the minimum and maximum both specified. + pub fn new(min: u32, max: Option) -> Limits { Limits { min, max } } + /// Creates a new `Limits` with the `min` specified and no maximum specified. pub fn at_least(min: u32) -> Limits { - Limits { - min, - max: ::std::u32::MAX, - } + Limits::new(min, None) } + /// Returns the minimum amount for these limits. pub fn min(&self) -> u32 { self.min } - pub fn max(&self) -> u32 { + /// Returs the maximum amount for these limits, if specified. + pub fn max(&self) -> Option { self.max } } @@ -90,52 +98,63 @@ impl ValType { // External Types +/// A list of all possible types which can be externally referenced from a +/// WebAssembly module. +/// +/// This list can be found in [`ImportType`] or [`ExportType`], so these types +/// can either be imported or exported. #[derive(Debug, Clone)] pub enum ExternType { - ExternFunc(FuncType), - ExternGlobal(GlobalType), - ExternTable(TableType), - ExternMemory(MemoryType), + Func(FuncType), + Global(GlobalType), + Table(TableType), + Memory(MemoryType), +} + +macro_rules! accessors { + ($(($variant:ident($ty:ty) $get:ident $unwrap:ident))*) => ($( + /// Attempt to return the underlying type of this external type, + /// returning `None` if it is a different type. + pub fn $get(&self) -> Option<&$ty> { + if let ExternType::$variant(e) = self { + Some(e) + } else { + None + } + } + + /// Returns the underlying descriptor of this [`ExternType`], panicking + /// if it is a different type. + /// + /// # Panics + /// + /// Panics if `self` is not of the right type. + pub fn $unwrap(&self) -> &$ty { + self.$get().expect(concat!("expected ", stringify!($ty))) + } + )*) } impl ExternType { - pub fn func(&self) -> &FuncType { - match self { - ExternType::ExternFunc(func) => func, - _ => panic!("ExternType::ExternFunc expected"), - } - } - pub fn global(&self) -> &GlobalType { - match self { - ExternType::ExternGlobal(func) => func, - _ => panic!("ExternType::ExternGlobal expected"), - } - } - pub fn table(&self) -> &TableType { - match self { - ExternType::ExternTable(table) => table, - _ => panic!("ExternType::ExternTable expected"), - } - } - pub fn memory(&self) -> &MemoryType { - match self { - ExternType::ExternMemory(memory) => memory, - _ => panic!("ExternType::ExternMemory expected"), - } + accessors! { + (Func(FuncType) func unwrap_func) + (Global(GlobalType) global unwrap_global) + (Table(TableType) table unwrap_table) + (Memory(MemoryType) memory unwrap_memory) } pub(crate) fn from_wasmtime_export(export: &wasmtime_runtime::Export) -> Self { match export { wasmtime_runtime::Export::Function { signature, .. } => { - ExternType::ExternFunc(FuncType::from_wasmtime_signature(signature.clone())) + ExternType::Func(FuncType::from_wasmtime_signature(signature.clone())) } wasmtime_runtime::Export::Memory { memory, .. } => { - ExternType::ExternMemory(MemoryType::from_wasmtime_memory(&memory.memory)) + ExternType::Memory(MemoryType::from_wasmtime_memory(&memory.memory)) } wasmtime_runtime::Export::Global { global, .. } => { - ExternType::ExternGlobal(GlobalType::from_wasmtime_global(&global)) + ExternType::Global(GlobalType::from_wasmtime_global(&global)) } wasmtime_runtime::Export::Table { table, .. } => { - ExternType::ExternTable(TableType::from_wasmtime_table(&table.table)) + ExternType::Table(TableType::from_wasmtime_table(&table.table)) } } } @@ -147,6 +166,9 @@ fn from_wasmtime_abiparam(param: &ir::AbiParam) -> ValType { ValType::from_wasmtime_type(param.value_type) } +/// A descriptor for a function in a WebAssembly module. +/// +/// WebAssembly functions can have 0 or more parameters and results. #[derive(Debug, Clone)] pub struct FuncType { params: Box<[ValType]>, @@ -155,10 +177,14 @@ pub struct FuncType { } impl FuncType { + /// Creates a new function descriptor from the given parameters and results. + /// + /// The function descriptor returned will represent a function which takes + /// `params` as arguments and returns `results` when it is finished. pub fn new(params: Box<[ValType]>, results: Box<[ValType]>) -> FuncType { - use crate::data_structures::ir::{types, AbiParam, ArgumentPurpose, Signature}; - use crate::data_structures::native_isa_call_conv; - let call_conv = native_isa_call_conv(); + use wasmtime_environ::ir::{types, AbiParam, ArgumentPurpose, Signature}; + use wasmtime_jit::native; + let call_conv = native::call_conv(); let signature: Signature = { let mut params = params .iter() @@ -182,9 +208,13 @@ impl FuncType { signature, } } + + /// Returns the list of parameter types for this function. pub fn params(&self) -> &[ValType] { &self.params } + + /// Returns the list of result types for this function. pub fn results(&self) -> &[ValType] { &self.results } @@ -215,6 +245,11 @@ impl FuncType { // Global Types +/// A WebAssembly global descriptor. +/// +/// This type describes an instance of a global in a WebAssembly module. Globals +/// are local to an [`Instance`](crate::Instance) and are either immutable or +/// mutable. #[derive(Debug, Clone)] pub struct GlobalType { content: ValType, @@ -222,15 +257,21 @@ pub struct GlobalType { } impl GlobalType { + /// Creates a new global descriptor of the specified `content` type and + /// whether or not it's mutable. pub fn new(content: ValType, mutability: Mutability) -> GlobalType { GlobalType { content, mutability, } } + + /// Returns the value type of this global descriptor. pub fn content(&self) -> &ValType { &self.content } + + /// Returns whether or not this global is mutable. pub fn mutability(&self) -> Mutability { self.mutability } @@ -248,6 +289,11 @@ impl GlobalType { // Table Types +/// A descriptor for a table in a WebAssembly module. +/// +/// Tables are contiguous chunks of a specific element, typically a `funcref` or +/// an `anyref`. The most common use for tables is a function table through +/// which `call_indirect` can invoke other functions. #[derive(Debug, Clone)] pub struct TableType { element: ValType, @@ -255,12 +301,18 @@ pub struct TableType { } impl TableType { + /// Creates a new table descriptor which will contain the specified + /// `element` and have the `limits` applied to its length. pub fn new(element: ValType, limits: Limits) -> TableType { TableType { element, limits } } + + /// Returns the element value type of this table. pub fn element(&self) -> &ValType { &self.element } + + /// Returns the limits, in units of elements, of this table. pub fn limits(&self) -> &Limits { &self.limits } @@ -272,103 +324,113 @@ impl TableType { false }); let ty = ValType::FuncRef; - let limits = Limits::new(table.minimum, table.maximum.unwrap_or(::std::u32::MAX)); + let limits = Limits::new(table.minimum, table.maximum); TableType::new(ty, limits) } } // Memory Types +/// A descriptor for a WebAssembly memory type. +/// +/// Memories are described in units of pages (64KB) and represent contiguous +/// chunks of addressable memory. #[derive(Debug, Clone)] pub struct MemoryType { limits: Limits, } impl MemoryType { + /// Creates a new descriptor for a WebAssembly memory given the specified + /// limits of the memory. pub fn new(limits: Limits) -> MemoryType { MemoryType { limits } } + + /// Returns the limits (in pages) that are configured for this memory. pub fn limits(&self) -> &Limits { &self.limits } pub(crate) fn from_wasmtime_memory(memory: &wasm::Memory) -> MemoryType { - MemoryType::new(Limits::new( - memory.minimum, - memory.maximum.unwrap_or(::std::u32::MAX), - )) + MemoryType::new(Limits::new(memory.minimum, memory.maximum)) } } // Import Types -#[derive(Debug, Clone)] -pub struct Name(String); - -impl Name { - pub fn new(value: &str) -> Self { - Name(value.to_owned()) - } - - pub fn as_str(&self) -> &str { - self.0.as_str() - } -} - -impl From for Name { - fn from(s: String) -> Name { - Name(s) - } -} - -impl ToString for Name { - fn to_string(&self) -> String { - self.0.to_owned() - } -} - +/// A descriptor for an imported value into a wasm module. +/// +/// This type is primarily accessed from the +/// [`Module::imports`](crate::Module::imports) API. Each [`ImportType`] +/// describes an import into the wasm module with the module/name that it's +/// imported from as well as the type of item that's being imported. #[derive(Debug, Clone)] pub struct ImportType { - module: Name, - name: Name, - r#type: ExternType, + module: String, + name: String, + ty: ExternType, } impl ImportType { - pub fn new(module: Name, name: Name, r#type: ExternType) -> ImportType { + /// Creates a new import descriptor which comes from `module` and `name` and + /// is of type `ty`. + pub fn new(module: &str, name: &str, ty: ExternType) -> ImportType { ImportType { - module, - name, - r#type, + module: module.to_string(), + name: name.to_string(), + ty, } } - pub fn module(&self) -> &Name { + + /// Returns the module name that this import is expected to come from. + pub fn module(&self) -> &str { &self.module } - pub fn name(&self) -> &Name { + + /// Returns the field name of the module that this import is expected to + /// come from. + pub fn name(&self) -> &str { &self.name } - pub fn r#type(&self) -> &ExternType { - &self.r#type + + /// Returns the expected type of this import. + pub fn ty(&self) -> &ExternType { + &self.ty } } // Export Types +/// A descriptor for an exported WebAssembly value. +/// +/// This type is primarily accessed from the +/// [`Module::exports`](crate::Module::exports) accessor and describes what +/// names are exported from a wasm module and the type of the item that is +/// exported. #[derive(Debug, Clone)] pub struct ExportType { - name: Name, - r#type: ExternType, + name: String, + ty: ExternType, } impl ExportType { - pub fn new(name: Name, r#type: ExternType) -> ExportType { - ExportType { name, r#type } + /// Creates a new export which is exported with the given `name` and has the + /// given `ty`. + pub fn new(name: &str, ty: ExternType) -> ExportType { + ExportType { + name: name.to_string(), + ty, + } } - pub fn name(&self) -> &Name { + + /// Returns the name by which this export is known by. + pub fn name(&self) -> &str { &self.name } - pub fn r#type(&self) -> &ExternType { - &self.r#type + + /// Returns the type of this export. + pub fn ty(&self) -> &ExternType { + &self.ty } } diff --git a/crates/api/src/values.rs b/crates/api/src/values.rs index e215e8b2f2..0d87ea6ddb 100644 --- a/crates/api/src/values.rs +++ b/crates/api/src/values.rs @@ -1,26 +1,76 @@ -use crate::data_structures::ir; use crate::externals::Func; use crate::r#ref::{AnyRef, HostRef}; use crate::runtime::Store; use crate::types::ValType; use std::ptr; +use wasmtime_environ::ir; use wasmtime_jit::RuntimeValue; +/// Possible runtime values that a WebAssembly module can either consume or +/// produce. #[derive(Debug, Clone)] pub enum Val { + /// A 32-bit integer I32(i32), + + /// A 64-bit integer I64(i64), + + /// A 32-bit float. + /// + /// Note that the raw bits of the float are stored here, and you can use + /// `f32::from_bits` to create an `f32` value. F32(u32), + + /// A 64-bit float. + /// + /// Note that the raw bits of the float are stored here, and you can use + /// `f64::from_bits` to create an `f64` value. F64(u64), + + /// An `anyref` value which can hold opaque data to the wasm instance itself. + /// + /// Note that this is a nullable value as well. AnyRef(AnyRef), + + /// A first-class reference to a WebAssembly function. FuncRef(HostRef), + + /// A 128-bit number + V128([u8; 16]), +} + +macro_rules! accessors { + ($bind:ident $(($variant:ident($ty:ty) $get:ident $unwrap:ident $cvt:expr))*) => ($( + /// Attempt to access the underlying value of this `Val`, returning + /// `None` if it is not the correct type. + pub fn $get(&self) -> Option<$ty> { + if let Val::$variant($bind) = self { + Some($cvt) + } else { + None + } + } + + /// Returns the underlying value of this `Val`, panicking if it's the + /// wrong type. + /// + /// # Panics + /// + /// Panics if `self` is not of the right type. + pub fn $unwrap(&self) -> $ty { + self.$get().expect(concat!("expected ", stringify!($ty))) + } + )*) } impl Val { - pub fn default() -> Val { + /// Returns a null `anyref` value. + pub fn null() -> Val { Val::AnyRef(AnyRef::null()) } + /// Returns the corresponding [`ValType`] for this `Val`. pub fn r#type(&self) -> ValType { match self { Val::I32(_) => ValType::I32, @@ -29,6 +79,7 @@ impl Val { Val::F64(_) => ValType::F64, Val::AnyRef(_) => ValType::AnyRef, Val::FuncRef(_) => ValType::FuncRef, + Val::V128(_) => ValType::V128, } } @@ -52,52 +103,36 @@ impl Val { } } - pub fn from_f32_bits(v: u32) -> Val { - Val::F32(v) + accessors! { + e + (I32(i32) i32 unwrap_i32 *e) + (I64(i64) i64 unwrap_i64 *e) + (F32(f32) f32 unwrap_f32 f32::from_bits(*e)) + (F64(f64) f64 unwrap_f64 f64::from_bits(*e)) + (FuncRef(&HostRef) funcref unwrap_funcref e) + (V128(&[u8; 16]) v128 unwrap_v128 e) } - pub fn from_f64_bits(v: u64) -> Val { - Val::F64(v) - } - - pub fn i32(&self) -> i32 { - if let Val::I32(i) = self { - *i - } else { - panic!("Invalid conversion of {:?} to i32.", self); + /// Attempt to access the underlying value of this `Val`, returning + /// `None` if it is not the correct type. + /// + /// This will return `Some` for both the `AnyRef` and `FuncRef` types. + pub fn anyref(&self) -> Option { + match self { + Val::AnyRef(e) => Some(e.clone()), + Val::FuncRef(e) => Some(e.anyref()), + _ => None, } } - pub fn i64(&self) -> i64 { - if let Val::I64(i) = self { - *i - } else { - panic!("Invalid conversion of {:?} to i64.", self); - } - } - - pub fn f32(&self) -> f32 { - RuntimeValue::F32(self.f32_bits()).unwrap_f32() - } - - pub fn f64(&self) -> f64 { - RuntimeValue::F64(self.f64_bits()).unwrap_f64() - } - - pub fn f32_bits(&self) -> u32 { - if let Val::F32(i) = self { - *i - } else { - panic!("Invalid conversion of {:?} to f32.", self); - } - } - - pub fn f64_bits(&self) -> u64 { - if let Val::F64(i) = self { - *i - } else { - panic!("Invalid conversion of {:?} to f64.", self); - } + /// Returns the underlying value of this `Val`, panicking if it's the + /// wrong type. + /// + /// # Panics + /// + /// Panics if `self` is not of the right type. + pub fn unwrap_anyref(&self) -> AnyRef { + self.anyref().expect("expected anyref") } } @@ -125,30 +160,6 @@ impl From for Val { } } -impl Into for Val { - fn into(self) -> i32 { - self.i32() - } -} - -impl Into for Val { - fn into(self) -> i64 { - self.i64() - } -} - -impl Into for Val { - fn into(self) -> f32 { - self.f32() - } -} - -impl Into for Val { - fn into(self) -> f64 { - self.f64() - } -} - impl From for Val { fn from(val: AnyRef) -> Val { match &val { @@ -170,16 +181,6 @@ impl From> for Val { } } -impl Into for Val { - fn into(self) -> AnyRef { - match self { - Val::AnyRef(r) => r, - Val::FuncRef(f) => f.anyref(), - _ => panic!("Invalid conversion of {:?} to anyref.", self), - } - } -} - impl From for Val { fn from(rv: RuntimeValue) -> Self { match rv { @@ -187,23 +188,7 @@ impl From for Val { RuntimeValue::I64(i) => Val::I64(i), RuntimeValue::F32(u) => Val::F32(u), RuntimeValue::F64(u) => Val::F64(u), - x => { - panic!("unsupported {:?}", x); - } - } - } -} - -impl Into for Val { - fn into(self) -> RuntimeValue { - match self { - Val::I32(i) => RuntimeValue::I32(i), - Val::I64(i) => RuntimeValue::I64(i), - Val::F32(u) => RuntimeValue::F32(u), - Val::F64(u) => RuntimeValue::F64(u), - x => { - panic!("unsupported {:?}", x); - } + RuntimeValue::V128(u) => Val::V128(u), } } } diff --git a/crates/api/src/wasm.rs b/crates/api/src/wasm.rs index 2b4f68753a..98b65433a3 100644 --- a/crates/api/src/wasm.rs +++ b/crates/api/src/wasm.rs @@ -7,8 +7,8 @@ use super::{ AnyRef, Callable, Engine, ExportType, Extern, ExternType, Func, FuncType, Global, GlobalType, - HostInfo, HostRef, ImportType, Instance, Limits, Memory, MemoryType, Module, Name, Store, - Table, TableType, Trap, Val, ValType, + HostInfo, HostRef, ImportType, Instance, Limits, Memory, MemoryType, Module, Store, Table, + TableType, Trap, Val, ValType, }; use std::rc::Rc; use std::{mem, ptr, slice}; @@ -56,7 +56,7 @@ macro_rules! declare_vec { #[allow(dead_code)] fn as_slice(&self) -> &[$elem_ty] { - unsafe { slice::from_raw_parts(self.data as *const $elem_ty, self.size) } + unsafe { slice::from_raw_parts(self.data, self.size) } } } @@ -122,8 +122,8 @@ macro_rules! declare_vec { } #[allow(dead_code)] - fn as_slice(&self) -> &[$elem_ty] { - unsafe { slice::from_raw_parts(self.data as *const $elem_ty, self.size) } + fn as_slice(&self) -> &[*mut $elem_ty] { + unsafe { slice::from_raw_parts(self.data, self.size) } } } @@ -714,11 +714,8 @@ pub unsafe extern "C" fn wasm_module_delete(module: *mut wasm_module_t) { } impl wasm_name_t { - fn from_name(name: &Name) -> wasm_name_t { - let s = name.to_string(); - let mut buffer = Vec::new(); - buffer.extend_from_slice(s.as_bytes()); - buffer.into() + fn from_name(name: &str) -> wasm_name_t { + name.to_string().into_bytes().into() } } @@ -977,7 +974,7 @@ pub unsafe extern "C" fn wasm_importtype_type( it: *const wasm_importtype_t, ) -> *const wasm_externtype_t { let ty = Box::new(wasm_externtype_t { - ty: (*it).ty.r#type().clone(), + ty: (*it).ty.ty().clone(), cache: wasm_externtype_t_type_cache::Empty, }); Box::into_raw(ty) @@ -1004,7 +1001,7 @@ pub unsafe extern "C" fn wasm_exporttype_type( if (*et).type_cache.is_none() { let et = (et as *mut wasm_exporttype_t).as_mut().unwrap(); et.type_cache = Some(wasm_externtype_t { - ty: (*et).ty.r#type().clone(), + ty: (*et).ty.ty().clone(), cache: wasm_externtype_t_type_cache::Empty, }); } @@ -1018,10 +1015,10 @@ pub unsafe extern "C" fn wasm_exporttype_vec_delete(et: *mut wasm_exporttype_vec fn from_externtype(ty: &ExternType) -> wasm_externkind_t { match ty { - ExternType::ExternFunc(_) => 0, - ExternType::ExternGlobal(_) => 1, - ExternType::ExternTable(_) => 2, - ExternType::ExternMemory(_) => 3, + ExternType::Func(_) => 0, + ExternType::Global(_) => 1, + ExternType::Table(_) => 2, + ExternType::Memory(_) => 3, } } @@ -1044,7 +1041,7 @@ pub unsafe extern "C" fn wasm_externtype_as_functype_const( et: *const wasm_externtype_t, ) -> *const wasm_functype_t { if let wasm_externtype_t_type_cache::Empty = (*et).cache { - let functype = (*et).ty.func().clone(); + let functype = (*et).ty.unwrap_func().clone(); let f = wasm_functype_t { functype, params_cache: None, @@ -1064,7 +1061,7 @@ pub unsafe extern "C" fn wasm_externtype_as_globaltype_const( et: *const wasm_externtype_t, ) -> *const wasm_globaltype_t { if let wasm_externtype_t_type_cache::Empty = (*et).cache { - let globaltype = (*et).ty.global().clone(); + let globaltype = (*et).ty.unwrap_global().clone(); let g = wasm_globaltype_t { globaltype, content_cache: None, @@ -1083,7 +1080,7 @@ pub unsafe extern "C" fn wasm_externtype_as_tabletype_const( et: *const wasm_externtype_t, ) -> *const wasm_tabletype_t { if let wasm_externtype_t_type_cache::Empty = (*et).cache { - let tabletype = (*et).ty.table().clone(); + let tabletype = (*et).ty.unwrap_table().clone(); let t = wasm_tabletype_t { tabletype, element_cache: None, @@ -1103,7 +1100,7 @@ pub unsafe extern "C" fn wasm_externtype_as_memorytype_const( et: *const wasm_externtype_t, ) -> *const wasm_memorytype_t { if let wasm_externtype_t_type_cache::Empty = (*et).cache { - let memorytype = (*et).ty.memory().clone(); + let memorytype = (*et).ty.unwrap_memory().clone(); let m = wasm_memorytype_t { memorytype, limits_cache: None, @@ -1210,7 +1207,7 @@ pub unsafe extern "C" fn wasm_memorytype_limits( let limits = (*mt).memorytype.limits(); mt.limits_cache = Some(wasm_limits_t { min: limits.min(), - max: limits.max(), + max: limits.max().unwrap_or(u32::max_value()), }); } (*mt).limits_cache.as_ref().unwrap() @@ -1270,7 +1267,7 @@ pub unsafe extern "C" fn wasm_tabletype_limits( let limits = (*tt).tabletype.limits(); tt.limits_cache = Some(wasm_limits_t { min: limits.min(), - max: limits.max(), + max: limits.max().unwrap_or(u32::max_value()), }); } (*tt).limits_cache.as_ref().unwrap() @@ -1469,7 +1466,12 @@ pub unsafe extern "C" fn wasm_memorytype_delete(mt: *mut wasm_memorytype_t) { pub unsafe extern "C" fn wasm_memorytype_new( limits: *const wasm_limits_t, ) -> *mut wasm_memorytype_t { - let limits = Limits::new((*limits).min, (*limits).max); + let max = if (*limits).max == u32::max_value() { + None + } else { + Some((*limits).max) + }; + let limits = Limits::new((*limits).min, max); let mt = Box::new(wasm_memorytype_t { memorytype: MemoryType::new(limits), limits_cache: None, @@ -1553,7 +1555,11 @@ unsafe fn into_funcref(val: Val) -> *mut wasm_ref_t { if let Val::AnyRef(AnyRef::Null) = val { return ptr::null_mut(); } - let r = Box::new(wasm_ref_t { r: val.into() }); + let anyref = match val.anyref() { + Some(anyref) => anyref, + None => return ptr::null_mut(), + }; + let r = Box::new(wasm_ref_t { r: anyref }); Box::into_raw(r) } @@ -1615,7 +1621,12 @@ pub unsafe extern "C" fn wasm_tabletype_new( limits: *const wasm_limits_t, ) -> *mut wasm_tabletype_t { let ty = Box::from_raw(ty).ty; - let limits = Limits::new((*limits).min, (*limits).max); + let max = if (*limits).max == u32::max_value() { + None + } else { + Some((*limits).max) + }; + let limits = Limits::new((*limits).min, max); let tt = Box::new(wasm_tabletype_t { tabletype: TableType::new(ty, limits), element_cache: None, diff --git a/crates/debug/Cargo.toml b/crates/debug/Cargo.toml index 6ded069424..2f753cc23b 100644 --- a/crates/debug/Cargo.toml +++ b/crates/debug/Cargo.toml @@ -13,10 +13,7 @@ edition = "2018" [dependencies] gimli = "0.19.0" -wasmparser = "0.39.2" -cranelift-codegen = { version = "0.50.0", features = ["enable-serde"] } -cranelift-entity = { version = "0.50.0", features = ["enable-serde"] } -cranelift-wasm = { version = "0.50.0", features = ["enable-serde"] } +wasmparser = "0.44.0" faerie = "0.13.0" wasmtime-environ = { path = "../environ" } target-lexicon = { version = "0.9.0", default-features = false } diff --git a/crates/debug/src/lib.rs b/crates/debug/src/lib.rs index 9cbce9d60a..0b26105ee2 100644 --- a/crates/debug/src/lib.rs +++ b/crates/debug/src/lib.rs @@ -3,10 +3,10 @@ #![allow(clippy::cast_ptr_alignment)] use anyhow::Error; -use cranelift_codegen::isa::TargetFrontendConfig; use faerie::{Artifact, Decl}; use more_asserts::assert_gt; use target_lexicon::{BinaryFormat, Triple}; +use wasmtime_environ::isa::TargetFrontendConfig; use wasmtime_environ::{ModuleAddressMap, ModuleVmctxInfo, ValueLabelsRanges}; pub use crate::read_debuginfo::{read_debuginfo, DebugInfoData, WasmFileInfo}; diff --git a/crates/debug/src/transform/address_transform.rs b/crates/debug/src/transform/address_transform.rs index 43ecf0b8c8..fa83ab52ac 100644 --- a/crates/debug/src/transform/address_transform.rs +++ b/crates/debug/src/transform/address_transform.rs @@ -1,12 +1,12 @@ use crate::WasmFileInfo; -use cranelift_codegen::ir::SourceLoc; -use cranelift_entity::{EntityRef, PrimaryMap}; -use cranelift_wasm::DefinedFuncIndex; use gimli::write; use more_asserts::assert_le; use std::collections::BTreeMap; use std::collections::HashMap; use std::iter::FromIterator; +use wasmtime_environ::entity::{EntityRef, PrimaryMap}; +use wasmtime_environ::ir::SourceLoc; +use wasmtime_environ::wasm::DefinedFuncIndex; use wasmtime_environ::{FunctionAddressMap, ModuleAddressMap}; pub type GeneratedAddress = usize; @@ -492,10 +492,10 @@ impl AddressTransform { mod tests { use super::{build_function_lookup, get_wasm_code_offset, AddressTransform}; use crate::read_debuginfo::WasmFileInfo; - use cranelift_codegen::ir::SourceLoc; - use cranelift_entity::PrimaryMap; use gimli::write::Address; use std::iter::FromIterator; + use wasmtime_environ::entity::PrimaryMap; + use wasmtime_environ::ir::SourceLoc; use wasmtime_environ::{FunctionAddressMap, InstructionAddressMap, ModuleAddressMap}; #[test] diff --git a/crates/debug/src/transform/expression.rs b/crates/debug/src/transform/expression.rs index 6da8818e55..7b0f05dcf2 100644 --- a/crates/debug/src/transform/expression.rs +++ b/crates/debug/src/transform/expression.rs @@ -1,13 +1,12 @@ use super::address_transform::AddressTransform; use anyhow::Error; -use cranelift_codegen::ir::{StackSlots, ValueLabel, ValueLoc}; -use cranelift_codegen::isa::RegUnit; -use cranelift_codegen::ValueLabelsRanges; -use cranelift_entity::EntityRef; -use cranelift_wasm::{get_vmctx_value_label, DefinedFuncIndex}; use gimli::{self, write, Expression, Operation, Reader, ReaderOffset, Register, X86_64}; use more_asserts::{assert_le, assert_lt}; use std::collections::{HashMap, HashSet}; +use wasmtime_environ::entity::EntityRef; +use wasmtime_environ::ir::{StackSlots, ValueLabel, ValueLabelsRanges, ValueLoc}; +use wasmtime_environ::isa::RegUnit; +use wasmtime_environ::wasm::{get_vmctx_value_label, DefinedFuncIndex}; #[derive(Debug)] pub struct FunctionFrameInfo<'a> { diff --git a/crates/debug/src/transform/line_program.rs b/crates/debug/src/transform/line_program.rs index d9e1434136..f0e715519f 100644 --- a/crates/debug/src/transform/line_program.rs +++ b/crates/debug/src/transform/line_program.rs @@ -2,13 +2,13 @@ use super::address_transform::AddressTransform; use super::attr::clone_attr_string; use super::{Reader, TransformError}; use anyhow::Error; -use cranelift_entity::EntityRef; use gimli::{ write, DebugLine, DebugLineOffset, DebugStr, DebuggingInformationEntry, LineEncoding, Unit, }; use more_asserts::assert_le; use std::collections::BTreeMap; use std::iter::FromIterator; +use wasmtime_environ::entity::EntityRef; #[derive(Debug)] enum SavedLineProgramRow { diff --git a/crates/debug/src/transform/mod.rs b/crates/debug/src/transform/mod.rs index 65dc055b8b..9c91d6798c 100644 --- a/crates/debug/src/transform/mod.rs +++ b/crates/debug/src/transform/mod.rs @@ -1,7 +1,6 @@ use crate::gc::build_dependencies; use crate::DebugInfoData; use anyhow::Error; -use cranelift_codegen::isa::TargetFrontendConfig; use gimli::{ write, DebugAddr, DebugAddrBase, DebugLine, DebugStr, LocationLists, RangeLists, UnitSectionOffset, @@ -10,6 +9,7 @@ use simulate::generate_simulated_dwarf; use std::collections::HashSet; use thiserror::Error; use unit::clone_unit; +use wasmtime_environ::isa::TargetFrontendConfig; use wasmtime_environ::{ModuleAddressMap, ModuleVmctxInfo, ValueLabelsRanges}; pub use address_transform::AddressTransform; diff --git a/crates/debug/src/transform/range_info_builder.rs b/crates/debug/src/transform/range_info_builder.rs index d9077fa3e8..199a6df403 100644 --- a/crates/debug/src/transform/range_info_builder.rs +++ b/crates/debug/src/transform/range_info_builder.rs @@ -1,10 +1,10 @@ use super::address_transform::AddressTransform; use super::{DebugInputContext, Reader}; use anyhow::Error; -use cranelift_entity::EntityRef; -use cranelift_wasm::DefinedFuncIndex; use gimli::{write, AttributeValue, DebuggingInformationEntry, RangeListsOffset}; use more_asserts::assert_lt; +use wasmtime_environ::entity::EntityRef; +use wasmtime_environ::wasm::DefinedFuncIndex; pub(crate) enum RangeInfoBuilder { Undefined, diff --git a/crates/debug/src/transform/simulate.rs b/crates/debug/src/transform/simulate.rs index 197fe4cd58..8ac24e2efe 100644 --- a/crates/debug/src/transform/simulate.rs +++ b/crates/debug/src/transform/simulate.rs @@ -3,12 +3,12 @@ use super::utils::{add_internal_types, append_vmctx_info, get_function_frame_inf use super::AddressTransform; use crate::read_debuginfo::WasmFileInfo; use anyhow::Error; -use cranelift_entity::EntityRef; -use cranelift_wasm::get_vmctx_value_label; use gimli::write; use gimli::{self, LineEncoding}; use std::collections::{HashMap, HashSet}; use std::path::PathBuf; +use wasmtime_environ::entity::EntityRef; +use wasmtime_environ::wasm::get_vmctx_value_label; use wasmtime_environ::{ModuleVmctxInfo, ValueLabelsRanges}; pub use crate::read_debuginfo::{DebugInfoData, FunctionMetadata, WasmType}; diff --git a/crates/debug/src/transform/unit.rs b/crates/debug/src/transform/unit.rs index 64bd5f82be..e1b515c17c 100644 --- a/crates/debug/src/transform/unit.rs +++ b/crates/debug/src/transform/unit.rs @@ -6,10 +6,10 @@ use super::range_info_builder::RangeInfoBuilder; use super::utils::{add_internal_types, append_vmctx_info, get_function_frame_info}; use super::{DebugInputContext, Reader, TransformError}; use anyhow::Error; -use cranelift_entity::EntityRef; use gimli::write; use gimli::{AttributeValue, DebuggingInformationEntry, Unit, UnitOffset}; use std::collections::{HashMap, HashSet}; +use wasmtime_environ::entity::EntityRef; use wasmtime_environ::{ModuleVmctxInfo, ValueLabelsRanges}; pub(crate) type PendingDieRef = (write::UnitEntryId, gimli::DwAt, UnitOffset); diff --git a/crates/debug/src/transform/utils.rs b/crates/debug/src/transform/utils.rs index 4fca0d0837..9faaa9f62a 100644 --- a/crates/debug/src/transform/utils.rs +++ b/crates/debug/src/transform/utils.rs @@ -1,8 +1,8 @@ use super::address_transform::AddressTransform; use super::expression::{CompiledExpression, FunctionFrameInfo}; use anyhow::Error; -use cranelift_wasm::DefinedFuncIndex; use gimli::write; +use wasmtime_environ::wasm::DefinedFuncIndex; use wasmtime_environ::{ModuleVmctxInfo, ValueLabelsRanges}; pub(crate) fn add_internal_types( diff --git a/crates/environ/Cargo.toml b/crates/environ/Cargo.toml index 7125ed26b6..4f0fdad613 100644 --- a/crates/environ/Cargo.toml +++ b/crates/environ/Cargo.toml @@ -15,7 +15,7 @@ edition = "2018" cranelift-codegen = { version = "0.50.0", features = ["enable-serde"] } cranelift-entity = { version = "0.50.0", features = ["enable-serde"] } cranelift-wasm = { version = "0.50.0", features = ["enable-serde"] } -wasmparser = "0.39.2" +wasmparser = "0.44.0" lightbeam = { path = "../lightbeam", optional = true } indexmap = "1.0.2" rayon = "1.2.1" diff --git a/crates/environ/src/data_structures.rs b/crates/environ/src/data_structures.rs new file mode 100644 index 0000000000..eeef93b8bf --- /dev/null +++ b/crates/environ/src/data_structures.rs @@ -0,0 +1,29 @@ +#![doc(hidden)] + +pub mod ir { + pub use cranelift_codegen::ir::{ + types, AbiParam, ArgumentPurpose, Signature, SourceLoc, StackSlots, TrapCode, Type, + ValueLabel, ValueLoc, + }; + pub use cranelift_codegen::ValueLabelsRanges; +} + +pub mod settings { + pub use cranelift_codegen::settings::{builder, Configurable, Flags}; +} + +pub mod isa { + pub use cranelift_codegen::isa::{CallConv, RegUnit, TargetFrontendConfig, TargetIsa}; +} + +pub mod entity { + pub use cranelift_entity::{BoxedSlice, EntityRef, PrimaryMap}; +} + +pub mod wasm { + pub use cranelift_wasm::{ + get_vmctx_value_label, DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, + DefinedTableIndex, FuncIndex, Global, GlobalIndex, GlobalInit, Memory, MemoryIndex, + SignatureIndex, Table, TableElementType, TableIndex, + }; +} diff --git a/crates/environ/src/lib.rs b/crates/environ/src/lib.rs index 5e23601c91..fdbea6c870 100644 --- a/crates/environ/src/lib.rs +++ b/crates/environ/src/lib.rs @@ -26,6 +26,7 @@ mod address_map; mod compilation; +mod data_structures; mod func_environ; mod module; mod module_environ; @@ -47,6 +48,7 @@ pub use crate::compilation::{ Relocations, TrapInformation, Traps, }; pub use crate::cranelift::Cranelift; +pub use crate::data_structures::*; pub use crate::func_environ::BuiltinFunctionIndex; #[cfg(feature = "lightbeam")] pub use crate::lightbeam::Lightbeam; diff --git a/crates/fuzzing/Cargo.toml b/crates/fuzzing/Cargo.toml index 152cdeae64..0fcd41ed39 100644 --- a/crates/fuzzing/Cargo.toml +++ b/crates/fuzzing/Cargo.toml @@ -12,13 +12,12 @@ version = "0.1.0" anyhow = "1.0.22" arbitrary = "0.2.0" binaryen = "0.8.2" -cranelift-codegen = "0.50.0" -cranelift-native = "0.50.0" env_logger = { version = "0.7.1", optional = true } log = "0.4.8" -wasmparser = "0.42.1" +wasmparser = "0.44.0" wasmprinter = "0.2.0" wasmtime = { path = "../api" } +wasmtime-environ = { path = "../environ" } wasmtime-jit = { path = "../jit" } [dev-dependencies] diff --git a/crates/fuzzing/src/oracles.rs b/crates/fuzzing/src/oracles.rs index 889ed8a043..b5de268f5d 100644 --- a/crates/fuzzing/src/oracles.rs +++ b/crates/fuzzing/src/oracles.rs @@ -12,17 +12,17 @@ pub mod dummy; -use cranelift_codegen::settings; use dummy::dummy_imports; use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; use wasmtime::{Config, Engine, HostRef, Instance, Module, Store}; -use wasmtime_jit::{CompilationStrategy, CompiledModule, Compiler, NullResolver}; +use wasmtime_environ::{isa, settings}; +use wasmtime_jit::{native, CompilationStrategy, CompiledModule, Compiler, NullResolver}; -fn host_isa() -> Box { +fn host_isa() -> Box { let flag_builder = settings::builder(); - let isa_builder = cranelift_native::builder().expect("host machine is not a supported target"); + let isa_builder = native::builder(); isa_builder.finish(settings::Flags::new(flag_builder)) } diff --git a/crates/fuzzing/src/oracles/dummy.rs b/crates/fuzzing/src/oracles/dummy.rs index 58a8605e88..b20212a1db 100644 --- a/crates/fuzzing/src/oracles/dummy.rs +++ b/crates/fuzzing/src/oracles/dummy.rs @@ -13,17 +13,17 @@ pub fn dummy_imports( ) -> Result, HostRef> { let mut imports = Vec::with_capacity(import_tys.len()); for imp in import_tys { - imports.push(match imp.r#type() { - ExternType::ExternFunc(func_ty) => { + imports.push(match imp.ty() { + ExternType::Func(func_ty) => { Extern::Func(HostRef::new(DummyFunc::new(&store, func_ty.clone()))) } - ExternType::ExternGlobal(global_ty) => { + ExternType::Global(global_ty) => { Extern::Global(HostRef::new(dummy_global(&store, global_ty.clone())?)) } - ExternType::ExternTable(table_ty) => { + ExternType::Table(table_ty) => { Extern::Table(HostRef::new(dummy_table(&store, table_ty.clone())?)) } - ExternType::ExternMemory(mem_ty) => { + ExternType::Memory(mem_ty) => { Extern::Memory(HostRef::new(dummy_memory(&store, mem_ty.clone()))) } }); diff --git a/crates/interface-types/Cargo.toml b/crates/interface-types/Cargo.toml index b94178016d..4ba91a80c6 100644 --- a/crates/interface-types/Cargo.toml +++ b/crates/interface-types/Cargo.toml @@ -12,12 +12,12 @@ edition = "2018" [dependencies] anyhow = "1.0.19" -cranelift-codegen = { version = "0.50.0", default-features = false } walrus = "0.13" -wasmparser = { version = "0.39.2", default-features = false } +wasmparser = { version = "0.44.0", default-features = false } wasm-webidl-bindings = "0.6" wasmtime = { path = '../api' } wasmtime-jit = { path = '../jit' } +wasmtime-environ = { path = '../environ' } wasmtime-runtime = { path = '../runtime' } wasmtime-wasi = { path = '../wasi' } diff --git a/crates/interface-types/src/lib.rs b/crates/interface-types/src/lib.rs index e6de5144c2..512ec33222 100644 --- a/crates/interface-types/src/lib.rs +++ b/crates/interface-types/src/lib.rs @@ -8,10 +8,10 @@ #![deny(missing_docs)] use anyhow::{bail, format_err, Result}; -use cranelift_codegen::ir; use std::convert::TryFrom; use std::str; use wasm_webidl_bindings::ast; +use wasmtime_environ::ir; use wasmtime_jit::RuntimeValue; use wasmtime_runtime::{Export, InstanceHandle}; @@ -152,7 +152,14 @@ impl ModuleData { Ok(values) => values .to_vec() .into_iter() - .map(|v: wasmtime::Val| v.into()) + .map(|v: wasmtime::Val| match v { + wasmtime::Val::I32(i) => RuntimeValue::I32(i), + wasmtime::Val::I64(i) => RuntimeValue::I64(i), + wasmtime::Val::F32(i) => RuntimeValue::F32(i), + wasmtime::Val::F64(i) => RuntimeValue::F64(i), + wasmtime::Val::V128(i) => RuntimeValue::V128(i), + _ => panic!("unsupported value {:?}", v), + }) .collect::>(), Err(trap) => bail!("trapped: {:?}", trap), }; diff --git a/crates/jit/Cargo.toml b/crates/jit/Cargo.toml index 3fc9e0c644..04cfcabe83 100644 --- a/crates/jit/Cargo.toml +++ b/crates/jit/Cargo.toml @@ -14,6 +14,7 @@ edition = "2018" cranelift-codegen = { version = "0.50.0", features = ["enable-serde"] } cranelift-entity = { version = "0.50.0", features = ["enable-serde"] } cranelift-wasm = { version = "0.50.0", features = ["enable-serde"] } +cranelift-native = "0.50.0" cranelift-frontend = "0.50.0" wasmtime-environ = { path = "../environ" } wasmtime-runtime = { path = "../runtime" } @@ -21,7 +22,7 @@ wasmtime-debug = { path = "../debug" } region = "2.0.0" thiserror = "1.0.4" target-lexicon = { version = "0.9.0", default-features = false } -wasmparser = { version = "0.39.2", default-features = false } +wasmparser = { version = "0.44.0", default-features = false } more-asserts = "0.2.1" anyhow = "1.0" diff --git a/crates/jit/src/action.rs b/crates/jit/src/action.rs index f293ff7ba6..994d653922 100644 --- a/crates/jit/src/action.rs +++ b/crates/jit/src/action.rs @@ -2,10 +2,10 @@ use crate::compiler::Compiler; use crate::instantiate::SetupError; -use cranelift_codegen::ir; use std::cmp::max; use std::{fmt, mem, ptr, slice}; use thiserror::Error; +use wasmtime_environ::ir; use wasmtime_runtime::{wasmtime_call_trampoline, Export, InstanceHandle, VMInvokeArgument}; /// A runtime value. diff --git a/crates/jit/src/compiler.rs b/crates/jit/src/compiler.rs index ff30bc71eb..eb396e5fe5 100644 --- a/crates/jit/src/compiler.rs +++ b/crates/jit/src/compiler.rs @@ -4,16 +4,17 @@ use crate::code_memory::CodeMemory; use crate::instantiate::SetupError; use crate::target_tunables::target_tunables; use cranelift_codegen::ir::InstBuilder; -use cranelift_codegen::isa::{TargetFrontendConfig, TargetIsa}; use cranelift_codegen::print_errors::pretty_error; use cranelift_codegen::Context; use cranelift_codegen::{binemit, ir}; -use cranelift_entity::{EntityRef, PrimaryMap}; use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; -use cranelift_wasm::{DefinedFuncIndex, DefinedMemoryIndex, ModuleTranslationState}; +use cranelift_wasm::ModuleTranslationState; use std::collections::HashMap; use std::convert::TryFrom; use wasmtime_debug::{emit_debugsections_image, DebugInfoData}; +use wasmtime_environ::entity::{EntityRef, PrimaryMap}; +use wasmtime_environ::isa::{TargetFrontendConfig, TargetIsa}; +use wasmtime_environ::wasm::{DefinedFuncIndex, DefinedMemoryIndex}; use wasmtime_environ::{ Compilation, CompileError, CompiledFunction, Compiler as _C, FunctionBodyData, Module, ModuleVmctxInfo, Relocations, Traps, Tunables, VMOffsets, diff --git a/crates/jit/src/context.rs b/crates/jit/src/context.rs index e835110b09..06b1c5c5b1 100644 --- a/crates/jit/src/context.rs +++ b/crates/jit/src/context.rs @@ -3,12 +3,12 @@ use crate::{ instantiate, ActionError, ActionOutcome, CompilationStrategy, CompiledModule, Compiler, InstanceHandle, Namespace, RuntimeValue, SetupError, }; -use cranelift_codegen::isa::TargetIsa; use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; use thiserror::Error; use wasmparser::{validate, OperatorValidatorConfig, ValidatingParserConfig}; +use wasmtime_environ::isa::TargetIsa; /// Indicates an unknown instance was specified. #[derive(Error, Debug)] diff --git a/crates/jit/src/instantiate.rs b/crates/jit/src/instantiate.rs index 34240e942c..ca1d61b163 100644 --- a/crates/jit/src/instantiate.rs +++ b/crates/jit/src/instantiate.rs @@ -6,14 +6,14 @@ use crate::compiler::Compiler; use crate::link::link_module; use crate::resolver::Resolver; -use cranelift_entity::{BoxedSlice, PrimaryMap}; -use cranelift_wasm::{DefinedFuncIndex, SignatureIndex}; use std::cell::RefCell; use std::collections::HashMap; use std::io::Write; use std::rc::Rc; use thiserror::Error; use wasmtime_debug::read_debuginfo; +use wasmtime_environ::entity::{BoxedSlice, PrimaryMap}; +use wasmtime_environ::wasm::{DefinedFuncIndex, SignatureIndex}; use wasmtime_environ::{ CompileError, DataInitializer, DataInitializerLocation, Module, ModuleEnvironment, }; diff --git a/crates/jit/src/lib.rs b/crates/jit/src/lib.rs index 2405180254..2590056251 100644 --- a/crates/jit/src/lib.rs +++ b/crates/jit/src/lib.rs @@ -32,6 +32,9 @@ mod namespace; mod resolver; mod target_tunables; +pub mod native; +pub mod trampoline; + pub use crate::action::{ActionError, ActionOutcome, RuntimeValue}; pub use crate::code_memory::CodeMemory; pub use crate::compiler::{CompilationStrategy, Compiler}; diff --git a/crates/jit/src/link.rs b/crates/jit/src/link.rs index 44369b9286..9fa05a1eb1 100644 --- a/crates/jit/src/link.rs +++ b/crates/jit/src/link.rs @@ -3,11 +3,13 @@ use crate::resolver::Resolver; use cranelift_codegen::binemit::Reloc; use cranelift_codegen::ir::JumpTableOffsets; -use cranelift_entity::PrimaryMap; -use cranelift_wasm::{DefinedFuncIndex, Global, GlobalInit, Memory, Table, TableElementType}; use more_asserts::assert_ge; use std::collections::HashSet; use std::ptr::write_unaligned; +use wasmtime_environ::entity::PrimaryMap; +use wasmtime_environ::wasm::{ + DefinedFuncIndex, Global, GlobalInit, Memory, Table, TableElementType, +}; use wasmtime_environ::{ MemoryPlan, MemoryStyle, Module, Relocation, RelocationTarget, Relocations, TablePlan, }; diff --git a/crates/jit/src/native.rs b/crates/jit/src/native.rs new file mode 100644 index 0000000000..9d1fdd7b66 --- /dev/null +++ b/crates/jit/src/native.rs @@ -0,0 +1,14 @@ +#![allow(missing_docs)] + +use cranelift_codegen; + +pub fn builder() -> cranelift_codegen::isa::Builder { + cranelift_native::builder().expect("host machine is not a supported target") +} + +pub fn call_conv() -> cranelift_codegen::isa::CallConv { + use target_lexicon::HOST; + cranelift_codegen::isa::CallConv::triple_default(&HOST) +} + +pub use cranelift_codegen::isa::lookup; diff --git a/crates/jit/src/trampoline.rs b/crates/jit/src/trampoline.rs new file mode 100644 index 0000000000..e5a2aaa6f9 --- /dev/null +++ b/crates/jit/src/trampoline.rs @@ -0,0 +1,56 @@ +#![allow(missing_docs)] + +pub mod ir { + pub use cranelift_codegen::ir::{ + ExternalName, Function, InstBuilder, MemFlags, StackSlotData, StackSlotKind, + }; +} +pub use cranelift_codegen::print_errors::pretty_error; +pub use cranelift_codegen::Context; +pub use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; + +pub mod binemit { + pub use cranelift_codegen::binemit::{CodeOffset, NullStackmapSink, TrapSink}; + + use cranelift_codegen::{binemit, ir}; + + /// We don't expect trampoline compilation to produce any relocations, so + /// this `RelocSink` just asserts that it doesn't recieve any. + pub struct TrampolineRelocSink {} + + impl binemit::RelocSink for TrampolineRelocSink { + fn reloc_ebb( + &mut self, + _offset: binemit::CodeOffset, + _reloc: binemit::Reloc, + _ebb_offset: binemit::CodeOffset, + ) { + panic!("trampoline compilation should not produce ebb relocs"); + } + fn reloc_external( + &mut self, + _offset: binemit::CodeOffset, + _reloc: binemit::Reloc, + _name: &ir::ExternalName, + _addend: binemit::Addend, + ) { + panic!("trampoline compilation should not produce external symbol relocs"); + } + fn reloc_constant( + &mut self, + _code_offset: binemit::CodeOffset, + _reloc: binemit::Reloc, + _constant_offset: ir::ConstantOffset, + ) { + panic!("trampoline compilation should not produce constant relocs"); + } + fn reloc_jt( + &mut self, + _offset: binemit::CodeOffset, + _reloc: binemit::Reloc, + _jt: ir::JumpTable, + ) { + panic!("trampoline compilation should not produce jump table relocs"); + } + } +} diff --git a/crates/misc/dotnet/src/Instance.cs b/crates/misc/dotnet/src/Instance.cs index c145d2be83..86f0b3f825 100644 --- a/crates/misc/dotnet/src/Instance.cs +++ b/crates/misc/dotnet/src/Instance.cs @@ -17,8 +17,10 @@ namespace Wasmtime Host = host; Module = module; - var bindings = host.GetImportBindings(module); - var handles = bindings.Select(b => b.Bind(module.Store, host)).ToList(); + //Save the bindings to root the objects. + //Otherwise the GC may collect the delegates from ExternFunction for example. + _bindings = host.GetImportBindings(module); + var handles = _bindings.Select(b => b.Bind(module.Store, host)).ToList(); unsafe { @@ -141,5 +143,6 @@ namespace Wasmtime private Interop.wasm_extern_vec_t _externs; private Dictionary _functions; private Dictionary _globals; + private List _bindings; } } diff --git a/crates/misc/dotnet/src/ValueKind.cs b/crates/misc/dotnet/src/ValueKind.cs index cce415aee4..0a6f72f123 100644 --- a/crates/misc/dotnet/src/ValueKind.cs +++ b/crates/misc/dotnet/src/ValueKind.cs @@ -5,7 +5,7 @@ namespace Wasmtime /// /// Represents the possible kinds of WebAssembly values. /// - public enum ValueKind + public enum ValueKind : byte { /// /// The value is a 32-bit integer. diff --git a/crates/misc/py/Cargo.toml b/crates/misc/py/Cargo.toml index a0bb8caf6b..cf9a6b92d5 100644 --- a/crates/misc/py/Cargo.toml +++ b/crates/misc/py/Cargo.toml @@ -15,11 +15,6 @@ name = "_wasmtime" crate-type = ["cdylib"] [dependencies] -cranelift-codegen = "0.50.0" -cranelift-native = "0.50.0" -cranelift-entity = "0.50.0" -cranelift-wasm = "0.50.0" -cranelift-frontend = "0.50.0" wasmtime = { path = "../../api" } wasmtime-environ = { path = "../../environ" } wasmtime-interface-types = { path = "../../interface-types" } @@ -29,7 +24,7 @@ wasmtime-wasi = { path = "../../wasi" } target-lexicon = { version = "0.9.0", default-features = false } anyhow = "1.0.19" region = "2.0.0" -wasmparser = "0.39.2" +wasmparser = "0.44.0" pyo3 = { version = "0.8.0", features = ["extension-module"] } [badges] diff --git a/crates/misc/py/src/instance.rs b/crates/misc/py/src/instance.rs index a8c5781f49..5975371cd4 100644 --- a/crates/misc/py/src/instance.rs +++ b/crates/misc/py/src/instance.rs @@ -22,8 +22,8 @@ impl Instance { let exports = PyDict::new(py); let module = self.instance.borrow().module().clone(); for (i, e) in module.borrow().exports().iter().enumerate() { - match e.r#type() { - wasmtime::ExternType::ExternFunc(ft) => { + match e.ty() { + wasmtime::ExternType::Func(ft) => { let mut args_types = Vec::new(); for ty in ft.params().iter() { args_types.push(ty.clone()); @@ -39,7 +39,7 @@ impl Instance { )?; exports.set_item(e.name().to_string(), f)?; } - wasmtime::ExternType::ExternMemory(_) => { + wasmtime::ExternType::Memory(_) => { let f = Py::new( py, Memory { diff --git a/crates/misc/py/src/lib.rs b/crates/misc/py/src/lib.rs index ca584a4ed9..7e56b06977 100644 --- a/crates/misc/py/src/lib.rs +++ b/crates/misc/py/src/lib.rs @@ -111,21 +111,18 @@ pub fn instantiate( let mut imports: Vec = Vec::new(); for i in module.borrow().imports() { - let module_name = i.module().as_str(); + let module_name = i.module(); if let Some(m) = import_obj.get_item(module_name) { - let e = find_export_in(m, &store, i.name().as_str())?; + let e = find_export_in(m, &store, i.name())?; imports.push(e); } else if wasi.is_some() && module_name == wasi.as_ref().unwrap().0 { let e = wasi .as_ref() .unwrap() .1 - .find_export_by_name(i.name().as_str()) + .find_export_by_name(i.name()) .ok_or_else(|| { - PyErr::new::(format!( - "wasi export {} is not found", - i.name().as_str() - )) + PyErr::new::(format!("wasi export {} is not found", i.name(),)) })?; imports.push(e.clone()); } else { diff --git a/crates/misc/rust/macro/src/lib.rs b/crates/misc/rust/macro/src/lib.rs index 9327e55f9d..03d167eef8 100644 --- a/crates/misc/rust/macro/src/lib.rs +++ b/crates/misc/rust/macro/src/lib.rs @@ -69,13 +69,13 @@ fn generate_load(item: &syn::ItemTrait) -> syn::Result { let wasi_instance = #root::wasmtime_wasi::create_wasi_instance(&store, &[], &[], &[]) .map_err(|e| format_err!("wasm instantiation error: {:?}", e))?; for i in module.borrow().imports().iter() { - if i.module().as_str() != module_name { - bail!("unknown import module {}", i.module().as_str()); + if i.module() != module_name { + bail!("unknown import module {}", i.module()); } - if let Some(export) = wasi_instance.find_export_by_name(i.name().as_str()) { + if let Some(export) = wasi_instance.find_export_by_name(i.name()) { imports.push(export.clone()); } else { - bail!("unknown import {}:{}", i.module().as_str(), i.name().as_str()) + bail!("unknown import {}:{}", i.module(), i.name()) } } } diff --git a/crates/obj/Cargo.toml b/crates/obj/Cargo.toml index cfebcaa828..c8ed4b1672 100644 --- a/crates/obj/Cargo.toml +++ b/crates/obj/Cargo.toml @@ -11,9 +11,6 @@ readme = "README.md" edition = "2018" [dependencies] -cranelift-codegen = { version = "0.50.0", features = ["enable-serde"] } -cranelift-entity = { version = "0.50.0", features = ["enable-serde"] } -cranelift-wasm = { version = "0.50.0", features = ["enable-serde"] } wasmtime-environ = { path = "../environ" } faerie = "0.13.0" more-asserts = "0.2.1" diff --git a/crates/obj/src/context.rs b/crates/obj/src/context.rs index 5ee980f77d..98cf034714 100644 --- a/crates/obj/src/context.rs +++ b/crates/obj/src/context.rs @@ -1,12 +1,12 @@ #![allow(clippy::cast_ptr_alignment)] -use cranelift_codegen::isa::TargetFrontendConfig; -use cranelift_entity::EntityRef; -use cranelift_wasm::GlobalInit; use more_asserts::assert_le; use std::collections::hash_map::Entry; use std::collections::HashMap; use std::ptr; +use wasmtime_environ::entity::EntityRef; +use wasmtime_environ::isa::TargetFrontendConfig; +use wasmtime_environ::wasm::GlobalInit; use wasmtime_environ::{Module, TargetSharedSignatureIndex, VMOffsets}; pub struct TableRelocation { diff --git a/crates/obj/src/function.rs b/crates/obj/src/function.rs index 59d3fcf049..dc1832d75d 100644 --- a/crates/obj/src/function.rs +++ b/crates/obj/src/function.rs @@ -1,7 +1,7 @@ -use cranelift_codegen::settings; -use cranelift_codegen::settings::Configurable; -use cranelift_entity::EntityRef; use faerie::{Artifact, Decl, Link}; +use wasmtime_environ::entity::EntityRef; +use wasmtime_environ::settings; +use wasmtime_environ::settings::Configurable; use wasmtime_environ::{Compilation, Module, RelocationTarget, Relocations}; fn get_reloc_target_special_import_name(target: RelocationTarget) -> Option<&'static str> { diff --git a/crates/obj/src/module.rs b/crates/obj/src/module.rs index d35e652251..b9621c22b5 100644 --- a/crates/obj/src/module.rs +++ b/crates/obj/src/module.rs @@ -2,8 +2,8 @@ use crate::context::layout_vmcontext; use crate::data_segment::{declare_data_segment, emit_data_segment}; use crate::function::{declare_functions, emit_functions}; use crate::table::{declare_table, emit_table}; -use cranelift_codegen::isa::TargetFrontendConfig; use faerie::{Artifact, Decl, Link}; +use wasmtime_environ::isa::TargetFrontendConfig; use wasmtime_environ::{Compilation, DataInitializer, Module, Relocations}; fn emit_vmcontext_init( diff --git a/crates/runtime/Cargo.toml b/crates/runtime/Cargo.toml index 8bf93483f1..01775882c9 100644 --- a/crates/runtime/Cargo.toml +++ b/crates/runtime/Cargo.toml @@ -11,9 +11,6 @@ readme = "README.md" edition = "2018" [dependencies] -cranelift-codegen = { version = "0.50.0", features = ["enable-serde"] } -cranelift-entity = { version = "0.50.0", features = ["enable-serde"] } -cranelift-wasm = { version = "0.50.0", features = ["enable-serde"] } wasmtime-environ = { path = "../environ" } region = "2.0.0" lazy_static = "1.2.0" diff --git a/crates/runtime/src/export.rs b/crates/runtime/src/export.rs index f71bfebacc..547d9593cd 100644 --- a/crates/runtime/src/export.rs +++ b/crates/runtime/src/export.rs @@ -1,8 +1,8 @@ use crate::vmcontext::{ VMContext, VMFunctionBody, VMGlobalDefinition, VMMemoryDefinition, VMTableDefinition, }; -use cranelift_codegen::ir; -use cranelift_wasm::Global; +use wasmtime_environ::ir; +use wasmtime_environ::wasm::Global; use wasmtime_environ::{MemoryPlan, TablePlan}; /// The value of an export passed from one instance to another. diff --git a/crates/runtime/src/imports.rs b/crates/runtime/src/imports.rs index 082f799ae6..0214ce67b0 100644 --- a/crates/runtime/src/imports.rs +++ b/crates/runtime/src/imports.rs @@ -1,8 +1,8 @@ use crate::instance::InstanceHandle; use crate::vmcontext::{VMFunctionImport, VMGlobalImport, VMMemoryImport, VMTableImport}; -use cranelift_entity::{BoxedSlice, PrimaryMap}; -use cranelift_wasm::{FuncIndex, GlobalIndex, MemoryIndex, TableIndex}; use std::collections::HashSet; +use wasmtime_environ::entity::{BoxedSlice, PrimaryMap}; +use wasmtime_environ::wasm::{FuncIndex, GlobalIndex, MemoryIndex, TableIndex}; /// Resolved import pointers. #[derive(Clone)] diff --git a/crates/runtime/src/instance.rs b/crates/runtime/src/instance.rs index 70babd8ad0..751cce5069 100644 --- a/crates/runtime/src/instance.rs +++ b/crates/runtime/src/instance.rs @@ -15,11 +15,6 @@ use crate::vmcontext::{ VMGlobalDefinition, VMGlobalImport, VMMemoryDefinition, VMMemoryImport, VMSharedSignatureIndex, VMTableDefinition, VMTableImport, }; -use cranelift_entity::{BoxedSlice, EntityRef, PrimaryMap}; -use cranelift_wasm::{ - DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, FuncIndex, - GlobalIndex, GlobalInit, MemoryIndex, SignatureIndex, TableIndex, -}; use memoffset::offset_of; use more_asserts::assert_lt; use std::any::Any; @@ -30,6 +25,11 @@ use std::convert::TryFrom; use std::rc::Rc; use std::{mem, ptr, slice}; use thiserror::Error; +use wasmtime_environ::entity::{BoxedSlice, EntityRef, PrimaryMap}; +use wasmtime_environ::wasm::{ + DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, FuncIndex, + GlobalIndex, GlobalInit, MemoryIndex, SignatureIndex, TableIndex, +}; use wasmtime_environ::{DataInitializer, Module, TableElements, VMOffsets}; fn signature_id( @@ -469,37 +469,6 @@ impl Instance { fn invoke_start_function(&mut self) -> Result<(), InstantiationError> { if let Some(start_index) = self.module.start_func { self.invoke_function(start_index) - } else if let Some(start_export) = self.module.exports.get("_start") { - // As a compatibility measure, if the module doesn't have a start - // function but does have a _start function exported, call that. - match *start_export { - wasmtime_environ::Export::Function(func_index) => { - let sig = &self.module.signatures[self.module.functions[func_index]]; - // No wasm params or returns; just the vmctx param. - if sig.params.len() == 1 && sig.returns.is_empty() { - self.invoke_function(func_index) - } else { - Ok(()) - } - } - _ => Ok(()), - } - } else if let Some(main_export) = self.module.exports.get("main") { - // As a further compatibility measure, if the module doesn't have a - // start function or a _start function exported, but does have a main - // function exported, call that. - match *main_export { - wasmtime_environ::Export::Function(func_index) => { - let sig = &self.module.signatures[self.module.functions[func_index]]; - // No wasm params or returns; just the vmctx param. - if sig.params.len() == 1 && sig.returns.is_empty() { - self.invoke_function(func_index) - } else { - Ok(()) - } - } - _ => Ok(()), - } } else { Ok(()) } diff --git a/crates/runtime/src/libcalls.rs b/crates/runtime/src/libcalls.rs index 5d816658de..9349bca791 100644 --- a/crates/runtime/src/libcalls.rs +++ b/crates/runtime/src/libcalls.rs @@ -3,7 +3,7 @@ //! instructions which compute them directly. use crate::vmcontext::VMContext; -use cranelift_wasm::{DefinedMemoryIndex, MemoryIndex}; +use wasmtime_environ::wasm::{DefinedMemoryIndex, MemoryIndex}; /// Implementation of f32.ceil pub extern "C" fn wasmtime_f32_ceil(x: f32) -> f32 { diff --git a/crates/runtime/src/sig_registry.rs b/crates/runtime/src/sig_registry.rs index 243417af86..70277ebe88 100644 --- a/crates/runtime/src/sig_registry.rs +++ b/crates/runtime/src/sig_registry.rs @@ -2,10 +2,10 @@ //! signature checking. use crate::vmcontext::VMSharedSignatureIndex; -use cranelift_codegen::ir; use more_asserts::{assert_lt, debug_assert_lt}; use std::collections::{hash_map, HashMap}; use std::convert::TryFrom; +use wasmtime_environ::ir; /// WebAssembly requires that the caller and callee signatures in an indirect /// call must match. To implement this efficiently, keep a registry of all diff --git a/crates/runtime/src/table.rs b/crates/runtime/src/table.rs index c7fb288f53..af950b9cb4 100644 --- a/crates/runtime/src/table.rs +++ b/crates/runtime/src/table.rs @@ -3,8 +3,8 @@ //! `Table` is to WebAssembly tables what `LinearMemory` is to WebAssembly linear memories. use crate::vmcontext::{VMCallerCheckedAnyfunc, VMTableDefinition}; -use cranelift_wasm::TableElementType; use std::convert::{TryFrom, TryInto}; +use wasmtime_environ::wasm::TableElementType; use wasmtime_environ::{TablePlan, TableStyle}; /// A table instance. diff --git a/crates/runtime/src/trap_registry.rs b/crates/runtime/src/trap_registry.rs index b62d58392b..5afcc16837 100644 --- a/crates/runtime/src/trap_registry.rs +++ b/crates/runtime/src/trap_registry.rs @@ -1,7 +1,7 @@ -use cranelift_codegen::ir; use lazy_static::lazy_static; use std::collections::HashMap; use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard}; +use wasmtime_environ::ir; lazy_static! { static ref REGISTRY: RwLock = RwLock::new(TrapRegistry::default()); diff --git a/crates/runtime/src/traphandlers.rs b/crates/runtime/src/traphandlers.rs index 83ecb0a17c..a2166f20b7 100644 --- a/crates/runtime/src/traphandlers.rs +++ b/crates/runtime/src/traphandlers.rs @@ -4,9 +4,9 @@ use crate::trap_registry::get_trap_registry; use crate::trap_registry::TrapDescription; use crate::vmcontext::{VMContext, VMFunctionBody}; -use cranelift_codegen::ir; use std::cell::Cell; use std::ptr; +use wasmtime_environ::ir; extern "C" { fn WasmtimeCallTrampoline( diff --git a/crates/test-programs/Cargo.toml b/crates/test-programs/Cargo.toml index ae6f67944a..4f24e2f9c1 100644 --- a/crates/test-programs/Cargo.toml +++ b/crates/test-programs/Cargo.toml @@ -16,7 +16,6 @@ wasmtime-environ = { path = "../environ" } wasmtime-jit = { path = "../jit" } wasmtime-wasi = { path = "../wasi" } wasmtime = { path = "../api" } -cranelift-codegen = "0.50.0" target-lexicon = "0.9.0" pretty_env_logger = "0.3.0" tempfile = "3.1.0" diff --git a/crates/test-programs/build.rs b/crates/test-programs/build.rs index 0c453be639..0f223b1c71 100644 --- a/crates/test-programs/build.rs +++ b/crates/test-programs/build.rs @@ -30,6 +30,8 @@ mod wasi_tests { println!("cargo:rerun-if-changed={}", test_file_path); } } + println!("cargo:rerun-if-changed=wasi-tests/Cargo.toml"); + println!("cargo:rerun-if-changed=wasi-tests/src/lib.rs"); // Build tests to OUT_DIR (target/*/build/wasi-common-*/out/wasm32-wasi/release/*.wasm) let out_dir = PathBuf::from( env::var("OUT_DIR").expect("The OUT_DIR environment variable must be set"), diff --git a/crates/test-programs/tests/wasm_tests/runtime.rs b/crates/test-programs/tests/wasm_tests/runtime.rs index e954ccb001..ea631eaab1 100644 --- a/crates/test-programs/tests/wasm_tests/runtime.rs +++ b/crates/test-programs/tests/wasm_tests/runtime.rs @@ -1,8 +1,8 @@ use anyhow::{bail, Context}; -use cranelift_codegen::settings::{self, Configurable}; use std::fs::File; -use std::{collections::HashMap, path::Path}; +use std::path::Path; use wasmtime::{Config, Engine, HostRef, Instance, Module, Store}; +use wasmtime_environ::settings::{self, Configurable}; pub fn instantiate(data: &[u8], bin_name: &str, workspace: Option<&Path>) -> anyhow::Result<()> { // Prepare runtime @@ -18,7 +18,6 @@ pub fn instantiate(data: &[u8], bin_name: &str, workspace: Option<&Path>) -> any let engine = HostRef::new(Engine::new(&config)); let store = HostRef::new(Store::new(&engine)); - let mut module_registry = HashMap::new(); let global_exports = store.borrow().global_exports().clone(); let get_preopens = |workspace: Option<&Path>| -> anyhow::Result> { if let Some(workspace) = workspace { @@ -33,7 +32,7 @@ pub fn instantiate(data: &[u8], bin_name: &str, workspace: Option<&Path>) -> any // Create our wasi context with pretty standard arguments/inheritance/etc. // Additionally register andy preopened directories if we have them. - let mut builder = wasi_common::old::snapshot_0::WasiCtxBuilder::new() + let mut builder = wasi_common::WasiCtxBuilder::new() .arg(bin_name) .arg(".") .inherit_stdio(); @@ -47,19 +46,33 @@ pub fn instantiate(data: &[u8], bin_name: &str, workspace: Option<&Path>) -> any // stdin is closed which causes tests to fail. let (reader, _writer) = os_pipe::pipe()?; builder = builder.stdin(reader_to_file(reader)); + let snapshot1 = Instance::from_handle( + &store, + wasmtime_wasi::instantiate_wasi_with_context( + global_exports.clone(), + builder.build().context("failed to build wasi context")?, + ) + .context("failed to instantiate wasi")?, + ); - // The current stable Rust toolchain uses the old `wasi_unstable` ABI, - // aka `snapshot_0`. - module_registry.insert( - "wasi_unstable".to_owned(), - Instance::from_handle( - &store, - wasmtime_wasi::old::snapshot_0::instantiate_wasi_with_context( - global_exports.clone(), - builder.build().context("failed to build wasi context")?, - ) - .context("failed to instantiate wasi")?, - ), + // ... and then do the same as above but for the old snapshot of wasi, since + // a few tests still test that + let mut builder = wasi_common::old::snapshot_0::WasiCtxBuilder::new() + .arg(bin_name) + .arg(".") + .inherit_stdio(); + for (dir, file) in get_preopens(workspace)? { + builder = builder.preopened_dir(file, dir); + } + let (reader, _writer) = os_pipe::pipe()?; + builder = builder.stdin(reader_to_file(reader)); + let snapshot0 = Instance::from_handle( + &store, + wasmtime_wasi::old::snapshot_0::instantiate_wasi_with_context( + global_exports.clone(), + builder.build().context("failed to build wasi context")?, + ) + .context("failed to instantiate wasi")?, ); let module = HostRef::new(Module::new(&store, &data).context("failed to create wasm module")?); @@ -68,20 +81,22 @@ pub fn instantiate(data: &[u8], bin_name: &str, workspace: Option<&Path>) -> any .imports() .iter() .map(|i| { - let module_name = i.module().as_str(); - if let Some(instance) = module_registry.get(module_name) { - let field_name = i.name().as_str(); - if let Some(export) = instance.find_export_by_name(field_name) { - Ok(export.clone()) - } else { - bail!( - "import {} was not found in module {}", - field_name, - module_name - ) - } + let instance = if i.module() == "wasi_unstable" { + &snapshot0 + } else if i.module() == "wasi_snapshot_preview1" { + &snapshot1 } else { - bail!("import module {} was not found", module_name) + bail!("import module {} was not found", i.module()) + }; + let field_name = i.name(); + if let Some(export) = instance.find_export_by_name(field_name) { + Ok(export.clone()) + } else { + bail!( + "import {} was not found in module {}", + field_name, + i.module(), + ) } }) .collect::, _>>()?; diff --git a/crates/test-programs/wasi-tests/Cargo.toml b/crates/test-programs/wasi-tests/Cargo.toml index 5e7c471b04..345e5ce50a 100644 --- a/crates/test-programs/wasi-tests/Cargo.toml +++ b/crates/test-programs/wasi-tests/Cargo.toml @@ -8,7 +8,8 @@ publish = false [dependencies] libc = "0.2.65" -wasi = "0.7.0" +wasi = "0.9.0" +wasi-old = { version = "0.7.0", package = "wasi" } more-asserts = "0.2.1" # This crate is built with the wasm32-wasi target, so it's separate diff --git a/crates/test-programs/wasi-tests/src/bin/big_random_buf.rs b/crates/test-programs/wasi-tests/src/bin/big_random_buf.rs index 5cbbc11928..ad40497f5c 100644 --- a/crates/test-programs/wasi-tests/src/bin/big_random_buf.rs +++ b/crates/test-programs/wasi-tests/src/bin/big_random_buf.rs @@ -1,12 +1,9 @@ -use wasi::wasi_unstable; - fn test_big_random_buf() { let mut buf = Vec::new(); buf.resize(1024, 0); - assert!( - wasi_unstable::random_get(&mut buf).is_ok(), - "calling get_random on a large buffer" - ); + unsafe { + wasi::random_get(buf.as_mut_ptr(), 1024).expect("failed to call random_get"); + } // Chances are pretty good that at least *one* byte will be non-zero in // any meaningful random function producing 1024 u8 values. assert!(buf.iter().any(|x| *x != 0), "random_get returned all zeros"); diff --git a/crates/test-programs/wasi-tests/src/bin/clock_time_get.rs b/crates/test-programs/wasi-tests/src/bin/clock_time_get.rs index 0ad84d37fd..2ef8a6c8f3 100644 --- a/crates/test-programs/wasi-tests/src/bin/clock_time_get.rs +++ b/crates/test-programs/wasi-tests/src/bin/clock_time_get.rs @@ -1,33 +1,15 @@ use more_asserts::assert_le; -use wasi::wasi_unstable; -use wasi_tests::wasi_wrappers::wasi_clock_time_get; unsafe fn test_clock_time_get() { // Test that clock_time_get succeeds. Even in environments where it's not // desirable to expose high-precision timers, it should still succeed. // clock_res_get is where information about precision can be provided. - let mut time: wasi_unstable::Timestamp = 0; - let status = wasi_clock_time_get(wasi_unstable::CLOCK_MONOTONIC, 1, &mut time); - assert_eq!( - status, - wasi_unstable::raw::__WASI_ESUCCESS, - "clock_time_get with a precision of 1" - ); + wasi::clock_time_get(wasi::CLOCKID_MONOTONIC, 1).expect("precision 1 should work"); - let status = wasi_clock_time_get(wasi_unstable::CLOCK_MONOTONIC, 0, &mut time); - assert_eq!( - status, - wasi_unstable::raw::__WASI_ESUCCESS, - "clock_time_get with a precision of 0" - ); - let first_time = time; + let first_time = + wasi::clock_time_get(wasi::CLOCKID_MONOTONIC, 0).expect("precision 0 should work"); - let status = wasi_clock_time_get(wasi_unstable::CLOCK_MONOTONIC, 0, &mut time); - assert_eq!( - status, - wasi_unstable::raw::__WASI_ESUCCESS, - "clock_time_get with a precision of 0" - ); + let time = wasi::clock_time_get(wasi::CLOCKID_MONOTONIC, 0).expect("re-fetch time should work"); assert_le!(first_time, time, "CLOCK_MONOTONIC should be monotonic"); } diff --git a/crates/test-programs/wasi-tests/src/bin/close_preopen.rs b/crates/test-programs/wasi-tests/src/bin/close_preopen.rs index 57acb6f747..bf06d109ef 100644 --- a/crates/test-programs/wasi-tests/src/bin/close_preopen.rs +++ b/crates/test-programs/wasi-tests/src/bin/close_preopen.rs @@ -1,60 +1,46 @@ -use libc; use more_asserts::assert_gt; -use std::{env, mem, process}; -use wasi::wasi_unstable; -use wasi_tests::open_scratch_directory; -use wasi_tests::wasi_wrappers::wasi_fd_fdstat_get; +use std::{env, process}; +use wasi_tests::open_scratch_directory_new; -unsafe fn test_close_preopen(dir_fd: wasi_unstable::Fd) { - let pre_fd: wasi_unstable::Fd = (libc::STDERR_FILENO + 1) as wasi_unstable::Fd; +unsafe fn test_close_preopen(dir_fd: wasi::Fd) { + let pre_fd: wasi::Fd = (libc::STDERR_FILENO + 1) as wasi::Fd; assert_gt!(dir_fd, pre_fd, "dir_fd number"); // Try to close a preopened directory handle. assert_eq!( - wasi_unstable::fd_close(pre_fd), - Err(wasi_unstable::ENOTSUP), + wasi::fd_close(pre_fd).unwrap_err().raw_error(), + wasi::ERRNO_NOTSUP, "closing a preopened file descriptor", ); // Try to renumber over a preopened directory handle. assert_eq!( - wasi_unstable::fd_renumber(dir_fd, pre_fd), - Err(wasi_unstable::ENOTSUP), + wasi::fd_renumber(dir_fd, pre_fd).unwrap_err().raw_error(), + wasi::ERRNO_NOTSUP, "renumbering over a preopened file descriptor", ); // Ensure that dir_fd is still open. - let mut dir_fdstat: wasi_unstable::FdStat = mem::zeroed(); - let mut status = wasi_fd_fdstat_get(dir_fd, &mut dir_fdstat); - assert_eq!( - status, - wasi_unstable::raw::__WASI_ESUCCESS, - "calling fd_fdstat on the scratch directory" - ); + let dir_fdstat = wasi::fd_fdstat_get(dir_fd).expect("failed fd_fdstat_get"); assert_eq!( dir_fdstat.fs_filetype, - wasi_unstable::FILETYPE_DIRECTORY, + wasi::FILETYPE_DIRECTORY, "expected the scratch directory to be a directory", ); // Try to renumber a preopened directory handle. assert_eq!( - wasi_unstable::fd_renumber(pre_fd, dir_fd), - Err(wasi_unstable::ENOTSUP), + wasi::fd_renumber(pre_fd, dir_fd).unwrap_err().raw_error(), + wasi::ERRNO_NOTSUP, "renumbering over a preopened file descriptor", ); // Ensure that dir_fd is still open. - status = wasi_fd_fdstat_get(dir_fd, &mut dir_fdstat); - assert_eq!( - status, - wasi_unstable::raw::__WASI_ESUCCESS, - "calling fd_fdstat on the scratch directory" - ); + let dir_fdstat = wasi::fd_fdstat_get(dir_fd).expect("failed fd_fdstat_get"); assert_eq!( dir_fdstat.fs_filetype, - wasi_unstable::FILETYPE_DIRECTORY, + wasi::FILETYPE_DIRECTORY, "expected the scratch directory to be a directory", ); } @@ -70,7 +56,7 @@ fn main() { }; // Open scratch directory - let dir_fd = match open_scratch_directory(&arg) { + let dir_fd = match open_scratch_directory_new(&arg) { Ok(dir_fd) => dir_fd, Err(err) => { eprintln!("{}", err); diff --git a/crates/test-programs/wasi-tests/src/bin/dangling_fd.rs b/crates/test-programs/wasi-tests/src/bin/dangling_fd.rs index 0f085724b2..23e08d3a32 100644 --- a/crates/test-programs/wasi-tests/src/bin/dangling_fd.rs +++ b/crates/test-programs/wasi-tests/src/bin/dangling_fd.rs @@ -1,6 +1,6 @@ use more_asserts::assert_gt; use std::{env, process}; -use wasi::wasi_unstable; +use wasi_old::wasi_unstable; use wasi_tests::open_scratch_directory; use wasi_tests::utils::{cleanup_dir, cleanup_file, create_dir, create_file}; use wasi_tests::wasi_wrappers::wasi_path_open; diff --git a/crates/test-programs/wasi-tests/src/bin/dangling_symlink.rs b/crates/test-programs/wasi-tests/src/bin/dangling_symlink.rs index 4a36f052ad..a634742f70 100644 --- a/crates/test-programs/wasi-tests/src/bin/dangling_symlink.rs +++ b/crates/test-programs/wasi-tests/src/bin/dangling_symlink.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi::wasi_unstable; +use wasi_old::wasi_unstable; use wasi_tests::open_scratch_directory; use wasi_tests::utils::cleanup_file; use wasi_tests::wasi_wrappers::{wasi_path_open, wasi_path_symlink}; diff --git a/crates/test-programs/wasi-tests/src/bin/directory_seek.rs b/crates/test-programs/wasi-tests/src/bin/directory_seek.rs index 6cf3ff10a9..714f1f7e36 100644 --- a/crates/test-programs/wasi-tests/src/bin/directory_seek.rs +++ b/crates/test-programs/wasi-tests/src/bin/directory_seek.rs @@ -1,6 +1,6 @@ use more_asserts::assert_gt; use std::{env, mem, process}; -use wasi::wasi_unstable; +use wasi_old::wasi_unstable; use wasi_tests::open_scratch_directory; use wasi_tests::utils::{cleanup_dir, close_fd, create_dir}; use wasi_tests::wasi_wrappers::{wasi_fd_fdstat_get, wasi_fd_seek, wasi_path_open}; diff --git a/crates/test-programs/wasi-tests/src/bin/fd_advise.rs b/crates/test-programs/wasi-tests/src/bin/fd_advise.rs index c436cec6de..e0c0056e2f 100644 --- a/crates/test-programs/wasi-tests/src/bin/fd_advise.rs +++ b/crates/test-programs/wasi-tests/src/bin/fd_advise.rs @@ -1,7 +1,7 @@ use libc; use more_asserts::assert_gt; use std::{env, process}; -use wasi::wasi_unstable; +use wasi_old::wasi_unstable; use wasi_tests::open_scratch_directory; use wasi_tests::utils::{cleanup_file, close_fd}; use wasi_tests::wasi_wrappers::{wasi_fd_advise, wasi_fd_filestat_get, wasi_path_open}; diff --git a/crates/test-programs/wasi-tests/src/bin/fd_filestat_set.rs b/crates/test-programs/wasi-tests/src/bin/fd_filestat_set.rs index 13bfd4a8b9..7d5f68f0ae 100644 --- a/crates/test-programs/wasi-tests/src/bin/fd_filestat_set.rs +++ b/crates/test-programs/wasi-tests/src/bin/fd_filestat_set.rs @@ -1,7 +1,7 @@ use libc; use more_asserts::assert_gt; use std::{env, process}; -use wasi::wasi_unstable; +use wasi_old::wasi_unstable; use wasi_tests::open_scratch_directory; use wasi_tests::utils::{cleanup_file, close_fd}; use wasi_tests::wasi_wrappers::{wasi_fd_filestat_get, wasi_path_open}; diff --git a/crates/test-programs/wasi-tests/src/bin/fd_readdir.rs b/crates/test-programs/wasi-tests/src/bin/fd_readdir.rs index 7c93ab6ee2..52902a07ba 100644 --- a/crates/test-programs/wasi-tests/src/bin/fd_readdir.rs +++ b/crates/test-programs/wasi-tests/src/bin/fd_readdir.rs @@ -1,7 +1,7 @@ use libc; use more_asserts::assert_gt; use std::{cmp::min, env, mem, process, slice, str}; -use wasi::wasi_unstable; +use wasi_old::wasi_unstable; use wasi_tests::open_scratch_directory; use wasi_tests::wasi_wrappers::{wasi_fd_filestat_get, wasi_fd_readdir, wasi_path_open}; diff --git a/crates/test-programs/wasi-tests/src/bin/file_allocate.rs b/crates/test-programs/wasi-tests/src/bin/file_allocate.rs index 5c9d5784f6..bb0e8e4c24 100644 --- a/crates/test-programs/wasi-tests/src/bin/file_allocate.rs +++ b/crates/test-programs/wasi-tests/src/bin/file_allocate.rs @@ -1,6 +1,6 @@ use more_asserts::assert_gt; use std::{env, process}; -use wasi::wasi_unstable; +use wasi_old::wasi_unstable; use wasi_tests::open_scratch_directory; use wasi_tests::utils::{cleanup_file, close_fd}; use wasi_tests::wasi_wrappers::{wasi_fd_filestat_get, wasi_path_open}; diff --git a/crates/test-programs/wasi-tests/src/bin/file_pread_pwrite.rs b/crates/test-programs/wasi-tests/src/bin/file_pread_pwrite.rs index b9e9924ed1..d1c2c3fed9 100644 --- a/crates/test-programs/wasi-tests/src/bin/file_pread_pwrite.rs +++ b/crates/test-programs/wasi-tests/src/bin/file_pread_pwrite.rs @@ -1,7 +1,7 @@ use libc; use more_asserts::assert_gt; use std::{env, process}; -use wasi::wasi_unstable; +use wasi_old::wasi_unstable; use wasi_tests::open_scratch_directory; use wasi_tests::utils::{cleanup_file, close_fd}; use wasi_tests::wasi_wrappers::{wasi_fd_pread, wasi_fd_pwrite, wasi_path_open}; diff --git a/crates/test-programs/wasi-tests/src/bin/file_seek_tell.rs b/crates/test-programs/wasi-tests/src/bin/file_seek_tell.rs index abdd0aa56b..30fef2194a 100644 --- a/crates/test-programs/wasi-tests/src/bin/file_seek_tell.rs +++ b/crates/test-programs/wasi-tests/src/bin/file_seek_tell.rs @@ -1,7 +1,7 @@ use libc; use more_asserts::assert_gt; use std::{env, process}; -use wasi::wasi_unstable; +use wasi_old::wasi_unstable; use wasi_tests::open_scratch_directory; use wasi_tests::utils::{cleanup_file, close_fd}; use wasi_tests::wasi_wrappers::{wasi_fd_seek, wasi_fd_tell, wasi_fd_write, wasi_path_open}; diff --git a/crates/test-programs/wasi-tests/src/bin/file_unbuffered_write.rs b/crates/test-programs/wasi-tests/src/bin/file_unbuffered_write.rs index 656a04429a..1e8f6ddf0a 100644 --- a/crates/test-programs/wasi-tests/src/bin/file_unbuffered_write.rs +++ b/crates/test-programs/wasi-tests/src/bin/file_unbuffered_write.rs @@ -1,6 +1,6 @@ use more_asserts::assert_gt; use std::{env, process}; -use wasi::wasi_unstable; +use wasi_old::wasi_unstable; use wasi_tests::open_scratch_directory; use wasi_tests::utils::{cleanup_file, close_fd, create_file}; use wasi_tests::wasi_wrappers::{wasi_fd_read, wasi_fd_write, wasi_path_open}; diff --git a/crates/test-programs/wasi-tests/src/bin/interesting_paths.rs b/crates/test-programs/wasi-tests/src/bin/interesting_paths.rs index d712d09ec9..a8e97c1d77 100644 --- a/crates/test-programs/wasi-tests/src/bin/interesting_paths.rs +++ b/crates/test-programs/wasi-tests/src/bin/interesting_paths.rs @@ -1,7 +1,7 @@ use libc; use more_asserts::assert_gt; use std::{env, process}; -use wasi::wasi_unstable; +use wasi_old::wasi_unstable; use wasi_tests::open_scratch_directory; use wasi_tests::utils::{close_fd, create_dir, create_file}; use wasi_tests::wasi_wrappers::{ diff --git a/crates/test-programs/wasi-tests/src/bin/isatty.rs b/crates/test-programs/wasi-tests/src/bin/isatty.rs index e2996d52f9..988b9fb069 100644 --- a/crates/test-programs/wasi-tests/src/bin/isatty.rs +++ b/crates/test-programs/wasi-tests/src/bin/isatty.rs @@ -1,6 +1,6 @@ use more_asserts::assert_gt; use std::{env, process}; -use wasi::wasi_unstable; +use wasi_old::wasi_unstable; use wasi_tests::open_scratch_directory; use wasi_tests::utils::{cleanup_file, close_fd}; use wasi_tests::wasi_wrappers::wasi_path_open; diff --git a/crates/test-programs/wasi-tests/src/bin/nofollow_errors.rs b/crates/test-programs/wasi-tests/src/bin/nofollow_errors.rs index 8102104d09..ca77a1a713 100644 --- a/crates/test-programs/wasi-tests/src/bin/nofollow_errors.rs +++ b/crates/test-programs/wasi-tests/src/bin/nofollow_errors.rs @@ -1,7 +1,7 @@ use libc; use more_asserts::assert_gt; use std::{env, process}; -use wasi::wasi_unstable; +use wasi_old::wasi_unstable; use wasi_tests::open_scratch_directory; use wasi_tests::utils::{cleanup_file, close_fd, create_dir, create_file}; use wasi_tests::wasi_wrappers::{wasi_path_open, wasi_path_remove_directory, wasi_path_symlink}; diff --git a/crates/test-programs/wasi-tests/src/bin/path_filestat.rs b/crates/test-programs/wasi-tests/src/bin/path_filestat.rs index 006e25c772..762f808143 100644 --- a/crates/test-programs/wasi-tests/src/bin/path_filestat.rs +++ b/crates/test-programs/wasi-tests/src/bin/path_filestat.rs @@ -1,6 +1,6 @@ use more_asserts::assert_gt; use std::{env, process}; -use wasi::wasi_unstable; +use wasi_old::wasi_unstable; use wasi_tests::open_scratch_directory; use wasi_tests::utils::{cleanup_file, close_fd}; use wasi_tests::wasi_wrappers::{ diff --git a/crates/test-programs/wasi-tests/src/bin/path_link.rs b/crates/test-programs/wasi-tests/src/bin/path_link.rs index d4b0200722..787265535d 100644 --- a/crates/test-programs/wasi-tests/src/bin/path_link.rs +++ b/crates/test-programs/wasi-tests/src/bin/path_link.rs @@ -1,6 +1,6 @@ use more_asserts::assert_gt; use std::{env, process}; -use wasi::wasi_unstable; +use wasi_old::wasi_unstable; use wasi_tests::open_scratch_directory; use wasi_tests::utils::{cleanup_dir, cleanup_file, create_dir, create_file}; use wasi_tests::wasi_wrappers::{ diff --git a/crates/test-programs/wasi-tests/src/bin/path_open_create_existing.rs b/crates/test-programs/wasi-tests/src/bin/path_open_create_existing.rs index 11bf603a54..296f18a318 100644 --- a/crates/test-programs/wasi-tests/src/bin/path_open_create_existing.rs +++ b/crates/test-programs/wasi-tests/src/bin/path_open_create_existing.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi::wasi_unstable; +use wasi_old::wasi_unstable; use wasi_tests::open_scratch_directory; use wasi_tests::utils::{cleanup_file, close_fd}; use wasi_tests::wasi_wrappers::wasi_path_open; diff --git a/crates/test-programs/wasi-tests/src/bin/path_open_dirfd_not_dir.rs b/crates/test-programs/wasi-tests/src/bin/path_open_dirfd_not_dir.rs index 113c1d6a57..e478ce52fa 100644 --- a/crates/test-programs/wasi-tests/src/bin/path_open_dirfd_not_dir.rs +++ b/crates/test-programs/wasi-tests/src/bin/path_open_dirfd_not_dir.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi::wasi_unstable; +use wasi_old::wasi_unstable; use wasi_tests::open_scratch_directory; use wasi_tests::utils::close_fd; use wasi_tests::wasi_wrappers::wasi_path_open; diff --git a/crates/test-programs/wasi-tests/src/bin/path_rename.rs b/crates/test-programs/wasi-tests/src/bin/path_rename.rs index d8d2fd3f22..08df1b0aab 100644 --- a/crates/test-programs/wasi-tests/src/bin/path_rename.rs +++ b/crates/test-programs/wasi-tests/src/bin/path_rename.rs @@ -1,6 +1,6 @@ use more_asserts::assert_gt; use std::{env, process}; -use wasi::wasi_unstable; +use wasi_old::wasi_unstable; use wasi_tests::open_scratch_directory; use wasi_tests::utils::{cleanup_dir, cleanup_file, close_fd, create_dir, create_file}; use wasi_tests::wasi_wrappers::{wasi_path_open, wasi_path_rename}; diff --git a/crates/test-programs/wasi-tests/src/bin/path_rename_trailing_slashes.rs b/crates/test-programs/wasi-tests/src/bin/path_rename_trailing_slashes.rs index a1b347c683..eef7525d50 100644 --- a/crates/test-programs/wasi-tests/src/bin/path_rename_trailing_slashes.rs +++ b/crates/test-programs/wasi-tests/src/bin/path_rename_trailing_slashes.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi::wasi_unstable; +use wasi_old::wasi_unstable; use wasi_tests::open_scratch_directory; use wasi_tests::utils::{cleanup_dir, cleanup_file, create_dir, create_file}; use wasi_tests::wasi_wrappers::wasi_path_rename; diff --git a/crates/test-programs/wasi-tests/src/bin/path_symlink_trailing_slashes.rs b/crates/test-programs/wasi-tests/src/bin/path_symlink_trailing_slashes.rs index 7eb4eea2b4..c9d38eee3a 100644 --- a/crates/test-programs/wasi-tests/src/bin/path_symlink_trailing_slashes.rs +++ b/crates/test-programs/wasi-tests/src/bin/path_symlink_trailing_slashes.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi::wasi_unstable; +use wasi_old::wasi_unstable; use wasi_tests::open_scratch_directory; use wasi_tests::utils::{cleanup_dir, cleanup_file, create_dir, create_file}; use wasi_tests::wasi_wrappers::wasi_path_symlink; diff --git a/crates/test-programs/wasi-tests/src/bin/poll_oneoff.rs b/crates/test-programs/wasi-tests/src/bin/poll_oneoff.rs index 37b51d3985..6054340778 100644 --- a/crates/test-programs/wasi-tests/src/bin/poll_oneoff.rs +++ b/crates/test-programs/wasi-tests/src/bin/poll_oneoff.rs @@ -1,6 +1,6 @@ use more_asserts::assert_gt; use std::{env, mem::MaybeUninit, process}; -use wasi::wasi_unstable; +use wasi_old::wasi_unstable; use wasi_tests::{ open_scratch_directory, utils::{cleanup_file, close_fd}, diff --git a/crates/test-programs/wasi-tests/src/bin/readlink.rs b/crates/test-programs/wasi-tests/src/bin/readlink.rs index 11486d1607..69c6a487bc 100644 --- a/crates/test-programs/wasi-tests/src/bin/readlink.rs +++ b/crates/test-programs/wasi-tests/src/bin/readlink.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi::wasi_unstable; +use wasi_old::wasi_unstable; use wasi_tests::open_scratch_directory; use wasi_tests::utils::{cleanup_file, create_file}; use wasi_tests::wasi_wrappers::{wasi_path_readlink, wasi_path_symlink}; diff --git a/crates/test-programs/wasi-tests/src/bin/readlink_no_buffer.rs b/crates/test-programs/wasi-tests/src/bin/readlink_no_buffer.rs index 9886258a05..d04f3f600a 100644 --- a/crates/test-programs/wasi-tests/src/bin/readlink_no_buffer.rs +++ b/crates/test-programs/wasi-tests/src/bin/readlink_no_buffer.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi::wasi_unstable; +use wasi_old::wasi_unstable; use wasi_tests::open_scratch_directory; use wasi_tests::utils::cleanup_file; use wasi_tests::wasi_wrappers::{wasi_path_readlink, wasi_path_symlink}; diff --git a/crates/test-programs/wasi-tests/src/bin/remove_directory_trailing_slashes.rs b/crates/test-programs/wasi-tests/src/bin/remove_directory_trailing_slashes.rs index a1bc56a82e..7c7c6e9b8c 100644 --- a/crates/test-programs/wasi-tests/src/bin/remove_directory_trailing_slashes.rs +++ b/crates/test-programs/wasi-tests/src/bin/remove_directory_trailing_slashes.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi::wasi_unstable; +use wasi_old::wasi_unstable; use wasi_tests::open_scratch_directory; use wasi_tests::utils::{cleanup_file, create_dir, create_file}; use wasi_tests::wasi_wrappers::wasi_path_remove_directory; diff --git a/crates/test-programs/wasi-tests/src/bin/remove_nonempty_directory.rs b/crates/test-programs/wasi-tests/src/bin/remove_nonempty_directory.rs index 81f9107dcd..e8ea9cf159 100644 --- a/crates/test-programs/wasi-tests/src/bin/remove_nonempty_directory.rs +++ b/crates/test-programs/wasi-tests/src/bin/remove_nonempty_directory.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi::wasi_unstable; +use wasi_old::wasi_unstable; use wasi_tests::open_scratch_directory; use wasi_tests::utils::{cleanup_dir, create_dir}; use wasi_tests::wasi_wrappers::wasi_path_remove_directory; diff --git a/crates/test-programs/wasi-tests/src/bin/renumber.rs b/crates/test-programs/wasi-tests/src/bin/renumber.rs index 3bab561028..33c0fc4764 100644 --- a/crates/test-programs/wasi-tests/src/bin/renumber.rs +++ b/crates/test-programs/wasi-tests/src/bin/renumber.rs @@ -1,7 +1,7 @@ use libc; use more_asserts::assert_gt; use std::{env, mem, process}; -use wasi::wasi_unstable; +use wasi_old::wasi_unstable; use wasi_tests::open_scratch_directory; use wasi_tests::utils::close_fd; use wasi_tests::wasi_wrappers::{wasi_fd_fdstat_get, wasi_path_open}; diff --git a/crates/test-programs/wasi-tests/src/bin/sched_yield.rs b/crates/test-programs/wasi-tests/src/bin/sched_yield.rs index d498f88495..57d2b73d9e 100644 --- a/crates/test-programs/wasi-tests/src/bin/sched_yield.rs +++ b/crates/test-programs/wasi-tests/src/bin/sched_yield.rs @@ -1,4 +1,4 @@ -use wasi::wasi_unstable; +use wasi_old::wasi_unstable; fn test_sched_yield() { assert!(wasi_unstable::sched_yield().is_ok(), "sched_yield"); diff --git a/crates/test-programs/wasi-tests/src/bin/stdio.rs b/crates/test-programs/wasi-tests/src/bin/stdio.rs index ae48aae8c1..10437e66a9 100644 --- a/crates/test-programs/wasi-tests/src/bin/stdio.rs +++ b/crates/test-programs/wasi-tests/src/bin/stdio.rs @@ -1,5 +1,5 @@ use std::mem::MaybeUninit; -use wasi::wasi_unstable; +use wasi_old::wasi_unstable; use wasi_tests::wasi_wrappers::wasi_fd_fdstat_get; unsafe fn test_stdio() { diff --git a/crates/test-programs/wasi-tests/src/bin/symlink_loop.rs b/crates/test-programs/wasi-tests/src/bin/symlink_loop.rs index b8d8b34a34..013f5de9e8 100644 --- a/crates/test-programs/wasi-tests/src/bin/symlink_loop.rs +++ b/crates/test-programs/wasi-tests/src/bin/symlink_loop.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi::wasi_unstable; +use wasi_old::wasi_unstable; use wasi_tests::open_scratch_directory; use wasi_tests::utils::cleanup_file; use wasi_tests::wasi_wrappers::{wasi_path_open, wasi_path_symlink}; diff --git a/crates/test-programs/wasi-tests/src/bin/truncation_rights.rs b/crates/test-programs/wasi-tests/src/bin/truncation_rights.rs index 11ba2e77f0..427d3b2a4a 100644 --- a/crates/test-programs/wasi-tests/src/bin/truncation_rights.rs +++ b/crates/test-programs/wasi-tests/src/bin/truncation_rights.rs @@ -1,5 +1,5 @@ use std::{env, mem, process}; -use wasi::wasi_unstable; +use wasi_old::wasi_unstable; use wasi_tests::open_scratch_directory; use wasi_tests::utils::{cleanup_file, close_fd, create_file}; use wasi_tests::wasi_wrappers::{wasi_fd_fdstat_get, wasi_path_open}; diff --git a/crates/test-programs/wasi-tests/src/bin/unlink_file_trailing_slashes.rs b/crates/test-programs/wasi-tests/src/bin/unlink_file_trailing_slashes.rs index 14031c5f13..b32f8a619c 100644 --- a/crates/test-programs/wasi-tests/src/bin/unlink_file_trailing_slashes.rs +++ b/crates/test-programs/wasi-tests/src/bin/unlink_file_trailing_slashes.rs @@ -1,5 +1,5 @@ use std::{env, process}; -use wasi::wasi_unstable; +use wasi_old::wasi_unstable; use wasi_tests::open_scratch_directory; use wasi_tests::utils::{cleanup_dir, create_dir, create_file}; use wasi_tests::wasi_wrappers::wasi_path_unlink_file; diff --git a/crates/test-programs/wasi-tests/src/lib.rs b/crates/test-programs/wasi-tests/src/lib.rs index 5960d09f5c..75fd1bb04d 100644 --- a/crates/test-programs/wasi-tests/src/lib.rs +++ b/crates/test-programs/wasi-tests/src/lib.rs @@ -4,8 +4,12 @@ pub mod wasi_wrappers; use libc; use std::ffi::CString; use std::io; -use wasi::wasi_unstable; +use wasi_old::wasi_unstable; +/// Opens a fresh file descriptor for `path` where `path` should be a preopened +/// directory. This is intended to be used with `wasi_unstable`, not with +/// `wasi_snapshot_preview1`. This is getting phased out and will likely be +/// deleted soon. pub fn open_scratch_directory(path: &str) -> Result { // Open the scratch directory. let dir_fd: wasi_unstable::Fd = unsafe { @@ -23,3 +27,33 @@ pub fn open_scratch_directory(path: &str) -> Result { Ok(dir_fd) } } + +/// Same as `open_scratch_directory` above, except uses `wasi_snapshot_preview1` +/// APIs instead of `wasi_unstable` ones. +/// +/// This is intended to replace `open_scratch_directory` once all the tests are +/// updated. +pub fn open_scratch_directory_new(path: &str) -> Result { + unsafe { + for i in 3.. { + let stat = match wasi::fd_prestat_get(i) { + Ok(s) => s, + Err(_) => break, + }; + if stat.pr_type != wasi::PREOPENTYPE_DIR { + continue; + } + let mut dst = Vec::with_capacity(stat.u.dir.pr_name_len); + if wasi::fd_prestat_dir_name(i, dst.as_mut_ptr(), dst.capacity()).is_err() { + continue; + } + dst.set_len(stat.u.dir.pr_name_len); + if dst == path.as_bytes() { + return Ok(wasi::path_open(i, 0, ".", wasi::OFLAGS_DIRECTORY, 0, 0, 0) + .expect("failed to open dir")); + } + } + + Err(format!("failed to find scratch dir")) + } +} diff --git a/crates/test-programs/wasi-tests/src/utils.rs b/crates/test-programs/wasi-tests/src/utils.rs index b9c1f1620c..fbf4e5b035 100644 --- a/crates/test-programs/wasi-tests/src/utils.rs +++ b/crates/test-programs/wasi-tests/src/utils.rs @@ -1,6 +1,6 @@ use crate::wasi_wrappers::*; use more_asserts::assert_gt; -use wasi::wasi_unstable; +use wasi_old::wasi_unstable; pub unsafe fn create_dir(dir_fd: wasi_unstable::Fd, dir_name: &str) { assert!( diff --git a/crates/test-programs/wasi-tests/src/wasi_wrappers.rs b/crates/test-programs/wasi-tests/src/wasi_wrappers.rs index 3fcfd8ba95..365ab7c62e 100644 --- a/crates/test-programs/wasi-tests/src/wasi_wrappers.rs +++ b/crates/test-programs/wasi-tests/src/wasi_wrappers.rs @@ -6,7 +6,7 @@ //! interfaces so that we can test whether they are stored to. In the future, //! WASI should switch to multi-value and eliminate out parameters altogether. -use wasi::wasi_unstable; +use wasi_old::wasi_unstable; pub unsafe fn wasi_path_create_directory( dir_fd: wasi_unstable::Fd, diff --git a/crates/wasi-common/Cargo.toml b/crates/wasi-common/Cargo.toml index 8793e5dfc3..f9d0f08ec2 100644 --- a/crates/wasi-common/Cargo.toml +++ b/crates/wasi-common/Cargo.toml @@ -24,7 +24,7 @@ num = { version = "0.2.0", default-features = false } wig = { path = "wig" } [target.'cfg(unix)'.dependencies] -nix = "0.15" +yanix = { path = "yanix" } [target.'cfg(windows)'.dependencies] winx = { path = "winx" } diff --git a/crates/wasi-common/WASI b/crates/wasi-common/WASI index 9b298d7911..9150e66a34 160000 --- a/crates/wasi-common/WASI +++ b/crates/wasi-common/WASI @@ -1 +1 @@ -Subproject commit 9b298d7911986788bdfac662ed115ea6701b2c9a +Subproject commit 9150e66a349dff6215b0290ee8f66794fbfa5ea8 diff --git a/crates/wasi-common/src/error.rs b/crates/wasi-common/src/error.rs index e1728e504a..f6fa7be1da 100644 --- a/crates/wasi-common/src/error.rs +++ b/crates/wasi-common/src/error.rs @@ -3,7 +3,7 @@ use crate::wasi; use std::convert::Infallible; use std::num::TryFromIntError; -use std::{fmt, str}; +use std::{ffi, fmt, str}; use thiserror::Error; #[derive(Clone, Copy, Debug, Error, Eq, PartialEq)] @@ -107,7 +107,7 @@ pub enum Error { Wasi(WasiError), Io(std::io::Error), #[cfg(unix)] - Nix(nix::Error), + Nix(yanix::YanixError), #[cfg(windows)] Win(winx::winerror::WinError), } @@ -119,8 +119,8 @@ impl From for Error { } #[cfg(unix)] -impl From for Error { - fn from(err: nix::Error) -> Self { +impl From for Error { + fn from(err: yanix::YanixError) -> Self { Self::Nix(err) } } @@ -149,6 +149,12 @@ impl From for Error { } } +impl From<&ffi::NulError> for Error { + fn from(_: &ffi::NulError) -> Self { + Self::Wasi(WasiError::EILSEQ) + } +} + #[cfg(windows)] impl From for Error { fn from(err: winx::winerror::WinError) -> Self { @@ -162,16 +168,15 @@ impl Error { Self::Wasi(no) => no.as_raw_errno(), Self::Io(e) => errno_from_ioerror(e.to_owned()), #[cfg(unix)] - Self::Nix(err) => err - .as_errno() - .map_or_else( - || { - log::debug!("Unknown nix errno: {}", err); - Self::ENOSYS - }, - crate::sys::host_impl::errno_from_nix, - ) - .as_wasi_errno(), + Self::Nix(err) => { + use yanix::YanixError::*; + let err = match err { + Errno(errno) => crate::sys::host_impl::errno_from_nix(*errno), + NulError(err) => err.into(), + TryFromIntError(err) => (*err).into(), + }; + err.as_wasi_errno() + } #[cfg(windows)] Self::Win(err) => crate::sys::host_impl::errno_from_win(*err), } diff --git a/crates/wasi-common/src/host.rs b/crates/wasi-common/src/host.rs index 4e37d12c40..8af57ec476 100644 --- a/crates/wasi-common/src/host.rs +++ b/crates/wasi-common/src/host.rs @@ -5,7 +5,8 @@ #![allow(non_snake_case)] use crate::wasi::*; -use std::{io, slice}; +use crate::{Error, Result}; +use std::{convert::TryInto, io, mem, slice}; use wig::witx_host_types; witx_host_types!("snapshot" "wasi_snapshot_preview1"); @@ -20,6 +21,64 @@ pub(crate) unsafe fn iovec_to_host_mut(iovec: &mut __wasi_iovec_t) -> io::IoSlic io::IoSliceMut::new(slice) } +#[allow(dead_code)] // trouble with sockets +#[derive(Clone, Copy, Debug)] +#[repr(u8)] +pub(crate) enum FileType { + Unknown = __WASI_FILETYPE_UNKNOWN, + BlockDevice = __WASI_FILETYPE_BLOCK_DEVICE, + CharacterDevice = __WASI_FILETYPE_CHARACTER_DEVICE, + Directory = __WASI_FILETYPE_DIRECTORY, + RegularFile = __WASI_FILETYPE_REGULAR_FILE, + SocketDgram = __WASI_FILETYPE_SOCKET_DGRAM, + SocketStream = __WASI_FILETYPE_SOCKET_STREAM, + Symlink = __WASI_FILETYPE_SYMBOLIC_LINK, +} + +impl FileType { + pub(crate) fn to_wasi(&self) -> __wasi_filetype_t { + *self as __wasi_filetype_t + } +} + +#[derive(Debug, Clone)] +pub(crate) struct Dirent { + pub name: String, + pub ftype: FileType, + pub ino: u64, + pub cookie: __wasi_dircookie_t, +} + +impl Dirent { + /// Serialize the directory entry to the format define by `__wasi_fd_readdir`, + /// so that the serialized entries can be concatenated by the implementation. + pub fn to_wasi_raw(&self) -> Result> { + let name = self.name.as_bytes(); + let namlen = name.len(); + let dirent_size = mem::size_of::<__wasi_dirent_t>(); + let offset = dirent_size.checked_add(namlen).ok_or(Error::EOVERFLOW)?; + + let mut raw = Vec::::with_capacity(offset); + raw.resize(offset, 0); + + let sys_dirent = raw.as_mut_ptr() as *mut __wasi_dirent_t; + unsafe { + *sys_dirent = __wasi_dirent_t { + d_namlen: namlen.try_into()?, + d_ino: self.ino, + d_next: self.cookie, + d_type: self.ftype.to_wasi(), + }; + } + + let sys_name = unsafe { sys_dirent.offset(1) as *mut u8 }; + let sys_name = unsafe { slice::from_raw_parts_mut(sys_name, namlen) }; + sys_name.copy_from_slice(&name); + + Ok(raw) + } +} + #[cfg(test)] mod test { use super::*; diff --git a/crates/wasi-common/src/hostcalls_impl/fs.rs b/crates/wasi-common/src/hostcalls_impl/fs.rs index 8550efb17e..71d84f52ca 100644 --- a/crates/wasi-common/src/hostcalls_impl/fs.rs +++ b/crates/wasi-common/src/hostcalls_impl/fs.rs @@ -10,10 +10,8 @@ use crate::sys::{host_impl, hostcalls_impl}; use crate::{helpers, host, wasi, wasi32, Error, Result}; use filetime::{set_file_handle_times, FileTime}; use log::trace; -use std::convert::TryInto; use std::fs::File; use std::io::{self, Read, Seek, SeekFrom, Write}; -use std::mem; use std::time::{Duration, SystemTime, UNIX_EPOCH}; pub(crate) unsafe fn fd_close(wasi_ctx: &mut WasiCtx, fd: wasi::__wasi_fd_t) -> Result<()> { @@ -1041,63 +1039,3 @@ pub(crate) unsafe fn fd_readdir( enc_usize_byref(memory, buf_used, host_bufused) } - -#[allow(dead_code)] // trouble with sockets -#[derive(Clone, Copy, Debug)] -#[repr(u8)] -pub(crate) enum FileType { - Unknown = wasi::__WASI_FILETYPE_UNKNOWN, - BlockDevice = wasi::__WASI_FILETYPE_BLOCK_DEVICE, - CharacterDevice = wasi::__WASI_FILETYPE_CHARACTER_DEVICE, - Directory = wasi::__WASI_FILETYPE_DIRECTORY, - RegularFile = wasi::__WASI_FILETYPE_REGULAR_FILE, - SocketDgram = wasi::__WASI_FILETYPE_SOCKET_DGRAM, - SocketStream = wasi::__WASI_FILETYPE_SOCKET_STREAM, - Symlink = wasi::__WASI_FILETYPE_SYMBOLIC_LINK, -} - -impl FileType { - pub(crate) fn to_wasi(&self) -> wasi::__wasi_filetype_t { - *self as wasi::__wasi_filetype_t - } -} - -#[derive(Debug, Clone)] -pub(crate) struct Dirent { - pub name: String, - pub ftype: FileType, - pub ino: u64, - pub cookie: wasi::__wasi_dircookie_t, -} - -impl Dirent { - /// Serialize the directory entry to the format define by `__wasi_fd_readdir`, - /// so that the serialized entries can be concatenated by the implementation. - pub fn to_wasi_raw(&self) -> Result> { - use std::slice; - - let name = self.name.as_bytes(); - let namlen = name.len(); - let dirent_size = mem::size_of::(); - let offset = dirent_size.checked_add(namlen).ok_or(Error::EOVERFLOW)?; - - let mut raw = Vec::::with_capacity(offset); - raw.resize(offset, 0); - - let sys_dirent = raw.as_mut_ptr() as *mut wasi::__wasi_dirent_t; - unsafe { - sys_dirent.write_unaligned(wasi::__wasi_dirent_t { - d_namlen: namlen.try_into()?, - d_ino: self.ino, - d_next: self.cookie, - d_type: self.ftype.to_wasi(), - }); - } - - let sys_name = unsafe { sys_dirent.offset(1) as *mut u8 }; - let sys_name = unsafe { slice::from_raw_parts_mut(sys_name, namlen) }; - sys_name.copy_from_slice(&name); - - Ok(raw) - } -} diff --git a/crates/wasi-common/src/old/snapshot_0/error.rs b/crates/wasi-common/src/old/snapshot_0/error.rs index 70566892c3..33dce550f5 100644 --- a/crates/wasi-common/src/old/snapshot_0/error.rs +++ b/crates/wasi-common/src/old/snapshot_0/error.rs @@ -3,7 +3,7 @@ use crate::old::snapshot_0::wasi; use std::convert::Infallible; use std::num::TryFromIntError; -use std::{fmt, str}; +use std::{ffi, fmt, str}; use thiserror::Error; #[derive(Clone, Copy, Debug, Error, Eq, PartialEq)] @@ -107,7 +107,7 @@ pub enum Error { Wasi(WasiError), Io(std::io::Error), #[cfg(unix)] - Nix(nix::Error), + Nix(yanix::YanixError), #[cfg(windows)] Win(winx::winerror::WinError), } @@ -119,8 +119,8 @@ impl From for Error { } #[cfg(unix)] -impl From for Error { - fn from(err: nix::Error) -> Self { +impl From for Error { + fn from(err: yanix::YanixError) -> Self { Self::Nix(err) } } @@ -149,6 +149,12 @@ impl From for Error { } } +impl From<&ffi::NulError> for Error { + fn from(_: &ffi::NulError) -> Self { + Self::Wasi(WasiError::EILSEQ) + } +} + #[cfg(windows)] impl From for Error { fn from(err: winx::winerror::WinError) -> Self { @@ -162,16 +168,15 @@ impl Error { Self::Wasi(no) => no.as_raw_errno(), Self::Io(e) => errno_from_ioerror(e.to_owned()), #[cfg(unix)] - Self::Nix(err) => err - .as_errno() - .map_or_else( - || { - log::debug!("Unknown nix errno: {}", err); - Self::ENOSYS - }, - crate::old::snapshot_0::sys::host_impl::errno_from_nix, - ) - .as_wasi_errno(), + Self::Nix(err) => { + use yanix::YanixError::*; + let err = match err { + Errno(errno) => crate::old::snapshot_0::sys::host_impl::errno_from_nix(*errno), + NulError(err) => err.into(), + TryFromIntError(err) => (*err).into(), + }; + err.as_wasi_errno() + } #[cfg(windows)] Self::Win(err) => crate::old::snapshot_0::sys::host_impl::errno_from_win(*err), } diff --git a/crates/wasi-common/src/old/snapshot_0/host.rs b/crates/wasi-common/src/old/snapshot_0/host.rs index ae6f156b40..87d8eb3845 100644 --- a/crates/wasi-common/src/old/snapshot_0/host.rs +++ b/crates/wasi-common/src/old/snapshot_0/host.rs @@ -5,7 +5,8 @@ #![allow(non_snake_case)] use crate::old::snapshot_0::wasi::*; -use std::{io, slice}; +use crate::old::snapshot_0::{Error, Result}; +use std::{convert::TryInto, io, mem, slice}; use wig::witx_host_types; witx_host_types!("old/snapshot_0" "wasi_unstable"); @@ -20,6 +21,64 @@ pub(crate) unsafe fn iovec_to_host_mut(iovec: &mut __wasi_iovec_t) -> io::IoSlic io::IoSliceMut::new(slice) } +#[allow(dead_code)] // trouble with sockets +#[derive(Clone, Copy, Debug)] +#[repr(u8)] +pub(crate) enum FileType { + Unknown = __WASI_FILETYPE_UNKNOWN, + BlockDevice = __WASI_FILETYPE_BLOCK_DEVICE, + CharacterDevice = __WASI_FILETYPE_CHARACTER_DEVICE, + Directory = __WASI_FILETYPE_DIRECTORY, + RegularFile = __WASI_FILETYPE_REGULAR_FILE, + SocketDgram = __WASI_FILETYPE_SOCKET_DGRAM, + SocketStream = __WASI_FILETYPE_SOCKET_STREAM, + Symlink = __WASI_FILETYPE_SYMBOLIC_LINK, +} + +impl FileType { + pub(crate) fn to_wasi(&self) -> __wasi_filetype_t { + *self as __wasi_filetype_t + } +} + +#[derive(Debug, Clone)] +pub(crate) struct Dirent { + pub name: String, + pub ftype: FileType, + pub ino: u64, + pub cookie: __wasi_dircookie_t, +} + +impl Dirent { + /// Serialize the directory entry to the format define by `__wasi_fd_readdir`, + /// so that the serialized entries can be concatenated by the implementation. + pub fn to_wasi_raw(&self) -> Result> { + let name = self.name.as_bytes(); + let namlen = name.len(); + let dirent_size = mem::size_of::<__wasi_dirent_t>(); + let offset = dirent_size.checked_add(namlen).ok_or(Error::EOVERFLOW)?; + + let mut raw = Vec::::with_capacity(offset); + raw.resize(offset, 0); + + let sys_dirent = raw.as_mut_ptr() as *mut __wasi_dirent_t; + unsafe { + *sys_dirent = __wasi_dirent_t { + d_namlen: namlen.try_into()?, + d_ino: self.ino, + d_next: self.cookie, + d_type: self.ftype.to_wasi(), + }; + } + + let sys_name = unsafe { sys_dirent.offset(1) as *mut u8 }; + let sys_name = unsafe { slice::from_raw_parts_mut(sys_name, namlen) }; + sys_name.copy_from_slice(&name); + + Ok(raw) + } +} + #[cfg(test)] mod test { use super::*; diff --git a/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/fs.rs b/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/fs.rs index 4481dc461f..da51e01f2e 100644 --- a/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/fs.rs +++ b/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/fs.rs @@ -10,10 +10,8 @@ use crate::old::snapshot_0::sys::{host_impl, hostcalls_impl}; use crate::old::snapshot_0::{helpers, host, wasi, wasi32, Error, Result}; use filetime::{set_file_handle_times, FileTime}; use log::trace; -use std::convert::TryInto; use std::fs::File; use std::io::{self, Read, Seek, SeekFrom, Write}; -use std::mem; use std::time::{Duration, SystemTime, UNIX_EPOCH}; pub(crate) unsafe fn fd_close(wasi_ctx: &mut WasiCtx, fd: wasi::__wasi_fd_t) -> Result<()> { @@ -1041,63 +1039,3 @@ pub(crate) unsafe fn fd_readdir( enc_usize_byref(memory, buf_used, host_bufused) } - -#[allow(dead_code)] // trouble with sockets -#[derive(Clone, Copy, Debug)] -#[repr(u8)] -pub(crate) enum FileType { - Unknown = wasi::__WASI_FILETYPE_UNKNOWN, - BlockDevice = wasi::__WASI_FILETYPE_BLOCK_DEVICE, - CharacterDevice = wasi::__WASI_FILETYPE_CHARACTER_DEVICE, - Directory = wasi::__WASI_FILETYPE_DIRECTORY, - RegularFile = wasi::__WASI_FILETYPE_REGULAR_FILE, - SocketDgram = wasi::__WASI_FILETYPE_SOCKET_DGRAM, - SocketStream = wasi::__WASI_FILETYPE_SOCKET_STREAM, - Symlink = wasi::__WASI_FILETYPE_SYMBOLIC_LINK, -} - -impl FileType { - pub(crate) fn to_wasi(&self) -> wasi::__wasi_filetype_t { - *self as wasi::__wasi_filetype_t - } -} - -#[derive(Debug, Clone)] -pub(crate) struct Dirent { - pub name: String, - pub ftype: FileType, - pub ino: u64, - pub cookie: wasi::__wasi_dircookie_t, -} - -impl Dirent { - /// Serialize the directory entry to the format define by `__wasi_fd_readdir`, - /// so that the serialized entries can be concatenated by the implementation. - pub fn to_wasi_raw(&self) -> Result> { - use std::slice; - - let name = self.name.as_bytes(); - let namlen = name.len(); - let dirent_size = mem::size_of::(); - let offset = dirent_size.checked_add(namlen).ok_or(Error::EOVERFLOW)?; - - let mut raw = Vec::::with_capacity(offset); - raw.resize(offset, 0); - - let sys_dirent = raw.as_mut_ptr() as *mut wasi::__wasi_dirent_t; - unsafe { - *sys_dirent = wasi::__wasi_dirent_t { - d_namlen: namlen.try_into()?, - d_ino: self.ino, - d_next: self.cookie, - d_type: self.ftype.to_wasi(), - }; - } - - let sys_name = unsafe { sys_dirent.offset(1) as *mut u8 }; - let sys_name = unsafe { slice::from_raw_parts_mut(sys_name, namlen) }; - sys_name.copy_from_slice(&name); - - Ok(raw) - } -} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/mod.rs b/crates/wasi-common/src/old/snapshot_0/sys/mod.rs index 960c6337e4..2e62d6f1c4 100644 --- a/crates/wasi-common/src/old/snapshot_0/sys/mod.rs +++ b/crates/wasi-common/src/old/snapshot_0/sys/mod.rs @@ -7,7 +7,7 @@ cfg_if! { pub(crate) use self::unix::*; pub(crate) fn errno_from_host(err: i32) -> wasi::__wasi_errno_t { - host_impl::errno_from_nix(nix::errno::from_i32(err)).as_wasi_errno() + host_impl::errno_from_nix(yanix::Errno::from_i32(err)).as_wasi_errno() } } else if #[cfg(windows)] { mod windows; diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/hostcalls_impl.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/hostcalls_impl.rs index edcd0c80db..7db8808ea7 100644 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/hostcalls_impl.rs +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/hostcalls_impl.rs @@ -1,27 +1,22 @@ -use super::super::dir::{Dir, Entry, SeekLoc}; -use super::oshandle::OsHandle; -use crate::old::snapshot_0::hostcalls_impl::{Dirent, PathGet}; +use crate::old::snapshot_0::hostcalls_impl::PathGet; use crate::old::snapshot_0::sys::host_impl; -use crate::old::snapshot_0::sys::unix::str_to_cstring; -use crate::old::snapshot_0::{wasi, Error, Result}; -use nix::libc; -use std::convert::TryInto; -use std::fs::File; +use crate::old::snapshot_0::{Error, Result}; use std::os::unix::prelude::AsRawFd; -use std::sync::MutexGuard; pub(crate) fn path_unlink_file(resolved: PathGet) -> Result<()> { - use nix::errno; - use nix::libc::unlinkat; - - let path_cstr = str_to_cstring(resolved.path())?; - - // nix doesn't expose unlinkat() yet - match unsafe { unlinkat(resolved.dirfd().as_raw_fd(), path_cstr.as_ptr(), 0) } { - 0 => Ok(()), - _ => { - let mut e = errno::Errno::last(); - + use yanix::{ + file::{unlinkat, AtFlag}, + Errno, YanixError, + }; + unsafe { + unlinkat( + resolved.dirfd().as_raw_fd(), + resolved.path(), + AtFlag::empty(), + ) + } + .map_err(|err| { + if let YanixError::Errno(mut errno) = err { // Non-Linux implementations may return EPERM when attempting to remove a // directory without REMOVEDIR. While that's what POSIX specifies, it's // less useful. Adjust this to EISDIR. It doesn't matter that this is not @@ -29,84 +24,84 @@ pub(crate) fn path_unlink_file(resolved: PathGet) -> Result<()> { // is created before fstatat sees it, we're racing with that change anyway // and unlinkat could have legitimately seen the directory if the race had // turned out differently. - use nix::fcntl::AtFlags; - use nix::sys::stat::{fstatat, SFlag}; + use yanix::file::{fstatat, SFlag}; - if e == errno::Errno::EPERM { - if let Ok(stat) = fstatat( - resolved.dirfd().as_raw_fd(), - resolved.path(), - AtFlags::AT_SYMLINK_NOFOLLOW, - ) { - if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::S_IFDIR) { - e = errno::Errno::EISDIR; + if errno == Errno::EPERM { + if let Ok(stat) = unsafe { + fstatat( + resolved.dirfd().as_raw_fd(), + resolved.path(), + AtFlag::SYMLINK_NOFOLLOW, + ) + } { + if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::IFDIR) { + errno = Errno::EISDIR; } } else { - e = errno::Errno::last(); + errno = Errno::last(); } } - - Err(host_impl::errno_from_nix(e)) + errno.into() + } else { + err } - } + }) + .map_err(Into::into) } pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> Result<()> { - use nix::{errno::Errno, fcntl::AtFlags, libc::symlinkat, sys::stat::fstatat}; - - let old_path_cstr = str_to_cstring(old_path)?; - let new_path_cstr = str_to_cstring(resolved.path())?; + use yanix::{ + file::{fstatat, symlinkat, AtFlag}, + Errno, YanixError, + }; log::debug!("path_symlink old_path = {:?}", old_path); log::debug!("path_symlink resolved = {:?}", resolved); - let res = unsafe { - symlinkat( - old_path_cstr.as_ptr(), - resolved.dirfd().as_raw_fd(), - new_path_cstr.as_ptr(), - ) - }; - if res != 0 { - match Errno::last() { - Errno::ENOTDIR => { - // On BSD, symlinkat returns ENOTDIR when it should in fact - // return a EEXIST. It seems that it gets confused with by - // the trailing slash in the target path. Thus, we strip - // the trailing slash and check if the path exists, and - // adjust the error code appropriately. - let new_path = resolved.path().trim_end_matches('/'); - if let Ok(_) = fstatat( - resolved.dirfd().as_raw_fd(), - new_path, - AtFlags::AT_SYMLINK_NOFOLLOW, - ) { - Err(Error::EEXIST) - } else { - Err(Error::ENOTDIR) + unsafe { symlinkat(old_path, resolved.dirfd().as_raw_fd(), resolved.path()) }.or_else(|err| { + if let YanixError::Errno(errno) = err { + match errno { + Errno::ENOTDIR => { + // On BSD, symlinkat returns ENOTDIR when it should in fact + // return a EEXIST. It seems that it gets confused with by + // the trailing slash in the target path. Thus, we strip + // the trailing slash and check if the path exists, and + // adjust the error code appropriately. + let new_path = resolved.path().trim_end_matches('/'); + if let Ok(_) = unsafe { + fstatat( + resolved.dirfd().as_raw_fd(), + new_path, + AtFlag::SYMLINK_NOFOLLOW, + ) + } { + Err(Error::EEXIST) + } else { + Err(Error::ENOTDIR) + } } + x => Err(host_impl::errno_from_nix(x)), } - x => Err(host_impl::errno_from_nix(x)), + } else { + Err(err.into()) } - } else { - Ok(()) - } + }) } pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> Result<()> { - use nix::{errno::Errno, fcntl::AtFlags, libc::renameat, sys::stat::fstatat}; - let old_path_cstr = str_to_cstring(resolved_old.path())?; - let new_path_cstr = str_to_cstring(resolved_new.path())?; - - let res = unsafe { + use yanix::{ + file::{fstatat, renameat, AtFlag}, + Errno, YanixError, + }; + unsafe { renameat( resolved_old.dirfd().as_raw_fd(), - old_path_cstr.as_ptr(), + resolved_old.path(), resolved_new.dirfd().as_raw_fd(), - new_path_cstr.as_ptr(), + resolved_new.path(), ) - }; - if res != 0 { + } + .or_else(|err| { // Currently, this is verified to be correct on macOS, where // ENOENT can be returned in case when we try to rename a file // into a name with a trailing slash. On macOS, if the latter does @@ -116,175 +111,60 @@ pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> Resul // // TODO // Verify on other BSD-based OSes. - match Errno::last() { - Errno::ENOENT => { - // check if the source path exists - if let Ok(_) = fstatat( - resolved_old.dirfd().as_raw_fd(), - resolved_old.path(), - AtFlags::AT_SYMLINK_NOFOLLOW, - ) { - // check if destination contains a trailing slash - if resolved_new.path().contains('/') { - Err(Error::ENOTDIR) + if let YanixError::Errno(errno) = err { + match errno { + Errno::ENOENT => { + // check if the source path exists + if let Ok(_) = unsafe { + fstatat( + resolved_old.dirfd().as_raw_fd(), + resolved_old.path(), + AtFlag::SYMLINK_NOFOLLOW, + ) + } { + // check if destination contains a trailing slash + if resolved_new.path().contains('/') { + Err(Error::ENOTDIR) + } else { + Err(Error::ENOENT) + } } else { Err(Error::ENOENT) } - } else { - Err(Error::ENOENT) } + x => Err(host_impl::errno_from_nix(x)), } - x => Err(host_impl::errno_from_nix(x)), + } else { + Err(err.into()) } - } else { - Ok(()) - } + }) } -#[cfg(any(target_os = "macos", target_os = "ios"))] -pub(crate) fn fd_advise( - file: &File, - advice: wasi::__wasi_advice_t, - offset: wasi::__wasi_filesize_t, - len: wasi::__wasi_filesize_t, -) -> Result<()> { - use nix::errno::Errno; +pub(crate) mod fd_readdir_impl { + use crate::old::snapshot_0::sys::fdentry_impl::OsHandle; + use crate::old::snapshot_0::Result; + use std::sync::{Mutex, MutexGuard}; + use yanix::dir::Dir; - match advice { - wasi::__WASI_ADVICE_DONTNEED => return Ok(()), - // unfortunately, the advisory syscall in macOS doesn't take any flags of this - // sort (unlike on Linux), hence, they are left here as a noop - wasi::__WASI_ADVICE_SEQUENTIAL - | wasi::__WASI_ADVICE_WILLNEED - | wasi::__WASI_ADVICE_NOREUSE - | wasi::__WASI_ADVICE_RANDOM - | wasi::__WASI_ADVICE_NORMAL => {} - _ => return Err(Error::EINVAL), - } - - // From macOS man pages: - // F_RDADVISE Issue an advisory read async with no copy to user. - // - // The F_RDADVISE command operates on the following structure which holds information passed from - // the user to the system: - // - // struct radvisory { - // off_t ra_offset; /* offset into the file */ - // int ra_count; /* size of the read */ - // }; - let advisory = libc::radvisory { - ra_offset: offset.try_into()?, - ra_count: len.try_into()?, - }; - - let res = unsafe { libc::fcntl(file.as_raw_fd(), libc::F_RDADVISE, &advisory) }; - Errno::result(res).map(|_| ()).map_err(Error::from) -} - -// TODO -// It seems that at least some BSDs do support `posix_fadvise`, -// so we should investigate further. -#[cfg(not(any(target_os = "macos", target_os = "ios")))] -pub(crate) fn fd_advise( - _file: &File, - advice: wasi::__wasi_advice_t, - _offset: wasi::__wasi_filesize_t, - _len: wasi::__wasi_filesize_t, -) -> Result<()> { - match advice { - wasi::__WASI_ADVICE_DONTNEED - | wasi::__WASI_ADVICE_SEQUENTIAL - | wasi::__WASI_ADVICE_WILLNEED - | wasi::__WASI_ADVICE_NOREUSE - | wasi::__WASI_ADVICE_RANDOM - | wasi::__WASI_ADVICE_NORMAL => {} - _ => return Err(Error::EINVAL), - } - - Ok(()) -} - -pub(crate) fn fd_readdir<'a>( - os_handle: &'a mut OsHandle, - cookie: wasi::__wasi_dircookie_t, -) -> Result> + 'a> { - use std::sync::Mutex; - - let dir = match os_handle.dir { - Some(ref mut dir) => dir, - None => { - // We need to duplicate the fd, because `opendir(3)`: - // Upon successful return from fdopendir(), the file descriptor is under - // control of the system, and if any attempt is made to close the file - // descriptor, or to modify the state of the associated description other - // than by means of closedir(), readdir(), readdir_r(), or rewinddir(), - // the behaviour is undefined. - let fd = (*os_handle).try_clone()?; - let dir = Dir::from(fd)?; - os_handle.dir.get_or_insert(Mutex::new(dir)) - } - }; - let mut dir = dir.lock().unwrap(); - - // Seek if needed. Unless cookie is wasi::__WASI_DIRCOOKIE_START, - // new items may not be returned to the caller. - if cookie == wasi::__WASI_DIRCOOKIE_START { - log::trace!(" | fd_readdir: doing rewinddir"); - dir.rewind(); - } else { - log::trace!(" | fd_readdir: doing seekdir to {}", cookie); - let loc = unsafe { SeekLoc::from_raw(cookie as i64) }; - dir.seek(loc); - } - - Ok(DirIter(dir).map(|entry| { - let (entry, loc): (Entry, SeekLoc) = entry?; - Ok(Dirent { - name: entry - // TODO can we reuse path_from_host for CStr? - .file_name() - .to_str()? - .to_owned(), - ino: entry.ino(), - ftype: entry.file_type().into(), - // Set cookie manually: - // * on macOS d_seekoff is not set for some reason - // * on FreeBSD d_seekoff doesn't exist; there is d_off but it is - // not equivalent to the value read from telldir call - cookie: loc.to_raw().try_into()?, - }) - })) -} - -struct DirIter<'a>(MutexGuard<'a, Dir>); - -impl<'a> Iterator for DirIter<'a> { - type Item = nix::Result<(Entry, SeekLoc)>; - - fn next(&mut self) -> Option { - use libc::readdir; - use nix::{errno::Errno, Error}; - - unsafe { - let errno = Errno::last(); - let ent = readdir((self.0).0.as_ptr()); - if ent.is_null() { - if errno != Errno::last() { - // TODO This should be verified on different BSD-flavours. - // - // According to 4.3BSD/POSIX.1-2001 man pages, there was an error - // if the errno value has changed at some point during the sequence - // of readdir calls. - Some(Err(Error::last())) - } else { - // Not an error. We've simply reached the end of the stream. - None - } - } else { - let entry = Entry(*ent); - let loc = self.0.tell(); - Some(Ok((entry, loc))) + pub(crate) fn get_dir_from_os_handle<'a>( + os_handle: &'a mut OsHandle, + ) -> Result> { + let dir = match os_handle.dir { + Some(ref mut dir) => dir, + None => { + // We need to duplicate the fd, because `opendir(3)`: + // Upon successful return from fdopendir(), the file descriptor is under + // control of the system, and if any attempt is made to close the file + // descriptor, or to modify the state of the associated description other + // than by means of closedir(), readdir(), readdir_r(), or rewinddir(), + // the behaviour is undefined. + let fd = (*os_handle).try_clone()?; + let dir = Dir::from(fd)?; + os_handle.dir.get_or_insert(Mutex::new(dir)) } - } + }; + // Note that from this point on, until the end of the parent scope (i.e., enclosing this + // function), we're locking the `Dir` member of this `OsHandle`. + Ok(dir.lock().unwrap()) } } diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/mod.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/mod.rs index bdc611b279..7b14ae69ad 100644 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/mod.rs +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/mod.rs @@ -2,36 +2,17 @@ pub(crate) mod filetime; pub(crate) mod hostcalls_impl; pub(crate) mod oshandle; -pub(crate) mod fdentry_impl { - use crate::old::snapshot_0::{sys::host_impl, Result}; - use std::os::unix::prelude::AsRawFd; - - pub(crate) unsafe fn isatty(fd: &impl AsRawFd) -> Result { - let res = libc::isatty(fd.as_raw_fd()); - if res == 1 { - // isatty() returns 1 if fd is an open file descriptor referring to a terminal... - Ok(true) - } else { - // ... otherwise 0 is returned, and errno is set to indicate the error. - match nix::errno::Errno::last() { - nix::errno::Errno::ENOTTY => Ok(false), - x => Err(host_impl::errno_from_nix(x)), - } - } - } -} - pub(crate) mod host_impl { use crate::old::snapshot_0::{wasi, Result}; use std::convert::TryFrom; - pub(crate) const O_RSYNC: nix::fcntl::OFlag = nix::fcntl::OFlag::O_SYNC; + pub(crate) const O_RSYNC: yanix::file::OFlag = yanix::file::OFlag::SYNC; - pub(crate) fn stdev_from_nix(dev: nix::libc::dev_t) -> Result { + pub(crate) fn stdev_from_nix(dev: libc::dev_t) -> Result { wasi::__wasi_device_t::try_from(dev).map_err(Into::into) } - pub(crate) fn stino_from_nix(ino: nix::libc::ino_t) -> Result { + pub(crate) fn stino_from_nix(ino: libc::ino_t) -> Result { wasi::__wasi_device_t::try_from(ino).map_err(Into::into) } } diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/oshandle.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/oshandle.rs index eccc7ab33e..70d1a8c4e8 100644 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/oshandle.rs +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/oshandle.rs @@ -1,8 +1,8 @@ -use super::super::dir::Dir; use std::fs; use std::ops::{Deref, DerefMut}; use std::os::unix::prelude::{AsRawFd, RawFd}; use std::sync::Mutex; +use yanix::dir::Dir; #[derive(Debug)] pub(crate) struct OsHandle { diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/dir.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/dir.rs deleted file mode 100644 index 81bb2e6877..0000000000 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/dir.rs +++ /dev/null @@ -1,174 +0,0 @@ -// Based on src/dir.rs from nix -use crate::old::snapshot_0::hostcalls_impl::FileType; -use libc; -use nix::{Error, Result}; -use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd}; -use std::{ffi, ptr}; - -#[cfg(target_os = "linux")] -use libc::dirent64 as dirent; - -#[cfg(not(target_os = "linux",))] -use libc::dirent; - -/// An open directory. -/// -/// This is a lower-level interface than `std::fs::ReadDir`. Notable differences: -/// * can be opened from a file descriptor (as returned by `openat`, perhaps before knowing -/// if the path represents a file or directory). -/// * implements `AsRawFd`, so it can be passed to `fstat`, `openat`, etc. -/// The file descriptor continues to be owned by the `Dir`, so callers must not keep a `RawFd` -/// after the `Dir` is dropped. -/// * can be iterated through multiple times without closing and reopening the file -/// descriptor. Each iteration rewinds when finished. -/// * returns entries for `.` (current directory) and `..` (parent directory). -/// * returns entries' names as a `CStr` (no allocation or conversion beyond whatever libc -/// does). -#[derive(Clone, Debug, Eq, Hash, PartialEq)] -pub(crate) struct Dir(pub(crate) ptr::NonNull); - -impl Dir { - /// Converts from a descriptor-based object, closing the descriptor on success or failure. - #[inline] - pub(crate) fn from(fd: F) -> Result { - unsafe { Self::from_fd(fd.into_raw_fd()) } - } - - /// Converts from a file descriptor, closing it on success or failure. - unsafe fn from_fd(fd: RawFd) -> Result { - let d = libc::fdopendir(fd); - if d.is_null() { - let e = Error::last(); - libc::close(fd); - return Err(e); - }; - // Always guaranteed to be non-null by the previous check - Ok(Self(ptr::NonNull::new(d).unwrap())) - } - - /// Set the position of the directory stream, see `seekdir(3)`. - #[cfg(not(target_os = "android"))] - pub(crate) fn seek(&mut self, loc: SeekLoc) { - unsafe { libc::seekdir(self.0.as_ptr(), loc.0) } - } - - /// Reset directory stream, see `rewinddir(3)`. - pub(crate) fn rewind(&mut self) { - unsafe { libc::rewinddir(self.0.as_ptr()) } - } - - /// Get the current position in the directory stream. - /// - /// If this location is given to `Dir::seek`, the entries up to the previously returned - /// will be omitted and the iteration will start from the currently pending directory entry. - #[cfg(not(target_os = "android"))] - #[allow(dead_code)] - pub(crate) fn tell(&self) -> SeekLoc { - let loc = unsafe { libc::telldir(self.0.as_ptr()) }; - SeekLoc(loc) - } -} - -// `Dir` is not `Sync`. With the current implementation, it could be, but according to -// https://www.gnu.org/software/libc/manual/html_node/Reading_002fClosing-Directory.html, -// future versions of POSIX are likely to obsolete `readdir_r` and specify that it's unsafe to -// call `readdir` simultaneously from multiple threads. -// -// `Dir` is safe to pass from one thread to another, as it's not reference-counted. -unsafe impl Send for Dir {} - -impl AsRawFd for Dir { - fn as_raw_fd(&self) -> RawFd { - unsafe { libc::dirfd(self.0.as_ptr()) } - } -} - -impl Drop for Dir { - fn drop(&mut self) { - unsafe { libc::closedir(self.0.as_ptr()) }; - } -} - -/// A directory entry, similar to `std::fs::DirEntry`. -/// -/// Note that unlike the std version, this may represent the `.` or `..` entries. -#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] -#[repr(transparent)] -pub(crate) struct Entry(pub(crate) dirent); - -pub(crate) type Type = FileType; - -impl Entry { - /// Returns the inode number (`d_ino`) of the underlying `dirent`. - #[cfg(any( - target_os = "android", - target_os = "emscripten", - target_os = "fuchsia", - target_os = "haiku", - target_os = "ios", - target_os = "l4re", - target_os = "linux", - target_os = "macos", - target_os = "solaris" - ))] - pub(crate) fn ino(&self) -> u64 { - self.0.d_ino.into() - } - - /// Returns the inode number (`d_fileno`) of the underlying `dirent`. - #[cfg(not(any( - target_os = "android", - target_os = "emscripten", - target_os = "fuchsia", - target_os = "haiku", - target_os = "ios", - target_os = "l4re", - target_os = "linux", - target_os = "macos", - target_os = "solaris" - )))] - pub(crate) fn ino(&self) -> u64 { - u64::from(self.0.d_fileno) - } - - /// Returns the bare file name of this directory entry without any other leading path component. - pub(crate) fn file_name(&self) -> &ffi::CStr { - unsafe { ::std::ffi::CStr::from_ptr(self.0.d_name.as_ptr()) } - } - - /// Returns the type of this directory entry, if known. - /// - /// See platform `readdir(3)` or `dirent(5)` manpage for when the file type is known; - /// notably, some Linux filesystems don't implement this. The caller should use `stat` or - /// `fstat` if this returns `None`. - pub(crate) fn file_type(&self) -> FileType { - match self.0.d_type { - libc::DT_CHR => Type::CharacterDevice, - libc::DT_DIR => Type::Directory, - libc::DT_BLK => Type::BlockDevice, - libc::DT_REG => Type::RegularFile, - libc::DT_LNK => Type::Symlink, - /* libc::DT_UNKNOWN | libc::DT_SOCK | libc::DT_FIFO */ _ => Type::Unknown, - } - } - - #[cfg(target_os = "linux")] - pub(crate) fn seek_loc(&self) -> SeekLoc { - unsafe { SeekLoc::from_raw(self.0.d_off) } - } -} - -#[cfg(not(target_os = "android"))] -#[derive(Clone, Copy, Debug)] -pub(crate) struct SeekLoc(libc::c_long); - -#[cfg(not(target_os = "android"))] -impl SeekLoc { - pub(crate) unsafe fn from_raw(loc: i64) -> Self { - Self(loc.into()) - } - - pub(crate) fn to_raw(&self) -> i64 { - self.0.into() - } -} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/fdentry_impl.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/fdentry_impl.rs index 3a628123d7..5adaf22f30 100644 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/fdentry_impl.rs +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/fdentry_impl.rs @@ -8,7 +8,6 @@ use std::os::unix::prelude::{AsRawFd, FileTypeExt, FromRawFd, RawFd}; cfg_if::cfg_if! { if #[cfg(target_os = "linux")] { pub(crate) use super::linux::oshandle::*; - pub(crate) use super::linux::fdentry_impl::*; } else if #[cfg(any( target_os = "macos", target_os = "netbsd", @@ -18,7 +17,6 @@ cfg_if::cfg_if! { target_os = "dragonfly" ))] { pub(crate) use super::bsd::oshandle::*; - pub(crate) use super::bsd::fdentry_impl::*; } } @@ -51,13 +49,12 @@ pub(crate) unsafe fn determine_type_and_access_rights( )> { let (file_type, mut rights_base, rights_inheriting) = determine_type_rights(fd)?; - use nix::fcntl::{fcntl, OFlag, F_GETFL}; - let flags_bits = fcntl(fd.as_raw_fd(), F_GETFL)?; - let flags = OFlag::from_bits_truncate(flags_bits); - let accmode = flags & OFlag::O_ACCMODE; - if accmode == OFlag::O_RDONLY { + use yanix::{fcntl, file::OFlag}; + let flags = fcntl::get_status_flags(fd.as_raw_fd())?; + let accmode = flags & OFlag::ACCMODE; + if accmode == OFlag::RDONLY { rights_base &= !wasi::__WASI_RIGHTS_FD_WRITE; - } else if accmode == OFlag::O_WRONLY { + } else if accmode == OFlag::WRONLY { rights_base &= !wasi::__WASI_RIGHTS_FD_READ; } @@ -85,7 +82,8 @@ pub(crate) unsafe fn determine_type_rights( ) } else if ft.is_char_device() { log::debug!("Host fd {:?} is a char device", fd.as_raw_fd()); - if isatty(fd)? { + use yanix::file::isatty; + if isatty(fd.as_raw_fd())? { ( wasi::__WASI_FILETYPE_CHARACTER_DEVICE, wasi::RIGHTS_TTY_BASE, @@ -114,14 +112,14 @@ pub(crate) unsafe fn determine_type_rights( ) } else if ft.is_socket() { log::debug!("Host fd {:?} is a socket", fd.as_raw_fd()); - use nix::sys::socket; - match socket::getsockopt(fd.as_raw_fd(), socket::sockopt::SockType)? { - socket::SockType::Datagram => ( + use yanix::socket::{get_socket_type, SockType}; + match get_socket_type(fd.as_raw_fd())? { + SockType::Datagram => ( wasi::__WASI_FILETYPE_SOCKET_DGRAM, wasi::RIGHTS_SOCKET_BASE, wasi::RIGHTS_SOCKET_INHERITING, ), - socket::SockType::Stream => ( + SockType::Stream => ( wasi::__WASI_FILETYPE_SOCKET_STREAM, wasi::RIGHTS_SOCKET_BASE, wasi::RIGHTS_SOCKET_INHERITING, diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/host_impl.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/host_impl.rs index 534468c6bd..cd15382587 100644 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/host_impl.rs +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/host_impl.rs @@ -2,11 +2,14 @@ #![allow(non_camel_case_types)] #![allow(non_snake_case)] #![allow(dead_code)] -use crate::old::snapshot_0::hostcalls_impl::FileType; +use crate::old::snapshot_0::host::FileType; use crate::old::snapshot_0::{helpers, wasi, Error, Result}; -use log::warn; use std::ffi::OsStr; use std::os::unix::prelude::OsStrExt; +use yanix::{ + file::{OFlag, SFlag}, + Errno, +}; cfg_if::cfg_if! { if #[cfg(target_os = "linux")] { @@ -23,178 +26,168 @@ cfg_if::cfg_if! { } } -pub(crate) fn errno_from_nix(errno: nix::errno::Errno) -> Error { +pub(crate) fn errno_from_nix(errno: Errno) -> Error { match errno { - nix::errno::Errno::EPERM => Error::EPERM, - nix::errno::Errno::ENOENT => Error::ENOENT, - nix::errno::Errno::ESRCH => Error::ESRCH, - nix::errno::Errno::EINTR => Error::EINTR, - nix::errno::Errno::EIO => Error::EIO, - nix::errno::Errno::ENXIO => Error::ENXIO, - nix::errno::Errno::E2BIG => Error::E2BIG, - nix::errno::Errno::ENOEXEC => Error::ENOEXEC, - nix::errno::Errno::EBADF => Error::EBADF, - nix::errno::Errno::ECHILD => Error::ECHILD, - nix::errno::Errno::EAGAIN => Error::EAGAIN, - nix::errno::Errno::ENOMEM => Error::ENOMEM, - nix::errno::Errno::EACCES => Error::EACCES, - nix::errno::Errno::EFAULT => Error::EFAULT, - nix::errno::Errno::EBUSY => Error::EBUSY, - nix::errno::Errno::EEXIST => Error::EEXIST, - nix::errno::Errno::EXDEV => Error::EXDEV, - nix::errno::Errno::ENODEV => Error::ENODEV, - nix::errno::Errno::ENOTDIR => Error::ENOTDIR, - nix::errno::Errno::EISDIR => Error::EISDIR, - nix::errno::Errno::EINVAL => Error::EINVAL, - nix::errno::Errno::ENFILE => Error::ENFILE, - nix::errno::Errno::EMFILE => Error::EMFILE, - nix::errno::Errno::ENOTTY => Error::ENOTTY, - nix::errno::Errno::ETXTBSY => Error::ETXTBSY, - nix::errno::Errno::EFBIG => Error::EFBIG, - nix::errno::Errno::ENOSPC => Error::ENOSPC, - nix::errno::Errno::ESPIPE => Error::ESPIPE, - nix::errno::Errno::EROFS => Error::EROFS, - nix::errno::Errno::EMLINK => Error::EMLINK, - nix::errno::Errno::EPIPE => Error::EPIPE, - nix::errno::Errno::EDOM => Error::EDOM, - nix::errno::Errno::ERANGE => Error::ERANGE, - nix::errno::Errno::EDEADLK => Error::EDEADLK, - nix::errno::Errno::ENAMETOOLONG => Error::ENAMETOOLONG, - nix::errno::Errno::ENOLCK => Error::ENOLCK, - nix::errno::Errno::ENOSYS => Error::ENOSYS, - nix::errno::Errno::ENOTEMPTY => Error::ENOTEMPTY, - nix::errno::Errno::ELOOP => Error::ELOOP, - nix::errno::Errno::ENOMSG => Error::ENOMSG, - nix::errno::Errno::EIDRM => Error::EIDRM, - nix::errno::Errno::ENOLINK => Error::ENOLINK, - nix::errno::Errno::EPROTO => Error::EPROTO, - nix::errno::Errno::EMULTIHOP => Error::EMULTIHOP, - nix::errno::Errno::EBADMSG => Error::EBADMSG, - nix::errno::Errno::EOVERFLOW => Error::EOVERFLOW, - nix::errno::Errno::EILSEQ => Error::EILSEQ, - nix::errno::Errno::ENOTSOCK => Error::ENOTSOCK, - nix::errno::Errno::EDESTADDRREQ => Error::EDESTADDRREQ, - nix::errno::Errno::EMSGSIZE => Error::EMSGSIZE, - nix::errno::Errno::EPROTOTYPE => Error::EPROTOTYPE, - nix::errno::Errno::ENOPROTOOPT => Error::ENOPROTOOPT, - nix::errno::Errno::EPROTONOSUPPORT => Error::EPROTONOSUPPORT, - nix::errno::Errno::EAFNOSUPPORT => Error::EAFNOSUPPORT, - nix::errno::Errno::EADDRINUSE => Error::EADDRINUSE, - nix::errno::Errno::EADDRNOTAVAIL => Error::EADDRNOTAVAIL, - nix::errno::Errno::ENETDOWN => Error::ENETDOWN, - nix::errno::Errno::ENETUNREACH => Error::ENETUNREACH, - nix::errno::Errno::ENETRESET => Error::ENETRESET, - nix::errno::Errno::ECONNABORTED => Error::ECONNABORTED, - nix::errno::Errno::ECONNRESET => Error::ECONNRESET, - nix::errno::Errno::ENOBUFS => Error::ENOBUFS, - nix::errno::Errno::EISCONN => Error::EISCONN, - nix::errno::Errno::ENOTCONN => Error::ENOTCONN, - nix::errno::Errno::ETIMEDOUT => Error::ETIMEDOUT, - nix::errno::Errno::ECONNREFUSED => Error::ECONNREFUSED, - nix::errno::Errno::EHOSTUNREACH => Error::EHOSTUNREACH, - nix::errno::Errno::EALREADY => Error::EALREADY, - nix::errno::Errno::EINPROGRESS => Error::EINPROGRESS, - nix::errno::Errno::ESTALE => Error::ESTALE, - nix::errno::Errno::EDQUOT => Error::EDQUOT, - nix::errno::Errno::ECANCELED => Error::ECANCELED, - nix::errno::Errno::EOWNERDEAD => Error::EOWNERDEAD, - nix::errno::Errno::ENOTRECOVERABLE => Error::ENOTRECOVERABLE, - other => { - warn!("Unknown error from nix: {}", other); - Error::ENOSYS - } + Errno::EPERM => Error::EPERM, + Errno::ENOENT => Error::ENOENT, + Errno::ESRCH => Error::ESRCH, + Errno::EINTR => Error::EINTR, + Errno::EIO => Error::EIO, + Errno::ENXIO => Error::ENXIO, + Errno::E2BIG => Error::E2BIG, + Errno::ENOEXEC => Error::ENOEXEC, + Errno::EBADF => Error::EBADF, + Errno::ECHILD => Error::ECHILD, + Errno::EAGAIN => Error::EAGAIN, + Errno::ENOMEM => Error::ENOMEM, + Errno::EACCES => Error::EACCES, + Errno::EFAULT => Error::EFAULT, + Errno::EBUSY => Error::EBUSY, + Errno::EEXIST => Error::EEXIST, + Errno::EXDEV => Error::EXDEV, + Errno::ENODEV => Error::ENODEV, + Errno::ENOTDIR => Error::ENOTDIR, + Errno::EISDIR => Error::EISDIR, + Errno::EINVAL => Error::EINVAL, + Errno::ENFILE => Error::ENFILE, + Errno::EMFILE => Error::EMFILE, + Errno::ENOTTY => Error::ENOTTY, + Errno::ETXTBSY => Error::ETXTBSY, + Errno::EFBIG => Error::EFBIG, + Errno::ENOSPC => Error::ENOSPC, + Errno::ESPIPE => Error::ESPIPE, + Errno::EROFS => Error::EROFS, + Errno::EMLINK => Error::EMLINK, + Errno::EPIPE => Error::EPIPE, + Errno::EDOM => Error::EDOM, + Errno::ERANGE => Error::ERANGE, + Errno::EDEADLK => Error::EDEADLK, + Errno::ENAMETOOLONG => Error::ENAMETOOLONG, + Errno::ENOLCK => Error::ENOLCK, + Errno::ENOSYS => Error::ENOSYS, + Errno::ENOTEMPTY => Error::ENOTEMPTY, + Errno::ELOOP => Error::ELOOP, + Errno::ENOMSG => Error::ENOMSG, + Errno::EIDRM => Error::EIDRM, + Errno::ENOLINK => Error::ENOLINK, + Errno::EPROTO => Error::EPROTO, + Errno::EMULTIHOP => Error::EMULTIHOP, + Errno::EBADMSG => Error::EBADMSG, + Errno::EOVERFLOW => Error::EOVERFLOW, + Errno::EILSEQ => Error::EILSEQ, + Errno::ENOTSOCK => Error::ENOTSOCK, + Errno::EDESTADDRREQ => Error::EDESTADDRREQ, + Errno::EMSGSIZE => Error::EMSGSIZE, + Errno::EPROTOTYPE => Error::EPROTOTYPE, + Errno::ENOPROTOOPT => Error::ENOPROTOOPT, + Errno::EPROTONOSUPPORT => Error::EPROTONOSUPPORT, + Errno::EAFNOSUPPORT => Error::EAFNOSUPPORT, + Errno::EADDRINUSE => Error::EADDRINUSE, + Errno::EADDRNOTAVAIL => Error::EADDRNOTAVAIL, + Errno::ENETDOWN => Error::ENETDOWN, + Errno::ENETUNREACH => Error::ENETUNREACH, + Errno::ENETRESET => Error::ENETRESET, + Errno::ECONNABORTED => Error::ECONNABORTED, + Errno::ECONNRESET => Error::ECONNRESET, + Errno::ENOBUFS => Error::ENOBUFS, + Errno::EISCONN => Error::EISCONN, + Errno::ENOTCONN => Error::ENOTCONN, + Errno::ETIMEDOUT => Error::ETIMEDOUT, + Errno::ECONNREFUSED => Error::ECONNREFUSED, + Errno::EHOSTUNREACH => Error::EHOSTUNREACH, + Errno::EALREADY => Error::EALREADY, + Errno::EINPROGRESS => Error::EINPROGRESS, + Errno::ESTALE => Error::ESTALE, + Errno::EDQUOT => Error::EDQUOT, + Errno::ECANCELED => Error::ECANCELED, + Errno::EOWNERDEAD => Error::EOWNERDEAD, + Errno::ENOTRECOVERABLE => Error::ENOTRECOVERABLE, } } -pub(crate) fn nix_from_fdflags(fdflags: wasi::__wasi_fdflags_t) -> nix::fcntl::OFlag { - use nix::fcntl::OFlag; +pub(crate) fn nix_from_fdflags(fdflags: wasi::__wasi_fdflags_t) -> OFlag { let mut nix_flags = OFlag::empty(); if fdflags & wasi::__WASI_FDFLAGS_APPEND != 0 { - nix_flags.insert(OFlag::O_APPEND); + nix_flags.insert(OFlag::APPEND); } if fdflags & wasi::__WASI_FDFLAGS_DSYNC != 0 { - nix_flags.insert(OFlag::O_DSYNC); + nix_flags.insert(OFlag::DSYNC); } if fdflags & wasi::__WASI_FDFLAGS_NONBLOCK != 0 { - nix_flags.insert(OFlag::O_NONBLOCK); + nix_flags.insert(OFlag::NONBLOCK); } if fdflags & wasi::__WASI_FDFLAGS_RSYNC != 0 { nix_flags.insert(O_RSYNC); } if fdflags & wasi::__WASI_FDFLAGS_SYNC != 0 { - nix_flags.insert(OFlag::O_SYNC); + nix_flags.insert(OFlag::SYNC); } nix_flags } -pub(crate) fn fdflags_from_nix(oflags: nix::fcntl::OFlag) -> wasi::__wasi_fdflags_t { - use nix::fcntl::OFlag; +pub(crate) fn fdflags_from_nix(oflags: OFlag) -> wasi::__wasi_fdflags_t { let mut fdflags = 0; - if oflags.contains(OFlag::O_APPEND) { + if oflags.contains(OFlag::APPEND) { fdflags |= wasi::__WASI_FDFLAGS_APPEND; } - if oflags.contains(OFlag::O_DSYNC) { + if oflags.contains(OFlag::DSYNC) { fdflags |= wasi::__WASI_FDFLAGS_DSYNC; } - if oflags.contains(OFlag::O_NONBLOCK) { + if oflags.contains(OFlag::NONBLOCK) { fdflags |= wasi::__WASI_FDFLAGS_NONBLOCK; } if oflags.contains(O_RSYNC) { fdflags |= wasi::__WASI_FDFLAGS_RSYNC; } - if oflags.contains(OFlag::O_SYNC) { + if oflags.contains(OFlag::SYNC) { fdflags |= wasi::__WASI_FDFLAGS_SYNC; } fdflags } -pub(crate) fn nix_from_oflags(oflags: wasi::__wasi_oflags_t) -> nix::fcntl::OFlag { - use nix::fcntl::OFlag; +pub(crate) fn nix_from_oflags(oflags: wasi::__wasi_oflags_t) -> OFlag { let mut nix_flags = OFlag::empty(); if oflags & wasi::__WASI_OFLAGS_CREAT != 0 { - nix_flags.insert(OFlag::O_CREAT); + nix_flags.insert(OFlag::CREAT); } if oflags & wasi::__WASI_OFLAGS_DIRECTORY != 0 { - nix_flags.insert(OFlag::O_DIRECTORY); + nix_flags.insert(OFlag::DIRECTORY); } if oflags & wasi::__WASI_OFLAGS_EXCL != 0 { - nix_flags.insert(OFlag::O_EXCL); + nix_flags.insert(OFlag::EXCL); } if oflags & wasi::__WASI_OFLAGS_TRUNC != 0 { - nix_flags.insert(OFlag::O_TRUNC); + nix_flags.insert(OFlag::TRUNC); } nix_flags } -pub(crate) fn filetype_from_nix(sflags: nix::sys::stat::SFlag) -> FileType { - use nix::sys::stat::SFlag; - if sflags.contains(SFlag::S_IFCHR) { +pub(crate) fn filetype_from_nix(sflags: SFlag) -> FileType { + if sflags.contains(SFlag::IFCHR) { FileType::CharacterDevice - } else if sflags.contains(SFlag::S_IFBLK) { + } else if sflags.contains(SFlag::IFBLK) { FileType::BlockDevice - } else if sflags.contains(SFlag::S_IFSOCK) { + } else if sflags.contains(SFlag::IFSOCK) { FileType::SocketStream - } else if sflags.contains(SFlag::S_IFDIR) { + } else if sflags.contains(SFlag::IFDIR) { FileType::Directory - } else if sflags.contains(SFlag::S_IFREG) { + } else if sflags.contains(SFlag::IFREG) { FileType::RegularFile - } else if sflags.contains(SFlag::S_IFLNK) { + } else if sflags.contains(SFlag::IFLNK) { FileType::Symlink } else { FileType::Unknown } } -pub(crate) fn filestat_from_nix( - filestat: nix::sys::stat::FileStat, -) -> Result { +pub(crate) fn filestat_from_nix(filestat: libc::stat) -> Result { fn filestat_to_timestamp(secs: u64, nsecs: u64) -> Result { secs.checked_mul(1_000_000_000) .and_then(|sec_nsec| sec_nsec.checked_add(nsecs)) .ok_or(Error::EOVERFLOW) } - let filetype = nix::sys::stat::SFlag::from_bits_truncate(filestat.st_mode); + let filetype = SFlag::from_bits_truncate(filestat.st_mode); let dev = stdev_from_nix(filestat.st_dev)?; let ino = stino_from_nix(filestat.st_ino)?; let atim = filestat_to_timestamp(filestat.st_atime as u64, filestat.st_atime_nsec as u64)?; @@ -214,7 +207,7 @@ pub(crate) fn filestat_from_nix( } pub(crate) fn dirent_filetype_from_host( - host_entry: &nix::libc::dirent, + host_entry: &libc::dirent, ) -> Result { match host_entry.d_type { libc::DT_FIFO => Ok(wasi::__WASI_FILETYPE_UNKNOWN), @@ -243,3 +236,17 @@ pub(crate) fn dirent_filetype_from_host( pub(crate) fn path_from_host>(s: S) -> Result { helpers::path_from_slice(s.as_ref().as_bytes()).map(String::from) } + +impl From for FileType { + fn from(ft: yanix::dir::FileType) -> Self { + use yanix::dir::FileType::*; + match ft { + RegularFile => Self::RegularFile, + Symlink => Self::Symlink, + Directory => Self::Directory, + BlockDevice => Self::BlockDevice, + CharacterDevice => Self::CharacterDevice, + /* Unknown | Socket | Fifo */ _ => Self::Unknown, + } + } +} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/fs.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/fs.rs index cafce8ba0f..3cb42f4e72 100644 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/fs.rs +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/fs.rs @@ -1,11 +1,10 @@ #![allow(non_camel_case_types)] #![allow(unused_unsafe)] use crate::old::snapshot_0::helpers::systemtime_to_timestamp; -use crate::old::snapshot_0::hostcalls_impl::{FileType, PathGet}; -use crate::old::snapshot_0::sys::host_impl; -use crate::old::snapshot_0::sys::unix::str_to_cstring; +use crate::old::snapshot_0::host::{Dirent, FileType}; +use crate::old::snapshot_0::hostcalls_impl::PathGet; +use crate::old::snapshot_0::sys::{fdentry_impl::OsHandle, host_impl}; use crate::old::snapshot_0::{wasi, Error, Result}; -use nix::libc; use std::convert::TryInto; use std::fs::{File, Metadata}; use std::os::unix::fs::FileExt; @@ -39,53 +38,61 @@ pub(crate) fn fd_pwrite(file: &File, buf: &[u8], offset: wasi::__wasi_filesize_t } pub(crate) fn fd_fdstat_get(fd: &File) -> Result { - use nix::fcntl::{fcntl, OFlag, F_GETFL}; - match fcntl(fd.as_raw_fd(), F_GETFL).map(OFlag::from_bits_truncate) { - Ok(flags) => Ok(host_impl::fdflags_from_nix(flags)), - Err(e) => Err(host_impl::errno_from_nix(e.as_errno().unwrap())), - } + unsafe { yanix::fcntl::get_status_flags(fd.as_raw_fd()) } + .map(host_impl::fdflags_from_nix) + .map_err(Into::into) } pub(crate) fn fd_fdstat_set_flags(fd: &File, fdflags: wasi::__wasi_fdflags_t) -> Result<()> { - use nix::fcntl::{fcntl, F_SETFL}; let nix_flags = host_impl::nix_from_fdflags(fdflags); - match fcntl(fd.as_raw_fd(), F_SETFL(nix_flags)) { - Ok(_) => Ok(()), - Err(e) => Err(host_impl::errno_from_nix(e.as_errno().unwrap())), - } + unsafe { yanix::fcntl::set_status_flags(fd.as_raw_fd(), nix_flags) }.map_err(Into::into) +} + +pub(crate) fn fd_advise( + file: &File, + advice: wasi::__wasi_advice_t, + offset: wasi::__wasi_filesize_t, + len: wasi::__wasi_filesize_t, +) -> Result<()> { + use yanix::fadvise::{posix_fadvise, PosixFadviseAdvice}; + let offset = offset.try_into()?; + let len = len.try_into()?; + let host_advice = match advice { + wasi::__WASI_ADVICE_DONTNEED => PosixFadviseAdvice::DontNeed, + wasi::__WASI_ADVICE_SEQUENTIAL => PosixFadviseAdvice::Sequential, + wasi::__WASI_ADVICE_WILLNEED => PosixFadviseAdvice::WillNeed, + wasi::__WASI_ADVICE_NOREUSE => PosixFadviseAdvice::NoReuse, + wasi::__WASI_ADVICE_RANDOM => PosixFadviseAdvice::Random, + wasi::__WASI_ADVICE_NORMAL => PosixFadviseAdvice::Normal, + _ => return Err(Error::EINVAL), + }; + unsafe { posix_fadvise(file.as_raw_fd(), offset, len, host_advice) }.map_err(Into::into) } pub(crate) fn path_create_directory(resolved: PathGet) -> Result<()> { - use nix::libc::mkdirat; - let path_cstr = str_to_cstring(resolved.path())?; - // nix doesn't expose mkdirat() yet - match unsafe { mkdirat(resolved.dirfd().as_raw_fd(), path_cstr.as_ptr(), 0o777) } { - 0 => Ok(()), - _ => Err(host_impl::errno_from_nix(nix::errno::Errno::last())), + use yanix::file::{mkdirat, Mode}; + unsafe { + mkdirat( + resolved.dirfd().as_raw_fd(), + resolved.path(), + Mode::from_bits_truncate(0o777), + ) } + .map_err(Into::into) } pub(crate) fn path_link(resolved_old: PathGet, resolved_new: PathGet) -> Result<()> { - use nix::libc::linkat; - let old_path_cstr = str_to_cstring(resolved_old.path())?; - let new_path_cstr = str_to_cstring(resolved_new.path())?; - - // Not setting AT_SYMLINK_FOLLOW fails on most filesystems - let atflags = libc::AT_SYMLINK_FOLLOW; - let res = unsafe { + use yanix::file::{linkat, AtFlag}; + unsafe { linkat( resolved_old.dirfd().as_raw_fd(), - old_path_cstr.as_ptr(), + resolved_old.path(), resolved_new.dirfd().as_raw_fd(), - new_path_cstr.as_ptr(), - atflags, + resolved_new.path(), + AtFlag::SYMLINK_FOLLOW, ) - }; - if res != 0 { - Err(host_impl::errno_from_nix(nix::errno::Errno::last())) - } else { - Ok(()) } + .map_err(Into::into) } pub(crate) fn path_open( @@ -95,20 +102,21 @@ pub(crate) fn path_open( oflags: wasi::__wasi_oflags_t, fs_flags: wasi::__wasi_fdflags_t, ) -> Result { - use nix::errno::Errno; - use nix::fcntl::{openat, AtFlags, OFlag}; - use nix::sys::stat::{fstatat, Mode, SFlag}; + use yanix::{ + file::{fstatat, openat, AtFlag, Mode, OFlag, SFlag}, + Errno, + }; let mut nix_all_oflags = if read && write { - OFlag::O_RDWR + OFlag::RDWR } else if write { - OFlag::O_WRONLY + OFlag::WRONLY } else { - OFlag::O_RDONLY + OFlag::RDONLY }; // on non-Capsicum systems, we always want nofollow - nix_all_oflags.insert(OFlag::O_NOFOLLOW); + nix_all_oflags.insert(OFlag::NOFOLLOW); // convert open flags nix_all_oflags.insert(host_impl::nix_from_oflags(oflags)); @@ -123,54 +131,63 @@ pub(crate) fn path_open( log::debug!("path_open resolved = {:?}", resolved); log::debug!("path_open oflags = {:?}", nix_all_oflags); - let new_fd = match openat( - resolved.dirfd().as_raw_fd(), - resolved.path(), - nix_all_oflags, - Mode::from_bits_truncate(0o666), - ) { + let new_fd = match unsafe { + openat( + resolved.dirfd().as_raw_fd(), + resolved.path(), + nix_all_oflags, + Mode::from_bits_truncate(0o666), + ) + } { Ok(fd) => fd, Err(e) => { - match e.as_errno() { - // Linux returns ENXIO instead of EOPNOTSUPP when opening a socket - Some(Errno::ENXIO) => { - if let Ok(stat) = fstatat( - resolved.dirfd().as_raw_fd(), - resolved.path(), - AtFlags::AT_SYMLINK_NOFOLLOW, - ) { - if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::S_IFSOCK) { - return Err(Error::ENOTSUP); + if let yanix::YanixError::Errno(errno) = e { + match errno { + // Linux returns ENXIO instead of EOPNOTSUPP when opening a socket + Errno::ENXIO => { + if let Ok(stat) = unsafe { + fstatat( + resolved.dirfd().as_raw_fd(), + resolved.path(), + AtFlag::SYMLINK_NOFOLLOW, + ) + } { + if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::IFSOCK) { + return Err(Error::ENOTSUP); + } else { + return Err(Error::ENXIO); + } } else { return Err(Error::ENXIO); } - } else { - return Err(Error::ENXIO); } - } - // Linux returns ENOTDIR instead of ELOOP when using O_NOFOLLOW|O_DIRECTORY - // on a symlink. - Some(Errno::ENOTDIR) - if !(nix_all_oflags & (OFlag::O_NOFOLLOW | OFlag::O_DIRECTORY)).is_empty() => - { - if let Ok(stat) = fstatat( - resolved.dirfd().as_raw_fd(), - resolved.path(), - AtFlags::AT_SYMLINK_NOFOLLOW, - ) { - if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::S_IFLNK) { - return Err(Error::ELOOP); + // Linux returns ENOTDIR instead of ELOOP when using O_NOFOLLOW|O_DIRECTORY + // on a symlink. + Errno::ENOTDIR + if !(nix_all_oflags & (OFlag::NOFOLLOW | OFlag::DIRECTORY)).is_empty() => + { + if let Ok(stat) = unsafe { + fstatat( + resolved.dirfd().as_raw_fd(), + resolved.path(), + AtFlag::SYMLINK_NOFOLLOW, + ) + } { + if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::IFLNK) { + return Err(Error::ELOOP); + } } + return Err(Error::ENOTDIR); } - return Err(Error::ENOTDIR); + // FreeBSD returns EMLINK instead of ELOOP when using O_NOFOLLOW on + // a symlink. + Errno::EMLINK if !(nix_all_oflags & OFlag::NOFOLLOW).is_empty() => { + return Err(Error::ELOOP); + } + errno => return Err(host_impl::errno_from_nix(errno)), } - // FreeBSD returns EMLINK instead of ELOOP when using O_NOFOLLOW on - // a symlink. - Some(Errno::EMLINK) if !(nix_all_oflags & OFlag::O_NOFOLLOW).is_empty() => { - return Err(Error::ELOOP); - } - Some(e) => return Err(host_impl::errno_from_nix(e)), - None => return Err(Error::ENOSYS), + } else { + return Err(e.into()); } } }; @@ -182,34 +199,16 @@ pub(crate) fn path_open( } pub(crate) fn path_readlink(resolved: PathGet, buf: &mut [u8]) -> Result { - use nix::errno::Errno; - let path_cstr = str_to_cstring(resolved.path())?; - - // Linux requires that the buffer size is positive, whereas POSIX does not. - // Use a fake buffer to store the results if the size is zero. - // TODO: instead of using raw libc::readlinkat call here, this should really - // be fixed in `nix` crate - let fakebuf: &mut [u8] = &mut [0]; - let buf_len = buf.len(); - let len = unsafe { - libc::readlinkat( - resolved.dirfd().as_raw_fd(), - path_cstr.as_ptr() as *const libc::c_char, - if buf_len == 0 { - fakebuf.as_mut_ptr() - } else { - buf.as_mut_ptr() - } as *mut libc::c_char, - if buf_len == 0 { fakebuf.len() } else { buf_len }, - ) - }; - - if len < 0 { - Err(host_impl::errno_from_nix(Errno::last())) - } else { - let len = len as usize; - Ok(if len < buf_len { len } else { buf_len }) + use std::cmp::min; + use yanix::file::readlinkat; + let read_link = unsafe { readlinkat(resolved.dirfd().as_raw_fd(), resolved.path()) } + .map_err(Into::into) + .and_then(host_impl::path_from_host)?; + let copy_len = min(read_link.len(), buf.len()); + if copy_len > 0 { + buf[..copy_len].copy_from_slice(&read_link.as_bytes()[..copy_len]); } + Ok(copy_len) } pub(crate) fn fd_filestat_get_impl(file: &std::fs::File) -> Result { @@ -229,8 +228,8 @@ pub(crate) fn fd_filestat_get_impl(file: &std::fs::File) -> Result Result { - use nix::sys::socket::{self, SockType}; use std::os::unix::fs::FileTypeExt; + use yanix::socket::{get_socket_type, SockType}; let ftype = metadata.file_type(); if ftype.is_file() { Ok(FileType::RegularFile) @@ -243,10 +242,7 @@ fn filetype(file: &File, metadata: &Metadata) -> Result { } else if ftype.is_block_device() { Ok(FileType::BlockDevice) } else if ftype.is_socket() { - match socket::getsockopt(file.as_raw_fd(), socket::sockopt::SockType) - .map_err(|err| err.as_errno().unwrap()) - .map_err(host_impl::errno_from_nix)? - { + match unsafe { get_socket_type(file.as_raw_fd())? } { SockType::Datagram => Ok(FileType::SocketDgram), SockType::Stream => Ok(FileType::SocketStream), _ => Ok(FileType::Unknown), @@ -260,17 +256,14 @@ pub(crate) fn path_filestat_get( resolved: PathGet, dirflags: wasi::__wasi_lookupflags_t, ) -> Result { - use nix::fcntl::AtFlags; - use nix::sys::stat::fstatat; - + use yanix::file::{fstatat, AtFlag}; let atflags = match dirflags { - 0 => AtFlags::empty(), - _ => AtFlags::AT_SYMLINK_NOFOLLOW, + 0 => AtFlag::empty(), + _ => AtFlag::SYMLINK_NOFOLLOW, }; - - let filestat = fstatat(resolved.dirfd().as_raw_fd(), resolved.path(), atflags) - .map_err(|err| host_impl::errno_from_nix(err.as_errno().unwrap()))?; - host_impl::filestat_from_nix(filestat) + unsafe { fstatat(resolved.dirfd().as_raw_fd(), resolved.path(), atflags) } + .map_err(Into::into) + .and_then(host_impl::filestat_from_nix) } pub(crate) fn path_filestat_set_times( @@ -321,20 +314,49 @@ pub(crate) fn path_filestat_set_times( } pub(crate) fn path_remove_directory(resolved: PathGet) -> Result<()> { - use nix::errno; - use nix::libc::{unlinkat, AT_REMOVEDIR}; - - let path_cstr = str_to_cstring(resolved.path())?; - - // nix doesn't expose unlinkat() yet - match unsafe { + use yanix::file::{unlinkat, AtFlag}; + unsafe { unlinkat( resolved.dirfd().as_raw_fd(), - path_cstr.as_ptr(), - AT_REMOVEDIR, + resolved.path(), + AtFlag::REMOVEDIR, ) - } { - 0 => Ok(()), - _ => Err(host_impl::errno_from_nix(errno::Errno::last())), } + .map_err(Into::into) +} + +pub(crate) fn fd_readdir<'a>( + os_handle: &'a mut OsHandle, + cookie: wasi::__wasi_dircookie_t, +) -> Result> + 'a> { + use yanix::dir::{DirIter, Entry, EntryExt, SeekLoc}; + + // Get an instance of `Dir`; this is host-specific due to intricasies + // of managing a dir stream between Linux and BSD *nixes + let mut dir = fd_readdir_impl::get_dir_from_os_handle(os_handle)?; + + // Seek if needed. Unless cookie is wasi::__WASI_DIRCOOKIE_START, + // new items may not be returned to the caller. + if cookie == wasi::__WASI_DIRCOOKIE_START { + log::trace!(" | fd_readdir: doing rewinddir"); + dir.rewind(); + } else { + log::trace!(" | fd_readdir: doing seekdir to {}", cookie); + let loc = unsafe { SeekLoc::from_raw(cookie as i64) }; + dir.seek(loc); + } + + Ok(DirIter::new(dir).map(|entry| { + let entry: Entry = entry?; + Ok(Dirent { + name: entry + // TODO can we reuse path_from_host for CStr? + .file_name() + .to_str()? + .to_owned(), + ino: entry.ino(), + ftype: entry.file_type().into(), + cookie: entry.seek_loc().to_raw().try_into()?, + }) + })) } diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/fs_helpers.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/fs_helpers.rs index eb83287599..609e42dc5c 100644 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/fs_helpers.rs +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/fs_helpers.rs @@ -3,6 +3,7 @@ use crate::old::snapshot_0::sys::host_impl; use crate::old::snapshot_0::{wasi, Result}; use std::fs::File; +use yanix::file::OFlag; pub(crate) fn path_open_rights( rights_base: wasi::__wasi_rights_t, @@ -10,27 +11,25 @@ pub(crate) fn path_open_rights( oflags: wasi::__wasi_oflags_t, fs_flags: wasi::__wasi_fdflags_t, ) -> (wasi::__wasi_rights_t, wasi::__wasi_rights_t) { - use nix::fcntl::OFlag; - // which rights are needed on the dirfd? let mut needed_base = wasi::__WASI_RIGHTS_PATH_OPEN; let mut needed_inheriting = rights_base | rights_inheriting; // convert open flags let oflags = host_impl::nix_from_oflags(oflags); - if oflags.contains(OFlag::O_CREAT) { + if oflags.contains(OFlag::CREAT) { needed_base |= wasi::__WASI_RIGHTS_PATH_CREATE_FILE; } - if oflags.contains(OFlag::O_TRUNC) { + if oflags.contains(OFlag::TRUNC) { needed_base |= wasi::__WASI_RIGHTS_PATH_FILESTAT_SET_SIZE; } // convert file descriptor flags let fdflags = host_impl::nix_from_fdflags(fs_flags); - if fdflags.contains(OFlag::O_DSYNC) { + if fdflags.contains(OFlag::DSYNC) { needed_inheriting |= wasi::__WASI_RIGHTS_FD_DATASYNC; } - if fdflags.intersects(host_impl::O_RSYNC | OFlag::O_SYNC) { + if fdflags.intersects(host_impl::O_RSYNC | OFlag::SYNC) { needed_inheriting |= wasi::__WASI_RIGHTS_FD_SYNC; } @@ -38,31 +37,30 @@ pub(crate) fn path_open_rights( } pub(crate) fn openat(dirfd: &File, path: &str) -> Result { - use nix::fcntl::{self, OFlag}; - use nix::sys::stat::Mode; use std::os::unix::prelude::{AsRawFd, FromRawFd}; + use yanix::file::{openat, Mode}; log::debug!("path_get openat path = {:?}", path); - fcntl::openat( - dirfd.as_raw_fd(), - path, - OFlag::O_RDONLY | OFlag::O_DIRECTORY | OFlag::O_NOFOLLOW, - Mode::empty(), - ) + unsafe { + openat( + dirfd.as_raw_fd(), + path, + OFlag::RDONLY | OFlag::DIRECTORY | OFlag::NOFOLLOW, + Mode::empty(), + ) + } .map(|new_fd| unsafe { File::from_raw_fd(new_fd) }) .map_err(Into::into) } pub(crate) fn readlinkat(dirfd: &File, path: &str) -> Result { - use nix::fcntl; use std::os::unix::prelude::AsRawFd; + use yanix::file::readlinkat; log::debug!("path_get readlinkat path = {:?}", path); - let readlink_buf = &mut [0u8; libc::PATH_MAX as usize + 1]; - - fcntl::readlinkat(dirfd.as_raw_fd(), path, readlink_buf) + unsafe { readlinkat(dirfd.as_raw_fd(), path) } .map_err(Into::into) .and_then(host_impl::path_from_host) } diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/misc.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/misc.rs index 43acdf1fd9..ac84e3b7c2 100644 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/misc.rs +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/misc.rs @@ -3,29 +3,22 @@ use crate::old::snapshot_0::hostcalls_impl::{ClockEventData, FdEventData}; use crate::old::snapshot_0::sys::host_impl; use crate::old::snapshot_0::{wasi, Error, Result}; -use nix::libc::{self, c_int}; -use std::mem::MaybeUninit; +use yanix::clock::{clock_getres, ClockId}; -fn wasi_clock_id_to_unix(clock_id: wasi::__wasi_clockid_t) -> Result { - // convert the supported clocks to the libc types, or return EINVAL +fn wasi_clock_id_to_unix(clock_id: wasi::__wasi_clockid_t) -> Result { + // convert the supported clocks to libc types, or return EINVAL match clock_id { - wasi::__WASI_CLOCKID_REALTIME => Ok(libc::CLOCK_REALTIME), - wasi::__WASI_CLOCKID_MONOTONIC => Ok(libc::CLOCK_MONOTONIC), - wasi::__WASI_CLOCKID_PROCESS_CPUTIME_ID => Ok(libc::CLOCK_PROCESS_CPUTIME_ID), - wasi::__WASI_CLOCKID_THREAD_CPUTIME_ID => Ok(libc::CLOCK_THREAD_CPUTIME_ID), + wasi::__WASI_CLOCKID_REALTIME => Ok(ClockId::Realtime), + wasi::__WASI_CLOCKID_MONOTONIC => Ok(ClockId::Monotonic), + wasi::__WASI_CLOCKID_PROCESS_CPUTIME_ID => Ok(ClockId::ProcessCPUTime), + wasi::__WASI_CLOCKID_THREAD_CPUTIME_ID => Ok(ClockId::ThreadCPUTime), _ => Err(Error::EINVAL), } } pub(crate) fn clock_res_get(clock_id: wasi::__wasi_clockid_t) -> Result { let clock_id = wasi_clock_id_to_unix(clock_id)?; - // no `nix` wrapper for clock_getres, so we do it ourselves - let mut timespec = MaybeUninit::::uninit(); - let res = unsafe { libc::clock_getres(clock_id, timespec.as_mut_ptr()) }; - if res != 0 { - return Err(host_impl::errno_from_nix(nix::errno::Errno::last())); - } - let timespec = unsafe { timespec.assume_init() }; + let timespec = clock_getres(clock_id)?; // convert to nanoseconds, returning EOVERFLOW in case of overflow; // this is freelancing a bit from the spec but seems like it'll @@ -46,13 +39,7 @@ pub(crate) fn clock_res_get(clock_id: wasi::__wasi_clockid_t) -> Result Result { let clock_id = wasi_clock_id_to_unix(clock_id)?; - // no `nix` wrapper for clock_getres, so we do it ourselves - let mut timespec = MaybeUninit::::uninit(); - let res = unsafe { libc::clock_gettime(clock_id, timespec.as_mut_ptr()) }; - if res != 0 { - return Err(host_impl::errno_from_nix(nix::errno::Errno::last())); - } - let timespec = unsafe { timespec.assume_init() }; + let timespec = clock_getres(clock_id)?; // convert to nanoseconds, returning EOVERFLOW in case of overflow; this is freelancing a bit // from the spec but seems like it'll be an unusual situation to hit @@ -67,11 +54,11 @@ pub(crate) fn poll_oneoff( fd_events: Vec, events: &mut Vec, ) -> Result<()> { - use nix::{ - errno::Errno, - poll::{poll, PollFd, PollFlags}, - }; use std::{convert::TryInto, os::unix::prelude::AsRawFd}; + use yanix::{ + poll::{poll, PollFd, PollFlags}, + Errno, + }; if fd_events.is_empty() && timeout.is_none() { return Ok(()); @@ -89,13 +76,13 @@ pub(crate) fn poll_oneoff( // events we filtered before. If we get something else here, the code has a serious bug. _ => unreachable!(), }; - PollFd::new(event.descriptor.as_raw_fd(), flags) + unsafe { PollFd::new(event.descriptor.as_raw_fd(), flags) } }) .collect(); let poll_timeout = timeout.map_or(-1, |timeout| { let delay = timeout.delay / 1_000_000; // poll syscall requires delay to expressed in milliseconds - delay.try_into().unwrap_or(c_int::max_value()) + delay.try_into().unwrap_or(libc::c_int::max_value()) }); log::debug!("poll_oneoff poll_timeout = {:?}", poll_timeout); @@ -107,7 +94,7 @@ pub(crate) fn poll_oneoff( } return Err(host_impl::errno_from_nix(Errno::last())); } - Ok(ready) => break ready as usize, + Ok(ready) => break ready, } }; @@ -119,9 +106,6 @@ pub(crate) fn poll_oneoff( }) } -// define the `fionread()` function, equivalent to `ioctl(fd, FIONREAD, *bytes)` -nix::ioctl_read_bad!(fionread, nix::libc::FIONREAD, c_int); - fn poll_oneoff_handle_timeout_event( timeout: ClockEventData, events: &mut Vec, @@ -140,11 +124,11 @@ fn poll_oneoff_handle_timeout_event( } fn poll_oneoff_handle_fd_event<'a>( - ready_events: impl Iterator, nix::poll::PollFd)>, + ready_events: impl Iterator, yanix::poll::PollFd)>, events: &mut Vec, ) -> Result<()> { - use nix::poll::PollFlags; use std::{convert::TryInto, os::unix::prelude::AsRawFd}; + use yanix::{file::fionread, poll::PollFlags}; for (fd_event, poll_fd) in ready_events { log::debug!("poll_oneoff_handle_fd_event fd_event = {:?}", fd_event); @@ -157,10 +141,11 @@ fn poll_oneoff_handle_fd_event<'a>( log::debug!("poll_oneoff_handle_fd_event revents = {:?}", revents); - let mut nbytes = 0; - if fd_event.r#type == wasi::__WASI_EVENTTYPE_FD_READ { - let _ = unsafe { fionread(fd_event.descriptor.as_raw_fd(), &mut nbytes) }; - } + let nbytes = if fd_event.r#type == wasi::__WASI_EVENTTYPE_FD_READ { + unsafe { fionread(fd_event.descriptor.as_raw_fd())? } + } else { + 0 + }; let output_event = if revents.contains(PollFlags::POLLNVAL) { wasi::__wasi_event_t { diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/hostcalls_impl.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/hostcalls_impl.rs index 538eed841c..86b1ec443e 100644 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/hostcalls_impl.rs +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/hostcalls_impl.rs @@ -1,176 +1,60 @@ -use super::super::dir::{Dir, Entry, SeekLoc}; -use crate::old::snapshot_0::hostcalls_impl::{Dirent, PathGet}; -use crate::old::snapshot_0::sys::host_impl; -use crate::old::snapshot_0::sys::unix::str_to_cstring; -use crate::old::snapshot_0::{wasi, Error, Result}; -use log::trace; -use std::convert::TryInto; -use std::fs::File; +use crate::old::snapshot_0::hostcalls_impl::PathGet; +use crate::old::snapshot_0::Result; use std::os::unix::prelude::AsRawFd; pub(crate) fn path_unlink_file(resolved: PathGet) -> Result<()> { - use nix::errno; - use nix::libc::unlinkat; - - let path_cstr = str_to_cstring(resolved.path())?; - - // nix doesn't expose unlinkat() yet - let res = unsafe { unlinkat(resolved.dirfd().as_raw_fd(), path_cstr.as_ptr(), 0) }; - if res == 0 { - Ok(()) - } else { - Err(host_impl::errno_from_nix(errno::Errno::last())) + use yanix::file::{unlinkat, AtFlag}; + unsafe { + unlinkat( + resolved.dirfd().as_raw_fd(), + resolved.path(), + AtFlag::empty(), + ) } + .map_err(Into::into) } pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> Result<()> { - use nix::{errno::Errno, libc::symlinkat}; - - let old_path_cstr = str_to_cstring(old_path)?; - let new_path_cstr = str_to_cstring(resolved.path())?; + use yanix::file::symlinkat; log::debug!("path_symlink old_path = {:?}", old_path); log::debug!("path_symlink resolved = {:?}", resolved); - let res = unsafe { - symlinkat( - old_path_cstr.as_ptr(), - resolved.dirfd().as_raw_fd(), - new_path_cstr.as_ptr(), - ) - }; - if res != 0 { - Err(host_impl::errno_from_nix(Errno::last())) - } else { - Ok(()) - } + unsafe { symlinkat(old_path, resolved.dirfd().as_raw_fd(), resolved.path()) } + .map_err(Into::into) } pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> Result<()> { - use nix::libc::renameat; - let old_path_cstr = str_to_cstring(resolved_old.path())?; - let new_path_cstr = str_to_cstring(resolved_new.path())?; - - let res = unsafe { + use yanix::file::renameat; + unsafe { renameat( resolved_old.dirfd().as_raw_fd(), - old_path_cstr.as_ptr(), + resolved_old.path(), resolved_new.dirfd().as_raw_fd(), - new_path_cstr.as_ptr(), + resolved_new.path(), ) - }; - if res != 0 { - Err(host_impl::errno_from_nix(nix::errno::Errno::last())) - } else { - Ok(()) } + .map_err(Into::into) } -pub(crate) fn fd_readdir( - fd: &File, - cookie: wasi::__wasi_dircookie_t, -) -> Result>> { - // We need to duplicate the fd, because `opendir(3)`: - // After a successful call to fdopendir(), fd is used internally by the implementation, - // and should not otherwise be used by the application. - // `opendir(3p)` also says that it's undefined behavior to - // modify the state of the fd in a different way than by accessing DIR*. - // - // Still, rewinddir will be needed because the two file descriptors - // share progress. But we can safely execute closedir now. - let fd = fd.try_clone()?; - let mut dir = Dir::from(fd)?; +pub(crate) mod fd_readdir_impl { + use crate::old::snapshot_0::sys::fdentry_impl::OsHandle; + use crate::old::snapshot_0::Result; + use yanix::dir::Dir; - // Seek if needed. Unless cookie is wasi::__WASI_DIRCOOKIE_START, - // new items may not be returned to the caller. - // - // According to `opendir(3p)`: - // If a file is removed from or added to the directory after the most recent call - // to opendir() or rewinddir(), whether a subsequent call to readdir() returns an entry - // for that file is unspecified. - if cookie == wasi::__WASI_DIRCOOKIE_START { - trace!(" | fd_readdir: doing rewinddir"); - dir.rewind(); - } else { - trace!(" | fd_readdir: doing seekdir to {}", cookie); - let loc = unsafe { SeekLoc::from_raw(cookie as i64) }; - dir.seek(loc); - } - - Ok(DirIter(dir).map(|entry| { - let entry: Entry = entry?; - Ok(Dirent { - name: entry // TODO can we reuse path_from_host for CStr? - .file_name() - .to_str()? - .to_owned(), - ino: entry.ino(), - ftype: entry.file_type().into(), - cookie: entry.seek_loc().to_raw().try_into()?, - }) - })) -} - -struct DirIter(Dir); - -impl Iterator for DirIter { - type Item = nix::Result; - - fn next(&mut self) -> Option { - use libc::{dirent64, readdir64_r}; - use nix::errno::Errno; - - unsafe { - // Note: POSIX specifies that portable applications should dynamically allocate a - // buffer with room for a `d_name` field of size `pathconf(..., _PC_NAME_MAX)` plus 1 - // for the NUL byte. It doesn't look like the std library does this; it just uses - // fixed-sized buffers (and libc's dirent seems to be sized so this is appropriate). - // Probably fine here too then. - // - // See `impl Iterator for ReadDir` [1] for more details. - // [1] https://github.com/rust-lang/rust/blob/master/src/libstd/sys/unix/fs.rs - let mut ent = std::mem::MaybeUninit::::uninit(); - let mut result = std::ptr::null_mut(); - if let Err(e) = Errno::result(readdir64_r( - (self.0).0.as_ptr(), - ent.as_mut_ptr(), - &mut result, - )) { - return Some(Err(e)); - } - if result.is_null() { - None - } else { - assert_eq!(result, ent.as_mut_ptr(), "readdir_r specification violated"); - Some(Ok(Entry(ent.assume_init()))) - } - } + pub(crate) fn get_dir_from_os_handle(os_handle: &mut OsHandle) -> Result> { + // We need to duplicate the fd, because `opendir(3)`: + // After a successful call to fdopendir(), fd is used internally by the implementation, + // and should not otherwise be used by the application. + // `opendir(3p)` also says that it's undefined behavior to + // modify the state of the fd in a different way than by accessing DIR*. + // + // Still, rewinddir will be needed because the two file descriptors + // share progress. But we can safely execute closedir now. + let fd = os_handle.try_clone()?; + // TODO This doesn't look very clean. Can we do something about it? + // Boxing is needed here in order to satisfy `yanix`'s trait requirement for the `DirIter` + // where `T: Deref`. + Ok(Box::new(Dir::from(fd)?)) } } - -pub(crate) fn fd_advise( - file: &File, - advice: wasi::__wasi_advice_t, - offset: wasi::__wasi_filesize_t, - len: wasi::__wasi_filesize_t, -) -> Result<()> { - { - use nix::fcntl::{posix_fadvise, PosixFadviseAdvice}; - - let offset = offset.try_into()?; - let len = len.try_into()?; - let host_advice = match advice { - wasi::__WASI_ADVICE_DONTNEED => PosixFadviseAdvice::POSIX_FADV_DONTNEED, - wasi::__WASI_ADVICE_SEQUENTIAL => PosixFadviseAdvice::POSIX_FADV_SEQUENTIAL, - wasi::__WASI_ADVICE_WILLNEED => PosixFadviseAdvice::POSIX_FADV_WILLNEED, - wasi::__WASI_ADVICE_NOREUSE => PosixFadviseAdvice::POSIX_FADV_NOREUSE, - wasi::__WASI_ADVICE_RANDOM => PosixFadviseAdvice::POSIX_FADV_RANDOM, - wasi::__WASI_ADVICE_NORMAL => PosixFadviseAdvice::POSIX_FADV_NORMAL, - _ => return Err(Error::EINVAL), - }; - - posix_fadvise(file.as_raw_fd(), offset, len, host_advice)?; - } - - Ok(()) -} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/mod.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/mod.rs index e16410b61e..c177e9d6e4 100644 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/mod.rs +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/mod.rs @@ -2,42 +2,16 @@ pub(crate) mod filetime; pub(crate) mod hostcalls_impl; pub(crate) mod oshandle; -pub(crate) mod fdentry_impl { - use crate::old::snapshot_0::{sys::host_impl, Result}; - use std::os::unix::prelude::AsRawFd; - - pub(crate) unsafe fn isatty(fd: &impl AsRawFd) -> Result { - use nix::errno::Errno; - - let res = libc::isatty(fd.as_raw_fd()); - if res == 1 { - // isatty() returns 1 if fd is an open file descriptor referring to a terminal... - Ok(true) - } else { - // ... otherwise 0 is returned, and errno is set to indicate the error. - match Errno::last() { - // While POSIX specifies ENOTTY if the passed - // fd is *not* a tty, on Linux, some implementations - // may return EINVAL instead. - // - // https://linux.die.net/man/3/isatty - Errno::ENOTTY | Errno::EINVAL => Ok(false), - x => Err(host_impl::errno_from_nix(x)), - } - } - } -} - pub(crate) mod host_impl { use crate::old::snapshot_0::{wasi, Result}; - pub(crate) const O_RSYNC: nix::fcntl::OFlag = nix::fcntl::OFlag::O_RSYNC; + pub(crate) const O_RSYNC: yanix::file::OFlag = yanix::file::OFlag::RSYNC; - pub(crate) fn stdev_from_nix(dev: nix::libc::dev_t) -> Result { + pub(crate) fn stdev_from_nix(dev: libc::dev_t) -> Result { Ok(wasi::__wasi_device_t::from(dev)) } - pub(crate) fn stino_from_nix(ino: nix::libc::ino_t) -> Result { + pub(crate) fn stino_from_nix(ino: libc::ino_t) -> Result { Ok(wasi::__wasi_device_t::from(ino)) } } diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/mod.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/mod.rs index b6288512dd..3a4db31aea 100644 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/mod.rs +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/mod.rs @@ -2,7 +2,6 @@ pub(crate) mod fdentry_impl; pub(crate) mod host_impl; pub(crate) mod hostcalls_impl; -mod dir; mod filetime; #[cfg(any( @@ -17,8 +16,7 @@ mod bsd; #[cfg(target_os = "linux")] mod linux; -use crate::old::snapshot_0::{Error, Result}; -use std::ffi::CString; +use crate::old::snapshot_0::Result; use std::fs::{File, OpenOptions}; pub(crate) fn dev_null() -> Result { @@ -28,7 +26,3 @@ pub(crate) fn dev_null() -> Result { .open("/dev/null") .map_err(Into::into) } - -pub(crate) fn str_to_cstring(s: &str) -> Result { - CString::new(s.as_bytes()).map_err(|_| Error::EILSEQ) -} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/windows/hostcalls_impl/fs.rs b/crates/wasi-common/src/old/snapshot_0/sys/windows/hostcalls_impl/fs.rs index a3c8acee9d..84228c28d9 100644 --- a/crates/wasi-common/src/old/snapshot_0/sys/windows/hostcalls_impl/fs.rs +++ b/crates/wasi-common/src/old/snapshot_0/sys/windows/hostcalls_impl/fs.rs @@ -4,9 +4,8 @@ use super::fs_helpers::*; use crate::old::snapshot_0::ctx::WasiCtx; use crate::old::snapshot_0::fdentry::FdEntry; use crate::old::snapshot_0::helpers::systemtime_to_timestamp; -use crate::old::snapshot_0::hostcalls_impl::{ - fd_filestat_set_times_impl, Dirent, FileType, PathGet, -}; +use crate::old::snapshot_0::host::{Dirent, FileType}; +use crate::old::snapshot_0::hostcalls_impl::{fd_filestat_set_times_impl, PathGet}; use crate::old::snapshot_0::sys::fdentry_impl::determine_type_rights; use crate::old::snapshot_0::sys::host_impl::{self, path_from_host}; use crate::old::snapshot_0::sys::hostcalls_impl::fs_helpers::PathGetExt; diff --git a/crates/wasi-common/src/sys/mod.rs b/crates/wasi-common/src/sys/mod.rs index 972726bd56..db73bf1a78 100644 --- a/crates/wasi-common/src/sys/mod.rs +++ b/crates/wasi-common/src/sys/mod.rs @@ -8,7 +8,7 @@ cfg_if! { pub use self::unix::preopen_dir; pub(crate) fn errno_from_host(err: i32) -> wasi::__wasi_errno_t { - host_impl::errno_from_nix(nix::errno::from_i32(err)).as_wasi_errno() + host_impl::errno_from_nix(yanix::Errno::from_i32(err)).as_wasi_errno() } } else if #[cfg(windows)] { mod windows; diff --git a/crates/wasi-common/src/sys/unix/bsd/hostcalls_impl.rs b/crates/wasi-common/src/sys/unix/bsd/hostcalls_impl.rs index 67194f02ac..015c52da4f 100644 --- a/crates/wasi-common/src/sys/unix/bsd/hostcalls_impl.rs +++ b/crates/wasi-common/src/sys/unix/bsd/hostcalls_impl.rs @@ -1,27 +1,22 @@ -use super::super::dir::{Dir, Entry, SeekLoc}; -use super::oshandle::OsHandle; -use crate::hostcalls_impl::{Dirent, PathGet}; +use crate::hostcalls_impl::PathGet; use crate::sys::host_impl; -use crate::sys::unix::str_to_cstring; -use crate::{wasi, Error, Result}; -use nix::libc; -use std::convert::TryInto; -use std::fs::File; +use crate::{Error, Result}; use std::os::unix::prelude::AsRawFd; -use std::sync::MutexGuard; pub(crate) fn path_unlink_file(resolved: PathGet) -> Result<()> { - use nix::errno; - use nix::libc::unlinkat; - - let path_cstr = str_to_cstring(resolved.path())?; - - // nix doesn't expose unlinkat() yet - match unsafe { unlinkat(resolved.dirfd().as_raw_fd(), path_cstr.as_ptr(), 0) } { - 0 => Ok(()), - _ => { - let mut e = errno::Errno::last(); - + use yanix::{ + file::{unlinkat, AtFlag}, + Errno, YanixError, + }; + unsafe { + unlinkat( + resolved.dirfd().as_raw_fd(), + resolved.path(), + AtFlag::empty(), + ) + } + .map_err(|err| { + if let YanixError::Errno(mut errno) = err { // Non-Linux implementations may return EPERM when attempting to remove a // directory without REMOVEDIR. While that's what POSIX specifies, it's // less useful. Adjust this to EISDIR. It doesn't matter that this is not @@ -29,84 +24,84 @@ pub(crate) fn path_unlink_file(resolved: PathGet) -> Result<()> { // is created before fstatat sees it, we're racing with that change anyway // and unlinkat could have legitimately seen the directory if the race had // turned out differently. - use nix::fcntl::AtFlags; - use nix::sys::stat::{fstatat, SFlag}; + use yanix::file::{fstatat, SFlag}; - if e == errno::Errno::EPERM { - if let Ok(stat) = fstatat( - resolved.dirfd().as_raw_fd(), - resolved.path(), - AtFlags::AT_SYMLINK_NOFOLLOW, - ) { - if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::S_IFDIR) { - e = errno::Errno::EISDIR; + if errno == Errno::EPERM { + if let Ok(stat) = unsafe { + fstatat( + resolved.dirfd().as_raw_fd(), + resolved.path(), + AtFlag::SYMLINK_NOFOLLOW, + ) + } { + if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::IFDIR) { + errno = Errno::EISDIR; } } else { - e = errno::Errno::last(); + errno = Errno::last(); } } - - Err(host_impl::errno_from_nix(e)) + errno.into() + } else { + err } - } + }) + .map_err(Into::into) } pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> Result<()> { - use nix::{errno::Errno, fcntl::AtFlags, libc::symlinkat, sys::stat::fstatat}; - - let old_path_cstr = str_to_cstring(old_path)?; - let new_path_cstr = str_to_cstring(resolved.path())?; + use yanix::{ + file::{fstatat, symlinkat, AtFlag}, + Errno, YanixError, + }; log::debug!("path_symlink old_path = {:?}", old_path); log::debug!("path_symlink resolved = {:?}", resolved); - let res = unsafe { - symlinkat( - old_path_cstr.as_ptr(), - resolved.dirfd().as_raw_fd(), - new_path_cstr.as_ptr(), - ) - }; - if res != 0 { - match Errno::last() { - Errno::ENOTDIR => { - // On BSD, symlinkat returns ENOTDIR when it should in fact - // return a EEXIST. It seems that it gets confused with by - // the trailing slash in the target path. Thus, we strip - // the trailing slash and check if the path exists, and - // adjust the error code appropriately. - let new_path = resolved.path().trim_end_matches('/'); - if let Ok(_) = fstatat( - resolved.dirfd().as_raw_fd(), - new_path, - AtFlags::AT_SYMLINK_NOFOLLOW, - ) { - Err(Error::EEXIST) - } else { - Err(Error::ENOTDIR) + unsafe { symlinkat(old_path, resolved.dirfd().as_raw_fd(), resolved.path()) }.or_else(|err| { + if let YanixError::Errno(errno) = err { + match errno { + Errno::ENOTDIR => { + // On BSD, symlinkat returns ENOTDIR when it should in fact + // return a EEXIST. It seems that it gets confused with by + // the trailing slash in the target path. Thus, we strip + // the trailing slash and check if the path exists, and + // adjust the error code appropriately. + let new_path = resolved.path().trim_end_matches('/'); + if let Ok(_) = unsafe { + fstatat( + resolved.dirfd().as_raw_fd(), + new_path, + AtFlag::SYMLINK_NOFOLLOW, + ) + } { + Err(Error::EEXIST) + } else { + Err(Error::ENOTDIR) + } } + x => Err(host_impl::errno_from_nix(x)), } - x => Err(host_impl::errno_from_nix(x)), + } else { + Err(err.into()) } - } else { - Ok(()) - } + }) } pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> Result<()> { - use nix::{errno::Errno, fcntl::AtFlags, libc::renameat, sys::stat::fstatat}; - let old_path_cstr = str_to_cstring(resolved_old.path())?; - let new_path_cstr = str_to_cstring(resolved_new.path())?; - - let res = unsafe { + use yanix::{ + file::{fstatat, renameat, AtFlag}, + Errno, YanixError, + }; + unsafe { renameat( resolved_old.dirfd().as_raw_fd(), - old_path_cstr.as_ptr(), + resolved_old.path(), resolved_new.dirfd().as_raw_fd(), - new_path_cstr.as_ptr(), + resolved_new.path(), ) - }; - if res != 0 { + } + .or_else(|err| { // Currently, this is verified to be correct on macOS, where // ENOENT can be returned in case when we try to rename a file // into a name with a trailing slash. On macOS, if the latter does @@ -116,175 +111,60 @@ pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> Resul // // TODO // Verify on other BSD-based OSes. - match Errno::last() { - Errno::ENOENT => { - // check if the source path exists - if let Ok(_) = fstatat( - resolved_old.dirfd().as_raw_fd(), - resolved_old.path(), - AtFlags::AT_SYMLINK_NOFOLLOW, - ) { - // check if destination contains a trailing slash - if resolved_new.path().contains('/') { - Err(Error::ENOTDIR) + if let YanixError::Errno(errno) = err { + match errno { + Errno::ENOENT => { + // check if the source path exists + if let Ok(_) = unsafe { + fstatat( + resolved_old.dirfd().as_raw_fd(), + resolved_old.path(), + AtFlag::SYMLINK_NOFOLLOW, + ) + } { + // check if destination contains a trailing slash + if resolved_new.path().contains('/') { + Err(Error::ENOTDIR) + } else { + Err(Error::ENOENT) + } } else { Err(Error::ENOENT) } - } else { - Err(Error::ENOENT) } + x => Err(host_impl::errno_from_nix(x)), } - x => Err(host_impl::errno_from_nix(x)), + } else { + Err(err.into()) } - } else { - Ok(()) - } + }) } -#[cfg(any(target_os = "macos", target_os = "ios"))] -pub(crate) fn fd_advise( - file: &File, - advice: wasi::__wasi_advice_t, - offset: wasi::__wasi_filesize_t, - len: wasi::__wasi_filesize_t, -) -> Result<()> { - use nix::errno::Errno; +pub(crate) mod fd_readdir_impl { + use crate::sys::fdentry_impl::OsHandle; + use crate::Result; + use std::sync::{Mutex, MutexGuard}; + use yanix::dir::Dir; - match advice { - wasi::__WASI_ADVICE_DONTNEED => return Ok(()), - // unfortunately, the advisory syscall in macOS doesn't take any flags of this - // sort (unlike on Linux), hence, they are left here as a noop - wasi::__WASI_ADVICE_SEQUENTIAL - | wasi::__WASI_ADVICE_WILLNEED - | wasi::__WASI_ADVICE_NOREUSE - | wasi::__WASI_ADVICE_RANDOM - | wasi::__WASI_ADVICE_NORMAL => {} - _ => return Err(Error::EINVAL), - } - - // From macOS man pages: - // F_RDADVISE Issue an advisory read async with no copy to user. - // - // The F_RDADVISE command operates on the following structure which holds information passed from - // the user to the system: - // - // struct radvisory { - // off_t ra_offset; /* offset into the file */ - // int ra_count; /* size of the read */ - // }; - let advisory = libc::radvisory { - ra_offset: offset.try_into()?, - ra_count: len.try_into()?, - }; - - let res = unsafe { libc::fcntl(file.as_raw_fd(), libc::F_RDADVISE, &advisory) }; - Errno::result(res).map(|_| ()).map_err(Error::from) -} - -// TODO -// It seems that at least some BSDs do support `posix_fadvise`, -// so we should investigate further. -#[cfg(not(any(target_os = "macos", target_os = "ios")))] -pub(crate) fn fd_advise( - _file: &File, - advice: wasi::__wasi_advice_t, - _offset: wasi::__wasi_filesize_t, - _len: wasi::__wasi_filesize_t, -) -> Result<()> { - match advice { - wasi::__WASI_ADVICE_DONTNEED - | wasi::__WASI_ADVICE_SEQUENTIAL - | wasi::__WASI_ADVICE_WILLNEED - | wasi::__WASI_ADVICE_NOREUSE - | wasi::__WASI_ADVICE_RANDOM - | wasi::__WASI_ADVICE_NORMAL => {} - _ => return Err(Error::EINVAL), - } - - Ok(()) -} - -pub(crate) fn fd_readdir<'a>( - os_handle: &'a mut OsHandle, - cookie: wasi::__wasi_dircookie_t, -) -> Result> + 'a> { - use std::sync::Mutex; - - let dir = match os_handle.dir { - Some(ref mut dir) => dir, - None => { - // We need to duplicate the fd, because `opendir(3)`: - // Upon successful return from fdopendir(), the file descriptor is under - // control of the system, and if any attempt is made to close the file - // descriptor, or to modify the state of the associated description other - // than by means of closedir(), readdir(), readdir_r(), or rewinddir(), - // the behaviour is undefined. - let fd = (*os_handle).try_clone()?; - let dir = Dir::from(fd)?; - os_handle.dir.get_or_insert(Mutex::new(dir)) - } - }; - let mut dir = dir.lock().unwrap(); - - // Seek if needed. Unless cookie is wasi::__WASI_DIRCOOKIE_START, - // new items may not be returned to the caller. - if cookie == wasi::__WASI_DIRCOOKIE_START { - log::trace!(" | fd_readdir: doing rewinddir"); - dir.rewind(); - } else { - log::trace!(" | fd_readdir: doing seekdir to {}", cookie); - let loc = unsafe { SeekLoc::from_raw(cookie as i64) }; - dir.seek(loc); - } - - Ok(DirIter(dir).map(|entry| { - let (entry, loc): (Entry, SeekLoc) = entry?; - Ok(Dirent { - name: entry - // TODO can we reuse path_from_host for CStr? - .file_name() - .to_str()? - .to_owned(), - ino: entry.ino(), - ftype: entry.file_type().into(), - // Set cookie manually: - // * on macOS d_seekoff is not set for some reason - // * on FreeBSD d_seekoff doesn't exist; there is d_off but it is - // not equivalent to the value read from telldir call - cookie: loc.to_raw().try_into()?, - }) - })) -} - -struct DirIter<'a>(MutexGuard<'a, Dir>); - -impl<'a> Iterator for DirIter<'a> { - type Item = nix::Result<(Entry, SeekLoc)>; - - fn next(&mut self) -> Option { - use libc::readdir; - use nix::{errno::Errno, Error}; - - unsafe { - let errno = Errno::last(); - let ent = readdir((self.0).0.as_ptr()); - if ent.is_null() { - if errno != Errno::last() { - // TODO This should be verified on different BSD-flavours. - // - // According to 4.3BSD/POSIX.1-2001 man pages, there was an error - // if the errno value has changed at some point during the sequence - // of readdir calls. - Some(Err(Error::last())) - } else { - // Not an error. We've simply reached the end of the stream. - None - } - } else { - let entry = Entry(*ent); - let loc = self.0.tell(); - Some(Ok((entry, loc))) + pub(crate) fn get_dir_from_os_handle<'a>( + os_handle: &'a mut OsHandle, + ) -> Result> { + let dir = match os_handle.dir { + Some(ref mut dir) => dir, + None => { + // We need to duplicate the fd, because `opendir(3)`: + // Upon successful return from fdopendir(), the file descriptor is under + // control of the system, and if any attempt is made to close the file + // descriptor, or to modify the state of the associated description other + // than by means of closedir(), readdir(), readdir_r(), or rewinddir(), + // the behaviour is undefined. + let fd = (*os_handle).try_clone()?; + let dir = Dir::from(fd)?; + os_handle.dir.get_or_insert(Mutex::new(dir)) } - } + }; + // Note that from this point on, until the end of the parent scope (i.e., enclosing this + // function), we're locking the `Dir` member of this `OsHandle`. + Ok(dir.lock().unwrap()) } } diff --git a/crates/wasi-common/src/sys/unix/bsd/mod.rs b/crates/wasi-common/src/sys/unix/bsd/mod.rs index 4fe76b1be7..bdb8dc6f68 100644 --- a/crates/wasi-common/src/sys/unix/bsd/mod.rs +++ b/crates/wasi-common/src/sys/unix/bsd/mod.rs @@ -2,36 +2,17 @@ pub(crate) mod filetime; pub(crate) mod hostcalls_impl; pub(crate) mod oshandle; -pub(crate) mod fdentry_impl { - use crate::{sys::host_impl, Result}; - use std::os::unix::prelude::AsRawFd; - - pub(crate) unsafe fn isatty(fd: &impl AsRawFd) -> Result { - let res = libc::isatty(fd.as_raw_fd()); - if res == 1 { - // isatty() returns 1 if fd is an open file descriptor referring to a terminal... - Ok(true) - } else { - // ... otherwise 0 is returned, and errno is set to indicate the error. - match nix::errno::Errno::last() { - nix::errno::Errno::ENOTTY => Ok(false), - x => Err(host_impl::errno_from_nix(x)), - } - } - } -} - pub(crate) mod host_impl { use crate::{wasi, Result}; use std::convert::TryFrom; - pub(crate) const O_RSYNC: nix::fcntl::OFlag = nix::fcntl::OFlag::O_SYNC; + pub(crate) const O_RSYNC: yanix::file::OFlag = yanix::file::OFlag::SYNC; - pub(crate) fn stdev_from_nix(dev: nix::libc::dev_t) -> Result { + pub(crate) fn stdev_from_nix(dev: libc::dev_t) -> Result { wasi::__wasi_device_t::try_from(dev).map_err(Into::into) } - pub(crate) fn stino_from_nix(ino: nix::libc::ino_t) -> Result { + pub(crate) fn stino_from_nix(ino: libc::ino_t) -> Result { wasi::__wasi_device_t::try_from(ino).map_err(Into::into) } } diff --git a/crates/wasi-common/src/sys/unix/bsd/oshandle.rs b/crates/wasi-common/src/sys/unix/bsd/oshandle.rs index eccc7ab33e..70d1a8c4e8 100644 --- a/crates/wasi-common/src/sys/unix/bsd/oshandle.rs +++ b/crates/wasi-common/src/sys/unix/bsd/oshandle.rs @@ -1,8 +1,8 @@ -use super::super::dir::Dir; use std::fs; use std::ops::{Deref, DerefMut}; use std::os::unix::prelude::{AsRawFd, RawFd}; use std::sync::Mutex; +use yanix::dir::Dir; #[derive(Debug)] pub(crate) struct OsHandle { diff --git a/crates/wasi-common/src/sys/unix/dir.rs b/crates/wasi-common/src/sys/unix/dir.rs deleted file mode 100644 index e3612dea1c..0000000000 --- a/crates/wasi-common/src/sys/unix/dir.rs +++ /dev/null @@ -1,174 +0,0 @@ -// Based on src/dir.rs from nix -use crate::hostcalls_impl::FileType; -use libc; -use nix::{Error, Result}; -use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd}; -use std::{ffi, ptr}; - -#[cfg(target_os = "linux")] -use libc::dirent64 as dirent; - -#[cfg(not(target_os = "linux",))] -use libc::dirent; - -/// An open directory. -/// -/// This is a lower-level interface than `std::fs::ReadDir`. Notable differences: -/// * can be opened from a file descriptor (as returned by `openat`, perhaps before knowing -/// if the path represents a file or directory). -/// * implements `AsRawFd`, so it can be passed to `fstat`, `openat`, etc. -/// The file descriptor continues to be owned by the `Dir`, so callers must not keep a `RawFd` -/// after the `Dir` is dropped. -/// * can be iterated through multiple times without closing and reopening the file -/// descriptor. Each iteration rewinds when finished. -/// * returns entries for `.` (current directory) and `..` (parent directory). -/// * returns entries' names as a `CStr` (no allocation or conversion beyond whatever libc -/// does). -#[derive(Clone, Debug, Eq, Hash, PartialEq)] -pub(crate) struct Dir(pub(crate) ptr::NonNull); - -impl Dir { - /// Converts from a descriptor-based object, closing the descriptor on success or failure. - #[inline] - pub(crate) fn from(fd: F) -> Result { - unsafe { Self::from_fd(fd.into_raw_fd()) } - } - - /// Converts from a file descriptor, closing it on success or failure. - unsafe fn from_fd(fd: RawFd) -> Result { - let d = libc::fdopendir(fd); - if d.is_null() { - let e = Error::last(); - libc::close(fd); - return Err(e); - }; - // Always guaranteed to be non-null by the previous check - Ok(Self(ptr::NonNull::new(d).unwrap())) - } - - /// Set the position of the directory stream, see `seekdir(3)`. - #[cfg(not(target_os = "android"))] - pub(crate) fn seek(&mut self, loc: SeekLoc) { - unsafe { libc::seekdir(self.0.as_ptr(), loc.0) } - } - - /// Reset directory stream, see `rewinddir(3)`. - pub(crate) fn rewind(&mut self) { - unsafe { libc::rewinddir(self.0.as_ptr()) } - } - - /// Get the current position in the directory stream. - /// - /// If this location is given to `Dir::seek`, the entries up to the previously returned - /// will be omitted and the iteration will start from the currently pending directory entry. - #[cfg(not(target_os = "android"))] - #[allow(dead_code)] - pub(crate) fn tell(&self) -> SeekLoc { - let loc = unsafe { libc::telldir(self.0.as_ptr()) }; - SeekLoc(loc) - } -} - -// `Dir` is not `Sync`. With the current implementation, it could be, but according to -// https://www.gnu.org/software/libc/manual/html_node/Reading_002fClosing-Directory.html, -// future versions of POSIX are likely to obsolete `readdir_r` and specify that it's unsafe to -// call `readdir` simultaneously from multiple threads. -// -// `Dir` is safe to pass from one thread to another, as it's not reference-counted. -unsafe impl Send for Dir {} - -impl AsRawFd for Dir { - fn as_raw_fd(&self) -> RawFd { - unsafe { libc::dirfd(self.0.as_ptr()) } - } -} - -impl Drop for Dir { - fn drop(&mut self) { - unsafe { libc::closedir(self.0.as_ptr()) }; - } -} - -/// A directory entry, similar to `std::fs::DirEntry`. -/// -/// Note that unlike the std version, this may represent the `.` or `..` entries. -#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] -#[repr(transparent)] -pub(crate) struct Entry(pub(crate) dirent); - -pub(crate) type Type = FileType; - -impl Entry { - /// Returns the inode number (`d_ino`) of the underlying `dirent`. - #[cfg(any( - target_os = "android", - target_os = "emscripten", - target_os = "fuchsia", - target_os = "haiku", - target_os = "ios", - target_os = "l4re", - target_os = "linux", - target_os = "macos", - target_os = "solaris" - ))] - pub(crate) fn ino(&self) -> u64 { - self.0.d_ino.into() - } - - /// Returns the inode number (`d_fileno`) of the underlying `dirent`. - #[cfg(not(any( - target_os = "android", - target_os = "emscripten", - target_os = "fuchsia", - target_os = "haiku", - target_os = "ios", - target_os = "l4re", - target_os = "linux", - target_os = "macos", - target_os = "solaris" - )))] - pub(crate) fn ino(&self) -> u64 { - u64::from(self.0.d_fileno) - } - - /// Returns the bare file name of this directory entry without any other leading path component. - pub(crate) fn file_name(&self) -> &ffi::CStr { - unsafe { ::std::ffi::CStr::from_ptr(self.0.d_name.as_ptr()) } - } - - /// Returns the type of this directory entry, if known. - /// - /// See platform `readdir(3)` or `dirent(5)` manpage for when the file type is known; - /// notably, some Linux filesystems don't implement this. The caller should use `stat` or - /// `fstat` if this returns `None`. - pub(crate) fn file_type(&self) -> FileType { - match self.0.d_type { - libc::DT_CHR => Type::CharacterDevice, - libc::DT_DIR => Type::Directory, - libc::DT_BLK => Type::BlockDevice, - libc::DT_REG => Type::RegularFile, - libc::DT_LNK => Type::Symlink, - /* libc::DT_UNKNOWN | libc::DT_SOCK | libc::DT_FIFO */ _ => Type::Unknown, - } - } - - #[cfg(target_os = "linux")] - pub(crate) fn seek_loc(&self) -> SeekLoc { - unsafe { SeekLoc::from_raw(self.0.d_off) } - } -} - -#[cfg(not(target_os = "android"))] -#[derive(Clone, Copy, Debug)] -pub(crate) struct SeekLoc(libc::c_long); - -#[cfg(not(target_os = "android"))] -impl SeekLoc { - pub(crate) unsafe fn from_raw(loc: i64) -> Self { - Self(loc.into()) - } - - pub(crate) fn to_raw(&self) -> i64 { - self.0.into() - } -} diff --git a/crates/wasi-common/src/sys/unix/fdentry_impl.rs b/crates/wasi-common/src/sys/unix/fdentry_impl.rs index 8015eebf6f..b178fcb38e 100644 --- a/crates/wasi-common/src/sys/unix/fdentry_impl.rs +++ b/crates/wasi-common/src/sys/unix/fdentry_impl.rs @@ -8,7 +8,6 @@ use std::os::unix::prelude::{AsRawFd, FileTypeExt, FromRawFd, RawFd}; cfg_if::cfg_if! { if #[cfg(target_os = "linux")] { pub(crate) use super::linux::oshandle::*; - pub(crate) use super::linux::fdentry_impl::*; } else if #[cfg(any( target_os = "macos", target_os = "netbsd", @@ -18,7 +17,6 @@ cfg_if::cfg_if! { target_os = "dragonfly" ))] { pub(crate) use super::bsd::oshandle::*; - pub(crate) use super::bsd::fdentry_impl::*; } } @@ -51,13 +49,12 @@ pub(crate) unsafe fn determine_type_and_access_rights( )> { let (file_type, mut rights_base, rights_inheriting) = determine_type_rights(fd)?; - use nix::fcntl::{fcntl, OFlag, F_GETFL}; - let flags_bits = fcntl(fd.as_raw_fd(), F_GETFL)?; - let flags = OFlag::from_bits_truncate(flags_bits); - let accmode = flags & OFlag::O_ACCMODE; - if accmode == OFlag::O_RDONLY { + use yanix::{fcntl, file::OFlag}; + let flags = fcntl::get_status_flags(fd.as_raw_fd())?; + let accmode = flags & OFlag::ACCMODE; + if accmode == OFlag::RDONLY { rights_base &= !wasi::__WASI_RIGHTS_FD_WRITE; - } else if accmode == OFlag::O_WRONLY { + } else if accmode == OFlag::WRONLY { rights_base &= !wasi::__WASI_RIGHTS_FD_READ; } @@ -85,7 +82,8 @@ pub(crate) unsafe fn determine_type_rights( ) } else if ft.is_char_device() { log::debug!("Host fd {:?} is a char device", fd.as_raw_fd()); - if isatty(fd)? { + use yanix::file::isatty; + if isatty(fd.as_raw_fd())? { ( wasi::__WASI_FILETYPE_CHARACTER_DEVICE, wasi::RIGHTS_TTY_BASE, @@ -114,14 +112,14 @@ pub(crate) unsafe fn determine_type_rights( ) } else if ft.is_socket() { log::debug!("Host fd {:?} is a socket", fd.as_raw_fd()); - use nix::sys::socket; - match socket::getsockopt(fd.as_raw_fd(), socket::sockopt::SockType)? { - socket::SockType::Datagram => ( + use yanix::socket::{get_socket_type, SockType}; + match get_socket_type(fd.as_raw_fd())? { + SockType::Datagram => ( wasi::__WASI_FILETYPE_SOCKET_DGRAM, wasi::RIGHTS_SOCKET_BASE, wasi::RIGHTS_SOCKET_INHERITING, ), - socket::SockType::Stream => ( + SockType::Stream => ( wasi::__WASI_FILETYPE_SOCKET_STREAM, wasi::RIGHTS_SOCKET_BASE, wasi::RIGHTS_SOCKET_INHERITING, diff --git a/crates/wasi-common/src/sys/unix/host_impl.rs b/crates/wasi-common/src/sys/unix/host_impl.rs index 9807cda5f2..41306837b0 100644 --- a/crates/wasi-common/src/sys/unix/host_impl.rs +++ b/crates/wasi-common/src/sys/unix/host_impl.rs @@ -2,11 +2,14 @@ #![allow(non_camel_case_types)] #![allow(non_snake_case)] #![allow(dead_code)] -use crate::hostcalls_impl::FileType; +use crate::host::FileType; use crate::{helpers, wasi, Error, Result}; -use log::warn; use std::ffi::OsStr; use std::os::unix::prelude::OsStrExt; +use yanix::{ + file::{OFlag, SFlag}, + Errno, +}; cfg_if::cfg_if! { if #[cfg(target_os = "linux")] { @@ -23,178 +26,168 @@ cfg_if::cfg_if! { } } -pub(crate) fn errno_from_nix(errno: nix::errno::Errno) -> Error { +pub(crate) fn errno_from_nix(errno: Errno) -> Error { match errno { - nix::errno::Errno::EPERM => Error::EPERM, - nix::errno::Errno::ENOENT => Error::ENOENT, - nix::errno::Errno::ESRCH => Error::ESRCH, - nix::errno::Errno::EINTR => Error::EINTR, - nix::errno::Errno::EIO => Error::EIO, - nix::errno::Errno::ENXIO => Error::ENXIO, - nix::errno::Errno::E2BIG => Error::E2BIG, - nix::errno::Errno::ENOEXEC => Error::ENOEXEC, - nix::errno::Errno::EBADF => Error::EBADF, - nix::errno::Errno::ECHILD => Error::ECHILD, - nix::errno::Errno::EAGAIN => Error::EAGAIN, - nix::errno::Errno::ENOMEM => Error::ENOMEM, - nix::errno::Errno::EACCES => Error::EACCES, - nix::errno::Errno::EFAULT => Error::EFAULT, - nix::errno::Errno::EBUSY => Error::EBUSY, - nix::errno::Errno::EEXIST => Error::EEXIST, - nix::errno::Errno::EXDEV => Error::EXDEV, - nix::errno::Errno::ENODEV => Error::ENODEV, - nix::errno::Errno::ENOTDIR => Error::ENOTDIR, - nix::errno::Errno::EISDIR => Error::EISDIR, - nix::errno::Errno::EINVAL => Error::EINVAL, - nix::errno::Errno::ENFILE => Error::ENFILE, - nix::errno::Errno::EMFILE => Error::EMFILE, - nix::errno::Errno::ENOTTY => Error::ENOTTY, - nix::errno::Errno::ETXTBSY => Error::ETXTBSY, - nix::errno::Errno::EFBIG => Error::EFBIG, - nix::errno::Errno::ENOSPC => Error::ENOSPC, - nix::errno::Errno::ESPIPE => Error::ESPIPE, - nix::errno::Errno::EROFS => Error::EROFS, - nix::errno::Errno::EMLINK => Error::EMLINK, - nix::errno::Errno::EPIPE => Error::EPIPE, - nix::errno::Errno::EDOM => Error::EDOM, - nix::errno::Errno::ERANGE => Error::ERANGE, - nix::errno::Errno::EDEADLK => Error::EDEADLK, - nix::errno::Errno::ENAMETOOLONG => Error::ENAMETOOLONG, - nix::errno::Errno::ENOLCK => Error::ENOLCK, - nix::errno::Errno::ENOSYS => Error::ENOSYS, - nix::errno::Errno::ENOTEMPTY => Error::ENOTEMPTY, - nix::errno::Errno::ELOOP => Error::ELOOP, - nix::errno::Errno::ENOMSG => Error::ENOMSG, - nix::errno::Errno::EIDRM => Error::EIDRM, - nix::errno::Errno::ENOLINK => Error::ENOLINK, - nix::errno::Errno::EPROTO => Error::EPROTO, - nix::errno::Errno::EMULTIHOP => Error::EMULTIHOP, - nix::errno::Errno::EBADMSG => Error::EBADMSG, - nix::errno::Errno::EOVERFLOW => Error::EOVERFLOW, - nix::errno::Errno::EILSEQ => Error::EILSEQ, - nix::errno::Errno::ENOTSOCK => Error::ENOTSOCK, - nix::errno::Errno::EDESTADDRREQ => Error::EDESTADDRREQ, - nix::errno::Errno::EMSGSIZE => Error::EMSGSIZE, - nix::errno::Errno::EPROTOTYPE => Error::EPROTOTYPE, - nix::errno::Errno::ENOPROTOOPT => Error::ENOPROTOOPT, - nix::errno::Errno::EPROTONOSUPPORT => Error::EPROTONOSUPPORT, - nix::errno::Errno::EAFNOSUPPORT => Error::EAFNOSUPPORT, - nix::errno::Errno::EADDRINUSE => Error::EADDRINUSE, - nix::errno::Errno::EADDRNOTAVAIL => Error::EADDRNOTAVAIL, - nix::errno::Errno::ENETDOWN => Error::ENETDOWN, - nix::errno::Errno::ENETUNREACH => Error::ENETUNREACH, - nix::errno::Errno::ENETRESET => Error::ENETRESET, - nix::errno::Errno::ECONNABORTED => Error::ECONNABORTED, - nix::errno::Errno::ECONNRESET => Error::ECONNRESET, - nix::errno::Errno::ENOBUFS => Error::ENOBUFS, - nix::errno::Errno::EISCONN => Error::EISCONN, - nix::errno::Errno::ENOTCONN => Error::ENOTCONN, - nix::errno::Errno::ETIMEDOUT => Error::ETIMEDOUT, - nix::errno::Errno::ECONNREFUSED => Error::ECONNREFUSED, - nix::errno::Errno::EHOSTUNREACH => Error::EHOSTUNREACH, - nix::errno::Errno::EALREADY => Error::EALREADY, - nix::errno::Errno::EINPROGRESS => Error::EINPROGRESS, - nix::errno::Errno::ESTALE => Error::ESTALE, - nix::errno::Errno::EDQUOT => Error::EDQUOT, - nix::errno::Errno::ECANCELED => Error::ECANCELED, - nix::errno::Errno::EOWNERDEAD => Error::EOWNERDEAD, - nix::errno::Errno::ENOTRECOVERABLE => Error::ENOTRECOVERABLE, - other => { - warn!("Unknown error from nix: {}", other); - Error::ENOSYS - } + Errno::EPERM => Error::EPERM, + Errno::ENOENT => Error::ENOENT, + Errno::ESRCH => Error::ESRCH, + Errno::EINTR => Error::EINTR, + Errno::EIO => Error::EIO, + Errno::ENXIO => Error::ENXIO, + Errno::E2BIG => Error::E2BIG, + Errno::ENOEXEC => Error::ENOEXEC, + Errno::EBADF => Error::EBADF, + Errno::ECHILD => Error::ECHILD, + Errno::EAGAIN => Error::EAGAIN, + Errno::ENOMEM => Error::ENOMEM, + Errno::EACCES => Error::EACCES, + Errno::EFAULT => Error::EFAULT, + Errno::EBUSY => Error::EBUSY, + Errno::EEXIST => Error::EEXIST, + Errno::EXDEV => Error::EXDEV, + Errno::ENODEV => Error::ENODEV, + Errno::ENOTDIR => Error::ENOTDIR, + Errno::EISDIR => Error::EISDIR, + Errno::EINVAL => Error::EINVAL, + Errno::ENFILE => Error::ENFILE, + Errno::EMFILE => Error::EMFILE, + Errno::ENOTTY => Error::ENOTTY, + Errno::ETXTBSY => Error::ETXTBSY, + Errno::EFBIG => Error::EFBIG, + Errno::ENOSPC => Error::ENOSPC, + Errno::ESPIPE => Error::ESPIPE, + Errno::EROFS => Error::EROFS, + Errno::EMLINK => Error::EMLINK, + Errno::EPIPE => Error::EPIPE, + Errno::EDOM => Error::EDOM, + Errno::ERANGE => Error::ERANGE, + Errno::EDEADLK => Error::EDEADLK, + Errno::ENAMETOOLONG => Error::ENAMETOOLONG, + Errno::ENOLCK => Error::ENOLCK, + Errno::ENOSYS => Error::ENOSYS, + Errno::ENOTEMPTY => Error::ENOTEMPTY, + Errno::ELOOP => Error::ELOOP, + Errno::ENOMSG => Error::ENOMSG, + Errno::EIDRM => Error::EIDRM, + Errno::ENOLINK => Error::ENOLINK, + Errno::EPROTO => Error::EPROTO, + Errno::EMULTIHOP => Error::EMULTIHOP, + Errno::EBADMSG => Error::EBADMSG, + Errno::EOVERFLOW => Error::EOVERFLOW, + Errno::EILSEQ => Error::EILSEQ, + Errno::ENOTSOCK => Error::ENOTSOCK, + Errno::EDESTADDRREQ => Error::EDESTADDRREQ, + Errno::EMSGSIZE => Error::EMSGSIZE, + Errno::EPROTOTYPE => Error::EPROTOTYPE, + Errno::ENOPROTOOPT => Error::ENOPROTOOPT, + Errno::EPROTONOSUPPORT => Error::EPROTONOSUPPORT, + Errno::EAFNOSUPPORT => Error::EAFNOSUPPORT, + Errno::EADDRINUSE => Error::EADDRINUSE, + Errno::EADDRNOTAVAIL => Error::EADDRNOTAVAIL, + Errno::ENETDOWN => Error::ENETDOWN, + Errno::ENETUNREACH => Error::ENETUNREACH, + Errno::ENETRESET => Error::ENETRESET, + Errno::ECONNABORTED => Error::ECONNABORTED, + Errno::ECONNRESET => Error::ECONNRESET, + Errno::ENOBUFS => Error::ENOBUFS, + Errno::EISCONN => Error::EISCONN, + Errno::ENOTCONN => Error::ENOTCONN, + Errno::ETIMEDOUT => Error::ETIMEDOUT, + Errno::ECONNREFUSED => Error::ECONNREFUSED, + Errno::EHOSTUNREACH => Error::EHOSTUNREACH, + Errno::EALREADY => Error::EALREADY, + Errno::EINPROGRESS => Error::EINPROGRESS, + Errno::ESTALE => Error::ESTALE, + Errno::EDQUOT => Error::EDQUOT, + Errno::ECANCELED => Error::ECANCELED, + Errno::EOWNERDEAD => Error::EOWNERDEAD, + Errno::ENOTRECOVERABLE => Error::ENOTRECOVERABLE, } } -pub(crate) fn nix_from_fdflags(fdflags: wasi::__wasi_fdflags_t) -> nix::fcntl::OFlag { - use nix::fcntl::OFlag; +pub(crate) fn nix_from_fdflags(fdflags: wasi::__wasi_fdflags_t) -> OFlag { let mut nix_flags = OFlag::empty(); if fdflags & wasi::__WASI_FDFLAGS_APPEND != 0 { - nix_flags.insert(OFlag::O_APPEND); + nix_flags.insert(OFlag::APPEND); } if fdflags & wasi::__WASI_FDFLAGS_DSYNC != 0 { - nix_flags.insert(OFlag::O_DSYNC); + nix_flags.insert(OFlag::DSYNC); } if fdflags & wasi::__WASI_FDFLAGS_NONBLOCK != 0 { - nix_flags.insert(OFlag::O_NONBLOCK); + nix_flags.insert(OFlag::NONBLOCK); } if fdflags & wasi::__WASI_FDFLAGS_RSYNC != 0 { nix_flags.insert(O_RSYNC); } if fdflags & wasi::__WASI_FDFLAGS_SYNC != 0 { - nix_flags.insert(OFlag::O_SYNC); + nix_flags.insert(OFlag::SYNC); } nix_flags } -pub(crate) fn fdflags_from_nix(oflags: nix::fcntl::OFlag) -> wasi::__wasi_fdflags_t { - use nix::fcntl::OFlag; +pub(crate) fn fdflags_from_nix(oflags: OFlag) -> wasi::__wasi_fdflags_t { let mut fdflags = 0; - if oflags.contains(OFlag::O_APPEND) { + if oflags.contains(OFlag::APPEND) { fdflags |= wasi::__WASI_FDFLAGS_APPEND; } - if oflags.contains(OFlag::O_DSYNC) { + if oflags.contains(OFlag::DSYNC) { fdflags |= wasi::__WASI_FDFLAGS_DSYNC; } - if oflags.contains(OFlag::O_NONBLOCK) { + if oflags.contains(OFlag::NONBLOCK) { fdflags |= wasi::__WASI_FDFLAGS_NONBLOCK; } if oflags.contains(O_RSYNC) { fdflags |= wasi::__WASI_FDFLAGS_RSYNC; } - if oflags.contains(OFlag::O_SYNC) { + if oflags.contains(OFlag::SYNC) { fdflags |= wasi::__WASI_FDFLAGS_SYNC; } fdflags } -pub(crate) fn nix_from_oflags(oflags: wasi::__wasi_oflags_t) -> nix::fcntl::OFlag { - use nix::fcntl::OFlag; +pub(crate) fn nix_from_oflags(oflags: wasi::__wasi_oflags_t) -> OFlag { let mut nix_flags = OFlag::empty(); if oflags & wasi::__WASI_OFLAGS_CREAT != 0 { - nix_flags.insert(OFlag::O_CREAT); + nix_flags.insert(OFlag::CREAT); } if oflags & wasi::__WASI_OFLAGS_DIRECTORY != 0 { - nix_flags.insert(OFlag::O_DIRECTORY); + nix_flags.insert(OFlag::DIRECTORY); } if oflags & wasi::__WASI_OFLAGS_EXCL != 0 { - nix_flags.insert(OFlag::O_EXCL); + nix_flags.insert(OFlag::EXCL); } if oflags & wasi::__WASI_OFLAGS_TRUNC != 0 { - nix_flags.insert(OFlag::O_TRUNC); + nix_flags.insert(OFlag::TRUNC); } nix_flags } -pub(crate) fn filetype_from_nix(sflags: nix::sys::stat::SFlag) -> FileType { - use nix::sys::stat::SFlag; - if sflags.contains(SFlag::S_IFCHR) { +pub(crate) fn filetype_from_nix(sflags: SFlag) -> FileType { + if sflags.contains(SFlag::IFCHR) { FileType::CharacterDevice - } else if sflags.contains(SFlag::S_IFBLK) { + } else if sflags.contains(SFlag::IFBLK) { FileType::BlockDevice - } else if sflags.contains(SFlag::S_IFSOCK) { + } else if sflags.contains(SFlag::IFSOCK) { FileType::SocketStream - } else if sflags.contains(SFlag::S_IFDIR) { + } else if sflags.contains(SFlag::IFDIR) { FileType::Directory - } else if sflags.contains(SFlag::S_IFREG) { + } else if sflags.contains(SFlag::IFREG) { FileType::RegularFile - } else if sflags.contains(SFlag::S_IFLNK) { + } else if sflags.contains(SFlag::IFLNK) { FileType::Symlink } else { FileType::Unknown } } -pub(crate) fn filestat_from_nix( - filestat: nix::sys::stat::FileStat, -) -> Result { +pub(crate) fn filestat_from_nix(filestat: libc::stat) -> Result { fn filestat_to_timestamp(secs: u64, nsecs: u64) -> Result { secs.checked_mul(1_000_000_000) .and_then(|sec_nsec| sec_nsec.checked_add(nsecs)) .ok_or(Error::EOVERFLOW) } - let filetype = nix::sys::stat::SFlag::from_bits_truncate(filestat.st_mode); + let filetype = SFlag::from_bits_truncate(filestat.st_mode); let dev = stdev_from_nix(filestat.st_dev)?; let ino = stino_from_nix(filestat.st_ino)?; let atim = filestat_to_timestamp(filestat.st_atime as u64, filestat.st_atime_nsec as u64)?; @@ -214,7 +207,7 @@ pub(crate) fn filestat_from_nix( } pub(crate) fn dirent_filetype_from_host( - host_entry: &nix::libc::dirent, + host_entry: &libc::dirent, ) -> Result { match host_entry.d_type { libc::DT_FIFO => Ok(wasi::__WASI_FILETYPE_UNKNOWN), @@ -243,3 +236,17 @@ pub(crate) fn dirent_filetype_from_host( pub(crate) fn path_from_host>(s: S) -> Result { helpers::path_from_slice(s.as_ref().as_bytes()).map(String::from) } + +impl From for FileType { + fn from(ft: yanix::dir::FileType) -> Self { + use yanix::dir::FileType::*; + match ft { + RegularFile => Self::RegularFile, + Symlink => Self::Symlink, + Directory => Self::Directory, + BlockDevice => Self::BlockDevice, + CharacterDevice => Self::CharacterDevice, + /* Unknown | Socket | Fifo */ _ => Self::Unknown, + } + } +} diff --git a/crates/wasi-common/src/sys/unix/hostcalls_impl/fs.rs b/crates/wasi-common/src/sys/unix/hostcalls_impl/fs.rs index 8a853d510c..8e6baa7bf5 100644 --- a/crates/wasi-common/src/sys/unix/hostcalls_impl/fs.rs +++ b/crates/wasi-common/src/sys/unix/hostcalls_impl/fs.rs @@ -1,11 +1,10 @@ #![allow(non_camel_case_types)] #![allow(unused_unsafe)] use crate::helpers::systemtime_to_timestamp; -use crate::hostcalls_impl::{FileType, PathGet}; -use crate::sys::host_impl; -use crate::sys::unix::str_to_cstring; +use crate::host::{Dirent, FileType}; +use crate::hostcalls_impl::PathGet; +use crate::sys::{fdentry_impl::OsHandle, host_impl}; use crate::{wasi, Error, Result}; -use nix::libc; use std::convert::TryInto; use std::fs::{File, Metadata}; use std::os::unix::fs::FileExt; @@ -39,53 +38,61 @@ pub(crate) fn fd_pwrite(file: &File, buf: &[u8], offset: wasi::__wasi_filesize_t } pub(crate) fn fd_fdstat_get(fd: &File) -> Result { - use nix::fcntl::{fcntl, OFlag, F_GETFL}; - match fcntl(fd.as_raw_fd(), F_GETFL).map(OFlag::from_bits_truncate) { - Ok(flags) => Ok(host_impl::fdflags_from_nix(flags)), - Err(e) => Err(host_impl::errno_from_nix(e.as_errno().unwrap())), - } + unsafe { yanix::fcntl::get_status_flags(fd.as_raw_fd()) } + .map(host_impl::fdflags_from_nix) + .map_err(Into::into) } pub(crate) fn fd_fdstat_set_flags(fd: &File, fdflags: wasi::__wasi_fdflags_t) -> Result<()> { - use nix::fcntl::{fcntl, F_SETFL}; let nix_flags = host_impl::nix_from_fdflags(fdflags); - match fcntl(fd.as_raw_fd(), F_SETFL(nix_flags)) { - Ok(_) => Ok(()), - Err(e) => Err(host_impl::errno_from_nix(e.as_errno().unwrap())), - } + unsafe { yanix::fcntl::set_status_flags(fd.as_raw_fd(), nix_flags) }.map_err(Into::into) +} + +pub(crate) fn fd_advise( + file: &File, + advice: wasi::__wasi_advice_t, + offset: wasi::__wasi_filesize_t, + len: wasi::__wasi_filesize_t, +) -> Result<()> { + use yanix::fadvise::{posix_fadvise, PosixFadviseAdvice}; + let offset = offset.try_into()?; + let len = len.try_into()?; + let host_advice = match advice { + wasi::__WASI_ADVICE_DONTNEED => PosixFadviseAdvice::DontNeed, + wasi::__WASI_ADVICE_SEQUENTIAL => PosixFadviseAdvice::Sequential, + wasi::__WASI_ADVICE_WILLNEED => PosixFadviseAdvice::WillNeed, + wasi::__WASI_ADVICE_NOREUSE => PosixFadviseAdvice::NoReuse, + wasi::__WASI_ADVICE_RANDOM => PosixFadviseAdvice::Random, + wasi::__WASI_ADVICE_NORMAL => PosixFadviseAdvice::Normal, + _ => return Err(Error::EINVAL), + }; + unsafe { posix_fadvise(file.as_raw_fd(), offset, len, host_advice) }.map_err(Into::into) } pub(crate) fn path_create_directory(resolved: PathGet) -> Result<()> { - use nix::libc::mkdirat; - let path_cstr = str_to_cstring(resolved.path())?; - // nix doesn't expose mkdirat() yet - match unsafe { mkdirat(resolved.dirfd().as_raw_fd(), path_cstr.as_ptr(), 0o777) } { - 0 => Ok(()), - _ => Err(host_impl::errno_from_nix(nix::errno::Errno::last())), + use yanix::file::{mkdirat, Mode}; + unsafe { + mkdirat( + resolved.dirfd().as_raw_fd(), + resolved.path(), + Mode::from_bits_truncate(0o777), + ) } + .map_err(Into::into) } pub(crate) fn path_link(resolved_old: PathGet, resolved_new: PathGet) -> Result<()> { - use nix::libc::linkat; - let old_path_cstr = str_to_cstring(resolved_old.path())?; - let new_path_cstr = str_to_cstring(resolved_new.path())?; - - // Not setting AT_SYMLINK_FOLLOW fails on most filesystems - let atflags = libc::AT_SYMLINK_FOLLOW; - let res = unsafe { + use yanix::file::{linkat, AtFlag}; + unsafe { linkat( resolved_old.dirfd().as_raw_fd(), - old_path_cstr.as_ptr(), + resolved_old.path(), resolved_new.dirfd().as_raw_fd(), - new_path_cstr.as_ptr(), - atflags, + resolved_new.path(), + AtFlag::SYMLINK_FOLLOW, ) - }; - if res != 0 { - Err(host_impl::errno_from_nix(nix::errno::Errno::last())) - } else { - Ok(()) } + .map_err(Into::into) } pub(crate) fn path_open( @@ -95,20 +102,21 @@ pub(crate) fn path_open( oflags: wasi::__wasi_oflags_t, fs_flags: wasi::__wasi_fdflags_t, ) -> Result { - use nix::errno::Errno; - use nix::fcntl::{openat, AtFlags, OFlag}; - use nix::sys::stat::{fstatat, Mode, SFlag}; + use yanix::{ + file::{fstatat, openat, AtFlag, Mode, OFlag, SFlag}, + Errno, + }; let mut nix_all_oflags = if read && write { - OFlag::O_RDWR + OFlag::RDWR } else if write { - OFlag::O_WRONLY + OFlag::WRONLY } else { - OFlag::O_RDONLY + OFlag::RDONLY }; // on non-Capsicum systems, we always want nofollow - nix_all_oflags.insert(OFlag::O_NOFOLLOW); + nix_all_oflags.insert(OFlag::NOFOLLOW); // convert open flags nix_all_oflags.insert(host_impl::nix_from_oflags(oflags)); @@ -123,54 +131,63 @@ pub(crate) fn path_open( log::debug!("path_open resolved = {:?}", resolved); log::debug!("path_open oflags = {:?}", nix_all_oflags); - let new_fd = match openat( - resolved.dirfd().as_raw_fd(), - resolved.path(), - nix_all_oflags, - Mode::from_bits_truncate(0o666), - ) { + let new_fd = match unsafe { + openat( + resolved.dirfd().as_raw_fd(), + resolved.path(), + nix_all_oflags, + Mode::from_bits_truncate(0o666), + ) + } { Ok(fd) => fd, Err(e) => { - match e.as_errno() { - // Linux returns ENXIO instead of EOPNOTSUPP when opening a socket - Some(Errno::ENXIO) => { - if let Ok(stat) = fstatat( - resolved.dirfd().as_raw_fd(), - resolved.path(), - AtFlags::AT_SYMLINK_NOFOLLOW, - ) { - if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::S_IFSOCK) { - return Err(Error::ENOTSUP); + if let yanix::YanixError::Errno(errno) = e { + match errno { + // Linux returns ENXIO instead of EOPNOTSUPP when opening a socket + Errno::ENXIO => { + if let Ok(stat) = unsafe { + fstatat( + resolved.dirfd().as_raw_fd(), + resolved.path(), + AtFlag::SYMLINK_NOFOLLOW, + ) + } { + if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::IFSOCK) { + return Err(Error::ENOTSUP); + } else { + return Err(Error::ENXIO); + } } else { return Err(Error::ENXIO); } - } else { - return Err(Error::ENXIO); } - } - // Linux returns ENOTDIR instead of ELOOP when using O_NOFOLLOW|O_DIRECTORY - // on a symlink. - Some(Errno::ENOTDIR) - if !(nix_all_oflags & (OFlag::O_NOFOLLOW | OFlag::O_DIRECTORY)).is_empty() => - { - if let Ok(stat) = fstatat( - resolved.dirfd().as_raw_fd(), - resolved.path(), - AtFlags::AT_SYMLINK_NOFOLLOW, - ) { - if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::S_IFLNK) { - return Err(Error::ELOOP); + // Linux returns ENOTDIR instead of ELOOP when using O_NOFOLLOW|O_DIRECTORY + // on a symlink. + Errno::ENOTDIR + if !(nix_all_oflags & (OFlag::NOFOLLOW | OFlag::DIRECTORY)).is_empty() => + { + if let Ok(stat) = unsafe { + fstatat( + resolved.dirfd().as_raw_fd(), + resolved.path(), + AtFlag::SYMLINK_NOFOLLOW, + ) + } { + if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::IFLNK) { + return Err(Error::ELOOP); + } } + return Err(Error::ENOTDIR); } - return Err(Error::ENOTDIR); + // FreeBSD returns EMLINK instead of ELOOP when using O_NOFOLLOW on + // a symlink. + Errno::EMLINK if !(nix_all_oflags & OFlag::NOFOLLOW).is_empty() => { + return Err(Error::ELOOP); + } + errno => return Err(host_impl::errno_from_nix(errno)), } - // FreeBSD returns EMLINK instead of ELOOP when using O_NOFOLLOW on - // a symlink. - Some(Errno::EMLINK) if !(nix_all_oflags & OFlag::O_NOFOLLOW).is_empty() => { - return Err(Error::ELOOP); - } - Some(e) => return Err(host_impl::errno_from_nix(e)), - None => return Err(Error::ENOSYS), + } else { + return Err(e.into()); } } }; @@ -182,34 +199,16 @@ pub(crate) fn path_open( } pub(crate) fn path_readlink(resolved: PathGet, buf: &mut [u8]) -> Result { - use nix::errno::Errno; - let path_cstr = str_to_cstring(resolved.path())?; - - // Linux requires that the buffer size is positive, whereas POSIX does not. - // Use a fake buffer to store the results if the size is zero. - // TODO: instead of using raw libc::readlinkat call here, this should really - // be fixed in `nix` crate - let fakebuf: &mut [u8] = &mut [0]; - let buf_len = buf.len(); - let len = unsafe { - libc::readlinkat( - resolved.dirfd().as_raw_fd(), - path_cstr.as_ptr() as *const libc::c_char, - if buf_len == 0 { - fakebuf.as_mut_ptr() - } else { - buf.as_mut_ptr() - } as *mut libc::c_char, - if buf_len == 0 { fakebuf.len() } else { buf_len }, - ) - }; - - if len < 0 { - Err(host_impl::errno_from_nix(Errno::last())) - } else { - let len = len as usize; - Ok(if len < buf_len { len } else { buf_len }) + use std::cmp::min; + use yanix::file::readlinkat; + let read_link = unsafe { readlinkat(resolved.dirfd().as_raw_fd(), resolved.path()) } + .map_err(Into::into) + .and_then(host_impl::path_from_host)?; + let copy_len = min(read_link.len(), buf.len()); + if copy_len > 0 { + buf[..copy_len].copy_from_slice(&read_link.as_bytes()[..copy_len]); } + Ok(copy_len) } pub(crate) fn fd_filestat_get_impl(file: &std::fs::File) -> Result { @@ -229,8 +228,8 @@ pub(crate) fn fd_filestat_get_impl(file: &std::fs::File) -> Result Result { - use nix::sys::socket::{self, SockType}; use std::os::unix::fs::FileTypeExt; + use yanix::socket::{get_socket_type, SockType}; let ftype = metadata.file_type(); if ftype.is_file() { Ok(FileType::RegularFile) @@ -243,10 +242,7 @@ fn filetype(file: &File, metadata: &Metadata) -> Result { } else if ftype.is_block_device() { Ok(FileType::BlockDevice) } else if ftype.is_socket() { - match socket::getsockopt(file.as_raw_fd(), socket::sockopt::SockType) - .map_err(|err| err.as_errno().unwrap()) - .map_err(host_impl::errno_from_nix)? - { + match unsafe { get_socket_type(file.as_raw_fd())? } { SockType::Datagram => Ok(FileType::SocketDgram), SockType::Stream => Ok(FileType::SocketStream), _ => Ok(FileType::Unknown), @@ -260,17 +256,14 @@ pub(crate) fn path_filestat_get( resolved: PathGet, dirflags: wasi::__wasi_lookupflags_t, ) -> Result { - use nix::fcntl::AtFlags; - use nix::sys::stat::fstatat; - + use yanix::file::{fstatat, AtFlag}; let atflags = match dirflags { - 0 => AtFlags::empty(), - _ => AtFlags::AT_SYMLINK_NOFOLLOW, + 0 => AtFlag::empty(), + _ => AtFlag::SYMLINK_NOFOLLOW, }; - - let filestat = fstatat(resolved.dirfd().as_raw_fd(), resolved.path(), atflags) - .map_err(|err| host_impl::errno_from_nix(err.as_errno().unwrap()))?; - host_impl::filestat_from_nix(filestat) + unsafe { fstatat(resolved.dirfd().as_raw_fd(), resolved.path(), atflags) } + .map_err(Into::into) + .and_then(host_impl::filestat_from_nix) } pub(crate) fn path_filestat_set_times( @@ -321,20 +314,49 @@ pub(crate) fn path_filestat_set_times( } pub(crate) fn path_remove_directory(resolved: PathGet) -> Result<()> { - use nix::errno; - use nix::libc::{unlinkat, AT_REMOVEDIR}; - - let path_cstr = str_to_cstring(resolved.path())?; - - // nix doesn't expose unlinkat() yet - match unsafe { + use yanix::file::{unlinkat, AtFlag}; + unsafe { unlinkat( resolved.dirfd().as_raw_fd(), - path_cstr.as_ptr(), - AT_REMOVEDIR, + resolved.path(), + AtFlag::REMOVEDIR, ) - } { - 0 => Ok(()), - _ => Err(host_impl::errno_from_nix(errno::Errno::last())), } + .map_err(Into::into) +} + +pub(crate) fn fd_readdir<'a>( + os_handle: &'a mut OsHandle, + cookie: wasi::__wasi_dircookie_t, +) -> Result> + 'a> { + use yanix::dir::{DirIter, Entry, EntryExt, SeekLoc}; + + // Get an instance of `Dir`; this is host-specific due to intricasies + // of managing a dir stream between Linux and BSD *nixes + let mut dir = fd_readdir_impl::get_dir_from_os_handle(os_handle)?; + + // Seek if needed. Unless cookie is wasi::__WASI_DIRCOOKIE_START, + // new items may not be returned to the caller. + if cookie == wasi::__WASI_DIRCOOKIE_START { + log::trace!(" | fd_readdir: doing rewinddir"); + dir.rewind(); + } else { + log::trace!(" | fd_readdir: doing seekdir to {}", cookie); + let loc = unsafe { SeekLoc::from_raw(cookie as i64) }; + dir.seek(loc); + } + + Ok(DirIter::new(dir).map(|entry| { + let entry: Entry = entry?; + Ok(Dirent { + name: entry + // TODO can we reuse path_from_host for CStr? + .file_name() + .to_str()? + .to_owned(), + ino: entry.ino(), + ftype: entry.file_type().into(), + cookie: entry.seek_loc().to_raw().try_into()?, + }) + })) } diff --git a/crates/wasi-common/src/sys/unix/hostcalls_impl/fs_helpers.rs b/crates/wasi-common/src/sys/unix/hostcalls_impl/fs_helpers.rs index c57cc3476f..785b553d96 100644 --- a/crates/wasi-common/src/sys/unix/hostcalls_impl/fs_helpers.rs +++ b/crates/wasi-common/src/sys/unix/hostcalls_impl/fs_helpers.rs @@ -3,6 +3,7 @@ use crate::sys::host_impl; use crate::{wasi, Result}; use std::fs::File; +use yanix::file::OFlag; pub(crate) fn path_open_rights( rights_base: wasi::__wasi_rights_t, @@ -10,27 +11,25 @@ pub(crate) fn path_open_rights( oflags: wasi::__wasi_oflags_t, fs_flags: wasi::__wasi_fdflags_t, ) -> (wasi::__wasi_rights_t, wasi::__wasi_rights_t) { - use nix::fcntl::OFlag; - // which rights are needed on the dirfd? let mut needed_base = wasi::__WASI_RIGHTS_PATH_OPEN; let mut needed_inheriting = rights_base | rights_inheriting; // convert open flags let oflags = host_impl::nix_from_oflags(oflags); - if oflags.contains(OFlag::O_CREAT) { + if oflags.contains(OFlag::CREAT) { needed_base |= wasi::__WASI_RIGHTS_PATH_CREATE_FILE; } - if oflags.contains(OFlag::O_TRUNC) { + if oflags.contains(OFlag::TRUNC) { needed_base |= wasi::__WASI_RIGHTS_PATH_FILESTAT_SET_SIZE; } // convert file descriptor flags let fdflags = host_impl::nix_from_fdflags(fs_flags); - if fdflags.contains(OFlag::O_DSYNC) { + if fdflags.contains(OFlag::DSYNC) { needed_inheriting |= wasi::__WASI_RIGHTS_FD_DATASYNC; } - if fdflags.intersects(host_impl::O_RSYNC | OFlag::O_SYNC) { + if fdflags.intersects(host_impl::O_RSYNC | OFlag::SYNC) { needed_inheriting |= wasi::__WASI_RIGHTS_FD_SYNC; } @@ -38,31 +37,30 @@ pub(crate) fn path_open_rights( } pub(crate) fn openat(dirfd: &File, path: &str) -> Result { - use nix::fcntl::{self, OFlag}; - use nix::sys::stat::Mode; use std::os::unix::prelude::{AsRawFd, FromRawFd}; + use yanix::file::{openat, Mode}; log::debug!("path_get openat path = {:?}", path); - fcntl::openat( - dirfd.as_raw_fd(), - path, - OFlag::O_RDONLY | OFlag::O_DIRECTORY | OFlag::O_NOFOLLOW, - Mode::empty(), - ) + unsafe { + openat( + dirfd.as_raw_fd(), + path, + OFlag::RDONLY | OFlag::DIRECTORY | OFlag::NOFOLLOW, + Mode::empty(), + ) + } .map(|new_fd| unsafe { File::from_raw_fd(new_fd) }) .map_err(Into::into) } pub(crate) fn readlinkat(dirfd: &File, path: &str) -> Result { - use nix::fcntl; use std::os::unix::prelude::AsRawFd; + use yanix::file::readlinkat; log::debug!("path_get readlinkat path = {:?}", path); - let readlink_buf = &mut [0u8; libc::PATH_MAX as usize + 1]; - - fcntl::readlinkat(dirfd.as_raw_fd(), path, readlink_buf) + unsafe { readlinkat(dirfd.as_raw_fd(), path) } .map_err(Into::into) .and_then(host_impl::path_from_host) } diff --git a/crates/wasi-common/src/sys/unix/hostcalls_impl/misc.rs b/crates/wasi-common/src/sys/unix/hostcalls_impl/misc.rs index 46c29c14e2..077adb3868 100644 --- a/crates/wasi-common/src/sys/unix/hostcalls_impl/misc.rs +++ b/crates/wasi-common/src/sys/unix/hostcalls_impl/misc.rs @@ -3,29 +3,22 @@ use crate::hostcalls_impl::{ClockEventData, FdEventData}; use crate::sys::host_impl; use crate::{wasi, Error, Result}; -use nix::libc::{self, c_int}; -use std::mem::MaybeUninit; +use yanix::clock::{clock_getres, ClockId}; -fn wasi_clock_id_to_unix(clock_id: wasi::__wasi_clockid_t) -> Result { - // convert the supported clocks to the libc types, or return EINVAL +fn wasi_clock_id_to_unix(clock_id: wasi::__wasi_clockid_t) -> Result { + // convert the supported clocks to libc types, or return EINVAL match clock_id { - wasi::__WASI_CLOCKID_REALTIME => Ok(libc::CLOCK_REALTIME), - wasi::__WASI_CLOCKID_MONOTONIC => Ok(libc::CLOCK_MONOTONIC), - wasi::__WASI_CLOCKID_PROCESS_CPUTIME_ID => Ok(libc::CLOCK_PROCESS_CPUTIME_ID), - wasi::__WASI_CLOCKID_THREAD_CPUTIME_ID => Ok(libc::CLOCK_THREAD_CPUTIME_ID), + wasi::__WASI_CLOCKID_REALTIME => Ok(ClockId::Realtime), + wasi::__WASI_CLOCKID_MONOTONIC => Ok(ClockId::Monotonic), + wasi::__WASI_CLOCKID_PROCESS_CPUTIME_ID => Ok(ClockId::ProcessCPUTime), + wasi::__WASI_CLOCKID_THREAD_CPUTIME_ID => Ok(ClockId::ThreadCPUTime), _ => Err(Error::EINVAL), } } pub(crate) fn clock_res_get(clock_id: wasi::__wasi_clockid_t) -> Result { let clock_id = wasi_clock_id_to_unix(clock_id)?; - // no `nix` wrapper for clock_getres, so we do it ourselves - let mut timespec = MaybeUninit::::uninit(); - let res = unsafe { libc::clock_getres(clock_id, timespec.as_mut_ptr()) }; - if res != 0 { - return Err(host_impl::errno_from_nix(nix::errno::Errno::last())); - } - let timespec = unsafe { timespec.assume_init() }; + let timespec = clock_getres(clock_id)?; // convert to nanoseconds, returning EOVERFLOW in case of overflow; // this is freelancing a bit from the spec but seems like it'll @@ -46,13 +39,7 @@ pub(crate) fn clock_res_get(clock_id: wasi::__wasi_clockid_t) -> Result Result { let clock_id = wasi_clock_id_to_unix(clock_id)?; - // no `nix` wrapper for clock_getres, so we do it ourselves - let mut timespec = MaybeUninit::::uninit(); - let res = unsafe { libc::clock_gettime(clock_id, timespec.as_mut_ptr()) }; - if res != 0 { - return Err(host_impl::errno_from_nix(nix::errno::Errno::last())); - } - let timespec = unsafe { timespec.assume_init() }; + let timespec = clock_getres(clock_id)?; // convert to nanoseconds, returning EOVERFLOW in case of overflow; this is freelancing a bit // from the spec but seems like it'll be an unusual situation to hit @@ -67,11 +54,11 @@ pub(crate) fn poll_oneoff( fd_events: Vec, events: &mut Vec, ) -> Result<()> { - use nix::{ - errno::Errno, - poll::{poll, PollFd, PollFlags}, - }; use std::{convert::TryInto, os::unix::prelude::AsRawFd}; + use yanix::{ + poll::{poll, PollFd, PollFlags}, + Errno, + }; if fd_events.is_empty() && timeout.is_none() { return Ok(()); @@ -89,13 +76,13 @@ pub(crate) fn poll_oneoff( // events we filtered before. If we get something else here, the code has a serious bug. _ => unreachable!(), }; - PollFd::new(event.descriptor.as_raw_fd(), flags) + unsafe { PollFd::new(event.descriptor.as_raw_fd(), flags) } }) .collect(); let poll_timeout = timeout.map_or(-1, |timeout| { let delay = timeout.delay / 1_000_000; // poll syscall requires delay to expressed in milliseconds - delay.try_into().unwrap_or(c_int::max_value()) + delay.try_into().unwrap_or(libc::c_int::max_value()) }); log::debug!("poll_oneoff poll_timeout = {:?}", poll_timeout); @@ -107,7 +94,7 @@ pub(crate) fn poll_oneoff( } return Err(host_impl::errno_from_nix(Errno::last())); } - Ok(ready) => break ready as usize, + Ok(ready) => break ready, } }; @@ -119,9 +106,6 @@ pub(crate) fn poll_oneoff( }) } -// define the `fionread()` function, equivalent to `ioctl(fd, FIONREAD, *bytes)` -nix::ioctl_read_bad!(fionread, nix::libc::FIONREAD, c_int); - fn poll_oneoff_handle_timeout_event( timeout: ClockEventData, events: &mut Vec, @@ -140,11 +124,11 @@ fn poll_oneoff_handle_timeout_event( } fn poll_oneoff_handle_fd_event<'a>( - ready_events: impl Iterator, nix::poll::PollFd)>, + ready_events: impl Iterator, yanix::poll::PollFd)>, events: &mut Vec, ) -> Result<()> { - use nix::poll::PollFlags; use std::{convert::TryInto, os::unix::prelude::AsRawFd}; + use yanix::{file::fionread, poll::PollFlags}; for (fd_event, poll_fd) in ready_events { log::debug!("poll_oneoff_handle_fd_event fd_event = {:?}", fd_event); @@ -157,10 +141,11 @@ fn poll_oneoff_handle_fd_event<'a>( log::debug!("poll_oneoff_handle_fd_event revents = {:?}", revents); - let mut nbytes = 0; - if fd_event.r#type == wasi::__WASI_EVENTTYPE_FD_READ { - let _ = unsafe { fionread(fd_event.descriptor.as_raw_fd(), &mut nbytes) }; - } + let nbytes = if fd_event.r#type == wasi::__WASI_EVENTTYPE_FD_READ { + unsafe { fionread(fd_event.descriptor.as_raw_fd())? } + } else { + 0 + }; let output_event = if revents.contains(PollFlags::POLLNVAL) { wasi::__wasi_event_t { diff --git a/crates/wasi-common/src/sys/unix/linux/hostcalls_impl.rs b/crates/wasi-common/src/sys/unix/linux/hostcalls_impl.rs index 2279a15ae2..0ddac0f41a 100644 --- a/crates/wasi-common/src/sys/unix/linux/hostcalls_impl.rs +++ b/crates/wasi-common/src/sys/unix/linux/hostcalls_impl.rs @@ -1,176 +1,60 @@ -use super::super::dir::{Dir, Entry, SeekLoc}; -use crate::hostcalls_impl::{Dirent, PathGet}; -use crate::sys::host_impl; -use crate::sys::unix::str_to_cstring; -use crate::{wasi, Error, Result}; -use log::trace; -use std::convert::TryInto; -use std::fs::File; +use crate::hostcalls_impl::PathGet; +use crate::Result; use std::os::unix::prelude::AsRawFd; pub(crate) fn path_unlink_file(resolved: PathGet) -> Result<()> { - use nix::errno; - use nix::libc::unlinkat; - - let path_cstr = str_to_cstring(resolved.path())?; - - // nix doesn't expose unlinkat() yet - let res = unsafe { unlinkat(resolved.dirfd().as_raw_fd(), path_cstr.as_ptr(), 0) }; - if res == 0 { - Ok(()) - } else { - Err(host_impl::errno_from_nix(errno::Errno::last())) + use yanix::file::{unlinkat, AtFlag}; + unsafe { + unlinkat( + resolved.dirfd().as_raw_fd(), + resolved.path(), + AtFlag::empty(), + ) } + .map_err(Into::into) } pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> Result<()> { - use nix::{errno::Errno, libc::symlinkat}; - - let old_path_cstr = str_to_cstring(old_path)?; - let new_path_cstr = str_to_cstring(resolved.path())?; + use yanix::file::symlinkat; log::debug!("path_symlink old_path = {:?}", old_path); log::debug!("path_symlink resolved = {:?}", resolved); - let res = unsafe { - symlinkat( - old_path_cstr.as_ptr(), - resolved.dirfd().as_raw_fd(), - new_path_cstr.as_ptr(), - ) - }; - if res != 0 { - Err(host_impl::errno_from_nix(Errno::last())) - } else { - Ok(()) - } + unsafe { symlinkat(old_path, resolved.dirfd().as_raw_fd(), resolved.path()) } + .map_err(Into::into) } pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> Result<()> { - use nix::libc::renameat; - let old_path_cstr = str_to_cstring(resolved_old.path())?; - let new_path_cstr = str_to_cstring(resolved_new.path())?; - - let res = unsafe { + use yanix::file::renameat; + unsafe { renameat( resolved_old.dirfd().as_raw_fd(), - old_path_cstr.as_ptr(), + resolved_old.path(), resolved_new.dirfd().as_raw_fd(), - new_path_cstr.as_ptr(), + resolved_new.path(), ) - }; - if res != 0 { - Err(host_impl::errno_from_nix(nix::errno::Errno::last())) - } else { - Ok(()) } + .map_err(Into::into) } -pub(crate) fn fd_readdir( - fd: &File, - cookie: wasi::__wasi_dircookie_t, -) -> Result>> { - // We need to duplicate the fd, because `opendir(3)`: - // After a successful call to fdopendir(), fd is used internally by the implementation, - // and should not otherwise be used by the application. - // `opendir(3p)` also says that it's undefined behavior to - // modify the state of the fd in a different way than by accessing DIR*. - // - // Still, rewinddir will be needed because the two file descriptors - // share progress. But we can safely execute closedir now. - let fd = fd.try_clone()?; - let mut dir = Dir::from(fd)?; +pub(crate) mod fd_readdir_impl { + use crate::sys::fdentry_impl::OsHandle; + use crate::Result; + use yanix::dir::Dir; - // Seek if needed. Unless cookie is wasi::__WASI_DIRCOOKIE_START, - // new items may not be returned to the caller. - // - // According to `opendir(3p)`: - // If a file is removed from or added to the directory after the most recent call - // to opendir() or rewinddir(), whether a subsequent call to readdir() returns an entry - // for that file is unspecified. - if cookie == wasi::__WASI_DIRCOOKIE_START { - trace!(" | fd_readdir: doing rewinddir"); - dir.rewind(); - } else { - trace!(" | fd_readdir: doing seekdir to {}", cookie); - let loc = unsafe { SeekLoc::from_raw(cookie as i64) }; - dir.seek(loc); - } - - Ok(DirIter(dir).map(|entry| { - let entry: Entry = entry?; - Ok(Dirent { - name: entry // TODO can we reuse path_from_host for CStr? - .file_name() - .to_str()? - .to_owned(), - ino: entry.ino(), - ftype: entry.file_type().into(), - cookie: entry.seek_loc().to_raw().try_into()?, - }) - })) -} - -struct DirIter(Dir); - -impl Iterator for DirIter { - type Item = nix::Result; - - fn next(&mut self) -> Option { - use libc::{dirent64, readdir64_r}; - use nix::errno::Errno; - - unsafe { - // Note: POSIX specifies that portable applications should dynamically allocate a - // buffer with room for a `d_name` field of size `pathconf(..., _PC_NAME_MAX)` plus 1 - // for the NUL byte. It doesn't look like the std library does this; it just uses - // fixed-sized buffers (and libc's dirent seems to be sized so this is appropriate). - // Probably fine here too then. - // - // See `impl Iterator for ReadDir` [1] for more details. - // [1] https://github.com/rust-lang/rust/blob/master/src/libstd/sys/unix/fs.rs - let mut ent = std::mem::MaybeUninit::::uninit(); - let mut result = std::ptr::null_mut(); - if let Err(e) = Errno::result(readdir64_r( - (self.0).0.as_ptr(), - ent.as_mut_ptr(), - &mut result, - )) { - return Some(Err(e)); - } - if result.is_null() { - None - } else { - assert_eq!(result, ent.as_mut_ptr(), "readdir_r specification violated"); - Some(Ok(Entry(ent.assume_init()))) - } - } + pub(crate) fn get_dir_from_os_handle(os_handle: &mut OsHandle) -> Result> { + // We need to duplicate the fd, because `opendir(3)`: + // After a successful call to fdopendir(), fd is used internally by the implementation, + // and should not otherwise be used by the application. + // `opendir(3p)` also says that it's undefined behavior to + // modify the state of the fd in a different way than by accessing DIR*. + // + // Still, rewinddir will be needed because the two file descriptors + // share progress. But we can safely execute closedir now. + let fd = os_handle.try_clone()?; + // TODO This doesn't look very clean. Can we do something about it? + // Boxing is needed here in order to satisfy `yanix`'s trait requirement for the `DirIter` + // where `T: Deref`. + Ok(Box::new(Dir::from(fd)?)) } } - -pub(crate) fn fd_advise( - file: &File, - advice: wasi::__wasi_advice_t, - offset: wasi::__wasi_filesize_t, - len: wasi::__wasi_filesize_t, -) -> Result<()> { - { - use nix::fcntl::{posix_fadvise, PosixFadviseAdvice}; - - let offset = offset.try_into()?; - let len = len.try_into()?; - let host_advice = match advice { - wasi::__WASI_ADVICE_DONTNEED => PosixFadviseAdvice::POSIX_FADV_DONTNEED, - wasi::__WASI_ADVICE_SEQUENTIAL => PosixFadviseAdvice::POSIX_FADV_SEQUENTIAL, - wasi::__WASI_ADVICE_WILLNEED => PosixFadviseAdvice::POSIX_FADV_WILLNEED, - wasi::__WASI_ADVICE_NOREUSE => PosixFadviseAdvice::POSIX_FADV_NOREUSE, - wasi::__WASI_ADVICE_RANDOM => PosixFadviseAdvice::POSIX_FADV_RANDOM, - wasi::__WASI_ADVICE_NORMAL => PosixFadviseAdvice::POSIX_FADV_NORMAL, - _ => return Err(Error::EINVAL), - }; - - posix_fadvise(file.as_raw_fd(), offset, len, host_advice)?; - } - - Ok(()) -} diff --git a/crates/wasi-common/src/sys/unix/linux/mod.rs b/crates/wasi-common/src/sys/unix/linux/mod.rs index 32955b18c7..a9fd0557d5 100644 --- a/crates/wasi-common/src/sys/unix/linux/mod.rs +++ b/crates/wasi-common/src/sys/unix/linux/mod.rs @@ -2,42 +2,16 @@ pub(crate) mod filetime; pub(crate) mod hostcalls_impl; pub(crate) mod oshandle; -pub(crate) mod fdentry_impl { - use crate::{sys::host_impl, Result}; - use std::os::unix::prelude::AsRawFd; - - pub(crate) unsafe fn isatty(fd: &impl AsRawFd) -> Result { - use nix::errno::Errno; - - let res = libc::isatty(fd.as_raw_fd()); - if res == 1 { - // isatty() returns 1 if fd is an open file descriptor referring to a terminal... - Ok(true) - } else { - // ... otherwise 0 is returned, and errno is set to indicate the error. - match Errno::last() { - // While POSIX specifies ENOTTY if the passed - // fd is *not* a tty, on Linux, some implementations - // may return EINVAL instead. - // - // https://linux.die.net/man/3/isatty - Errno::ENOTTY | Errno::EINVAL => Ok(false), - x => Err(host_impl::errno_from_nix(x)), - } - } - } -} - pub(crate) mod host_impl { use crate::{wasi, Result}; - pub(crate) const O_RSYNC: nix::fcntl::OFlag = nix::fcntl::OFlag::O_RSYNC; + pub(crate) const O_RSYNC: yanix::file::OFlag = yanix::file::OFlag::RSYNC; - pub(crate) fn stdev_from_nix(dev: nix::libc::dev_t) -> Result { + pub(crate) fn stdev_from_nix(dev: libc::dev_t) -> Result { Ok(wasi::__wasi_device_t::from(dev)) } - pub(crate) fn stino_from_nix(ino: nix::libc::ino_t) -> Result { + pub(crate) fn stino_from_nix(ino: libc::ino_t) -> Result { Ok(wasi::__wasi_device_t::from(ino)) } } diff --git a/crates/wasi-common/src/sys/unix/mod.rs b/crates/wasi-common/src/sys/unix/mod.rs index 52c6e3e4eb..7aa05706ff 100644 --- a/crates/wasi-common/src/sys/unix/mod.rs +++ b/crates/wasi-common/src/sys/unix/mod.rs @@ -2,7 +2,6 @@ pub(crate) mod fdentry_impl; pub(crate) mod host_impl; pub(crate) mod hostcalls_impl; -mod dir; mod filetime; #[cfg(any( @@ -17,8 +16,7 @@ mod bsd; #[cfg(target_os = "linux")] mod linux; -use crate::{Error, Result}; -use std::ffi::CString; +use crate::Result; use std::fs::{File, OpenOptions}; use std::path::Path; @@ -30,10 +28,6 @@ pub(crate) fn dev_null() -> Result { .map_err(Into::into) } -pub(crate) fn str_to_cstring(s: &str) -> Result { - CString::new(s.as_bytes()).map_err(|_| Error::EILSEQ) -} - pub fn preopen_dir>(path: P) -> Result { File::open(path).map_err(Into::into) } diff --git a/crates/wasi-common/src/sys/windows/hostcalls_impl/fs.rs b/crates/wasi-common/src/sys/windows/hostcalls_impl/fs.rs index 4c8cfb269f..4f5d3c8783 100644 --- a/crates/wasi-common/src/sys/windows/hostcalls_impl/fs.rs +++ b/crates/wasi-common/src/sys/windows/hostcalls_impl/fs.rs @@ -4,7 +4,8 @@ use super::fs_helpers::*; use crate::ctx::WasiCtx; use crate::fdentry::FdEntry; use crate::helpers::systemtime_to_timestamp; -use crate::hostcalls_impl::{fd_filestat_set_times_impl, Dirent, FileType, PathGet}; +use crate::host::{Dirent, FileType}; +use crate::hostcalls_impl::{fd_filestat_set_times_impl, PathGet}; use crate::sys::fdentry_impl::determine_type_rights; use crate::sys::host_impl::{self, path_from_host}; use crate::sys::hostcalls_impl::fs_helpers::PathGetExt; diff --git a/crates/wasi-common/wig/src/raw_types.rs b/crates/wasi-common/wig/src/raw_types.rs index c03b03471e..15de007bdb 100644 --- a/crates/wasi-common/wig/src/raw_types.rs +++ b/crates/wasi-common/wig/src/raw_types.rs @@ -39,114 +39,114 @@ pub fn gen(args: TokenStream, mode: Mode) -> TokenStream { } fn gen_datatypes(output: &mut TokenStream, doc: &witx::Document, mode: Mode) { - for datatype in doc.datatypes() { - if mode.include_target_types() != type_has_target_size(doc, &datatype) { + for namedtype in doc.typenames() { + if mode.include_target_types() != namedtype_has_target_size(&namedtype) { continue; } - gen_datatype(output, doc, mode, &datatype); + gen_datatype(output, mode, &namedtype); } } -fn gen_datatype( - output: &mut TokenStream, - doc: &witx::Document, - mode: Mode, - datatype: &witx::Datatype, -) { - match &datatype.variant { - witx::DatatypeVariant::Alias(a) => { - if a.name.as_str() == "size" { - let wasi_name = format_ident!("__wasi_{}_t", a.name.as_str()); - match mode { - Mode::Host => output.extend(quote!(pub type #wasi_name = usize;)), - Mode::Wasi => panic!("size has target-specific size"), - Mode::Wasi32 => output.extend(quote!(pub type #wasi_name = u32;)), +fn gen_datatype(output: &mut TokenStream, mode: Mode, namedtype: &witx::NamedType) { + let wasi_name = format_ident!("__wasi_{}_t", namedtype.name.as_str()); + match &namedtype.dt { + witx::TypeRef::Name(alias_to) => { + let to = tref_tokens(mode, &alias_to.dt); + output.extend(quote!(pub type #wasi_name = #to;)); + } + witx::TypeRef::Value(v) => match &**v { + witx::Type::Enum(e) => { + let repr = int_repr_tokens(e.repr); + output.extend(quote!(pub type #wasi_name = #repr;)); + for (index, variant) in e.variants.iter().enumerate() { + let value_name = format_ident!( + "__WASI_{}_{}", + namedtype.name.as_str().to_shouty_snake_case(), + variant.name.as_str().to_shouty_snake_case() + ); + let index_name = Literal::usize_unsuffixed(index); + output.extend(quote!(pub const #value_name: #wasi_name = #index_name;)); } - } else { - let wasi_name = format_ident!("__wasi_{}_t", a.name.as_str()); - let to = ident_tokens(mode, &a.to); - output.extend(quote!(pub type #wasi_name = #to;)); } - } - witx::DatatypeVariant::Enum(e) => { - let wasi_name = format_ident!("__wasi_{}_t", e.name.as_str()); - let repr = int_repr_tokens(e.repr); - output.extend(quote!(pub type #wasi_name = #repr;)); - for (index, variant) in e.variants.iter().enumerate() { - let value_name = format_ident!( - "__WASI_{}_{}", - e.name.as_str().to_shouty_snake_case(), - variant.name.as_str().to_shouty_snake_case() - ); - let index_name = Literal::usize_unsuffixed(index); - output.extend(quote!(pub const #value_name: #wasi_name = #index_name;)); + witx::Type::Flags(f) => { + let repr = int_repr_tokens(f.repr); + output.extend(quote!(pub type #wasi_name = #repr;)); + for (index, flag) in f.flags.iter().enumerate() { + let value_name = format_ident!( + "__WASI_{}_{}", + namedtype.name.as_str().to_shouty_snake_case(), + flag.name.as_str().to_shouty_snake_case() + ); + let flag_value = Literal::u128_unsuffixed( + 1u128 + .checked_shl(u32::try_from(index).expect("flag value overflow")) + .expect("flag value overflow"), + ); + output.extend(quote!(pub const #value_name: #wasi_name = #flag_value;)); + } } - } - witx::DatatypeVariant::Flags(f) => { - let wasi_name = format_ident!("__wasi_{}_t", f.name.as_str()); - let repr = int_repr_tokens(f.repr); - output.extend(quote!(pub type #wasi_name = #repr;)); - for (index, flag) in f.flags.iter().enumerate() { - let value_name = format_ident!( - "__WASI_{}_{}", - f.name.as_str().to_shouty_snake_case(), - flag.name.as_str().to_shouty_snake_case() - ); - let flag_value = Literal::u128_unsuffixed( - 1u128 - .checked_shl(u32::try_from(index).expect("flag value overflow")) - .expect("flag value overflow"), - ); - output.extend(quote!(pub const #value_name: #wasi_name = #flag_value;)); - } - } - witx::DatatypeVariant::Struct(s) => { - output.extend(quote!(#[repr(C)])); + witx::Type::Struct(s) => { + output.extend(quote!(#[repr(C)])); + // Types which contain unions can't trivially implement Debug, + // Hash, or Eq, because the type itself doesn't record which + // union member is active. + if struct_has_union(&s) { + output.extend(quote!(#[derive(Copy, Clone)])); + output.extend(quote!(#[allow(missing_debug_implementations)])); + } else { + output.extend(quote!(#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)])); + } - // Types which contain unions can't trivially implement Debug, - // Hash, or Eq, because the type itself doesn't record which - // union member is active. - if struct_has_union(&doc, s) { + output.extend(quote!(pub struct #wasi_name)); + + let mut inner = TokenStream::new(); + for member in &s.members { + let member_name = format_ident!("r#{}", member.name.as_str()); + let member_type = tref_tokens(mode, &member.tref); + inner.extend(quote!(pub #member_name: #member_type,)); + } + let braced = Group::new(Delimiter::Brace, inner); + output.extend(TokenStream::from(TokenTree::Group(braced))); + } + witx::Type::Union(u) => { + output.extend(quote!(#[repr(C)])); output.extend(quote!(#[derive(Copy, Clone)])); output.extend(quote!(#[allow(missing_debug_implementations)])); - } else { - output.extend(quote!(#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)])); + + output.extend(quote!(pub union #wasi_name)); + + let mut inner = TokenStream::new(); + for variant in &u.variants { + let variant_name = format_ident!("r#{}", variant.name.as_str()); + let variant_type = tref_tokens(mode, &variant.tref); + inner.extend(quote!(pub #variant_name: #variant_type,)); + } + let braced = Group::new(Delimiter::Brace, inner); + output.extend(TokenStream::from(TokenTree::Group(braced))); } - - let wasi_name = format_ident!("__wasi_{}_t", s.name.as_str()); - output.extend(quote!(pub struct #wasi_name)); - - let mut inner = TokenStream::new(); - for member in &s.members { - let member_name = format_ident!("r#{}", member.name.as_str()); - let member_type = ident_tokens(mode, &member.type_); - inner.extend(quote!(pub #member_name: #member_type,)); + witx::Type::Handle(_h) => { + output.extend(quote!(pub type #wasi_name = u32;)); } - let braced = Group::new(Delimiter::Brace, inner); - output.extend(TokenStream::from(TokenTree::Group(braced))); - } - witx::DatatypeVariant::Union(u) => { - output.extend(quote!(#[repr(C)])); - output.extend(quote!(#[derive(Copy, Clone)])); - output.extend(quote!(#[allow(missing_debug_implementations)])); - - let wasi_name = format_ident!("__wasi_{}_t", u.name.as_str()); - output.extend(quote!(pub union #wasi_name)); - - let mut inner = TokenStream::new(); - for variant in &u.variants { - let variant_name = format_ident!("r#{}", variant.name.as_str()); - let variant_type = ident_tokens(mode, &variant.type_); - inner.extend(quote!(pub #variant_name: #variant_type,)); + witx::Type::Builtin(b) => { + if namedtype.name.as_str() == "size" { + match mode { + Mode::Host => output.extend(quote!(pub type #wasi_name = usize;)), + Mode::Wasi => panic!("size has target-specific size"), + Mode::Wasi32 => output.extend(quote!(pub type #wasi_name = u32;)), + } + } else { + let b_type = builtin_tokens(mode, *b); + output.extend(quote!(pub type #wasi_name = #b_type;)); + } } - let braced = Group::new(Delimiter::Brace, inner); - output.extend(TokenStream::from(TokenTree::Group(braced))); - } - witx::DatatypeVariant::Handle(a) => { - let wasi_name = format_ident!("__wasi_{}_t", a.name.as_str()); - output.extend(quote!(pub type #wasi_name = u32;)); - } + witx::Type::Pointer { .. } + | witx::Type::ConstPointer { .. } + | witx::Type::Array { .. } => { + let tref_tokens = tref_tokens(mode, &namedtype.dt); + output.extend(quote!(pub type #wasi_name = #tref_tokens;)); + } + }, } } @@ -179,86 +179,77 @@ fn builtin_tokens(mode: Mode, builtin: witx::BuiltinType) -> TokenStream { } } -fn ident_tokens(mode: Mode, ident: &witx::DatatypeIdent) -> TokenStream { - match ident { - witx::DatatypeIdent::Builtin(builtin) => builtin_tokens(mode, *builtin), - witx::DatatypeIdent::Ident(ident) => TokenStream::from(TokenTree::Ident(format_ident!( +fn tref_tokens(mode: Mode, tref: &witx::TypeRef) -> TokenStream { + match tref { + witx::TypeRef::Name(n) => TokenStream::from(TokenTree::Ident(format_ident!( "__wasi_{}_t", - ident.name.as_str() + n.name.as_str() ))), - witx::DatatypeIdent::Pointer(pointee) => { - let pointee = ident_tokens(mode, pointee); - match mode { - Mode::Host => quote!(*mut #pointee), - Mode::Wasi => panic!("pointers have target-specific size"), - Mode::Wasi32 => quote!(u32), + witx::TypeRef::Value(v) => match &**v { + witx::Type::Builtin(b) => builtin_tokens(mode, *b), + witx::Type::Pointer(pointee) => { + let pointee = tref_tokens(mode, pointee); + match mode { + Mode::Host => quote!(*mut #pointee), + Mode::Wasi => panic!("pointers have target-specific size"), + Mode::Wasi32 => quote!(u32), + } } - } - witx::DatatypeIdent::ConstPointer(pointee) => { - let pointee = ident_tokens(mode, pointee); - match mode { - Mode::Host => quote!(*const #pointee), - Mode::Wasi => panic!("pointers have target-specific size"), - Mode::Wasi32 => quote!(u32), + witx::Type::ConstPointer(pointee) => { + let pointee = tref_tokens(mode, pointee); + match mode { + Mode::Host => quote!(*const #pointee), + Mode::Wasi => panic!("pointers have target-specific size"), + Mode::Wasi32 => quote!(u32), + } } - } - witx::DatatypeIdent::Array(element) => { - let element_name = ident_tokens(mode, element); - match mode { - Mode::Host => quote!((*const #element_name, usize)), - Mode::Wasi => panic!("arrays have target-specific size"), - Mode::Wasi32 => quote!((u32, u32)), + witx::Type::Array(element) => { + let element_name = tref_tokens(mode, element); + match mode { + Mode::Host => quote!((*const #element_name, usize)), + Mode::Wasi => panic!("arrays have target-specific size"), + Mode::Wasi32 => quote!((u32, u32)), + } } - } + t => panic!("cannot give name to anonymous type {:?}", t), + }, } } /// Test whether the given struct contains any union members. -fn struct_has_union(doc: &witx::Document, s: &witx::StructDatatype) -> bool { - s.members.iter().any(|member| match &member.type_ { - witx::DatatypeIdent::Ident(ident) => match &doc.datatype(&ident.name).unwrap().variant { - witx::DatatypeVariant::Union(_) => true, - witx::DatatypeVariant::Struct(s) => struct_has_union(doc, &s), - _ => false, - }, +fn struct_has_union(s: &witx::StructDatatype) -> bool { + s.members.iter().any(|member| match &*member.tref.type_() { + witx::Type::Union { .. } => true, + witx::Type::Struct(s) => struct_has_union(&s), _ => false, }) } -/// Test whether the given type has a target-specific size. -fn type_has_target_size(doc: &witx::Document, type_: &witx::Datatype) -> bool { - match &type_.variant { - witx::DatatypeVariant::Alias(a) => { - a.name.as_str() == "size" || ident_has_target_size(doc, &a.to) - } - witx::DatatypeVariant::Enum(_) => false, - witx::DatatypeVariant::Flags(_) => false, - witx::DatatypeVariant::Struct(s) => s - .members - .iter() - .any(|m| ident_has_target_size(doc, &m.type_)), - witx::DatatypeVariant::Union(u) => u - .variants - .iter() - .any(|v| ident_has_target_size(doc, &v.type_)), - witx::DatatypeVariant::Handle(_) => false, +/// Test whether the type referred to has a target-specific size. +fn tref_has_target_size(tref: &witx::TypeRef) -> bool { + match tref { + witx::TypeRef::Name(nt) => namedtype_has_target_size(&nt), + witx::TypeRef::Value(t) => type_has_target_size(&t), } } -/// Test whether the given type ident has a target-specific size. -fn ident_has_target_size(doc: &witx::Document, ident: &witx::DatatypeIdent) -> bool { - match ident { - witx::DatatypeIdent::Ident(ident) => { - type_has_target_size(doc, &doc.datatype(&ident.name).unwrap()) - } - witx::DatatypeIdent::Builtin(builtin) => { - if let witx::BuiltinType::String = builtin { - true - } else { - false - } - } - witx::DatatypeIdent::Pointer(_) | witx::DatatypeIdent::ConstPointer(_) => true, - witx::DatatypeIdent::Array(element) => ident_has_target_size(doc, element), +/// Test whether the given named type has a target-specific size. +fn namedtype_has_target_size(nt: &witx::NamedType) -> bool { + if nt.name.as_str() == "size" { + true + } else { + tref_has_target_size(&nt.dt) + } +} + +/// Test whether the given type has a target-specific size. +fn type_has_target_size(ty: &witx::Type) -> bool { + match ty { + witx::Type::Builtin(witx::BuiltinType::String) => true, + witx::Type::Pointer { .. } | witx::Type::ConstPointer { .. } => true, + witx::Type::Array(elem) => tref_has_target_size(elem), + witx::Type::Struct(s) => s.members.iter().any(|m| tref_has_target_size(&m.tref)), + witx::Type::Union(u) => u.variants.iter().any(|v| tref_has_target_size(&v.tref)), + _ => false, } } diff --git a/crates/wasi-common/yanix/Cargo.toml b/crates/wasi-common/yanix/Cargo.toml new file mode 100644 index 0000000000..61c52488cb --- /dev/null +++ b/crates/wasi-common/yanix/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "yanix" +version = "0.1.0" +authors = ["The Wasmtime Project Developers"] +description = "Yet Another Nix crate: a Unix API helper library" +license = "Apache-2.0 WITH LLVM-exception" +repository = "https://github.com/bytecodealliance/wasmtime" +edition = "2018" + +[dependencies] +log = "0.4" +libc = { version = "0.2", features = ["extra_traits"] } +thiserror = "1.0" +bitflags = "1.2" +cfg-if = "0.1.9" + +[badges] +maintenance = { status = "actively-developed" } diff --git a/crates/wasi-common/yanix/LICENSE b/crates/wasi-common/yanix/LICENSE new file mode 100644 index 0000000000..f9d81955f4 --- /dev/null +++ b/crates/wasi-common/yanix/LICENSE @@ -0,0 +1,220 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +--- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. + diff --git a/crates/wasi-common/yanix/src/clock.rs b/crates/wasi-common/yanix/src/clock.rs new file mode 100644 index 0000000000..530272cc19 --- /dev/null +++ b/crates/wasi-common/yanix/src/clock.rs @@ -0,0 +1,29 @@ +use crate::{Errno, Result}; +use std::mem::MaybeUninit; + +#[derive(Debug, Copy, Clone)] +pub enum ClockId { + Realtime, + Monotonic, + ProcessCPUTime, + ThreadCPUTime, +} + +impl ClockId { + pub fn as_raw(&self) -> libc::clockid_t { + match self { + Self::Realtime => libc::CLOCK_REALTIME, + Self::Monotonic => libc::CLOCK_MONOTONIC, + Self::ProcessCPUTime => libc::CLOCK_PROCESS_CPUTIME_ID, + Self::ThreadCPUTime => libc::CLOCK_THREAD_CPUTIME_ID, + } + } +} + +pub fn clock_getres(clock_id: ClockId) -> Result { + let mut timespec = MaybeUninit::::uninit(); + Errno::from_success_code(unsafe { + libc::clock_getres(clock_id.as_raw(), timespec.as_mut_ptr()) + })?; + Ok(unsafe { timespec.assume_init() }) +} diff --git a/crates/wasi-common/yanix/src/dir.rs b/crates/wasi-common/yanix/src/dir.rs new file mode 100644 index 0000000000..e395491f4e --- /dev/null +++ b/crates/wasi-common/yanix/src/dir.rs @@ -0,0 +1,162 @@ +use crate::{ + sys::dir::{iter_impl, EntryImpl}, + Errno, Result, +}; +use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd}; +use std::{ffi::CStr, ops::Deref, ptr}; + +pub use crate::sys::EntryExt; + +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub struct Dir(pub(crate) ptr::NonNull); + +impl Dir { + /// Takes the ownership of the passed-in descriptor-based object, + /// and creates a new instance of `Dir`. + #[inline] + pub fn from(fd: F) -> Result { + let fd = fd.into_raw_fd(); + unsafe { Self::from_fd(fd) } + } + + unsafe fn from_fd(fd: RawFd) -> Result { + let d = libc::fdopendir(fd); + if let Some(d) = ptr::NonNull::new(d) { + Ok(Self(d)) + } else { + let e = Errno::last(); + libc::close(fd); + Err(e.into()) + } + } + + /// Set the position of the directory stream, see `seekdir(3)`. + #[cfg(not(target_os = "android"))] + pub fn seek(&mut self, loc: SeekLoc) { + unsafe { libc::seekdir(self.0.as_ptr(), loc.0) } + } + + /// Reset directory stream, see `rewinddir(3)`. + pub fn rewind(&mut self) { + unsafe { libc::rewinddir(self.0.as_ptr()) } + } + + /// Get the current position in the directory stream. + /// + /// If this location is given to `Dir::seek`, the entries up to the previously returned + /// will be omitted and the iteration will start from the currently pending directory entry. + #[cfg(not(target_os = "android"))] + #[allow(dead_code)] + pub fn tell(&self) -> SeekLoc { + let loc = unsafe { libc::telldir(self.0.as_ptr()) }; + SeekLoc(loc) + } +} + +unsafe impl Send for Dir {} + +impl AsRawFd for Dir { + fn as_raw_fd(&self) -> RawFd { + unsafe { libc::dirfd(self.0.as_ptr()) } + } +} + +impl Drop for Dir { + fn drop(&mut self) { + unsafe { libc::closedir(self.0.as_ptr()) }; + } +} + +#[derive(Debug, Copy, Clone)] +pub struct Entry(pub(crate) EntryImpl); + +impl Entry { + /// Returns the file name of this directory entry. + pub fn file_name(&self) -> &CStr { + unsafe { CStr::from_ptr(self.0.d_name.as_ptr()) } + } + + /// Returns the type of this directory entry. + pub fn file_type(&self) -> FileType { + unsafe { FileType::from_raw(self.0.d_type) } + } +} + +#[cfg(not(target_os = "android"))] +#[derive(Clone, Copy, Debug)] +pub struct SeekLoc(libc::c_long); + +#[cfg(not(target_os = "android"))] +impl SeekLoc { + pub unsafe fn from_raw(loc: i64) -> Self { + Self(loc.into()) + } + + pub fn to_raw(&self) -> i64 { + self.0.into() + } +} + +#[derive(Clone, Copy, Debug)] +#[repr(u8)] +pub enum FileType { + CharacterDevice = libc::DT_CHR, + Directory = libc::DT_DIR, + BlockDevice = libc::DT_BLK, + RegularFile = libc::DT_REG, + Symlink = libc::DT_LNK, + Fifo = libc::DT_FIFO, + Socket = libc::DT_SOCK, + Unknown = libc::DT_UNKNOWN, +} + +impl FileType { + pub unsafe fn from_raw(file_type: u8) -> Self { + match file_type { + libc::DT_CHR => Self::CharacterDevice, + libc::DT_DIR => Self::Directory, + libc::DT_BLK => Self::BlockDevice, + libc::DT_REG => Self::RegularFile, + libc::DT_LNK => Self::Symlink, + libc::DT_SOCK => Self::Socket, + libc::DT_FIFO => Self::Fifo, + /* libc::DT_UNKNOWN */ _ => Self::Unknown, + } + } + + pub fn to_raw(&self) -> u8 { + match self { + Self::CharacterDevice => libc::DT_CHR, + Self::Directory => libc::DT_DIR, + Self::BlockDevice => libc::DT_BLK, + Self::RegularFile => libc::DT_REG, + Self::Symlink => libc::DT_LNK, + Self::Socket => libc::DT_SOCK, + Self::Fifo => libc::DT_FIFO, + Self::Unknown => libc::DT_UNKNOWN, + } + } +} + +#[derive(Debug)] +pub struct DirIter>(T); + +impl DirIter +where + T: Deref, +{ + pub fn new(dir: T) -> Self { + Self(dir) + } +} + +impl Iterator for DirIter +where + T: Deref, +{ + type Item = Result; + + fn next(&mut self) -> Option { + unsafe { iter_impl(&self.0).map(|x| x.map(Entry)) } + } +} diff --git a/crates/wasi-common/yanix/src/errno.rs b/crates/wasi-common/yanix/src/errno.rs new file mode 100644 index 0000000000..8f8be64dc8 --- /dev/null +++ b/crates/wasi-common/yanix/src/errno.rs @@ -0,0 +1,227 @@ +//! Errno-specific for different Unix platforms +use crate::Result; +use std::{fmt, io}; +use thiserror::Error; + +#[derive(Debug, Copy, Clone, Error, PartialEq, Eq, Hash)] +#[repr(i32)] +pub enum Errno { + EPERM = libc::EPERM, + ENOENT = libc::ENOENT, + ESRCH = libc::ESRCH, + EINTR = libc::EINTR, + EIO = libc::EIO, + ENXIO = libc::ENXIO, + E2BIG = libc::E2BIG, + ENOEXEC = libc::ENOEXEC, + EBADF = libc::EBADF, + ECHILD = libc::ECHILD, + EAGAIN = libc::EAGAIN, + ENOMEM = libc::ENOMEM, + EACCES = libc::EACCES, + EFAULT = libc::EFAULT, + EBUSY = libc::EBUSY, + EEXIST = libc::EEXIST, + EXDEV = libc::EXDEV, + ENODEV = libc::ENODEV, + ENOTDIR = libc::ENOTDIR, + EISDIR = libc::EISDIR, + EINVAL = libc::EINVAL, + ENFILE = libc::ENFILE, + EMFILE = libc::EMFILE, + ENOTTY = libc::ENOTTY, + ETXTBSY = libc::ETXTBSY, + EFBIG = libc::EFBIG, + ENOSPC = libc::ENOSPC, + ESPIPE = libc::ESPIPE, + EROFS = libc::EROFS, + EMLINK = libc::EMLINK, + EPIPE = libc::EPIPE, + EDOM = libc::EDOM, + ERANGE = libc::ERANGE, + EDEADLK = libc::EDEADLK, + ENAMETOOLONG = libc::ENAMETOOLONG, + ENOLCK = libc::ENOLCK, + ENOSYS = libc::ENOSYS, + ENOTEMPTY = libc::ENOTEMPTY, + ELOOP = libc::ELOOP, + ENOMSG = libc::ENOMSG, + EIDRM = libc::EIDRM, + ENOLINK = libc::ENOLINK, + EPROTO = libc::EPROTO, + EMULTIHOP = libc::EMULTIHOP, + EBADMSG = libc::EBADMSG, + EOVERFLOW = libc::EOVERFLOW, + EILSEQ = libc::EILSEQ, + ENOTSOCK = libc::ENOTSOCK, + EDESTADDRREQ = libc::EDESTADDRREQ, + EMSGSIZE = libc::EMSGSIZE, + EPROTOTYPE = libc::EPROTOTYPE, + ENOPROTOOPT = libc::ENOPROTOOPT, + EPROTONOSUPPORT = libc::EPROTONOSUPPORT, + EAFNOSUPPORT = libc::EAFNOSUPPORT, + EADDRINUSE = libc::EADDRINUSE, + EADDRNOTAVAIL = libc::EADDRNOTAVAIL, + ENETDOWN = libc::ENETDOWN, + ENETUNREACH = libc::ENETUNREACH, + ENETRESET = libc::ENETRESET, + ECONNABORTED = libc::ECONNABORTED, + ECONNRESET = libc::ECONNRESET, + ENOBUFS = libc::ENOBUFS, + EISCONN = libc::EISCONN, + ENOTCONN = libc::ENOTCONN, + ETIMEDOUT = libc::ETIMEDOUT, + ECONNREFUSED = libc::ECONNREFUSED, + EHOSTUNREACH = libc::EHOSTUNREACH, + EALREADY = libc::EALREADY, + EINPROGRESS = libc::EINPROGRESS, + ESTALE = libc::ESTALE, + EDQUOT = libc::EDQUOT, + ECANCELED = libc::ECANCELED, + EOWNERDEAD = libc::EOWNERDEAD, + ENOTRECOVERABLE = libc::ENOTRECOVERABLE, +} + +impl Errno { + pub fn from_i32(err: i32) -> Self { + match err { + libc::EPERM => Self::EPERM, + libc::ENOENT => Self::ENOENT, + libc::ESRCH => Self::ESRCH, + libc::EINTR => Self::EINTR, + libc::EIO => Self::EIO, + libc::ENXIO => Self::ENXIO, + libc::E2BIG => Self::E2BIG, + libc::ENOEXEC => Self::ENOEXEC, + libc::EBADF => Self::EBADF, + libc::ECHILD => Self::ECHILD, + libc::EAGAIN => Self::EAGAIN, + libc::ENOMEM => Self::ENOMEM, + libc::EACCES => Self::EACCES, + libc::EFAULT => Self::EFAULT, + libc::EBUSY => Self::EBUSY, + libc::EEXIST => Self::EEXIST, + libc::EXDEV => Self::EXDEV, + libc::ENODEV => Self::ENODEV, + libc::ENOTDIR => Self::ENOTDIR, + libc::EISDIR => Self::EISDIR, + libc::EINVAL => Self::EINVAL, + libc::ENFILE => Self::ENFILE, + libc::EMFILE => Self::EMFILE, + libc::ENOTTY => Self::ENOTTY, + libc::ETXTBSY => Self::ETXTBSY, + libc::EFBIG => Self::EFBIG, + libc::ENOSPC => Self::ENOSPC, + libc::ESPIPE => Self::ESPIPE, + libc::EROFS => Self::EROFS, + libc::EMLINK => Self::EMLINK, + libc::EPIPE => Self::EPIPE, + libc::EDOM => Self::EDOM, + libc::ERANGE => Self::ERANGE, + libc::EDEADLK => Self::EDEADLK, + libc::ENAMETOOLONG => Self::ENAMETOOLONG, + libc::ENOLCK => Self::ENOLCK, + libc::ENOSYS => Self::ENOSYS, + libc::ENOTEMPTY => Self::ENOTEMPTY, + libc::ELOOP => Self::ELOOP, + libc::ENOMSG => Self::ENOMSG, + libc::EIDRM => Self::EIDRM, + libc::ENOLINK => Self::ENOLINK, + libc::EPROTO => Self::EPROTO, + libc::EMULTIHOP => Self::EMULTIHOP, + libc::EBADMSG => Self::EBADMSG, + libc::EOVERFLOW => Self::EOVERFLOW, + libc::EILSEQ => Self::EILSEQ, + libc::ENOTSOCK => Self::ENOTSOCK, + libc::EDESTADDRREQ => Self::EDESTADDRREQ, + libc::EMSGSIZE => Self::EMSGSIZE, + libc::EPROTOTYPE => Self::EPROTOTYPE, + libc::ENOPROTOOPT => Self::ENOPROTOOPT, + libc::EPROTONOSUPPORT => Self::EPROTONOSUPPORT, + libc::EAFNOSUPPORT => Self::EAFNOSUPPORT, + libc::EADDRINUSE => Self::EADDRINUSE, + libc::EADDRNOTAVAIL => Self::EADDRNOTAVAIL, + libc::ENETDOWN => Self::ENETDOWN, + libc::ENETUNREACH => Self::ENETUNREACH, + libc::ENETRESET => Self::ENETRESET, + libc::ECONNABORTED => Self::ECONNABORTED, + libc::ECONNRESET => Self::ECONNRESET, + libc::ENOBUFS => Self::ENOBUFS, + libc::EISCONN => Self::EISCONN, + libc::ENOTCONN => Self::ENOTCONN, + libc::ETIMEDOUT => Self::ETIMEDOUT, + libc::ECONNREFUSED => Self::ECONNREFUSED, + libc::EHOSTUNREACH => Self::EHOSTUNREACH, + libc::EALREADY => Self::EALREADY, + libc::EINPROGRESS => Self::EINPROGRESS, + libc::ESTALE => Self::ESTALE, + libc::EDQUOT => Self::EDQUOT, + libc::ECANCELED => Self::ECANCELED, + libc::EOWNERDEAD => Self::EOWNERDEAD, + libc::ENOTRECOVERABLE => Self::ENOTRECOVERABLE, + other => { + log::warn!("Unknown errno: {}", other); + Self::ENOSYS + } + } + } + + pub fn last() -> Self { + let errno = io::Error::last_os_error() + .raw_os_error() + .unwrap_or(libc::ENOSYS); + Self::from_i32(errno) + } + + pub fn from_success_code(t: T) -> Result<()> { + if t.is_zero() { + Ok(()) + } else { + Err(Self::last().into()) + } + } + + pub fn from_result(t: T) -> Result { + if t.is_minus_one() { + Err(Self::last().into()) + } else { + Ok(t) + } + } +} + +impl fmt::Display for Errno { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Errno code: {}", self) + } +} + +#[doc(hidden)] +pub trait IsZero { + fn is_zero(&self) -> bool; +} + +macro_rules! impl_is_zero { + ($($t:ident)*) => ($(impl IsZero for $t { + fn is_zero(&self) -> bool { + *self == 0 + } + })*) +} + +impl_is_zero! { i32 i64 isize } + +#[doc(hidden)] +pub trait IsMinusOne { + fn is_minus_one(&self) -> bool; +} + +macro_rules! impl_is_minus_one { + ($($t:ident)*) => ($(impl IsMinusOne for $t { + fn is_minus_one(&self) -> bool { + *self == -1 + } + })*) +} + +impl_is_minus_one! { i32 i64 isize } diff --git a/crates/wasi-common/yanix/src/fcntl.rs b/crates/wasi-common/yanix/src/fcntl.rs new file mode 100644 index 0000000000..87d9827af3 --- /dev/null +++ b/crates/wasi-common/yanix/src/fcntl.rs @@ -0,0 +1,33 @@ +use crate::{ + file::{FdFlag, OFlag}, + Errno, Result, +}; +use std::os::unix::prelude::*; + +pub unsafe fn dup_fd(fd: RawFd, close_on_exec: bool) -> Result { + // Both fcntl commands expect a RawFd arg which will specify + // the minimum duplicated RawFd number. In our case, I don't + // think we have to worry about this that much, so passing in + // the RawFd descriptor we want duplicated + Errno::from_result(if close_on_exec { + libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, fd) + } else { + libc::fcntl(fd, libc::F_DUPFD, fd) + }) +} + +pub unsafe fn get_fd_flags(fd: RawFd) -> Result { + Errno::from_result(libc::fcntl(fd, libc::F_GETFD)).map(FdFlag::from_bits_truncate) +} + +pub unsafe fn set_fd_flags(fd: RawFd, flags: FdFlag) -> Result<()> { + Errno::from_success_code(libc::fcntl(fd, libc::F_SETFD, flags.bits())) +} + +pub unsafe fn get_status_flags(fd: RawFd) -> Result { + Errno::from_result(libc::fcntl(fd, libc::F_GETFL)).map(OFlag::from_bits_truncate) +} + +pub unsafe fn set_status_flags(fd: RawFd, flags: OFlag) -> Result<()> { + Errno::from_success_code(libc::fcntl(fd, libc::F_SETFL, flags.bits())) +} diff --git a/crates/wasi-common/yanix/src/file.rs b/crates/wasi-common/yanix/src/file.rs new file mode 100644 index 0000000000..36f12141a1 --- /dev/null +++ b/crates/wasi-common/yanix/src/file.rs @@ -0,0 +1,198 @@ +use crate::{Errno, Result}; +use bitflags::bitflags; +use std::{ + convert::TryInto, + ffi::{CString, OsStr, OsString}, + os::unix::prelude::*, +}; + +pub use crate::sys::file::*; + +bitflags! { + pub struct FdFlag: libc::c_int { + const CLOEXEC = libc::FD_CLOEXEC; + } +} + +bitflags! { + pub struct AtFlag: libc::c_int { + const REMOVEDIR = libc::AT_REMOVEDIR; + const SYMLINK_FOLLOW = libc::AT_SYMLINK_FOLLOW; + const SYMLINK_NOFOLLOW = libc::AT_SYMLINK_NOFOLLOW; + } +} + +bitflags! { + pub struct Mode: libc::mode_t { + const IRWXU = libc::S_IRWXU; + const IRUSR = libc::S_IRUSR; + const IWUSR = libc::S_IWUSR; + const IXUSR = libc::S_IXUSR; + const IRWXG = libc::S_IRWXG; + const IRGRP = libc::S_IRGRP; + const IWGRP = libc::S_IWGRP; + const IXGRP = libc::S_IXGRP; + const IRWXO = libc::S_IRWXO; + const IROTH = libc::S_IROTH; + const IWOTH = libc::S_IWOTH; + const IXOTH = libc::S_IXOTH; + const ISUID = libc::S_ISUID as libc::mode_t; + const ISGID = libc::S_ISGID as libc::mode_t; + const ISVTX = libc::S_ISVTX as libc::mode_t; + } +} + +bitflags! { + pub struct OFlag: libc::c_int { + const ACCMODE = libc::O_ACCMODE; + const APPEND = libc::O_APPEND; + const CREAT = libc::O_CREAT; + const DIRECTORY = libc::O_DIRECTORY; + #[cfg(any(target_os = "android", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "emscripten"))] + const DSYNC = libc::O_DSYNC; + const EXCL = libc::O_EXCL; + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + all(target_os = "linux", not(target_env = "musl")), + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + const FSYNC = libc::O_FSYNC; + const NOFOLLOW = libc::O_NOFOLLOW; + const NONBLOCK = libc::O_NONBLOCK; + const RDONLY = libc::O_RDONLY; + const WRONLY = libc::O_WRONLY; + const RDWR = libc::O_RDWR; + #[cfg(any(target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_os = "emscripten"))] + const RSYNC = libc::O_RSYNC; + const SYNC = libc::O_SYNC; + const TRUNC = libc::O_TRUNC; + } +} + +bitflags! { + pub struct SFlag: libc::mode_t { + const IFIFO = libc::S_IFIFO; + const IFCHR = libc::S_IFCHR; + const IFDIR = libc::S_IFDIR; + const IFBLK = libc::S_IFBLK; + const IFREG = libc::S_IFREG; + const IFLNK = libc::S_IFLNK; + const IFSOCK = libc::S_IFSOCK; + const IFMT = libc::S_IFMT; + } +} + +pub unsafe fn openat>( + dirfd: RawFd, + path: P, + oflag: OFlag, + mode: Mode, +) -> Result { + let path = CString::new(path.as_ref().as_bytes())?; + Errno::from_result(libc::openat( + dirfd, + path.as_ptr(), + oflag.bits(), + libc::c_uint::from(mode.bits()), + )) +} + +pub unsafe fn readlinkat>(dirfd: RawFd, path: P) -> Result { + let path = CString::new(path.as_ref().as_bytes())?; + let buffer = &mut [0u8; libc::PATH_MAX as usize + 1]; + Errno::from_result(libc::readlinkat( + dirfd, + path.as_ptr(), + buffer.as_mut_ptr() as *mut _, + buffer.len(), + )) + .and_then(|nread| { + let link = OsStr::from_bytes(&buffer[0..nread.try_into()?]); + Ok(link.into()) + }) +} + +pub unsafe fn mkdirat>(dirfd: RawFd, path: P, mode: Mode) -> Result<()> { + let path = CString::new(path.as_ref().as_bytes())?; + Errno::from_success_code(libc::mkdirat(dirfd, path.as_ptr(), mode.bits())) +} + +pub unsafe fn linkat>( + old_dirfd: RawFd, + old_path: P, + new_dirfd: RawFd, + new_path: P, + flags: AtFlag, +) -> Result<()> { + let old_path = CString::new(old_path.as_ref().as_bytes())?; + let new_path = CString::new(new_path.as_ref().as_bytes())?; + Errno::from_success_code(libc::linkat( + old_dirfd, + old_path.as_ptr(), + new_dirfd, + new_path.as_ptr(), + flags.bits(), + )) +} + +pub unsafe fn unlinkat>(dirfd: RawFd, path: P, flags: AtFlag) -> Result<()> { + let path = CString::new(path.as_ref().as_bytes())?; + Errno::from_success_code(libc::unlinkat(dirfd, path.as_ptr(), flags.bits())) +} + +pub unsafe fn renameat>( + old_dirfd: RawFd, + old_path: P, + new_dirfd: RawFd, + new_path: P, +) -> Result<()> { + let old_path = CString::new(old_path.as_ref().as_bytes())?; + let new_path = CString::new(new_path.as_ref().as_bytes())?; + Errno::from_success_code(libc::renameat( + old_dirfd, + old_path.as_ptr(), + new_dirfd, + new_path.as_ptr(), + )) +} + +pub unsafe fn symlinkat>(old_path: P, new_dirfd: RawFd, new_path: P) -> Result<()> { + let old_path = CString::new(old_path.as_ref().as_bytes())?; + let new_path = CString::new(new_path.as_ref().as_bytes())?; + Errno::from_success_code(libc::symlinkat( + old_path.as_ptr(), + new_dirfd, + new_path.as_ptr(), + )) +} + +pub unsafe fn fstatat>(dirfd: RawFd, path: P, flags: AtFlag) -> Result { + use std::mem::MaybeUninit; + let path = CString::new(path.as_ref().as_bytes())?; + let mut filestat = MaybeUninit::::uninit(); + Errno::from_result(libc::fstatat( + dirfd, + path.as_ptr(), + filestat.as_mut_ptr(), + flags.bits(), + ))?; + Ok(filestat.assume_init()) +} + +/// `fionread()` function, equivalent to `ioctl(fd, FIONREAD, *bytes)`. +pub unsafe fn fionread(fd: RawFd) -> Result { + let mut nread: libc::c_int = 0; + Errno::from_result(libc::ioctl(fd, libc::FIONREAD, &mut nread as *mut _))?; + Ok(nread.try_into()?) +} diff --git a/crates/wasi-common/yanix/src/lib.rs b/crates/wasi-common/yanix/src/lib.rs new file mode 100644 index 0000000000..e97e4e0b0f --- /dev/null +++ b/crates/wasi-common/yanix/src/lib.rs @@ -0,0 +1,40 @@ +//! `yanix` stands for Yet Another Nix crate, and, well, it is simply +//! a yet another crate in the spirit of the [nix] crate. As such, +//! this crate is inspired by the original `nix` crate, however, +//! it takes a different approach, using lower-level interfaces with +//! less abstraction, so that it fits better with its main use case +//! which is our WASI implementation, [wasi-common]. +//! +//! [nix]: https://github.com/nix-rust/nix +//! [wasi-common]: https://github.com/bytecodealliance/wasmtime/tree/master/crates/wasi-common +#![cfg(unix)] + +pub mod clock; +pub mod dir; +pub mod fcntl; +pub mod file; +pub mod poll; +pub mod socket; + +mod errno; +mod sys; + +pub mod fadvise { + pub use super::sys::fadvise::*; +} + +pub use errno::Errno; +use std::{ffi, num}; +use thiserror::Error; + +pub type Result = std::result::Result; + +#[derive(Debug, Error)] +pub enum YanixError { + #[error("raw os error {0}")] + Errno(#[from] Errno), + #[error("a nul byte was not found in the expected position")] + NulError(#[from] ffi::NulError), + #[error("integral type conversion failed")] + TryFromIntError(#[from] num::TryFromIntError), +} diff --git a/crates/wasi-common/yanix/src/poll.rs b/crates/wasi-common/yanix/src/poll.rs new file mode 100644 index 0000000000..58cb4426cf --- /dev/null +++ b/crates/wasi-common/yanix/src/poll.rs @@ -0,0 +1,47 @@ +use crate::{Errno, Result}; +use bitflags::bitflags; +use std::{convert::TryInto, os::unix::prelude::*}; + +bitflags! { + pub struct PollFlags: libc::c_short { + const POLLIN = libc::POLLIN; + const POLLPRI = libc::POLLPRI; + const POLLOUT = libc::POLLOUT; + const POLLRDNORM = libc::POLLRDNORM; + const POLLWRNORM = libc::POLLWRNORM; + const POLLRDBAND = libc::POLLRDBAND; + const POLLWRBAND = libc::POLLWRBAND; + const POLLERR = libc::POLLERR; + const POLLHUP = libc::POLLHUP; + const POLLNVAL = libc::POLLNVAL; + } +} + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[repr(C)] +pub struct PollFd(libc::pollfd); + +impl PollFd { + pub unsafe fn new(fd: RawFd, events: PollFlags) -> Self { + Self(libc::pollfd { + fd, + events: events.bits(), + revents: PollFlags::empty().bits(), + }) + } + + pub fn revents(self) -> Option { + PollFlags::from_bits(self.0.revents) + } +} + +pub fn poll(fds: &mut [PollFd], timeout: i32) -> Result { + Errno::from_result(unsafe { + libc::poll( + fds.as_mut_ptr() as *mut libc::pollfd, + fds.len() as libc::nfds_t, + timeout, + ) + }) + .and_then(|nready| nready.try_into().map_err(Into::into)) +} diff --git a/crates/wasi-common/yanix/src/socket.rs b/crates/wasi-common/yanix/src/socket.rs new file mode 100644 index 0000000000..f0f1364e13 --- /dev/null +++ b/crates/wasi-common/yanix/src/socket.rs @@ -0,0 +1,31 @@ +use crate::{Errno, Result}; +use std::os::unix::prelude::*; + +#[derive(Debug, Clone, Copy)] +#[repr(i32)] +pub enum SockType { + Stream = libc::SOCK_STREAM, + Datagram = libc::SOCK_DGRAM, + SeqPacket = libc::SOCK_SEQPACKET, + Raw = libc::SOCK_RAW, + Rdm = libc::SOCK_RDM, +} + +pub unsafe fn get_socket_type(fd: RawFd) -> Result { + use std::mem::{self, MaybeUninit}; + let mut buffer = MaybeUninit::::zeroed().assume_init(); + let mut out_len = mem::size_of::() as libc::socklen_t; + Errno::from_success_code(libc::getsockopt( + fd, + libc::SOL_SOCKET, + libc::SO_TYPE, + &mut buffer as *mut SockType as *mut _, + &mut out_len, + ))?; + assert_eq!( + out_len as usize, + mem::size_of::(), + "invalid SockType value" + ); + Ok(buffer) +} diff --git a/crates/wasi-common/yanix/src/sys/bsd/dir.rs b/crates/wasi-common/yanix/src/sys/bsd/dir.rs new file mode 100644 index 0000000000..fee320e3e5 --- /dev/null +++ b/crates/wasi-common/yanix/src/sys/bsd/dir.rs @@ -0,0 +1,52 @@ +use crate::{ + dir::{Dir, Entry, EntryExt, SeekLoc}, + Errno, Result, +}; +use std::ops::Deref; + +#[derive(Copy, Clone, Debug)] +pub(crate) struct EntryImpl { + dirent: libc::dirent, + loc: SeekLoc, +} + +impl Deref for EntryImpl { + type Target = libc::dirent; + + fn deref(&self) -> &Self::Target { + &self.dirent + } +} + +pub(crate) unsafe fn iter_impl(dir: &Dir) -> Option> { + let errno = Errno::last(); + let dirent = libc::readdir(dir.0.as_ptr()); + if dirent.is_null() { + if errno != Errno::last() { + // TODO This should be verified on different BSD-flavours. + // + // According to 4.3BSD/POSIX.1-2001 man pages, there was an error + // if the errno value has changed at some point during the sequence + // of readdir calls. + Some(Err(Errno::last().into())) + } else { + // Not an error. We've simply reached the end of the stream. + None + } + } else { + Some(Ok(EntryImpl { + dirent: *dirent, + loc: dir.tell(), + })) + } +} + +impl EntryExt for Entry { + fn ino(&self) -> u64 { + self.0.d_ino.into() + } + + fn seek_loc(&self) -> SeekLoc { + self.0.loc + } +} diff --git a/crates/wasi-common/yanix/src/sys/bsd/fadvise.rs b/crates/wasi-common/yanix/src/sys/bsd/fadvise.rs new file mode 100644 index 0000000000..74150f3f27 --- /dev/null +++ b/crates/wasi-common/yanix/src/sys/bsd/fadvise.rs @@ -0,0 +1,51 @@ +use crate::{Errno, Result}; +use std::{convert::TryInto, os::unix::prelude::*}; + +#[derive(Debug, Copy, Clone)] +#[repr(i32)] +pub enum PosixFadviseAdvice { + Normal, + Sequential, + Random, + NoReuse, + WillNeed, + DontNeed, +} + +// There's no posix_fadvise on macOS but we can use fcntl with F_RDADVISE +// command instead to achieve the same +#[cfg(any(target_os = "macos", target_os = "ios"))] +pub unsafe fn posix_fadvise( + fd: RawFd, + offset: libc::off_t, + len: libc::off_t, + _advice: PosixFadviseAdvice, +) -> Result<()> { + // From macOS man pages: + // F_RDADVISE Issue an advisory read async with no copy to user. + // + // The F_RDADVISE command operates on the following structure which holds information passed from + // the user to the system: + // + // struct radvisory { + // off_t ra_offset; /* offset into the file */ + // int ra_count; /* size of the read */ + // }; + let advisory = libc::radvisory { + ra_offset: offset, + ra_count: len.try_into()?, + }; + Errno::from_success_code(libc::fcntl(fd, libc::F_RDADVISE, &advisory)) +} + +// TODO +// On non-macOS BSD's we leave it as no-op for now +#[cfg(not(any(target_os = "macos", target_os = "ios")))] +pub unsafe fn posix_fadvise( + _fd: RawFd, + _offset: libc::off_t, + _len: libc::off_t, + _advice: PosixFadviseAdvice, +) -> Result<()> { + Ok(()) +} diff --git a/crates/wasi-common/yanix/src/sys/bsd/file.rs b/crates/wasi-common/yanix/src/sys/bsd/file.rs new file mode 100644 index 0000000000..b367154812 --- /dev/null +++ b/crates/wasi-common/yanix/src/sys/bsd/file.rs @@ -0,0 +1,18 @@ +use crate::{Errno, Result}; +use std::os::unix::prelude::*; + +pub unsafe fn isatty(fd: RawFd) -> Result { + let res = libc::isatty(fd); + if res == 1 { + // isatty() returns 1 if fd is an open file descriptor referring to a terminal... + Ok(true) + } else { + // ... otherwise 0 is returned, and errno is set to indicate the error. + let errno = Errno::last(); + if errno == Errno::ENOTTY { + Ok(false) + } else { + Err(errno.into()) + } + } +} diff --git a/crates/wasi-common/yanix/src/sys/bsd/mod.rs b/crates/wasi-common/yanix/src/sys/bsd/mod.rs new file mode 100644 index 0000000000..3e6611f7b9 --- /dev/null +++ b/crates/wasi-common/yanix/src/sys/bsd/mod.rs @@ -0,0 +1,3 @@ +pub(crate) mod dir; +pub(crate) mod fadvise; +pub(crate) mod file; diff --git a/crates/wasi-common/yanix/src/sys/linux/dir.rs b/crates/wasi-common/yanix/src/sys/linux/dir.rs new file mode 100644 index 0000000000..72349e3cf4 --- /dev/null +++ b/crates/wasi-common/yanix/src/sys/linux/dir.rs @@ -0,0 +1,46 @@ +use crate::{ + dir::{Dir, Entry, EntryExt, SeekLoc}, + Errno, Result, +}; +use std::ops::Deref; + +#[derive(Copy, Clone, Debug)] +pub(crate) struct EntryImpl(libc::dirent64); + +impl Deref for EntryImpl { + type Target = libc::dirent64; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl EntryExt for Entry { + fn ino(&self) -> u64 { + self.0.d_ino.into() + } + + fn seek_loc(&self) -> SeekLoc { + unsafe { SeekLoc::from_raw(self.0.d_off) } + } +} + +pub(crate) unsafe fn iter_impl(dir: &Dir) -> Option> { + let errno = Errno::last(); + let dirent = libc::readdir64(dir.0.as_ptr()); + if dirent.is_null() { + if errno != Errno::last() { + // TODO This should be verified on different BSD-flavours. + // + // According to 4.3BSD/POSIX.1-2001 man pages, there was an error + // if the errno value has changed at some point during the sequence + // of readdir calls. + Some(Err(Errno::last().into())) + } else { + // Not an error. We've simply reached the end of the stream. + None + } + } else { + Some(Ok(EntryImpl(*dirent))) + } +} diff --git a/crates/wasi-common/yanix/src/sys/linux/fadvise.rs b/crates/wasi-common/yanix/src/sys/linux/fadvise.rs new file mode 100644 index 0000000000..999525e1c7 --- /dev/null +++ b/crates/wasi-common/yanix/src/sys/linux/fadvise.rs @@ -0,0 +1,22 @@ +use crate::{Errno, Result}; +use std::os::unix::prelude::*; + +#[derive(Debug, Copy, Clone)] +#[repr(i32)] +pub enum PosixFadviseAdvice { + Normal = libc::POSIX_FADV_NORMAL, + Sequential = libc::POSIX_FADV_SEQUENTIAL, + Random = libc::POSIX_FADV_RANDOM, + NoReuse = libc::POSIX_FADV_NOREUSE, + WillNeed = libc::POSIX_FADV_WILLNEED, + DontNeed = libc::POSIX_FADV_DONTNEED, +} + +pub unsafe fn posix_fadvise( + fd: RawFd, + offset: libc::off_t, + len: libc::off_t, + advice: PosixFadviseAdvice, +) -> Result<()> { + Errno::from_success_code(libc::posix_fadvise(fd, offset, len, advice as libc::c_int)) +} diff --git a/crates/wasi-common/yanix/src/sys/linux/file.rs b/crates/wasi-common/yanix/src/sys/linux/file.rs new file mode 100644 index 0000000000..c5f88f457b --- /dev/null +++ b/crates/wasi-common/yanix/src/sys/linux/file.rs @@ -0,0 +1,23 @@ +use crate::{Errno, Result}; +use std::os::unix::prelude::*; + +pub unsafe fn isatty(fd: RawFd) -> Result { + let res = libc::isatty(fd); + if res == 1 { + // isatty() returns 1 if fd is an open file descriptor referring to a terminal... + Ok(true) + } else { + // ... otherwise 0 is returned, and errno is set to indicate the error. + let errno = Errno::last(); + // While POSIX specifies ENOTTY if the passed + // fd is *not* a tty, on Linux, some implementations + // may return EINVAL instead. + // + // https://linux.die.net/man/3/isatty + if errno == Errno::ENOTTY || errno == Errno::EINVAL { + Ok(false) + } else { + Err(errno.into()) + } + } +} diff --git a/crates/wasi-common/yanix/src/sys/linux/mod.rs b/crates/wasi-common/yanix/src/sys/linux/mod.rs new file mode 100644 index 0000000000..3e6611f7b9 --- /dev/null +++ b/crates/wasi-common/yanix/src/sys/linux/mod.rs @@ -0,0 +1,3 @@ +pub(crate) mod dir; +pub(crate) mod fadvise; +pub(crate) mod file; diff --git a/crates/wasi-common/yanix/src/sys/mod.rs b/crates/wasi-common/yanix/src/sys/mod.rs new file mode 100644 index 0000000000..523e9bf754 --- /dev/null +++ b/crates/wasi-common/yanix/src/sys/mod.rs @@ -0,0 +1,27 @@ +use crate::dir::SeekLoc; +use cfg_if::cfg_if; + +cfg_if! { + if #[cfg(any(target_os = "linux", + target_os = "android", + target_os = "emscripten"))] { + mod linux; + pub(crate) use self::linux::*; + } + else if #[cfg(any(target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly"))] { + mod bsd; + pub(crate) use self::bsd::*; + } else { + compile_error!("yanix doesn't compile for this platform yet"); + } +} + +pub trait EntryExt { + fn ino(&self) -> u64; + fn seek_loc(&self) -> SeekLoc; +} diff --git a/crates/wast/Cargo.toml b/crates/wast/Cargo.toml index 30b71683a9..af24b7e51e 100644 --- a/crates/wast/Cargo.toml +++ b/crates/wast/Cargo.toml @@ -11,13 +11,10 @@ readme = "README.md" edition = "2018" [dependencies] -cranelift-codegen = { version = "0.50.0", features = ["enable-serde"] } -cranelift-entity = { version = "0.50.0", features = ["enable-serde"] } -cranelift-wasm = { version = "0.50.0", features = ["enable-serde"] } wasmtime-jit = { path = "../jit" } wasmtime-runtime = { path = "../runtime" } wasmtime-environ = { path = "../environ" } -wast = "3.0.0" +wast = "4.0.0" anyhow = "1.0.19" target-lexicon = "0.9.0" diff --git a/crates/wast/src/spectest.rs b/crates/wast/src/spectest.rs index 5d8649e925..293fc0542c 100644 --- a/crates/wast/src/spectest.rs +++ b/crates/wast/src/spectest.rs @@ -1,13 +1,15 @@ #![allow(improper_ctypes)] -use cranelift_codegen::ir::types; -use cranelift_codegen::{ir, isa}; -use cranelift_entity::PrimaryMap; -use cranelift_wasm::{DefinedFuncIndex, Global, GlobalInit, Memory, Table, TableElementType}; use std::cell::RefCell; use std::collections::hash_map::HashMap; use std::rc::Rc; use target_lexicon::HOST; +use wasmtime_environ::entity::PrimaryMap; +use wasmtime_environ::ir::types; +use wasmtime_environ::wasm::{ + DefinedFuncIndex, Global, GlobalInit, Memory, Table, TableElementType, +}; +use wasmtime_environ::{ir, isa}; use wasmtime_environ::{translate_signature, Export, MemoryPlan, Module, TablePlan}; use wasmtime_jit::target_tunables; use wasmtime_runtime::{Imports, InstanceHandle, InstantiationError, VMContext, VMFunctionBody}; diff --git a/crates/wast/src/wast.rs b/crates/wast/src/wast.rs index d2d5305148..17f353bf12 100644 --- a/crates/wast/src/wast.rs +++ b/crates/wast/src/wast.rs @@ -1,5 +1,6 @@ use crate::spectest::instantiate_spectest; use anyhow::{bail, Context as _, Result}; +use std::convert::TryInto; use std::path::Path; use std::str; use wasmtime_jit::{ @@ -254,12 +255,12 @@ impl WastContext { bail!("{}\nunexpected vector in NaN test", context(span)) } RuntimeValue::F32(x) => { - if (x & 0x7fffffff) != 0x7fc00000 { + if !is_canonical_f32_nan(x) { bail!("{}\nexpected canonical NaN", context(span)) } } RuntimeValue::F64(x) => { - if (x & 0x7fffffffffffffff) != 0x7ff8000000000000 { + if !is_canonical_f64_nan(x) { bail!("{}\nexpected canonical NaN", context(span)) } } @@ -271,6 +272,68 @@ impl WastContext { } } } + AssertReturnCanonicalNanF32x4 { span, invoke } => { + match self.perform_invoke(invoke).with_context(|| context(span))? { + ActionOutcome::Returned { values } => { + for v in values.iter() { + match v { + RuntimeValue::I32(_) | RuntimeValue::I64(_) => { + bail!("{}\nunexpected integer in NaN test", context(span)) + } + RuntimeValue::F32(_) | RuntimeValue::F64(_) => bail!( + "{}\nunexpected scalar float in vector NaN test", + context(span) + ), + RuntimeValue::V128(x) => { + for l in 0..4 { + if !is_canonical_f32_nan(&extract_lane_as_u32(x, l)?) { + bail!( + "{}\nexpected f32x4 canonical NaN in lane {}", + context(span), + l + ) + } + } + } + }; + } + } + ActionOutcome::Trapped { message } => { + bail!("{}\nunexpected trap: {}", context(span), message) + } + } + } + AssertReturnCanonicalNanF64x2 { span, invoke } => { + match self.perform_invoke(invoke).with_context(|| context(span))? { + ActionOutcome::Returned { values } => { + for v in values.iter() { + match v { + RuntimeValue::I32(_) | RuntimeValue::I64(_) => { + bail!("{}\nunexpected integer in NaN test", context(span)) + } + RuntimeValue::F32(_) | RuntimeValue::F64(_) => bail!( + "{}\nunexpected scalar float in vector NaN test", + context(span) + ), + RuntimeValue::V128(x) => { + for l in 0..2 { + if !is_canonical_f64_nan(&extract_lane_as_u64(x, l)?) { + bail!( + "{}\nexpected f64x2 canonical NaN in lane {}", + context(span), + l + ) + } + } + } + }; + } + } + ActionOutcome::Trapped { message } => { + bail!("{}\nunexpected trap: {}", context(span), message) + } + } + } AssertReturnArithmeticNan { span, invoke } => { match self.perform_invoke(invoke).with_context(|| context(span))? { ActionOutcome::Returned { values } => { @@ -283,12 +346,12 @@ impl WastContext { bail!("{}\nunexpected vector in NaN test", context(span)) } RuntimeValue::F32(x) => { - if (x & 0x00400000) != 0x00400000 { + if !is_arithmetic_f32_nan(x) { bail!("{}\nexpected arithmetic NaN", context(span)) } } RuntimeValue::F64(x) => { - if (x & 0x0008000000000000) != 0x0008000000000000 { + if !is_arithmetic_f64_nan(x) { bail!("{}\nexpected arithmetic NaN", context(span)) } } @@ -300,6 +363,68 @@ impl WastContext { } } } + AssertReturnArithmeticNanF32x4 { span, invoke } => { + match self.perform_invoke(invoke).with_context(|| context(span))? { + ActionOutcome::Returned { values } => { + for v in values.iter() { + match v { + RuntimeValue::I32(_) | RuntimeValue::I64(_) => { + bail!("{}\nunexpected integer in NaN test", context(span)) + } + RuntimeValue::F32(_) | RuntimeValue::F64(_) => bail!( + "{}\nunexpected scalar float in vector NaN test", + context(span) + ), + RuntimeValue::V128(x) => { + for l in 0..4 { + if !is_arithmetic_f32_nan(&extract_lane_as_u32(x, l)?) { + bail!( + "{}\nexpected f32x4 arithmetic NaN in lane {}", + context(span), + l + ) + } + } + } + }; + } + } + ActionOutcome::Trapped { message } => { + bail!("{}\nunexpected trap: {}", context(span), message) + } + } + } + AssertReturnArithmeticNanF64x2 { span, invoke } => { + match self.perform_invoke(invoke).with_context(|| context(span))? { + ActionOutcome::Returned { values } => { + for v in values.iter() { + match v { + RuntimeValue::I32(_) | RuntimeValue::I64(_) => { + bail!("{}\nunexpected integer in NaN test", context(span)) + } + RuntimeValue::F32(_) | RuntimeValue::F64(_) => bail!( + "{}\nunexpected scalar float in vector NaN test", + context(span) + ), + RuntimeValue::V128(x) => { + for l in 0..2 { + if !is_arithmetic_f64_nan(&extract_lane_as_u64(x, l)?) { + bail!( + "{}\nexpected f64x2 arithmetic NaN in lane {}", + context(span), + l + ) + } + } + } + }; + } + } + ActionOutcome::Trapped { message } => { + bail!("{}\nunexpected trap: {}", context(span), message) + } + } + } AssertInvalid { span, mut module, @@ -384,3 +509,29 @@ impl WastContext { self.run_buffer(path.to_str().unwrap(), &bytes) } } + +fn extract_lane_as_u32(bytes: &[u8; 16], lane: usize) -> Result { + let i = lane * 4; + Ok(u32::from_le_bytes(bytes[i..i + 4].try_into()?)) +} + +fn extract_lane_as_u64(bytes: &[u8; 16], lane: usize) -> Result { + let i = lane * 8; + Ok(u64::from_le_bytes(bytes[i..i + 8].try_into()?)) +} + +fn is_canonical_f32_nan(bits: &u32) -> bool { + return (bits & 0x7fffffff) == 0x7fc00000; +} + +fn is_canonical_f64_nan(bits: &u64) -> bool { + return (bits & 0x7fffffffffffffff) == 0x7ff8000000000000; +} + +fn is_arithmetic_f32_nan(bits: &u32) -> bool { + return (bits & 0x00400000) == 0x00400000; +} + +fn is_arithmetic_f64_nan(bits: &u64) -> bool { + return (bits & 0x0008000000000000) == 0x0008000000000000; +} diff --git a/docs/wasm-rust.md b/docs/wasm-rust.md index 218c873290..d9e0c1fa3f 100644 --- a/docs/wasm-rust.md +++ b/docs/wasm-rust.md @@ -4,7 +4,7 @@ The [Rust Programming Language](https://www.rust-lang.org) supports WebAssembly as a compilation target. If you're not familiar with Rust it's recommended to start [with its introductory documentation](https://www.rust-lang.org/learn). Compiling to WebAssembly will involve specifying the desired target via the -`--target` flag, and to do this there are a number of "traget triples" for +`--target` flag, and to do this there are a number of "target triples" for WebAssembly compilation in Rust: * `wasm32-wasi` - when using `wasmtime` this is likely what you'll be using. The diff --git a/src/bin/wasm2obj.rs b/src/bin/wasm2obj.rs index 0a1ef33f41..067e8d032f 100644 --- a/src/bin/wasm2obj.rs +++ b/src/bin/wasm2obj.rs @@ -29,10 +29,6 @@ ) )] -use cranelift_codegen::settings::Configurable; -use cranelift_codegen::{isa, settings}; -use cranelift_entity::EntityRef; -use cranelift_wasm::DefinedMemoryIndex; use docopt::Docopt; use faerie::Artifact; use serde::Deserialize; @@ -44,13 +40,17 @@ use std::{process, str}; use target_lexicon::Triple; use wasmtime_cli::pick_compilation_strategy; use wasmtime_debug::{emit_debugsections, read_debuginfo}; +use wasmtime_environ::entity::EntityRef; +use wasmtime_environ::settings; +use wasmtime_environ::settings::Configurable; +use wasmtime_environ::wasm::DefinedMemoryIndex; #[cfg(feature = "lightbeam")] use wasmtime_environ::Lightbeam; use wasmtime_environ::{ cache_create_new_config, cache_init, Compiler, Cranelift, ModuleEnvironment, ModuleVmctxInfo, Tunables, VMOffsets, }; -use wasmtime_jit::CompilationStrategy; +use wasmtime_jit::{native, CompilationStrategy}; use wasmtime_obj::emit_module; const USAGE: &str = " @@ -190,11 +190,9 @@ fn handle_module( let isa_builder = match *target { Some(ref target) => { let target = Triple::from_str(&target).map_err(|_| "could not parse --target")?; - isa::lookup(target).map_err(|err| format!("{:?}", err))? + native::lookup(target).map_err(|err| format!("{:?}", err))? } - None => cranelift_native::builder().unwrap_or_else(|_| { - panic!("host machine is not a supported target"); - }), + None => native::builder(), }; let mut flag_builder = settings::builder(); diff --git a/src/bin/wasmtime.rs b/src/bin/wasmtime.rs index 4bc56d6b2f..a0abb06fa6 100644 --- a/src/bin/wasmtime.rs +++ b/src/bin/wasmtime.rs @@ -31,7 +31,6 @@ )] use anyhow::{bail, Context as _, Result}; -use cranelift_codegen::{settings, settings::Configurable}; use docopt::Docopt; use serde::Deserialize; use std::path::{Component, Path}; @@ -40,6 +39,7 @@ use wasi_common::preopen_dir; use wasmtime::{Config, Engine, HostRef, Instance, Module, Store}; use wasmtime_cli::pick_compilation_strategy; use wasmtime_environ::{cache_create_new_config, cache_init}; +use wasmtime_environ::{settings, settings::Configurable}; use wasmtime_interface_types::ModuleData; use wasmtime_jit::Features; use wasmtime_wasi::create_wasi_instance; @@ -341,9 +341,9 @@ fn instantiate_module( .imports() .iter() .map(|i| { - let module_name = i.module().as_str(); + let module_name = i.module(); if let Some(instance) = module_registry.get(module_name) { - let field_name = i.name().as_str(); + let field_name = i.name(); if let Some(export) = instance.borrow().find_export_by_name(field_name) { Ok(export.clone()) } else { @@ -370,12 +370,27 @@ fn handle_module( args: &Args, path: &Path, ) -> Result<()> { - let (instance, _module, data) = instantiate_module(store, module_registry, path)?; + let (instance, module, data) = instantiate_module(store, module_registry, path)?; // If a function to invoke was given, invoke it. if let Some(f) = &args.flag_invoke { let data = ModuleData::new(&data)?; invoke_export(instance, &data, f, args)?; + } else if module + .borrow() + .exports() + .iter() + .find(|export| export.name().is_empty()) + .is_some() + { + // Launch the default command export. + let data = ModuleData::new(&data)?; + invoke_export(instance, &data, "", args)?; + } else { + // If the module doesn't have a default command export, launch the + // _start function if one is present, as a compatibility measure. + let data = ModuleData::new(&data)?; + invoke_export(instance, &data, "_start", args)?; } Ok(()) diff --git a/src/bin/wast.rs b/src/bin/wast.rs index 6637bd5b85..e0967305ba 100644 --- a/src/bin/wast.rs +++ b/src/bin/wast.rs @@ -25,16 +25,16 @@ ) )] -use cranelift_codegen::settings; -use cranelift_codegen::settings::Configurable; -use cranelift_native; use docopt::Docopt; use pretty_env_logger; use serde::Deserialize; use std::path::Path; use std::process; use wasmtime_cli::pick_compilation_strategy; +use wasmtime_environ::settings; +use wasmtime_environ::settings::Configurable; use wasmtime_environ::{cache_create_new_config, cache_init}; +use wasmtime_jit::native; use wasmtime_jit::{Compiler, Features}; use wasmtime_wast::WastContext; @@ -128,9 +128,7 @@ fn main() { process::exit(1); } - let isa_builder = cranelift_native::builder().unwrap_or_else(|_| { - panic!("host machine is not a supported target"); - }); + let isa_builder = native::builder(); let mut flag_builder = settings::builder(); let mut features: Features = Default::default(); diff --git a/tests/instantiate.rs b/tests/instantiate.rs index 5a51ab4c8e..1bf28864d8 100644 --- a/tests/instantiate.rs +++ b/tests/instantiate.rs @@ -1,11 +1,11 @@ -use cranelift_codegen::settings; -use cranelift_codegen::settings::Configurable; use more_asserts::assert_gt; use std::cell::RefCell; use std::collections::HashMap; use std::path::PathBuf; use std::rc::Rc; -use wasmtime_jit::{instantiate, CompilationStrategy, Compiler, NullResolver}; +use wasmtime_environ::settings; +use wasmtime_environ::settings::Configurable; +use wasmtime_jit::{instantiate, native, CompilationStrategy, Compiler, NullResolver}; const PATH_MODULE_RS2WASM_ADD_FUNC: &str = r"tests/wat/rs2wasm-add-func.wat"; @@ -19,9 +19,7 @@ fn test_environ_translate() { let mut flag_builder = settings::builder(); flag_builder.enable("enable_verifier").unwrap(); - let isa_builder = cranelift_native::builder().unwrap_or_else(|_| { - panic!("host machine is not a supported target"); - }); + let isa_builder = native::builder(); let isa = isa_builder.finish(settings::Flags::new(flag_builder)); let mut resolver = NullResolver {}; diff --git a/tests/wast_testsuites.rs b/tests/wast_testsuites.rs index 41c179ea00..8bf9886ae9 100644 --- a/tests/wast_testsuites.rs +++ b/tests/wast_testsuites.rs @@ -1,7 +1,7 @@ -use cranelift_codegen::settings::Configurable; -use cranelift_codegen::{isa, settings}; use std::path::Path; -use wasmtime_jit::{CompilationStrategy, Compiler, Features}; +use wasmtime_environ::settings::Configurable; +use wasmtime_environ::{isa, settings}; +use wasmtime_jit::{native, CompilationStrategy, Compiler, Features}; use wasmtime_wast::WastContext; include!(concat!(env!("OUT_DIR"), "/wast_testsuite_tests.rs")); @@ -30,9 +30,7 @@ fn native_isa() -> Box { flag_builder.enable("avoid_div_traps").unwrap(); flag_builder.enable("enable_simd").unwrap(); - let isa_builder = cranelift_native::builder().unwrap_or_else(|_| { - panic!("host machine is not a supported target"); - }); + let isa_builder = native::builder(); isa_builder.finish(settings::Flags::new(flag_builder)) }