From dbc4eebbfe4404a959153a09cdfb2975882ce203 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Wed, 4 Dec 2019 17:16:29 -0800 Subject: [PATCH 01/14] wig: update to latest witx --- crates/wasi-common/WASI | 2 +- crates/wasi-common/wig/src/raw_types.rs | 302 ++++++++++++------------ 2 files changed, 147 insertions(+), 157 deletions(-) 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/wig/src/raw_types.rs b/crates/wasi-common/wig/src/raw_types.rs index c03b03471e..7168dc3c97 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,76 @@ 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, }) } +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 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, +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 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), +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, } } From 8a4693ef05305d2871beaf14caf1f91143887e9f Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 5 Dec 2019 13:51:32 -0600 Subject: [PATCH 02/14] Pin an older nightly to fix wasi tests (#671) Rust's recent update to libstd of the wasm32-wasi target turned out to be buggy with respect to fetching the process arguments, so we'll need to wait on a fix there before we can run these tests with nightly again. --- .github/actions/install-rust/main.js | 6 +----- .github/workflows/main.yml | 4 +++- 2 files changed, 4 insertions(+), 6 deletions(-) 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..bfadd80e28 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -102,9 +102,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 From 0fce2ac288fbca73628cc8eda43ef5e53c1183c5 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Thu, 5 Dec 2019 11:51:47 -0800 Subject: [PATCH 03/14] deps: Update wasmparser in most places (#670) Doesn't update it in lightbeam. --- crates/api/Cargo.toml | 2 +- crates/debug/Cargo.toml | 2 +- crates/environ/Cargo.toml | 2 +- crates/fuzzing/Cargo.toml | 2 +- crates/interface-types/Cargo.toml | 2 +- crates/jit/Cargo.toml | 2 +- crates/misc/py/Cargo.toml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/api/Cargo.toml b/crates/api/Cargo.toml index 9e4689616c..73fc3efcb3 100644 --- a/crates/api/Cargo.toml +++ b/crates/api/Cargo.toml @@ -21,7 +21,7 @@ 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/debug/Cargo.toml b/crates/debug/Cargo.toml index 6ded069424..3da10aaf57 100644 --- a/crates/debug/Cargo.toml +++ b/crates/debug/Cargo.toml @@ -13,7 +13,7 @@ edition = "2018" [dependencies] gimli = "0.19.0" -wasmparser = "0.39.2" +wasmparser = "0.44.0" 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"] } 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/fuzzing/Cargo.toml b/crates/fuzzing/Cargo.toml index 152cdeae64..476bf3a46a 100644 --- a/crates/fuzzing/Cargo.toml +++ b/crates/fuzzing/Cargo.toml @@ -16,7 +16,7 @@ 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-jit = { path = "../jit" } diff --git a/crates/interface-types/Cargo.toml b/crates/interface-types/Cargo.toml index b94178016d..3a0ec65409 100644 --- a/crates/interface-types/Cargo.toml +++ b/crates/interface-types/Cargo.toml @@ -14,7 +14,7 @@ edition = "2018" 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' } diff --git a/crates/jit/Cargo.toml b/crates/jit/Cargo.toml index 3fc9e0c644..36a789a9a5 100644 --- a/crates/jit/Cargo.toml +++ b/crates/jit/Cargo.toml @@ -21,7 +21,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/misc/py/Cargo.toml b/crates/misc/py/Cargo.toml index a0bb8caf6b..22955b0535 100644 --- a/crates/misc/py/Cargo.toml +++ b/crates/misc/py/Cargo.toml @@ -29,7 +29,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] From cc02214084b8d793aa8d2fb36629504b3ce4d753 Mon Sep 17 00:00:00 2001 From: Peter Huene Date: Thu, 5 Dec 2019 12:33:55 -0800 Subject: [PATCH 04/14] Fix .NET interop issue for Windows release builds. For Windows release builds, the `wasm_valtype_kind` C API return value is being returned as a single byte. The .NET interop signature for this function was expecting an integer-sized return, resulting in three extra bytes being used on Windows. The fix is to limit the corresponding C# enum to a byte representation, which will properly mask the return value from `wasm_valtype_kind`. CI has also been updated to test both debug and release configurations (previously it was only testing debug, hence why this was missed). Also fixed a cast bug in the `declare_vec!` macro in the C API when the element types were pointers to values. The `as_slice` implementation was incorrectly casting away a level of pointer indirection, resulting in corrupted data when accessing the slice's elements. --- .github/workflows/main.yml | 31 ++++++++++++++++++++++++----- crates/api/src/wasm.rs | 6 +++--- crates/misc/dotnet/src/ValueKind.cs | 2 +- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a03f0e55a9..832fcc3388 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -328,7 +328,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 +358,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/crates/api/src/wasm.rs b/crates/api/src/wasm.rs index 2b4f68753a..25f3946a63 100644 --- a/crates/api/src/wasm.rs +++ b/crates/api/src/wasm.rs @@ -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) } } } 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. From 5793b14dd6b7311578f7d6b6f3e9d7642d461556 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Thu, 5 Dec 2019 13:54:39 -0800 Subject: [PATCH 05/14] wig: update comments per code review --- crates/wasi-common/wig/src/raw_types.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/wasi-common/wig/src/raw_types.rs b/crates/wasi-common/wig/src/raw_types.rs index 7168dc3c97..15de007bdb 100644 --- a/crates/wasi-common/wig/src/raw_types.rs +++ b/crates/wasi-common/wig/src/raw_types.rs @@ -225,6 +225,7 @@ fn struct_has_union(s: &witx::StructDatatype) -> bool { }) } +/// 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), @@ -232,7 +233,7 @@ fn tref_has_target_size(tref: &witx::TypeRef) -> bool { } } -/// Test whether the given type has a target-specific size. +/// 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 @@ -241,7 +242,7 @@ fn namedtype_has_target_size(nt: &witx::NamedType) -> bool { } } -/// Test whether the given type ident has a target-specific size. +/// 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, From 8a1b7965d82755d0f0dd5ab41f2339c677cda4c8 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Thu, 5 Dec 2019 14:03:17 -0800 Subject: [PATCH 06/14] Move command function invocation out of wasmtime-instance. (#585) Previously, "_start" was run as part of module instantiation, which meant it was always run, even for wasm modules that weren't being loaded as commands. Now, just invoke it from the wasmtime driver, which for now is the only place that runs wasm modules as actual commands. Also, stop recognizing the old "main" entry point, which tools have stopped using a while ago, and switch to start recognizing the "" entrypoint. --- crates/runtime/src/instance.rs | 31 ------------------------------- src/bin/wasmtime.rs | 17 ++++++++++++++++- 2 files changed, 16 insertions(+), 32 deletions(-) diff --git a/crates/runtime/src/instance.rs b/crates/runtime/src/instance.rs index 70babd8ad0..f1ff001950 100644 --- a/crates/runtime/src/instance.rs +++ b/crates/runtime/src/instance.rs @@ -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/src/bin/wasmtime.rs b/src/bin/wasmtime.rs index 4bc56d6b2f..f0712b11b3 100644 --- a/src/bin/wasmtime.rs +++ b/src/bin/wasmtime.rs @@ -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().as_str().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(()) From cc6e8e1af25e5f9b64e183970d50f62c8338f259 Mon Sep 17 00:00:00 2001 From: Yury Delendik Date: Thu, 5 Dec 2019 16:07:34 -0600 Subject: [PATCH 07/14] Move cranelift dependencies to wasmtime-environ (#669) Groups all CL data structures into single dependency to be used accross wasmtime project. --- Cargo.toml | 4 -- crates/api/Cargo.toml | 5 -- crates/api/src/callable.rs | 2 +- crates/api/src/context.rs | 6 +- crates/api/src/data_structures.rs | 26 --------- crates/api/src/externals.rs | 2 +- crates/api/src/lib.rs | 1 - crates/api/src/runtime.rs | 2 +- crates/api/src/trampoline/create_handle.rs | 4 +- crates/api/src/trampoline/func.rs | 22 ++++--- crates/api/src/trampoline/global.rs | 4 +- crates/api/src/trampoline/memory.rs | 4 +- crates/api/src/trampoline/mod.rs | 58 ------------------- crates/api/src/trampoline/table.rs | 4 +- crates/api/src/trampoline/trap.rs | 4 +- crates/api/src/types.rs | 8 +-- crates/api/src/values.rs | 2 +- crates/debug/Cargo.toml | 3 - crates/debug/src/lib.rs | 2 +- .../debug/src/transform/address_transform.rs | 10 ++-- crates/debug/src/transform/expression.rs | 9 ++- crates/debug/src/transform/line_program.rs | 2 +- crates/debug/src/transform/mod.rs | 2 +- .../debug/src/transform/range_info_builder.rs | 4 +- crates/debug/src/transform/simulate.rs | 4 +- crates/debug/src/transform/unit.rs | 2 +- crates/debug/src/transform/utils.rs | 2 +- crates/environ/src/data_structures.rs | 29 ++++++++++ crates/environ/src/lib.rs | 2 + crates/fuzzing/Cargo.toml | 3 +- crates/fuzzing/src/oracles.rs | 8 +-- crates/interface-types/Cargo.toml | 2 +- crates/interface-types/src/lib.rs | 2 +- crates/jit/Cargo.toml | 1 + crates/jit/src/action.rs | 2 +- crates/jit/src/compiler.rs | 7 ++- crates/jit/src/context.rs | 2 +- crates/jit/src/instantiate.rs | 4 +- crates/jit/src/lib.rs | 3 + crates/jit/src/link.rs | 6 +- crates/jit/src/native.rs | 14 +++++ crates/jit/src/trampoline.rs | 56 ++++++++++++++++++ crates/misc/py/Cargo.toml | 5 -- crates/obj/Cargo.toml | 3 - crates/obj/src/context.rs | 6 +- crates/obj/src/function.rs | 6 +- crates/obj/src/module.rs | 2 +- crates/runtime/Cargo.toml | 3 - crates/runtime/src/export.rs | 4 +- crates/runtime/src/imports.rs | 4 +- crates/runtime/src/instance.rs | 10 ++-- crates/runtime/src/libcalls.rs | 2 +- crates/runtime/src/sig_registry.rs | 2 +- crates/runtime/src/table.rs | 2 +- crates/runtime/src/trap_registry.rs | 2 +- crates/runtime/src/traphandlers.rs | 2 +- crates/test-programs/Cargo.toml | 1 - .../test-programs/tests/wasm_tests/runtime.rs | 2 +- crates/wast/Cargo.toml | 3 - crates/wast/src/spectest.rs | 10 ++-- src/bin/wasm2obj.rs | 16 +++-- src/bin/wasmtime.rs | 2 +- src/bin/wast.rs | 10 ++-- tests/instantiate.rs | 10 ++-- tests/wast_testsuites.rs | 10 ++-- 65 files changed, 223 insertions(+), 233 deletions(-) delete mode 100644 crates/api/src/data_structures.rs create mode 100644 crates/environ/src/data_structures.rs create mode 100644 crates/jit/src/native.rs create mode 100644 crates/jit/src/trampoline.rs 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 73fc3efcb3..f0359a97e4 100644 --- a/crates/api/Cargo.toml +++ b/crates/api/Cargo.toml @@ -13,11 +13,6 @@ 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" } 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..2c8ad3e887 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 diff --git a/crates/api/src/lib.rs b/crates/api/src/lib.rs index 7f2a20354c..c817c3dcc2 100644 --- a/crates/api/src/lib.rs +++ b/crates/api/src/lib.rs @@ -4,7 +4,6 @@ mod callable; mod context; -mod data_structures; mod externals; mod instance; mod module; 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..d2fcfb3d4c 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, }; @@ -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..bbe116b78a 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)] 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..405a54e88c 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 { 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..0398442b3d 100644 --- a/crates/api/src/types.rs +++ b/crates/api/src/types.rs @@ -1,4 +1,4 @@ -use crate::data_structures::{ir, wasm}; +use wasmtime_environ::{ir, wasm}; // Type Representations @@ -156,9 +156,9 @@ pub struct FuncType { impl FuncType { 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() diff --git a/crates/api/src/values.rs b/crates/api/src/values.rs index e215e8b2f2..dc0d96e871 100644 --- a/crates/api/src/values.rs +++ b/crates/api/src/values.rs @@ -1,9 +1,9 @@ -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; #[derive(Debug, Clone)] diff --git a/crates/debug/Cargo.toml b/crates/debug/Cargo.toml index 3da10aaf57..2f753cc23b 100644 --- a/crates/debug/Cargo.toml +++ b/crates/debug/Cargo.toml @@ -14,9 +14,6 @@ edition = "2018" [dependencies] gimli = "0.19.0" wasmparser = "0.44.0" -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"] } 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/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 476bf3a46a..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.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/interface-types/Cargo.toml b/crates/interface-types/Cargo.toml index 3a0ec65409..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.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..f505dbd136 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}; diff --git a/crates/jit/Cargo.toml b/crates/jit/Cargo.toml index 36a789a9a5..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" } 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/py/Cargo.toml b/crates/misc/py/Cargo.toml index 22955b0535..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" } 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 f1ff001950..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( 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/tests/wasm_tests/runtime.rs b/crates/test-programs/tests/wasm_tests/runtime.rs index e954ccb001..52b79a657e 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 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 diff --git a/crates/wast/Cargo.toml b/crates/wast/Cargo.toml index 30b71683a9..03b1ba07df 100644 --- a/crates/wast/Cargo.toml +++ b/crates/wast/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-jit = { path = "../jit" } wasmtime-runtime = { path = "../runtime" } wasmtime-environ = { path = "../environ" } 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/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 f0712b11b3..3ab3217180 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; 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)) } From 1d01779b2e58f898689e84ce20a9c9ae183a14fe Mon Sep 17 00:00:00 2001 From: Austin Wise Date: Thu, 5 Dec 2019 18:58:09 -0800 Subject: [PATCH 08/14] Root bindings so the delegate in FunctionBinding is not GCed. --- crates/misc/dotnet/src/Instance.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) 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; } } From 3d69e04659a9dade78fa80a76e69e3f1b9d1af07 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 6 Dec 2019 16:19:37 -0600 Subject: [PATCH 09/14] Tweak the API of the `Val` type (#679) * Tweak the API of the `Val` type A few updates to the API of the `Val` type: * Added a payload for `V128`. * Replace existing accessor methods with `Option`-returning versions. * Add `unwrap_xxx` family of methods to extract a value and panic. * Remove `Into` conversions which panic, since panicking in `From` or `Into` isn't idiomatic in Rust * Add documentation to all methods/values/enums/etc. * Rename `Val::default` to `Val::null` * Run rustfmt * Review comments --- crates/api/examples/memory.rs | 2 +- crates/api/examples/multi.rs | 14 +-- crates/api/src/externals.rs | 6 +- crates/api/src/trampoline/func.rs | 2 +- crates/api/src/values.rs | 171 ++++++++++++++---------------- crates/api/src/wasm.rs | 6 +- crates/interface-types/src/lib.rs | 9 +- 7 files changed, 103 insertions(+), 107 deletions(-) diff --git a/crates/api/examples/memory.rs b/crates/api/examples/memory.rs index 3c72d87bbb..2b84d81d03 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"); } 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/externals.rs b/crates/api/src/externals.rs index 2c8ad3e887..b02589bf17 100644 --- a/crates/api/src/externals.rs +++ b/crates/api/src/externals.rs @@ -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/trampoline/func.rs b/crates/api/src/trampoline/func.rs index d2fcfb3d4c..d283370b44 100644 --- a/crates/api/src/trampoline/func.rs +++ b/crates/api/src/trampoline/func.rs @@ -80,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::() diff --git a/crates/api/src/values.rs b/crates/api/src/values.rs index dc0d96e871..0d87ea6ddb 100644 --- a/crates/api/src/values.rs +++ b/crates/api/src/values.rs @@ -6,21 +6,71 @@ 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 25f3946a63..5c58edaa10 100644 --- a/crates/api/src/wasm.rs +++ b/crates/api/src/wasm.rs @@ -1553,7 +1553,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) } diff --git a/crates/interface-types/src/lib.rs b/crates/interface-types/src/lib.rs index f505dbd136..512ec33222 100644 --- a/crates/interface-types/src/lib.rs +++ b/crates/interface-types/src/lib.rs @@ -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), }; From e134505b9030e4d169b598890ac116b684f22d30 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 6 Dec 2019 16:19:55 -0600 Subject: [PATCH 10/14] Refactor the `types.rs` types and structures (#681) * Refactor the `types.rs` types and structures A few changes applied along the way: * Documentation added to most methods and types. * Limits are now stored with the maximum as optional rather than a sentinel u32 value for `None`. * The `Name` type was removed in favor of just using a bare `String`. * The `Extern` prefix in the varaints of `ExternType` has been removed since it was redundant. * Accessors of `ExternType` variants no longer panic, and unwrapping versions were added with "unwrap" in the name. * Fields and methods named `r#type` were renamed to `ty` to avoid requiring a raw identifier to use them. * Remove `fail-fast: false` This was left around since the development of GitHub Actions for wasmtime, but they're no longer needed! * Fix compilation of the test-programs code * Fix compilation of wasmtime-py package * Run rustfmt --- .github/workflows/main.yml | 3 - crates/api/examples/memory.rs | 2 +- crates/api/src/externals.rs | 8 +- crates/api/src/instance.rs | 6 +- crates/api/src/lib.rs | 8 +- crates/api/src/module.rs | 33 +-- crates/api/src/trampoline/memory.rs | 6 +- crates/api/src/trampoline/table.rs | 6 +- crates/api/src/types.rs | 234 +++++++++++------- crates/api/src/wasm.rs | 49 ++-- crates/fuzzing/src/oracles/dummy.rs | 10 +- crates/misc/py/src/instance.rs | 6 +- crates/misc/py/src/lib.rs | 11 +- crates/misc/rust/macro/src/lib.rs | 8 +- .../test-programs/tests/wasm_tests/runtime.rs | 4 +- src/bin/wasmtime.rs | 6 +- 16 files changed, 226 insertions(+), 174 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3b25c039fb..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: @@ -165,7 +164,6 @@ jobs: name: Python Wheel runs-on: ${{ matrix.os }} strategy: - fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] steps: @@ -262,7 +260,6 @@ jobs: name: Build wasmtime runs-on: ${{ matrix.os }} strategy: - fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] steps: diff --git a/crates/api/examples/memory.rs b/crates/api/examples/memory.rs index 2b84d81d03..7214a95e8c 100644 --- a/crates/api/examples/memory.rs +++ b/crates/api/examples/memory.rs @@ -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/src/externals.rs b/crates/api/src/externals.rs index b02589bf17..6e1a00bbce 100644 --- a/crates/api/src/externals.rs +++ b/crates/api/src/externals.rs @@ -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()), } } 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 c817c3dcc2..cde6b0a336 100644 --- a/crates/api/src/lib.rs +++ b/crates/api/src/lib.rs @@ -1,4 +1,10 @@ -//! 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)] 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/trampoline/memory.rs b/crates/api/src/trampoline/memory.rs index bbe116b78a..9437876219 100644 --- a/crates/api/src/trampoline/memory.rs +++ b/crates/api/src/trampoline/memory.rs @@ -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/table.rs b/crates/api/src/trampoline/table.rs index 405a54e88c..5d2441bf92 100644 --- a/crates/api/src/trampoline/table.rs +++ b/crates/api/src/trampoline/table.rs @@ -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/types.rs b/crates/api/src/types.rs index 0398442b3d..2084985f87 100644 --- a/crates/api/src/types.rs +++ b/crates/api/src/types.rs @@ -4,35 +4,43 @@ use wasmtime_environ::{ir, wasm}; // 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,6 +177,10 @@ 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 wasmtime_environ::ir::{types, AbiParam, ArgumentPurpose, Signature}; use wasmtime_jit::native; @@ -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/wasm.rs b/crates/api/src/wasm.rs index 5c58edaa10..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}; @@ -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, @@ -1619,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/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/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/test-programs/tests/wasm_tests/runtime.rs b/crates/test-programs/tests/wasm_tests/runtime.rs index 52b79a657e..2d21c29e88 100644 --- a/crates/test-programs/tests/wasm_tests/runtime.rs +++ b/crates/test-programs/tests/wasm_tests/runtime.rs @@ -68,9 +68,9 @@ pub fn instantiate(data: &[u8], bin_name: &str, workspace: Option<&Path>) -> any .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.find_export_by_name(field_name) { Ok(export.clone()) } else { diff --git a/src/bin/wasmtime.rs b/src/bin/wasmtime.rs index 3ab3217180..a0abb06fa6 100644 --- a/src/bin/wasmtime.rs +++ b/src/bin/wasmtime.rs @@ -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 { @@ -380,7 +380,7 @@ fn handle_module( .borrow() .exports() .iter() - .find(|export| export.name().as_str().is_empty()) + .find(|export| export.name().is_empty()) .is_some() { // Launch the default command export. From ec8144b87d42a82b254fc18762d1e1557d3e90f6 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Fri, 6 Dec 2019 19:47:29 -0800 Subject: [PATCH 11/14] Add support for SIMD NaN directives (#686) * Add support for SIMD NaN directives * Use is_*_nan helper methods for scalar NaN directives --- crates/wast/Cargo.toml | 2 +- crates/wast/src/wast.rs | 159 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 156 insertions(+), 5 deletions(-) diff --git a/crates/wast/Cargo.toml b/crates/wast/Cargo.toml index 03b1ba07df..af24b7e51e 100644 --- a/crates/wast/Cargo.toml +++ b/crates/wast/Cargo.toml @@ -14,7 +14,7 @@ edition = "2018" 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/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; +} From 51f880f62550f2aabf11567eec4abb31d9d84f07 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 9 Dec 2019 01:40:05 +0100 Subject: [PATCH 12/14] Add yanix crate and replace nix with yanix in wasi-common (#649) * Add yanix crate This commit adds `yanix` crate as a Unix dependency for `wasi-common`. `yanix` stands for Yet Another Nix crate and is exactly what the name suggests: a crate in the spirit of the `nix` crate, but which takes a different approach, using lower-level interfaces with less abstraction, so that it fits better with its main use case, implementation of WASI syscalls. * Replace nix with yanix crate Having introduced `yanix` crate as an in-house replacement for the `nix` crate, this commit makes the necessary changes to `wasi-common` to depend _only_ on `yanix` crate. * Address review comments * make `fd_dup` unsafe * rename `get_fd` to `get_fd_flags`, etc. * reuse `io::Error::last_os_error()` to get the last errno value * Address more comments * make all `fcntl` fns unsafe * adjust `wasi-common` impl appropriately * Make all fns operating on RawFd unsafe * Fix linux build * Address more comments --- crates/wasi-common/Cargo.toml | 2 +- crates/wasi-common/src/error.rs | 33 +- crates/wasi-common/src/host.rs | 61 ++- crates/wasi-common/src/hostcalls_impl/fs.rs | 62 ---- .../wasi-common/src/old/snapshot_0/error.rs | 33 +- crates/wasi-common/src/old/snapshot_0/host.rs | 61 ++- .../src/old/snapshot_0/hostcalls_impl/fs.rs | 62 ---- .../wasi-common/src/old/snapshot_0/sys/mod.rs | 2 +- .../snapshot_0/sys/unix/bsd/hostcalls_impl.rs | 350 ++++++------------ .../src/old/snapshot_0/sys/unix/bsd/mod.rs | 25 +- .../old/snapshot_0/sys/unix/bsd/oshandle.rs | 2 +- .../src/old/snapshot_0/sys/unix/dir.rs | 174 --------- .../old/snapshot_0/sys/unix/fdentry_impl.rs | 24 +- .../src/old/snapshot_0/sys/unix/host_impl.rs | 231 ++++++------ .../snapshot_0/sys/unix/hostcalls_impl/fs.rs | 290 ++++++++------- .../sys/unix/hostcalls_impl/fs_helpers.rs | 34 +- .../sys/unix/hostcalls_impl/misc.rs | 61 ++- .../sys/unix/linux/hostcalls_impl.rs | 188 ++-------- .../src/old/snapshot_0/sys/unix/linux/mod.rs | 32 +- .../src/old/snapshot_0/sys/unix/mod.rs | 8 +- .../sys/windows/hostcalls_impl/fs.rs | 5 +- crates/wasi-common/src/sys/mod.rs | 2 +- .../src/sys/unix/bsd/hostcalls_impl.rs | 350 ++++++------------ crates/wasi-common/src/sys/unix/bsd/mod.rs | 25 +- .../wasi-common/src/sys/unix/bsd/oshandle.rs | 2 +- crates/wasi-common/src/sys/unix/dir.rs | 174 --------- .../wasi-common/src/sys/unix/fdentry_impl.rs | 24 +- crates/wasi-common/src/sys/unix/host_impl.rs | 231 ++++++------ .../src/sys/unix/hostcalls_impl/fs.rs | 290 ++++++++------- .../src/sys/unix/hostcalls_impl/fs_helpers.rs | 34 +- .../src/sys/unix/hostcalls_impl/misc.rs | 61 ++- .../src/sys/unix/linux/hostcalls_impl.rs | 188 ++-------- crates/wasi-common/src/sys/unix/linux/mod.rs | 32 +- crates/wasi-common/src/sys/unix/mod.rs | 8 +- .../src/sys/windows/hostcalls_impl/fs.rs | 3 +- crates/wasi-common/yanix/Cargo.toml | 18 + crates/wasi-common/yanix/LICENSE | 220 +++++++++++ crates/wasi-common/yanix/src/clock.rs | 29 ++ crates/wasi-common/yanix/src/dir.rs | 162 ++++++++ crates/wasi-common/yanix/src/errno.rs | 227 ++++++++++++ crates/wasi-common/yanix/src/fcntl.rs | 33 ++ crates/wasi-common/yanix/src/file.rs | 198 ++++++++++ crates/wasi-common/yanix/src/lib.rs | 40 ++ crates/wasi-common/yanix/src/poll.rs | 47 +++ crates/wasi-common/yanix/src/socket.rs | 31 ++ crates/wasi-common/yanix/src/sys/bsd/dir.rs | 52 +++ .../wasi-common/yanix/src/sys/bsd/fadvise.rs | 51 +++ crates/wasi-common/yanix/src/sys/bsd/file.rs | 18 + crates/wasi-common/yanix/src/sys/bsd/mod.rs | 3 + crates/wasi-common/yanix/src/sys/linux/dir.rs | 46 +++ .../yanix/src/sys/linux/fadvise.rs | 22 ++ .../wasi-common/yanix/src/sys/linux/file.rs | 23 ++ crates/wasi-common/yanix/src/sys/linux/mod.rs | 3 + crates/wasi-common/yanix/src/sys/mod.rs | 27 ++ 54 files changed, 2383 insertions(+), 2031 deletions(-) delete mode 100644 crates/wasi-common/src/old/snapshot_0/sys/unix/dir.rs delete mode 100644 crates/wasi-common/src/sys/unix/dir.rs create mode 100644 crates/wasi-common/yanix/Cargo.toml create mode 100644 crates/wasi-common/yanix/LICENSE create mode 100644 crates/wasi-common/yanix/src/clock.rs create mode 100644 crates/wasi-common/yanix/src/dir.rs create mode 100644 crates/wasi-common/yanix/src/errno.rs create mode 100644 crates/wasi-common/yanix/src/fcntl.rs create mode 100644 crates/wasi-common/yanix/src/file.rs create mode 100644 crates/wasi-common/yanix/src/lib.rs create mode 100644 crates/wasi-common/yanix/src/poll.rs create mode 100644 crates/wasi-common/yanix/src/socket.rs create mode 100644 crates/wasi-common/yanix/src/sys/bsd/dir.rs create mode 100644 crates/wasi-common/yanix/src/sys/bsd/fadvise.rs create mode 100644 crates/wasi-common/yanix/src/sys/bsd/file.rs create mode 100644 crates/wasi-common/yanix/src/sys/bsd/mod.rs create mode 100644 crates/wasi-common/yanix/src/sys/linux/dir.rs create mode 100644 crates/wasi-common/yanix/src/sys/linux/fadvise.rs create mode 100644 crates/wasi-common/yanix/src/sys/linux/file.rs create mode 100644 crates/wasi-common/yanix/src/sys/linux/mod.rs create mode 100644 crates/wasi-common/yanix/src/sys/mod.rs 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/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/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; +} From e13fabb2760b31ee0b6a0976940f9c957c4c4c9b Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 9 Dec 2019 04:08:47 -0800 Subject: [PATCH 13/14] Start to update the `wasi` crate in wasi tests (#675) * Move `wasi` to `wasi_old` in wasi-tests Leave space for the new `wasi` crate but allow us to incrementally update tests. * Update the big_random_buf test * Update clock_time_get test * Update close_preopen test * Review comments * Update to latest Wasmtime API --- crates/test-programs/build.rs | 2 + .../test-programs/tests/wasm_tests/runtime.rs | 71 +++++++++++-------- crates/test-programs/wasi-tests/Cargo.toml | 3 +- .../wasi-tests/src/bin/big_random_buf.rs | 9 +-- .../wasi-tests/src/bin/clock_time_get.rs | 26 ++----- .../wasi-tests/src/bin/close_preopen.rs | 44 ++++-------- .../wasi-tests/src/bin/dangling_fd.rs | 2 +- .../wasi-tests/src/bin/dangling_symlink.rs | 2 +- .../wasi-tests/src/bin/directory_seek.rs | 2 +- .../wasi-tests/src/bin/fd_advise.rs | 2 +- .../wasi-tests/src/bin/fd_filestat_set.rs | 2 +- .../wasi-tests/src/bin/fd_readdir.rs | 2 +- .../wasi-tests/src/bin/file_allocate.rs | 2 +- .../wasi-tests/src/bin/file_pread_pwrite.rs | 2 +- .../wasi-tests/src/bin/file_seek_tell.rs | 2 +- .../src/bin/file_unbuffered_write.rs | 2 +- .../wasi-tests/src/bin/interesting_paths.rs | 2 +- .../wasi-tests/src/bin/isatty.rs | 2 +- .../wasi-tests/src/bin/nofollow_errors.rs | 2 +- .../wasi-tests/src/bin/path_filestat.rs | 2 +- .../wasi-tests/src/bin/path_link.rs | 2 +- .../src/bin/path_open_create_existing.rs | 2 +- .../src/bin/path_open_dirfd_not_dir.rs | 2 +- .../wasi-tests/src/bin/path_rename.rs | 2 +- .../src/bin/path_rename_trailing_slashes.rs | 2 +- .../src/bin/path_symlink_trailing_slashes.rs | 2 +- .../wasi-tests/src/bin/poll_oneoff.rs | 2 +- .../wasi-tests/src/bin/readlink.rs | 2 +- .../wasi-tests/src/bin/readlink_no_buffer.rs | 2 +- .../bin/remove_directory_trailing_slashes.rs | 2 +- .../src/bin/remove_nonempty_directory.rs | 2 +- .../wasi-tests/src/bin/renumber.rs | 2 +- .../wasi-tests/src/bin/sched_yield.rs | 2 +- .../test-programs/wasi-tests/src/bin/stdio.rs | 2 +- .../wasi-tests/src/bin/symlink_loop.rs | 2 +- .../wasi-tests/src/bin/truncation_rights.rs | 2 +- .../src/bin/unlink_file_trailing_slashes.rs | 2 +- crates/test-programs/wasi-tests/src/lib.rs | 36 +++++++++- crates/test-programs/wasi-tests/src/utils.rs | 2 +- .../wasi-tests/src/wasi_wrappers.rs | 2 +- 40 files changed, 137 insertions(+), 120 deletions(-) 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 2d21c29e88..ea631eaab1 100644 --- a/crates/test-programs/tests/wasm_tests/runtime.rs +++ b/crates/test-programs/tests/wasm_tests/runtime.rs @@ -1,6 +1,6 @@ use anyhow::{bail, Context}; 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}; @@ -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(); - if let Some(instance) = module_registry.get(module_name) { - 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, - 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, From bb5f063edf54da20844f6935e7b8d6ba83777781 Mon Sep 17 00:00:00 2001 From: Cedric Hutchings Date: Mon, 9 Dec 2019 10:28:32 -0500 Subject: [PATCH 14/14] Replace 'traget' with 'target' (#687) --- docs/wasm-rust.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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