kobako 0.28.0 → 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.release-please-manifest.json +1 -1
- data/CHANGELOG.md +170 -0
- data/Cargo.lock +111 -82
- data/README.md +154 -36
- data/ROADMAP.md +5 -0
- data/SECURITY.md +1 -1
- data/crates/kobako-runtime/CHANGELOG.md +86 -0
- data/crates/kobako-runtime/Cargo.toml +27 -6
- data/crates/kobako-runtime/README.md +13 -8
- data/crates/kobako-runtime/src/dispatch.rs +8 -0
- data/crates/kobako-runtime/src/filesystem/directory/spelling.rs +194 -0
- data/crates/kobako-runtime/src/filesystem/directory.rs +1082 -0
- data/crates/kobako-runtime/src/filesystem/memory.rs +1100 -0
- data/crates/kobako-runtime/src/filesystem/overlay/exclusive.rs +88 -0
- data/crates/kobako-runtime/src/filesystem/overlay/stack.rs +808 -0
- data/crates/kobako-runtime/src/filesystem/overlay.rs +1955 -0
- data/crates/kobako-runtime/src/filesystem/path.rs +60 -0
- data/crates/kobako-runtime/src/filesystem/temporary.rs +167 -0
- data/crates/kobako-runtime/src/filesystem.rs +392 -0
- data/crates/kobako-runtime/src/grant.rs +20 -0
- data/crates/kobako-runtime/src/lib.rs +11 -1
- data/crates/kobako-runtime/src/mount.rs +178 -0
- data/crates/kobako-runtime/src/profile.rs +4 -4
- data/crates/kobako-runtime/src/runtime.rs +12 -0
- data/crates/kobako-runtime/src/snapshot.rs +20 -5
- data/crates/kobako-runtime/src/yielder.rs +11 -6
- data/crates/kobako-transport/CHANGELOG.md +17 -0
- data/crates/kobako-transport/Cargo.toml +1 -1
- data/crates/kobako-transport/README.md +1 -1
- data/crates/kobako-transport/src/abi.rs +1 -1
- data/crates/kobako-transport/src/envelope/fault.rs +2 -1
- data/crates/kobako-transport/src/envelope/preamble.rs +12 -12
- data/crates/kobako-transport/src/envelope/reply.rs +32 -14
- data/crates/kobako-transport/src/envelope/request.rs +18 -8
- data/crates/kobako-wasmtime/CHANGELOG.md +119 -0
- data/crates/kobako-wasmtime/Cargo.toml +21 -16
- data/crates/kobako-wasmtime/README.md +5 -3
- data/crates/kobako-wasmtime/src/abi.rs +66 -32
- data/crates/kobako-wasmtime/src/ambient.rs +63 -4
- data/crates/kobako-wasmtime/src/cache.rs +185 -74
- data/crates/kobako-wasmtime/src/config.rs +58 -1
- data/crates/kobako-wasmtime/src/descriptors.rs +167 -0
- data/crates/kobako-wasmtime/src/dispatch.rs +47 -29
- data/crates/kobako-wasmtime/src/driver.rs +46 -44
- data/crates/kobako-wasmtime/src/guest_mem.rs +13 -7
- data/crates/kobako-wasmtime/src/imports.rs +46 -0
- data/crates/kobako-wasmtime/src/instance_pre.rs +17 -45
- data/crates/kobako-wasmtime/src/invocation.rs +35 -46
- data/crates/kobako-wasmtime/src/io.rs +5 -202
- data/crates/kobako-wasmtime/src/lib.rs +4 -0
- data/crates/kobako-wasmtime/src/preview1/args.rs +60 -0
- data/crates/kobako-wasmtime/src/preview1/beneath.rs +188 -0
- data/crates/kobako-wasmtime/src/preview1/differential/mount.rs +609 -0
- data/crates/kobako-wasmtime/src/preview1/differential.rs +372 -0
- data/crates/kobako-wasmtime/src/preview1/errno.rs +267 -0
- data/crates/kobako-wasmtime/src/preview1/fd.rs +672 -0
- data/crates/kobako-wasmtime/src/preview1/memory.rs +205 -0
- data/crates/kobako-wasmtime/src/preview1/mount_view.rs +2499 -0
- data/crates/kobako-wasmtime/src/preview1/path.rs +421 -0
- data/crates/kobako-wasmtime/src/preview1/poll.rs +333 -0
- data/crates/kobako-wasmtime/src/preview1/sock.rs +30 -0
- data/crates/kobako-wasmtime/src/preview1.rs +461 -0
- data/crates/kobako-wasmtime/src/trap.rs +39 -19
- data/crates/kobako-wasmtime/src/wasi.rs +317 -0
- data/crates/kobako-wasmtime/tests/compile_posture.rs +43 -0
- data/crates/kobako-wasmtime/tests/guest_file.rs +1054 -0
- data/crates/kobako-wasmtime/tests/preamble_flags.rs +3 -2
- data/crates/kobako-wasmtime/tests/snippet_compile.rs +4 -3
- data/crates/kobako-wasmtime/tests/support/mod.rs +9 -13
- data/crates/kobako-wasmtime/tests/unanswered_call.rs +86 -0
- data/crates/kobako-wasmtime/tests/unsupported_call.rs +1 -0
- data/data/kobako.wasm +0 -0
- data/ext/kobako/Cargo.toml +4 -2
- data/ext/kobako/src/runtime/bridge.rs +154 -81
- data/ext/kobako/src/runtime/escape.rs +53 -0
- data/ext/kobako/src/runtime/filesystem/grants.rs +91 -0
- data/ext/kobako/src/runtime/filesystem/ruby.rs +400 -0
- data/ext/kobako/src/runtime/filesystem.rs +236 -0
- data/ext/kobako/src/runtime/gvl.rs +7 -1
- data/ext/kobako/src/runtime/pinned_value.rs +31 -0
- data/ext/kobako/src/runtime/snapshot.rs +127 -0
- data/ext/kobako/src/runtime.rs +120 -199
- data/lib/kobako/catalog/flags.rb +2 -14
- data/lib/kobako/catalog/grants.rb +30 -0
- data/lib/kobako/catalog/mount.rb +27 -0
- data/lib/kobako/catalog/services.rb +11 -0
- data/lib/kobako/catalog/snippets.rb +5 -9
- data/lib/kobako/catalog.rb +11 -0
- data/lib/kobako/codec/decoder.rb +1 -0
- data/lib/kobako/codec/ext_types.rb +1 -1
- data/lib/kobako/codec/state.rb +19 -3
- data/lib/kobako/codec/value_budget.rb +86 -0
- data/lib/kobako/codec.rb +7 -0
- data/lib/kobako/context.rb +38 -11
- data/lib/kobako/errors.rb +15 -14
- data/lib/kobako/filesystem.rb +38 -0
- data/lib/kobako/pool.rb +2 -2
- data/lib/kobako/sandbox.rb +40 -12
- data/lib/kobako/sandbox_options.rb +39 -12
- data/lib/kobako/snippet/{binary.rb → bytecode.rb} +1 -1
- data/lib/kobako/snippet.rb +1 -1
- data/lib/kobako/transport/block_failure.rb +35 -0
- data/lib/kobako/transport/dispatcher.rb +30 -26
- data/lib/kobako/transport/exposure.rb +5 -7
- data/lib/kobako/transport/reflection.rb +79 -23
- data/lib/kobako/transport/yielder.rb +8 -30
- data/lib/kobako/transport.rb +1 -0
- data/lib/kobako/version.rb +1 -1
- data/lib/kobako.rb +1 -0
- data/sig/kobako/catalog/flags.rbs +0 -4
- data/sig/kobako/catalog/grants.rbs +13 -0
- data/sig/kobako/catalog/mount.rbs +28 -0
- data/sig/kobako/catalog/services.rbs +4 -0
- data/sig/kobako/catalog/snippets.rbs +1 -1
- data/sig/kobako/catalog.rbs +1 -0
- data/sig/kobako/codec/state.rbs +7 -1
- data/sig/kobako/codec/value_budget.rbs +23 -0
- data/sig/kobako/codec.rbs +2 -0
- data/sig/kobako/context.rbs +14 -6
- data/sig/kobako/errors.rbs +4 -4
- data/sig/kobako/filesystem.rbs +88 -0
- data/sig/kobako/guest_yielder.rbs +9 -0
- data/sig/kobako/pool.rbs +1 -1
- data/sig/kobako/runtime.rbs +21 -49
- data/sig/kobako/sandbox.rbs +7 -1
- data/sig/kobako/sandbox_options.rbs +24 -2
- data/sig/kobako/snapshot.rbs +20 -0
- data/sig/kobako/snippet/bytecode.rbs +12 -0
- data/sig/kobako/transport/block_failure.rbs +13 -0
- data/sig/kobako/transport/dispatcher.rbs +3 -3
- data/sig/kobako/transport/exposure.rbs +1 -1
- data/sig/kobako/transport/reflection.rbs +14 -1
- data/sig/kobako/transport/yielder.rbs +3 -7
- data/sig/kobako/transport.rbs +2 -2
- metadata +60 -7
- data/sig/kobako/snippet/binary.rbs +0 -12
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
//! The differential probe: every call kobako answers, made on descriptors
|
|
2
|
+
//! 0 to 3 against kobako's linker and against plain wasmtime-wasi preview1,
|
|
3
|
+
//! must leave the same errno, the same guest memory, and the same captures.
|
|
4
|
+
//!
|
|
5
|
+
//! It guards each wasmtime-wasi upgrade: a change in what preview1 answers
|
|
6
|
+
//! shows up here rather than in a guest. The deliberate differences — a
|
|
7
|
+
//! clock wait under the hermetic rung — are witnessed in `poll`.
|
|
8
|
+
|
|
9
|
+
mod mount;
|
|
10
|
+
|
|
11
|
+
use std::sync::OnceLock;
|
|
12
|
+
|
|
13
|
+
use wasmtime::{Linker, Module, Store};
|
|
14
|
+
use wasmtime_wasi::p1::{self, WasiP1Ctx};
|
|
15
|
+
use wasmtime_wasi::p2::pipe::{MemoryInputPipe, MemoryOutputPipe};
|
|
16
|
+
use wasmtime_wasi::WasiCtxBuilder;
|
|
17
|
+
|
|
18
|
+
use crate::cache::shared_engine;
|
|
19
|
+
use crate::config::Config;
|
|
20
|
+
use crate::imports;
|
|
21
|
+
use crate::invocation::Invocation;
|
|
22
|
+
use crate::trap::NO_TIMEOUT_EPOCH_DELTA;
|
|
23
|
+
use crate::wasi::Wasi;
|
|
24
|
+
use kobako_runtime::profile::Profile;
|
|
25
|
+
|
|
26
|
+
/// The Preamble bytes stdin carries after its length prefix.
|
|
27
|
+
const PREAMBLE: &[u8] = b"hello";
|
|
28
|
+
|
|
29
|
+
/// Every export takes a descriptor and answers the call's errno. Memory
|
|
30
|
+
/// holds an `iovec` at 64 over 512..528, an empty one at 72, a `ciovec` at 80 over "hello" at
|
|
31
|
+
/// 600, and the paths "a" and "b" at 700 and 702; results land at 128 and
|
|
32
|
+
/// 256, subscriptions are built at 800 and events written at 896.
|
|
33
|
+
const PROBE_WAT: &str = r#"
|
|
34
|
+
(module
|
|
35
|
+
(import "wasi_snapshot_preview1" "fd_advise" (func $fd_advise (param i32 i64 i64 i32) (result i32)))
|
|
36
|
+
(import "wasi_snapshot_preview1" "fd_allocate" (func $fd_allocate (param i32 i64 i64) (result i32)))
|
|
37
|
+
(import "wasi_snapshot_preview1" "fd_close" (func $fd_close (param i32) (result i32)))
|
|
38
|
+
(import "wasi_snapshot_preview1" "fd_datasync" (func $fd_datasync (param i32) (result i32)))
|
|
39
|
+
(import "wasi_snapshot_preview1" "fd_fdstat_get" (func $fd_fdstat_get (param i32 i32) (result i32)))
|
|
40
|
+
(import "wasi_snapshot_preview1" "fd_fdstat_set_flags" (func $fd_fdstat_set_flags (param i32 i32) (result i32)))
|
|
41
|
+
(import "wasi_snapshot_preview1" "fd_fdstat_set_rights" (func $fd_fdstat_set_rights (param i32 i64 i64) (result i32)))
|
|
42
|
+
(import "wasi_snapshot_preview1" "fd_filestat_get" (func $fd_filestat_get (param i32 i32) (result i32)))
|
|
43
|
+
(import "wasi_snapshot_preview1" "fd_filestat_set_size" (func $fd_filestat_set_size (param i32 i64) (result i32)))
|
|
44
|
+
(import "wasi_snapshot_preview1" "fd_filestat_set_times" (func $fd_filestat_set_times (param i32 i64 i64 i32) (result i32)))
|
|
45
|
+
(import "wasi_snapshot_preview1" "fd_pread" (func $fd_pread (param i32 i32 i32 i64 i32) (result i32)))
|
|
46
|
+
(import "wasi_snapshot_preview1" "fd_prestat_get" (func $fd_prestat_get (param i32 i32) (result i32)))
|
|
47
|
+
(import "wasi_snapshot_preview1" "fd_prestat_dir_name" (func $fd_prestat_dir_name (param i32 i32 i32) (result i32)))
|
|
48
|
+
(import "wasi_snapshot_preview1" "fd_pwrite" (func $fd_pwrite (param i32 i32 i32 i64 i32) (result i32)))
|
|
49
|
+
(import "wasi_snapshot_preview1" "fd_read" (func $fd_read (param i32 i32 i32 i32) (result i32)))
|
|
50
|
+
(import "wasi_snapshot_preview1" "fd_readdir" (func $fd_readdir (param i32 i32 i32 i64 i32) (result i32)))
|
|
51
|
+
(import "wasi_snapshot_preview1" "fd_renumber" (func $fd_renumber (param i32 i32) (result i32)))
|
|
52
|
+
(import "wasi_snapshot_preview1" "fd_seek" (func $fd_seek (param i32 i64 i32 i32) (result i32)))
|
|
53
|
+
(import "wasi_snapshot_preview1" "fd_sync" (func $fd_sync (param i32) (result i32)))
|
|
54
|
+
(import "wasi_snapshot_preview1" "fd_tell" (func $fd_tell (param i32 i32) (result i32)))
|
|
55
|
+
(import "wasi_snapshot_preview1" "fd_write" (func $fd_write (param i32 i32 i32 i32) (result i32)))
|
|
56
|
+
(import "wasi_snapshot_preview1" "path_create_directory" (func $path_create_directory (param i32 i32 i32) (result i32)))
|
|
57
|
+
(import "wasi_snapshot_preview1" "path_filestat_get" (func $path_filestat_get (param i32 i32 i32 i32 i32) (result i32)))
|
|
58
|
+
(import "wasi_snapshot_preview1" "path_filestat_set_times" (func $path_filestat_set_times (param i32 i32 i32 i32 i64 i64 i32) (result i32)))
|
|
59
|
+
(import "wasi_snapshot_preview1" "path_link" (func $path_link (param i32 i32 i32 i32 i32 i32 i32) (result i32)))
|
|
60
|
+
(import "wasi_snapshot_preview1" "path_open" (func $path_open (param i32 i32 i32 i32 i32 i64 i64 i32 i32) (result i32)))
|
|
61
|
+
(import "wasi_snapshot_preview1" "path_readlink" (func $path_readlink (param i32 i32 i32 i32 i32 i32) (result i32)))
|
|
62
|
+
(import "wasi_snapshot_preview1" "path_remove_directory" (func $path_remove_directory (param i32 i32 i32) (result i32)))
|
|
63
|
+
(import "wasi_snapshot_preview1" "path_rename" (func $path_rename (param i32 i32 i32 i32 i32 i32) (result i32)))
|
|
64
|
+
(import "wasi_snapshot_preview1" "path_symlink" (func $path_symlink (param i32 i32 i32 i32 i32) (result i32)))
|
|
65
|
+
(import "wasi_snapshot_preview1" "path_unlink_file" (func $path_unlink_file (param i32 i32 i32) (result i32)))
|
|
66
|
+
(import "wasi_snapshot_preview1" "poll_oneoff" (func $poll_oneoff (param i32 i32 i32 i32) (result i32)))
|
|
67
|
+
(import "wasi_snapshot_preview1" "sock_accept" (func $sock_accept (param i32 i32 i32) (result i32)))
|
|
68
|
+
(import "wasi_snapshot_preview1" "sock_recv" (func $sock_recv (param i32 i32 i32 i32 i32 i32) (result i32)))
|
|
69
|
+
(import "wasi_snapshot_preview1" "sock_send" (func $sock_send (param i32 i32 i32 i32 i32) (result i32)))
|
|
70
|
+
(import "wasi_snapshot_preview1" "sock_shutdown" (func $sock_shutdown (param i32 i32) (result i32)))
|
|
71
|
+
(memory (export "memory") 1)
|
|
72
|
+
(data (i32.const 64) "\00\02\00\00\10\00\00\00")
|
|
73
|
+
(data (i32.const 72) "\00\02\00\00\00\00\00\00")
|
|
74
|
+
(data (i32.const 80) "\58\02\00\00\05\00\00\00")
|
|
75
|
+
(data (i32.const 88) "\00\00\02\00\10\00\00\00")
|
|
76
|
+
(data (i32.const 600) "hello")
|
|
77
|
+
(data (i32.const 700) "a\00b")
|
|
78
|
+
|
|
79
|
+
;; One subscription at 800: userdata 7, the tag, and the descriptor.
|
|
80
|
+
(func $subscribe (param $tag i32) (param $fd i32)
|
|
81
|
+
(i64.store (i32.const 800) (i64.const 7))
|
|
82
|
+
(i32.store8 (i32.const 808) (local.get $tag))
|
|
83
|
+
(i32.store (i32.const 816) (local.get $fd)))
|
|
84
|
+
|
|
85
|
+
(func (export "fd_advise") (param i32) (result i32) (call $fd_advise (local.get 0) (i64.const 0) (i64.const 0) (i32.const 0)))
|
|
86
|
+
(func (export "fd_allocate") (param i32) (result i32) (call $fd_allocate (local.get 0) (i64.const 0) (i64.const 0)))
|
|
87
|
+
(func (export "fd_close") (param i32) (result i32) (call $fd_close (local.get 0)))
|
|
88
|
+
(func (export "fd_datasync") (param i32) (result i32) (call $fd_datasync (local.get 0)))
|
|
89
|
+
(func (export "fd_fdstat_get") (param i32) (result i32) (call $fd_fdstat_get (local.get 0) (i32.const 256)))
|
|
90
|
+
(func (export "fd_fdstat_set_flags") (param i32) (result i32) (call $fd_fdstat_set_flags (local.get 0) (i32.const 0)))
|
|
91
|
+
(func (export "fd_fdstat_set_rights") (param i32) (result i32) (call $fd_fdstat_set_rights (local.get 0) (i64.const 0) (i64.const 0)))
|
|
92
|
+
(func (export "fd_filestat_get") (param i32) (result i32) (call $fd_filestat_get (local.get 0) (i32.const 256)))
|
|
93
|
+
(func (export "fd_filestat_set_size") (param i32) (result i32) (call $fd_filestat_set_size (local.get 0) (i64.const 0)))
|
|
94
|
+
(func (export "fd_filestat_set_times") (param i32) (result i32) (call $fd_filestat_set_times (local.get 0) (i64.const 0) (i64.const 0) (i32.const 0)))
|
|
95
|
+
(func (export "fd_pread") (param i32) (result i32) (call $fd_pread (local.get 0) (i32.const 64) (i32.const 1) (i64.const 0) (i32.const 128)))
|
|
96
|
+
(func (export "fd_prestat_get") (param i32) (result i32) (call $fd_prestat_get (local.get 0) (i32.const 256)))
|
|
97
|
+
(func (export "fd_prestat_dir_name") (param i32) (result i32) (call $fd_prestat_dir_name (local.get 0) (i32.const 512) (i32.const 16)))
|
|
98
|
+
(func (export "fd_pwrite") (param i32) (result i32) (call $fd_pwrite (local.get 0) (i32.const 80) (i32.const 1) (i64.const 0) (i32.const 128)))
|
|
99
|
+
(func (export "fd_read") (param i32) (result i32) (call $fd_read (local.get 0) (i32.const 64) (i32.const 1) (i32.const 128)))
|
|
100
|
+
(func (export "fd_readdir") (param i32) (result i32) (call $fd_readdir (local.get 0) (i32.const 512) (i32.const 64) (i64.const 0) (i32.const 128)))
|
|
101
|
+
(func (export "fd_renumber") (param i32) (result i32) (call $fd_renumber (local.get 0) (local.get 0)))
|
|
102
|
+
(func (export "fd_seek") (param i32) (result i32) (call $fd_seek (local.get 0) (i64.const 0) (i32.const 0) (i32.const 128)))
|
|
103
|
+
(func (export "fd_sync") (param i32) (result i32) (call $fd_sync (local.get 0)))
|
|
104
|
+
(func (export "fd_tell") (param i32) (result i32) (call $fd_tell (local.get 0) (i32.const 128)))
|
|
105
|
+
(func (export "fd_write") (param i32) (result i32) (call $fd_write (local.get 0) (i32.const 80) (i32.const 1) (i32.const 128)))
|
|
106
|
+
(func (export "path_create_directory") (param i32) (result i32) (call $path_create_directory (local.get 0) (i32.const 700) (i32.const 1)))
|
|
107
|
+
(func (export "path_filestat_get") (param i32) (result i32) (call $path_filestat_get (local.get 0) (i32.const 0) (i32.const 700) (i32.const 1) (i32.const 256)))
|
|
108
|
+
(func (export "path_filestat_set_times") (param i32) (result i32) (call $path_filestat_set_times (local.get 0) (i32.const 0) (i32.const 700) (i32.const 1) (i64.const 0) (i64.const 0) (i32.const 0)))
|
|
109
|
+
(func (export "path_link") (param i32) (result i32) (call $path_link (local.get 0) (i32.const 0) (i32.const 700) (i32.const 1) (local.get 0) (i32.const 702) (i32.const 1)))
|
|
110
|
+
(func (export "path_open") (param i32) (result i32) (call $path_open (local.get 0) (i32.const 0) (i32.const 700) (i32.const 1) (i32.const 0) (i64.const 0) (i64.const 0) (i32.const 0) (i32.const 128)))
|
|
111
|
+
(func (export "path_readlink") (param i32) (result i32) (call $path_readlink (local.get 0) (i32.const 700) (i32.const 1) (i32.const 512) (i32.const 16) (i32.const 128)))
|
|
112
|
+
(func (export "path_remove_directory") (param i32) (result i32) (call $path_remove_directory (local.get 0) (i32.const 700) (i32.const 1)))
|
|
113
|
+
(func (export "path_rename") (param i32) (result i32) (call $path_rename (local.get 0) (i32.const 700) (i32.const 1) (local.get 0) (i32.const 702) (i32.const 1)))
|
|
114
|
+
(func (export "path_symlink") (param i32) (result i32) (call $path_symlink (i32.const 700) (i32.const 1) (local.get 0) (i32.const 702) (i32.const 1)))
|
|
115
|
+
(func (export "path_unlink_file") (param i32) (result i32) (call $path_unlink_file (local.get 0) (i32.const 700) (i32.const 1)))
|
|
116
|
+
(func (export "poll_oneoff_read") (param i32) (result i32)
|
|
117
|
+
(call $subscribe (i32.const 1) (local.get 0))
|
|
118
|
+
(call $poll_oneoff (i32.const 800) (i32.const 896) (i32.const 1) (i32.const 128)))
|
|
119
|
+
(func (export "poll_oneoff_write") (param i32) (result i32)
|
|
120
|
+
(call $subscribe (i32.const 2) (local.get 0))
|
|
121
|
+
(call $poll_oneoff (i32.const 800) (i32.const 896) (i32.const 1) (i32.const 128)))
|
|
122
|
+
;; A lone relative wait of no time on the clock the descriptor numbers.
|
|
123
|
+
(func (export "poll_oneoff_clock") (param i32) (result i32)
|
|
124
|
+
(call $subscribe (i32.const 0) (local.get 0))
|
|
125
|
+
(call $poll_oneoff (i32.const 800) (i32.const 896) (i32.const 1) (i32.const 128)))
|
|
126
|
+
(func (export "poll_oneoff_none") (param i32) (result i32)
|
|
127
|
+
(call $poll_oneoff (i32.const 800) (i32.const 896) (i32.const 0) (i32.const 128)))
|
|
128
|
+
(func (export "sock_accept") (param i32) (result i32) (call $sock_accept (local.get 0) (i32.const 0) (i32.const 128)))
|
|
129
|
+
(func (export "sock_recv") (param i32) (result i32) (call $sock_recv (local.get 0) (i32.const 64) (i32.const 1) (i32.const 0) (i32.const 128) (i32.const 132)))
|
|
130
|
+
(func (export "sock_send") (param i32) (result i32) (call $sock_send (local.get 0) (i32.const 80) (i32.const 1) (i32.const 0) (i32.const 128)))
|
|
131
|
+
(func (export "sock_shutdown") (param i32) (result i32) (call $sock_shutdown (local.get 0) (i32.const 0)))
|
|
132
|
+
|
|
133
|
+
;; A buffer past the one page of memory, through the iovec at 88.
|
|
134
|
+
(func (export "fd_read_far") (param i32) (result i32) (call $fd_read (local.get 0) (i32.const 88) (i32.const 1) (i32.const 128)))
|
|
135
|
+
(func (export "fd_pread_far") (param i32) (result i32) (call $fd_pread (local.get 0) (i32.const 88) (i32.const 1) (i64.const 0) (i32.const 128)))
|
|
136
|
+
(func (export "fd_write_far") (param i32) (result i32) (call $fd_write (local.get 0) (i32.const 88) (i32.const 1) (i32.const 128)))
|
|
137
|
+
(func (export "fd_pwrite_far") (param i32) (result i32) (call $fd_pwrite (local.get 0) (i32.const 88) (i32.const 1) (i64.const 0) (i32.const 128)))
|
|
138
|
+
(func (export "fd_readdir_far") (param i32) (result i32) (call $fd_readdir (local.get 0) (i32.const 0x20000) (i32.const 0) (i64.const 0) (i32.const 128)))
|
|
139
|
+
(func (export "path_open_far") (param i32) (result i32) (call $path_open (local.get 0) (i32.const 0) (i32.const 0x20000) (i32.const 1) (i32.const 0) (i64.const 0) (i64.const 0) (i32.const 0) (i32.const 128)))
|
|
140
|
+
;; A path longer than one call's hostcall fuel.
|
|
141
|
+
(func (export "path_open_huge") (param i32) (result i32) (call $path_open (local.get 0) (i32.const 0) (i32.const 700) (i32.const 0x0c800000) (i32.const 0) (i64.const 0) (i64.const 0) (i32.const 0) (i32.const 128)))
|
|
142
|
+
;; Each argument one past what the witx defines.
|
|
143
|
+
(func (export "fd_advise_undefined") (param i32) (result i32) (call $fd_advise (local.get 0) (i64.const 0) (i64.const 0) (i32.const 6)))
|
|
144
|
+
(func (export "fd_seek_undefined") (param i32) (result i32) (call $fd_seek (local.get 0) (i64.const 0) (i32.const 3) (i32.const 128)))
|
|
145
|
+
(func (export "fd_fdstat_set_flags_undefined") (param i32) (result i32) (call $fd_fdstat_set_flags (local.get 0) (i32.const 0x20)))
|
|
146
|
+
(func (export "fd_fdstat_set_rights_undefined") (param i32) (result i32) (call $fd_fdstat_set_rights (local.get 0) (i64.const 0x40000000) (i64.const 0)))
|
|
147
|
+
(func (export "fd_filestat_set_times_undefined") (param i32) (result i32) (call $fd_filestat_set_times (local.get 0) (i64.const 0) (i64.const 0) (i32.const 0x10)))
|
|
148
|
+
(func (export "path_filestat_get_undefined") (param i32) (result i32) (call $path_filestat_get (local.get 0) (i32.const 2) (i32.const 700) (i32.const 1) (i32.const 256)))
|
|
149
|
+
(func (export "path_open_undefined") (param i32) (result i32) (call $path_open (local.get 0) (i32.const 0) (i32.const 700) (i32.const 1) (i32.const 0x10) (i64.const 0) (i64.const 0) (i32.const 0) (i32.const 128)))
|
|
150
|
+
(func (export "path_link_undefined") (param i32) (result i32) (call $path_link (local.get 0) (i32.const 2) (i32.const 700) (i32.const 1) (local.get 0) (i32.const 702) (i32.const 1)))
|
|
151
|
+
(func (export "sock_recv_undefined") (param i32) (result i32) (call $sock_recv (local.get 0) (i32.const 64) (i32.const 1) (i32.const 4) (i32.const 128) (i32.const 132)))
|
|
152
|
+
(func (export "sock_shutdown_undefined") (param i32) (result i32) (call $sock_shutdown (local.get 0) (i32.const 4)))
|
|
153
|
+
|
|
154
|
+
(func (export "fd_read_empty") (param i32) (result i32) (call $fd_read (local.get 0) (i32.const 72) (i32.const 1) (i32.const 128)))
|
|
155
|
+
(func (export "fd_write_empty") (param i32) (result i32) (call $fd_write (local.get 0) (i32.const 72) (i32.const 1) (i32.const 128)))
|
|
156
|
+
(func (export "close_twice") (param i32) (result i32)
|
|
157
|
+
(drop (call $fd_close (local.get 0)))
|
|
158
|
+
(call $fd_close (local.get 0)))
|
|
159
|
+
(func (export "renumber_onto_stdin") (param i32) (result i32)
|
|
160
|
+
(drop (call $fd_renumber (local.get 0) (i32.const 0)))
|
|
161
|
+
(call $fd_fdstat_get (i32.const 0) (i32.const 256)))
|
|
162
|
+
(func (export "read_twice") (param i32) (result i32)
|
|
163
|
+
(drop (call $fd_read (local.get 0) (i32.const 64) (i32.const 1) (i32.const 128)))
|
|
164
|
+
(call $fd_read (local.get 0) (i32.const 64) (i32.const 1) (i32.const 132)))
|
|
165
|
+
(func (export "write_twice") (param i32) (result i32)
|
|
166
|
+
(drop (call $fd_write (local.get 0) (i32.const 80) (i32.const 1) (i32.const 128)))
|
|
167
|
+
(call $fd_write (local.get 0) (i32.const 80) (i32.const 1) (i32.const 132))))
|
|
168
|
+
"#;
|
|
169
|
+
|
|
170
|
+
const CALLS: &[&str] = &[
|
|
171
|
+
"fd_advise",
|
|
172
|
+
"fd_allocate",
|
|
173
|
+
"fd_close",
|
|
174
|
+
"fd_datasync",
|
|
175
|
+
"fd_fdstat_get",
|
|
176
|
+
"fd_fdstat_set_flags",
|
|
177
|
+
"fd_fdstat_set_rights",
|
|
178
|
+
"fd_filestat_get",
|
|
179
|
+
"fd_filestat_set_size",
|
|
180
|
+
"fd_filestat_set_times",
|
|
181
|
+
"fd_pread",
|
|
182
|
+
"fd_prestat_get",
|
|
183
|
+
"fd_prestat_dir_name",
|
|
184
|
+
"fd_pwrite",
|
|
185
|
+
"fd_read",
|
|
186
|
+
"fd_readdir",
|
|
187
|
+
"fd_renumber",
|
|
188
|
+
"fd_seek",
|
|
189
|
+
"fd_sync",
|
|
190
|
+
"fd_tell",
|
|
191
|
+
"fd_write",
|
|
192
|
+
"path_create_directory",
|
|
193
|
+
"path_filestat_get",
|
|
194
|
+
"path_filestat_set_times",
|
|
195
|
+
"path_link",
|
|
196
|
+
"path_open",
|
|
197
|
+
"path_readlink",
|
|
198
|
+
"path_remove_directory",
|
|
199
|
+
"path_rename",
|
|
200
|
+
"path_symlink",
|
|
201
|
+
"path_unlink_file",
|
|
202
|
+
"poll_oneoff_read",
|
|
203
|
+
"poll_oneoff_write",
|
|
204
|
+
"poll_oneoff_clock",
|
|
205
|
+
"poll_oneoff_none",
|
|
206
|
+
"sock_accept",
|
|
207
|
+
"sock_recv",
|
|
208
|
+
"sock_send",
|
|
209
|
+
"sock_shutdown",
|
|
210
|
+
"fd_read_far",
|
|
211
|
+
"fd_pread_far",
|
|
212
|
+
"fd_write_far",
|
|
213
|
+
"fd_pwrite_far",
|
|
214
|
+
"fd_readdir_far",
|
|
215
|
+
"path_open_far",
|
|
216
|
+
"path_open_huge",
|
|
217
|
+
"fd_advise_undefined",
|
|
218
|
+
"fd_seek_undefined",
|
|
219
|
+
"fd_fdstat_set_flags_undefined",
|
|
220
|
+
"fd_fdstat_set_rights_undefined",
|
|
221
|
+
"fd_filestat_set_times_undefined",
|
|
222
|
+
"path_filestat_get_undefined",
|
|
223
|
+
"path_open_undefined",
|
|
224
|
+
"path_link_undefined",
|
|
225
|
+
"sock_recv_undefined",
|
|
226
|
+
"sock_shutdown_undefined",
|
|
227
|
+
"fd_read_empty",
|
|
228
|
+
"fd_write_empty",
|
|
229
|
+
"close_twice",
|
|
230
|
+
"renumber_onto_stdin",
|
|
231
|
+
"read_twice",
|
|
232
|
+
"write_twice",
|
|
233
|
+
];
|
|
234
|
+
|
|
235
|
+
/// What one call leaves behind; `None` for a call that trapped.
|
|
236
|
+
#[derive(Debug, PartialEq)]
|
|
237
|
+
struct Observation {
|
|
238
|
+
errno: Option<i32>,
|
|
239
|
+
memory: Vec<u8>,
|
|
240
|
+
stdout: Vec<u8>,
|
|
241
|
+
stderr: Vec<u8>,
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/// A capped Channel exercises the pipe filling mid-write.
|
|
245
|
+
const CHANNEL_LIMITS: [Option<usize>; 2] = [None, Some(3)];
|
|
246
|
+
|
|
247
|
+
fn stdin_bytes() -> Vec<u8> {
|
|
248
|
+
let mut bytes = (PREAMBLE.len() as u32).to_be_bytes().to_vec();
|
|
249
|
+
bytes.extend_from_slice(PREAMBLE);
|
|
250
|
+
bytes
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
fn observe<T>(
|
|
254
|
+
mut store: Store<T>,
|
|
255
|
+
linker: &Linker<T>,
|
|
256
|
+
call: &str,
|
|
257
|
+
fd: i32,
|
|
258
|
+
) -> (Observation, Store<T>) {
|
|
259
|
+
static MODULE: OnceLock<Module> = OnceLock::new();
|
|
260
|
+
let module = MODULE
|
|
261
|
+
.get_or_init(|| Module::new(store.engine(), PROBE_WAT).expect("probe module must compile"));
|
|
262
|
+
let instance = linker
|
|
263
|
+
.instantiate(&mut store, module)
|
|
264
|
+
.expect("probe module must instantiate");
|
|
265
|
+
let errno = instance
|
|
266
|
+
.get_typed_func::<i32, i32>(&mut store, call)
|
|
267
|
+
.expect("probe export must resolve")
|
|
268
|
+
.call(&mut store, fd)
|
|
269
|
+
.ok();
|
|
270
|
+
let memory = instance
|
|
271
|
+
.get_memory(&mut store, "memory")
|
|
272
|
+
.expect("probe exports its memory")
|
|
273
|
+
.data(&store)[..1024]
|
|
274
|
+
.to_vec();
|
|
275
|
+
let observation = Observation {
|
|
276
|
+
errno,
|
|
277
|
+
memory,
|
|
278
|
+
stdout: Vec::new(),
|
|
279
|
+
stderr: Vec::new(),
|
|
280
|
+
};
|
|
281
|
+
(observation, store)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
fn through_kobako(call: &str, fd: i32, limit: Option<usize>) -> Observation {
|
|
285
|
+
let engine = shared_engine().expect("shared engine must be constructible");
|
|
286
|
+
let config = Config {
|
|
287
|
+
timeout: None,
|
|
288
|
+
setup_timeout: None,
|
|
289
|
+
memory_limit: None,
|
|
290
|
+
stdout_limit: limit,
|
|
291
|
+
stderr_limit: limit,
|
|
292
|
+
open_file_limit: None,
|
|
293
|
+
profile: Profile::Hermetic,
|
|
294
|
+
};
|
|
295
|
+
let wasi = Wasi::for_invocation(&config, PREAMBLE, &[]).expect("WASI context must build");
|
|
296
|
+
let mut store = Store::new(engine, Invocation::new(None, wasi));
|
|
297
|
+
store.set_epoch_deadline(NO_TIMEOUT_EPOCH_DELTA);
|
|
298
|
+
let linker = imports::linker().expect("host imports must link");
|
|
299
|
+
let (mut observation, store) = observe(store, &linker, call, fd);
|
|
300
|
+
observation.stdout = store.data().wasi().stdout_bytes();
|
|
301
|
+
observation.stderr = store.data().wasi().stderr_bytes();
|
|
302
|
+
observation
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/// Plain wasmtime-wasi preview1 over the same stdin and pipes.
|
|
306
|
+
struct Oracle {
|
|
307
|
+
ctx: WasiP1Ctx,
|
|
308
|
+
stdout: MemoryOutputPipe,
|
|
309
|
+
stderr: MemoryOutputPipe,
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
fn through_wasmtime_wasi(call: &str, fd: i32, limit: Option<usize>) -> Observation {
|
|
313
|
+
let engine = shared_engine().expect("shared engine must be constructible");
|
|
314
|
+
let stdout = MemoryOutputPipe::new(crate::capture::pipe_capacity(limit));
|
|
315
|
+
let stderr = MemoryOutputPipe::new(crate::capture::pipe_capacity(limit));
|
|
316
|
+
let ctx = WasiCtxBuilder::new()
|
|
317
|
+
.stdin(MemoryInputPipe::new(stdin_bytes()))
|
|
318
|
+
.stdout(stdout.clone())
|
|
319
|
+
.stderr(stderr.clone())
|
|
320
|
+
.build_p1();
|
|
321
|
+
let mut store = Store::new(
|
|
322
|
+
engine,
|
|
323
|
+
Oracle {
|
|
324
|
+
ctx,
|
|
325
|
+
stdout,
|
|
326
|
+
stderr,
|
|
327
|
+
},
|
|
328
|
+
);
|
|
329
|
+
store.set_epoch_deadline(NO_TIMEOUT_EPOCH_DELTA);
|
|
330
|
+
let mut linker = Linker::new(engine);
|
|
331
|
+
p1::add_to_linker_sync(&mut linker, |oracle: &mut Oracle| &mut oracle.ctx)
|
|
332
|
+
.expect("preview1 must link");
|
|
333
|
+
let (mut observation, store) = observe(store, &linker, call, fd);
|
|
334
|
+
observation.stdout = store.data().stdout.contents().to_vec();
|
|
335
|
+
observation.stderr = store.data().stderr.contents().to_vec();
|
|
336
|
+
observation
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Descriptor 3 is not open, so each call can only answer by the order its
|
|
340
|
+
// checks run in.
|
|
341
|
+
// @behavior RT-138
|
|
342
|
+
#[test]
|
|
343
|
+
fn an_argument_preview1_cannot_hold_answers_in_wasmtime_wasis_order() {
|
|
344
|
+
let errno = |call| through_kobako(call, 3, None).errno;
|
|
345
|
+
assert_eq!(
|
|
346
|
+
(
|
|
347
|
+
errno("fd_seek_undefined"),
|
|
348
|
+
errno("fd_read_far"),
|
|
349
|
+
errno("path_open_huge"),
|
|
350
|
+
),
|
|
351
|
+
(None, Some(8), Some(48)),
|
|
352
|
+
"through kobako's preview1 imports on a descriptor not open, an undefined whence must \
|
|
353
|
+
trap, a buffer past memory must answer EBADF before it is bounded, and a path past the \
|
|
354
|
+
call's fuel must answer ENOMEM before it is read, as wasmtime-wasi answers each"
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
#[test]
|
|
359
|
+
fn every_descriptor_call_answers_as_wasmtime_wasi_preview1() {
|
|
360
|
+
for limit in CHANNEL_LIMITS {
|
|
361
|
+
for call in CALLS {
|
|
362
|
+
for fd in 0..=3 {
|
|
363
|
+
assert_eq!(
|
|
364
|
+
through_kobako(call, fd, limit),
|
|
365
|
+
through_wasmtime_wasi(call, fd, limit),
|
|
366
|
+
"{call} on descriptor {fd} with Channels capped at {limit:?} through \
|
|
367
|
+
kobako's preview1 imports must answer as wasmtime-wasi's preview1 does"
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
//! What a preview1 call answers when it does not succeed: an error code
|
|
2
|
+
//! the guest reads, or a trap that ends the invocation.
|
|
3
|
+
|
|
4
|
+
use kobako_runtime::filesystem::FilesystemError;
|
|
5
|
+
use kobako_runtime::mount::Denial;
|
|
6
|
+
|
|
7
|
+
/// One preview1 `errno`, numbered as the witx `$errno` enum numbers it.
|
|
8
|
+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
9
|
+
#[repr(u16)]
|
|
10
|
+
pub(crate) enum Errno {
|
|
11
|
+
TooBig = 1,
|
|
12
|
+
Acces = 2,
|
|
13
|
+
Addrinuse = 3,
|
|
14
|
+
Addrnotavail = 4,
|
|
15
|
+
Afnosupport = 5,
|
|
16
|
+
Again = 6,
|
|
17
|
+
Already = 7,
|
|
18
|
+
Badf = 8,
|
|
19
|
+
Badmsg = 9,
|
|
20
|
+
Busy = 10,
|
|
21
|
+
Canceled = 11,
|
|
22
|
+
Child = 12,
|
|
23
|
+
Connaborted = 13,
|
|
24
|
+
Connrefused = 14,
|
|
25
|
+
Connreset = 15,
|
|
26
|
+
Deadlk = 16,
|
|
27
|
+
Destaddrreq = 17,
|
|
28
|
+
Dom = 18,
|
|
29
|
+
Dquot = 19,
|
|
30
|
+
Exist = 20,
|
|
31
|
+
Fault = 21,
|
|
32
|
+
Fbig = 22,
|
|
33
|
+
Hostunreach = 23,
|
|
34
|
+
Idrm = 24,
|
|
35
|
+
Ilseq = 25,
|
|
36
|
+
Inprogress = 26,
|
|
37
|
+
Intr = 27,
|
|
38
|
+
Inval = 28,
|
|
39
|
+
Io = 29,
|
|
40
|
+
Isconn = 30,
|
|
41
|
+
Isdir = 31,
|
|
42
|
+
Loop = 32,
|
|
43
|
+
Mfile = 33,
|
|
44
|
+
Mlink = 34,
|
|
45
|
+
Msgsize = 35,
|
|
46
|
+
Multihop = 36,
|
|
47
|
+
Nametoolong = 37,
|
|
48
|
+
Netdown = 38,
|
|
49
|
+
Netreset = 39,
|
|
50
|
+
Netunreach = 40,
|
|
51
|
+
Nfile = 41,
|
|
52
|
+
Nobufs = 42,
|
|
53
|
+
Nodev = 43,
|
|
54
|
+
Noent = 44,
|
|
55
|
+
Noexec = 45,
|
|
56
|
+
Nolck = 46,
|
|
57
|
+
Nolink = 47,
|
|
58
|
+
Nomem = 48,
|
|
59
|
+
Nomsg = 49,
|
|
60
|
+
Noprotoopt = 50,
|
|
61
|
+
Nospc = 51,
|
|
62
|
+
Nosys = 52,
|
|
63
|
+
Notconn = 53,
|
|
64
|
+
Notdir = 54,
|
|
65
|
+
Notempty = 55,
|
|
66
|
+
Notrecoverable = 56,
|
|
67
|
+
Notsock = 57,
|
|
68
|
+
Notsup = 58,
|
|
69
|
+
Notty = 59,
|
|
70
|
+
Nxio = 60,
|
|
71
|
+
Overflow = 61,
|
|
72
|
+
Ownerdead = 62,
|
|
73
|
+
Perm = 63,
|
|
74
|
+
Pipe = 64,
|
|
75
|
+
Proto = 65,
|
|
76
|
+
Protonosupport = 66,
|
|
77
|
+
Prototype = 67,
|
|
78
|
+
Range = 68,
|
|
79
|
+
Rofs = 69,
|
|
80
|
+
Spipe = 70,
|
|
81
|
+
Srch = 71,
|
|
82
|
+
Stale = 72,
|
|
83
|
+
Timedout = 73,
|
|
84
|
+
Txtbsy = 74,
|
|
85
|
+
Xdev = 75,
|
|
86
|
+
Notcapable = 76,
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
impl Errno {
|
|
90
|
+
/// The code a POSIX error name stands for: the witx case, `E`
|
|
91
|
+
/// prefixed and upper-cased. `ENOTCAPABLE` is WASI's own.
|
|
92
|
+
fn from_posix_name(name: &str) -> Option<Self> {
|
|
93
|
+
Some(match name {
|
|
94
|
+
"E2BIG" => Self::TooBig,
|
|
95
|
+
"EACCES" => Self::Acces,
|
|
96
|
+
"EADDRINUSE" => Self::Addrinuse,
|
|
97
|
+
"EADDRNOTAVAIL" => Self::Addrnotavail,
|
|
98
|
+
"EAFNOSUPPORT" => Self::Afnosupport,
|
|
99
|
+
"EAGAIN" => Self::Again,
|
|
100
|
+
"EALREADY" => Self::Already,
|
|
101
|
+
"EBADF" => Self::Badf,
|
|
102
|
+
"EBADMSG" => Self::Badmsg,
|
|
103
|
+
"EBUSY" => Self::Busy,
|
|
104
|
+
"ECANCELED" => Self::Canceled,
|
|
105
|
+
"ECHILD" => Self::Child,
|
|
106
|
+
"ECONNABORTED" => Self::Connaborted,
|
|
107
|
+
"ECONNREFUSED" => Self::Connrefused,
|
|
108
|
+
"ECONNRESET" => Self::Connreset,
|
|
109
|
+
"EDEADLK" => Self::Deadlk,
|
|
110
|
+
"EDESTADDRREQ" => Self::Destaddrreq,
|
|
111
|
+
"EDOM" => Self::Dom,
|
|
112
|
+
"EDQUOT" => Self::Dquot,
|
|
113
|
+
"EEXIST" => Self::Exist,
|
|
114
|
+
"EFAULT" => Self::Fault,
|
|
115
|
+
"EFBIG" => Self::Fbig,
|
|
116
|
+
"EHOSTUNREACH" => Self::Hostunreach,
|
|
117
|
+
"EIDRM" => Self::Idrm,
|
|
118
|
+
"EILSEQ" => Self::Ilseq,
|
|
119
|
+
"EINPROGRESS" => Self::Inprogress,
|
|
120
|
+
"EINTR" => Self::Intr,
|
|
121
|
+
"EINVAL" => Self::Inval,
|
|
122
|
+
"EIO" => Self::Io,
|
|
123
|
+
"EISCONN" => Self::Isconn,
|
|
124
|
+
"EISDIR" => Self::Isdir,
|
|
125
|
+
"ELOOP" => Self::Loop,
|
|
126
|
+
"EMFILE" => Self::Mfile,
|
|
127
|
+
"EMLINK" => Self::Mlink,
|
|
128
|
+
"EMSGSIZE" => Self::Msgsize,
|
|
129
|
+
"EMULTIHOP" => Self::Multihop,
|
|
130
|
+
"ENAMETOOLONG" => Self::Nametoolong,
|
|
131
|
+
"ENETDOWN" => Self::Netdown,
|
|
132
|
+
"ENETRESET" => Self::Netreset,
|
|
133
|
+
"ENETUNREACH" => Self::Netunreach,
|
|
134
|
+
"ENFILE" => Self::Nfile,
|
|
135
|
+
"ENOBUFS" => Self::Nobufs,
|
|
136
|
+
"ENODEV" => Self::Nodev,
|
|
137
|
+
"ENOENT" => Self::Noent,
|
|
138
|
+
"ENOEXEC" => Self::Noexec,
|
|
139
|
+
"ENOLCK" => Self::Nolck,
|
|
140
|
+
"ENOLINK" => Self::Nolink,
|
|
141
|
+
"ENOMEM" => Self::Nomem,
|
|
142
|
+
"ENOMSG" => Self::Nomsg,
|
|
143
|
+
"ENOPROTOOPT" => Self::Noprotoopt,
|
|
144
|
+
"ENOSPC" => Self::Nospc,
|
|
145
|
+
"ENOSYS" => Self::Nosys,
|
|
146
|
+
"ENOTCONN" => Self::Notconn,
|
|
147
|
+
"ENOTDIR" => Self::Notdir,
|
|
148
|
+
"ENOTEMPTY" => Self::Notempty,
|
|
149
|
+
"ENOTRECOVERABLE" => Self::Notrecoverable,
|
|
150
|
+
"ENOTSOCK" => Self::Notsock,
|
|
151
|
+
"ENOTSUP" => Self::Notsup,
|
|
152
|
+
"ENOTTY" => Self::Notty,
|
|
153
|
+
"ENXIO" => Self::Nxio,
|
|
154
|
+
"EOVERFLOW" => Self::Overflow,
|
|
155
|
+
"EOWNERDEAD" => Self::Ownerdead,
|
|
156
|
+
"EPERM" => Self::Perm,
|
|
157
|
+
"EPIPE" => Self::Pipe,
|
|
158
|
+
"EPROTO" => Self::Proto,
|
|
159
|
+
"EPROTONOSUPPORT" => Self::Protonosupport,
|
|
160
|
+
"EPROTOTYPE" => Self::Prototype,
|
|
161
|
+
"ERANGE" => Self::Range,
|
|
162
|
+
"EROFS" => Self::Rofs,
|
|
163
|
+
"ESPIPE" => Self::Spipe,
|
|
164
|
+
"ESRCH" => Self::Srch,
|
|
165
|
+
"ESTALE" => Self::Stale,
|
|
166
|
+
"ETIMEDOUT" => Self::Timedout,
|
|
167
|
+
"ETXTBSY" => Self::Txtbsy,
|
|
168
|
+
"EXDEV" => Self::Xdev,
|
|
169
|
+
"ENOTCAPABLE" => Self::Notcapable,
|
|
170
|
+
_ => return None,
|
|
171
|
+
})
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/// A call that did not succeed. preview1 traps rather than answering
|
|
176
|
+
/// for a pointer outside guest memory or an argument outside what the
|
|
177
|
+
/// witx defines, as wasmtime-wasi does.
|
|
178
|
+
pub(crate) enum Failure {
|
|
179
|
+
Errno(Errno),
|
|
180
|
+
Trap(wasmtime::Error),
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
impl Failure {
|
|
184
|
+
/// A trap saying what the guest handed over that preview1 cannot hold.
|
|
185
|
+
pub(crate) fn trap(message: &'static str) -> Self {
|
|
186
|
+
Self::Trap(wasmtime::Error::msg(message))
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
impl From<Errno> for Failure {
|
|
191
|
+
fn from(errno: Errno) -> Self {
|
|
192
|
+
Self::Errno(errno)
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/// The code a Filesystem's failure reaches the guest as: the one its
|
|
197
|
+
/// POSIX name stands for, or `IO` for a failure without one.
|
|
198
|
+
impl From<FilesystemError> for Failure {
|
|
199
|
+
fn from(error: FilesystemError) -> Self {
|
|
200
|
+
Self::Errno(
|
|
201
|
+
error
|
|
202
|
+
.posix_name()
|
|
203
|
+
.and_then(Errno::from_posix_name)
|
|
204
|
+
.unwrap_or(Errno::Io),
|
|
205
|
+
)
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/// A change beneath a read-only Mount is `ROFS`; a Policy's refusal is
|
|
210
|
+
/// `ACCES`.
|
|
211
|
+
impl From<Denial> for Failure {
|
|
212
|
+
fn from(denial: Denial) -> Self {
|
|
213
|
+
Self::Errno(match denial {
|
|
214
|
+
Denial::ReadOnly => Errno::Rofs,
|
|
215
|
+
Denial::Refused => Errno::Acces,
|
|
216
|
+
})
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
pub(crate) type Answer<T = ()> = Result<T, Failure>;
|
|
221
|
+
|
|
222
|
+
#[cfg(test)]
|
|
223
|
+
mod tests {
|
|
224
|
+
use super::*;
|
|
225
|
+
|
|
226
|
+
#[test]
|
|
227
|
+
fn each_filesystem_failure_reaches_the_guest_as_its_own_code() {
|
|
228
|
+
let cases = [
|
|
229
|
+
(FilesystemError::NotFound, 44),
|
|
230
|
+
(FilesystemError::AlreadyExists, 20),
|
|
231
|
+
(FilesystemError::NotADirectory, 54),
|
|
232
|
+
(FilesystemError::IsADirectory, 31),
|
|
233
|
+
(FilesystemError::DirectoryNotEmpty, 55),
|
|
234
|
+
(FilesystemError::PermissionDenied, 2),
|
|
235
|
+
(FilesystemError::ReadOnlyFilesystem, 69),
|
|
236
|
+
(FilesystemError::InvalidInput, 28),
|
|
237
|
+
(FilesystemError::Unsupported, 58),
|
|
238
|
+
(FilesystemError::StorageFull, 51),
|
|
239
|
+
(FilesystemError::FileTooLarge, 22),
|
|
240
|
+
(FilesystemError::CrossesDevices, 75),
|
|
241
|
+
(FilesystemError::Deadlock, 16),
|
|
242
|
+
(FilesystemError::FilesystemLoop, 32),
|
|
243
|
+
(FilesystemError::NotPermitted, 63),
|
|
244
|
+
(FilesystemError::BadDescriptor, 8),
|
|
245
|
+
(FilesystemError::ResourceBusy, 10),
|
|
246
|
+
(FilesystemError::IllegalSequence, 25),
|
|
247
|
+
(FilesystemError::Interrupted, 27),
|
|
248
|
+
(FilesystemError::TooManyOpenFiles, 33),
|
|
249
|
+
(FilesystemError::InvalidFilename, 37),
|
|
250
|
+
(FilesystemError::OutOfMemory, 48),
|
|
251
|
+
(FilesystemError::NotASocket, 57),
|
|
252
|
+
(FilesystemError::ValueTooLarge, 61),
|
|
253
|
+
(FilesystemError::NotSeekable, 70),
|
|
254
|
+
(FilesystemError::Other("x".into()), 29),
|
|
255
|
+
];
|
|
256
|
+
for (error, code) in cases {
|
|
257
|
+
let label = format!("{error:?}");
|
|
258
|
+
let Failure::Errno(errno) = Failure::from(error) else {
|
|
259
|
+
panic!("{label} must answer an error code, not a trap");
|
|
260
|
+
};
|
|
261
|
+
assert_eq!(
|
|
262
|
+
errno as u16, code,
|
|
263
|
+
"{label} through Failure::from must reach the guest as preview1 errno {code}"
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|