kobako 0.28.0 → 0.30.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.
Files changed (218) hide show
  1. checksums.yaml +4 -4
  2. data/.release-please-manifest.json +1 -1
  3. data/CHANGELOG.md +377 -0
  4. data/Cargo.lock +181 -154
  5. data/README.md +99 -459
  6. data/ROADMAP.md +7 -1
  7. data/SECURITY.md +1 -1
  8. data/crates/kobako-runtime/CHANGELOG.md +354 -0
  9. data/crates/kobako-runtime/Cargo.toml +38 -7
  10. data/crates/kobako-runtime/README.md +17 -8
  11. data/crates/kobako-runtime/src/dispatch.rs +9 -1
  12. data/crates/kobako-runtime/src/endpoint/buffer.rs +192 -0
  13. data/crates/kobako-runtime/src/endpoint/capture.rs +115 -0
  14. data/crates/kobako-runtime/src/endpoint/dispatch.rs +253 -0
  15. data/crates/kobako-runtime/src/endpoint/invoke.rs +194 -0
  16. data/crates/kobako-runtime/src/endpoint/memory_cap.rs +238 -0
  17. data/crates/kobako-runtime/src/endpoint.rs +166 -0
  18. data/crates/kobako-runtime/src/filesystem/bind/fid.rs +82 -0
  19. data/crates/kobako-runtime/src/filesystem/bind/lookup.rs +236 -0
  20. data/crates/kobako-runtime/src/filesystem/bind/walk.rs +249 -0
  21. data/crates/kobako-runtime/src/filesystem/bind.rs +2665 -0
  22. data/crates/kobako-runtime/src/filesystem/overlay/file.rs +89 -0
  23. data/crates/kobako-runtime/src/filesystem/overlay/inode.rs +106 -0
  24. data/crates/kobako-runtime/src/filesystem/overlay/path.rs +285 -0
  25. data/crates/kobako-runtime/src/filesystem/overlay/stack/copy_up.rs +135 -0
  26. data/crates/kobako-runtime/src/filesystem/overlay/stack/dir.rs +263 -0
  27. data/crates/kobako-runtime/src/filesystem/overlay/stack/namei.rs +135 -0
  28. data/crates/kobako-runtime/src/filesystem/overlay/stack/readdir.rs +57 -0
  29. data/crates/kobako-runtime/src/filesystem/overlay/stack.rs +301 -0
  30. data/crates/kobako-runtime/src/filesystem/overlay.rs +3469 -0
  31. data/crates/kobako-runtime/src/filesystem/path.rs +21 -0
  32. data/crates/kobako-runtime/src/filesystem/pipe.rs +221 -0
  33. data/crates/kobako-runtime/src/filesystem/rdir.rs +101 -0
  34. data/crates/kobako-runtime/src/filesystem/tmpfs/tmpfile.rs +32 -0
  35. data/crates/kobako-runtime/src/filesystem/tmpfs/whiteout.rs +85 -0
  36. data/crates/kobako-runtime/src/filesystem/tmpfs/xattr.rs +22 -0
  37. data/crates/kobako-runtime/src/filesystem/tmpfs.rs +1985 -0
  38. data/crates/kobako-runtime/src/filesystem/wrap.rs +224 -0
  39. data/crates/kobako-runtime/src/filesystem.rs +26 -0
  40. data/crates/kobako-runtime/src/grant.rs +20 -0
  41. data/crates/kobako-runtime/src/hashtable.rs +44 -0
  42. data/crates/kobako-runtime/src/lib.rs +16 -1
  43. data/crates/kobako-runtime/src/locking/seqlock.rs +66 -0
  44. data/crates/kobako-runtime/src/locking.rs +218 -0
  45. data/crates/kobako-runtime/src/mount.rs +105 -0
  46. data/crates/kobako-runtime/src/profile.rs +140 -4
  47. data/crates/kobako-runtime/src/runtime.rs +12 -0
  48. data/crates/kobako-runtime/src/sched.rs +210 -0
  49. data/crates/kobako-runtime/src/security.rs +696 -0
  50. data/crates/kobako-runtime/src/snapshot.rs +20 -5
  51. data/crates/kobako-runtime/src/vfs/attr.rs +162 -0
  52. data/crates/kobako-runtime/src/vfs/cred.rs +334 -0
  53. data/crates/kobako-runtime/src/vfs/dentry.rs +1412 -0
  54. data/crates/kobako-runtime/src/vfs/errno.rs +153 -0
  55. data/crates/kobako-runtime/src/vfs/file.rs +1915 -0
  56. data/crates/kobako-runtime/src/vfs/inode.rs +429 -0
  57. data/crates/kobako-runtime/src/vfs/kern_path.rs +217 -0
  58. data/crates/kobako-runtime/src/vfs/mount.rs +59 -0
  59. data/crates/kobako-runtime/src/vfs/namei.rs +5708 -0
  60. data/crates/kobako-runtime/src/vfs/openat2.rs +116 -0
  61. data/crates/kobako-runtime/src/vfs/path.rs +64 -0
  62. data/crates/kobako-runtime/src/vfs/readdir.rs +163 -0
  63. data/crates/kobako-runtime/src/vfs/stat.rs +157 -0
  64. data/crates/kobako-runtime/src/vfs/super_block.rs +122 -0
  65. data/crates/kobako-runtime/src/vfs/utimes.rs +289 -0
  66. data/crates/kobako-runtime/src/vfs.rs +60 -0
  67. data/crates/kobako-runtime/src/yielder.rs +11 -6
  68. data/crates/kobako-runtime/tests/linux_baseline/calls.rs +171 -0
  69. data/crates/kobako-runtime/tests/linux_baseline/concurrent.rs +276 -0
  70. data/crates/kobako-runtime/tests/linux_baseline/kobako.rs +323 -0
  71. data/crates/kobako-runtime/tests/linux_baseline/linux-baseline-expected.txt +16 -0
  72. data/crates/kobako-runtime/tests/linux_baseline/linux.rs +449 -0
  73. data/crates/kobako-runtime/tests/linux_baseline/main.rs +611 -0
  74. data/crates/kobako-runtime/tests/linux_baseline/permissions.rs +236 -0
  75. data/crates/kobako-transport/CHANGELOG.md +23 -0
  76. data/crates/kobako-transport/Cargo.toml +3 -3
  77. data/crates/kobako-transport/README.md +6 -5
  78. data/crates/kobako-transport/src/abi.rs +1 -1
  79. data/crates/kobako-transport/src/envelope/bytes.rs +33 -25
  80. data/crates/kobako-transport/src/envelope/call.rs +5 -5
  81. data/crates/kobako-transport/src/envelope/compile_reply.rs +2 -2
  82. data/crates/kobako-transport/src/envelope/error_record.rs +3 -3
  83. data/crates/kobako-transport/src/envelope/fault.rs +4 -3
  84. data/crates/kobako-transport/src/envelope/outcome.rs +3 -3
  85. data/crates/kobako-transport/src/envelope/preamble.rs +24 -24
  86. data/crates/kobako-transport/src/envelope/reply.rs +34 -16
  87. data/crates/kobako-transport/src/envelope/request.rs +19 -9
  88. data/crates/kobako-transport/src/envelope/run.rs +1 -1
  89. data/crates/kobako-transport/src/envelope/unsupported.rs +2 -2
  90. data/crates/kobako-transport/src/envelope.rs +2 -2
  91. data/crates/kobako-wasmtime/CHANGELOG.md +265 -0
  92. data/crates/kobako-wasmtime/Cargo.toml +25 -19
  93. data/crates/kobako-wasmtime/README.md +13 -7
  94. data/crates/kobako-wasmtime/src/abi.rs +75 -49
  95. data/crates/kobako-wasmtime/src/ambient.rs +64 -6
  96. data/crates/kobako-wasmtime/src/cache.rs +188 -77
  97. data/crates/kobako-wasmtime/src/config.rs +77 -5
  98. data/crates/kobako-wasmtime/src/descriptors.rs +200 -0
  99. data/crates/kobako-wasmtime/src/driver.rs +70 -135
  100. data/crates/kobako-wasmtime/src/exports.rs +5 -16
  101. data/crates/kobako-wasmtime/src/guest.rs +38 -0
  102. data/crates/kobako-wasmtime/src/imports.rs +52 -0
  103. data/crates/kobako-wasmtime/src/instance_pre.rs +18 -46
  104. data/crates/kobako-wasmtime/src/invocation.rs +43 -74
  105. data/crates/kobako-wasmtime/src/lib.rs +5 -4
  106. data/crates/kobako-wasmtime/src/limiter.rs +6 -230
  107. data/crates/kobako-wasmtime/src/preview1/differential/mount.rs +632 -0
  108. data/crates/kobako-wasmtime/src/preview1/differential.rs +486 -0
  109. data/crates/kobako-wasmtime/src/preview1/errno.rs +135 -0
  110. data/crates/kobako-wasmtime/src/preview1/fd.rs +1420 -0
  111. data/crates/kobako-wasmtime/src/preview1/memory.rs +172 -0
  112. data/crates/kobako-wasmtime/src/preview1/mount_view/wrap.rs +211 -0
  113. data/crates/kobako-wasmtime/src/preview1/mount_view.rs +3766 -0
  114. data/crates/kobako-wasmtime/src/preview1/path.rs +302 -0
  115. data/crates/kobako-wasmtime/src/preview1/poll.rs +381 -0
  116. data/crates/kobako-wasmtime/src/preview1/sock.rs +13 -0
  117. data/crates/kobako-wasmtime/src/preview1/testsuite-expected.txt +6 -0
  118. data/crates/kobako-wasmtime/src/preview1/testsuite.rs +157 -0
  119. data/crates/kobako-wasmtime/src/preview1.rs +512 -0
  120. data/crates/kobako-wasmtime/src/trap.rs +45 -25
  121. data/crates/kobako-wasmtime/src/wasi.rs +333 -0
  122. data/crates/kobako-wasmtime/tests/integration/compile_posture.rs +43 -0
  123. data/crates/kobako-wasmtime/tests/integration/guest_file.rs +1085 -0
  124. data/crates/kobako-wasmtime/tests/integration/main.rs +29 -0
  125. data/crates/kobako-wasmtime/tests/{preamble_flags.rs → integration/preamble_flags.rs} +4 -3
  126. data/crates/kobako-wasmtime/tests/{snippet_compile.rs → integration/snippet_compile.rs} +5 -4
  127. data/crates/kobako-wasmtime/tests/{support → integration/support}/mod.rs +9 -13
  128. data/crates/kobako-wasmtime/tests/integration/unanswered_call.rs +86 -0
  129. data/crates/kobako-wasmtime/tests/{unsupported_call.rs → integration/unsupported_call.rs} +2 -1
  130. data/crates/kobako-wasmtime/witx/typenames.witx +750 -0
  131. data/crates/kobako-wasmtime/witx/wasi_snapshot_preview1.witx +441 -0
  132. data/data/kobako.wasm +0 -0
  133. data/ext/kobako/Cargo.toml +4 -2
  134. data/ext/kobako/src/runtime/bridge.rs +154 -81
  135. data/ext/kobako/src/runtime/errors.rs +1 -1
  136. data/ext/kobako/src/runtime/escape.rs +67 -0
  137. data/ext/kobako/src/runtime/filesystem/grants.rs +107 -0
  138. data/ext/kobako/src/runtime/filesystem/ruby.rs +708 -0
  139. data/ext/kobako/src/runtime/filesystem.rs +208 -0
  140. data/ext/kobako/src/runtime/gvl.rs +49 -8
  141. data/ext/kobako/src/runtime/pinned_value.rs +31 -0
  142. data/ext/kobako/src/runtime/snapshot.rs +127 -0
  143. data/ext/kobako/src/runtime.rs +125 -199
  144. data/lib/kobako/catalog/extensions.rb +5 -5
  145. data/lib/kobako/catalog/flags.rb +2 -14
  146. data/lib/kobako/catalog/grants.rb +30 -0
  147. data/lib/kobako/catalog/handles.rb +6 -6
  148. data/lib/kobako/catalog/mount.rb +19 -0
  149. data/lib/kobako/catalog/services.rb +13 -2
  150. data/lib/kobako/catalog/snippets.rb +11 -15
  151. data/lib/kobako/catalog.rb +11 -0
  152. data/lib/kobako/codec/decoder.rb +10 -9
  153. data/lib/kobako/codec/ext_types.rb +4 -6
  154. data/lib/kobako/codec/handle_walk.rb +4 -6
  155. data/lib/kobako/codec/nesting.rb +7 -7
  156. data/lib/kobako/codec/state.rb +19 -3
  157. data/lib/kobako/codec/utils.rb +1 -1
  158. data/lib/kobako/codec/value_budget.rb +86 -0
  159. data/lib/kobako/codec.rb +7 -0
  160. data/lib/kobako/context.rb +40 -13
  161. data/lib/kobako/errors.rb +15 -14
  162. data/lib/kobako/filesystem.rb +69 -0
  163. data/lib/kobako/payload/arguments.rb +2 -2
  164. data/lib/kobako/pool.rb +6 -6
  165. data/lib/kobako/sandbox.rb +55 -27
  166. data/lib/kobako/sandbox_options.rb +39 -13
  167. data/lib/kobako/snippet/{binary.rb → bytecode.rb} +2 -2
  168. data/lib/kobako/snippet/source.rb +1 -1
  169. data/lib/kobako/snippet.rb +1 -1
  170. data/lib/kobako/transport/block_failure.rb +35 -0
  171. data/lib/kobako/transport/dispatcher.rb +33 -29
  172. data/lib/kobako/transport/exposure.rb +7 -9
  173. data/lib/kobako/transport/reflection.rb +79 -23
  174. data/lib/kobako/transport/run.rb +5 -5
  175. data/lib/kobako/transport/yielder.rb +9 -31
  176. data/lib/kobako/transport.rb +1 -0
  177. data/lib/kobako/version.rb +1 -1
  178. data/lib/kobako.rb +35 -0
  179. data/release-please-config.json +1 -0
  180. data/sig/kobako/catalog/extensions.rbs +3 -3
  181. data/sig/kobako/catalog/flags.rbs +0 -4
  182. data/sig/kobako/catalog/grants.rbs +13 -0
  183. data/sig/kobako/catalog/handles.rbs +3 -3
  184. data/sig/kobako/catalog/mount.rbs +24 -0
  185. data/sig/kobako/catalog/services.rbs +5 -1
  186. data/sig/kobako/catalog/snippets.rbs +4 -4
  187. data/sig/kobako/catalog.rbs +1 -0
  188. data/sig/kobako/codec/decoder.rbs +1 -1
  189. data/sig/kobako/codec/handle_walk.rbs +1 -1
  190. data/sig/kobako/codec/nesting.rbs +2 -2
  191. data/sig/kobako/codec/state.rbs +7 -1
  192. data/sig/kobako/codec/utils.rbs +1 -1
  193. data/sig/kobako/codec/value_budget.rbs +23 -0
  194. data/sig/kobako/codec.rbs +3 -2
  195. data/sig/kobako/context.rbs +14 -6
  196. data/sig/kobako/errors.rbs +4 -4
  197. data/sig/kobako/filesystem.rbs +93 -0
  198. data/sig/kobako/guest_yielder.rbs +9 -0
  199. data/sig/kobako/payload/arguments.rbs +1 -1
  200. data/sig/kobako/pool.rbs +3 -3
  201. data/sig/kobako/runtime.rbs +21 -49
  202. data/sig/kobako/sandbox.rbs +9 -5
  203. data/sig/kobako/sandbox_options.rbs +25 -3
  204. data/sig/kobako/snapshot.rbs +20 -0
  205. data/sig/kobako/snippet/bytecode.rbs +12 -0
  206. data/sig/kobako/transport/block_failure.rbs +13 -0
  207. data/sig/kobako/transport/dispatcher.rbs +4 -4
  208. data/sig/kobako/transport/exposure.rbs +2 -2
  209. data/sig/kobako/transport/reflection.rbs +14 -1
  210. data/sig/kobako/transport/run.rbs +3 -3
  211. data/sig/kobako/transport/yielder.rbs +3 -7
  212. data/sig/kobako/transport.rbs +3 -3
  213. metadata +116 -15
  214. data/crates/kobako-wasmtime/src/capture.rs +0 -96
  215. data/crates/kobako-wasmtime/src/dispatch.rs +0 -107
  216. data/crates/kobako-wasmtime/src/guest_mem.rs +0 -265
  217. data/crates/kobako-wasmtime/src/io.rs +0 -286
  218. data/sig/kobako/snippet/binary.rbs +0 -12
