├── .gitignore ├── crates └── wasi-preview1-component-adapter │ ├── .gitignore │ ├── wit │ ├── deps.toml │ ├── deps │ │ ├── wasi-cli-base │ │ │ ├── exit.wit │ │ │ ├── environment.wit │ │ │ └── preopens.wit │ │ ├── preview │ │ │ ├── proxy.wit │ │ │ ├── command.wit │ │ │ ├── reactor.wit │ │ │ └── command-extended.wit │ │ ├── sockets │ │ │ ├── instance-network.wit │ │ │ ├── udp-create-socket.wit │ │ │ ├── tcp-create-socket.wit │ │ │ └── network.wit │ │ ├── http │ │ │ ├── outgoing-handler.wit │ │ │ └── incoming-handler.wit │ │ ├── clocks │ │ │ ├── monotonic-clock.wit │ │ │ └── wall-clock.wit │ │ ├── logging │ │ │ └── handler.wit │ │ ├── random │ │ │ └── random.wit │ │ └── poll │ │ │ └── poll.wit │ ├── command.wit │ ├── reactor.wit │ └── deps.lock │ ├── byte-array-literals │ ├── Cargo.toml │ └── README.md │ ├── verify │ ├── Cargo.toml │ └── README.md │ ├── Cargo.toml │ └── README.md ├── test-programs ├── command-tests │ ├── src │ │ └── bin │ │ │ ├── exit_default.rs │ │ │ ├── panic.rs │ │ │ ├── exit_failure.rs │ │ │ ├── exit_success.rs │ │ │ ├── exit_panic.rs │ │ │ ├── hello_stdout.rs │ │ │ ├── random.rs │ │ │ ├── stdin.rs │ │ │ ├── args.rs │ │ │ ├── time.rs │ │ │ ├── env.rs │ │ │ ├── poll_stdin.rs │ │ │ ├── file_dir_sync.rs │ │ │ ├── default_clocks.rs │ │ │ ├── file_read.rs │ │ │ ├── file_append.rs │ │ │ ├── directory_list.rs │ │ │ ├── read_only.rs │ │ │ └── export_cabi_realloc.rs │ └── Cargo.toml ├── reactor-tests │ ├── wit │ │ ├── deps.toml │ │ ├── deps │ │ │ ├── wasi-cli-base │ │ │ │ ├── exit.wit │ │ │ │ ├── environment.wit │ │ │ │ └── preopens.wit │ │ │ ├── preview │ │ │ │ ├── proxy.wit │ │ │ │ ├── command.wit │ │ │ │ ├── reactor.wit │ │ │ │ └── command-extended.wit │ │ │ ├── sockets │ │ │ │ ├── instance-network.wit │ │ │ │ ├── udp-create-socket.wit │ │ │ │ ├── tcp-create-socket.wit │ │ │ │ └── network.wit │ │ │ ├── http │ │ │ │ ├── outgoing-handler.wit │ │ │ │ └── incoming-handler.wit │ │ │ ├── clocks │ │ │ │ ├── monotonic-clock.wit │ │ │ │ ├── wall-clock.wit │ │ │ │ └── timezone.wit │ │ │ ├── logging │ │ │ │ └── handler.wit │ │ │ ├── random │ │ │ │ └── random.wit │ │ │ └── poll │ │ │ │ └── poll.wit │ │ ├── test-reactor.wit │ │ └── deps.lock │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── wasi-tests │ ├── README.md │ ├── src │ │ ├── bin │ │ │ ├── environment.rs │ │ │ ├── sched_yield.rs │ │ │ ├── stdio.rs │ │ │ ├── big_random_buf.rs │ │ │ ├── clock_time_get.rs │ │ │ ├── path_open_missing.rs │ │ │ ├── fd_filestat_get.rs │ │ │ ├── path_open_dirfd_not_dir.rs │ │ │ ├── path_open_create_existing.rs │ │ │ ├── isatty.rs │ │ │ ├── symlink_loop.rs │ │ │ ├── close_preopen.rs │ │ │ ├── directory_seek.rs │ │ │ ├── path_rename_dir_trailing_slashes.rs │ │ │ ├── overwrite_preopen.rs │ │ │ ├── remove_nonempty_directory.rs │ │ │ ├── dangling_symlink.rs │ │ │ ├── file_allocate.rs │ │ │ ├── path_rename_file_trailing_slashes.rs │ │ │ ├── readlink.rs │ │ │ ├── fd_advise.rs │ │ │ ├── dangling_fd.rs │ │ │ ├── remove_directory_trailing_slashes.rs │ │ │ ├── unlink_file_trailing_slashes.rs │ │ │ ├── file_truncation.rs │ │ │ ├── file_unbuffered_write.rs │ │ │ └── fd_filestat_set.rs │ │ └── config.rs │ ├── Cargo.toml │ └── Cargo.lock ├── Cargo.toml └── src │ └── lib.rs ├── wasi ├── wit │ ├── deps │ │ ├── cli │ │ │ ├── run.wit │ │ │ ├── exit.wit │ │ │ ├── stdio.wit │ │ │ ├── environment.wit │ │ │ ├── command.wit │ │ │ └── terminal.wit │ │ ├── filesystem │ │ │ ├── world.wit │ │ │ └── preopens.wit │ │ ├── sockets │ │ │ ├── instance-network.wit │ │ │ ├── udp-create-socket.wit │ │ │ └── tcp-create-socket.wit │ │ ├── http │ │ │ ├── outgoing-handler.wit │ │ │ ├── incoming-handler.wit │ │ │ └── proxy.wit │ │ ├── random │ │ │ ├── insecure.wit │ │ │ ├── random.wit │ │ │ └── insecure-seed.wit │ │ ├── preview │ │ │ ├── main.wit │ │ │ ├── test.wit │ │ │ └── command-extended.wit │ │ ├── clocks │ │ │ ├── monotonic-clock.wit │ │ │ ├── wall-clock.wit │ │ │ └── timezone.wit │ │ ├── logging │ │ │ └── logging.wit │ │ └── poll │ │ │ └── poll.wit │ ├── deps.toml │ └── reactor.wit ├── src │ └── lib.rs └── Cargo.toml ├── wit ├── deps │ ├── wasi-cli-base │ │ ├── exit.wit │ │ ├── environment.wit │ │ └── preopens.wit │ ├── sockets │ │ ├── instance-network.wit │ │ ├── udp-create-socket.wit │ │ ├── tcp-create-socket.wit │ │ └── network.wit │ ├── http │ │ ├── outgoing-handler.wit │ │ └── incoming-handler.wit │ ├── clocks │ │ ├── monotonic-clock.wit │ │ ├── wall-clock.wit │ │ └── timezone.wit │ ├── logging │ │ └── handler.wit │ ├── random │ │ └── random.wit │ └── poll │ │ └── poll.wit ├── proxy.wit ├── command.wit ├── reactor.wit └── command-extended.wit ├── wasi-common ├── src │ ├── preview2 │ │ ├── mod.rs │ │ ├── exit.rs │ │ ├── logging.rs │ │ ├── default_outgoing_http.rs │ │ ├── random.rs │ │ ├── env.rs │ │ └── clocks.rs │ ├── clocks.rs │ ├── lib.rs │ ├── wasi │ │ ├── mod.rs │ │ ├── proxy.rs │ │ └── command.rs │ ├── error.rs │ ├── random.rs │ └── clocks │ │ └── host.rs ├── Cargo.toml └── README.md ├── wasi-sockets ├── Cargo.toml ├── src │ ├── wasi.rs │ ├── network.rs │ ├── ip_name_lookup.rs │ ├── udp_socket.rs │ ├── tcp_socket.rs │ └── network_impl.rs └── sync │ ├── Cargo.toml │ └── src │ └── lib.rs ├── host └── Cargo.toml ├── COPYRIGHT ├── LICENSE-MIT ├── Cargo.toml ├── SECURITY.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | /wasi/wit/deps.lock 4 | -------------------------------------------------------------------------------- /crates/wasi-preview1-component-adapter/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /test-programs/command-tests/src/bin/exit_default.rs: -------------------------------------------------------------------------------- 1 | fn main() {} 2 | -------------------------------------------------------------------------------- /test-programs/reactor-tests/wit/deps.toml: -------------------------------------------------------------------------------- 1 | preview = "../../../wit" 2 | -------------------------------------------------------------------------------- /crates/wasi-preview1-component-adapter/wit/deps.toml: -------------------------------------------------------------------------------- 1 | preview = "../../../wit" 2 | -------------------------------------------------------------------------------- /test-programs/command-tests/src/bin/panic.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | panic!("idk!!!"); 3 | } 4 | -------------------------------------------------------------------------------- /test-programs/command-tests/src/bin/exit_failure.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | std::process::exit(1) 3 | } 4 | -------------------------------------------------------------------------------- /test-programs/command-tests/src/bin/exit_success.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | std::process::exit(0) 3 | } 4 | -------------------------------------------------------------------------------- /wasi/wit/deps/cli/run.wit: -------------------------------------------------------------------------------- 1 | interface run { 2 | /// Run the program. 3 | run: func() -> result 4 | } 5 | -------------------------------------------------------------------------------- /test-programs/command-tests/src/bin/exit_panic.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | panic!("Curiouser and curiouser!") 3 | } 4 | -------------------------------------------------------------------------------- /wasi/wit/deps.toml: -------------------------------------------------------------------------------- 1 | preview = "https://gitpkg.now.sh/bytecodealliance/wasmtime/crates/wasi?aec4b25b8f62f409175a3cc6c4a4ed18b446d3ae" 2 | -------------------------------------------------------------------------------- /test-programs/command-tests/src/bin/hello_stdout.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("hello, world"); 3 | eprintln!("hello, world"); 4 | } 5 | -------------------------------------------------------------------------------- /wasi/wit/deps/cli/exit.wit: -------------------------------------------------------------------------------- 1 | interface exit { 2 | /// Exit the current instance and any linked instances. 3 | exit: func(status: result) 4 | } 5 | -------------------------------------------------------------------------------- /wasi/wit/deps/filesystem/world.wit: -------------------------------------------------------------------------------- 1 | package wasi:filesystem 2 | 3 | world example-world { 4 | import types 5 | import preopens 6 | } 7 | -------------------------------------------------------------------------------- /wit/deps/wasi-cli-base/exit.wit: -------------------------------------------------------------------------------- 1 | default interface wasi-exit { 2 | /// Exit the curerent instance and any linked instances. 3 | exit: func(status: result) 4 | } 5 | -------------------------------------------------------------------------------- /test-programs/command-tests/src/bin/random.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let mut byte = [0_u8]; 3 | getrandom::getrandom(&mut byte).unwrap(); 4 | 5 | assert_eq!(42, byte[0]); 6 | } 7 | -------------------------------------------------------------------------------- /test-programs/reactor-tests/wit/deps/wasi-cli-base/exit.wit: -------------------------------------------------------------------------------- 1 | default interface wasi-exit { 2 | /// Exit the curerent instance and any linked instances. 3 | exit: func(status: result) 4 | } 5 | -------------------------------------------------------------------------------- /crates/wasi-preview1-component-adapter/wit/deps/wasi-cli-base/exit.wit: -------------------------------------------------------------------------------- 1 | default interface wasi-exit { 2 | /// Exit the curerent instance and any linked instances. 3 | exit: func(status: result) 4 | } 5 | -------------------------------------------------------------------------------- /test-programs/wasi-tests/README.md: -------------------------------------------------------------------------------- 1 | This is the `wasi-tests` crate, which contains source code for the system-level WASI tests. 2 | 3 | Building these tests requires that the `wasm32-wasi` target be installed. 4 | -------------------------------------------------------------------------------- /wasi-common/src/preview2/mod.rs: -------------------------------------------------------------------------------- 1 | mod clocks; 2 | mod default_outgoing_http; 3 | mod env; 4 | mod exit; 5 | mod filesystem; 6 | mod http_types; 7 | mod io; 8 | mod logging; 9 | mod poll; 10 | mod random; 11 | -------------------------------------------------------------------------------- /wit/proxy.wit: -------------------------------------------------------------------------------- 1 | default world proxy { 2 | import random: random.random 3 | import console: logging.handler 4 | import default-outgoing-HTTP: http.outgoing-handler 5 | export HTTP: http.incoming-handler 6 | } 7 | -------------------------------------------------------------------------------- /test-programs/command-tests/src/bin/stdin.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | 3 | fn main() { 4 | assert_eq!( 5 | "So rested he by the Tumtum tree", 6 | &io::read_to_string(io::stdin().lock()).unwrap() 7 | ); 8 | } 9 | -------------------------------------------------------------------------------- /test-programs/wasi-tests/src/bin/environment.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | fn main() { 4 | assert_eq!(env::args().collect::>(), ["program", "/foo"]); 5 | assert_eq!(env::var("TEST").as_deref(), Ok("1")); 6 | } 7 | -------------------------------------------------------------------------------- /test-programs/wasi-tests/src/bin/sched_yield.rs: -------------------------------------------------------------------------------- 1 | unsafe fn test_sched_yield() { 2 | wasi::sched_yield().expect("sched_yield"); 3 | } 4 | 5 | fn main() { 6 | // Run tests 7 | unsafe { test_sched_yield() } 8 | } 9 | -------------------------------------------------------------------------------- /crates/wasi-preview1-component-adapter/byte-array-literals/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "byte-array-literals" 3 | version.workspace = true 4 | authors.workspace = true 5 | edition.workspace = true 6 | 7 | [lib] 8 | proc-macro = true 9 | -------------------------------------------------------------------------------- /test-programs/command-tests/src/bin/args.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let args = std::env::args().collect::>(); 3 | assert_eq!( 4 | args, 5 | ["hello", "this", "", "is an argument", "with 🚩 emoji"] 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /wasi/wit/deps/filesystem/preopens.wit: -------------------------------------------------------------------------------- 1 | interface preopens { 2 | use types.{descriptor} 3 | 4 | /// Return the set of preopened directories, and their path. 5 | get-directories: func() -> list> 6 | } 7 | -------------------------------------------------------------------------------- /test-programs/reactor-tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "reactor-tests" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [lib] 8 | crate-type=["cdylib"] 9 | 10 | [dependencies] 11 | wit-bindgen = "0.4.0" 12 | -------------------------------------------------------------------------------- /test-programs/reactor-tests/wit/deps/preview/proxy.wit: -------------------------------------------------------------------------------- 1 | default world proxy { 2 | import random: random.random 3 | import console: logging.handler 4 | import default-outgoing-HTTP: http.outgoing-handler 5 | export HTTP: http.incoming-handler 6 | } 7 | -------------------------------------------------------------------------------- /test-programs/wasi-tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wasi-tests" 3 | version = "0.0.0" 4 | readme = "README.md" 5 | edition = "2021" 6 | publish = false 7 | 8 | [dependencies] 9 | libc = "0.2.65" 10 | wasi = "0.11.0" 11 | once_cell = "1.12" 12 | -------------------------------------------------------------------------------- /crates/wasi-preview1-component-adapter/wit/deps/preview/proxy.wit: -------------------------------------------------------------------------------- 1 | default world proxy { 2 | import random: random.random 3 | import console: logging.handler 4 | import default-outgoing-HTTP: http.outgoing-handler 5 | export HTTP: http.incoming-handler 6 | } 7 | -------------------------------------------------------------------------------- /wasi/wit/deps/sockets/instance-network.wit: -------------------------------------------------------------------------------- 1 | 2 | /// This interface provides a value-export of the default network handle.. 3 | interface instance-network { 4 | use network.{network} 5 | 6 | /// Get a handle to the default network. 7 | instance-network: func() -> network 8 | 9 | } 10 | -------------------------------------------------------------------------------- /wit/deps/sockets/instance-network.wit: -------------------------------------------------------------------------------- 1 | 2 | /// This interface provides a value-export of the default network handle.. 3 | default interface instance-network { 4 | use pkg.network.{network} 5 | 6 | /// Get a handle to the default network. 7 | instance-network: func() -> network 8 | 9 | } 10 | -------------------------------------------------------------------------------- /crates/wasi-preview1-component-adapter/verify/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "verify-component-adapter" 3 | version.workspace = true 4 | edition.workspace = true 5 | authors.workspace = true 6 | publish = false 7 | 8 | [dependencies] 9 | wasmparser = "0.92.0" 10 | wat = "1.0.62" 11 | anyhow = "1" 12 | -------------------------------------------------------------------------------- /wasi/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "http-client")] 2 | pub mod http_client; 3 | 4 | pub mod snapshots { 5 | pub mod preview_2 { 6 | wit_bindgen::generate!({ 7 | world: "wasi:preview/reactor", 8 | std_feature, 9 | ownership: Owning, 10 | }); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test-programs/command-tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "command-tests" 3 | version = "0.0.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | anyhow = "1" 9 | getrandom = "0.2.8" 10 | rustix = "0.37.9" 11 | cap-std = "1.0.12" 12 | wit-bindgen = { workspace = true, default-features = true } 13 | -------------------------------------------------------------------------------- /test-programs/reactor-tests/wit/deps/sockets/instance-network.wit: -------------------------------------------------------------------------------- 1 | 2 | /// This interface provides a value-export of the default network handle.. 3 | default interface instance-network { 4 | use pkg.network.{network} 5 | 6 | /// Get a handle to the default network. 7 | instance-network: func() -> network 8 | 9 | } 10 | -------------------------------------------------------------------------------- /crates/wasi-preview1-component-adapter/wit/deps/sockets/instance-network.wit: -------------------------------------------------------------------------------- 1 | 2 | /// This interface provides a value-export of the default network handle.. 3 | default interface instance-network { 4 | use pkg.network.{network} 5 | 6 | /// Get a handle to the default network. 7 | instance-network: func() -> network 8 | 9 | } 10 | -------------------------------------------------------------------------------- /test-programs/command-tests/src/bin/time.rs: -------------------------------------------------------------------------------- 1 | use std::time::{Duration, Instant, SystemTime}; 2 | 3 | fn main() { 4 | let then = Instant::now(); 5 | 6 | assert_eq!(Duration::from_secs(42), then.elapsed()); 7 | 8 | assert_eq!( 9 | SystemTime::UNIX_EPOCH + Duration::new(1431648000, 100), 10 | SystemTime::now() 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /test-programs/command-tests/src/bin/env.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let vars = std::env::vars().collect::>(); 3 | assert_eq!( 4 | vars, 5 | [("frabjous", "day"), ("callooh", "callay")] 6 | .into_iter() 7 | .map(|(k, v)| (k.to_owned(), v.to_owned())) 8 | .collect() 9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /test-programs/command-tests/src/bin/poll_stdin.rs: -------------------------------------------------------------------------------- 1 | use rustix::io::{PollFd, PollFlags}; 2 | 3 | fn main() { 4 | let stdin = std::io::stdin(); 5 | let mut pollfds = vec![PollFd::new(&stdin, PollFlags::IN)]; 6 | let num = rustix::io::poll(&mut pollfds, -1).unwrap(); 7 | assert_eq!(num, 1); 8 | assert_eq!(pollfds[0].revents(), PollFlags::IN); 9 | } 10 | -------------------------------------------------------------------------------- /wasi/wit/deps/cli/stdio.wit: -------------------------------------------------------------------------------- 1 | interface stdin { 2 | use wasi:io/streams.{input-stream} 3 | 4 | get-stdin: func() -> input-stream 5 | } 6 | 7 | interface stdout { 8 | use wasi:io/streams.{output-stream} 9 | 10 | get-stdout: func() -> output-stream 11 | } 12 | 13 | interface stderr { 14 | use wasi:io/streams.{output-stream} 15 | 16 | get-stderr: func() -> output-stream 17 | } 18 | -------------------------------------------------------------------------------- /test-programs/command-tests/src/bin/file_dir_sync.rs: -------------------------------------------------------------------------------- 1 | fn main() -> std::io::Result<()> { 2 | let file = std::fs::File::open("bar.txt")?; 3 | file.sync_all()?; 4 | file.sync_data()?; 5 | 6 | /* 7 | * TODO: Support opening directories with `File::open` on Windows. 8 | let dir = cap_std::fs::Dir::open(".")?; 9 | dir.sync_all()?; 10 | dir.sync_data()?; 11 | */ 12 | 13 | Ok(()) 14 | } 15 | -------------------------------------------------------------------------------- /test-programs/wasi-tests/src/bin/stdio.rs: -------------------------------------------------------------------------------- 1 | use wasi_tests::{STDERR_FD, STDIN_FD, STDOUT_FD}; 2 | 3 | unsafe fn test_stdio() { 4 | for fd in &[STDIN_FD, STDOUT_FD, STDERR_FD] { 5 | wasi::fd_fdstat_get(*fd).expect("fd_fdstat_get on stdio"); 6 | wasi::fd_renumber(*fd, *fd + 100).expect("renumbering stdio"); 7 | } 8 | } 9 | 10 | fn main() { 11 | // Run the tests. 12 | unsafe { test_stdio() } 13 | } 14 | -------------------------------------------------------------------------------- /wasi-common/src/preview2/exit.rs: -------------------------------------------------------------------------------- 1 | use crate::wasi::exit; 2 | use crate::{I32Exit, WasiView}; 3 | 4 | #[async_trait::async_trait] 5 | impl exit::Host for T { 6 | async fn exit(&mut self, status: Result<(), ()>) -> anyhow::Result<()> { 7 | let status = match status { 8 | Ok(()) => 0, 9 | Err(()) => 1, 10 | }; 11 | Err(anyhow::anyhow!(I32Exit(status))) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test-programs/command-tests/src/bin/default_clocks.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let a = std::time::Instant::now(); 3 | let b = std::time::Instant::now(); 4 | let _ = b.checked_duration_since(a).unwrap(); 5 | 6 | let c = std::time::SystemTime::now(); 7 | let d = std::time::SystemTime::now(); 8 | let _ = c.duration_since(std::time::UNIX_EPOCH).unwrap(); 9 | let _ = d.duration_since(std::time::UNIX_EPOCH).unwrap(); 10 | } 11 | -------------------------------------------------------------------------------- /wasi-common/src/preview2/logging.rs: -------------------------------------------------------------------------------- 1 | use crate::wasi::console; 2 | use crate::WasiView; 3 | 4 | #[async_trait::async_trait] 5 | impl console::Host for T { 6 | async fn log( 7 | &mut self, 8 | level: console::Level, 9 | context: String, 10 | message: String, 11 | ) -> anyhow::Result<()> { 12 | println!("{:?} {}: {}", level, context, message); 13 | Ok(()) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test-programs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test-programs" 3 | version = "0.0.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [lib] 8 | proc-macro = true 9 | doctest = false 10 | test = false 11 | 12 | [dependencies] 13 | quote = "1.0" 14 | 15 | [build-dependencies] 16 | wit-component = { git = "https://github.com/bytecodealliance/wasm-tools", rev = "b17fc48a23152735bafd8f7ee95e3b55683d9511" } 17 | cargo_metadata = "0.15.3" 18 | heck = "0.4.0" 19 | -------------------------------------------------------------------------------- /wasi-common/src/clocks.rs: -------------------------------------------------------------------------------- 1 | pub mod host; 2 | use cap_std::time::Duration; 3 | 4 | pub trait WasiWallClock: Send + Sync { 5 | fn resolution(&self) -> Duration; 6 | fn now(&self) -> Duration; 7 | } 8 | 9 | pub trait WasiMonotonicClock: Send + Sync { 10 | fn resolution(&self) -> u64; 11 | fn now(&self) -> u64; 12 | } 13 | 14 | pub struct WasiClocks { 15 | pub wall: Box, 16 | pub monotonic: Box, 17 | } 18 | -------------------------------------------------------------------------------- /wasi-sockets/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wasmtime-wasi-sockets" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | anyhow = { workspace = true } 9 | async-trait = { workspace = true } 10 | bitflags = { workspace = true } 11 | cap-std = { workspace = true } 12 | cap-net-ext = { workspace = true } 13 | rustix = { workspace = true } 14 | tracing = { workspace = true } 15 | wasmtime = { workspace = true } 16 | wasi-common = { workspace = true } 17 | ipnet = { workspace = true } 18 | -------------------------------------------------------------------------------- /wasi-common/src/preview2/default_outgoing_http.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | wasi, 3 | wasi::types::{FutureIncomingResponse as Response, OutgoingRequest as Request, RequestOptions}, 4 | WasiView, 5 | }; 6 | 7 | #[async_trait::async_trait] 8 | impl wasi::default_outgoing_http::Host for T { 9 | async fn handle( 10 | &mut self, 11 | _req: Request, 12 | _options: Option, 13 | ) -> wasmtime::Result { 14 | anyhow::bail!("not implemented") 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /crates/wasi-preview1-component-adapter/verify/README.md: -------------------------------------------------------------------------------- 1 | # verify-component-adapter 2 | 3 | The `wasi-preview1-component-adapter` crate must compile to a wasm binary that 4 | meets a challenging set of constraints, in order to be used as an adapter by 5 | the `wasm-tools component new` tool. 6 | 7 | There are a limited set of wasm sections allowed in the binary, and a limited 8 | set of wasm modules we allow imports from. 9 | 10 | This crate is a bin target which parses a wasm file and reports an error if it 11 | does not fit in those constraints. 12 | -------------------------------------------------------------------------------- /test-programs/command-tests/src/bin/file_read.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | error::Error, 3 | fs::File, 4 | io::{self, Seek, SeekFrom}, 5 | }; 6 | 7 | fn main() -> Result<(), Box> { 8 | let mut file = File::open("bar.txt")?; 9 | 10 | assert_eq!(27, file.metadata()?.len()); 11 | 12 | assert_eq!( 13 | "And stood awhile in thought", 14 | &io::read_to_string(&mut file)? 15 | ); 16 | 17 | file.seek(SeekFrom::Start(11))?; 18 | 19 | assert_eq!("while in thought", &io::read_to_string(&mut file)?); 20 | 21 | Ok(()) 22 | } 23 | -------------------------------------------------------------------------------- /test-programs/wasi-tests/src/bin/big_random_buf.rs: -------------------------------------------------------------------------------- 1 | fn test_big_random_buf() { 2 | let mut buf = Vec::new(); 3 | buf.resize(1024, 0); 4 | unsafe { 5 | wasi::random_get(buf.as_mut_ptr(), 1024).expect("failed to call random_get"); 6 | } 7 | // Chances are pretty good that at least *one* byte will be non-zero in 8 | // any meaningful random function producing 1024 u8 values. 9 | assert!(buf.iter().any(|x| *x != 0), "random_get returned all zeros"); 10 | } 11 | 12 | fn main() { 13 | // Run the tests. 14 | test_big_random_buf() 15 | } 16 | -------------------------------------------------------------------------------- /test-programs/command-tests/src/bin/file_append.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | error::Error, 3 | fs::OpenOptions, 4 | io::{Seek, SeekFrom, Write}, 5 | }; 6 | 7 | fn main() -> Result<(), Box> { 8 | let mut file = OpenOptions::new().append(true).open("bar.txt")?; 9 | 10 | file.write_all(b"Did gyre and gimble in the wabe;\n") 11 | .unwrap(); 12 | file.seek(SeekFrom::Start(0)).unwrap(); 13 | file.write_all(b"All mimsy were the borogoves,\n").unwrap(); 14 | file.write_all(b"And the mome raths outgrabe.\n").unwrap(); 15 | 16 | Ok(()) 17 | } 18 | -------------------------------------------------------------------------------- /wit/deps/wasi-cli-base/environment.wit: -------------------------------------------------------------------------------- 1 | default interface environment { 2 | /// Get the POSIX-style environment variables. 3 | /// 4 | /// Each environment variable is provided as a pair of string variable names 5 | /// and string value. 6 | /// 7 | /// Morally, these are a value import, but until value imports are available 8 | /// in the component model, this import function should return the same 9 | /// values each time it is called. 10 | get-environment: func() -> list> 11 | 12 | /// Get the POSIX-style arguments to the program. 13 | get-arguments: func() -> list 14 | } 15 | -------------------------------------------------------------------------------- /wit/deps/wasi-cli-base/preopens.wit: -------------------------------------------------------------------------------- 1 | default interface preopens { 2 | use filesystem.filesystem.{descriptor} 3 | use io.streams.{input-stream, output-stream} 4 | 5 | /// Stdio preopens: these are the resources that provide stdin, stdout, and 6 | /// stderr. 7 | record stdio-preopens { 8 | stdin: input-stream, 9 | stdout: output-stream, 10 | stderr: output-stream, 11 | } 12 | 13 | /// Return the set of stdio preopens. 14 | get-stdio: func() -> stdio-preopens 15 | /// Return the set of of preopened directories, and their path. 16 | get-directories: func() -> list> 17 | } 18 | -------------------------------------------------------------------------------- /test-programs/reactor-tests/wit/deps/wasi-cli-base/environment.wit: -------------------------------------------------------------------------------- 1 | default interface environment { 2 | /// Get the POSIX-style environment variables. 3 | /// 4 | /// Each environment variable is provided as a pair of string variable names 5 | /// and string value. 6 | /// 7 | /// Morally, these are a value import, but until value imports are available 8 | /// in the component model, this import function should return the same 9 | /// values each time it is called. 10 | get-environment: func() -> list> 11 | 12 | /// Get the POSIX-style arguments to the program. 13 | get-arguments: func() -> list 14 | } 15 | -------------------------------------------------------------------------------- /test-programs/reactor-tests/wit/deps/wasi-cli-base/preopens.wit: -------------------------------------------------------------------------------- 1 | default interface preopens { 2 | use filesystem.filesystem.{descriptor} 3 | use io.streams.{input-stream, output-stream} 4 | 5 | /// Stdio preopens: these are the resources that provide stdin, stdout, and 6 | /// stderr. 7 | record stdio-preopens { 8 | stdin: input-stream, 9 | stdout: output-stream, 10 | stderr: output-stream, 11 | } 12 | 13 | /// Return the set of stdio preopens. 14 | get-stdio: func() -> stdio-preopens 15 | /// Return the set of of preopened directories, and their path. 16 | get-directories: func() -> list> 17 | } 18 | -------------------------------------------------------------------------------- /test-programs/reactor-tests/wit/test-reactor.wit: -------------------------------------------------------------------------------- 1 | default world test-reactor { 2 | 3 | import environment: wasi-cli-base.environment 4 | import streams: io.streams 5 | import preopens: wasi-cli-base.preopens 6 | import filesystem: filesystem.filesystem 7 | import exit: wasi-cli-base.exit 8 | 9 | export add-strings: func(s: list) -> u32 10 | export get-strings: func() -> list 11 | 12 | use io.streams.{output-stream} 13 | 14 | export write-strings-to: func(o: output-stream) -> result 15 | 16 | use filesystem.filesystem.{descriptor-stat} 17 | export pass-an-imported-record: func(d: descriptor-stat) -> string 18 | } 19 | -------------------------------------------------------------------------------- /crates/wasi-preview1-component-adapter/wit/deps/wasi-cli-base/environment.wit: -------------------------------------------------------------------------------- 1 | default interface environment { 2 | /// Get the POSIX-style environment variables. 3 | /// 4 | /// Each environment variable is provided as a pair of string variable names 5 | /// and string value. 6 | /// 7 | /// Morally, these are a value import, but until value imports are available 8 | /// in the component model, this import function should return the same 9 | /// values each time it is called. 10 | get-environment: func() -> list> 11 | 12 | /// Get the POSIX-style arguments to the program. 13 | get-arguments: func() -> list 14 | } 15 | -------------------------------------------------------------------------------- /crates/wasi-preview1-component-adapter/wit/deps/wasi-cli-base/preopens.wit: -------------------------------------------------------------------------------- 1 | default interface preopens { 2 | use filesystem.filesystem.{descriptor} 3 | use io.streams.{input-stream, output-stream} 4 | 5 | /// Stdio preopens: these are the resources that provide stdin, stdout, and 6 | /// stderr. 7 | record stdio-preopens { 8 | stdin: input-stream, 9 | stdout: output-stream, 10 | stderr: output-stream, 11 | } 12 | 13 | /// Return the set of stdio preopens. 14 | get-stdio: func() -> stdio-preopens 15 | /// Return the set of of preopened directories, and their path. 16 | get-directories: func() -> list> 17 | } 18 | -------------------------------------------------------------------------------- /wasi-common/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod clocks; 2 | mod ctx; 3 | mod error; 4 | pub(crate) mod filesystem; 5 | pub mod pipe; 6 | #[cfg(feature = "preview1")] 7 | pub mod preview1; 8 | pub mod preview2; 9 | pub mod random; 10 | pub mod sched; 11 | pub mod stdio; 12 | pub mod stream; 13 | pub mod table; 14 | pub mod wasi; 15 | 16 | pub use cap_fs_ext::SystemTimeSpec; 17 | pub use cap_rand::RngCore; 18 | pub use clocks::{WasiClocks, WasiMonotonicClock, WasiWallClock}; 19 | pub use ctx::{WasiCtx, WasiCtxBuilder, WasiView}; 20 | pub use error::I32Exit; 21 | pub use filesystem::{DirPerms, FilePerms}; 22 | pub use sched::{Poll, WasiSched}; 23 | pub use stream::{InputStream, OutputStream}; 24 | pub use table::{Table, TableError}; 25 | -------------------------------------------------------------------------------- /wasi/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | # name = "wasi" 3 | name = "wasi-preview2-prototype" 4 | version = "0.0.3" 5 | # version = "0.12.0+wasi-snapshot-preview2" 6 | description = "Experimental WASI Preview2 API bindings for Rust" 7 | edition.workspace = true 8 | license = "Apache-2.0 WITH LLVM-exception" 9 | 10 | [dependencies] 11 | wit-bindgen = { workspace = true, features = ["macros"] } 12 | 13 | # Dependencies for HTTP feature 14 | anyhow = { version = "1.0", optional = true } 15 | bytes = { version = "1.4", optional = true } 16 | http = { version = "0.2", optional = true } 17 | 18 | [lib] 19 | crate-type = ["lib"] 20 | 21 | [features] 22 | default = [] 23 | http-client = ["dep:anyhow", "dep:bytes", "dep:http"] 24 | -------------------------------------------------------------------------------- /wasi-common/src/preview2/random.rs: -------------------------------------------------------------------------------- 1 | use cap_rand::{distributions::Standard, Rng}; 2 | 3 | use crate::wasi; 4 | use crate::WasiView; 5 | 6 | #[async_trait::async_trait] 7 | impl wasi::random::Host for T { 8 | async fn get_random_bytes(&mut self, len: u64) -> anyhow::Result> { 9 | Ok((&mut self.ctx_mut().random) 10 | .sample_iter(Standard) 11 | .take(len as usize) 12 | .collect()) 13 | } 14 | 15 | async fn get_random_u64(&mut self) -> anyhow::Result { 16 | Ok(self.ctx_mut().random.sample(Standard)) 17 | } 18 | 19 | async fn insecure_random(&mut self) -> anyhow::Result<(u64, u64)> { 20 | Ok((0, 0)) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /wasi-sockets/src/wasi.rs: -------------------------------------------------------------------------------- 1 | wasmtime::component::bindgen!({ 2 | path: "../wit", 3 | interfaces : " 4 | import instance-network: sockets.instance-network 5 | import ip-name-lookup: sockets.ip-name-lookup 6 | import network: sockets.network 7 | import tcp-create-socket: sockets.tcp-create-socket 8 | import tcp: sockets.tcp 9 | import udp-create-socket: sockets.udp-create-socket 10 | import udp: sockets.udp 11 | ", 12 | tracing: true, 13 | async: true, 14 | trappable_error_type: { 15 | "streams"::"stream-error": Error, 16 | }, 17 | with: { 18 | "streams": wasi_common::wasi::streams, 19 | "poll": wasi_common::wasi::poll, 20 | } 21 | }); 22 | -------------------------------------------------------------------------------- /wasi/wit/deps/cli/environment.wit: -------------------------------------------------------------------------------- 1 | interface environment { 2 | /// Get the POSIX-style environment variables. 3 | /// 4 | /// Each environment variable is provided as a pair of string variable names 5 | /// and string value. 6 | /// 7 | /// Morally, these are a value import, but until value imports are available 8 | /// in the component model, this import function should return the same 9 | /// values each time it is called. 10 | get-environment: func() -> list> 11 | 12 | /// Get the POSIX-style arguments to the program. 13 | get-arguments: func() -> list 14 | 15 | /// Return a path that programs should use as their initial current working 16 | /// directory, interpreting `.` as shorthand for this. 17 | initial-cwd: func() -> option 18 | } 19 | -------------------------------------------------------------------------------- /wit/command.wit: -------------------------------------------------------------------------------- 1 | default world command { 2 | import wall-clock: clocks.wall-clock 3 | import monotonic-clock: clocks.monotonic-clock 4 | import timezone: clocks.timezone 5 | import filesystem: filesystem.filesystem 6 | import instance-network: sockets.instance-network 7 | import ip-name-lookup: sockets.ip-name-lookup 8 | import network: sockets.network 9 | import tcp-create-socket: sockets.tcp-create-socket 10 | import tcp: sockets.tcp 11 | import udp-create-socket: sockets.udp-create-socket 12 | import udp: sockets.udp 13 | import random: random.random 14 | import poll: poll.poll 15 | import streams: io.streams 16 | import environment: wasi-cli-base.environment 17 | import preopens: wasi-cli-base.preopens 18 | import exit: wasi-cli-base.exit 19 | 20 | export run: func() -> result 21 | } 22 | -------------------------------------------------------------------------------- /test-programs/wasi-tests/src/bin/clock_time_get.rs: -------------------------------------------------------------------------------- 1 | unsafe fn test_clock_time_get() { 2 | // Test that clock_time_get succeeds. Even in environments where it's not 3 | // desirable to expose high-precision timers, it should still succeed. 4 | // clock_res_get is where information about precision can be provided. 5 | wasi::clock_time_get(wasi::CLOCKID_MONOTONIC, 1).expect("precision 1 should work"); 6 | 7 | let first_time = 8 | wasi::clock_time_get(wasi::CLOCKID_MONOTONIC, 0).expect("precision 0 should work"); 9 | 10 | let time = wasi::clock_time_get(wasi::CLOCKID_MONOTONIC, 0).expect("re-fetch time should work"); 11 | assert!(first_time <= time, "CLOCK_MONOTONIC should be monotonic"); 12 | } 13 | 14 | fn main() { 15 | // Run the tests. 16 | unsafe { test_clock_time_get() } 17 | } 18 | -------------------------------------------------------------------------------- /crates/wasi-preview1-component-adapter/byte-array-literals/README.md: -------------------------------------------------------------------------------- 1 | # byte-array-literals 2 | 3 | This crate exists to solve a very peculiar problem for the 4 | `wasi-preview1-component-adapter`: we want to use string literals in our 5 | source code, but the resulting binary (when compiled for 6 | wasm32-unknown-unknown) cannot contain any data sections. 7 | 8 | The answer that @sunfishcode discovered is that these string literals, if 9 | represented as an array of u8 literals, these will somehow not end up in the 10 | data section, at least when compiled with opt-level='s' on today's rustc 11 | (1.69.0). So, this crate exists to transform these literals using a proc 12 | macro. 13 | 14 | It is very possible this cheat code will abruptly stop working in some future 15 | compiler, but we'll cross that bridge when we get to it. 16 | -------------------------------------------------------------------------------- /wasi/wit/deps/http/outgoing-handler.wit: -------------------------------------------------------------------------------- 1 | // The `wasi:http/outgoing-handler` interface is meant to be imported by 2 | // components and implemented by the host. 3 | // 4 | // NOTE: in Preview3, this interface will be merged with 5 | // `wasi:http/outgoing-handler` into a single `wasi:http/handler` interface 6 | // that takes a `request` parameter and returns a `response` result. 7 | // 8 | interface outgoing-handler { 9 | use types.{outgoing-request, request-options, future-incoming-response} 10 | 11 | // The parameter and result types of the `handle` function allow the caller 12 | // to concurrently stream the bodies of the outgoing request and the incoming 13 | // response. 14 | handle: func( 15 | request: outgoing-request, 16 | options: option 17 | ) -> future-incoming-response 18 | } 19 | -------------------------------------------------------------------------------- /wit/deps/http/outgoing-handler.wit: -------------------------------------------------------------------------------- 1 | // The `wasi:http/outgoing-handler` interface is meant to be imported by 2 | // components and implemented by the host. 3 | // 4 | // NOTE: in Preview3, this interface will be merged with 5 | // `wasi:http/outgoing-handler` into a single `wasi:http/handler` interface 6 | // that takes a `request` parameter and returns a `response` result. 7 | // 8 | default interface outgoing-handler { 9 | use pkg.types.{outgoing-request, request-options, future-incoming-response} 10 | 11 | // The parameter and result types of the `handle` function allow the caller 12 | // to concurrently stream the bodies of the outgoing request and the incoming 13 | // response. 14 | handle: func( 15 | request: outgoing-request, 16 | options: option 17 | ) -> future-incoming-response 18 | } 19 | -------------------------------------------------------------------------------- /crates/wasi-preview1-component-adapter/wit/command.wit: -------------------------------------------------------------------------------- 1 | default world command { 2 | import wall-clock: clocks.wall-clock 3 | import monotonic-clock: clocks.monotonic-clock 4 | import timezone: clocks.timezone 5 | import filesystem: filesystem.filesystem 6 | import instance-network: sockets.instance-network 7 | import ip-name-lookup: sockets.ip-name-lookup 8 | import network: sockets.network 9 | import tcp-create-socket: sockets.tcp-create-socket 10 | import tcp: sockets.tcp 11 | import udp-create-socket: sockets.udp-create-socket 12 | import udp: sockets.udp 13 | import random: random.random 14 | import poll: poll.poll 15 | import streams: io.streams 16 | import environment: wasi-cli-base.environment 17 | import preopens: wasi-cli-base.preopens 18 | import exit: wasi-cli-base.exit 19 | 20 | export run: func() -> result 21 | } 22 | -------------------------------------------------------------------------------- /host/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "host" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | anyhow = { workspace = true } 8 | cap-std = { workspace = true } 9 | wasmtime = { workspace = true } 10 | wasi-common = { workspace = true } 11 | wasmtime-wasi-sockets = { workspace = true } 12 | wasmtime-wasi-sockets-sync = { workspace = true } 13 | clap = { version = "4.1.9", features = ["derive"] } 14 | tracing-subscriber = { version = "0.3", default-features = false, features = ["env-filter", "fmt" ]} 15 | tokio = { version = "1", features = ["full"] } 16 | 17 | [dev-dependencies] 18 | tracing = { workspace = true } 19 | cap-rand = { workspace = true } 20 | test-programs = { path = "../test-programs" } 21 | test-log = { version = "0.2", default-features = false, features = ["trace"] } 22 | tempfile = "3.3.0" 23 | lazy_static = "1" 24 | -------------------------------------------------------------------------------- /test-programs/reactor-tests/wit/deps/http/outgoing-handler.wit: -------------------------------------------------------------------------------- 1 | // The `wasi:http/outgoing-handler` interface is meant to be imported by 2 | // components and implemented by the host. 3 | // 4 | // NOTE: in Preview3, this interface will be merged with 5 | // `wasi:http/outgoing-handler` into a single `wasi:http/handler` interface 6 | // that takes a `request` parameter and returns a `response` result. 7 | // 8 | default interface outgoing-handler { 9 | use pkg.types.{outgoing-request, request-options, future-incoming-response} 10 | 11 | // The parameter and result types of the `handle` function allow the caller 12 | // to concurrently stream the bodies of the outgoing request and the incoming 13 | // response. 14 | handle: func( 15 | request: outgoing-request, 16 | options: option 17 | ) -> future-incoming-response 18 | } 19 | -------------------------------------------------------------------------------- /test-programs/reactor-tests/wit/deps/preview/command.wit: -------------------------------------------------------------------------------- 1 | default world command { 2 | import wall-clock: clocks.wall-clock 3 | import monotonic-clock: clocks.monotonic-clock 4 | import timezone: clocks.timezone 5 | import filesystem: filesystem.filesystem 6 | import instance-network: sockets.instance-network 7 | import ip-name-lookup: sockets.ip-name-lookup 8 | import network: sockets.network 9 | import tcp-create-socket: sockets.tcp-create-socket 10 | import tcp: sockets.tcp 11 | import udp-create-socket: sockets.udp-create-socket 12 | import udp: sockets.udp 13 | import random: random.random 14 | import poll: poll.poll 15 | import streams: io.streams 16 | import environment: wasi-cli-base.environment 17 | import preopens: wasi-cli-base.preopens 18 | import exit: wasi-cli-base.exit 19 | 20 | export run: func() -> result 21 | } 22 | -------------------------------------------------------------------------------- /crates/wasi-preview1-component-adapter/wit/deps/http/outgoing-handler.wit: -------------------------------------------------------------------------------- 1 | // The `wasi:http/outgoing-handler` interface is meant to be imported by 2 | // components and implemented by the host. 3 | // 4 | // NOTE: in Preview3, this interface will be merged with 5 | // `wasi:http/outgoing-handler` into a single `wasi:http/handler` interface 6 | // that takes a `request` parameter and returns a `response` result. 7 | // 8 | default interface outgoing-handler { 9 | use pkg.types.{outgoing-request, request-options, future-incoming-response} 10 | 11 | // The parameter and result types of the `handle` function allow the caller 12 | // to concurrently stream the bodies of the outgoing request and the incoming 13 | // response. 14 | handle: func( 15 | request: outgoing-request, 16 | options: option 17 | ) -> future-incoming-response 18 | } 19 | -------------------------------------------------------------------------------- /crates/wasi-preview1-component-adapter/wit/deps/preview/command.wit: -------------------------------------------------------------------------------- 1 | default world command { 2 | import wall-clock: clocks.wall-clock 3 | import monotonic-clock: clocks.monotonic-clock 4 | import timezone: clocks.timezone 5 | import filesystem: filesystem.filesystem 6 | import instance-network: sockets.instance-network 7 | import ip-name-lookup: sockets.ip-name-lookup 8 | import network: sockets.network 9 | import tcp-create-socket: sockets.tcp-create-socket 10 | import tcp: sockets.tcp 11 | import udp-create-socket: sockets.udp-create-socket 12 | import udp: sockets.udp 13 | import random: random.random 14 | import poll: poll.poll 15 | import streams: io.streams 16 | import environment: wasi-cli-base.environment 17 | import preopens: wasi-cli-base.preopens 18 | import exit: wasi-cli-base.exit 19 | 20 | export run: func() -> result 21 | } 22 | -------------------------------------------------------------------------------- /wit/reactor.wit: -------------------------------------------------------------------------------- 1 | default world reactor { 2 | import wall-clock: clocks.wall-clock 3 | import monotonic-clock: clocks.monotonic-clock 4 | import timezone: clocks.timezone 5 | import filesystem: filesystem.filesystem 6 | import instance-network: sockets.instance-network 7 | import ip-name-lookup: sockets.ip-name-lookup 8 | import network: sockets.network 9 | import tcp-create-socket: sockets.tcp-create-socket 10 | import tcp: sockets.tcp 11 | import udp-create-socket: sockets.udp-create-socket 12 | import udp: sockets.udp 13 | import random: random.random 14 | import poll: poll.poll 15 | import streams: io.streams 16 | import console: logging.handler 17 | import default-outgoing-HTTP: http.outgoing-handler 18 | import environment: wasi-cli-base.environment 19 | import preopens: wasi-cli-base.preopens 20 | import exit: wasi-cli-base.exit 21 | } 22 | -------------------------------------------------------------------------------- /wasi-sockets/src/network.rs: -------------------------------------------------------------------------------- 1 | //! IP Networks. 2 | 3 | use anyhow::Error; 4 | use std::any::Any; 5 | 6 | /// An IP network. 7 | #[async_trait::async_trait] 8 | pub trait WasiNetwork: Send + Sync { 9 | fn as_any(&self) -> &dyn Any; 10 | 11 | fn pool(&self) -> &cap_std::net::Pool; 12 | } 13 | 14 | pub trait TableNetworkExt { 15 | fn get_network(&self, fd: u32) -> Result<&dyn WasiNetwork, Error>; 16 | fn get_network_mut(&mut self, fd: u32) -> Result<&mut Box, Error>; 17 | } 18 | impl TableNetworkExt for wasi_common::Table { 19 | fn get_network(&self, fd: u32) -> Result<&dyn WasiNetwork, Error> { 20 | Ok(self.get::>(fd).map(|f| f.as_ref())?) 21 | } 22 | fn get_network_mut(&mut self, fd: u32) -> Result<&mut Box, Error> { 23 | Ok(self.get_mut::>(fd)?) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /wasi-common/src/wasi/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod command; 2 | pub mod proxy; 3 | 4 | wasmtime::component::bindgen!({ 5 | path: "../wit", 6 | interfaces: " 7 | import wall-clock: clocks.wall-clock 8 | import monotonic-clock: clocks.monotonic-clock 9 | import timezone: clocks.timezone 10 | import filesystem: filesystem.filesystem 11 | import random: random.random 12 | import poll: poll.poll 13 | import streams: io.streams 14 | import environment: wasi-cli-base.environment 15 | import preopens: wasi-cli-base.preopens 16 | import exit: wasi-cli-base.exit 17 | import console: logging.handler 18 | import default-outgoing-HTTP: http.outgoing-handler 19 | ", 20 | tracing: true, 21 | async: true, 22 | trappable_error_type: { 23 | "filesystem"::"error-code": Error, 24 | "streams"::"stream-error": Error, 25 | } 26 | }); 27 | -------------------------------------------------------------------------------- /crates/wasi-preview1-component-adapter/wit/reactor.wit: -------------------------------------------------------------------------------- 1 | default world reactor { 2 | import wall-clock: clocks.wall-clock 3 | import monotonic-clock: clocks.monotonic-clock 4 | import timezone: clocks.timezone 5 | import filesystem: filesystem.filesystem 6 | import instance-network: sockets.instance-network 7 | import ip-name-lookup: sockets.ip-name-lookup 8 | import network: sockets.network 9 | import tcp-create-socket: sockets.tcp-create-socket 10 | import tcp: sockets.tcp 11 | import udp-create-socket: sockets.udp-create-socket 12 | import udp: sockets.udp 13 | import random: random.random 14 | import poll: poll.poll 15 | import streams: io.streams 16 | import console: logging.handler 17 | import default-outgoing-HTTP: http.outgoing-handler 18 | import environment: wasi-cli-base.environment 19 | import preopens: wasi-cli-base.preopens 20 | import exit: wasi-cli-base.exit 21 | } 22 | -------------------------------------------------------------------------------- /test-programs/reactor-tests/wit/deps/preview/reactor.wit: -------------------------------------------------------------------------------- 1 | default world reactor { 2 | import wall-clock: clocks.wall-clock 3 | import monotonic-clock: clocks.monotonic-clock 4 | import timezone: clocks.timezone 5 | import filesystem: filesystem.filesystem 6 | import instance-network: sockets.instance-network 7 | import ip-name-lookup: sockets.ip-name-lookup 8 | import network: sockets.network 9 | import tcp-create-socket: sockets.tcp-create-socket 10 | import tcp: sockets.tcp 11 | import udp-create-socket: sockets.udp-create-socket 12 | import udp: sockets.udp 13 | import random: random.random 14 | import poll: poll.poll 15 | import streams: io.streams 16 | import console: logging.handler 17 | import default-outgoing-HTTP: http.outgoing-handler 18 | import environment: wasi-cli-base.environment 19 | import preopens: wasi-cli-base.preopens 20 | import exit: wasi-cli-base.exit 21 | } 22 | -------------------------------------------------------------------------------- /crates/wasi-preview1-component-adapter/wit/deps/preview/reactor.wit: -------------------------------------------------------------------------------- 1 | default world reactor { 2 | import wall-clock: clocks.wall-clock 3 | import monotonic-clock: clocks.monotonic-clock 4 | import timezone: clocks.timezone 5 | import filesystem: filesystem.filesystem 6 | import instance-network: sockets.instance-network 7 | import ip-name-lookup: sockets.ip-name-lookup 8 | import network: sockets.network 9 | import tcp-create-socket: sockets.tcp-create-socket 10 | import tcp: sockets.tcp 11 | import udp-create-socket: sockets.udp-create-socket 12 | import udp: sockets.udp 13 | import random: random.random 14 | import poll: poll.poll 15 | import streams: io.streams 16 | import console: logging.handler 17 | import default-outgoing-HTTP: http.outgoing-handler 18 | import environment: wasi-cli-base.environment 19 | import preopens: wasi-cli-base.preopens 20 | import exit: wasi-cli-base.exit 21 | } 22 | -------------------------------------------------------------------------------- /wasi/wit/deps/random/insecure.wit: -------------------------------------------------------------------------------- 1 | /// The insecure interface for insecure pseudo-random numbers. 2 | /// 3 | /// It is intended to be portable at least between Unix-family platforms and 4 | /// Windows. 5 | interface insecure { 6 | /// Return `len` insecure pseudo-random bytes. 7 | /// 8 | /// This function is not cryptographically secure. Do not use it for 9 | /// anything related to security. 10 | /// 11 | /// There are no requirements on the values of the returned bytes, however 12 | /// implementations are encouraged to return evenly distributed values with 13 | /// a long period. 14 | get-insecure-random-bytes: func(len: u64) -> list 15 | 16 | /// Return an insecure pseudo-random `u64` value. 17 | /// 18 | /// This function returns the same type of pseudo-random data as 19 | /// `get-insecure-random-bytes`, represented as a `u64`. 20 | get-insecure-random-u64: func() -> u64 21 | } 22 | -------------------------------------------------------------------------------- /wit/deps/sockets/udp-create-socket.wit: -------------------------------------------------------------------------------- 1 | 2 | default interface udp-create-socket { 3 | use pkg.network.{network, error, ip-address-family} 4 | use pkg.udp.{udp-socket} 5 | 6 | /// Create a new UDP socket. 7 | /// 8 | /// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX. 9 | /// 10 | /// This function does not require a network capability handle. This is considered to be safe because 11 | /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`connect` is called, 12 | /// the socket is effectively an in-memory configuration object, unable to communicate with the outside world. 13 | /// 14 | /// References: 15 | /// - 16 | /// - 17 | /// 18 | create-udp-socket: func(address-family: ip-address-family) -> result 19 | } 20 | -------------------------------------------------------------------------------- /wit/deps/sockets/tcp-create-socket.wit: -------------------------------------------------------------------------------- 1 | 2 | default interface tcp-create-socket { 3 | use pkg.network.{network, error, ip-address-family} 4 | use pkg.tcp.{tcp-socket} 5 | 6 | /// Create a new TCP socket. 7 | /// 8 | /// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX. 9 | /// 10 | /// This function does not require a network capability handle. This is considered to be safe because 11 | /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`listen`/`connect` 12 | /// is called, the socket is effectively an in-memory configuration object, unable to communicate with the outside world. 13 | /// 14 | /// References: 15 | /// - 16 | /// - 17 | /// 18 | create-tcp-socket: func(address-family: ip-address-family) -> result 19 | } 20 | -------------------------------------------------------------------------------- /test-programs/wasi-tests/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "libc" 7 | version = "0.2.72" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "a9f8082297d534141b30c8d39e9b1773713ab50fdbe4ff30f750d063b3bfd701" 10 | 11 | [[package]] 12 | name = "once_cell" 13 | version = "1.12.0" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" 16 | 17 | [[package]] 18 | name = "wasi" 19 | version = "0.11.0+wasi-snapshot-preview1" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 22 | 23 | [[package]] 24 | name = "wasi-tests" 25 | version = "0.0.0" 26 | dependencies = [ 27 | "libc", 28 | "once_cell", 29 | "wasi", 30 | ] 31 | -------------------------------------------------------------------------------- /test-programs/reactor-tests/wit/deps/sockets/udp-create-socket.wit: -------------------------------------------------------------------------------- 1 | 2 | default interface udp-create-socket { 3 | use pkg.network.{network, error, ip-address-family} 4 | use pkg.udp.{udp-socket} 5 | 6 | /// Create a new UDP socket. 7 | /// 8 | /// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX. 9 | /// 10 | /// This function does not require a network capability handle. This is considered to be safe because 11 | /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`connect` is called, 12 | /// the socket is effectively an in-memory configuration object, unable to communicate with the outside world. 13 | /// 14 | /// References: 15 | /// - 16 | /// - 17 | /// 18 | create-udp-socket: func(address-family: ip-address-family) -> result 19 | } 20 | -------------------------------------------------------------------------------- /wasi/wit/deps/cli/command.wit: -------------------------------------------------------------------------------- 1 | package wasi:cli 2 | 3 | world command { 4 | import wasi:clocks/wall-clock 5 | import wasi:clocks/monotonic-clock 6 | import wasi:clocks/timezone 7 | import wasi:filesystem/types 8 | import wasi:filesystem/preopens 9 | import wasi:sockets/instance-network 10 | import wasi:sockets/ip-name-lookup 11 | import wasi:sockets/network 12 | import wasi:sockets/tcp-create-socket 13 | import wasi:sockets/tcp 14 | import wasi:sockets/udp-create-socket 15 | import wasi:sockets/udp 16 | import wasi:random/random 17 | import wasi:random/insecure 18 | import wasi:random/insecure-seed 19 | import wasi:poll/poll 20 | import wasi:io/streams 21 | 22 | import environment 23 | import exit 24 | import stdin 25 | import stdout 26 | import stderr 27 | import terminal-input 28 | import terminal-output 29 | import terminal-stdin 30 | import terminal-stdout 31 | import terminal-stderr 32 | export run 33 | } 34 | -------------------------------------------------------------------------------- /crates/wasi-preview1-component-adapter/wit/deps/sockets/udp-create-socket.wit: -------------------------------------------------------------------------------- 1 | 2 | default interface udp-create-socket { 3 | use pkg.network.{network, error, ip-address-family} 4 | use pkg.udp.{udp-socket} 5 | 6 | /// Create a new UDP socket. 7 | /// 8 | /// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX. 9 | /// 10 | /// This function does not require a network capability handle. This is considered to be safe because 11 | /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`connect` is called, 12 | /// the socket is effectively an in-memory configuration object, unable to communicate with the outside world. 13 | /// 14 | /// References: 15 | /// - 16 | /// - 17 | /// 18 | create-udp-socket: func(address-family: ip-address-family) -> result 19 | } 20 | -------------------------------------------------------------------------------- /test-programs/reactor-tests/wit/deps/sockets/tcp-create-socket.wit: -------------------------------------------------------------------------------- 1 | 2 | default interface tcp-create-socket { 3 | use pkg.network.{network, error, ip-address-family} 4 | use pkg.tcp.{tcp-socket} 5 | 6 | /// Create a new TCP socket. 7 | /// 8 | /// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX. 9 | /// 10 | /// This function does not require a network capability handle. This is considered to be safe because 11 | /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`listen`/`connect` 12 | /// is called, the socket is effectively an in-memory configuration object, unable to communicate with the outside world. 13 | /// 14 | /// References: 15 | /// - 16 | /// - 17 | /// 18 | create-tcp-socket: func(address-family: ip-address-family) -> result 19 | } 20 | -------------------------------------------------------------------------------- /crates/wasi-preview1-component-adapter/wit/deps/sockets/tcp-create-socket.wit: -------------------------------------------------------------------------------- 1 | 2 | default interface tcp-create-socket { 3 | use pkg.network.{network, error, ip-address-family} 4 | use pkg.tcp.{tcp-socket} 5 | 6 | /// Create a new TCP socket. 7 | /// 8 | /// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX. 9 | /// 10 | /// This function does not require a network capability handle. This is considered to be safe because 11 | /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`listen`/`connect` 12 | /// is called, the socket is effectively an in-memory configuration object, unable to communicate with the outside world. 13 | /// 14 | /// References: 15 | /// - 16 | /// - 17 | /// 18 | create-tcp-socket: func(address-family: ip-address-family) -> result 19 | } 20 | -------------------------------------------------------------------------------- /test-programs/command-tests/src/bin/directory_list.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{Context, Error}; 2 | use std::{collections::HashSet, fs, path::PathBuf}; 3 | 4 | fn main() -> Result<(), Error> { 5 | assert_eq!( 6 | ["/foo.txt", "/bar.txt", "/baz.txt", "/sub"] 7 | .into_iter() 8 | .map(PathBuf::from) 9 | .collect::>(), 10 | fs::read_dir("/") 11 | .context("read_dir /")? 12 | .map(|r| r.map(|d| d.path())) 13 | .collect::>() 14 | .context("elem in /")? 15 | ); 16 | 17 | assert_eq!( 18 | ["/sub/wow.txt", "/sub/yay.txt"] 19 | .into_iter() 20 | .map(PathBuf::from) 21 | .collect::>(), 22 | fs::read_dir("/sub") 23 | .context("read_dir /sub")? 24 | .map(|r| r.map(|d| d.path())) 25 | .collect::>() 26 | .context("elem in /sub")? 27 | ); 28 | 29 | Ok(()) 30 | } 31 | -------------------------------------------------------------------------------- /wasi-common/src/preview2/env.rs: -------------------------------------------------------------------------------- 1 | use crate::wasi; 2 | use crate::WasiView; 3 | 4 | #[async_trait::async_trait] 5 | impl wasi::environment::Host for T { 6 | async fn get_environment(&mut self) -> anyhow::Result> { 7 | Ok(self.ctx().env.clone()) 8 | } 9 | async fn get_arguments(&mut self) -> anyhow::Result> { 10 | Ok(self.ctx().args.clone()) 11 | } 12 | } 13 | 14 | #[async_trait::async_trait] 15 | impl wasi::preopens::Host for T { 16 | async fn get_stdio(&mut self) -> Result { 17 | Ok(wasi::preopens::StdioPreopens { 18 | stdin: self.ctx().stdin, 19 | stdout: self.ctx().stdout, 20 | stderr: self.ctx().stderr, 21 | }) 22 | } 23 | async fn get_directories( 24 | &mut self, 25 | ) -> Result, anyhow::Error> { 26 | Ok(self.ctx().preopens.clone()) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | Short version for non-lawyers: 2 | 3 | `wasi_snapshot_preview1.wasm` is triple-licensed under Apache 2.0 with the LLVM Exception, 4 | Apache 2.0, and MIT terms. 5 | 6 | 7 | Longer version: 8 | 9 | Copyrights in the `wasi_snapshot_preview1.wasm` project are retained by their contributors. 10 | No copyright assignment is required to contribute to the `wasi_snapshot_preview1.wasm` 11 | project. 12 | 13 | Some files include code derived from Rust's `libstd`; see the comments in 14 | the code for details. 15 | 16 | Except as otherwise noted (below and/or in individual files), `wasi_snapshot_preview1.wasm` 17 | is licensed under: 18 | 19 | - the Apache License, Version 2.0, with the LLVM Exception 20 | or 21 | 22 | - the Apache License, Version 2.0 23 | or 24 | , 25 | - or the MIT license 26 | or 27 | , 28 | 29 | at your option. 30 | -------------------------------------------------------------------------------- /wasi-sockets/sync/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wasmtime-wasi-sockets-sync" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | wasi-common = { workspace = true } 9 | wasmtime-wasi-sockets = { workspace = true } 10 | async-trait = { workspace = true } 11 | anyhow = { workspace = true } 12 | cap-std = { workspace = true } 13 | cap-fs-ext = { workspace = true } 14 | cap-net-ext = { workspace = true } 15 | cap-time-ext = "1.0.0" 16 | cap-rand = { workspace = true } 17 | fs-set-times = "0.19.0" 18 | system-interface = { workspace = true } 19 | tracing = { workspace = true } 20 | io-lifetimes = { workspace = true } 21 | is-terminal = "0.4.0" 22 | io-extras = "0.17.1" 23 | ipnet = { workspace = true } 24 | [target.'cfg(unix)'.dependencies] 25 | rustix = { workspace = true, features = ["fs"] } 26 | 27 | [target.'cfg(windows)'.dependencies] 28 | once_cell = { workspace = true } 29 | rustix = { workspace = true, features = ["net"] } 30 | 31 | [target.'cfg(windows)'.dependencies.windows-sys] 32 | workspace = true 33 | features = [ 34 | "Win32_Foundation", 35 | ] 36 | -------------------------------------------------------------------------------- /wasi/wit/reactor.wit: -------------------------------------------------------------------------------- 1 | package wasi:preview 2 | 3 | world reactor { 4 | import wasi:clocks/wall-clock 5 | import wasi:clocks/monotonic-clock 6 | import wasi:clocks/timezone 7 | import wasi:filesystem/types 8 | import wasi:filesystem/preopens 9 | import wasi:sockets/instance-network 10 | import wasi:sockets/ip-name-lookup 11 | import wasi:sockets/network 12 | import wasi:sockets/tcp-create-socket 13 | import wasi:sockets/tcp 14 | import wasi:sockets/udp-create-socket 15 | import wasi:sockets/udp 16 | import wasi:random/random 17 | import wasi:random/insecure 18 | import wasi:random/insecure-seed 19 | import wasi:poll/poll 20 | import wasi:io/streams 21 | import wasi:cli/environment 22 | import wasi:cli/exit 23 | import wasi:cli/stdin 24 | import wasi:cli/stdout 25 | import wasi:cli/stderr 26 | import wasi:cli/terminal-input 27 | import wasi:cli/terminal-output 28 | import wasi:cli/terminal-stdin 29 | import wasi:cli/terminal-stdout 30 | import wasi:cli/terminal-stderr 31 | 32 | import wasi:logging/logging 33 | import wasi:http/outgoing-handler 34 | } 35 | -------------------------------------------------------------------------------- /test-programs/wasi-tests/src/bin/path_open_missing.rs: -------------------------------------------------------------------------------- 1 | use std::{env, process}; 2 | use wasi_tests::{assert_errno, open_scratch_directory}; 3 | 4 | unsafe fn test_path_open_missing(dir_fd: wasi::Fd) { 5 | assert_errno!( 6 | wasi::path_open( 7 | dir_fd, 0, "file", 0, // not passing O_CREAT here 8 | 0, 0, 0, 9 | ) 10 | .expect_err("trying to open a file that doesn't exist"), 11 | wasi::ERRNO_NOENT 12 | ); 13 | } 14 | 15 | fn main() { 16 | let mut args = env::args(); 17 | let prog = args.next().unwrap(); 18 | let arg = if let Some(arg) = args.next() { 19 | arg 20 | } else { 21 | eprintln!("usage: {} ", prog); 22 | process::exit(1); 23 | }; 24 | 25 | // Open scratch directory 26 | let dir_fd = match open_scratch_directory(&arg) { 27 | Ok(dir_fd) => dir_fd, 28 | Err(err) => { 29 | eprintln!("{}", err); 30 | process::exit(1) 31 | } 32 | }; 33 | 34 | // Run the tests. 35 | unsafe { test_path_open_missing(dir_fd) } 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /test-programs/command-tests/src/bin/read_only.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | error::Error, 3 | fs::{self, File, OpenOptions}, 4 | io::{self, Seek, SeekFrom, Write}, 5 | }; 6 | 7 | fn main() -> Result<(), Box> { 8 | { 9 | let mut file = File::open("bar.txt")?; 10 | 11 | assert_eq!(27, file.metadata()?.len()); 12 | 13 | assert_eq!( 14 | "And stood awhile in thought", 15 | &io::read_to_string(&mut file)? 16 | ); 17 | 18 | file.seek(SeekFrom::Start(11))?; 19 | 20 | assert_eq!("while in thought", &io::read_to_string(&mut file)?); 21 | 22 | assert!(file 23 | .write_all(b"Did gyre and gimble in the wabe;\n") 24 | .is_err()); 25 | } 26 | 27 | assert!(OpenOptions::new().append(true).open("bar.txt").is_err()); 28 | assert!(File::create("new.txt").is_err()); 29 | assert!(fs::create_dir("sub2").is_err()); 30 | assert!(fs::rename("bar.txt", "baz.txt").is_err()); 31 | assert!(fs::remove_file("bar.txt").is_err()); 32 | assert!(fs::remove_dir("sub").is_err()); 33 | 34 | Ok(()) 35 | } 36 | -------------------------------------------------------------------------------- /wasi/wit/deps/random/random.wit: -------------------------------------------------------------------------------- 1 | package wasi:random 2 | 3 | /// WASI Random is a random data API. 4 | /// 5 | /// It is intended to be portable at least between Unix-family platforms and 6 | /// Windows. 7 | interface random { 8 | /// Return `len` cryptographically-secure pseudo-random bytes. 9 | /// 10 | /// This function must produce data from an adequately seeded 11 | /// cryptographically-secure pseudo-random number generator (CSPRNG), so it 12 | /// must not block, from the perspective of the calling program, and the 13 | /// returned data is always unpredictable. 14 | /// 15 | /// This function must always return fresh pseudo-random data. Deterministic 16 | /// environments must omit this function, rather than implementing it with 17 | /// deterministic data. 18 | get-random-bytes: func(len: u64) -> list 19 | 20 | /// Return a cryptographically-secure pseudo-random `u64` value. 21 | /// 22 | /// This function returns the same type of pseudo-random data as 23 | /// `get-random-bytes`, represented as a `u64`. 24 | get-random-u64: func() -> u64 25 | } 26 | -------------------------------------------------------------------------------- /wasi-common/src/error.rs: -------------------------------------------------------------------------------- 1 | //! wasi-common uses an [`Error`] type which represents either a preview 1 [`Errno`] enum, on 2 | //! [`anyhow::Error`] for trapping execution. 3 | //! 4 | //! The user can construct an [`Error`] out of an [`Errno`] using the `From`/`Into` traits. 5 | //! They may also use [`Error::trap`] to construct an error that traps execution. The contents 6 | //! can be inspected with [`Error::downcast`] and [`Error::downcast_ref`]. Additional context 7 | //! can be provided with the [`Error::context`] method. This context is only observable with the 8 | //! `Display` and `Debug` impls of the error. 9 | 10 | use std::fmt; 11 | 12 | /// An error returned from the `proc_exit` host syscall. 13 | /// 14 | /// Embedders can test if an error returned from wasm is this error, in which 15 | /// case it may signal a non-fatal trap. 16 | #[derive(Debug)] 17 | pub struct I32Exit(pub i32); 18 | 19 | impl fmt::Display for I32Exit { 20 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 21 | write!(f, "Exited with i32 exit status {}", self.0) 22 | } 23 | } 24 | 25 | impl std::error::Error for I32Exit {} 26 | -------------------------------------------------------------------------------- /wit/deps/clocks/monotonic-clock.wit: -------------------------------------------------------------------------------- 1 | /// WASI Monotonic Clock is a clock API intended to let users measure elapsed 2 | /// time. 3 | /// 4 | /// It is intended to be portable at least between Unix-family platforms and 5 | /// Windows. 6 | /// 7 | /// A monotonic clock is a clock which has an unspecified initial value, and 8 | /// successive reads of the clock will produce non-decreasing values. 9 | /// 10 | /// It is intended for measuring elapsed time. 11 | default interface monotonic-clock { 12 | use poll.poll.{pollable} 13 | 14 | /// A timestamp in nanoseconds. 15 | type instant = u64 16 | 17 | /// Read the current value of the clock. 18 | /// 19 | /// The clock is monotonic, therefore calling this function repeatedly will 20 | /// produce a sequence of non-decreasing values. 21 | now: func() -> instant 22 | 23 | /// Query the resolution of the clock. 24 | resolution: func() -> instant 25 | 26 | /// Create a `pollable` which will resolve once the specified time has been 27 | /// reached. 28 | subscribe: func( 29 | when: instant, 30 | absolute: bool 31 | ) -> pollable 32 | } 33 | -------------------------------------------------------------------------------- /test-programs/command-tests/src/bin/export_cabi_realloc.rs: -------------------------------------------------------------------------------- 1 | //! `wit-component` handles modules which export `cabi_realloc` in a special way, using it instead of `memory.grow` 2 | //! to allocate the adapter stack, hence this test. 3 | 4 | #[export_name = "cabi_realloc"] 5 | #[no_mangle] 6 | unsafe extern "C" fn cabi_realloc( 7 | old_ptr: *mut u8, 8 | old_len: usize, 9 | align: usize, 10 | new_len: usize, 11 | ) -> *mut u8 { 12 | use std::alloc::{self, Layout}; 13 | 14 | let layout; 15 | let ptr = if old_len == 0 { 16 | if new_len == 0 { 17 | return align as *mut u8; 18 | } 19 | layout = Layout::from_size_align_unchecked(new_len, align); 20 | alloc::alloc(layout) 21 | } else { 22 | debug_assert_ne!(new_len, 0, "non-zero old_len requires non-zero new_len!"); 23 | layout = Layout::from_size_align_unchecked(old_len, align); 24 | alloc::realloc(old_ptr, layout, new_len) 25 | }; 26 | if ptr.is_null() { 27 | core::arch::wasm32::unreachable(); 28 | } 29 | return ptr; 30 | } 31 | 32 | fn main() { 33 | println!("hello, world"); 34 | } 35 | -------------------------------------------------------------------------------- /wasi/wit/deps/preview/main.wit: -------------------------------------------------------------------------------- 1 | package wasmtime:wasi 2 | 3 | // All of the same imports available in the wasi:cli/command world, but no 4 | // export required: 5 | world preview1-adapter-reactor { 6 | import wasi:clocks/wall-clock 7 | import wasi:clocks/monotonic-clock 8 | import wasi:clocks/timezone 9 | import wasi:filesystem/types 10 | import wasi:filesystem/preopens 11 | import wasi:sockets/instance-network 12 | import wasi:sockets/ip-name-lookup 13 | import wasi:sockets/network 14 | import wasi:sockets/tcp-create-socket 15 | import wasi:sockets/tcp 16 | import wasi:sockets/udp-create-socket 17 | import wasi:sockets/udp 18 | import wasi:random/random 19 | import wasi:random/insecure 20 | import wasi:random/insecure-seed 21 | import wasi:poll/poll 22 | import wasi:io/streams 23 | import wasi:cli/environment 24 | import wasi:cli/exit 25 | import wasi:cli/stdin 26 | import wasi:cli/stdout 27 | import wasi:cli/stderr 28 | import wasi:cli/terminal-input 29 | import wasi:cli/terminal-output 30 | import wasi:cli/terminal-stdin 31 | import wasi:cli/terminal-stdout 32 | import wasi:cli/terminal-stderr 33 | } 34 | -------------------------------------------------------------------------------- /wit/command-extended.wit: -------------------------------------------------------------------------------- 1 | default world command-extended { 2 | import wall-clock: clocks.wall-clock 3 | import monotonic-clock: clocks.monotonic-clock 4 | import timezone: clocks.timezone 5 | import filesystem: filesystem.filesystem 6 | import instance-network: sockets.instance-network 7 | import ip-name-lookup: sockets.ip-name-lookup 8 | import network: sockets.network 9 | import tcp-create-socket: sockets.tcp-create-socket 10 | import tcp: sockets.tcp 11 | import udp-create-socket: sockets.udp-create-socket 12 | import udp: sockets.udp 13 | import random: random.random 14 | import poll: poll.poll 15 | import streams: io.streams 16 | import environment: wasi-cli-base.environment 17 | import preopens: wasi-cli-base.preopens 18 | import exit: wasi-cli-base.exit 19 | 20 | // We should replace all others with `include self.command` 21 | // as soon as the unioning of worlds is available: 22 | // https://github.com/WebAssembly/component-model/issues/169 23 | import console: logging.handler 24 | import default-outgoing-HTTP: http.outgoing-handler 25 | 26 | export run: func( 27 | args: list, 28 | ) -> result 29 | } 30 | -------------------------------------------------------------------------------- /test-programs/reactor-tests/wit/deps/clocks/monotonic-clock.wit: -------------------------------------------------------------------------------- 1 | /// WASI Monotonic Clock is a clock API intended to let users measure elapsed 2 | /// time. 3 | /// 4 | /// It is intended to be portable at least between Unix-family platforms and 5 | /// Windows. 6 | /// 7 | /// A monotonic clock is a clock which has an unspecified initial value, and 8 | /// successive reads of the clock will produce non-decreasing values. 9 | /// 10 | /// It is intended for measuring elapsed time. 11 | default interface monotonic-clock { 12 | use poll.poll.{pollable} 13 | 14 | /// A timestamp in nanoseconds. 15 | type instant = u64 16 | 17 | /// Read the current value of the clock. 18 | /// 19 | /// The clock is monotonic, therefore calling this function repeatedly will 20 | /// produce a sequence of non-decreasing values. 21 | now: func() -> instant 22 | 23 | /// Query the resolution of the clock. 24 | resolution: func() -> instant 25 | 26 | /// Create a `pollable` which will resolve once the specified time has been 27 | /// reached. 28 | subscribe: func( 29 | when: instant, 30 | absolute: bool 31 | ) -> pollable 32 | } 33 | -------------------------------------------------------------------------------- /wasi/wit/deps/clocks/monotonic-clock.wit: -------------------------------------------------------------------------------- 1 | package wasi:clocks 2 | 3 | /// WASI Monotonic Clock is a clock API intended to let users measure elapsed 4 | /// time. 5 | /// 6 | /// It is intended to be portable at least between Unix-family platforms and 7 | /// Windows. 8 | /// 9 | /// A monotonic clock is a clock which has an unspecified initial value, and 10 | /// successive reads of the clock will produce non-decreasing values. 11 | /// 12 | /// It is intended for measuring elapsed time. 13 | interface monotonic-clock { 14 | use wasi:poll/poll.{pollable} 15 | 16 | /// A timestamp in nanoseconds. 17 | type instant = u64 18 | 19 | /// Read the current value of the clock. 20 | /// 21 | /// The clock is monotonic, therefore calling this function repeatedly will 22 | /// produce a sequence of non-decreasing values. 23 | now: func() -> instant 24 | 25 | /// Query the resolution of the clock. 26 | resolution: func() -> instant 27 | 28 | /// Create a `pollable` which will resolve once the specified time has been 29 | /// reached. 30 | subscribe: func( 31 | when: instant, 32 | absolute: bool 33 | ) -> pollable 34 | } 35 | -------------------------------------------------------------------------------- /crates/wasi-preview1-component-adapter/wit/deps/clocks/monotonic-clock.wit: -------------------------------------------------------------------------------- 1 | /// WASI Monotonic Clock is a clock API intended to let users measure elapsed 2 | /// time. 3 | /// 4 | /// It is intended to be portable at least between Unix-family platforms and 5 | /// Windows. 6 | /// 7 | /// A monotonic clock is a clock which has an unspecified initial value, and 8 | /// successive reads of the clock will produce non-decreasing values. 9 | /// 10 | /// It is intended for measuring elapsed time. 11 | default interface monotonic-clock { 12 | use poll.poll.{pollable} 13 | 14 | /// A timestamp in nanoseconds. 15 | type instant = u64 16 | 17 | /// Read the current value of the clock. 18 | /// 19 | /// The clock is monotonic, therefore calling this function repeatedly will 20 | /// produce a sequence of non-decreasing values. 21 | now: func() -> instant 22 | 23 | /// Query the resolution of the clock. 24 | resolution: func() -> instant 25 | 26 | /// Create a `pollable` which will resolve once the specified time has been 27 | /// reached. 28 | subscribe: func( 29 | when: instant, 30 | absolute: bool 31 | ) -> pollable 32 | } 33 | -------------------------------------------------------------------------------- /wasi/wit/deps/random/insecure-seed.wit: -------------------------------------------------------------------------------- 1 | /// The insecure-seed interface for seeding hash-map DoS resistance. 2 | /// 3 | /// It is intended to be portable at least between Unix-family platforms and 4 | /// Windows. 5 | interface insecure-seed { 6 | /// Return a 128-bit value that may contain a pseudo-random value. 7 | /// 8 | /// The returned value is not required to be computed from a CSPRNG, and may 9 | /// even be entirely deterministic. Host implementations are encouraged to 10 | /// provide pseudo-random values to any program exposed to 11 | /// attacker-controlled content, to enable DoS protection built into many 12 | /// languages' hash-map implementations. 13 | /// 14 | /// This function is intended to only be called once, by a source language 15 | /// to initialize Denial Of Service (DoS) protection in its hash-map 16 | /// implementation. 17 | /// 18 | /// # Expected future evolution 19 | /// 20 | /// This will likely be changed to a value import, to prevent it from being 21 | /// called multiple times and potentially used for purposes other than DoS 22 | /// protection. 23 | insecure-seed: func() -> tuple 24 | } 25 | -------------------------------------------------------------------------------- /test-programs/reactor-tests/wit/deps/preview/command-extended.wit: -------------------------------------------------------------------------------- 1 | default world command-extended { 2 | import wall-clock: clocks.wall-clock 3 | import monotonic-clock: clocks.monotonic-clock 4 | import timezone: clocks.timezone 5 | import filesystem: filesystem.filesystem 6 | import instance-network: sockets.instance-network 7 | import ip-name-lookup: sockets.ip-name-lookup 8 | import network: sockets.network 9 | import tcp-create-socket: sockets.tcp-create-socket 10 | import tcp: sockets.tcp 11 | import udp-create-socket: sockets.udp-create-socket 12 | import udp: sockets.udp 13 | import random: random.random 14 | import poll: poll.poll 15 | import streams: io.streams 16 | import environment: wasi-cli-base.environment 17 | import preopens: wasi-cli-base.preopens 18 | import exit: wasi-cli-base.exit 19 | 20 | // We should replace all others with `include self.command` 21 | // as soon as the unioning of worlds is available: 22 | // https://github.com/WebAssembly/component-model/issues/169 23 | import console: logging.handler 24 | import default-outgoing-HTTP: http.outgoing-handler 25 | 26 | export run: func( 27 | args: list, 28 | ) -> result 29 | } 30 | -------------------------------------------------------------------------------- /test-programs/wasi-tests/src/bin/fd_filestat_get.rs: -------------------------------------------------------------------------------- 1 | unsafe fn test_fd_filestat_get() { 2 | let stat = wasi::fd_filestat_get(libc::STDIN_FILENO as u32).expect("failed filestat 0"); 3 | assert_eq!(stat.size, 0, "stdio size should be 0"); 4 | assert_eq!(stat.atim, 0, "stdio atim should be 0"); 5 | assert_eq!(stat.mtim, 0, "stdio mtim should be 0"); 6 | assert_eq!(stat.ctim, 0, "stdio ctim should be 0"); 7 | 8 | let stat = wasi::fd_filestat_get(libc::STDOUT_FILENO as u32).expect("failed filestat 1"); 9 | assert_eq!(stat.size, 0, "stdio size should be 0"); 10 | assert_eq!(stat.atim, 0, "stdio atim should be 0"); 11 | assert_eq!(stat.mtim, 0, "stdio mtim should be 0"); 12 | assert_eq!(stat.ctim, 0, "stdio ctim should be 0"); 13 | 14 | let stat = wasi::fd_filestat_get(libc::STDERR_FILENO as u32).expect("failed filestat 2"); 15 | assert_eq!(stat.size, 0, "stdio size should be 0"); 16 | assert_eq!(stat.atim, 0, "stdio atim should be 0"); 17 | assert_eq!(stat.mtim, 0, "stdio mtim should be 0"); 18 | assert_eq!(stat.ctim, 0, "stdio ctim should be 0"); 19 | } 20 | 21 | fn main() { 22 | // Run the tests. 23 | unsafe { test_fd_filestat_get() } 24 | } 25 | -------------------------------------------------------------------------------- /crates/wasi-preview1-component-adapter/wit/deps/preview/command-extended.wit: -------------------------------------------------------------------------------- 1 | default world command-extended { 2 | import wall-clock: clocks.wall-clock 3 | import monotonic-clock: clocks.monotonic-clock 4 | import timezone: clocks.timezone 5 | import filesystem: filesystem.filesystem 6 | import instance-network: sockets.instance-network 7 | import ip-name-lookup: sockets.ip-name-lookup 8 | import network: sockets.network 9 | import tcp-create-socket: sockets.tcp-create-socket 10 | import tcp: sockets.tcp 11 | import udp-create-socket: sockets.udp-create-socket 12 | import udp: sockets.udp 13 | import random: random.random 14 | import poll: poll.poll 15 | import streams: io.streams 16 | import environment: wasi-cli-base.environment 17 | import preopens: wasi-cli-base.preopens 18 | import exit: wasi-cli-base.exit 19 | 20 | // We should replace all others with `include self.command` 21 | // as soon as the unioning of worlds is available: 22 | // https://github.com/WebAssembly/component-model/issues/169 23 | import console: logging.handler 24 | import default-outgoing-HTTP: http.outgoing-handler 25 | 26 | export run: func( 27 | args: list, 28 | ) -> result 29 | } 30 | -------------------------------------------------------------------------------- /test-programs/reactor-tests/src/lib.rs: -------------------------------------------------------------------------------- 1 | wit_bindgen::generate!("test-reactor"); 2 | 3 | export_test_reactor!(T); 4 | 5 | struct T; 6 | 7 | static mut STATE: Vec = Vec::new(); 8 | 9 | impl TestReactor for T { 10 | fn add_strings(ss: Vec) -> u32 { 11 | for s in ss { 12 | match s.split_once("$") { 13 | Some((prefix, var)) if prefix.is_empty() => match std::env::var(var) { 14 | Ok(val) => unsafe { STATE.push(val) }, 15 | Err(_) => unsafe { STATE.push("undefined".to_owned()) }, 16 | }, 17 | _ => unsafe { STATE.push(s) }, 18 | } 19 | } 20 | unsafe { STATE.len() as u32 } 21 | } 22 | fn get_strings() -> Vec { 23 | unsafe { STATE.clone() } 24 | } 25 | 26 | fn write_strings_to(o: OutputStream) -> Result<(), ()> { 27 | unsafe { 28 | for s in STATE.iter() { 29 | streams::write(o, s.as_bytes()).map_err(|_| ())?; 30 | } 31 | Ok(()) 32 | } 33 | } 34 | fn pass_an_imported_record(stat: filesystem::DescriptorStat) -> String { 35 | format!("{stat:?}") 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /wasi/wit/deps/preview/test.wit: -------------------------------------------------------------------------------- 1 | // only used as part of `test-programs` 2 | world test-reactor { 3 | 4 | import wasi:cli/environment 5 | import wasi:io/streams 6 | import wasi:filesystem/types 7 | import wasi:filesystem/preopens 8 | import wasi:cli/exit 9 | 10 | export add-strings: func(s: list) -> u32 11 | export get-strings: func() -> list 12 | 13 | use wasi:io/streams.{output-stream} 14 | 15 | export write-strings-to: func(o: output-stream) -> result 16 | 17 | use wasi:filesystem/types.{descriptor-stat} 18 | export pass-an-imported-record: func(d: descriptor-stat) -> string 19 | } 20 | 21 | world test-command { 22 | import wasi:poll/poll 23 | import wasi:io/streams 24 | import wasi:cli/environment 25 | import wasi:cli/stdin 26 | import wasi:cli/stdout 27 | import wasi:cli/stderr 28 | } 29 | 30 | world test-command-with-sockets { 31 | import wasi:poll/poll 32 | import wasi:io/streams 33 | import wasi:cli/environment 34 | import wasi:cli/stdin 35 | import wasi:cli/stdout 36 | import wasi:cli/stderr 37 | import wasi:sockets/tcp 38 | import wasi:sockets/tcp-create-socket 39 | import wasi:sockets/network 40 | import wasi:sockets/instance-network 41 | } 42 | -------------------------------------------------------------------------------- /wit/deps/logging/handler.wit: -------------------------------------------------------------------------------- 1 | /// WASI Logging is a logging API intended to let users emit log messages with 2 | /// simple priority levels and context values. 3 | default interface handler { 4 | /// A log level, describing a kind of message. 5 | enum level { 6 | /// Describes messages about the values of variables and the flow of 7 | /// control within a program. 8 | trace, 9 | 10 | /// Describes messages likely to be of interest to someone debugging a 11 | /// program. 12 | debug, 13 | 14 | /// Describes messages likely to be of interest to someone monitoring a 15 | /// program. 16 | info, 17 | 18 | /// Describes messages indicating hazardous situations. 19 | warn, 20 | 21 | /// Describes messages indicating serious errors. 22 | error, 23 | } 24 | 25 | /// Emit a log message. 26 | /// 27 | /// A log message has a `level` describing what kind of message is being 28 | /// sent, a context, which is an uninterpreted string meant to help 29 | /// consumers group similar messages, and a string containing the message 30 | /// text. 31 | log: func(level: level, context: string, message: string) 32 | } 33 | -------------------------------------------------------------------------------- /wasi/wit/deps/http/incoming-handler.wit: -------------------------------------------------------------------------------- 1 | // The `wasi:http/incoming-handler` interface is meant to be exported by 2 | // components and called by the host in response to a new incoming HTTP 3 | // response. 4 | // 5 | // NOTE: in Preview3, this interface will be merged with 6 | // `wasi:http/outgoing-handler` into a single `wasi:http/handler` interface 7 | // that takes a `request` parameter and returns a `response` result. 8 | // 9 | interface incoming-handler { 10 | use types.{incoming-request, response-outparam} 11 | 12 | // The `handle` function takes an outparam instead of returning its response 13 | // so that the component may stream its response while streaming any other 14 | // request or response bodies. The callee MUST write a response to the 15 | // `response-out` and then finish the response before returning. The `handle` 16 | // function is allowed to continue execution after finishing the response's 17 | // output stream. While this post-response execution is taken off the 18 | // critical path, since there is no return value, there is no way to report 19 | // its success or failure. 20 | handle: func( 21 | request: incoming-request, 22 | response-out: response-outparam 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /wit/deps/http/incoming-handler.wit: -------------------------------------------------------------------------------- 1 | // The `wasi:http/incoming-handler` interface is meant to be exported by 2 | // components and called by the host in response to a new incoming HTTP 3 | // response. 4 | // 5 | // NOTE: in Preview3, this interface will be merged with 6 | // `wasi:http/outgoing-handler` into a single `wasi:http/handler` interface 7 | // that takes a `request` parameter and returns a `response` result. 8 | // 9 | default interface incoming-handler { 10 | use pkg.types.{incoming-request, response-outparam} 11 | 12 | // The `handle` function takes an outparam instead of returning its response 13 | // so that the component may stream its response while streaming any other 14 | // request or response bodies. The callee MUST write a response to the 15 | // `response-out` and then finish the response before returning. The `handle` 16 | // function is allowed to continue execution after finishing the response's 17 | // output stream. While this post-response execution is taken off the 18 | // critical path, since there is no return value, there is no way to report 19 | // its success or failure. 20 | handle: func( 21 | request: incoming-request, 22 | response-out: response-outparam 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /test-programs/reactor-tests/wit/deps/logging/handler.wit: -------------------------------------------------------------------------------- 1 | /// WASI Logging is a logging API intended to let users emit log messages with 2 | /// simple priority levels and context values. 3 | default interface handler { 4 | /// A log level, describing a kind of message. 5 | enum level { 6 | /// Describes messages about the values of variables and the flow of 7 | /// control within a program. 8 | trace, 9 | 10 | /// Describes messages likely to be of interest to someone debugging a 11 | /// program. 12 | debug, 13 | 14 | /// Describes messages likely to be of interest to someone monitoring a 15 | /// program. 16 | info, 17 | 18 | /// Describes messages indicating hazardous situations. 19 | warn, 20 | 21 | /// Describes messages indicating serious errors. 22 | error, 23 | } 24 | 25 | /// Emit a log message. 26 | /// 27 | /// A log message has a `level` describing what kind of message is being 28 | /// sent, a context, which is an uninterpreted string meant to help 29 | /// consumers group similar messages, and a string containing the message 30 | /// text. 31 | log: func(level: level, context: string, message: string) 32 | } 33 | -------------------------------------------------------------------------------- /crates/wasi-preview1-component-adapter/wit/deps/logging/handler.wit: -------------------------------------------------------------------------------- 1 | /// WASI Logging is a logging API intended to let users emit log messages with 2 | /// simple priority levels and context values. 3 | default interface handler { 4 | /// A log level, describing a kind of message. 5 | enum level { 6 | /// Describes messages about the values of variables and the flow of 7 | /// control within a program. 8 | trace, 9 | 10 | /// Describes messages likely to be of interest to someone debugging a 11 | /// program. 12 | debug, 13 | 14 | /// Describes messages likely to be of interest to someone monitoring a 15 | /// program. 16 | info, 17 | 18 | /// Describes messages indicating hazardous situations. 19 | warn, 20 | 21 | /// Describes messages indicating serious errors. 22 | error, 23 | } 24 | 25 | /// Emit a log message. 26 | /// 27 | /// A log message has a `level` describing what kind of message is being 28 | /// sent, a context, which is an uninterpreted string meant to help 29 | /// consumers group similar messages, and a string containing the message 30 | /// text. 31 | log: func(level: level, context: string, message: string) 32 | } 33 | -------------------------------------------------------------------------------- /test-programs/reactor-tests/wit/deps/http/incoming-handler.wit: -------------------------------------------------------------------------------- 1 | // The `wasi:http/incoming-handler` interface is meant to be exported by 2 | // components and called by the host in response to a new incoming HTTP 3 | // response. 4 | // 5 | // NOTE: in Preview3, this interface will be merged with 6 | // `wasi:http/outgoing-handler` into a single `wasi:http/handler` interface 7 | // that takes a `request` parameter and returns a `response` result. 8 | // 9 | default interface incoming-handler { 10 | use pkg.types.{incoming-request, response-outparam} 11 | 12 | // The `handle` function takes an outparam instead of returning its response 13 | // so that the component may stream its response while streaming any other 14 | // request or response bodies. The callee MUST write a response to the 15 | // `response-out` and then finish the response before returning. The `handle` 16 | // function is allowed to continue execution after finishing the response's 17 | // output stream. While this post-response execution is taken off the 18 | // critical path, since there is no return value, there is no way to report 19 | // its success or failure. 20 | handle: func( 21 | request: incoming-request, 22 | response-out: response-outparam 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /crates/wasi-preview1-component-adapter/wit/deps/http/incoming-handler.wit: -------------------------------------------------------------------------------- 1 | // The `wasi:http/incoming-handler` interface is meant to be exported by 2 | // components and called by the host in response to a new incoming HTTP 3 | // response. 4 | // 5 | // NOTE: in Preview3, this interface will be merged with 6 | // `wasi:http/outgoing-handler` into a single `wasi:http/handler` interface 7 | // that takes a `request` parameter and returns a `response` result. 8 | // 9 | default interface incoming-handler { 10 | use pkg.types.{incoming-request, response-outparam} 11 | 12 | // The `handle` function takes an outparam instead of returning its response 13 | // so that the component may stream its response while streaming any other 14 | // request or response bodies. The callee MUST write a response to the 15 | // `response-out` and then finish the response before returning. The `handle` 16 | // function is allowed to continue execution after finishing the response's 17 | // output stream. While this post-response execution is taken off the 18 | // critical path, since there is no return value, there is no way to report 19 | // its success or failure. 20 | handle: func( 21 | request: incoming-request, 22 | response-out: response-outparam 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /wasi-common/src/wasi/proxy.rs: -------------------------------------------------------------------------------- 1 | use crate::WasiView; 2 | 3 | wasmtime::component::bindgen!({ 4 | path: "../wit", 5 | world: "proxy", 6 | tracing: true, 7 | async: true, 8 | trappable_error_type: { 9 | "filesystem"::"error-code": Error, 10 | "streams"::"stream-error": Error, 11 | }, 12 | with: { 13 | "filesystem": crate::wasi::filesystem, 14 | "monotonic_clock": crate::wasi::monotonic_clock, 15 | "poll": crate::wasi::poll, 16 | "streams": crate::wasi::streams, 17 | "timezone": crate::wasi::timezone, 18 | "wall_clock": crate::wasi::wall_clock, 19 | "random": crate::wasi::random, 20 | "environment": crate::wasi::environment, 21 | "exit": crate::wasi::exit, 22 | "preopens": crate::wasi::preopens, 23 | }, 24 | }); 25 | 26 | pub fn add_to_linker(l: &mut wasmtime::component::Linker) -> anyhow::Result<()> { 27 | crate::wasi::random::add_to_linker(l, |t| t)?; 28 | crate::wasi::console::add_to_linker(l, |t| t)?; 29 | crate::wasi::types::add_to_linker(l, |t| t)?; 30 | crate::wasi::poll::add_to_linker(l, |t| t)?; 31 | crate::wasi::streams::add_to_linker(l, |t| t)?; 32 | crate::wasi::default_outgoing_http::add_to_linker(l, |t| t)?; 33 | Ok(()) 34 | } 35 | -------------------------------------------------------------------------------- /test-programs/wasi-tests/src/bin/path_open_dirfd_not_dir.rs: -------------------------------------------------------------------------------- 1 | use std::{env, process}; 2 | use wasi_tests::{assert_errno, open_scratch_directory}; 3 | 4 | unsafe fn test_dirfd_not_dir(dir_fd: wasi::Fd) { 5 | // Open a file. 6 | let file_fd = 7 | wasi::path_open(dir_fd, 0, "file", wasi::OFLAGS_CREAT, 0, 0, 0).expect("opening a file"); 8 | // Now try to open a file underneath it as if it were a directory. 9 | assert_errno!( 10 | wasi::path_open(file_fd, 0, "foo", wasi::OFLAGS_CREAT, 0, 0, 0) 11 | .expect_err("non-directory base fd should get ERRNO_NOTDIR"), 12 | wasi::ERRNO_NOTDIR 13 | ); 14 | wasi::fd_close(file_fd).expect("closing a file"); 15 | } 16 | 17 | fn main() { 18 | let mut args = env::args(); 19 | let prog = args.next().unwrap(); 20 | let arg = if let Some(arg) = args.next() { 21 | arg 22 | } else { 23 | eprintln!("usage: {} ", prog); 24 | process::exit(1); 25 | }; 26 | 27 | // Open scratch directory 28 | let dir_fd = match open_scratch_directory(&arg) { 29 | Ok(dir_fd) => dir_fd, 30 | Err(err) => { 31 | eprintln!("{}", err); 32 | process::exit(1) 33 | } 34 | }; 35 | 36 | // Run the tests. 37 | unsafe { test_dirfd_not_dir(dir_fd) } 38 | } 39 | -------------------------------------------------------------------------------- /wasi/wit/deps/logging/logging.wit: -------------------------------------------------------------------------------- 1 | package wasi:logging 2 | 3 | /// WASI Logging is a logging API intended to let users emit log messages with 4 | /// simple priority levels and context values. 5 | interface logging { 6 | /// A log level, describing a kind of message. 7 | enum level { 8 | /// Describes messages about the values of variables and the flow of 9 | /// control within a program. 10 | trace, 11 | 12 | /// Describes messages likely to be of interest to someone debugging a 13 | /// program. 14 | debug, 15 | 16 | /// Describes messages likely to be of interest to someone monitoring a 17 | /// program. 18 | info, 19 | 20 | /// Describes messages indicating hazardous situations. 21 | warn, 22 | 23 | /// Describes messages indicating serious errors. 24 | error, 25 | 26 | /// Describes messages indicating fatal errors. 27 | critical, 28 | } 29 | 30 | /// Emit a log message. 31 | /// 32 | /// A log message has a `level` describing what kind of message is being 33 | /// sent, a context, which is an uninterpreted string meant to help 34 | /// consumers group similar messages, and a string containing the message 35 | /// text. 36 | log: func(level: level, context: string, message: string) 37 | } 38 | -------------------------------------------------------------------------------- /test-programs/wasi-tests/src/bin/path_open_create_existing.rs: -------------------------------------------------------------------------------- 1 | use std::{env, process}; 2 | use wasi_tests::{assert_errno, create_file, open_scratch_directory}; 3 | 4 | unsafe fn test_path_open_create_existing(dir_fd: wasi::Fd) { 5 | create_file(dir_fd, "file"); 6 | assert_errno!( 7 | wasi::path_open( 8 | dir_fd, 9 | 0, 10 | "file", 11 | wasi::OFLAGS_CREAT | wasi::OFLAGS_EXCL, 12 | 0, 13 | 0, 14 | 0, 15 | ) 16 | .expect_err("trying to create a file that already exists"), 17 | wasi::ERRNO_EXIST 18 | ); 19 | wasi::path_unlink_file(dir_fd, "file").expect("removing a file"); 20 | } 21 | 22 | fn main() { 23 | let mut args = env::args(); 24 | let prog = args.next().unwrap(); 25 | let arg = if let Some(arg) = args.next() { 26 | arg 27 | } else { 28 | eprintln!("usage: {} ", prog); 29 | process::exit(1); 30 | }; 31 | 32 | // Open scratch directory 33 | let dir_fd = match open_scratch_directory(&arg) { 34 | Ok(dir_fd) => dir_fd, 35 | Err(err) => { 36 | eprintln!("{}", err); 37 | process::exit(1) 38 | } 39 | }; 40 | 41 | // Run the tests. 42 | unsafe { test_path_open_create_existing(dir_fd) } 43 | } 44 | -------------------------------------------------------------------------------- /test-programs/wasi-tests/src/bin/isatty.rs: -------------------------------------------------------------------------------- 1 | use std::{env, process}; 2 | use wasi_tests::open_scratch_directory; 3 | 4 | unsafe fn test_isatty(dir_fd: wasi::Fd) { 5 | // Create a file in the scratch directory and test if it's a tty. 6 | let file_fd = 7 | wasi::path_open(dir_fd, 0, "file", wasi::OFLAGS_CREAT, 0, 0, 0).expect("opening a file"); 8 | assert!( 9 | file_fd > libc::STDERR_FILENO as wasi::Fd, 10 | "file descriptor range check", 11 | ); 12 | assert_eq!( 13 | libc::isatty(file_fd as std::os::raw::c_int), 14 | 0, 15 | "file is a tty" 16 | ); 17 | wasi::fd_close(file_fd).expect("closing a file"); 18 | wasi::path_unlink_file(dir_fd, "file").expect("removing a file"); 19 | } 20 | 21 | fn main() { 22 | let mut args = env::args(); 23 | let prog = args.next().unwrap(); 24 | let arg = if let Some(arg) = args.next() { 25 | arg 26 | } else { 27 | eprintln!("usage: {} ", prog); 28 | process::exit(1); 29 | }; 30 | 31 | // Open scratch directory 32 | let dir_fd = match open_scratch_directory(&arg) { 33 | Ok(dir_fd) => dir_fd, 34 | Err(err) => { 35 | eprintln!("{}", err); 36 | process::exit(1) 37 | } 38 | }; 39 | 40 | // Run the tests. 41 | unsafe { test_isatty(dir_fd) } 42 | } 43 | -------------------------------------------------------------------------------- /test-programs/wasi-tests/src/bin/symlink_loop.rs: -------------------------------------------------------------------------------- 1 | use std::{env, process}; 2 | use wasi_tests::{assert_errno, open_scratch_directory, TESTCONFIG}; 3 | 4 | unsafe fn test_symlink_loop(dir_fd: wasi::Fd) { 5 | if TESTCONFIG.support_dangling_filesystem() { 6 | // Create a self-referencing symlink. 7 | wasi::path_symlink("symlink", dir_fd, "symlink").expect("creating a symlink"); 8 | 9 | // Try to open it. 10 | assert_errno!( 11 | wasi::path_open(dir_fd, 0, "symlink", 0, 0, 0, 0) 12 | .expect_err("opening a self-referencing symlink"), 13 | wasi::ERRNO_LOOP 14 | ); 15 | 16 | // Clean up. 17 | wasi::path_unlink_file(dir_fd, "symlink").expect("removing a file"); 18 | } 19 | } 20 | 21 | fn main() { 22 | let mut args = env::args(); 23 | let prog = args.next().unwrap(); 24 | let arg = if let Some(arg) = args.next() { 25 | arg 26 | } else { 27 | eprintln!("usage: {} ", prog); 28 | process::exit(1); 29 | }; 30 | 31 | // Open scratch directory 32 | let dir_fd = match open_scratch_directory(&arg) { 33 | Ok(dir_fd) => dir_fd, 34 | Err(err) => { 35 | eprintln!("{}", err); 36 | process::exit(1) 37 | } 38 | }; 39 | 40 | // Run the tests. 41 | unsafe { test_symlink_loop(dir_fd) } 42 | } 43 | -------------------------------------------------------------------------------- /wasi/wit/deps/preview/command-extended.wit: -------------------------------------------------------------------------------- 1 | // All of the same imports and exports available in the wasi:cli/command world 2 | // with addition of HTTP proxy related imports: 3 | world command-extended { 4 | import wasi:clocks/wall-clock 5 | import wasi:clocks/monotonic-clock 6 | import wasi:clocks/timezone 7 | import wasi:filesystem/types 8 | import wasi:filesystem/preopens 9 | import wasi:sockets/instance-network 10 | import wasi:sockets/ip-name-lookup 11 | import wasi:sockets/network 12 | import wasi:sockets/tcp-create-socket 13 | import wasi:sockets/tcp 14 | import wasi:sockets/udp-create-socket 15 | import wasi:sockets/udp 16 | import wasi:random/random 17 | import wasi:random/insecure 18 | import wasi:random/insecure-seed 19 | import wasi:poll/poll 20 | import wasi:io/streams 21 | import wasi:cli/environment 22 | import wasi:cli/exit 23 | import wasi:cli/stdin 24 | import wasi:cli/stdout 25 | import wasi:cli/stderr 26 | import wasi:cli/terminal-input 27 | import wasi:cli/terminal-output 28 | import wasi:cli/terminal-stdin 29 | import wasi:cli/terminal-stdout 30 | import wasi:cli/terminal-stderr 31 | 32 | export wasi:cli/run 33 | 34 | // We should replace all others with `include self.command` 35 | // as soon as the unioning of worlds is available: 36 | // https://github.com/WebAssembly/component-model/issues/169 37 | import wasi:logging/logging 38 | import wasi:http/outgoing-handler 39 | } 40 | -------------------------------------------------------------------------------- /wasi-common/src/wasi/command.rs: -------------------------------------------------------------------------------- 1 | use crate::WasiView; 2 | 3 | wasmtime::component::bindgen!({ 4 | path: "../wit", 5 | world: "command", 6 | tracing: true, 7 | async: true, 8 | trappable_error_type: { 9 | "filesystem"::"error-code": Error, 10 | "streams"::"stream-error": Error, 11 | }, 12 | with: { 13 | "filesystem": crate::wasi::filesystem, 14 | "monotonic_clock": crate::wasi::monotonic_clock, 15 | "poll": crate::wasi::poll, 16 | "streams": crate::wasi::streams, 17 | "timezone": crate::wasi::timezone, 18 | "wall_clock": crate::wasi::wall_clock, 19 | "random": crate::wasi::random, 20 | "environment": crate::wasi::environment, 21 | "exit": crate::wasi::exit, 22 | "preopens": crate::wasi::preopens, 23 | }, 24 | }); 25 | 26 | pub fn add_to_linker(l: &mut wasmtime::component::Linker) -> anyhow::Result<()> { 27 | crate::wasi::wall_clock::add_to_linker(l, |t| t)?; 28 | crate::wasi::monotonic_clock::add_to_linker(l, |t| t)?; 29 | crate::wasi::timezone::add_to_linker(l, |t| t)?; 30 | crate::wasi::filesystem::add_to_linker(l, |t| t)?; 31 | crate::wasi::poll::add_to_linker(l, |t| t)?; 32 | crate::wasi::streams::add_to_linker(l, |t| t)?; 33 | crate::wasi::random::add_to_linker(l, |t| t)?; 34 | crate::wasi::exit::add_to_linker(l, |t| t)?; 35 | crate::wasi::environment::add_to_linker(l, |t| t)?; 36 | crate::wasi::preopens::add_to_linker(l, |t| t)?; 37 | Ok(()) 38 | } 39 | -------------------------------------------------------------------------------- /wit/deps/sockets/network.wit: -------------------------------------------------------------------------------- 1 | 2 | default interface network { 3 | /// An opaque resource that represents access to (a subset of) the network. 4 | /// This enables context-based security for networking. 5 | /// There is no need for this to map 1:1 to a physical network interface. 6 | /// 7 | /// FYI, In the future this will be replaced by handle types. 8 | type network = u32 9 | 10 | /// Dispose of the specified `network`, after which it may no longer be used. 11 | /// 12 | /// Note: this function is scheduled to be removed when Resources are natively supported in Wit. 13 | drop-network: func(this: network) 14 | 15 | 16 | 17 | enum error { 18 | unknown, 19 | again, 20 | // TODO ... 21 | } 22 | 23 | enum ip-address-family { 24 | /// Similar to `AF_INET` in POSIX. 25 | ipv4, 26 | 27 | /// Similar to `AF_INET6` in POSIX. 28 | ipv6, 29 | } 30 | 31 | type ipv4-address = tuple 32 | type ipv6-address = tuple 33 | 34 | variant ip-address { 35 | ipv4(ipv4-address), 36 | ipv6(ipv6-address), 37 | } 38 | 39 | record ipv4-socket-address { 40 | port: u16, // sin_port 41 | address: ipv4-address, // sin_addr 42 | } 43 | 44 | record ipv6-socket-address { 45 | port: u16, // sin6_port 46 | flow-info: u32, // sin6_flowinfo 47 | address: ipv6-address, // sin6_addr 48 | scope-id: u32, // sin6_scope_id 49 | } 50 | 51 | variant ip-socket-address { 52 | ipv4(ipv4-socket-address), 53 | ipv6(ipv6-socket-address), 54 | } 55 | 56 | } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "host", 4 | "test-programs", 5 | "test-programs/command-tests", 6 | "test-programs/wasi-tests", 7 | "test-programs/reactor-tests", 8 | "wasi-common", 9 | "wasi-sockets", 10 | "wasi-sockets/sync", 11 | "wasi", 12 | ] 13 | 14 | [workspace.package] 15 | version = "0.0.0" 16 | authors = ["The Wasmtime Project Developers"] 17 | edition = "2021" 18 | 19 | [workspace.dependencies] 20 | anyhow = "1.0.22" 21 | thiserror = "1.0.15" 22 | tracing = "0.1.26" 23 | cap-std = "1.0.12" 24 | cap-rand = "1.0.12" 25 | cap-fs-ext = "1.0.12" 26 | cap-net-ext = "1.0.12" 27 | fs-set-times = "0.19.0" 28 | cap-time-ext = "1.0.0" 29 | bitflags = "1.2" 30 | windows-sys = "0.48.0" 31 | rustix = "0.37.9" 32 | async-trait = "0.1.59" 33 | io-lifetimes = { version = "1.0.0", default-features = false } 34 | wasi-common = { path = "wasi-common" } 35 | wasmtime-wasi-sockets = { path = "wasi-sockets" } 36 | wasmtime-wasi-sockets-sync = { path = "wasi-sockets/sync" } 37 | once_cell = "1.12.0" 38 | system-interface = { version = "0.25.1", features = ["cap_std_impls"] } 39 | wit-bindgen = { version = "0.9.0", default-features = false } 40 | ipnet = "2" # TODO: Move to cap_std::ipnet instead, when that's released. 41 | wasmtime = { git = "https://github.com/bytecodealliance/wasmtime", rev = "299131ae2d6655c49138bfab2c4469650763ef3b", features = [ 42 | "component-model", 43 | ] } 44 | wiggle = { git = "https://github.com/bytecodealliance/wasmtime", rev = "299131ae2d6655c49138bfab2c4469650763ef3b" } 45 | -------------------------------------------------------------------------------- /test-programs/reactor-tests/wit/deps/sockets/network.wit: -------------------------------------------------------------------------------- 1 | 2 | default interface network { 3 | /// An opaque resource that represents access to (a subset of) the network. 4 | /// This enables context-based security for networking. 5 | /// There is no need for this to map 1:1 to a physical network interface. 6 | /// 7 | /// FYI, In the future this will be replaced by handle types. 8 | type network = u32 9 | 10 | /// Dispose of the specified `network`, after which it may no longer be used. 11 | /// 12 | /// Note: this function is scheduled to be removed when Resources are natively supported in Wit. 13 | drop-network: func(this: network) 14 | 15 | 16 | 17 | enum error { 18 | unknown, 19 | again, 20 | // TODO ... 21 | } 22 | 23 | enum ip-address-family { 24 | /// Similar to `AF_INET` in POSIX. 25 | ipv4, 26 | 27 | /// Similar to `AF_INET6` in POSIX. 28 | ipv6, 29 | } 30 | 31 | type ipv4-address = tuple 32 | type ipv6-address = tuple 33 | 34 | variant ip-address { 35 | ipv4(ipv4-address), 36 | ipv6(ipv6-address), 37 | } 38 | 39 | record ipv4-socket-address { 40 | port: u16, // sin_port 41 | address: ipv4-address, // sin_addr 42 | } 43 | 44 | record ipv6-socket-address { 45 | port: u16, // sin6_port 46 | flow-info: u32, // sin6_flowinfo 47 | address: ipv6-address, // sin6_addr 48 | scope-id: u32, // sin6_scope_id 49 | } 50 | 51 | variant ip-socket-address { 52 | ipv4(ipv4-socket-address), 53 | ipv6(ipv6-socket-address), 54 | } 55 | 56 | } -------------------------------------------------------------------------------- /crates/wasi-preview1-component-adapter/wit/deps/sockets/network.wit: -------------------------------------------------------------------------------- 1 | 2 | default interface network { 3 | /// An opaque resource that represents access to (a subset of) the network. 4 | /// This enables context-based security for networking. 5 | /// There is no need for this to map 1:1 to a physical network interface. 6 | /// 7 | /// FYI, In the future this will be replaced by handle types. 8 | type network = u32 9 | 10 | /// Dispose of the specified `network`, after which it may no longer be used. 11 | /// 12 | /// Note: this function is scheduled to be removed when Resources are natively supported in Wit. 13 | drop-network: func(this: network) 14 | 15 | 16 | 17 | enum error { 18 | unknown, 19 | again, 20 | // TODO ... 21 | } 22 | 23 | enum ip-address-family { 24 | /// Similar to `AF_INET` in POSIX. 25 | ipv4, 26 | 27 | /// Similar to `AF_INET6` in POSIX. 28 | ipv6, 29 | } 30 | 31 | type ipv4-address = tuple 32 | type ipv6-address = tuple 33 | 34 | variant ip-address { 35 | ipv4(ipv4-address), 36 | ipv6(ipv6-address), 37 | } 38 | 39 | record ipv4-socket-address { 40 | port: u16, // sin_port 41 | address: ipv4-address, // sin_addr 42 | } 43 | 44 | record ipv6-socket-address { 45 | port: u16, // sin6_port 46 | flow-info: u32, // sin6_flowinfo 47 | address: ipv6-address, // sin6_addr 48 | scope-id: u32, // sin6_scope_id 49 | } 50 | 51 | variant ip-socket-address { 52 | ipv4(ipv4-socket-address), 53 | ipv6(ipv6-socket-address), 54 | } 55 | 56 | } -------------------------------------------------------------------------------- /wasi/wit/deps/sockets/udp-create-socket.wit: -------------------------------------------------------------------------------- 1 | 2 | interface udp-create-socket { 3 | use network.{network, error-code, ip-address-family} 4 | use udp.{udp-socket} 5 | 6 | /// Create a new UDP socket. 7 | /// 8 | /// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX. 9 | /// 10 | /// This function does not require a network capability handle. This is considered to be safe because 11 | /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`connect` is called, 12 | /// the socket is effectively an in-memory configuration object, unable to communicate with the outside world. 13 | /// 14 | /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. 15 | /// 16 | /// # Typical errors 17 | /// - `not-supported`: The host does not support UDP sockets. (EOPNOTSUPP) 18 | /// - `address-family-not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) 19 | /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) 20 | /// 21 | /// # References: 22 | /// - 23 | /// - 24 | /// - 25 | /// - 26 | create-udp-socket: func(address-family: ip-address-family) -> result 27 | } 28 | -------------------------------------------------------------------------------- /test-programs/wasi-tests/src/bin/close_preopen.rs: -------------------------------------------------------------------------------- 1 | use std::{env, process}; 2 | use wasi_tests::{assert_errno, open_scratch_directory}; 3 | 4 | unsafe fn test_close_preopen(dir_fd: wasi::Fd) { 5 | let pre_fd: wasi::Fd = (libc::STDERR_FILENO + 1) as wasi::Fd; 6 | 7 | assert!(dir_fd > pre_fd, "dir_fd number"); 8 | 9 | // Try to close a preopened directory handle. 10 | wasi::fd_close(pre_fd).expect("closing a preopened file descriptor"); 11 | 12 | // Ensure that dir_fd is still open. 13 | let dir_fdstat = wasi::fd_fdstat_get(dir_fd).expect("failed fd_fdstat_get"); 14 | assert_eq!( 15 | dir_fdstat.fs_filetype, 16 | wasi::FILETYPE_DIRECTORY, 17 | "expected the scratch directory to be a directory", 18 | ); 19 | 20 | // Ensure that pre_fd is closed. 21 | assert_errno!( 22 | wasi::fd_fdstat_get(pre_fd).expect_err("failed fd_fdstat_get"), 23 | wasi::ERRNO_BADF 24 | ); 25 | } 26 | 27 | fn main() { 28 | let mut args = env::args(); 29 | let prog = args.next().unwrap(); 30 | let arg = if let Some(arg) = args.next() { 31 | arg 32 | } else { 33 | eprintln!("usage: {} ", prog); 34 | process::exit(1); 35 | }; 36 | 37 | // Open scratch directory 38 | let dir_fd = match open_scratch_directory(&arg) { 39 | Ok(dir_fd) => dir_fd, 40 | Err(err) => { 41 | eprintln!("{}", err); 42 | process::exit(1) 43 | } 44 | }; 45 | 46 | // Run the tests. 47 | unsafe { test_close_preopen(dir_fd) } 48 | } 49 | -------------------------------------------------------------------------------- /wasi/wit/deps/sockets/tcp-create-socket.wit: -------------------------------------------------------------------------------- 1 | 2 | interface tcp-create-socket { 3 | use network.{network, error-code, ip-address-family} 4 | use tcp.{tcp-socket} 5 | 6 | /// Create a new TCP socket. 7 | /// 8 | /// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX. 9 | /// 10 | /// This function does not require a network capability handle. This is considered to be safe because 11 | /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`listen`/`connect` 12 | /// is called, the socket is effectively an in-memory configuration object, unable to communicate with the outside world. 13 | /// 14 | /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. 15 | /// 16 | /// # Typical errors 17 | /// - `not-supported`: The host does not support TCP sockets. (EOPNOTSUPP) 18 | /// - `address-family-not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) 19 | /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) 20 | /// 21 | /// # References 22 | /// - 23 | /// - 24 | /// - 25 | /// - 26 | create-tcp-socket: func(address-family: ip-address-family) -> result 27 | } 28 | -------------------------------------------------------------------------------- /wasi/wit/deps/http/proxy.wit: -------------------------------------------------------------------------------- 1 | package wasi:http 2 | 3 | // The `wasi:http/proxy` world captures a widely-implementable intersection of 4 | // hosts that includes HTTP forward and reverse proxies. Components targeting 5 | // this world may concurrently stream in and out any number of incoming and 6 | // outgoing HTTP requests. 7 | world proxy { 8 | // HTTP proxies have access to time and randomness. 9 | import wasi:clocks/wall-clock 10 | import wasi:clocks/monotonic-clock 11 | import wasi:clocks/timezone 12 | import wasi:random/random 13 | 14 | // Proxies have standard output and error streams which are expected to 15 | // terminate in a developer-facing console provided by the host. 16 | import wasi:cli/stdout 17 | import wasi:cli/stderr 18 | 19 | // TODO: this is a temporary workaround until component tooling is able to 20 | // gracefully handle the absence of stdin. Hosts must return an eof stream 21 | // for this import, which is what wasi-libc + tooling will do automatically 22 | // when this import is properly removed. 23 | import wasi:cli/stdin 24 | 25 | // This is the default handler to use when user code simply wants to make an 26 | // HTTP request (e.g., via `fetch()`). 27 | import outgoing-handler 28 | 29 | // The host delivers incoming HTTP requests to a component by calling the 30 | // `handle` function of this exported interface. A host may arbitrarily reuse 31 | // or not reuse component instance when delivering incoming HTTP requests and 32 | // thus a component must be able to handle 0..N calls to `handle`. 33 | export incoming-handler 34 | } 35 | -------------------------------------------------------------------------------- /wasi-sockets/src/ip_name_lookup.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_variables)] 2 | 3 | use crate::{ 4 | wasi::ip_name_lookup::{self, ResolveAddressStream}, 5 | wasi::network::{Error, IpAddress, IpAddressFamily, Network}, 6 | WasiSocketsView, 7 | }; 8 | use wasi_common::wasi::poll::Pollable; 9 | 10 | #[async_trait::async_trait] 11 | impl ip_name_lookup::Host for T { 12 | async fn resolve_addresses( 13 | &mut self, 14 | network: Network, 15 | name: String, 16 | address_family: Option, 17 | include_unavailable: bool, 18 | ) -> anyhow::Result> { 19 | todo!() 20 | } 21 | 22 | async fn resolve_next_address( 23 | &mut self, 24 | stream: ResolveAddressStream, 25 | ) -> anyhow::Result, Error>> { 26 | todo!() 27 | } 28 | 29 | async fn drop_resolve_address_stream( 30 | &mut self, 31 | stream: ResolveAddressStream, 32 | ) -> anyhow::Result<()> { 33 | todo!() 34 | } 35 | 36 | async fn non_blocking( 37 | &mut self, 38 | stream: ResolveAddressStream, 39 | ) -> anyhow::Result> { 40 | todo!() 41 | } 42 | 43 | async fn set_non_blocking( 44 | &mut self, 45 | stream: ResolveAddressStream, 46 | value: bool, 47 | ) -> anyhow::Result> { 48 | todo!() 49 | } 50 | 51 | async fn subscribe(&mut self, stream: ResolveAddressStream) -> anyhow::Result { 52 | todo!() 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /test-programs/wasi-tests/src/bin/directory_seek.rs: -------------------------------------------------------------------------------- 1 | use std::{env, process}; 2 | use wasi_tests::{assert_errno, open_scratch_directory}; 3 | 4 | unsafe fn test_directory_seek(dir_fd: wasi::Fd) { 5 | // Create a directory in the scratch directory. 6 | wasi::path_create_directory(dir_fd, "dir").expect("failed to make directory"); 7 | 8 | // Open the directory and attempt to request rights for seeking. 9 | let fd = wasi::path_open(dir_fd, 0, "dir", wasi::OFLAGS_DIRECTORY, 0, 0, 0) 10 | .expect("failed to open file"); 11 | assert!( 12 | fd > libc::STDERR_FILENO as wasi::Fd, 13 | "file descriptor range check", 14 | ); 15 | 16 | // Attempt to seek. 17 | assert_errno!( 18 | wasi::fd_seek(fd, 0, wasi::WHENCE_CUR).expect_err("seek on a directory"), 19 | wasi::ERRNO_BADF 20 | ); 21 | 22 | // Clean up. 23 | wasi::fd_close(fd).expect("failed to close fd"); 24 | wasi::path_remove_directory(dir_fd, "dir").expect("failed to remove dir"); 25 | } 26 | 27 | fn main() { 28 | let mut args = env::args(); 29 | let prog = args.next().unwrap(); 30 | let arg = if let Some(arg) = args.next() { 31 | arg 32 | } else { 33 | eprintln!("usage: {} ", prog); 34 | process::exit(1); 35 | }; 36 | 37 | // Open scratch directory 38 | let dir_fd = match open_scratch_directory(&arg) { 39 | Ok(dir_fd) => dir_fd, 40 | Err(err) => { 41 | eprintln!("{}", err); 42 | process::exit(1) 43 | } 44 | }; 45 | 46 | // Run the tests. 47 | unsafe { test_directory_seek(dir_fd) } 48 | } 49 | -------------------------------------------------------------------------------- /wasi-sockets/src/udp_socket.rs: -------------------------------------------------------------------------------- 1 | //! UDP sockets. 2 | 3 | use crate::Error; 4 | use bitflags::bitflags; 5 | use std::any::Any; 6 | 7 | /// A UDP socket. 8 | #[async_trait::async_trait] 9 | pub trait WasiUdpSocket: Send + Sync { 10 | fn as_any(&self) -> &dyn Any; 11 | 12 | async fn sock_recv<'a>( 13 | &mut self, 14 | ri_data: &mut [std::io::IoSliceMut<'a>], 15 | ri_flags: RiFlags, 16 | ) -> Result<(u64, RoFlags), Error>; 17 | 18 | async fn sock_send<'a>(&mut self, si_data: &[std::io::IoSlice<'a>]) -> Result; 19 | 20 | fn set_nonblocking(&mut self, flag: bool) -> Result<(), Error>; 21 | 22 | async fn readable(&self) -> Result<(), Error>; 23 | 24 | async fn writable(&self) -> Result<(), Error>; 25 | } 26 | 27 | bitflags! { 28 | pub struct RoFlags: u32 { 29 | const RECV_DATA_TRUNCATED = 0b1; 30 | } 31 | } 32 | 33 | bitflags! { 34 | pub struct RiFlags: u32 { 35 | const RECV_PEEK = 0b1; 36 | const RECV_WAITALL = 0b10; 37 | } 38 | } 39 | 40 | pub trait TableUdpSocketExt { 41 | fn get_udp_socket(&self, fd: u32) -> Result<&dyn WasiUdpSocket, Error>; 42 | fn get_udp_socket_mut(&mut self, fd: u32) -> Result<&mut Box, Error>; 43 | } 44 | impl TableUdpSocketExt for wasi_common::Table { 45 | fn get_udp_socket(&self, fd: u32) -> Result<&dyn WasiUdpSocket, Error> { 46 | Ok(self.get::>(fd).map(|f| f.as_ref())?) 47 | } 48 | fn get_udp_socket_mut(&mut self, fd: u32) -> Result<&mut Box, Error> { 49 | Ok(self.get_mut::>(fd)?) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /test-programs/wasi-tests/src/bin/path_rename_dir_trailing_slashes.rs: -------------------------------------------------------------------------------- 1 | use std::{env, process}; 2 | use wasi_tests::open_scratch_directory; 3 | 4 | unsafe fn test_path_rename_trailing_slashes(dir_fd: wasi::Fd) { 5 | // Test renaming a directory with a trailing slash in the name. 6 | wasi::path_create_directory(dir_fd, "source").expect("creating a directory"); 7 | wasi::path_rename(dir_fd, "source/", dir_fd, "target") 8 | .expect("renaming a directory with a trailing slash in the source name"); 9 | wasi::path_rename(dir_fd, "target", dir_fd, "source/") 10 | .expect("renaming a directory with a trailing slash in the destination name"); 11 | wasi::path_rename(dir_fd, "source/", dir_fd, "target/") 12 | .expect("renaming a directory with a trailing slash in the source and destination names"); 13 | wasi::path_rename(dir_fd, "target", dir_fd, "source") 14 | .expect("renaming a directory with no trailing slashes at all should work"); 15 | wasi::path_remove_directory(dir_fd, "source").expect("removing the directory"); 16 | } 17 | 18 | fn main() { 19 | let mut args = env::args(); 20 | let prog = args.next().unwrap(); 21 | let arg = if let Some(arg) = args.next() { 22 | arg 23 | } else { 24 | eprintln!("usage: {} ", prog); 25 | process::exit(1); 26 | }; 27 | 28 | // Open scratch directory 29 | let dir_fd = match open_scratch_directory(&arg) { 30 | Ok(dir_fd) => dir_fd, 31 | Err(err) => { 32 | eprintln!("{}", err); 33 | process::exit(1) 34 | } 35 | }; 36 | 37 | // Run the tests. 38 | unsafe { test_path_rename_trailing_slashes(dir_fd) } 39 | } 40 | -------------------------------------------------------------------------------- /crates/wasi-preview1-component-adapter/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wasi-preview1-component-adapter" 3 | version.workspace = true 4 | authors.workspace = true 5 | edition.workspace = true 6 | 7 | [dependencies] 8 | wasi = { version = "0.11.0", default-features = false } 9 | wit-bindgen = { version = "0.4.0", default-features = false, features = ["macros"] } 10 | byte-array-literals = { workspace = true } 11 | 12 | [build-dependencies] 13 | wasm-encoder = "0.25" 14 | object = { version = "0.30.0", default-features = false, features = ["archive"] } 15 | 16 | [lib] 17 | crate-type = ["cdylib"] 18 | 19 | [profile.release] 20 | # Omit any unwinding support. This is currently the default for wasm, though 21 | # that could theoretically change in the future. 22 | panic = 'abort' 23 | opt-level = 's' 24 | strip = 'debuginfo' 25 | 26 | # Make dev look like a release build since this adapter module won't work with 27 | # a debug build that uses data segments and such. 28 | [profile.dev] 29 | panic = 'abort' 30 | incremental = false 31 | opt-level = 's' 32 | 33 | # Omit assertions, which include failure messages which require string 34 | # initializers. 35 | debug-assertions = false 36 | 37 | # Omit integer overflow checks, which include failure messages which require 38 | # string initializers. 39 | overflow-checks = false 40 | 41 | [features] 42 | default = ["reactor"] 43 | reactor = [] 44 | command = [] 45 | 46 | [workspace] 47 | members = [ 48 | "verify", 49 | "byte-array-literals" 50 | ] 51 | 52 | [workspace.package] 53 | version = "0.0.1" 54 | edition = "2021" 55 | authors = ["The Wasmtime Project Developers"] 56 | 57 | [workspace.dependencies] 58 | byte-array-literals = { path = "byte-array-literals" } 59 | -------------------------------------------------------------------------------- /test-programs/wasi-tests/src/bin/overwrite_preopen.rs: -------------------------------------------------------------------------------- 1 | use std::{env, process}; 2 | use wasi_tests::{assert_errno, open_scratch_directory}; 3 | 4 | unsafe fn test_overwrite_preopen(dir_fd: wasi::Fd) { 5 | let pre_fd: wasi::Fd = (libc::STDERR_FILENO + 1) as wasi::Fd; 6 | 7 | assert!(dir_fd > pre_fd, "dir_fd number"); 8 | 9 | let old_dir_filestat = wasi::fd_filestat_get(dir_fd).expect("failed fd_filestat_get"); 10 | 11 | // Try to renumber over a preopened directory handle. 12 | wasi::fd_renumber(dir_fd, pre_fd).expect("renumbering over a preopened file descriptor"); 13 | 14 | // Ensure that pre_fd is still open. 15 | let new_dir_filestat = wasi::fd_filestat_get(pre_fd).expect("failed fd_filestat_get"); 16 | 17 | // Ensure that we renumbered. 18 | assert_eq!(old_dir_filestat.dev, new_dir_filestat.dev); 19 | assert_eq!(old_dir_filestat.ino, new_dir_filestat.ino); 20 | 21 | // Ensure that dir_fd is closed. 22 | assert_errno!( 23 | wasi::fd_fdstat_get(dir_fd).expect_err("failed fd_fdstat_get"), 24 | wasi::ERRNO_BADF 25 | ); 26 | } 27 | 28 | fn main() { 29 | let mut args = env::args(); 30 | let prog = args.next().unwrap(); 31 | let arg = if let Some(arg) = args.next() { 32 | arg 33 | } else { 34 | eprintln!("usage: {} ", prog); 35 | process::exit(1); 36 | }; 37 | 38 | // Open scratch directory 39 | let dir_fd = match open_scratch_directory(&arg) { 40 | Ok(dir_fd) => dir_fd, 41 | Err(err) => { 42 | eprintln!("{}", err); 43 | process::exit(1) 44 | } 45 | }; 46 | 47 | // Run the tests. 48 | unsafe { test_overwrite_preopen(dir_fd) } 49 | } 50 | -------------------------------------------------------------------------------- /test-programs/wasi-tests/src/bin/remove_nonempty_directory.rs: -------------------------------------------------------------------------------- 1 | use std::{env, process}; 2 | use wasi_tests::{assert_errno, open_scratch_directory}; 3 | 4 | unsafe fn test_remove_nonempty_directory(dir_fd: wasi::Fd) { 5 | // Create a directory in the scratch directory. 6 | wasi::path_create_directory(dir_fd, "dir").expect("creating a directory"); 7 | 8 | // Create a directory in the directory we just created. 9 | wasi::path_create_directory(dir_fd, "dir/nested").expect("creating a subdirectory"); 10 | 11 | // Test that attempting to unlink the first directory returns the expected error code. 12 | assert_errno!( 13 | wasi::path_remove_directory(dir_fd, "dir") 14 | .expect_err("remove_directory on a directory should return ENOTEMPTY"), 15 | wasi::ERRNO_NOTEMPTY 16 | ); 17 | 18 | // Removing the directories. 19 | wasi::path_remove_directory(dir_fd, "dir/nested") 20 | .expect("remove_directory on a nested directory should succeed"); 21 | wasi::path_remove_directory(dir_fd, "dir").expect("removing a directory"); 22 | } 23 | 24 | fn main() { 25 | let mut args = env::args(); 26 | let prog = args.next().unwrap(); 27 | let arg = if let Some(arg) = args.next() { 28 | arg 29 | } else { 30 | eprintln!("usage: {} ", prog); 31 | process::exit(1); 32 | }; 33 | 34 | // Open scratch directory 35 | let dir_fd = match open_scratch_directory(&arg) { 36 | Ok(dir_fd) => dir_fd, 37 | Err(err) => { 38 | eprintln!("{}", err); 39 | process::exit(1) 40 | } 41 | }; 42 | 43 | // Run the tests. 44 | unsafe { test_remove_nonempty_directory(dir_fd) } 45 | } 46 | -------------------------------------------------------------------------------- /test-programs/wasi-tests/src/bin/dangling_symlink.rs: -------------------------------------------------------------------------------- 1 | use std::{env, process}; 2 | use wasi_tests::{assert_errno, open_scratch_directory, TESTCONFIG}; 3 | 4 | unsafe fn test_dangling_symlink(dir_fd: wasi::Fd) { 5 | if TESTCONFIG.support_dangling_filesystem() { 6 | // First create a dangling symlink. 7 | wasi::path_symlink("target", dir_fd, "symlink").expect("creating a symlink"); 8 | 9 | // Try to open it as a directory with O_NOFOLLOW. 10 | assert_errno!( 11 | wasi::path_open(dir_fd, 0, "symlink", wasi::OFLAGS_DIRECTORY, 0, 0, 0) 12 | .expect_err("opening a dangling symlink as a directory"), 13 | wasi::ERRNO_NOTDIR, 14 | wasi::ERRNO_LOOP 15 | ); 16 | 17 | // Try to open it as a file with O_NOFOLLOW. 18 | assert_errno!( 19 | wasi::path_open(dir_fd, 0, "symlink", 0, 0, 0, 0) 20 | .expect_err("opening a dangling symlink as a file"), 21 | wasi::ERRNO_LOOP 22 | ); 23 | 24 | // Clean up. 25 | wasi::path_unlink_file(dir_fd, "symlink").expect("failed to remove file"); 26 | } 27 | } 28 | 29 | fn main() { 30 | let mut args = env::args(); 31 | let prog = args.next().unwrap(); 32 | let arg = if let Some(arg) = args.next() { 33 | arg 34 | } else { 35 | eprintln!("usage: {} ", prog); 36 | process::exit(1); 37 | }; 38 | 39 | // Open scratch directory 40 | let dir_fd = match open_scratch_directory(&arg) { 41 | Ok(dir_fd) => dir_fd, 42 | Err(err) => { 43 | eprintln!("{}", err); 44 | process::exit(1) 45 | } 46 | }; 47 | 48 | // Run the tests. 49 | unsafe { test_dangling_symlink(dir_fd) } 50 | } 51 | -------------------------------------------------------------------------------- /wit/deps/clocks/wall-clock.wit: -------------------------------------------------------------------------------- 1 | /// WASI Wall Clock is a clock API intended to let users query the current 2 | /// time. The name "wall" makes an analogy to a "clock on the wall", which 3 | /// is not necessarily monotonic as it may be reset. 4 | /// 5 | /// It is intended to be portable at least between Unix-family platforms and 6 | /// Windows. 7 | /// 8 | /// A wall clock is a clock which measures the date and time according to 9 | /// some external reference. 10 | /// 11 | /// External references may be reset, so this clock is not necessarily 12 | /// monotonic, making it unsuitable for measuring elapsed time. 13 | /// 14 | /// It is intended for reporting the current date and time for humans. 15 | default interface wall-clock { 16 | /// A time and date in seconds plus nanoseconds. 17 | record datetime { 18 | seconds: u64, 19 | nanoseconds: u32, 20 | } 21 | 22 | /// Read the current value of the clock. 23 | /// 24 | /// This clock is not monotonic, therefore calling this function repeatedly 25 | /// will not necessarily produce a sequence of non-decreasing values. 26 | /// 27 | /// The returned timestamps represent the number of seconds since 28 | /// 1970-01-01T00:00:00Z, also known as [POSIX's Seconds Since the Epoch], 29 | /// also known as [Unix Time]. 30 | /// 31 | /// The nanoseconds field of the output is always less than 1000000000. 32 | /// 33 | /// [POSIX's Seconds Since the Epoch]: https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_16 34 | /// [Unix Time]: https://en.wikipedia.org/wiki/Unix_time 35 | now: func() -> datetime 36 | 37 | /// Query the resolution of the clock. 38 | /// 39 | /// The nanoseconds field of the output is always less than 1000000000. 40 | resolution: func() -> datetime 41 | } 42 | -------------------------------------------------------------------------------- /wasi-common/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wasi-common" 3 | version.workspace = true 4 | authors.workspace = true 5 | description = "WASI implementation in Rust" 6 | license = "Apache-2.0 WITH LLVM-exception" 7 | categories = ["wasm"] 8 | keywords = ["webassembly", "wasm"] 9 | repository = "https://github.com/bytecodealliance/wasmtime" 10 | readme = "README.md" 11 | edition.workspace = true 12 | include = ["src/**/*", "WASI/phases/**/*", "README.md", "LICENSE"] 13 | publish = false 14 | 15 | [dependencies] 16 | anyhow = { workspace = true } 17 | thiserror = { workspace = true } 18 | tracing = { workspace = true } 19 | cap-std = { workspace = true } 20 | cap-rand = { workspace = true } 21 | cap-fs-ext = { workspace = true } 22 | cap-time-ext = { workspace = true } 23 | fs-set-times = { workspace = true } 24 | bitflags = { workspace = true } 25 | async-trait = { workspace = true } 26 | system-interface = { workspace = true } 27 | rustix = { workspace = true, features = ["net"] } 28 | wasmtime = { workspace = true } 29 | wiggle = { workspace = true, optional = true } 30 | 31 | [target.'cfg(unix)'.dependencies] 32 | rustix = { workspace = true, features = ["fs"] } 33 | 34 | [target.'cfg(windows)'.dependencies] 35 | io-extras = "0.17.1" 36 | 37 | [target.'cfg(windows)'.dependencies.windows-sys] 38 | workspace = true 39 | features = [ 40 | "Win32_Foundation", 41 | "Win32_Networking_WinSock", 42 | ] 43 | 44 | [badges] 45 | maintenance = { status = "actively-developed" } 46 | 47 | [features] 48 | default = ["trace_log", "preview1"] 49 | # This feature enables the `tracing` logs in the calls to target the `log` 50 | # ecosystem of backends (e.g. `env_logger`. Disable this if you want to use 51 | # `tracing-subscriber`. 52 | trace_log = [ "tracing/log" ] 53 | 54 | # This feature enables support for wasi preview 1 55 | preview1 = [ "dep:wiggle" ] 56 | -------------------------------------------------------------------------------- /test-programs/reactor-tests/wit/deps/clocks/wall-clock.wit: -------------------------------------------------------------------------------- 1 | /// WASI Wall Clock is a clock API intended to let users query the current 2 | /// time. The name "wall" makes an analogy to a "clock on the wall", which 3 | /// is not necessarily monotonic as it may be reset. 4 | /// 5 | /// It is intended to be portable at least between Unix-family platforms and 6 | /// Windows. 7 | /// 8 | /// A wall clock is a clock which measures the date and time according to 9 | /// some external reference. 10 | /// 11 | /// External references may be reset, so this clock is not necessarily 12 | /// monotonic, making it unsuitable for measuring elapsed time. 13 | /// 14 | /// It is intended for reporting the current date and time for humans. 15 | default interface wall-clock { 16 | /// A time and date in seconds plus nanoseconds. 17 | record datetime { 18 | seconds: u64, 19 | nanoseconds: u32, 20 | } 21 | 22 | /// Read the current value of the clock. 23 | /// 24 | /// This clock is not monotonic, therefore calling this function repeatedly 25 | /// will not necessarily produce a sequence of non-decreasing values. 26 | /// 27 | /// The returned timestamps represent the number of seconds since 28 | /// 1970-01-01T00:00:00Z, also known as [POSIX's Seconds Since the Epoch], 29 | /// also known as [Unix Time]. 30 | /// 31 | /// The nanoseconds field of the output is always less than 1000000000. 32 | /// 33 | /// [POSIX's Seconds Since the Epoch]: https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_16 34 | /// [Unix Time]: https://en.wikipedia.org/wiki/Unix_time 35 | now: func() -> datetime 36 | 37 | /// Query the resolution of the clock. 38 | /// 39 | /// The nanoseconds field of the output is always less than 1000000000. 40 | resolution: func() -> datetime 41 | } 42 | -------------------------------------------------------------------------------- /wasi/wit/deps/clocks/wall-clock.wit: -------------------------------------------------------------------------------- 1 | package wasi:clocks 2 | 3 | /// WASI Wall Clock is a clock API intended to let users query the current 4 | /// time. The name "wall" makes an analogy to a "clock on the wall", which 5 | /// is not necessarily monotonic as it may be reset. 6 | /// 7 | /// It is intended to be portable at least between Unix-family platforms and 8 | /// Windows. 9 | /// 10 | /// A wall clock is a clock which measures the date and time according to 11 | /// some external reference. 12 | /// 13 | /// External references may be reset, so this clock is not necessarily 14 | /// monotonic, making it unsuitable for measuring elapsed time. 15 | /// 16 | /// It is intended for reporting the current date and time for humans. 17 | interface wall-clock { 18 | /// A time and date in seconds plus nanoseconds. 19 | record datetime { 20 | seconds: u64, 21 | nanoseconds: u32, 22 | } 23 | 24 | /// Read the current value of the clock. 25 | /// 26 | /// This clock is not monotonic, therefore calling this function repeatedly 27 | /// will not necessarily produce a sequence of non-decreasing values. 28 | /// 29 | /// The returned timestamps represent the number of seconds since 30 | /// 1970-01-01T00:00:00Z, also known as [POSIX's Seconds Since the Epoch], 31 | /// also known as [Unix Time]. 32 | /// 33 | /// The nanoseconds field of the output is always less than 1000000000. 34 | /// 35 | /// [POSIX's Seconds Since the Epoch]: https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_16 36 | /// [Unix Time]: https://en.wikipedia.org/wiki/Unix_time 37 | now: func() -> datetime 38 | 39 | /// Query the resolution of the clock. 40 | /// 41 | /// The nanoseconds field of the output is always less than 1000000000. 42 | resolution: func() -> datetime 43 | } 44 | -------------------------------------------------------------------------------- /crates/wasi-preview1-component-adapter/wit/deps/clocks/wall-clock.wit: -------------------------------------------------------------------------------- 1 | /// WASI Wall Clock is a clock API intended to let users query the current 2 | /// time. The name "wall" makes an analogy to a "clock on the wall", which 3 | /// is not necessarily monotonic as it may be reset. 4 | /// 5 | /// It is intended to be portable at least between Unix-family platforms and 6 | /// Windows. 7 | /// 8 | /// A wall clock is a clock which measures the date and time according to 9 | /// some external reference. 10 | /// 11 | /// External references may be reset, so this clock is not necessarily 12 | /// monotonic, making it unsuitable for measuring elapsed time. 13 | /// 14 | /// It is intended for reporting the current date and time for humans. 15 | default interface wall-clock { 16 | /// A time and date in seconds plus nanoseconds. 17 | record datetime { 18 | seconds: u64, 19 | nanoseconds: u32, 20 | } 21 | 22 | /// Read the current value of the clock. 23 | /// 24 | /// This clock is not monotonic, therefore calling this function repeatedly 25 | /// will not necessarily produce a sequence of non-decreasing values. 26 | /// 27 | /// The returned timestamps represent the number of seconds since 28 | /// 1970-01-01T00:00:00Z, also known as [POSIX's Seconds Since the Epoch], 29 | /// also known as [Unix Time]. 30 | /// 31 | /// The nanoseconds field of the output is always less than 1000000000. 32 | /// 33 | /// [POSIX's Seconds Since the Epoch]: https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_16 34 | /// [Unix Time]: https://en.wikipedia.org/wiki/Unix_time 35 | now: func() -> datetime 36 | 37 | /// Query the resolution of the clock. 38 | /// 39 | /// The nanoseconds field of the output is always less than 1000000000. 40 | resolution: func() -> datetime 41 | } 42 | -------------------------------------------------------------------------------- /wasi/wit/deps/poll/poll.wit: -------------------------------------------------------------------------------- 1 | package wasi:poll 2 | 3 | /// A poll API intended to let users wait for I/O events on multiple handles 4 | /// at once. 5 | interface poll { 6 | /// A "pollable" handle. 7 | /// 8 | /// This is conceptually represents a `stream<_, _>`, or in other words, 9 | /// a stream that one can wait on, repeatedly, but which does not itself 10 | /// produce any data. It's temporary scaffolding until component-model's 11 | /// async features are ready. 12 | /// 13 | /// And at present, it is a `u32` instead of being an actual handle, until 14 | /// the wit-bindgen implementation of handles and resources is ready. 15 | /// 16 | /// `pollable` lifetimes are not automatically managed. Users must ensure 17 | /// that they do not outlive the resource they reference. 18 | /// 19 | /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). 20 | type pollable = u32 21 | 22 | /// Dispose of the specified `pollable`, after which it may no longer 23 | /// be used. 24 | drop-pollable: func(this: pollable) 25 | 26 | /// Poll for completion on a set of pollables. 27 | /// 28 | /// The "oneoff" in the name refers to the fact that this function must do a 29 | /// linear scan through the entire list of subscriptions, which may be 30 | /// inefficient if the number is large and the same subscriptions are used 31 | /// many times. In the future, this is expected to be obsoleted by the 32 | /// component model async proposal, which will include a scalable waiting 33 | /// facility. 34 | /// 35 | /// The result list is the same length as the argument 36 | /// list, and indicates the readiness of each corresponding 37 | /// element in that / list, with true indicating ready. 38 | poll-oneoff: func(in: list) -> list 39 | } 40 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | Building secure foundations for software development is at the core of what we do in the Bytecode Alliance. Contributions of external security researchers are a vital part of that. 4 | 5 | ## Scope 6 | 7 | If you believe you've found a security issue in any website, service, or software owned or operated by the Bytecode Alliance, we encourage you to notify us. 8 | 9 | ## How to Submit a Report 10 | 11 | To submit a vulnerability report to the Bytecode Alliance, please contact us at [security@bytecodealliance.org](mailto:security@bytecodealliance.org). Your submission will be reviewed and validated by a member of our security team. 12 | 13 | ## Safe Harbor 14 | 15 | The Bytecode Alliance supports safe harbor for security researchers who: 16 | 17 | * Make a good faith effort to avoid privacy violations, destruction of data, and interruption or degradation of our services. 18 | * Only interact with accounts you own or with explicit permission of the account holder. If you do encounter Personally Identifiable Information (PII) contact us immediately, do not proceed with access, and immediately purge any local information. 19 | * Provide us with a reasonable amount of time to resolve vulnerabilities prior to any disclosure to the public or a third-party. 20 | 21 | We will consider activities conducted consistent with this policy to constitute "authorized" conduct and will not pursue civil action or initiate a complaint to law enforcement. We will help to the extent we can if legal action is initiated by a third party against you. 22 | 23 | Please submit a report to us before engaging in conduct that may be inconsistent with or unaddressed by this policy. 24 | 25 | ## Preferences 26 | 27 | * Please provide detailed reports with reproducible steps and a clearly defined impact. 28 | * Submit one vulnerability per report. 29 | * Social engineering (e.g. phishing, vishing, smishing) is prohibited. 30 | -------------------------------------------------------------------------------- /test-programs/wasi-tests/src/bin/file_allocate.rs: -------------------------------------------------------------------------------- 1 | use std::{env, process}; 2 | use wasi_tests::open_scratch_directory; 3 | 4 | unsafe fn test_file_allocate(dir_fd: wasi::Fd) { 5 | // Create a file in the scratch directory. 6 | let file_fd = wasi::path_open( 7 | dir_fd, 8 | 0, 9 | "file", 10 | wasi::OFLAGS_CREAT, 11 | wasi::RIGHTS_FD_READ | wasi::RIGHTS_FD_WRITE, 12 | 0, 13 | 0, 14 | ) 15 | .expect("opening a file"); 16 | assert!( 17 | file_fd > libc::STDERR_FILENO as wasi::Fd, 18 | "file descriptor range check", 19 | ); 20 | 21 | // Check file size 22 | let mut stat = wasi::fd_filestat_get(file_fd).expect("reading file stats"); 23 | assert_eq!(stat.size, 0, "file size should be 0"); 24 | 25 | // Allocate some size 26 | let err = wasi::fd_allocate(file_fd, 0, 100) 27 | .err() 28 | .expect("fd_allocate must fail"); 29 | assert_eq!( 30 | err, 31 | wasi::ERRNO_NOTSUP, 32 | "fd_allocate should fail with NOTSUP" 33 | ); 34 | 35 | stat = wasi::fd_filestat_get(file_fd).expect("reading file stats"); 36 | assert_eq!(stat.size, 0, "file size should still be 0"); 37 | 38 | wasi::fd_close(file_fd).expect("closing a file"); 39 | wasi::path_unlink_file(dir_fd, "file").expect("removing a file"); 40 | } 41 | 42 | fn main() { 43 | let mut args = env::args(); 44 | let prog = args.next().unwrap(); 45 | let arg = if let Some(arg) = args.next() { 46 | arg 47 | } else { 48 | eprintln!("usage: {} ", prog); 49 | process::exit(1); 50 | }; 51 | 52 | // Open scratch directory 53 | let dir_fd = match open_scratch_directory(&arg) { 54 | Ok(dir_fd) => dir_fd, 55 | Err(err) => { 56 | eprintln!("{}", err); 57 | process::exit(1) 58 | } 59 | }; 60 | 61 | // Run the tests. 62 | unsafe { test_file_allocate(dir_fd) } 63 | } 64 | -------------------------------------------------------------------------------- /wasi-common/src/random.rs: -------------------------------------------------------------------------------- 1 | use cap_rand::RngCore; 2 | 3 | /// Implement `WasiRandom` using a deterministic cycle of bytes. 4 | pub struct Deterministic { 5 | cycle: std::iter::Cycle>, 6 | } 7 | 8 | impl Deterministic { 9 | pub fn new(bytes: Vec) -> Self { 10 | Deterministic { 11 | cycle: bytes.into_iter().cycle(), 12 | } 13 | } 14 | } 15 | 16 | impl RngCore for Deterministic { 17 | fn next_u32(&mut self) -> u32 { 18 | let b0 = self.cycle.next().expect("infinite sequence"); 19 | let b1 = self.cycle.next().expect("infinite sequence"); 20 | let b2 = self.cycle.next().expect("infinite sequence"); 21 | let b3 = self.cycle.next().expect("infinite sequence"); 22 | ((b0 as u32) << 24) + ((b1 as u32) << 16) + ((b2 as u32) << 8) + (b3 as u32) 23 | } 24 | fn next_u64(&mut self) -> u64 { 25 | let w0 = self.next_u32(); 26 | let w1 = self.next_u32(); 27 | ((w0 as u64) << 32) + (w1 as u64) 28 | } 29 | fn fill_bytes(&mut self, buf: &mut [u8]) { 30 | for b in buf.iter_mut() { 31 | *b = self.cycle.next().expect("infinite sequence"); 32 | } 33 | } 34 | fn try_fill_bytes(&mut self, buf: &mut [u8]) -> Result<(), cap_rand::Error> { 35 | self.fill_bytes(buf); 36 | Ok(()) 37 | } 38 | } 39 | 40 | #[cfg(test)] 41 | mod test { 42 | use super::*; 43 | #[test] 44 | fn deterministic() { 45 | let mut det = Deterministic::new(vec![1, 2, 3, 4]); 46 | let mut buf = vec![0; 1024]; 47 | det.try_fill_bytes(&mut buf).expect("get randomness"); 48 | for (ix, b) in buf.iter().enumerate() { 49 | assert_eq!(*b, (ix % 4) as u8 + 1) 50 | } 51 | } 52 | } 53 | 54 | pub fn thread_rng() -> Box { 55 | use cap_rand::{Rng, SeedableRng}; 56 | let mut rng = cap_rand::thread_rng(cap_rand::ambient_authority()); 57 | Box::new(cap_rand::rngs::StdRng::from_seed(rng.gen())) 58 | } 59 | -------------------------------------------------------------------------------- /test-programs/wasi-tests/src/bin/path_rename_file_trailing_slashes.rs: -------------------------------------------------------------------------------- 1 | use std::{env, process}; 2 | use wasi_tests::{assert_errno, create_file, open_scratch_directory}; 3 | 4 | unsafe fn test_path_rename_trailing_slashes(dir_fd: wasi::Fd) { 5 | // Test renaming a file with a trailing slash in the name. 6 | create_file(dir_fd, "source"); 7 | 8 | wasi::path_rename(dir_fd, "source", dir_fd, "target") 9 | .expect("no trailing slashes rename works"); 10 | wasi::path_rename(dir_fd, "target", dir_fd, "source").expect("rename it back to source"); 11 | 12 | assert_errno!( 13 | wasi::path_rename(dir_fd, "source/", dir_fd, "target") 14 | .expect_err("renaming a file with a trailing slash in the source name should fail"), 15 | wasi::ERRNO_NOTDIR 16 | ); 17 | assert_errno!( 18 | wasi::path_rename(dir_fd, "source", dir_fd, "target/").expect_err( 19 | "renaming a file with a trailing slash in the destination name should fail" 20 | ), 21 | wasi::ERRNO_NOTDIR 22 | ); 23 | assert_errno!( 24 | wasi::path_rename(dir_fd, "source/", dir_fd, "target/").expect_err( 25 | "renaming a file with a trailing slash in the source and destination names should fail" 26 | ), 27 | wasi::ERRNO_NOTDIR 28 | ); 29 | wasi::path_unlink_file(dir_fd, "source").expect("removing a file"); 30 | } 31 | 32 | fn main() { 33 | let mut args = env::args(); 34 | let prog = args.next().unwrap(); 35 | let arg = if let Some(arg) = args.next() { 36 | arg 37 | } else { 38 | eprintln!("usage: {} ", prog); 39 | process::exit(1); 40 | }; 41 | 42 | // Open scratch directory 43 | let dir_fd = match open_scratch_directory(&arg) { 44 | Ok(dir_fd) => dir_fd, 45 | Err(err) => { 46 | eprintln!("{}", err); 47 | process::exit(1) 48 | } 49 | }; 50 | 51 | // Run the tests. 52 | unsafe { test_path_rename_trailing_slashes(dir_fd) } 53 | } 54 | -------------------------------------------------------------------------------- /test-programs/wasi-tests/src/bin/readlink.rs: -------------------------------------------------------------------------------- 1 | use std::{env, process}; 2 | use wasi_tests::{assert_errno, create_file, open_scratch_directory}; 3 | 4 | unsafe fn test_readlink(dir_fd: wasi::Fd) { 5 | // Create a file in the scratch directory. 6 | create_file(dir_fd, "target"); 7 | 8 | // Create a symlink 9 | wasi::path_symlink("target", dir_fd, "symlink").expect("creating a symlink"); 10 | 11 | // Read link into the buffer 12 | let buf = &mut [0u8; 10]; 13 | let bufused = wasi::path_readlink(dir_fd, "symlink", buf.as_mut_ptr(), buf.len()) 14 | .expect("readlink should succeed"); 15 | assert_eq!(bufused, 6, "should use 6 bytes of the buffer"); 16 | assert_eq!(&buf[..6], b"target", "buffer should contain 'target'"); 17 | assert_eq!( 18 | &buf[6..], 19 | &[0u8; 4], 20 | "the remaining bytes should be untouched" 21 | ); 22 | 23 | // Read link into smaller buffer than the actual link's length 24 | let buf = &mut [0u8; 4]; 25 | let bufused = wasi::path_readlink(dir_fd, "symlink", buf.as_mut_ptr(), buf.len()) 26 | .expect("readlink with too-small buffer should silently truncate"); 27 | assert_eq!(bufused, 4); 28 | assert_eq!(buf, b"targ"); 29 | 30 | // Clean up. 31 | wasi::path_unlink_file(dir_fd, "target").expect("removing a file"); 32 | wasi::path_unlink_file(dir_fd, "symlink").expect("removing a file"); 33 | } 34 | 35 | fn main() { 36 | let mut args = env::args(); 37 | let prog = args.next().unwrap(); 38 | let arg = if let Some(arg) = args.next() { 39 | arg 40 | } else { 41 | eprintln!("usage: {} ", prog); 42 | process::exit(1); 43 | }; 44 | 45 | // Open scratch directory 46 | let dir_fd = match open_scratch_directory(&arg) { 47 | Ok(dir_fd) => dir_fd, 48 | Err(err) => { 49 | eprintln!("{}", err); 50 | process::exit(1) 51 | } 52 | }; 53 | 54 | // Run the tests. 55 | unsafe { test_readlink(dir_fd) } 56 | } 57 | -------------------------------------------------------------------------------- /wit/deps/random/random.wit: -------------------------------------------------------------------------------- 1 | /// WASI Random is a random data API. 2 | /// 3 | /// It is intended to be portable at least between Unix-family platforms and 4 | /// Windows. 5 | default interface random { 6 | /// Return `len` cryptographically-secure pseudo-random bytes. 7 | /// 8 | /// This function must produce data from an adequately seeded 9 | /// cryptographically-secure pseudo-random number generator (CSPRNG), so it 10 | /// must not block, from the perspective of the calling program, and the 11 | /// returned data is always unpredictable. 12 | /// 13 | /// This function must always return fresh pseudo-random data. Deterministic 14 | /// environments must omit this function, rather than implementing it with 15 | /// deterministic data. 16 | get-random-bytes: func(len: u64) -> list 17 | 18 | /// Return a cryptographically-secure pseudo-random `u64` value. 19 | /// 20 | /// This function returns the same type of pseudo-random data as 21 | /// `get-random-bytes`, represented as a `u64`. 22 | get-random-u64: func() -> u64 23 | 24 | /// Return a 128-bit value that may contain a pseudo-random value. 25 | /// 26 | /// The returned value is not required to be computed from a CSPRNG, and may 27 | /// even be entirely deterministic. Host implementations are encouraged to 28 | /// provide pseudo-random values to any program exposed to 29 | /// attacker-controlled content, to enable DoS protection built into many 30 | /// languages' hash-map implementations. 31 | /// 32 | /// This function is intended to only be called once, by a source language 33 | /// to initialize Denial Of Service (DoS) protection in its hash-map 34 | /// implementation. 35 | /// 36 | /// # Expected future evolution 37 | /// 38 | /// This will likely be changed to a value import, to prevent it from being 39 | /// called multiple times and potentially used for purposes other than DoS 40 | /// protection. 41 | insecure-random: func() -> tuple 42 | } 43 | -------------------------------------------------------------------------------- /wit/deps/poll/poll.wit: -------------------------------------------------------------------------------- 1 | /// A poll API intended to let users wait for I/O events on multiple handles 2 | /// at once. 3 | default interface poll { 4 | /// A "pollable" handle. 5 | /// 6 | /// This is conceptually represents a `stream<_, _>`, or in other words, 7 | /// a stream that one can wait on, repeatedly, but which does not itself 8 | /// produce any data. It's temporary scaffolding until component-model's 9 | /// async features are ready. 10 | /// 11 | /// And at present, it is a `u32` instead of being an actual handle, until 12 | /// the wit-bindgen implementation of handles and resources is ready. 13 | /// 14 | /// `pollable` lifetimes are not automatically managed. Users must ensure 15 | /// that they do not outlive the resource they reference. 16 | /// 17 | /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). 18 | type pollable = u32 19 | 20 | /// Dispose of the specified `pollable`, after which it may no longer 21 | /// be used. 22 | drop-pollable: func(this: pollable) 23 | 24 | /// Poll for completion on a set of pollables. 25 | /// 26 | /// The "oneoff" in the name refers to the fact that this function must do a 27 | /// linear scan through the entire list of subscriptions, which may be 28 | /// inefficient if the number is large and the same subscriptions are used 29 | /// many times. In the future, this is expected to be obsoleted by the 30 | /// component model async proposal, which will include a scalable waiting 31 | /// facility. 32 | /// 33 | /// Note that the return type would ideally be `list`, but that would 34 | /// be more difficult to polyfill given the current state of `wit-bindgen`. 35 | /// See 36 | /// for details. For now, we use zero to mean "not ready" and non-zero to 37 | /// mean "ready". 38 | poll-oneoff: func(in: list) -> list 39 | } 40 | -------------------------------------------------------------------------------- /test-programs/reactor-tests/wit/deps/random/random.wit: -------------------------------------------------------------------------------- 1 | /// WASI Random is a random data API. 2 | /// 3 | /// It is intended to be portable at least between Unix-family platforms and 4 | /// Windows. 5 | default interface random { 6 | /// Return `len` cryptographically-secure pseudo-random bytes. 7 | /// 8 | /// This function must produce data from an adequately seeded 9 | /// cryptographically-secure pseudo-random number generator (CSPRNG), so it 10 | /// must not block, from the perspective of the calling program, and the 11 | /// returned data is always unpredictable. 12 | /// 13 | /// This function must always return fresh pseudo-random data. Deterministic 14 | /// environments must omit this function, rather than implementing it with 15 | /// deterministic data. 16 | get-random-bytes: func(len: u64) -> list 17 | 18 | /// Return a cryptographically-secure pseudo-random `u64` value. 19 | /// 20 | /// This function returns the same type of pseudo-random data as 21 | /// `get-random-bytes`, represented as a `u64`. 22 | get-random-u64: func() -> u64 23 | 24 | /// Return a 128-bit value that may contain a pseudo-random value. 25 | /// 26 | /// The returned value is not required to be computed from a CSPRNG, and may 27 | /// even be entirely deterministic. Host implementations are encouraged to 28 | /// provide pseudo-random values to any program exposed to 29 | /// attacker-controlled content, to enable DoS protection built into many 30 | /// languages' hash-map implementations. 31 | /// 32 | /// This function is intended to only be called once, by a source language 33 | /// to initialize Denial Of Service (DoS) protection in its hash-map 34 | /// implementation. 35 | /// 36 | /// # Expected future evolution 37 | /// 38 | /// This will likely be changed to a value import, to prevent it from being 39 | /// called multiple times and potentially used for purposes other than DoS 40 | /// protection. 41 | insecure-random: func() -> tuple 42 | } 43 | -------------------------------------------------------------------------------- /test-programs/reactor-tests/wit/deps/poll/poll.wit: -------------------------------------------------------------------------------- 1 | /// A poll API intended to let users wait for I/O events on multiple handles 2 | /// at once. 3 | default interface poll { 4 | /// A "pollable" handle. 5 | /// 6 | /// This is conceptually represents a `stream<_, _>`, or in other words, 7 | /// a stream that one can wait on, repeatedly, but which does not itself 8 | /// produce any data. It's temporary scaffolding until component-model's 9 | /// async features are ready. 10 | /// 11 | /// And at present, it is a `u32` instead of being an actual handle, until 12 | /// the wit-bindgen implementation of handles and resources is ready. 13 | /// 14 | /// `pollable` lifetimes are not automatically managed. Users must ensure 15 | /// that they do not outlive the resource they reference. 16 | /// 17 | /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). 18 | type pollable = u32 19 | 20 | /// Dispose of the specified `pollable`, after which it may no longer 21 | /// be used. 22 | drop-pollable: func(this: pollable) 23 | 24 | /// Poll for completion on a set of pollables. 25 | /// 26 | /// The "oneoff" in the name refers to the fact that this function must do a 27 | /// linear scan through the entire list of subscriptions, which may be 28 | /// inefficient if the number is large and the same subscriptions are used 29 | /// many times. In the future, this is expected to be obsoleted by the 30 | /// component model async proposal, which will include a scalable waiting 31 | /// facility. 32 | /// 33 | /// Note that the return type would ideally be `list`, but that would 34 | /// be more difficult to polyfill given the current state of `wit-bindgen`. 35 | /// See 36 | /// for details. For now, we use zero to mean "not ready" and non-zero to 37 | /// mean "ready". 38 | poll-oneoff: func(in: list) -> list 39 | } 40 | -------------------------------------------------------------------------------- /crates/wasi-preview1-component-adapter/wit/deps/random/random.wit: -------------------------------------------------------------------------------- 1 | /// WASI Random is a random data API. 2 | /// 3 | /// It is intended to be portable at least between Unix-family platforms and 4 | /// Windows. 5 | default interface random { 6 | /// Return `len` cryptographically-secure pseudo-random bytes. 7 | /// 8 | /// This function must produce data from an adequately seeded 9 | /// cryptographically-secure pseudo-random number generator (CSPRNG), so it 10 | /// must not block, from the perspective of the calling program, and the 11 | /// returned data is always unpredictable. 12 | /// 13 | /// This function must always return fresh pseudo-random data. Deterministic 14 | /// environments must omit this function, rather than implementing it with 15 | /// deterministic data. 16 | get-random-bytes: func(len: u64) -> list 17 | 18 | /// Return a cryptographically-secure pseudo-random `u64` value. 19 | /// 20 | /// This function returns the same type of pseudo-random data as 21 | /// `get-random-bytes`, represented as a `u64`. 22 | get-random-u64: func() -> u64 23 | 24 | /// Return a 128-bit value that may contain a pseudo-random value. 25 | /// 26 | /// The returned value is not required to be computed from a CSPRNG, and may 27 | /// even be entirely deterministic. Host implementations are encouraged to 28 | /// provide pseudo-random values to any program exposed to 29 | /// attacker-controlled content, to enable DoS protection built into many 30 | /// languages' hash-map implementations. 31 | /// 32 | /// This function is intended to only be called once, by a source language 33 | /// to initialize Denial Of Service (DoS) protection in its hash-map 34 | /// implementation. 35 | /// 36 | /// # Expected future evolution 37 | /// 38 | /// This will likely be changed to a value import, to prevent it from being 39 | /// called multiple times and potentially used for purposes other than DoS 40 | /// protection. 41 | insecure-random: func() -> tuple 42 | } 43 | -------------------------------------------------------------------------------- /crates/wasi-preview1-component-adapter/wit/deps/poll/poll.wit: -------------------------------------------------------------------------------- 1 | /// A poll API intended to let users wait for I/O events on multiple handles 2 | /// at once. 3 | default interface poll { 4 | /// A "pollable" handle. 5 | /// 6 | /// This is conceptually represents a `stream<_, _>`, or in other words, 7 | /// a stream that one can wait on, repeatedly, but which does not itself 8 | /// produce any data. It's temporary scaffolding until component-model's 9 | /// async features are ready. 10 | /// 11 | /// And at present, it is a `u32` instead of being an actual handle, until 12 | /// the wit-bindgen implementation of handles and resources is ready. 13 | /// 14 | /// `pollable` lifetimes are not automatically managed. Users must ensure 15 | /// that they do not outlive the resource they reference. 16 | /// 17 | /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). 18 | type pollable = u32 19 | 20 | /// Dispose of the specified `pollable`, after which it may no longer 21 | /// be used. 22 | drop-pollable: func(this: pollable) 23 | 24 | /// Poll for completion on a set of pollables. 25 | /// 26 | /// The "oneoff" in the name refers to the fact that this function must do a 27 | /// linear scan through the entire list of subscriptions, which may be 28 | /// inefficient if the number is large and the same subscriptions are used 29 | /// many times. In the future, this is expected to be obsoleted by the 30 | /// component model async proposal, which will include a scalable waiting 31 | /// facility. 32 | /// 33 | /// Note that the return type would ideally be `list`, but that would 34 | /// be more difficult to polyfill given the current state of `wit-bindgen`. 35 | /// See 36 | /// for details. For now, we use zero to mean "not ready" and non-zero to 37 | /// mean "ready". 38 | poll-oneoff: func(in: list) -> list 39 | } 40 | -------------------------------------------------------------------------------- /test-programs/wasi-tests/src/bin/fd_advise.rs: -------------------------------------------------------------------------------- 1 | use std::{env, process}; 2 | use wasi_tests::open_scratch_directory; 3 | 4 | unsafe fn test_fd_advise(dir_fd: wasi::Fd) { 5 | // Create a file in the scratch directory. 6 | let file_fd = wasi::path_open( 7 | dir_fd, 8 | 0, 9 | "file", 10 | wasi::OFLAGS_CREAT, 11 | wasi::RIGHTS_FD_READ | wasi::RIGHTS_FD_WRITE, 12 | 0, 13 | 0, 14 | ) 15 | .expect("failed to open file"); 16 | assert!( 17 | file_fd > libc::STDERR_FILENO as wasi::Fd, 18 | "file descriptor range check", 19 | ); 20 | 21 | // Check file size 22 | let stat = wasi::fd_filestat_get(file_fd).expect("failed to fdstat"); 23 | assert_eq!(stat.size, 0, "file size should be 0"); 24 | 25 | // set_size it bigger 26 | wasi::fd_filestat_set_size(file_fd, 100).expect("setting size"); 27 | 28 | let stat = wasi::fd_filestat_get(file_fd).expect("failed to fdstat 2"); 29 | assert_eq!(stat.size, 100, "file size should be 100"); 30 | 31 | // Advise the kernel 32 | wasi::fd_advise(file_fd, 10, 50, wasi::ADVICE_NORMAL).expect("failed advise"); 33 | 34 | // Advise shouldnt change size 35 | let stat = wasi::fd_filestat_get(file_fd).expect("failed to fdstat 3"); 36 | assert_eq!(stat.size, 100, "file size should be 100"); 37 | 38 | wasi::fd_close(file_fd).expect("failed to close"); 39 | wasi::path_unlink_file(dir_fd, "file").expect("failed to unlink"); 40 | } 41 | fn main() { 42 | let mut args = env::args(); 43 | let prog = args.next().unwrap(); 44 | let arg = if let Some(arg) = args.next() { 45 | arg 46 | } else { 47 | eprintln!("usage: {} ", prog); 48 | process::exit(1); 49 | }; 50 | 51 | // Open scratch directory 52 | let dir_fd = match open_scratch_directory(&arg) { 53 | Ok(dir_fd) => dir_fd, 54 | Err(err) => { 55 | eprintln!("{}", err); 56 | process::exit(1) 57 | } 58 | }; 59 | 60 | // Run the tests. 61 | unsafe { test_fd_advise(dir_fd) } 62 | } 63 | -------------------------------------------------------------------------------- /wasi-sockets/sync/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod net; 2 | 3 | pub use cap_net_ext::AddressFamily; 4 | pub use cap_std::net::TcpListener; 5 | pub use cap_std::AmbientAuthority; 6 | 7 | use crate::net::{Network, TcpSocket}; 8 | use anyhow::Error; 9 | use cap_std::net::{Ipv4Addr, Ipv6Addr, Pool}; 10 | use ipnet::IpNet; 11 | use wasmtime_wasi_sockets::{WasiNetwork, WasiSocketsCtx, WasiTcpSocket}; 12 | 13 | pub struct WasiSocketsCtxBuilder { 14 | pool: Pool, 15 | } 16 | 17 | impl WasiSocketsCtxBuilder { 18 | pub fn new() -> Self { 19 | Self { pool: Pool::new() } 20 | } 21 | 22 | pub fn inherit_network(mut self, ambient_authority: AmbientAuthority) -> Self { 23 | self.pool.insert_ip_net_port_any( 24 | IpNet::new(Ipv4Addr::UNSPECIFIED.into(), 0).unwrap(), 25 | ambient_authority, 26 | ); 27 | self.pool.insert_ip_net_port_any( 28 | IpNet::new(Ipv6Addr::UNSPECIFIED.into(), 0).unwrap(), 29 | ambient_authority, 30 | ); 31 | self 32 | } 33 | 34 | /* FIXME: idk how to translate this idiom because i don't have any tests checked in showing its 35 | * use. we cant allocate the fd until build(). 36 | pub fn preopened_listener(mut self, fd: u32, listener: impl Into) -> Self { 37 | let listener: TcpSocket = listener.into(); 38 | let listener: Box = Box::new(TcpSocket::from(listener)); 39 | 40 | self.0.insert_listener(fd, listener); 41 | self 42 | } 43 | */ 44 | 45 | pub fn build(self) -> WasiSocketsCtx { 46 | WasiSocketsCtx::new( 47 | self.pool, 48 | Box::new(create_network), 49 | Box::new(create_tcp_socket), 50 | ) 51 | } 52 | } 53 | 54 | fn create_network(pool: Pool) -> Result, Error> { 55 | let network: Box = Box::new(Network::new(pool)); 56 | Ok(network) 57 | } 58 | 59 | fn create_tcp_socket(address_family: AddressFamily) -> Result, Error> { 60 | let socket: Box = Box::new(TcpSocket::new(address_family)?); 61 | Ok(socket) 62 | } 63 | -------------------------------------------------------------------------------- /test-programs/wasi-tests/src/config.rs: -------------------------------------------------------------------------------- 1 | pub struct TestConfig { 2 | errno_mode: ErrnoMode, 3 | no_dangling_filesystem: bool, 4 | no_rename_dir_to_empty_dir: bool, 5 | no_fdflags_sync_support: bool, 6 | } 7 | 8 | enum ErrnoMode { 9 | Unix, 10 | MacOS, 11 | Windows, 12 | Permissive, 13 | } 14 | 15 | impl TestConfig { 16 | pub fn from_env() -> Self { 17 | let errno_mode = if std::env::var("ERRNO_MODE_UNIX").is_ok() { 18 | ErrnoMode::Unix 19 | } else if std::env::var("ERRNO_MODE_MACOS").is_ok() { 20 | ErrnoMode::MacOS 21 | } else if std::env::var("ERRNO_MODE_WINDOWS").is_ok() { 22 | ErrnoMode::Windows 23 | } else { 24 | ErrnoMode::Permissive 25 | }; 26 | let no_dangling_filesystem = std::env::var("NO_DANGLING_FILESYSTEM").is_ok(); 27 | let no_rename_dir_to_empty_dir = std::env::var("NO_RENAME_DIR_TO_EMPTY_DIR").is_ok(); 28 | let no_fdflags_sync_support = std::env::var("NO_FDFLAGS_SYNC_SUPPORT").is_ok(); 29 | TestConfig { 30 | errno_mode, 31 | no_dangling_filesystem, 32 | no_rename_dir_to_empty_dir, 33 | no_fdflags_sync_support, 34 | } 35 | } 36 | pub fn errno_expect_unix(&self) -> bool { 37 | match self.errno_mode { 38 | ErrnoMode::Unix | ErrnoMode::MacOS => true, 39 | _ => false, 40 | } 41 | } 42 | pub fn errno_expect_macos(&self) -> bool { 43 | match self.errno_mode { 44 | ErrnoMode::MacOS => true, 45 | _ => false, 46 | } 47 | } 48 | pub fn errno_expect_windows(&self) -> bool { 49 | match self.errno_mode { 50 | ErrnoMode::Windows => true, 51 | _ => false, 52 | } 53 | } 54 | pub fn support_dangling_filesystem(&self) -> bool { 55 | !self.no_dangling_filesystem 56 | } 57 | pub fn support_rename_dir_to_empty_dir(&self) -> bool { 58 | !self.no_rename_dir_to_empty_dir 59 | } 60 | pub fn support_fdflags_sync(&self) -> bool { 61 | !self.no_fdflags_sync_support 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /test-programs/wasi-tests/src/bin/dangling_fd.rs: -------------------------------------------------------------------------------- 1 | use std::{env, process}; 2 | use wasi_tests::{open_scratch_directory, TESTCONFIG}; 3 | 4 | unsafe fn test_dangling_fd(dir_fd: wasi::Fd) { 5 | if TESTCONFIG.support_dangling_filesystem() { 6 | // Create a file, open it, delete it without closing the handle, 7 | // and then try creating it again 8 | let fd = wasi::path_open(dir_fd, 0, "file", wasi::OFLAGS_CREAT, 0, 0, 0).unwrap(); 9 | wasi::fd_close(fd).unwrap(); 10 | let file_fd = wasi::path_open(dir_fd, 0, "file", 0, 0, 0, 0).expect("failed to open"); 11 | assert!( 12 | file_fd > libc::STDERR_FILENO as wasi::Fd, 13 | "file descriptor range check", 14 | ); 15 | wasi::path_unlink_file(dir_fd, "file").expect("failed to unlink"); 16 | let fd = wasi::path_open(dir_fd, 0, "file", wasi::OFLAGS_CREAT, 0, 0, 0).unwrap(); 17 | wasi::fd_close(fd).unwrap(); 18 | 19 | // Now, repeat the same process but for a directory 20 | wasi::path_create_directory(dir_fd, "subdir").expect("failed to create dir"); 21 | let subdir_fd = wasi::path_open(dir_fd, 0, "subdir", wasi::OFLAGS_DIRECTORY, 0, 0, 0) 22 | .expect("failed to open dir"); 23 | assert!( 24 | subdir_fd > libc::STDERR_FILENO as wasi::Fd, 25 | "file descriptor range check", 26 | ); 27 | wasi::path_remove_directory(dir_fd, "subdir").expect("failed to remove dir 2"); 28 | wasi::path_create_directory(dir_fd, "subdir").expect("failed to create dir 2"); 29 | } 30 | } 31 | 32 | fn main() { 33 | let mut args = env::args(); 34 | let prog = args.next().unwrap(); 35 | let arg = if let Some(arg) = args.next() { 36 | arg 37 | } else { 38 | eprintln!("usage: {} ", prog); 39 | process::exit(1); 40 | }; 41 | 42 | // Open scratch directory 43 | let dir_fd = match open_scratch_directory(&arg) { 44 | Ok(dir_fd) => dir_fd, 45 | Err(err) => { 46 | eprintln!("{}", err); 47 | process::exit(1) 48 | } 49 | }; 50 | 51 | // Run the tests. 52 | unsafe { test_dangling_fd(dir_fd) } 53 | } 54 | -------------------------------------------------------------------------------- /test-programs/wasi-tests/src/bin/remove_directory_trailing_slashes.rs: -------------------------------------------------------------------------------- 1 | use std::{env, process}; 2 | use wasi_tests::{assert_errno, create_file, open_scratch_directory}; 3 | 4 | unsafe fn test_remove_directory_trailing_slashes(dir_fd: wasi::Fd) { 5 | // Create a directory in the scratch directory. 6 | wasi::path_create_directory(dir_fd, "dir").expect("creating a directory"); 7 | 8 | // Test that removing it succeeds. 9 | wasi::path_remove_directory(dir_fd, "dir") 10 | .expect("remove_directory on a directory should succeed"); 11 | 12 | wasi::path_create_directory(dir_fd, "dir").expect("creating a directory"); 13 | 14 | // Test that removing it with a trailing slash succeeds. 15 | wasi::path_remove_directory(dir_fd, "dir/") 16 | .expect("remove_directory with a trailing slash on a directory should succeed"); 17 | 18 | // Create a temporary file. 19 | create_file(dir_fd, "file"); 20 | 21 | // Test that removing it with no trailing slash fails. 22 | assert_errno!( 23 | wasi::path_remove_directory(dir_fd, "file") 24 | .expect_err("remove_directory without a trailing slash on a file should fail"), 25 | wasi::ERRNO_NOTDIR 26 | ); 27 | 28 | // Test that removing it with a trailing slash fails. 29 | assert_errno!( 30 | wasi::path_remove_directory(dir_fd, "file/") 31 | .expect_err("remove_directory with a trailing slash on a file should fail"), 32 | unix => wasi::ERRNO_NOTDIR, 33 | windows => wasi::ERRNO_NOENT 34 | ); 35 | 36 | wasi::path_unlink_file(dir_fd, "file").expect("removing a file"); 37 | } 38 | 39 | fn main() { 40 | let mut args = env::args(); 41 | let prog = args.next().unwrap(); 42 | let arg = if let Some(arg) = args.next() { 43 | arg 44 | } else { 45 | eprintln!("usage: {} ", prog); 46 | process::exit(1); 47 | }; 48 | 49 | // Open scratch directory 50 | let dir_fd = match open_scratch_directory(&arg) { 51 | Ok(dir_fd) => dir_fd, 52 | Err(err) => { 53 | eprintln!("{}", err); 54 | process::exit(1) 55 | } 56 | }; 57 | 58 | // Run the tests. 59 | unsafe { test_remove_directory_trailing_slashes(dir_fd) } 60 | } 61 | -------------------------------------------------------------------------------- /crates/wasi-preview1-component-adapter/README.md: -------------------------------------------------------------------------------- 1 | # `wasi_snapshot_preview1.wasm` 2 | 3 | > **Note**: This repository is a work in progress. This is intended to be an 4 | > internal tool which not everyone has to look at but many might rely on. You 5 | > may need to reach out via issues or 6 | > [Zulip](https://bytecodealliance.zulipchat.com/) to learn more about this 7 | > repository. 8 | 9 | This repository currently contains an implementation of a WebAssembly module: 10 | `wasi_snapshot_preview1.wasm`. This module bridges the `wasi_snapshot_preview1` 11 | ABI to the preview2 ABI of the component model. At this time the preview2 APIs 12 | themselves are not done being specified so a local copy of `wit/*.wit` is used 13 | instead. 14 | 15 | ## Building 16 | 17 | This adapter can be built with: 18 | 19 | ```sh 20 | $ cargo build --target wasm32-unknown-unknown --release 21 | ``` 22 | 23 | And the artifact will be located at 24 | `target/wasm32-unknown-unknown/release/wasi_snapshot_preview1.wasm`. 25 | Alternatively the latest copy of this can be [downloaded from the releases 26 | page][latest-release] 27 | 28 | [latest-release]: https://github.com/bytecodealliance/preview2-prototyping/releases/tag/latest 29 | 30 | ## Using 31 | 32 | With a `wasi_snapshot_preview1.wasm` file on-hand you can create a component 33 | from a module that imports WASI functions using the [`wasm-tools` 34 | CLI](https://github.com/bytecodealliance/wasm-tools) 35 | 36 | ```sh 37 | $ cat foo.rs 38 | fn main() { 39 | println!("Hello, world!"); 40 | } 41 | $ rustc foo.rs --target wasm32-wasi 42 | $ wasm-tools print foo.wasm | grep '(import' 43 | (import "wasi_snapshot_preview1" "fd_write" (func ... 44 | (import "wasi_snapshot_preview1" "environ_get" (func ... 45 | (import "wasi_snapshot_preview1" "environ_sizes_get" ... 46 | (import "wasi_snapshot_preview1" "proc_exit" (func ... 47 | $ wasm-tools component new foo.wasm --adapt wasi_snapshot_preview1.wasm -o component.wasm 48 | 49 | # Inspect the generated `component.wasm` 50 | $ wasm-tools validate component.wasm --features component-model 51 | $ wasm-tools component wit component.wasm 52 | ``` 53 | 54 | Here the `component.wasm` that's generated is a ready-to-run component which 55 | imports wasi preview2 functions and is compatible with the wasi-preview1-using 56 | module internally. 57 | -------------------------------------------------------------------------------- /test-programs/wasi-tests/src/bin/unlink_file_trailing_slashes.rs: -------------------------------------------------------------------------------- 1 | use std::{env, process}; 2 | use wasi_tests::{assert_errno, create_file, open_scratch_directory}; 3 | 4 | unsafe fn test_unlink_file_trailing_slashes(dir_fd: wasi::Fd) { 5 | // Create a directory in the scratch directory. 6 | wasi::path_create_directory(dir_fd, "dir").expect("creating a directory"); 7 | 8 | // Test that unlinking it fails. 9 | assert_errno!( 10 | wasi::path_unlink_file(dir_fd, "dir") 11 | .expect_err("unlink_file on a directory should fail"), 12 | macos => wasi::ERRNO_PERM, 13 | unix => wasi::ERRNO_ISDIR, 14 | windows => wasi::ERRNO_ACCES 15 | ); 16 | 17 | // Test that unlinking it with a trailing flash fails. 18 | assert_errno!( 19 | wasi::path_unlink_file(dir_fd, "dir/") 20 | .expect_err("unlink_file on a directory should fail"), 21 | macos => wasi::ERRNO_PERM, 22 | unix => wasi::ERRNO_ISDIR, 23 | windows => wasi::ERRNO_ACCES 24 | ); 25 | 26 | // Clean up. 27 | wasi::path_remove_directory(dir_fd, "dir").expect("removing a directory"); 28 | 29 | // Create a temporary file. 30 | create_file(dir_fd, "file"); 31 | 32 | // Test that unlinking it with a trailing flash fails. 33 | assert_errno!( 34 | wasi::path_unlink_file(dir_fd, "file/") 35 | .expect_err("unlink_file with a trailing slash should fail"), 36 | wasi::ERRNO_NOTDIR 37 | ); 38 | 39 | // Test that unlinking it with no trailing flash succeeds. 40 | wasi::path_unlink_file(dir_fd, "file") 41 | .expect("unlink_file with no trailing slash should succeed"); 42 | } 43 | 44 | fn main() { 45 | let mut args = env::args(); 46 | let prog = args.next().unwrap(); 47 | let arg = if let Some(arg) = args.next() { 48 | arg 49 | } else { 50 | eprintln!("usage: {} ", prog); 51 | process::exit(1); 52 | }; 53 | 54 | // Open scratch directory 55 | let dir_fd = match open_scratch_directory(&arg) { 56 | Ok(dir_fd) => dir_fd, 57 | Err(err) => { 58 | eprintln!("{}", err); 59 | process::exit(1) 60 | } 61 | }; 62 | 63 | // Run the tests. 64 | unsafe { test_unlink_file_trailing_slashes(dir_fd) } 65 | } 66 | -------------------------------------------------------------------------------- /wasi-common/src/clocks/host.rs: -------------------------------------------------------------------------------- 1 | use super::{WasiClocks, WasiMonotonicClock, WasiWallClock}; 2 | use cap_std::time::{Duration, Instant, SystemClock}; 3 | use cap_std::{ambient_authority, AmbientAuthority}; 4 | use cap_time_ext::{MonotonicClockExt, SystemClockExt}; 5 | 6 | pub struct WallClock { 7 | /// The underlying system clock. 8 | clock: cap_std::time::SystemClock, 9 | } 10 | 11 | impl WallClock { 12 | pub fn new(ambient_authority: AmbientAuthority) -> Self { 13 | Self { 14 | clock: cap_std::time::SystemClock::new(ambient_authority), 15 | } 16 | } 17 | } 18 | 19 | impl WasiWallClock for WallClock { 20 | fn resolution(&self) -> Duration { 21 | self.clock.resolution() 22 | } 23 | 24 | fn now(&self) -> Duration { 25 | // WASI defines wall clocks to return "Unix time". 26 | self.clock 27 | .now() 28 | .duration_since(SystemClock::UNIX_EPOCH) 29 | .unwrap() 30 | } 31 | } 32 | 33 | pub struct MonotonicClock { 34 | /// The underlying system clock. 35 | clock: cap_std::time::MonotonicClock, 36 | 37 | /// The `Instant` this clock was created. All returned times are 38 | /// durations since that time. 39 | initial: Instant, 40 | } 41 | 42 | impl MonotonicClock { 43 | pub fn new(ambient_authority: AmbientAuthority) -> Self { 44 | let clock = cap_std::time::MonotonicClock::new(ambient_authority); 45 | let initial = clock.now(); 46 | Self { clock, initial } 47 | } 48 | } 49 | 50 | impl WasiMonotonicClock for MonotonicClock { 51 | fn resolution(&self) -> u64 { 52 | self.clock.resolution().as_nanos().try_into().unwrap() 53 | } 54 | 55 | fn now(&self) -> u64 { 56 | // Unwrap here and in `resolution` above; a `u64` is wide enough to 57 | // hold over 584 years of nanoseconds. 58 | self.clock 59 | .now() 60 | .duration_since(self.initial) 61 | .as_nanos() 62 | .try_into() 63 | .unwrap() 64 | } 65 | } 66 | 67 | pub fn clocks_ctx() -> WasiClocks { 68 | // Create the per-instance clock resources. 69 | let monotonic = Box::new(MonotonicClock::new(ambient_authority())); 70 | let wall = Box::new(WallClock::new(ambient_authority())); 71 | 72 | WasiClocks { monotonic, wall } 73 | } 74 | -------------------------------------------------------------------------------- /wasi/wit/deps/cli/terminal.wit: -------------------------------------------------------------------------------- 1 | interface terminal-input { 2 | /// The input side of a terminal. 3 | /// 4 | /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). 5 | type terminal-input = u32 6 | 7 | // In the future, this may include functions for disabling echoing, 8 | // disabling input buffering so that keyboard events are sent through 9 | // immediately, querying supported features, and so on. 10 | 11 | /// Dispose of the specified terminal-input after which it may no longer 12 | /// be used. 13 | drop-terminal-input: func(this: terminal-input) 14 | } 15 | 16 | interface terminal-output { 17 | /// The output side of a terminal. 18 | /// 19 | /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). 20 | type terminal-output = u32 21 | 22 | // In the future, this may include functions for querying the terminal 23 | // size, being notified of terminal size changes, querying supported 24 | // features, and so on. 25 | 26 | /// Dispose of the specified terminal-output, after which it may no longer 27 | /// be used. 28 | drop-terminal-output: func(this: terminal-output) 29 | } 30 | 31 | /// An interface providing an optional `terminal-input` for stdin as a 32 | /// link-time authority. 33 | interface terminal-stdin { 34 | use terminal-input.{terminal-input} 35 | 36 | /// If stdin is connected to a terminal, return a `terminal-input` handle 37 | /// allowing further interaction with it. 38 | get-terminal-stdin: func() -> option 39 | } 40 | 41 | /// An interface providing an optional `terminal-output` for stdout as a 42 | /// link-time authority. 43 | interface terminal-stdout { 44 | use terminal-output.{terminal-output} 45 | 46 | /// If stdout is connected to a terminal, return a `terminal-output` handle 47 | /// allowing further interaction with it. 48 | get-terminal-stdout: func() -> option 49 | } 50 | 51 | /// An interface providing an optional `terminal-output` for stderr as a 52 | /// link-time authority. 53 | interface terminal-stderr { 54 | use terminal-output.{terminal-output} 55 | 56 | /// If stderr is connected to a terminal, return a `terminal-output` handle 57 | /// allowing further interaction with it. 58 | get-terminal-stderr: func() -> option 59 | } 60 | -------------------------------------------------------------------------------- /wasi-sockets/src/tcp_socket.rs: -------------------------------------------------------------------------------- 1 | //! TCP sockets. 2 | 3 | use crate::WasiNetwork; 4 | use anyhow::Error; 5 | use cap_std::net::{Shutdown, SocketAddr}; 6 | use std::any::Any; 7 | use wasi_common::{InputStream, OutputStream}; 8 | 9 | /// A TCP socket. 10 | #[async_trait::async_trait] 11 | pub trait WasiTcpSocket: Send + Sync { 12 | fn as_any(&self) -> &dyn Any; 13 | 14 | /// Return the host file descriptor so that it can be polled with a host poll. 15 | fn pollable(&self) -> rustix::fd::BorrowedFd; 16 | 17 | async fn listen(&self, network: &dyn WasiNetwork) -> Result<(), Error>; 18 | 19 | async fn accept( 20 | &self, 21 | nonblocking: bool, 22 | ) -> Result< 23 | ( 24 | Box, 25 | Box, 26 | Box, 27 | SocketAddr, 28 | ), 29 | Error, 30 | >; 31 | 32 | async fn connect( 33 | &self, 34 | network: &dyn WasiNetwork, 35 | remote_address: SocketAddr, 36 | ) -> Result<(Box, Box), Error>; 37 | 38 | async fn bind(&self, network: &dyn WasiNetwork, local_address: SocketAddr) 39 | -> Result<(), Error>; 40 | 41 | async fn shutdown(&self, how: Shutdown) -> Result<(), Error>; 42 | 43 | fn local_address(&self) -> Result; 44 | fn remote_address(&self) -> Result; 45 | 46 | fn nodelay(&self) -> Result; 47 | fn set_nodelay(&self, value: bool) -> Result<(), Error>; 48 | fn v6_only(&self) -> Result; 49 | fn set_v6_only(&self, value: bool) -> Result<(), Error>; 50 | 51 | fn set_nonblocking(&mut self, flag: bool) -> Result<(), Error>; 52 | 53 | async fn readable(&self) -> Result<(), Error>; 54 | 55 | async fn writable(&self) -> Result<(), Error>; 56 | } 57 | 58 | pub trait TableTcpSocketExt { 59 | fn get_tcp_socket(&self, fd: u32) -> Result<&dyn WasiTcpSocket, Error>; 60 | fn get_tcp_socket_mut(&mut self, fd: u32) -> Result<&mut Box, Error>; 61 | } 62 | impl TableTcpSocketExt for wasi_common::Table { 63 | fn get_tcp_socket(&self, fd: u32) -> Result<&dyn WasiTcpSocket, Error> { 64 | Ok(self.get::>(fd).map(|f| f.as_ref())?) 65 | } 66 | fn get_tcp_socket_mut(&mut self, fd: u32) -> Result<&mut Box, Error> { 67 | Ok(self.get_mut::>(fd)?) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /test-programs/wasi-tests/src/bin/file_truncation.rs: -------------------------------------------------------------------------------- 1 | use std::{env, process}; 2 | use wasi_tests::open_scratch_directory; 3 | 4 | unsafe fn test_file_truncation(dir_fd: wasi::Fd) { 5 | const FILENAME: &str = "test.txt"; 6 | 7 | // Open a file for writing 8 | let file_fd = wasi::path_open( 9 | dir_fd, 10 | 0, 11 | FILENAME, 12 | wasi::OFLAGS_CREAT, 13 | wasi::RIGHTS_FD_WRITE, 14 | 0, 15 | 0, 16 | ) 17 | .expect("creating a file for writing"); 18 | 19 | // Write to the file 20 | let content = b"this content will be truncated!"; 21 | let nwritten = wasi::fd_write( 22 | file_fd, 23 | &[wasi::Ciovec { 24 | buf: content.as_ptr() as *const _, 25 | buf_len: content.len(), 26 | }], 27 | ) 28 | .expect("writing file content"); 29 | assert_eq!(nwritten, content.len(), "nwritten bytes check"); 30 | 31 | wasi::fd_close(file_fd).expect("closing the file"); 32 | 33 | // Open the file for truncation 34 | let file_fd = wasi::path_open( 35 | dir_fd, 36 | 0, 37 | FILENAME, 38 | wasi::OFLAGS_CREAT | wasi::OFLAGS_TRUNC, 39 | wasi::RIGHTS_FD_WRITE | wasi::RIGHTS_FD_READ, 40 | 0, 41 | 0, 42 | ) 43 | .expect("creating a truncated file for reading"); 44 | 45 | // Read the file's contents 46 | let buffer = &mut [0u8; 100]; 47 | let nread = wasi::fd_read( 48 | file_fd, 49 | &[wasi::Iovec { 50 | buf: buffer.as_mut_ptr(), 51 | buf_len: buffer.len(), 52 | }], 53 | ) 54 | .expect("reading file content"); 55 | 56 | // The file should be empty due to truncation 57 | assert_eq!(nread, 0, "expected an empty file after truncation"); 58 | 59 | wasi::fd_close(file_fd).expect("closing the file"); 60 | } 61 | 62 | fn main() { 63 | let mut args = env::args(); 64 | let prog = args.next().unwrap(); 65 | let arg = if let Some(arg) = args.next() { 66 | arg 67 | } else { 68 | eprintln!("usage: {} ", prog); 69 | process::exit(1); 70 | }; 71 | 72 | // Open scratch directory 73 | let dir_fd = match open_scratch_directory(&arg) { 74 | Ok(dir_fd) => dir_fd, 75 | Err(err) => { 76 | eprintln!("{}", err); 77 | process::exit(1) 78 | } 79 | }; 80 | 81 | // Run the tests. 82 | unsafe { test_file_truncation(dir_fd) } 83 | } 84 | -------------------------------------------------------------------------------- /test-programs/wasi-tests/src/bin/file_unbuffered_write.rs: -------------------------------------------------------------------------------- 1 | use std::{env, process}; 2 | use wasi_tests::open_scratch_directory; 3 | 4 | unsafe fn test_file_unbuffered_write(dir_fd: wasi::Fd) { 5 | // Create and open file for reading 6 | let fd_read = wasi::path_open( 7 | dir_fd, 8 | 0, 9 | "file", 10 | wasi::OFLAGS_CREAT, 11 | wasi::RIGHTS_FD_READ, 12 | 0, 13 | 0, 14 | ) 15 | .expect("create and open file for reading"); 16 | assert!( 17 | fd_read > libc::STDERR_FILENO as wasi::Fd, 18 | "file descriptor range check", 19 | ); 20 | 21 | // Open the same file but for writing 22 | let fd_write = wasi::path_open(dir_fd, 0, "file", 0, wasi::RIGHTS_FD_WRITE, 0, 0) 23 | .expect("opening file for writing"); 24 | assert!( 25 | fd_write > libc::STDERR_FILENO as wasi::Fd, 26 | "file descriptor range check", 27 | ); 28 | 29 | // Write to file 30 | let contents = &[1u8]; 31 | let ciovec = wasi::Ciovec { 32 | buf: contents.as_ptr() as *const _, 33 | buf_len: contents.len(), 34 | }; 35 | let nwritten = wasi::fd_write(fd_write, &[ciovec]).expect("writing byte to file"); 36 | assert_eq!(nwritten, 1, "nwritten bytes check"); 37 | 38 | // Read from file 39 | let contents = &mut [0u8; 1]; 40 | let iovec = wasi::Iovec { 41 | buf: contents.as_mut_ptr() as *mut _, 42 | buf_len: contents.len(), 43 | }; 44 | let nread = wasi::fd_read(fd_read, &[iovec]).expect("reading bytes from file"); 45 | assert_eq!(nread, 1, "nread bytes check"); 46 | assert_eq!(contents, &[1u8], "written bytes equal read bytes"); 47 | 48 | // Clean up 49 | wasi::fd_close(fd_write).expect("closing a file"); 50 | wasi::fd_close(fd_read).expect("closing a file"); 51 | wasi::path_unlink_file(dir_fd, "file").expect("removing a file"); 52 | } 53 | fn main() { 54 | let mut args = env::args(); 55 | let prog = args.next().unwrap(); 56 | let arg = if let Some(arg) = args.next() { 57 | arg 58 | } else { 59 | eprintln!("usage: {} ", prog); 60 | process::exit(1); 61 | }; 62 | 63 | // Open scratch directory 64 | let dir_fd = match open_scratch_directory(&arg) { 65 | Ok(dir_fd) => dir_fd, 66 | Err(err) => { 67 | eprintln!("{}", err); 68 | process::exit(1) 69 | } 70 | }; 71 | 72 | // Run the tests. 73 | unsafe { test_file_unbuffered_write(dir_fd) } 74 | } 75 | -------------------------------------------------------------------------------- /test-programs/reactor-tests/wit/deps.lock: -------------------------------------------------------------------------------- 1 | [clocks] 2 | sha256 = "16155da02fdba4a51515b44dbd1c65e0909bb8c4d383bd32875c777044ce5389" 3 | sha512 = "30efd2697885954f3846322adf94f28a28f958595708407696ef66e8fe5bb62c79525e6f13ec469476cba8f2a12d3d9aef8250b1fe04c90a28542d3245db11f2" 4 | 5 | [filesystem] 6 | sha256 = "a3f57790dfd6af70758df33df82aa064f915eb795b71a190f272f10da9cfc9df" 7 | sha512 = "2b0059ffae0a4b1ce3bd8d489a40d08c1faa229bf1bb4cc8df0a98e0fd13d4694742117b2bc609d7fb75db1ed4aa148d3af7beca6a84866d87d65a1769b1d988" 8 | 9 | [http] 10 | sha256 = "b6a245b8f37f3be93650c1a8ae41c01923f3b7303bbf2d016d8d03a1c92b3c10" 11 | sha512 = "e2e0612213bb1272153b3858b13110a7fee0d89dc97d0021ab5ccccc8822ccc1f1629c8fa8dc582862411b2517ef3f79e7e51986c946654bf6ddd3f390cf36f2" 12 | 13 | [io] 14 | sha256 = "87d8bf336f37184edc9290af9a14f7cac38c8fe087a786e66d08d1a94e48d186" 15 | sha512 = "cdb5b35860c46c637ad37d784dd17f6023cf1c7c689d37973ec7c4094718baee4a16864e64622173b5578eed3950ad73211d9fe48a5b61b951809fdd9242b607" 16 | 17 | [logging] 18 | sha256 = "8c32c7f893c938e0b76cd42e3de97e268dfe8fd0e7525d1f42b7ad10a88b00bb" 19 | sha512 = "e9ce2adf02bb518c179a40b7d7af84bf44b99b2098609a897a114fb5af24946992dd7bd08da02c60d2768963f94414a16165f11343ec053a44b9e586058c812a" 20 | 21 | [poll] 22 | sha256 = "9f2e6b5ea1a017575f96a3c415c737fe31513b043d9b47aefeb21f6c27ab8425" 23 | sha512 = "f65965395742a0290fd9e4e55408c2b5ce3a4eae0ee22be1ff012e3db75910110da21c5f0a61083e043b4c510967a0a73ff4491ae9719e9158543f512dbeea5f" 24 | 25 | [preview] 26 | path = "../../../wit" 27 | sha256 = "007d4902193faef77b2df60714a8dfe8ec243e030c1ce61fb11c52788e3a5fe0" 28 | sha512 = "bd06e8338e7fad0fc2e56ae01c5b184282c66ca79d2040eaec5a11efb14a4002b2a6234baaf354aff3cd57dc99835e4979eb71cdbd5d12800ea289d9a13252f2" 29 | deps = ["clocks", "filesystem", "http", "io", "logging", "poll", "random", "sockets", "wasi-cli-base"] 30 | 31 | [random] 32 | sha256 = "19d57f2262530016ec2b32991db3d8b6705b3a0bc5a7b6ae8fff3bcef0cf1088" 33 | sha512 = "f9eba7dfd858d1edcaa868ce636f69545323bf010c3b0d4a2a1b23584d8027240c592d13e91882617e70a4dbf290754a8697724b5633147d11fa3db7869a2afe" 34 | 35 | [sockets] 36 | sha256 = "d5a51604d53eec88d2735356ce7089a0eeb06886262e7ad2fc74be6b4dd943b2" 37 | sha512 = "99bd86c0c59174bc9dafc559ca7dbab1aa9ed04a9b54ae3f7bb2d974b3a97bae8846acf1d77232edecda0aef5b23b9e4010d53ebf4a1d04292581078763530b7" 38 | 39 | [wasi-cli-base] 40 | sha256 = "a0beee69392d2b55baff0b5f2285f50f717473b1369bdb3fbbfbefbaa3bd6c86" 41 | sha512 = "d80eada98646bfeec1066437f1a4f69d67d117f38407f35c48d8b31667c171656059c5abf638c5b31169119ef5fa73bbf199933ac198f815f58281f124cb4f8d" 42 | -------------------------------------------------------------------------------- /wasi-common/src/preview2/clocks.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_variables)] 2 | 3 | use crate::preview2::poll::PollableEntry; 4 | use crate::wasi::{ 5 | monotonic_clock::{self, Instant}, 6 | poll::Pollable, 7 | timezone::{self, Timezone, TimezoneDisplay}, 8 | wall_clock::{self, Datetime}, 9 | }; 10 | use crate::WasiView; 11 | use cap_std::time::SystemTime; 12 | 13 | impl TryFrom for Datetime { 14 | type Error = anyhow::Error; 15 | 16 | fn try_from(time: SystemTime) -> Result { 17 | let duration = 18 | time.duration_since(SystemTime::from_std(std::time::SystemTime::UNIX_EPOCH))?; 19 | 20 | Ok(Datetime { 21 | seconds: duration.as_secs(), 22 | nanoseconds: duration.subsec_nanos(), 23 | }) 24 | } 25 | } 26 | 27 | #[async_trait::async_trait] 28 | impl wall_clock::Host for T { 29 | async fn now(&mut self) -> anyhow::Result { 30 | let now = self.ctx().clocks.wall.now(); 31 | Ok(Datetime { 32 | seconds: now.as_secs(), 33 | nanoseconds: now.subsec_nanos(), 34 | }) 35 | } 36 | 37 | async fn resolution(&mut self) -> anyhow::Result { 38 | let res = self.ctx().clocks.wall.resolution(); 39 | Ok(Datetime { 40 | seconds: res.as_secs(), 41 | nanoseconds: res.subsec_nanos(), 42 | }) 43 | } 44 | } 45 | 46 | #[async_trait::async_trait] 47 | impl monotonic_clock::Host for T { 48 | async fn now(&mut self) -> anyhow::Result { 49 | Ok(self.ctx().clocks.monotonic.now()) 50 | } 51 | 52 | async fn resolution(&mut self) -> anyhow::Result { 53 | Ok(self.ctx().clocks.monotonic.resolution()) 54 | } 55 | 56 | async fn subscribe(&mut self, when: Instant, absolute: bool) -> anyhow::Result { 57 | Ok(self 58 | .table_mut() 59 | .push(Box::new(PollableEntry::MonotonicClock(when, absolute)))?) 60 | } 61 | } 62 | 63 | #[async_trait::async_trait] 64 | impl timezone::Host for T { 65 | async fn display( 66 | &mut self, 67 | timezone: Timezone, 68 | when: Datetime, 69 | ) -> anyhow::Result { 70 | todo!() 71 | } 72 | 73 | async fn utc_offset(&mut self, timezone: Timezone, when: Datetime) -> anyhow::Result { 74 | todo!() 75 | } 76 | 77 | async fn drop_timezone(&mut self, timezone: Timezone) -> anyhow::Result<()> { 78 | todo!() 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /crates/wasi-preview1-component-adapter/wit/deps.lock: -------------------------------------------------------------------------------- 1 | [clocks] 2 | sha256 = "16155da02fdba4a51515b44dbd1c65e0909bb8c4d383bd32875c777044ce5389" 3 | sha512 = "30efd2697885954f3846322adf94f28a28f958595708407696ef66e8fe5bb62c79525e6f13ec469476cba8f2a12d3d9aef8250b1fe04c90a28542d3245db11f2" 4 | 5 | [filesystem] 6 | sha256 = "a3f57790dfd6af70758df33df82aa064f915eb795b71a190f272f10da9cfc9df" 7 | sha512 = "2b0059ffae0a4b1ce3bd8d489a40d08c1faa229bf1bb4cc8df0a98e0fd13d4694742117b2bc609d7fb75db1ed4aa148d3af7beca6a84866d87d65a1769b1d988" 8 | 9 | [http] 10 | sha256 = "b6a245b8f37f3be93650c1a8ae41c01923f3b7303bbf2d016d8d03a1c92b3c10" 11 | sha512 = "e2e0612213bb1272153b3858b13110a7fee0d89dc97d0021ab5ccccc8822ccc1f1629c8fa8dc582862411b2517ef3f79e7e51986c946654bf6ddd3f390cf36f2" 12 | 13 | [io] 14 | sha256 = "87d8bf336f37184edc9290af9a14f7cac38c8fe087a786e66d08d1a94e48d186" 15 | sha512 = "cdb5b35860c46c637ad37d784dd17f6023cf1c7c689d37973ec7c4094718baee4a16864e64622173b5578eed3950ad73211d9fe48a5b61b951809fdd9242b607" 16 | 17 | [logging] 18 | sha256 = "8c32c7f893c938e0b76cd42e3de97e268dfe8fd0e7525d1f42b7ad10a88b00bb" 19 | sha512 = "e9ce2adf02bb518c179a40b7d7af84bf44b99b2098609a897a114fb5af24946992dd7bd08da02c60d2768963f94414a16165f11343ec053a44b9e586058c812a" 20 | 21 | [poll] 22 | sha256 = "9f2e6b5ea1a017575f96a3c415c737fe31513b043d9b47aefeb21f6c27ab8425" 23 | sha512 = "f65965395742a0290fd9e4e55408c2b5ce3a4eae0ee22be1ff012e3db75910110da21c5f0a61083e043b4c510967a0a73ff4491ae9719e9158543f512dbeea5f" 24 | 25 | [preview] 26 | path = "../../../wit" 27 | sha256 = "007d4902193faef77b2df60714a8dfe8ec243e030c1ce61fb11c52788e3a5fe0" 28 | sha512 = "bd06e8338e7fad0fc2e56ae01c5b184282c66ca79d2040eaec5a11efb14a4002b2a6234baaf354aff3cd57dc99835e4979eb71cdbd5d12800ea289d9a13252f2" 29 | deps = ["clocks", "filesystem", "http", "io", "logging", "poll", "random", "sockets", "wasi-cli-base"] 30 | 31 | [random] 32 | sha256 = "19d57f2262530016ec2b32991db3d8b6705b3a0bc5a7b6ae8fff3bcef0cf1088" 33 | sha512 = "f9eba7dfd858d1edcaa868ce636f69545323bf010c3b0d4a2a1b23584d8027240c592d13e91882617e70a4dbf290754a8697724b5633147d11fa3db7869a2afe" 34 | 35 | [sockets] 36 | sha256 = "d5a51604d53eec88d2735356ce7089a0eeb06886262e7ad2fc74be6b4dd943b2" 37 | sha512 = "99bd86c0c59174bc9dafc559ca7dbab1aa9ed04a9b54ae3f7bb2d974b3a97bae8846acf1d77232edecda0aef5b23b9e4010d53ebf4a1d04292581078763530b7" 38 | 39 | [wasi-cli-base] 40 | sha256 = "a0beee69392d2b55baff0b5f2285f50f717473b1369bdb3fbbfbefbaa3bd6c86" 41 | sha512 = "d80eada98646bfeec1066437f1a4f69d67d117f38407f35c48d8b31667c171656059c5abf638c5b31169119ef5fa73bbf199933ac198f815f58281f124cb4f8d" 42 | -------------------------------------------------------------------------------- /test-programs/wasi-tests/src/bin/fd_filestat_set.rs: -------------------------------------------------------------------------------- 1 | use std::{env, process}; 2 | use wasi_tests::open_scratch_directory; 3 | 4 | unsafe fn test_fd_filestat_set(dir_fd: wasi::Fd) { 5 | // Create a file in the scratch directory. 6 | let file_fd = wasi::path_open( 7 | dir_fd, 8 | 0, 9 | "file", 10 | wasi::OFLAGS_CREAT, 11 | wasi::RIGHTS_FD_READ | wasi::RIGHTS_FD_WRITE, 12 | 0, 13 | 0, 14 | ) 15 | .expect("failed to create file"); 16 | assert!( 17 | file_fd > libc::STDERR_FILENO as wasi::Fd, 18 | "file descriptor range check", 19 | ); 20 | 21 | // Check file size 22 | let stat = wasi::fd_filestat_get(file_fd).expect("failed filestat"); 23 | assert_eq!(stat.size, 0, "file size should be 0"); 24 | 25 | // Check fd_filestat_set_size 26 | wasi::fd_filestat_set_size(file_fd, 100).expect("fd_filestat_set_size"); 27 | 28 | let stat = wasi::fd_filestat_get(file_fd).expect("failed filestat 2"); 29 | assert_eq!(stat.size, 100, "file size should be 100"); 30 | 31 | // Check fd_filestat_set_times 32 | let old_atim = stat.atim; 33 | let new_mtim = stat.mtim - 100; 34 | wasi::fd_filestat_set_times(file_fd, new_mtim, new_mtim, wasi::FSTFLAGS_MTIM) 35 | .expect("fd_filestat_set_times"); 36 | 37 | let stat = wasi::fd_filestat_get(file_fd).expect("failed filestat 3"); 38 | assert_eq!(stat.size, 100, "file size should remain unchanged at 100"); 39 | assert_eq!(stat.mtim, new_mtim, "mtim should change"); 40 | assert_eq!(stat.atim, old_atim, "atim should not change"); 41 | 42 | // let status = wasi_fd_filestat_set_times(file_fd, new_mtim, new_mtim, wasi::FILESTAT_SET_MTIM | wasi::FILESTAT_SET_MTIM_NOW); 43 | // assert_eq!(status, wasi::EINVAL, "ATIM & ATIM_NOW can't both be set"); 44 | 45 | wasi::fd_close(file_fd).expect("failed to close fd"); 46 | wasi::path_unlink_file(dir_fd, "file").expect("failed to remove dir"); 47 | } 48 | fn main() { 49 | let mut args = env::args(); 50 | let prog = args.next().unwrap(); 51 | let arg = if let Some(arg) = args.next() { 52 | arg 53 | } else { 54 | eprintln!("usage: {} ", prog); 55 | process::exit(1); 56 | }; 57 | 58 | // Open scratch directory 59 | let dir_fd = match open_scratch_directory(&arg) { 60 | Ok(dir_fd) => dir_fd, 61 | Err(err) => { 62 | eprintln!("{}", err); 63 | process::exit(1) 64 | } 65 | }; 66 | 67 | // Run the tests. 68 | unsafe { test_fd_filestat_set(dir_fd) } 69 | } 70 | -------------------------------------------------------------------------------- /wasi-sockets/src/network_impl.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | wasi::instance_network, 3 | wasi::network::{self, Network}, 4 | WasiNetwork, WasiSocketsView, 5 | }; 6 | use cap_std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; 7 | 8 | #[async_trait::async_trait] 9 | impl network::Host for T { 10 | async fn drop_network(&mut self, this: Network) -> anyhow::Result<()> { 11 | let table = self.table_mut(); 12 | if !table.delete::>(this).is_ok() { 13 | anyhow::bail!("{this} is not a network"); 14 | } 15 | Ok(()) 16 | } 17 | } 18 | 19 | #[async_trait::async_trait] 20 | impl instance_network::Host for T { 21 | async fn instance_network(&mut self) -> anyhow::Result { 22 | let ctx = self.ctx(); 23 | let network = (ctx.network_creator)(ctx.pool.clone())?; 24 | let table = self.table_mut(); 25 | let network = table.push(Box::new(network))?; 26 | Ok(network) 27 | } 28 | } 29 | 30 | impl From for network::IpSocketAddress { 31 | fn from(addr: SocketAddr) -> Self { 32 | match addr { 33 | SocketAddr::V4(v4) => Self::Ipv4(v4.into()), 34 | SocketAddr::V6(v6) => Self::Ipv6(v6.into()), 35 | } 36 | } 37 | } 38 | 39 | impl From for network::Ipv4SocketAddress { 40 | fn from(addr: SocketAddrV4) -> Self { 41 | Self { 42 | address: MyIpv4Addr::from(addr.ip()).0, 43 | port: addr.port(), 44 | } 45 | } 46 | } 47 | 48 | impl From for network::Ipv6SocketAddress { 49 | fn from(addr: SocketAddrV6) -> Self { 50 | Self { 51 | address: MyIpv6Addr::from(addr.ip()).0, 52 | port: addr.port(), 53 | flow_info: addr.flowinfo(), 54 | scope_id: addr.scope_id(), 55 | } 56 | } 57 | } 58 | 59 | // Newtypes to guide conversions. 60 | struct MyIpv4Addr((u8, u8, u8, u8)); 61 | struct MyIpv6Addr((u16, u16, u16, u16, u16, u16, u16, u16)); 62 | 63 | impl From<&Ipv4Addr> for MyIpv4Addr { 64 | fn from(addr: &Ipv4Addr) -> Self { 65 | let octets = addr.octets(); 66 | Self((octets[0], octets[1], octets[2], octets[3])) 67 | } 68 | } 69 | 70 | impl From<&Ipv6Addr> for MyIpv6Addr { 71 | fn from(addr: &Ipv6Addr) -> Self { 72 | let segments = addr.segments(); 73 | Self(( 74 | segments[0], 75 | segments[1], 76 | segments[2], 77 | segments[3], 78 | segments[4], 79 | segments[5], 80 | segments[6], 81 | segments[7], 82 | )) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /wasi-common/README.md: -------------------------------------------------------------------------------- 1 |
2 |

wasi-common

3 | 4 | A Bytecode Alliance project 5 | 6 |

7 | A library providing a common implementation of WASI hostcalls for re-use in any WASI-enabled runtime. 8 |

9 | 10 |

11 | Crates.io version 12 | Download 13 | docs.rs docs 14 |

15 |
16 | 17 | The `wasi-common` crate will ultimately serve as a library providing a common implementation of 18 | WASI hostcalls for re-use in any WASI (and potentially non-WASI) runtimes 19 | such as [Wasmtime] and [Lucet]. 20 | 21 | The library is an adaption of [lucet-wasi] crate from the [Lucet] project, and it is 22 | currently based on [40ae1df][lucet-wasi-tracker] git revision. 23 | 24 | Please note that the library requires Rust compiler version at least 1.37.0. 25 | 26 | [Wasmtime]: https://github.com/bytecodealliance/wasmtime 27 | [Lucet]: https://github.com/fastly/lucet 28 | [lucet-wasi]: https://github.com/fastly/lucet/tree/master/lucet-wasi 29 | [lucet-wasi-tracker]: https://github.com/fastly/lucet/commit/40ae1df64536250a2b6ab67e7f167d22f4aa7f94 30 | 31 | ## Supported syscalls 32 | 33 | ### *nix 34 | In our *nix implementation, we currently support the entire [WASI API] 35 | with the exception of the `proc_raise` hostcall, as it is expected to 36 | be dropped entirely from WASI. 37 | 38 | [WASI API]: https://github.com/WebAssembly/WASI/blob/master/phases/snapshot/docs.md 39 | 40 | ### Windows 41 | In our Windows implementation, we currently support the minimal subset of [WASI API] 42 | which allows for running the very basic "Hello world!" style WASM apps. More coming shortly, 43 | so stay tuned! 44 | 45 | ## Development hints 46 | When testing the crate, you may want to enable and run full wasm32 integration testsuite. This 47 | requires `wasm32-wasi` target installed which can be done as follows using [rustup] 48 | 49 | ``` 50 | rustup target add wasm32-wasi 51 | ``` 52 | 53 | [rustup]: https://rustup.rs 54 | 55 | Now, you should be able to run the integration testsuite by running `cargo test` on the 56 | `test-programs` package with `test-programs/test_programs` feature enabled: 57 | 58 | ``` 59 | cargo test --features test-programs/test_programs --package test-programs 60 | ``` 61 | 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # preview2-prototyping 2 | 3 | ## This repository will soon be retired! 4 | 5 | This repository was home to the prototype implementation of Wasmtime's WASI 6 | Preview 2 support for 7 months. We have now landed almost all of this 7 | repository upstream: 8 | 9 | * [Wasmtime issue 10 | #6370](https://github.com/bytecodealliance/wasmtime/issues/6370) describes 11 | the transition plan, and tracks its progress. 12 | 13 | * The component adapter, which used to live at the root of this repo and now 14 | lives at `crates/wasi-preview1-component-adapter`, has a new home at 15 | [`wasmtime/crates/wasi-preview1-component-adapter`](https://github.com/bytecodealliance/wasmtime/tree/main/crates/wasi-preview1-component-adapter). 16 | 17 | * Published binaries of the component adapter can now be found in the 18 | [Wasmtime `dev` tag 19 | assets](https://github.com/bytecodealliance/wasmtime/releases/tag/dev) 20 | under [`wasi_preview1_component_adapter.command.wasm`](https://github.com/bytecodealliance/wasmtime/releases/download/dev/wasi_preview1_component_adapter.command.wasm) 21 | and [`wasi_preview1_component_adapter.reactor.wasm`](https://github.com/bytecodealliance/wasmtime/releases/download/dev/wasi_preview1_component_adapter.reactor.wasm). 22 | 23 | * If you are invoking `wasm-tools component new` with the component adapter, 24 | you now need to specify the name to adapt is `wasi_snapshot_preview1`, e.g. 25 | `wasm-tools component new --adapt wasi_snapshot_preview1=./wasi_preview1_component_adapter.command.wasm` 26 | 27 | * The host implementation of WASI Preview 2 was found in this repository's 28 | `wasi-common` crate. The new home is in the 29 | [`wasmtime-wasi` crate under the `preview2` module](https://github.com/bytecodealliance/wasmtime/tree/main/crates/wasi/src/preview2). 30 | 31 | * If you were vendoring in the `wasi-common` from this repository, change your 32 | source code's `use wasi_common::{WasiCtxBuilder, WasiView, wasi::Command}` 33 | to `use wasmtime_wasi::preview2::{WasiCtxBuilder, WasiView, wasi::Command}` 34 | to switch to the new implementation. 35 | 36 | * If you are looking for the tests found under this repository's `host/tests`, 37 | those now live at 38 | [`wasmtime/crates/test-programs/tests`](https://github.com/bytecodealliance/wasmtime/tree/main/crates/test-programs/tests). 39 | 40 | * If you are looking for the contents of this repository's `test-programs`, 41 | those now live under 42 | [`wasmtime/crates/test-programs`](https://github.com/bytecodealliance/wasmtime/tree/main/crates/test-programs). 43 | 44 | 45 | ## Still active in this repository 46 | 47 | The `wasi-sockets` implementation in this repository is still a work in 48 | progress. It will land in Wasmtime in the future, but still lives here 49 | for the time being. 50 | -------------------------------------------------------------------------------- /wit/deps/clocks/timezone.wit: -------------------------------------------------------------------------------- 1 | default interface timezone { 2 | use pkg.wall-clock.{datetime} 3 | 4 | /// A timezone. 5 | /// 6 | /// In timezones that recognize daylight saving time, also known as daylight 7 | /// time and summer time, the information returned from the functions varies 8 | /// over time to reflect these adjustments. 9 | /// 10 | /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). 11 | type timezone = u32 12 | 13 | /// Return information needed to display the given `datetime`. This includes 14 | /// the UTC offset, the time zone name, and a flag indicating whether 15 | /// daylight saving time is active. 16 | /// 17 | /// If the timezone cannot be determined for the given `datetime`, return a 18 | /// `timezone-display` for `UTC` with a `utc-offset` of 0 and no daylight 19 | /// saving time. 20 | display: func(this: timezone, when: datetime) -> timezone-display 21 | 22 | /// The same as `display`, but only return the UTC offset. 23 | utc-offset: func(this: timezone, when: datetime) -> s32 24 | 25 | /// Dispose of the specified input-stream, after which it may no longer 26 | /// be used. 27 | drop-timezone: func(this: timezone) 28 | 29 | /// Information useful for displaying the timezone of a specific `datetime`. 30 | /// 31 | /// This information may vary within a single `timezone` to reflect daylight 32 | /// saving time adjustments. 33 | record timezone-display { 34 | /// The number of seconds difference between UTC time and the local 35 | /// time of the timezone. 36 | /// 37 | /// The returned value will always be less than 86400 which is the 38 | /// number of seconds in a day (24*60*60). 39 | /// 40 | /// In implementations that do not expose an actual time zone, this 41 | /// should return 0. 42 | utc-offset: s32, 43 | 44 | /// The abbreviated name of the timezone to display to a user. The name 45 | /// `UTC` indicates Coordinated Universal Time. Otherwise, this should 46 | /// reference local standards for the name of the time zone. 47 | /// 48 | /// In implementations that do not expose an actual time zone, this 49 | /// should be the string `UTC`. 50 | /// 51 | /// In time zones that do not have an applicable name, a formatted 52 | /// representation of the UTC offset may be returned, such as `-04:00`. 53 | name: string, 54 | 55 | /// Whether daylight saving time is active. 56 | /// 57 | /// In implementations that do not expose an actual time zone, this 58 | /// should return false. 59 | in-daylight-saving-time: bool, 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /test-programs/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use quote::quote; 3 | 4 | include!(concat!(env!("OUT_DIR"), "/components.rs")); 5 | 6 | fn compiled_components(components: &[(&str, &str)]) -> TokenStream { 7 | let globals = components.iter().map(|(stem, file)| { 8 | let global = quote::format_ident!("{}_COMPONENT", stem.to_uppercase()); 9 | quote! { 10 | lazy_static::lazy_static! { 11 | static ref #global: wasmtime::component::Component = { 12 | wasmtime::component::Component::from_file(&ENGINE, #file).unwrap() 13 | }; 14 | } 15 | } 16 | }); 17 | 18 | let cases = components.iter().map(|(stem, _file)| { 19 | let global = quote::format_ident!("{}_COMPONENT", stem.to_uppercase()); 20 | quote! { 21 | #stem => #global.clone() 22 | } 23 | }); 24 | quote! { 25 | #(#globals)* 26 | fn get_component(s: &str) -> wasmtime::component::Component { 27 | match s { 28 | #(#cases),*, 29 | _ => panic!("no such component: {}", s), 30 | } 31 | } 32 | } 33 | .into() 34 | } 35 | 36 | fn compiled_modules(modules: &[(&str, &str)]) -> TokenStream { 37 | let globals = modules.iter().map(|(stem, file)| { 38 | let global = quote::format_ident!("{}_MODULE", stem.to_uppercase()); 39 | quote! { 40 | lazy_static::lazy_static! { 41 | static ref #global: wasmtime::Module = { 42 | wasmtime::Module::from_file(&ENGINE, #file).unwrap() 43 | }; 44 | } 45 | } 46 | }); 47 | 48 | let cases = modules.iter().map(|(stem, _file)| { 49 | let global = quote::format_ident!("{}_MODULE", stem.to_uppercase()); 50 | quote! { 51 | #stem => #global.clone() 52 | } 53 | }); 54 | quote! { 55 | #(#globals)* 56 | fn get_module(s: &str) -> wasmtime::Module { 57 | match s { 58 | #(#cases),*, 59 | _ => panic!("no such component: {}", s), 60 | } 61 | } 62 | } 63 | .into() 64 | } 65 | 66 | #[proc_macro] 67 | pub fn command_tests_components(_input: TokenStream) -> TokenStream { 68 | compiled_components(COMMAND_TESTS_COMPONENTS) 69 | } 70 | 71 | #[proc_macro] 72 | pub fn wasi_tests_modules(_input: TokenStream) -> TokenStream { 73 | compiled_modules(WASI_TESTS_MODULES) 74 | } 75 | 76 | #[proc_macro] 77 | pub fn wasi_tests_components(_input: TokenStream) -> TokenStream { 78 | compiled_components(WASI_TESTS_COMPONENTS) 79 | } 80 | 81 | #[proc_macro] 82 | pub fn reactor_tests_components(_input: TokenStream) -> TokenStream { 83 | compiled_components(REACTOR_TESTS_COMPONENTS) 84 | } 85 | -------------------------------------------------------------------------------- /wasi/wit/deps/clocks/timezone.wit: -------------------------------------------------------------------------------- 1 | package wasi:clocks 2 | 3 | interface timezone { 4 | use wall-clock.{datetime} 5 | 6 | /// A timezone. 7 | /// 8 | /// In timezones that recognize daylight saving time, also known as daylight 9 | /// time and summer time, the information returned from the functions varies 10 | /// over time to reflect these adjustments. 11 | /// 12 | /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). 13 | type timezone = u32 14 | 15 | /// Return information needed to display the given `datetime`. This includes 16 | /// the UTC offset, the time zone name, and a flag indicating whether 17 | /// daylight saving time is active. 18 | /// 19 | /// If the timezone cannot be determined for the given `datetime`, return a 20 | /// `timezone-display` for `UTC` with a `utc-offset` of 0 and no daylight 21 | /// saving time. 22 | display: func(this: timezone, when: datetime) -> timezone-display 23 | 24 | /// The same as `display`, but only return the UTC offset. 25 | utc-offset: func(this: timezone, when: datetime) -> s32 26 | 27 | /// Dispose of the specified input-stream, after which it may no longer 28 | /// be used. 29 | drop-timezone: func(this: timezone) 30 | 31 | /// Information useful for displaying the timezone of a specific `datetime`. 32 | /// 33 | /// This information may vary within a single `timezone` to reflect daylight 34 | /// saving time adjustments. 35 | record timezone-display { 36 | /// The number of seconds difference between UTC time and the local 37 | /// time of the timezone. 38 | /// 39 | /// The returned value will always be less than 86400 which is the 40 | /// number of seconds in a day (24*60*60). 41 | /// 42 | /// In implementations that do not expose an actual time zone, this 43 | /// should return 0. 44 | utc-offset: s32, 45 | 46 | /// The abbreviated name of the timezone to display to a user. The name 47 | /// `UTC` indicates Coordinated Universal Time. Otherwise, this should 48 | /// reference local standards for the name of the time zone. 49 | /// 50 | /// In implementations that do not expose an actual time zone, this 51 | /// should be the string `UTC`. 52 | /// 53 | /// In time zones that do not have an applicable name, a formatted 54 | /// representation of the UTC offset may be returned, such as `-04:00`. 55 | name: string, 56 | 57 | /// Whether daylight saving time is active. 58 | /// 59 | /// In implementations that do not expose an actual time zone, this 60 | /// should return false. 61 | in-daylight-saving-time: bool, 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /test-programs/reactor-tests/wit/deps/clocks/timezone.wit: -------------------------------------------------------------------------------- 1 | default interface timezone { 2 | use pkg.wall-clock.{datetime} 3 | 4 | /// A timezone. 5 | /// 6 | /// In timezones that recognize daylight saving time, also known as daylight 7 | /// time and summer time, the information returned from the functions varies 8 | /// over time to reflect these adjustments. 9 | /// 10 | /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). 11 | type timezone = u32 12 | 13 | /// Return information needed to display the given `datetime`. This includes 14 | /// the UTC offset, the time zone name, and a flag indicating whether 15 | /// daylight saving time is active. 16 | /// 17 | /// If the timezone cannot be determined for the given `datetime`, return a 18 | /// `timezone-display` for `UTC` with a `utc-offset` of 0 and no daylight 19 | /// saving time. 20 | display: func(this: timezone, when: datetime) -> timezone-display 21 | 22 | /// The same as `display`, but only return the UTC offset. 23 | utc-offset: func(this: timezone, when: datetime) -> s32 24 | 25 | /// Dispose of the specified input-stream, after which it may no longer 26 | /// be used. 27 | drop-timezone: func(this: timezone) 28 | 29 | /// Information useful for displaying the timezone of a specific `datetime`. 30 | /// 31 | /// This information may vary within a single `timezone` to reflect daylight 32 | /// saving time adjustments. 33 | record timezone-display { 34 | /// The number of seconds difference between UTC time and the local 35 | /// time of the timezone. 36 | /// 37 | /// The returned value will always be less than 86400 which is the 38 | /// number of seconds in a day (24*60*60). 39 | /// 40 | /// In implementations that do not expose an actual time zone, this 41 | /// should return 0. 42 | utc-offset: s32, 43 | 44 | /// The abbreviated name of the timezone to display to a user. The name 45 | /// `UTC` indicates Coordinated Universal Time. Otherwise, this should 46 | /// reference local standards for the name of the time zone. 47 | /// 48 | /// In implementations that do not expose an actual time zone, this 49 | /// should be the string `UTC`. 50 | /// 51 | /// In time zones that do not have an applicable name, a formatted 52 | /// representation of the UTC offset may be returned, such as `-04:00`. 53 | name: string, 54 | 55 | /// Whether daylight saving time is active. 56 | /// 57 | /// In implementations that do not expose an actual time zone, this 58 | /// should return false. 59 | in-daylight-saving-time: bool, 60 | } 61 | } 62 | --------------------------------------------------------------------------------