From eb650f6fe0e1f40432e7fa8d62b2885d0e4c5467 Mon Sep 17 00:00:00 2001 From: Joshua Warner Date: Tue, 29 Sep 2020 11:20:14 -0700 Subject: [PATCH] filesystem example (#2236) --- Cargo.lock | 4 ++ Cargo.toml | 1 + crates/wasi-common/src/virtfs.rs | 8 ++- crates/wasi/src/lib.rs | 1 + docs/examples-rust-wasi-fs.md | 21 ++++++ examples/externref.c | 2 +- examples/gcd.c | 2 +- examples/hello.c | 2 +- examples/hello.cc | 2 +- examples/interrupt.c | 2 +- examples/linking.c | 2 +- examples/memory.c | 2 +- examples/multi.c | 2 +- examples/serialize.c | 2 +- examples/wasi-fs/main.c | 119 +++++++++++++++++++++++++++++++ examples/wasi-fs/main.rs | 46 ++++++++++++ examples/wasi-fs/test.txt | 1 + examples/wasi-fs/wasm/Cargo.toml | 10 +++ examples/wasi-fs/wasm/wasi-fs.rs | 4 ++ examples/wasi/main.c | 4 +- 20 files changed, 224 insertions(+), 13 deletions(-) create mode 100644 docs/examples-rust-wasi-fs.md create mode 100644 examples/wasi-fs/main.c create mode 100644 examples/wasi-fs/main.rs create mode 100644 examples/wasi-fs/test.txt create mode 100644 examples/wasi-fs/wasm/Cargo.toml create mode 100644 examples/wasi-fs/wasm/wasi-fs.rs diff --git a/Cargo.lock b/Cargo.lock index 82b9a2a64d..4fc0480e99 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -813,6 +813,10 @@ dependencies = [ name = "example-fib-debug-wasm" version = "0.0.0" +[[package]] +name = "example-wasi-fs-wasm" +version = "0.0.0" + [[package]] name = "example-wasi-wasm" version = "0.0.0" diff --git a/Cargo.toml b/Cargo.toml index 477bf3f27e..d77efca0a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,6 +70,7 @@ members = [ "crates/wiggle/wasmtime", "examples/fib-debug/wasm", "examples/wasi/wasm", + "examples/wasi-fs/wasm", "fuzz", ] diff --git a/crates/wasi-common/src/virtfs.rs b/crates/wasi-common/src/virtfs.rs index 8eba9808e6..dae91de485 100644 --- a/crates/wasi-common/src/virtfs.rs +++ b/crates/wasi-common/src/virtfs.rs @@ -127,16 +127,20 @@ impl FileContents for VecFileContents { } } -struct VecFileContents { +pub struct VecFileContents { content: Vec, } impl VecFileContents { - fn new() -> Self { + pub fn new() -> Self { Self { content: Vec::new(), } } + + pub fn with_content(content: Vec) -> Self { + Self { content } + } } /// An `InMemoryFile` is a shared handle to some underlying data. The relationship is analagous to diff --git a/crates/wasi/src/lib.rs b/crates/wasi/src/lib.rs index fc6c924313..166a0e1ef9 100644 --- a/crates/wasi/src/lib.rs +++ b/crates/wasi/src/lib.rs @@ -2,6 +2,7 @@ use wasmtime::Trap; pub mod old; +pub use wasi_common::virtfs; pub use wasi_common::{WasiCtx, WasiCtxBuilder}; // Defines a `struct Wasi` with member fields and appropriate APIs for dealing diff --git a/docs/examples-rust-wasi-fs.md b/docs/examples-rust-wasi-fs.md new file mode 100644 index 0000000000..c3b4f0857d --- /dev/null +++ b/docs/examples-rust-wasi-fs.md @@ -0,0 +1,21 @@ +# WASI + +You can also [browse this source code online][code] and clone the wasmtime +repository to run the example locally. + +[code]: https://github.com/bytecodealliance/wasmtime/blob/main/examples/wasi/main.rs + +This example shows off how to run a wasi binary with a memory filesystem. + +## Wasm Source code + +```rust,ignore +{{#include ../examples/wasi-fs/wasm/wasi-fs.rs}} +``` + + +## `wasi-fs.rs` + +```rust,ignore +{{#include ../examples/wasi-fs/main.rs}} +``` diff --git a/examples/externref.c b/examples/externref.c index 4ec007ffa7..5828d17665 100644 --- a/examples/externref.c +++ b/examples/externref.c @@ -3,7 +3,7 @@ Example of using `externref` values. You can compile and run this example on Linux with: - cargo build --release -p wasmtime + cargo build --release -p wasmtime-c-api cc examples/externref.c \ -I crates/c-api/include \ -I crates/c-api/wasm-c-api/include \ diff --git a/examples/gcd.c b/examples/gcd.c index 285bc1593f..76ca77faf7 100644 --- a/examples/gcd.c +++ b/examples/gcd.c @@ -4,7 +4,7 @@ function. You can compile and run this example on Linux with: - cargo build --release -p wasmtime + cargo build --release -p wasmtime-c-api cc examples/gcd.c \ -I crates/c-api/include \ -I crates/c-api/wasm-c-api/include \ diff --git a/examples/hello.c b/examples/hello.c index fd268a84de..a14ef18aca 100644 --- a/examples/hello.c +++ b/examples/hello.c @@ -4,7 +4,7 @@ function. You can compile and run this example on Linux with: - cargo build --release -p wasmtime + cargo build --release -p wasmtime-c-api cc examples/hello.c \ -I crates/c-api/include \ -I crates/c-api/wasm-c-api/include \ diff --git a/examples/hello.cc b/examples/hello.cc index 45ac5302ec..c83be42576 100644 --- a/examples/hello.cc +++ b/examples/hello.cc @@ -4,7 +4,7 @@ function. You can compile and run this example on Linux with: - cargo build --release -p wasmtime + cargo build --release -p wasmtime-c-api cc examples/hello.cc \ -I crates/c-api/include \ -I crates/c-api/wasm-c-api/include \ diff --git a/examples/interrupt.c b/examples/interrupt.c index d2b5a3ac75..d8ccd9a8f4 100644 --- a/examples/interrupt.c +++ b/examples/interrupt.c @@ -4,7 +4,7 @@ function. You can compile and run this example on Linux with: - cargo build --release -p wasmtime + cargo build --release -p wasmtime-c-api cc examples/interrupt.c \ -I crates/c-api/include \ -I crates/c-api/wasm-c-api/include \ diff --git a/examples/linking.c b/examples/linking.c index bc13f3cb97..f4c53b1f5a 100644 --- a/examples/linking.c +++ b/examples/linking.c @@ -4,7 +4,7 @@ together. You can compile and run this example on Linux with: - cargo build --release -p wasmtime + cargo build --release -p wasmtime-c-api cc examples/linking.c \ -I crates/c-api/include \ -I crates/c-api/wasm-c-api/include \ diff --git a/examples/memory.c b/examples/memory.c index f430fe415b..a8391e8c20 100644 --- a/examples/memory.c +++ b/examples/memory.c @@ -4,7 +4,7 @@ function. You can compile and run this example on Linux with: - cargo build --release -p wasmtime + cargo build --release -p wasmtime-c-api cc examples/memory.c \ -I crates/c-api/include \ -I crates/c-api/wasm-c-api/include \ diff --git a/examples/multi.c b/examples/multi.c index a56883884f..a1514b1833 100644 --- a/examples/multi.c +++ b/examples/multi.c @@ -4,7 +4,7 @@ function. You can compile and run this example on Linux with: - cargo build --release -p wasmtime + cargo build --release -p wasmtime-c-api cc examples/multi.c \ -I crates/c-api/include \ -I crates/c-api/wasm-c-api/include \ diff --git a/examples/serialize.c b/examples/serialize.c index 6e2407efe0..c25a1bf401 100644 --- a/examples/serialize.c +++ b/examples/serialize.c @@ -4,7 +4,7 @@ function. You can compile and run this example on Linux with: - cargo build --release -p wasmtime + cargo build --release -p wasmtime-c-api cc examples/hello.c \ -I crates/c-api/include \ -I crates/c-api/wasm-c-api/include \ diff --git a/examples/wasi-fs/main.c b/examples/wasi-fs/main.c new file mode 100644 index 0000000000..6452cb7d41 --- /dev/null +++ b/examples/wasi-fs/main.c @@ -0,0 +1,119 @@ +/* +Example of instantiating a WebAssembly which uses WASI imports. + +You can compile and run this example on Linux with: + + cargo build --release -p wasmtime-c-api + cc examples/wasi-fs/main.c \ + -I crates/c-api/include \ + -I crates/c-api/wasm-c-api/include \ + target/release/libwasmtime.a \ + -lpthread -ldl -lm \ + -o wasi-fs + ./wasi-fs + +Note that on Windows and macOS the command will be similar, but you'll need +to tweak the `-lpthread` and such annotations. +*/ + +#include +#include +#include +#include +#include +#include + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +static void exit_with_error(const char *message, wasmtime_error_t *error, wasm_trap_t *trap); + +int main() { + int ret = 0; + // Set up our context + wasm_engine_t *engine = wasm_engine_new(); + assert(engine != NULL); + wasm_store_t *store = wasm_store_new(engine); + assert(store != NULL); + + wasm_byte_vec_t wasm; + // Load our input file to parse it next + FILE* file = fopen("target/wasm32-wasi/debug/wasi-fs.wasm", "rb"); + if (!file) { + printf("> Error loading file!\n"); + exit(1); + } + fseek(file, 0L, SEEK_END); + size_t file_size = ftell(file); + wasm_byte_vec_new_uninitialized(&wasm, file_size); + fseek(file, 0L, SEEK_SET); + if (fread(wasm.data, file_size, 1, file) != 1) { + printf("> Error loading module!\n"); + exit(1); + } + fclose(file); + + // Compile our modules + wasm_module_t *module = NULL; + wasmtime_error_t *error = wasmtime_module_new(engine, &wasm, &module); + if (!module) + exit_with_error("failed to compile module", error, NULL); + wasm_byte_vec_delete(&wasm); + + // Instantiate wasi + wasi_config_t *wasi_config = wasi_config_new(); + assert(wasi_config); + wasi_config_inherit_argv(wasi_config); + wasi_config_inherit_env(wasi_config); + wasi_config_inherit_stdin(wasi_config); + wasi_config_inherit_stdout(wasi_config); + wasi_config_inherit_stderr(wasi_config); + wasi_config_preopen_dir(wasi_config, "examples/wasi-fs", "."); + wasm_trap_t *trap = NULL; + wasi_instance_t *wasi = wasi_instance_new(store, "wasi_snapshot_preview1", wasi_config, &trap); + if (wasi == NULL) + exit_with_error("failed to instantiate WASI", NULL, trap); + + wasmtime_linker_t *linker = wasmtime_linker_new(store); + error = wasmtime_linker_define_wasi(linker, wasi); + if (error != NULL) + exit_with_error("failed to link wasi", error, NULL); + + // Instantiate the module + wasm_name_t empty; + wasm_name_new_from_string(&empty, ""); + wasm_instance_t *instance = NULL; + error = wasmtime_linker_module(linker, &empty, module); + if (error != NULL) + exit_with_error("failed to instantiate module", error, NULL); + + // Run it. + wasm_func_t* func; + wasmtime_linker_get_default(linker, &empty, &func); + if (error != NULL) + exit_with_error("failed to locate default export for module", error, NULL); + error = wasmtime_func_call(func, NULL, 0, NULL, 0, &trap); + if (error != NULL) + exit_with_error("error calling default export", error, trap); + + // Clean up after ourselves at this point + wasm_name_delete(&empty); + wasm_module_delete(module); + wasm_store_delete(store); + wasm_engine_delete(engine); + return 0; +} + +static void exit_with_error(const char *message, wasmtime_error_t *error, wasm_trap_t *trap) { + fprintf(stderr, "error: %s\n", message); + wasm_byte_vec_t error_message; + if (error != NULL) { + wasmtime_error_message(error, &error_message); + wasmtime_error_delete(error); + } else { + wasm_trap_message(trap, &error_message); + wasm_trap_delete(trap); + } + fprintf(stderr, "%.*s\n", (int) error_message.size, error_message.data); + wasm_byte_vec_delete(&error_message); + exit(1); +} diff --git a/examples/wasi-fs/main.rs b/examples/wasi-fs/main.rs new file mode 100644 index 0000000000..111ce1521e --- /dev/null +++ b/examples/wasi-fs/main.rs @@ -0,0 +1,46 @@ +//! Example of running a wasi binary in a memory filesystem + +// The corresponding wasm binary can be built with: +// `cargo build -p example-wasi-fs-wasm --target wasm32-wasi` +// +// then you can execute this example with `cargo run --example wasi-fs` + +use anyhow::Result; +use std::collections::HashMap; +use wasmtime::*; +use wasmtime_wasi::virtfs::{VecFileContents, VirtualDirEntry}; +use wasmtime_wasi::{Wasi, WasiCtxBuilder}; + +fn main() -> Result<()> { + tracing_subscriber::FmtSubscriber::builder() + .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) + .with_ansi(true) + .init(); + + let store = Store::default(); + let mut linker = Linker::new(&store); + + // Create an instance of `Wasi` which contains a `WasiCtx`. Note that + // `WasiCtx` provides a number of ways to configure what the target program + // will have access to. + let entry = VirtualDirEntry::File(Box::new(VecFileContents::with_content( + "world".as_bytes().to_owned(), + ))); + let mut map = HashMap::new(); + map.insert("test.txt".to_string(), entry); + let dir = VirtualDirEntry::Directory(map); + let ctx = WasiCtxBuilder::new() + .inherit_stdout() + .inherit_stderr() + .preopened_virt(dir, ".") + .build()?; + let wasi = Wasi::new(&store, ctx); + wasi.add_to_linker(&mut linker)?; + + // Instantiate our module with the imports we've created, and run it. + let module = Module::from_file(store.engine(), "target/wasm32-wasi/debug/wasi-fs.wasm")?; + linker.module("", &module)?; + linker.get_default("")?.get0::<()>()?()?; + + Ok(()) +} diff --git a/examples/wasi-fs/test.txt b/examples/wasi-fs/test.txt new file mode 100644 index 0000000000..04fea06420 --- /dev/null +++ b/examples/wasi-fs/test.txt @@ -0,0 +1 @@ +world \ No newline at end of file diff --git a/examples/wasi-fs/wasm/Cargo.toml b/examples/wasi-fs/wasm/Cargo.toml new file mode 100644 index 0000000000..4bf1a06c63 --- /dev/null +++ b/examples/wasi-fs/wasm/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "example-wasi-fs-wasm" +version = "0.0.0" +authors = ["The Wasmtime Project Developers"] +edition = "2018" +publish = false + +[[bin]] +path = "wasi-fs.rs" +name = "wasi-fs" diff --git a/examples/wasi-fs/wasm/wasi-fs.rs b/examples/wasi-fs/wasm/wasi-fs.rs new file mode 100644 index 0000000000..218cfeb72c --- /dev/null +++ b/examples/wasi-fs/wasm/wasi-fs.rs @@ -0,0 +1,4 @@ +fn main() { + let contents = std::fs::read_to_string("test.txt").unwrap(); + println!("Hello, {}!", contents); +} diff --git a/examples/wasi/main.c b/examples/wasi/main.c index 2ad9592f4e..a8514c69ca 100644 --- a/examples/wasi/main.c +++ b/examples/wasi/main.c @@ -3,8 +3,8 @@ Example of instantiating a WebAssembly which uses WASI imports. You can compile and run this example on Linux with: - cargo build --release -p wasmtime - cc examples/wasi.c \ + cargo build --release -p wasmtime-c-api + cc examples/wasi/main.c \ -I crates/c-api/include \ -I crates/c-api/wasm-c-api/include \ target/release/libwasmtime.a \