@@ -0,0 +1,6 @@
1
+ nofollow_errors # no built-in Filesystem makes a link
2
+ path_exists # no built-in Filesystem makes a link
3
+ path_link # no built-in Filesystem makes a link
4
+ readlink # no built-in Filesystem makes a link
5
+ symlink_create # no built-in Filesystem makes a link
6
+ symlink_filestat # no built-in Filesystem makes a link
@@ -0,0 +1,157 @@
1
+ //! Runs wasi-testsuite's prebuilt preview1 tests against kobako's linker, over a
2
+ //! Bind of each test's own copy of the directory it is handed. The suite is
3
+ //! the second of what decides a preview1 answer, after the witx, so only the
4
+ //! calls kobako deliberately refuses may fail, and `testsuite-expected.txt`
5
+ //! names them.
6
+ //!
7
+ //! The suite is fetched by `rake crates:testsuite`, which pins its version, so
8
+ //! no gate reaches the network.
9
+
10
+ use std::collections::BTreeMap;
11
+ use std::fs;
12
+ use std::path::{Path, PathBuf};
13
+ use std::sync::Arc;
14
+ use std::time::Duration;
15
+
16
+ use kobako_runtime::filesystem::Bind;
17
+ use kobako_runtime::filesystem::Filesystem;
18
+ use kobako_runtime::grant::Grant;
19
+ use kobako_runtime::mount::{Access, Mount};
20
+ use kobako_runtime::profile::Profile;
21
+ use wasmtime::{Module, Store};
22
+ use wasmtime_wasi::I32Exit;
23
+
24
+ use crate::cache::load_engine;
25
+ use crate::config::Config;
26
+ use crate::imports;
27
+ use crate::invocation::Invocation;
28
+ use crate::trap::arm_deadline;
29
+ use crate::wasi::Wasi;
30
+
31
+ const SUITE: &str = concat!(
32
+ env!("CARGO_MANIFEST_DIR"),
33
+ "/../../tmp/wasi-testsuite/tests/rust/testsuite/wasm32-wasip1"
34
+ );
35
+
36
+ /// What may fail, one test per line, with why after a `#`.
37
+ const EXPECTED: &str = include_str!("testsuite-expected.txt");
38
+
39
+ fn copy_tree(from: &Path, to: &Path) {
40
+ fs::create_dir_all(to).expect("the copy must make its directory");
41
+ for entry in fs::read_dir(from).expect("the suite's directory must list") {
42
+ let entry = entry.expect("the suite's directory must list");
43
+ let target = to.join(entry.file_name());
44
+ match entry.file_type().expect("an entry has a type").is_dir() {
45
+ true => copy_tree(&entry.path(), &target),
46
+ false => {
47
+ fs::copy(entry.path(), target).expect("the suite's file must copy");
48
+ }
49
+ }
50
+ }
51
+ }
52
+
53
+ /// What the test at `wasm` ended with: its exit code, or why it never reached
54
+ /// one.
55
+ fn run(wasm: &Path, root: &Path) -> Result<i32, String> {
56
+ let engine = load_engine().map_err(|err| err.to_string())?;
57
+ let bind = Bind::new(root).map_err(|err| err.to_string())?;
58
+ let filesystem = Filesystem::new(Arc::new(bind)).map_err(|err| format!("{err:?}"))?;
59
+ let mount = Mount::new("/", filesystem, Access::ReadWrite).map_err(|err| err.to_string())?;
60
+ let config = Config {
61
+ timeout: None,
62
+ setup_timeout: None,
63
+ memory_limit: None,
64
+ stdout_limit: None,
65
+ stderr_limit: None,
66
+ open_file_limit: None,
67
+ profile: Profile::Permissive,
68
+ security: Vec::new(),
69
+ };
70
+ let wasi = Wasi::for_invocation(&config, Vec::new(), &[Grant::Mount(mount)])
71
+ .map_err(|err| err.to_string())?;
72
+ let mut store = Store::new(engine, Invocation::new(None, wasi));
73
+ arm_deadline(&mut store, Some(Duration::from_secs(30)));
74
+ let module = Module::from_file(engine, wasm).map_err(|err| err.to_string())?;
75
+ let instance = imports::linker()
76
+ .map_err(|err| err.to_string())?
77
+ .instantiate(&mut store, &module)
78
+ .map_err(|err| err.to_string())?;
79
+ let start = instance
80
+ .get_typed_func::<(), ()>(&mut store, "_start")
81
+ .map_err(|err| err.to_string())?;
82
+ match start.call(&mut store, ()) {
83
+ Ok(()) => Ok(0),
84
+ Err(err) => match err.downcast_ref::<I32Exit>() {
85
+ Some(exit) => Ok(exit.0),
86
+ // A test aborts on its first failed assertion, which it wrote to
87
+ // stderr, so that says what the call answered.
88
+ None => Err(String::from_utf8_lossy(&store.data().wasi().stderr_bytes()).to_string()),
89
+ },
90
+ }
91
+ }
92
+
93
+ fn expected_failures() -> BTreeMap<String, String> {
94
+ EXPECTED
95
+ .lines()
96
+ .filter(|line| !line.trim().is_empty())
97
+ .map(|line| {
98
+ let (name, why) = line.split_once('#').unwrap_or((line, ""));
99
+ (name.trim().to_string(), why.trim().to_string())
100
+ })
101
+ .collect()
102
+ }
103
+
104
+ // @behavior RT-192
105
+ #[test]
106
+ #[ignore = "run through `rake crates:testsuite`, which fetches the suite"]
107
+ fn only_what_kobako_refuses_fails_wasi_testsuite() {
108
+ let suite = PathBuf::from(SUITE);
109
+ assert!(
110
+ suite.exists(),
111
+ "wasi-testsuite is not at {SUITE}; run `rake crates:testsuite`"
112
+ );
113
+ let scratch = std::env::temp_dir().join(format!("kobako-testsuite-{}", std::process::id()));
114
+ let mut wasms: Vec<PathBuf> = fs::read_dir(&suite)
115
+ .expect("the suite must list")
116
+ .map(|entry| entry.expect("the suite must list").path())
117
+ .filter(|path| path.extension().is_some_and(|ext| ext == "wasm"))
118
+ .collect();
119
+ wasms.sort();
120
+
121
+ let mut failed = BTreeMap::new();
122
+ for wasm in &wasms {
123
+ let name = wasm
124
+ .file_stem()
125
+ .expect("a test has a name")
126
+ .to_string_lossy()
127
+ .to_string();
128
+ let root = scratch.join(&name);
129
+ copy_tree(&suite.join("fs-tests.dir"), &root);
130
+ match run(wasm, &root) {
131
+ Ok(0) => {}
132
+ Ok(code) => {
133
+ failed.insert(name, format!("exit {code}"));
134
+ }
135
+ Err(why) => {
136
+ failed.insert(name, why);
137
+ }
138
+ }
139
+ }
140
+ let _ = fs::remove_dir_all(&scratch);
141
+
142
+ let expected = expected_failures();
143
+ let unexpected: Vec<_> = failed
144
+ .iter()
145
+ .filter(|(name, _)| !expected.contains_key(*name))
146
+ .collect();
147
+ let passing: Vec<_> = expected
148
+ .keys()
149
+ .filter(|name| !failed.contains_key(*name))
150
+ .collect();
151
+ assert!(
152
+ unexpected.is_empty() && passing.is_empty(),
153
+ "wasi-testsuite through kobako's preview1 imports must fail only what \
154
+ testsuite-expected.txt names; unexpected failures: {unexpected:#?}; now passing: \
155
+ {passing:?}"
156
+ );
157
+ }
@@ -0,0 +1,512 @@
1
+ //! kobako's own answers to the 36 preview1 imports that read the
2
+ //! descriptor table: `fd_*`, `path_*`, `sock_*`, and `poll_oneoff`.
3
+ //!
4
+ //! wasmtime-wasi keeps its table private, so a descriptor kobako places
5
+ //! there would stay invisible to its calls. Answering every call that reads
6
+ //! a descriptor keeps the guest on one table, `crate::descriptors`, where
7
+ //! each Mount is a preopen. The other ten imports — arguments,
8
+ //! environment, clocks, process, scheduling, and randomness — stay
9
+ //! wasmtime-wasi's, so `witx/` declares only these 36. wiggle generates their
10
+ //! glue as it generates wasmtime-wasi's, so a value outside what the witx
11
+ //! defines traps there before a call starts.
12
+
13
+ mod errno;
14
+ mod fd;
15
+ mod memory;
16
+ mod path;
17
+ mod poll;
18
+ mod sock;
19
+
20
+ #[cfg(test)]
21
+ mod differential;
22
+ #[cfg(test)]
23
+ mod mount_view;
24
+ #[cfg(test)]
25
+ mod testsuite;
26
+
27
+ use wasmtime::Linker;
28
+ use wiggle::GuestPtr;
29
+
30
+ use crate::descriptors::Descriptors;
31
+ use crate::invocation::Invocation;
32
+ use errno::Answer;
33
+ use memory::GuestMemory;
34
+
35
+ // wiggle gives each generated call one parameter per witx argument, and no
36
+ // generated signature can be shrunk to fit the lint.
37
+ #[allow(clippy::too_many_arguments)]
38
+ mod witx {
39
+ wiggle::from_witx!({
40
+ witx: ["witx/wasi_snapshot_preview1.witx"],
41
+ tracing: false,
42
+ errors: { errno => trappable Error },
43
+ });
44
+
45
+ pub(super) mod glue {
46
+ wiggle::wasmtime_integration!({
47
+ witx: ["witx/wasi_snapshot_preview1.witx"],
48
+ target: super,
49
+ tracing: false,
50
+ errors: { errno => trappable Error },
51
+ });
52
+ }
53
+ }
54
+
55
+ use witx::{types, wasi_snapshot_preview1};
56
+
57
+ impl wiggle::GuestErrorType for types::Errno {
58
+ fn success() -> Self {
59
+ Self::Success
60
+ }
61
+ }
62
+
63
+ /// Define kobako's answers over wasmtime-wasi's for the same names.
64
+ pub(crate) fn shadow(linker: &mut Linker<Invocation>) -> wasmtime::Result<()> {
65
+ linker.allow_shadowing(true);
66
+ witx::glue::add_wasi_snapshot_preview1_to_linker(linker, |invocation: &mut Invocation| {
67
+ invocation
68
+ })?;
69
+ linker.allow_shadowing(false);
70
+ Ok(())
71
+ }
72
+
73
+ fn table(invocation: &mut Invocation) -> &mut Descriptors {
74
+ invocation.wasi_mut().descriptors_mut()
75
+ }
76
+
77
+ impl Invocation {
78
+ /// Run one call against the calling instance's memory, answering what it
79
+ /// answers unless its host has given the invocation up by then.
80
+ fn answer<T>(
81
+ &mut self,
82
+ memory: &mut wiggle::GuestMemory<'_>,
83
+ call: impl FnOnce(&mut GuestMemory<'_, '_>, &mut Invocation) -> Answer<T>,
84
+ ) -> Answer<T> {
85
+ let fuel = self.hostcall_fuel();
86
+ let answer = call(&mut GuestMemory::new(memory, fuel), self);
87
+ match self.abandonment() {
88
+ Some(trap) => Err(types::Error::trap(trap)),
89
+ None => answer,
90
+ }
91
+ }
92
+ }
93
+
94
+ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for Invocation {
95
+ fn set_hostcall_fuel(&mut self, fuel: usize) {
96
+ Invocation::set_hostcall_fuel(self, fuel);
97
+ }
98
+
99
+ fn fd_advise(
100
+ &mut self,
101
+ memory: &mut wiggle::GuestMemory<'_>,
102
+ fd: types::Fd,
103
+ offset: types::Filesize,
104
+ len: types::Filesize,
105
+ _advice: types::Advice,
106
+ ) -> Answer {
107
+ self.answer(memory, |_, inv| {
108
+ fd::advise(table(inv), fd.into(), offset, len)
109
+ })
110
+ }
111
+
112
+ fn fd_allocate(
113
+ &mut self,
114
+ memory: &mut wiggle::GuestMemory<'_>,
115
+ fd: types::Fd,
116
+ offset: types::Filesize,
117
+ len: types::Filesize,
118
+ ) -> Answer {
119
+ self.answer(memory, |_, inv| {
120
+ fd::allocate(table(inv), fd.into(), offset, len)
121
+ })
122
+ }
123
+
124
+ fn fd_close(&mut self, memory: &mut wiggle::GuestMemory<'_>, fd: types::Fd) -> Answer {
125
+ self.answer(memory, |_, inv| fd::close(table(inv), fd.into()))
126
+ }
127
+
128
+ fn fd_datasync(&mut self, memory: &mut wiggle::GuestMemory<'_>, fd: types::Fd) -> Answer {
129
+ self.answer(memory, |_, inv| fd::sync(table(inv), fd.into(), true))
130
+ }
131
+
132
+ fn fd_fdstat_get(
133
+ &mut self,
134
+ memory: &mut wiggle::GuestMemory<'_>,
135
+ fd: types::Fd,
136
+ ) -> Answer<types::Fdstat> {
137
+ self.answer(memory, |_, inv| fd::fdstat_get(table(inv), fd.into()))
138
+ }
139
+
140
+ fn fd_fdstat_set_flags(
141
+ &mut self,
142
+ memory: &mut wiggle::GuestMemory<'_>,
143
+ fd: types::Fd,
144
+ flags: types::Fdflags,
145
+ ) -> Answer {
146
+ self.answer(memory, |_, inv| {
147
+ fd::fdstat_set_flags(table(inv), fd.into(), flags)
148
+ })
149
+ }
150
+
151
+ fn fd_fdstat_set_rights(
152
+ &mut self,
153
+ memory: &mut wiggle::GuestMemory<'_>,
154
+ fd: types::Fd,
155
+ _fs_rights_base: types::Rights,
156
+ _fs_rights_inheriting: types::Rights,
157
+ ) -> Answer {
158
+ self.answer(memory, |_, inv| {
159
+ fd::fdstat_set_rights(table(inv), fd.into())
160
+ })
161
+ }
162
+
163
+ fn fd_filestat_get(
164
+ &mut self,
165
+ memory: &mut wiggle::GuestMemory<'_>,
166
+ fd: types::Fd,
167
+ ) -> Answer<types::Filestat> {
168
+ self.answer(memory, |_, inv| fd::filestat_get(table(inv), fd.into()))
169
+ }
170
+
171
+ fn fd_filestat_set_size(
172
+ &mut self,
173
+ memory: &mut wiggle::GuestMemory<'_>,
174
+ fd: types::Fd,
175
+ size: types::Filesize,
176
+ ) -> Answer {
177
+ self.answer(memory, |_, inv| {
178
+ fd::filestat_set_size(table(inv), fd.into(), size)
179
+ })
180
+ }
181
+
182
+ fn fd_filestat_set_times(
183
+ &mut self,
184
+ memory: &mut wiggle::GuestMemory<'_>,
185
+ fd: types::Fd,
186
+ atim: types::Timestamp,
187
+ mtim: types::Timestamp,
188
+ fst_flags: types::Fstflags,
189
+ ) -> Answer {
190
+ self.answer(memory, |_, inv| {
191
+ fd::utimensat(atim, mtim, fst_flags, |times| {
192
+ fd::filestat_set_times(table(inv), fd.into(), times)
193
+ })
194
+ })
195
+ }
196
+
197
+ fn fd_pread(
198
+ &mut self,
199
+ memory: &mut wiggle::GuestMemory<'_>,
200
+ fd: types::Fd,
201
+ iovs: types::IovecArray,
202
+ offset: types::Filesize,
203
+ ) -> Answer<types::Size> {
204
+ self.answer(memory, |mem, inv| {
205
+ fd::pread(table(inv), mem, fd.into(), iovs, offset)
206
+ })
207
+ }
208
+
209
+ fn fd_prestat_get(
210
+ &mut self,
211
+ memory: &mut wiggle::GuestMemory<'_>,
212
+ fd: types::Fd,
213
+ ) -> Answer<types::Prestat> {
214
+ self.answer(memory, |_, inv| fd::prestat_get(table(inv), fd.into()))
215
+ }
216
+
217
+ fn fd_prestat_dir_name(
218
+ &mut self,
219
+ memory: &mut wiggle::GuestMemory<'_>,
220
+ fd: types::Fd,
221
+ path: GuestPtr<u8>,
222
+ path_max_len: types::Size,
223
+ ) -> Answer {
224
+ self.answer(memory, |mem, inv| {
225
+ fd::prestat_dir_name(table(inv), mem, fd.into(), path, path_max_len)
226
+ })
227
+ }
228
+
229
+ fn fd_pwrite(
230
+ &mut self,
231
+ memory: &mut wiggle::GuestMemory<'_>,
232
+ fd: types::Fd,
233
+ ciovs: types::CiovecArray,
234
+ offset: types::Filesize,
235
+ ) -> Answer<types::Size> {
236
+ self.answer(memory, |mem, inv| {
237
+ fd::pwrite(table(inv), mem, fd.into(), ciovs, offset)
238
+ })
239
+ }
240
+
241
+ fn fd_read(
242
+ &mut self,
243
+ memory: &mut wiggle::GuestMemory<'_>,
244
+ fd: types::Fd,
245
+ iovs: types::IovecArray,
246
+ ) -> Answer<types::Size> {
247
+ self.answer(memory, |mem, inv| {
248
+ fd::read(table(inv), mem, fd.into(), iovs)
249
+ })
250
+ }
251
+
252
+ fn fd_readdir(
253
+ &mut self,
254
+ memory: &mut wiggle::GuestMemory<'_>,
255
+ fd: types::Fd,
256
+ buf: GuestPtr<u8>,
257
+ buf_len: types::Size,
258
+ cookie: types::Dircookie,
259
+ ) -> Answer<types::Size> {
260
+ self.answer(memory, |mem, inv| {
261
+ fd::readdir(table(inv), mem, fd.into(), buf, buf_len, cookie)
262
+ })
263
+ }
264
+
265
+ fn fd_renumber(
266
+ &mut self,
267
+ memory: &mut wiggle::GuestMemory<'_>,
268
+ from: types::Fd,
269
+ to: types::Fd,
270
+ ) -> Answer {
271
+ self.answer(memory, |_, inv| {
272
+ fd::renumber(table(inv), from.into(), to.into())
273
+ })
274
+ }
275
+
276
+ fn fd_seek(
277
+ &mut self,
278
+ memory: &mut wiggle::GuestMemory<'_>,
279
+ fd: types::Fd,
280
+ offset: types::Filedelta,
281
+ whence: types::Whence,
282
+ ) -> Answer<types::Filesize> {
283
+ self.answer(memory, |_, inv| {
284
+ fd::seek(table(inv), fd.into(), offset, whence)
285
+ })
286
+ }
287
+
288
+ fn fd_sync(&mut self, memory: &mut wiggle::GuestMemory<'_>, fd: types::Fd) -> Answer {
289
+ self.answer(memory, |_, inv| fd::sync(table(inv), fd.into(), false))
290
+ }
291
+
292
+ fn fd_tell(
293
+ &mut self,
294
+ memory: &mut wiggle::GuestMemory<'_>,
295
+ fd: types::Fd,
296
+ ) -> Answer<types::Filesize> {
297
+ self.answer(memory, |_, inv| fd::tell(table(inv), fd.into()))
298
+ }
299
+
300
+ fn fd_write(
301
+ &mut self,
302
+ memory: &mut wiggle::GuestMemory<'_>,
303
+ fd: types::Fd,
304
+ ciovs: types::CiovecArray,
305
+ ) -> Answer<types::Size> {
306
+ self.answer(memory, |mem, inv| {
307
+ fd::write(table(inv), mem, fd.into(), ciovs)
308
+ })
309
+ }
310
+
311
+ fn path_create_directory(
312
+ &mut self,
313
+ memory: &mut wiggle::GuestMemory<'_>,
314
+ fd: types::Fd,
315
+ path: GuestPtr<str>,
316
+ ) -> Answer {
317
+ self.answer(memory, |mem, inv| {
318
+ path::create_directory(table(inv), mem, fd.into(), path)
319
+ })
320
+ }
321
+
322
+ fn path_filestat_get(
323
+ &mut self,
324
+ memory: &mut wiggle::GuestMemory<'_>,
325
+ fd: types::Fd,
326
+ flags: types::Lookupflags,
327
+ path: GuestPtr<str>,
328
+ ) -> Answer<types::Filestat> {
329
+ self.answer(memory, |mem, inv| {
330
+ path::filestat_get(table(inv), mem, fd.into(), flags, path)
331
+ })
332
+ }
333
+
334
+ fn path_filestat_set_times(
335
+ &mut self,
336
+ memory: &mut wiggle::GuestMemory<'_>,
337
+ fd: types::Fd,
338
+ flags: types::Lookupflags,
339
+ path: GuestPtr<str>,
340
+ atim: types::Timestamp,
341
+ mtim: types::Timestamp,
342
+ fst_flags: types::Fstflags,
343
+ ) -> Answer {
344
+ self.answer(memory, |mem, inv| {
345
+ fd::utimensat(atim, mtim, fst_flags, |times| {
346
+ path::filestat_set_times(table(inv), mem, fd.into(), flags, path, times)
347
+ })
348
+ })
349
+ }
350
+
351
+ fn path_link(
352
+ &mut self,
353
+ memory: &mut wiggle::GuestMemory<'_>,
354
+ old_fd: types::Fd,
355
+ old_flags: types::Lookupflags,
356
+ old_path: GuestPtr<str>,
357
+ new_fd: types::Fd,
358
+ new_path: GuestPtr<str>,
359
+ ) -> Answer {
360
+ self.answer(memory, |mem, inv| {
361
+ path::link(
362
+ table(inv),
363
+ mem,
364
+ (old_fd.into(), old_path),
365
+ (new_fd.into(), new_path),
366
+ old_flags,
367
+ )
368
+ })
369
+ }
370
+
371
+ fn path_open(
372
+ &mut self,
373
+ memory: &mut wiggle::GuestMemory<'_>,
374
+ fd: types::Fd,
375
+ dirflags: types::Lookupflags,
376
+ path: GuestPtr<str>,
377
+ oflags: types::Oflags,
378
+ fs_rights_base: types::Rights,
379
+ _fs_rights_inheriting: types::Rights,
380
+ fdflags: types::Fdflags,
381
+ ) -> Answer<types::Fd> {
382
+ self.answer(memory, |mem, inv| {
383
+ path::open(
384
+ table(inv),
385
+ mem,
386
+ fd.into(),
387
+ path,
388
+ &path::PathOpen {
389
+ dirflags,
390
+ oflags,
391
+ rights_base: fs_rights_base,
392
+ fdflags,
393
+ },
394
+ )
395
+ })
396
+ }
397
+
398
+ fn path_readlink(
399
+ &mut self,
400
+ memory: &mut wiggle::GuestMemory<'_>,
401
+ fd: types::Fd,
402
+ path: GuestPtr<str>,
403
+ buf: GuestPtr<u8>,
404
+ buf_len: types::Size,
405
+ ) -> Answer<types::Size> {
406
+ self.answer(memory, |mem, inv| {
407
+ path::readlink(table(inv), mem, fd.into(), path, buf, buf_len)
408
+ })
409
+ }
410
+
411
+ fn path_remove_directory(
412
+ &mut self,
413
+ memory: &mut wiggle::GuestMemory<'_>,
414
+ fd: types::Fd,
415
+ path: GuestPtr<str>,
416
+ ) -> Answer {
417
+ self.answer(memory, |mem, inv| {
418
+ path::remove_directory(table(inv), mem, fd.into(), path)
419
+ })
420
+ }
421
+
422
+ fn path_rename(
423
+ &mut self,
424
+ memory: &mut wiggle::GuestMemory<'_>,
425
+ old_fd: types::Fd,
426
+ old_path: GuestPtr<str>,
427
+ new_fd: types::Fd,
428
+ new_path: GuestPtr<str>,
429
+ ) -> Answer {
430
+ self.answer(memory, |mem, inv| {
431
+ path::rename(
432
+ table(inv),
433
+ mem,
434
+ (old_fd.into(), old_path),
435
+ (new_fd.into(), new_path),
436
+ )
437
+ })
438
+ }
439
+
440
+ fn path_symlink(
441
+ &mut self,
442
+ memory: &mut wiggle::GuestMemory<'_>,
443
+ old_path: GuestPtr<str>,
444
+ fd: types::Fd,
445
+ new_path: GuestPtr<str>,
446
+ ) -> Answer {
447
+ self.answer(memory, |mem, inv| {
448
+ path::symlink(table(inv), mem, old_path, fd.into(), new_path)
449
+ })
450
+ }
451
+
452
+ fn path_unlink_file(
453
+ &mut self,
454
+ memory: &mut wiggle::GuestMemory<'_>,
455
+ fd: types::Fd,
456
+ path: GuestPtr<str>,
457
+ ) -> Answer {
458
+ self.answer(memory, |mem, inv| {
459
+ path::unlink_file(table(inv), mem, fd.into(), path)
460
+ })
461
+ }
462
+
463
+ fn poll_oneoff(
464
+ &mut self,
465
+ memory: &mut wiggle::GuestMemory<'_>,
466
+ subs: GuestPtr<types::Subscription>,
467
+ events: GuestPtr<types::Event>,
468
+ count: types::Size,
469
+ ) -> Answer<types::Size> {
470
+ self.answer(memory, |mem, inv| {
471
+ poll::poll_oneoff(inv, mem, subs, events, count)
472
+ })
473
+ }
474
+
475
+ fn sock_accept(
476
+ &mut self,
477
+ memory: &mut wiggle::GuestMemory<'_>,
478
+ fd: types::Fd,
479
+ _flags: types::Fdflags,
480
+ ) -> Answer<types::Fd> {
481
+ self.answer(memory, |_, inv| sock::socket(table(inv), fd.into()))
482
+ }
483
+
484
+ fn sock_recv(
485
+ &mut self,
486
+ memory: &mut wiggle::GuestMemory<'_>,
487
+ fd: types::Fd,
488
+ _ri_data: types::IovecArray,
489
+ _ri_flags: types::Riflags,
490
+ ) -> Answer<(types::Size, types::Roflags)> {
491
+ self.answer(memory, |_, inv| sock::socket(table(inv), fd.into()))
492
+ }
493
+
494
+ fn sock_send(
495
+ &mut self,
496
+ memory: &mut wiggle::GuestMemory<'_>,
497
+ fd: types::Fd,
498
+ _si_data: types::CiovecArray,
499
+ _si_flags: types::Siflags,
500
+ ) -> Answer<types::Size> {
501
+ self.answer(memory, |_, inv| sock::socket(table(inv), fd.into()))
502
+ }
503
+
504
+ fn sock_shutdown(
505
+ &mut self,
506
+ memory: &mut wiggle::GuestMemory<'_>,
507
+ fd: types::Fd,
508
+ _how: types::Sdflags,
509
+ ) -> Answer {
510
+ self.answer(memory, |_, inv| sock::socket(table(inv), fd.into()))
511
+ }
512
+ }