726: Add serialization for WASI state  r=MarkMcCaskey a=MarkMcCaskey

part of #700 

Due to the trait objects from #583 , we can't use `serde` derive for this or use serde traits directly, we have to do some custom serialization (edit: luckily there's a crate for this: `typetag`)

Co-authored-by: Mark McCaskey <mark@wasmer.io>
Co-authored-by: Mark McCaskey <markmccaskey@users.noreply.github.com>
Co-authored-by: Syrus Akbary <me@syrusakbary.com>
This commit is contained in:
bors[bot] 2019-09-04 23:08:10 +00:00 committed by GitHub
commit 15066555e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 539 additions and 137 deletions

83
Cargo.lock generated
View File

@ -409,6 +409,15 @@ dependencies = [
"memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ctor"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "digest"
version = "0.8.1"
@ -467,6 +476,14 @@ dependencies = [
"termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "erased-serde"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "errno"
version = "0.2.4"
@ -535,6 +552,7 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -555,6 +573,16 @@ dependencies = [
"wasi 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ghost"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "glob"
version = "0.2.11"
@ -629,6 +657,26 @@ dependencies = [
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "inventory"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ctor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"ghost 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"inventory-impl 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "inventory-impl"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "itertools"
version = "0.8.0"
@ -1344,6 +1392,28 @@ name = "typenum"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "typetag"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"erased-serde 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"inventory 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
"typetag-impl 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "typetag-impl"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicode-segmentation"
version = "1.3.0"
@ -1428,7 +1498,9 @@ dependencies = [
"errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
"structopt 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
"typetag 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"wabt 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmer-clif-backend 0.6.0",
"wasmer-dev-utils 0.6.0",
@ -1661,12 +1733,15 @@ dependencies = [
name = "wasmer-wasi"
version = "0.6.0"
dependencies = [
"bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"generational-arena 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
"typetag 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmer-runtime-core 0.6.0",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -1679,6 +1754,7 @@ dependencies = [
"wasmer-clif-backend 0.6.0",
"wasmer-dev-utils 0.6.0",
"wasmer-llvm-backend 0.6.0",
"wasmer-runtime 0.6.0",
"wasmer-runtime-core 0.6.0",
"wasmer-singlepass-backend 0.6.0",
"wasmer-wasi 0.6.0",
@ -1812,12 +1888,14 @@ dependencies = [
"checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6"
"checksum csv 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "37519ccdfd73a75821cac9319d4fce15a81b9fcf75f951df5b9988aa3a0af87d"
"checksum csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9b5cadb6b25c77aeff80ba701712494213f4a8418fcda2ee11b6560c3ad0bf4c"
"checksum ctor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "5b6b2f4752cc29efbfd03474c532ce8f916f2d44ec5bb8c21f93bc76e5365528"
"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
"checksum dynasm 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f36d49ab6f8ecc642d2c6ee10fda04ba68003ef0277300866745cdde160e6b40"
"checksum dynasmrt 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a4c408a211e7f5762829f5e46bdff0c14bc3b1517a21a4bb781c716bf88b0c68"
"checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b"
"checksum enum-methods 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7798e7da2d4cb0d6d6fc467e8d6b5bf247e9e989f786dde1732d79899c32bb10"
"checksum env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3"
"checksum erased-serde 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3beee4bc16478a1b26f2e80ad819a52d24745e292f521a63c16eea5f74b7eb60"
"checksum errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c2a071601ed01b988f896ab14b95e67335d1eeb50190932a1320f7fe3cadc84e"
"checksum errno-dragonfly 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "14ca354e36190500e1e1fb267c647932382b54053c50b14970856c0b00a35067"
"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2"
@ -1829,6 +1907,7 @@ dependencies = [
"checksum generational-arena 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4024f96ffa0ebaaf36aa589cd41f2fd69f3a5e6fd02c86e11e12cdf41d5b46a3"
"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
"checksum getrandom 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "6171a6cc63fbabbe27c2b5ee268e8b7fe5dc1eb0dd2dfad537c1dfed6f69117e"
"checksum ghost 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2a36606a68532b5640dc86bb1f33c64b45c4682aad4c50f3937b317ea387f3d6"
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
"checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
"checksum goblin 0.0.24 (registry+https://github.com/rust-lang/crates.io-index)" = "e3fa261d919c1ae9d1e4533c4a2f99e10938603c4208d56c05bec7a872b661b0"
@ -1838,6 +1917,8 @@ dependencies = [
"checksum indexmap 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a4d6d89e0948bf10c08b9ecc8ac5b83f07f857ebe2c0cbe38de15b4e4f510356"
"checksum inkwell 0.1.0 (git+https://github.com/wasmerio/inkwell?branch=llvm8-0)" = "<none>"
"checksum inkwell_internal_macros 0.1.0 (git+https://github.com/wasmerio/inkwell?branch=llvm8-0)" = "<none>"
"checksum inventory 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f4cece20baea71d9f3435e7bbe9adf4765f091c5fe404975f844006964a71299"
"checksum inventory-impl 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c2869bf972e998977b1cb87e60df70341d48e48dca0823f534feb91ea44adaf9"
"checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358"
"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
@ -1925,6 +2006,8 @@ dependencies = [
"checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f"
"checksum toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7aabe75941d914b72bf3e5d3932ed92ce0664d49d8432305a8b547c37227724"
"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169"
"checksum typetag 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6ebb2c484029d695fb68a06d80e1536c68d491b3e0cf874c66abed255e831cfe"
"checksum typetag-impl 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b63fd4799e4d0ec5cf0b055ebb8e2c3a657bbf76a84f6edc77ca60780e000204"
"checksum unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1967f4cdfc355b37fd76d2a954fb2ed3871034eb4f26d60537d88795cfc332a9"
"checksum unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20"
"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"

View File

@ -66,6 +66,10 @@ wabt = "0.9.1"
glob = "0.3.0"
rustc_version = "0.2.3"
[dev-dependencies]
serde = { version = "1", features = ["derive"] } # used by the plugin example
typetag = "0.1" # used by the plugin example
[features]
default = ["fast-tests", "wasi", "backend-cranelift"]
"loader-kernel" = ["wasmer-kernel-loader"]

View File

@ -70,12 +70,13 @@ wasitests-singlepass: wasitests-setup
cargo test --manifest-path lib/wasi-tests/Cargo.toml --release --features singlepass -- --test-threads=1
wasitests-cranelift: wasitests-setup
cargo test --manifest-path lib/wasi-tests/Cargo.toml --release --features clif -- --test-threads=1
cargo test --manifest-path lib/wasi-tests/Cargo.toml --release --features clif -- --test-threads=1 --nocapture
wasitests-llvm: wasitests-setup
cargo test --manifest-path lib/wasi-tests/Cargo.toml --release --features llvm -- --test-threads=1
wasitests-unit:
wasitests-unit: wasitests-setup
cargo test --manifest-path lib/wasi-tests/Cargo.toml --release --features clif -- --test-threads=1 --nocapture
cargo test --manifest-path lib/wasi/Cargo.toml --release
wasitests: wasitests-unit wasitests-singlepass wasitests-cranelift wasitests-llvm

View File

@ -1,3 +1,4 @@
use serde::{Deserialize, Serialize};
use wasmer_runtime::{func, imports, instantiate};
use wasmer_runtime_core::vm::Ctx;
use wasmer_wasi::{
@ -13,7 +14,7 @@ fn it_works(_ctx: &mut Ctx) -> i32 {
5
}
#[derive(Debug)]
#[derive(Debug, Serialize, Deserialize)]
pub struct LoggingWrapper {
pub wasm_module_name: String,
}
@ -86,6 +87,8 @@ impl std::io::Write for LoggingWrapper {
}
// the WasiFile methods aren't relevant for a write-only Stdout-like implementation
// we must use typetag and serde so that our trait objects can be safely Serialized and Deserialized
#[typetag::serde]
impl WasiFile for LoggingWrapper {
fn last_accessed(&self) -> u64 {
0

View File

@ -10,6 +10,7 @@ build = "build/mod.rs"
[dependencies]
wasmer-runtime-core = { path = "../runtime-core", version = "0.6.0" }
wasmer-runtime = { path = "../runtime", version = "0.6.0" }
wasmer-wasi = { path = "../wasi", version = "0.6.0" }
# hack to get tests to work
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.6.0", optional = true }

View File

@ -1 +1,60 @@
// nothing to see here
#![cfg(test)]
use wasmer_runtime::{compile, Func};
use wasmer_runtime_core::vm::Ctx;
use wasmer_wasi::{state::*, *};
use std::ffi::c_void;
#[test]
fn serializing_works() {
let args = vec![
b"program_name".into_iter().cloned().collect(),
b"arg1".into_iter().cloned().collect(),
];
let envs = vec![
b"PATH=/bin".into_iter().cloned().collect(),
b"GOROOT=$HOME/.cargo/bin".into_iter().cloned().collect(),
];
let wasm_binary = include_bytes!("../wasitests/fd_read.wasm");
let import_object = generate_import_object(
args.clone(),
envs.clone(),
vec![],
vec![(
".".to_string(),
std::path::PathBuf::from("wasitests/test_fs/hamlet"),
)],
);
let module = compile(&wasm_binary[..])
.map_err(|e| format!("Can't compile module: {:?}", e))
.unwrap();
let state_bytes = {
let instance = module.instantiate(&import_object).unwrap();
let start: Func<(), ()> = instance.func("_start").unwrap();
start.call().unwrap();
let state = get_wasi_state(instance.context());
assert_eq!(state.args, args);
assert_eq!(state.envs, envs);
let bytes = state.freeze().unwrap();
bytes
};
let mut instance = module.instantiate(&import_object).unwrap();
let wasi_state = Box::new(WasiState::unfreeze(&state_bytes).unwrap());
instance.context_mut().data = Box::into_raw(wasi_state) as *mut c_void;
let second_entry: Func<(), i32> = instance.func("second_entry").unwrap();
let result = second_entry.call().unwrap();
assert_eq!(result, true as i32);
}
#[allow(clippy::mut_from_ref)]
pub(crate) fn get_wasi_state(ctx: &Ctx) -> &mut WasiState {
unsafe { state::get_wasi_state(&mut *(ctx as *const Ctx as *mut Ctx)) }
}

View File

@ -0,0 +1,14 @@
#[test]
fn test_fd_read() {
assert_wasi_output!(
"../../wasitests/fd_read.wasm",
"fd_read",
vec![],
vec![(
".".to_string(),
::std::path::PathBuf::from("wasitests/test_fs/hamlet")
),],
vec![],
"../../wasitests/fd_read.out"
);
}

View File

@ -9,6 +9,7 @@ mod create_dir;
mod envvar;
mod fd_allocate;
mod fd_pread;
mod fd_read;
mod fd_sync;
mod file_metadata;
mod fs_sandbox_test;

View File

@ -0,0 +1,3 @@
SCENE IV. The Queen's closet.
Enter QUEEN GERTRUDE and POLO

View File

@ -0,0 +1,86 @@
// Args:
// mapdir: .:wasitests/test_fs/hamlet
// this program is used in the pause/resume test
use std::fs;
#[cfg(target_os = "wasi")]
use std::os::wasi::prelude::AsRawFd;
use std::path::PathBuf;
#[cfg(target_os = "wasi")]
#[repr(C)]
struct WasiIovec {
pub buf: u32,
pub buf_len: u32,
}
#[cfg(target_os = "wasi")]
#[link(wasm_import_module = "wasi_unstable")]
extern "C" {
fn fd_read(fd: u32, iovs: u32, iovs_len: u32, nread: u32) -> u16;
}
#[cfg(target_os = "wasi")]
fn read(fd: u32, iovs: &[&mut [u8]]) -> u32 {
let mut nread = 0;
let mut processed_iovs = vec![];
for iov in iovs {
processed_iovs.push(WasiIovec {
buf: iov.as_ptr() as usize as u32,
buf_len: iov.len() as u32,
})
}
unsafe {
fd_read(
fd,
processed_iovs.as_ptr() as usize as u32,
processed_iovs.len() as u32,
&mut nread as *mut u32 as usize as u32,
);
}
nread
}
fn main() {
#[cfg(not(target_os = "wasi"))]
let mut base = PathBuf::from("wasitests/test_fs/hamlet");
#[cfg(target_os = "wasi")]
let mut base = PathBuf::from(".");
base.push("act3/scene4.txt");
let mut file = fs::File::open(&base).expect("Could not open file");
let mut buffer = [0u8; 64];
#[cfg(target_os = "wasi")]
{
let raw_fd = file.as_raw_fd();
assert_eq!(read(raw_fd, &[&mut buffer]), 64);
let str_val = std::str::from_utf8(&buffer[..]).unwrap().to_string();
println!("{}", &str_val);
}
// leak the file handle so that we can use it later
std::mem::forget(file);
#[cfg(not(target_os = "wasi"))]
{
// eh, just print the output directly
print!(
"SCENE IV. The Queen's closet.
Enter QUEEN GERTRUDE and POLO"
);
}
}
#[cfg(target_os = "wasi")]
#[no_mangle]
fn second_entry() -> bool {
let raw_fd = 5;
let mut buffer = [0u8; 8];
let result = read(raw_fd, &[&mut buffer]);
&buffer == b"NIUS \n\nL"
}

Binary file not shown.

View File

@ -8,14 +8,17 @@ repository = "https://github.com/wasmerio/wasmer"
edition = "2018"
[dependencies]
wasmer-runtime-core = { path = "../runtime-core", version = "0.6.0" }
libc = "0.2.60"
rand = "0.7.0"
# wasmer-runtime-abi = { path = "../runtime-abi" }
generational-arena = "0.2.2"
log = "0.4.8"
bincode = "1"
byteorder = "1.3.2"
generational-arena = { version = "0.2.2", features = ["serde"] }
libc = "0.2.60"
log = "0.4.8"
rand = "0.7.0"
time = "0.1.42"
typetag = "0.1"
serde = { version = "1", features = ["derive"] }
# wasmer-runtime-abi = { path = "../runtime-abi" }
wasmer-runtime-core = { path = "../runtime-core", version = "0.6.0" }
[target.'cfg(windows)'.dependencies]
winapi = "0.3.8"

View File

@ -44,20 +44,23 @@ pub fn generate_import_object(
mapped_dirs: Vec<(String, PathBuf)>,
) -> ImportObject {
let state_gen = move || {
// TODO: look into removing all these unnecessary clones
fn state_destructor(data: *mut c_void) {
unsafe {
drop(Box::from_raw(data as *mut WasiState));
}
}
let preopened_files = preopened_files.clone();
let mapped_dirs = mapped_dirs.clone();
let state = Box::new(WasiState {
fs: WasiFs::new(&preopened_files, &mapped_dirs).unwrap(),
args: &args[..],
envs: &envs[..],
fs: WasiFs::new(&preopened_files, &mapped_dirs).expect("Could not create WASI FS"),
args: args.clone(),
envs: envs.clone(),
});
(
Box::leak(state) as *mut WasiState as *mut c_void,
Box::into_raw(state) as *mut c_void,
state_destructor as fn(*mut c_void),
)
};

View File

@ -8,12 +8,13 @@ pub use self::types::*;
use crate::syscalls::types::*;
use generational_arena::Arena;
pub use generational_arena::Index as Inode;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::{
borrow::Borrow,
cell::Cell,
fs,
io::{self, Write},
io::Write,
path::{Path, PathBuf},
time::SystemTime,
};
@ -35,7 +36,7 @@ pub unsafe fn get_wasi_state(ctx: &mut Ctx) -> &mut WasiState {
pub const MAX_SYMLINKS: u32 = 128;
/// A file that Wasi knows about that may or may not be open
#[derive(Debug)]
#[derive(Debug, Serialize, Deserialize)]
pub struct InodeVal {
pub stat: __wasi_filestat_t,
pub is_preopened: bool,
@ -43,26 +44,7 @@ pub struct InodeVal {
pub kind: Kind,
}
/*impl WasiFdBacking for InodeVal {
fn get_stat(&self) -> &__wasi_filestat_t {
&self.stat
}
fn get_stat_mut(&mut self) -> &mut __wasi_filestat_t {
&mut self.stat
}
fn is_preopened(&self) -> bool {
self.is_preopened
}
fn get_name(&self) -> &str {
self.name.as_ref()
}
}*/
#[allow(dead_code)]
#[derive(Debug)]
#[derive(Debug, Serialize, Deserialize)]
pub enum Kind {
File {
/// the open file, if it's open
@ -106,16 +88,25 @@ pub enum Kind {
},
}
#[derive(Debug)]
#[derive(Debug, Serialize, Deserialize)]
pub struct Fd {
pub rights: __wasi_rights_t,
pub rights_inheriting: __wasi_rights_t,
pub flags: __wasi_fdflags_t,
pub offset: u64,
pub open_flags: u16,
pub inode: Inode,
}
#[derive(Debug)]
impl Fd {
pub const READ: u16 = 1;
pub const WRITE: u16 = 2;
pub const APPEND: u16 = 4;
pub const TRUNCATE: u16 = 8;
pub const CREATE: u16 = 16;
}
#[derive(Debug, Serialize, Deserialize)]
/// Warning, modifying these fields directly may cause invariants to break and
/// should be considered unsafe. These fields may be made private in a future release
pub struct WasiFs {
@ -150,9 +141,9 @@ impl WasiFs {
inode_counter: Cell::new(1024),
orphan_fds: HashMap::new(),
stdin: Box::new(Stdin(io::stdin())),
stdout: Box::new(Stdout(io::stdout())),
stderr: Box::new(Stderr(io::stderr())),
stdin: Box::new(Stdin),
stdout: Box::new(Stdout),
stderr: Box::new(Stderr),
};
// create virtual root
let root_inode = {
@ -176,8 +167,8 @@ impl WasiFs {
& (!__WASI_RIGHT_PATH_REMOVE_DIRECTORY)*/;
let inode = wasi_fs.create_virtual_root();
let fd = wasi_fs
.create_fd(root_rights, root_rights, 0, inode)
.expect("Could not create root fd");
.create_fd(root_rights, root_rights, 0, Fd::READ, inode)
.map_err(|e| format!("Could not create root fd: {}", e))?;
wasi_fs.preopen_fds.push(fd);
inode
};
@ -188,7 +179,13 @@ impl WasiFs {
// TODO: think about this
let default_rights = 0x1FFFFFFF; // all rights
let cur_dir = PathBuf::from(dir);
let cur_dir_metadata = cur_dir.metadata().expect("Could not find directory");
let cur_dir_metadata = cur_dir.metadata().map_err(|e| {
format!(
"Could not get metadata for file {:?}: {}",
dir,
e.to_string()
)
})?;
let kind = if cur_dir_metadata.is_dir() {
Kind::Dir {
parent: Some(root_inode),
@ -211,8 +208,14 @@ impl WasiFs {
)
})?;
let fd = wasi_fs
.create_fd(default_rights, default_rights, 0, inode)
.expect("Could not open fd");
.create_fd(
default_rights,
default_rights,
0,
Fd::READ | Fd::WRITE,
inode,
)
.map_err(|e| format!("Could not open fd for file {:?}: {}", dir, e))?;
if let Kind::Root { entries } = &mut wasi_fs.inodes[root_inode].kind {
// todo handle collisions
assert!(entries.insert(dir.to_string(), inode).is_none())
@ -224,9 +227,13 @@ impl WasiFs {
debug!("Attempting to open {:?} at {}", real_dir, alias);
// TODO: think about this
let default_rights = 0x1FFFFFFF; // all rights
let cur_dir_metadata = real_dir
.metadata()
.expect("mapped dir not at previously verified location");
let cur_dir_metadata = real_dir.metadata().map_err(|e| {
format!(
"Could not get metadata for file {:?}: {}",
&real_dir,
e.to_string()
)
})?;
let kind = if cur_dir_metadata.is_dir() {
Kind::Dir {
parent: Some(root_inode),
@ -249,8 +256,14 @@ impl WasiFs {
)
})?;
let fd = wasi_fs
.create_fd(default_rights, default_rights, 0, inode)
.expect("Could not open fd");
.create_fd(
default_rights,
default_rights,
0,
Fd::READ | Fd::WRITE,
inode,
)
.map_err(|e| format!("Could not open fd for file {:?}: {}", &real_dir, e))?;
if let Kind::Root { entries } = &mut wasi_fs.inodes[root_inode].kind {
// todo handle collisions
assert!(entries.insert(alias.clone(), inode).is_none());
@ -276,6 +289,7 @@ impl WasiFs {
&mut self,
base: __wasi_fd_t,
file: Box<dyn WasiFile>,
open_flags: u16,
name: String,
rights: __wasi_rights_t,
rights_inheriting: __wasi_rights_t,
@ -312,7 +326,7 @@ impl WasiFs {
_ => unreachable!("Dir or Root became not Dir or Root"),
}
self.create_fd(rights, rights_inheriting, flags, inode)
self.create_fd(rights, rights_inheriting, flags, open_flags, inode)
.map_err(WasiFsError::from_wasi_err)
}
_ => Err(WasiFsError::BaseNotDirectory),
@ -754,10 +768,13 @@ impl WasiFs {
let inode = &mut self.inodes[fd.inode];
match &mut inode.kind {
Kind::File {
handle: Some(handle),
..
} => handle.flush().map_err(|_| __WASI_EIO)?,
Kind::File { handle, .. } => {
if let Some(file) = handle {
file.flush().map_err(|_| __WASI_EIO)?
} else {
return Err(__WASI_EIO);
}
}
// TODO: verify this behavior
Kind::Dir { .. } => return Err(__WASI_EISDIR),
Kind::Symlink { .. } => unimplemented!(),
@ -810,6 +827,7 @@ impl WasiFs {
rights: __wasi_rights_t,
rights_inheriting: __wasi_rights_t,
flags: __wasi_fdflags_t,
open_flags: u16,
inode: Inode,
) -> Result<__wasi_fd_t, __wasi_errno_t> {
let idx = self.next_fd.get();
@ -821,6 +839,7 @@ impl WasiFs {
rights_inheriting,
flags,
offset: 0,
open_flags,
inode,
},
);
@ -924,11 +943,23 @@ impl WasiFs {
}
}
#[derive(Debug)]
pub struct WasiState<'a> {
#[derive(Debug, Serialize, Deserialize)]
pub struct WasiState {
pub fs: WasiFs,
pub args: &'a [Vec<u8>],
pub envs: &'a [Vec<u8>],
pub args: Vec<Vec<u8>>,
pub envs: Vec<Vec<u8>>,
}
impl WasiState {
/// Turn the WasiState into bytes
pub fn freeze(&self) -> Option<Vec<u8>> {
bincode::serialize(self).ok()
}
/// Get a WasiState from bytes
pub fn unfreeze(bytes: &[u8]) -> Option<Self> {
bincode::deserialize(bytes).ok()
}
}
pub fn host_file_type_to_wasi_file_type(file_type: fs::FileType) -> __wasi_filetype_t {

View File

@ -1,5 +1,6 @@
/// types for use in the WASI filesystem
use crate::syscalls::types::*;
use serde::{de, Deserialize, Serialize};
#[cfg(unix)]
use std::convert::TryInto;
use std::{
@ -117,6 +118,7 @@ impl WasiFsError {
}
/// This trait relies on your file closing when it goes out of scope via `Drop`
#[typetag::serde(tag = "type")]
pub trait WasiFile: std::fmt::Debug + Write + Read + Seek {
/// the last time the file was accessed in nanoseconds as a UNIX timestamp
fn last_accessed(&self) -> __wasi_timestamp_t;
@ -339,18 +341,122 @@ pub(crate) fn poll(
pub trait WasiPath {}
/// A thin wrapper around `std::fs::File`
#[derive(Debug)]
#[derive(Debug, Serialize)]
pub struct HostFile {
#[serde(skip_serializing)]
pub inner: fs::File,
pub host_path: PathBuf,
flags: u16,
}
impl<'de> Deserialize<'de> for HostFile {
fn deserialize<D>(deserializer: D) -> Result<HostFile, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(field_identifier, rename_all = "snake_case")]
enum Field {
HostPath,
Flags,
}
struct HostFileVisitor;
impl<'de> de::Visitor<'de> for HostFileVisitor {
type Value = HostFile;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("struct HostFile")
}
fn visit_seq<V>(self, mut seq: V) -> Result<Self::Value, V::Error>
where
V: de::SeqAccess<'de>,
{
let host_path = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
let flags = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(1, &self))?;
let inner = std::fs::OpenOptions::new()
.read(flags & HostFile::READ != 0)
.write(flags & HostFile::WRITE != 0)
.append(flags & HostFile::APPEND != 0)
.open(&host_path)
.map_err(|_| de::Error::custom("Could not open file on this system"))?;
Ok(HostFile {
inner,
host_path,
flags,
})
}
fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
where
V: de::MapAccess<'de>,
{
let mut host_path = None;
let mut flags = None;
while let Some(key) = map.next_key()? {
match key {
Field::HostPath => {
if host_path.is_some() {
return Err(de::Error::duplicate_field("host_path"));
}
host_path = Some(map.next_value()?);
}
Field::Flags => {
if flags.is_some() {
return Err(de::Error::duplicate_field("flags"));
}
flags = Some(map.next_value()?);
}
}
}
let host_path = host_path.ok_or_else(|| de::Error::missing_field("host_path"))?;
let flags = flags.ok_or_else(|| de::Error::missing_field("flags"))?;
let inner = std::fs::OpenOptions::new()
.read(flags & HostFile::READ != 0)
.write(flags & HostFile::WRITE != 0)
.append(flags & HostFile::APPEND != 0)
.open(&host_path)
.map_err(|_| de::Error::custom("Could not open file on this system"))?;
Ok(HostFile {
inner,
host_path,
flags,
})
}
}
const FIELDS: &'static [&'static str] = &["host_path", "flags"];
deserializer.deserialize_struct("HostFile", FIELDS, HostFileVisitor)
}
}
impl HostFile {
const READ: u16 = 1;
const WRITE: u16 = 2;
const APPEND: u16 = 4;
/// creates a new host file from a `std::fs::File` and a path
pub fn new(file: fs::File, host_path: PathBuf) -> Self {
pub fn new(file: fs::File, host_path: PathBuf, read: bool, write: bool, append: bool) -> Self {
let mut flags = 0;
if read {
flags |= Self::READ;
}
if write {
flags |= Self::WRITE;
}
if append {
flags |= Self::APPEND;
}
Self {
inner: file,
host_path,
flags,
}
}
@ -393,6 +499,7 @@ impl Write for HostFile {
}
}
#[typetag::serde]
impl WasiFile for HostFile {
fn last_accessed(&self) -> u64 {
self.metadata()
@ -519,57 +626,55 @@ fn host_file_bytes_available(_raw_fd: i32) -> Result<usize, WasiFsError> {
unimplemented!("host_file_bytes_available not yet implemented for non-Unix-like targets. This probably means the program tried to use wasi::poll_oneoff")
}
#[derive(Debug)]
pub struct Stdout(pub std::io::Stdout);
#[derive(Debug, Serialize, Deserialize)]
pub struct Stdout;
impl Read for Stdout {
fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
Err(io::Error::new(
io::ErrorKind::Other,
"can not read from stdout",
))
}
fn read_to_end(&mut self, _buf: &mut Vec<u8>) -> io::Result<usize> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
Err(io::Error::new(
io::ErrorKind::Other,
"can not read from stdout",
))
}
fn read_to_string(&mut self, _buf: &mut String) -> io::Result<usize> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
Err(io::Error::new(
io::ErrorKind::Other,
"can not read from stdout",
))
}
fn read_exact(&mut self, _buf: &mut [u8]) -> io::Result<()> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
Err(io::Error::new(
io::ErrorKind::Other,
"can not read from stdout",
))
}
}
impl Seek for Stdout {
fn seek(&mut self, _pos: io::SeekFrom) -> io::Result<u64> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not seek stdout",
))
Err(io::Error::new(io::ErrorKind::Other, "can not seek stdout"))
}
}
impl Write for Stdout {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0.write(buf)
io::stdout().write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.0.flush()
io::stdout().flush()
}
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
self.0.write_all(buf)
io::stdout().write_all(buf)
}
fn write_fmt(&mut self, fmt: ::std::fmt::Arguments) -> io::Result<()> {
self.0.write_fmt(fmt)
io::stdout().write_fmt(fmt)
}
}
#[typetag::serde]
impl WasiFile for Stdout {
fn last_accessed(&self) -> u64 {
0
@ -594,7 +699,7 @@ impl WasiFile for Stdout {
#[cfg(unix)]
fn get_raw_fd(&self) -> Option<i32> {
use std::os::unix::io::AsRawFd;
Some(self.0.as_raw_fd())
Some(io::stdout().as_raw_fd())
}
#[cfg(not(unix))]
@ -605,57 +710,55 @@ impl WasiFile for Stdout {
}
}
#[derive(Debug)]
pub struct Stderr(pub std::io::Stderr);
#[derive(Debug, Serialize, Deserialize)]
pub struct Stderr;
impl Read for Stderr {
fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
Err(io::Error::new(
io::ErrorKind::Other,
"can not read from stderr",
))
}
fn read_to_end(&mut self, _buf: &mut Vec<u8>) -> io::Result<usize> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
Err(io::Error::new(
io::ErrorKind::Other,
"can not read from stderr",
))
}
fn read_to_string(&mut self, _buf: &mut String) -> io::Result<usize> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
Err(io::Error::new(
io::ErrorKind::Other,
"can not read from stderr",
))
}
fn read_exact(&mut self, _buf: &mut [u8]) -> io::Result<()> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
Err(io::Error::new(
io::ErrorKind::Other,
"can not read from stderr",
))
}
}
impl Seek for Stderr {
fn seek(&mut self, _pos: io::SeekFrom) -> io::Result<u64> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not seek stderr",
))
Err(io::Error::new(io::ErrorKind::Other, "can not seek stderr"))
}
}
impl Write for Stderr {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0.write(buf)
io::stderr().write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.0.flush()
io::stderr().flush()
}
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
self.0.write_all(buf)
io::stderr().write_all(buf)
}
fn write_fmt(&mut self, fmt: ::std::fmt::Arguments) -> io::Result<()> {
self.0.write_fmt(fmt)
io::stderr().write_fmt(fmt)
}
}
#[typetag::serde]
impl WasiFile for Stderr {
fn last_accessed(&self) -> u64 {
0
@ -680,7 +783,7 @@ impl WasiFile for Stderr {
#[cfg(unix)]
fn get_raw_fd(&self) -> Option<i32> {
use std::os::unix::io::AsRawFd;
Some(self.0.as_raw_fd())
Some(io::stderr().as_raw_fd())
}
#[cfg(not(unix))]
@ -691,57 +794,55 @@ impl WasiFile for Stderr {
}
}
#[derive(Debug)]
pub struct Stdin(pub std::io::Stdin);
#[derive(Debug, Serialize, Deserialize)]
pub struct Stdin;
impl Read for Stdin {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.0.read(buf)
io::stdin().read(buf)
}
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
self.0.read_to_end(buf)
io::stdin().read_to_end(buf)
}
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
self.0.read_to_string(buf)
io::stdin().read_to_string(buf)
}
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
self.0.read_exact(buf)
io::stdin().read_exact(buf)
}
}
impl Seek for Stdin {
fn seek(&mut self, _pos: io::SeekFrom) -> io::Result<u64> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not seek stdin",
))
Err(io::Error::new(io::ErrorKind::Other, "can not seek stdin"))
}
}
impl Write for Stdin {
fn write(&mut self, _buf: &[u8]) -> io::Result<usize> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
Err(io::Error::new(
io::ErrorKind::Other,
"can not write to stdin",
))
}
fn flush(&mut self) -> io::Result<()> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
Err(io::Error::new(
io::ErrorKind::Other,
"can not write to stdin",
))
}
fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
Err(io::Error::new(
io::ErrorKind::Other,
"can not write to stdin",
))
}
fn write_fmt(&mut self, _fmt: ::std::fmt::Arguments) -> io::Result<()> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
Err(io::Error::new(
io::ErrorKind::Other,
"can not write to stdin",
))
}
}
#[typetag::serde]
impl WasiFile for Stdin {
fn last_accessed(&self) -> u64 {
0
@ -766,7 +867,7 @@ impl WasiFile for Stdin {
#[cfg(unix)]
fn get_raw_fd(&self) -> Option<i32> {
use std::os::unix::io::AsRawFd;
Some(self.0.as_raw_fd())
Some(io::stdin().as_raw_fd())
}
#[cfg(not(unix))]

View File

@ -689,23 +689,11 @@ pub fn fd_pread(
match &mut state.fs.inodes[inode].kind {
Kind::File { handle, .. } => {
if let Some(h) = handle {
let current_pos =
wasi_try!(h.seek(std::io::SeekFrom::Current(0)).ok(), __WASI_EIO);
wasi_try!(
h.seek(std::io::SeekFrom::Start(offset as u64)).ok(),
__WASI_EIO
);
let bytes_read = wasi_try!(read_bytes(h, memory, iov_cells));
// reborrow so we can seek it back (the &mut gets moved into `read_bytes`
// and we can't use it after)
// If you're in the future and there's a nicer way to do this, please
// clean up this code
if let Some(h) = handle {
wasi_try!(
h.seek(std::io::SeekFrom::Start(current_pos)).ok(),
__WASI_EIO
);
}
bytes_read
} else {
return __WASI_EINVAL;
@ -1681,6 +1669,7 @@ pub fn path_open(
if let Ok(m) = maybe_inode {
&state.fs.inodes[m];
}
let mut open_flags = 0;
// TODO: traverse rights of dirs properly
// COMMENTED OUT: WASI isn't giving appropriate rights here when opening
@ -1708,10 +1697,22 @@ pub fn path_open(
.write(adjusted_rights & __WASI_RIGHT_FD_WRITE != 0)
.create(o_flags & __WASI_O_CREAT != 0)
.truncate(o_flags & __WASI_O_TRUNC != 0);
open_flags |= Fd::READ;
if adjusted_rights & __WASI_RIGHT_FD_WRITE != 0 {
open_flags |= Fd::WRITE;
}
if o_flags & __WASI_O_CREAT != 0 {
open_flags |= Fd::CREATE;
}
if o_flags & __WASI_O_TRUNC != 0 {
open_flags |= Fd::TRUNCATE;
}
*handle = Some(Box::new(HostFile::new(
wasi_try!(open_options.open(&path).map_err(|_| __WASI_EIO)),
path.to_path_buf(),
true,
adjusted_rights & __WASI_RIGHT_FD_WRITE != 0,
false,
)));
}
Kind::Buffer { .. } => unimplemented!("wasi::path_open for Buffer type files"),
@ -1768,6 +1769,7 @@ pub fn path_open(
// write access is required for creating a file
.write(true)
.create_new(true);
open_flags |= Fd::READ | Fd::WRITE | Fd::CREATE | Fd::TRUNCATE;
Some(Box::new(HostFile::new(
wasi_try!(open_options.open(&new_file_host_path).map_err(|e| {
@ -1775,6 +1777,9 @@ pub fn path_open(
__WASI_EIO
})),
new_file_host_path.clone(),
true,
true,
true,
)) as Box<dyn WasiFile>)
};
@ -1806,10 +1811,13 @@ pub fn path_open(
// TODO: check and reduce these
// TODO: ensure a mutable fd to root can never be opened
let out_fd =
wasi_try!(state
.fs
.create_fd(adjusted_rights, fs_rights_inheriting, fs_flags, inode));
let out_fd = wasi_try!(state.fs.create_fd(
adjusted_rights,
fs_rights_inheriting,
fs_flags,
open_flags,
inode
));
fd_cell.set(out_fd);

View File

@ -2,6 +2,7 @@
use crate::ptr::{Array, WasmPtr};
use byteorder::{ReadBytesExt, WriteBytesExt, LE};
use serde::{Deserialize, Serialize};
use std::fmt;
use std::mem;
use wasmer_runtime_core::types::ValueType;
@ -316,7 +317,7 @@ pub type __wasi_filedelta_t = i64;
pub type __wasi_filesize_t = u64;
#[derive(Copy, Clone, PartialEq, Eq)]
#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[repr(C)]
pub struct __wasi_filestat_t {
pub st_dev: __wasi_device_t,