kobako 0.29.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 (201) hide show
  1. checksums.yaml +4 -4
  2. data/.release-please-manifest.json +1 -1
  3. data/CHANGELOG.md +217 -10
  4. data/Cargo.lock +139 -141
  5. data/README.md +96 -574
  6. data/ROADMAP.md +2 -1
  7. data/crates/kobako-runtime/CHANGELOG.md +269 -1
  8. data/crates/kobako-runtime/Cargo.toml +25 -15
  9. data/crates/kobako-runtime/README.md +7 -3
  10. data/crates/kobako-runtime/src/dispatch.rs +1 -1
  11. data/crates/kobako-runtime/src/endpoint/buffer.rs +192 -0
  12. data/crates/kobako-runtime/src/endpoint/capture.rs +115 -0
  13. data/crates/kobako-runtime/src/endpoint/dispatch.rs +253 -0
  14. data/crates/kobako-runtime/src/endpoint/invoke.rs +194 -0
  15. data/crates/kobako-runtime/src/endpoint/memory_cap.rs +238 -0
  16. data/crates/kobako-runtime/src/endpoint.rs +166 -0
  17. data/crates/kobako-runtime/src/filesystem/bind/fid.rs +82 -0
  18. data/crates/kobako-runtime/src/filesystem/{directory/spelling.rs → bind/lookup.rs} +91 -49
  19. data/crates/kobako-runtime/src/filesystem/bind/walk.rs +249 -0
  20. data/crates/kobako-runtime/src/filesystem/bind.rs +2665 -0
  21. data/crates/kobako-runtime/src/filesystem/overlay/file.rs +89 -0
  22. data/crates/kobako-runtime/src/filesystem/overlay/inode.rs +106 -0
  23. data/crates/kobako-runtime/src/filesystem/overlay/path.rs +285 -0
  24. data/crates/kobako-runtime/src/filesystem/overlay/stack/copy_up.rs +135 -0
  25. data/crates/kobako-runtime/src/filesystem/overlay/stack/dir.rs +263 -0
  26. data/crates/kobako-runtime/src/filesystem/overlay/stack/namei.rs +135 -0
  27. data/crates/kobako-runtime/src/filesystem/overlay/stack/readdir.rs +57 -0
  28. data/crates/kobako-runtime/src/filesystem/overlay/stack.rs +227 -734
  29. data/crates/kobako-runtime/src/filesystem/overlay.rs +2649 -1135
  30. data/crates/kobako-runtime/src/filesystem/path.rs +2 -41
  31. data/crates/kobako-runtime/src/filesystem/pipe.rs +221 -0
  32. data/crates/kobako-runtime/src/filesystem/rdir.rs +101 -0
  33. data/crates/kobako-runtime/src/filesystem/tmpfs/tmpfile.rs +32 -0
  34. data/crates/kobako-runtime/src/filesystem/tmpfs/whiteout.rs +85 -0
  35. data/crates/kobako-runtime/src/filesystem/tmpfs/xattr.rs +22 -0
  36. data/crates/kobako-runtime/src/filesystem/tmpfs.rs +1985 -0
  37. data/crates/kobako-runtime/src/filesystem/wrap.rs +224 -0
  38. data/crates/kobako-runtime/src/filesystem.rs +21 -387
  39. data/crates/kobako-runtime/src/hashtable.rs +44 -0
  40. data/crates/kobako-runtime/src/lib.rs +11 -6
  41. data/crates/kobako-runtime/src/locking/seqlock.rs +66 -0
  42. data/crates/kobako-runtime/src/locking.rs +218 -0
  43. data/crates/kobako-runtime/src/mount.rs +31 -104
  44. data/crates/kobako-runtime/src/profile.rs +137 -1
  45. data/crates/kobako-runtime/src/sched.rs +210 -0
  46. data/crates/kobako-runtime/src/security.rs +696 -0
  47. data/crates/kobako-runtime/src/vfs/attr.rs +162 -0
  48. data/crates/kobako-runtime/src/vfs/cred.rs +334 -0
  49. data/crates/kobako-runtime/src/vfs/dentry.rs +1412 -0
  50. data/crates/kobako-runtime/src/vfs/errno.rs +153 -0
  51. data/crates/kobako-runtime/src/vfs/file.rs +1915 -0
  52. data/crates/kobako-runtime/src/vfs/inode.rs +429 -0
  53. data/crates/kobako-runtime/src/vfs/kern_path.rs +217 -0
  54. data/crates/kobako-runtime/src/vfs/mount.rs +59 -0
  55. data/crates/kobako-runtime/src/vfs/namei.rs +5708 -0
  56. data/crates/kobako-runtime/src/vfs/openat2.rs +116 -0
  57. data/crates/kobako-runtime/src/vfs/path.rs +64 -0
  58. data/crates/kobako-runtime/src/vfs/readdir.rs +163 -0
  59. data/crates/kobako-runtime/src/vfs/stat.rs +157 -0
  60. data/crates/kobako-runtime/src/vfs/super_block.rs +122 -0
  61. data/crates/kobako-runtime/src/vfs/utimes.rs +289 -0
  62. data/crates/kobako-runtime/src/vfs.rs +60 -0
  63. data/crates/kobako-runtime/tests/linux_baseline/calls.rs +171 -0
  64. data/crates/kobako-runtime/tests/linux_baseline/concurrent.rs +276 -0
  65. data/crates/kobako-runtime/tests/linux_baseline/kobako.rs +323 -0
  66. data/crates/kobako-runtime/tests/linux_baseline/linux-baseline-expected.txt +16 -0
  67. data/crates/kobako-runtime/tests/linux_baseline/linux.rs +449 -0
  68. data/crates/kobako-runtime/tests/linux_baseline/main.rs +611 -0
  69. data/crates/kobako-runtime/tests/linux_baseline/permissions.rs +236 -0
  70. data/crates/kobako-transport/CHANGELOG.md +8 -2
  71. data/crates/kobako-transport/Cargo.toml +3 -3
  72. data/crates/kobako-transport/README.md +6 -5
  73. data/crates/kobako-transport/src/envelope/bytes.rs +33 -25
  74. data/crates/kobako-transport/src/envelope/call.rs +5 -5
  75. data/crates/kobako-transport/src/envelope/compile_reply.rs +2 -2
  76. data/crates/kobako-transport/src/envelope/error_record.rs +3 -3
  77. data/crates/kobako-transport/src/envelope/fault.rs +2 -2
  78. data/crates/kobako-transport/src/envelope/outcome.rs +3 -3
  79. data/crates/kobako-transport/src/envelope/preamble.rs +12 -12
  80. data/crates/kobako-transport/src/envelope/reply.rs +2 -2
  81. data/crates/kobako-transport/src/envelope/request.rs +2 -2
  82. data/crates/kobako-transport/src/envelope/run.rs +1 -1
  83. data/crates/kobako-transport/src/envelope/unsupported.rs +2 -2
  84. data/crates/kobako-transport/src/envelope.rs +2 -2
  85. data/crates/kobako-wasmtime/CHANGELOG.md +154 -8
  86. data/crates/kobako-wasmtime/Cargo.toml +15 -14
  87. data/crates/kobako-wasmtime/README.md +9 -5
  88. data/crates/kobako-wasmtime/src/abi.rs +12 -20
  89. data/crates/kobako-wasmtime/src/ambient.rs +1 -2
  90. data/crates/kobako-wasmtime/src/cache.rs +5 -5
  91. data/crates/kobako-wasmtime/src/config.rs +22 -7
  92. data/crates/kobako-wasmtime/src/descriptors.rs +133 -100
  93. data/crates/kobako-wasmtime/src/driver.rs +29 -96
  94. data/crates/kobako-wasmtime/src/exports.rs +5 -16
  95. data/crates/kobako-wasmtime/src/guest.rs +38 -0
  96. data/crates/kobako-wasmtime/src/imports.rs +14 -8
  97. data/crates/kobako-wasmtime/src/instance_pre.rs +3 -3
  98. data/crates/kobako-wasmtime/src/invocation.rs +26 -46
  99. data/crates/kobako-wasmtime/src/lib.rs +1 -4
  100. data/crates/kobako-wasmtime/src/limiter.rs +6 -230
  101. data/crates/kobako-wasmtime/src/preview1/differential/mount.rs +69 -46
  102. data/crates/kobako-wasmtime/src/preview1/differential.rs +121 -7
  103. data/crates/kobako-wasmtime/src/preview1/errno.rs +74 -206
  104. data/crates/kobako-wasmtime/src/preview1/fd.rs +1248 -500
  105. data/crates/kobako-wasmtime/src/preview1/memory.rs +117 -150
  106. data/crates/kobako-wasmtime/src/preview1/mount_view/wrap.rs +211 -0
  107. data/crates/kobako-wasmtime/src/preview1/mount_view.rs +1939 -672
  108. data/crates/kobako-wasmtime/src/preview1/path.rs +230 -349
  109. data/crates/kobako-wasmtime/src/preview1/poll.rs +148 -100
  110. data/crates/kobako-wasmtime/src/preview1/sock.rs +6 -23
  111. data/crates/kobako-wasmtime/src/preview1/testsuite-expected.txt +6 -0
  112. data/crates/kobako-wasmtime/src/preview1/testsuite.rs +157 -0
  113. data/crates/kobako-wasmtime/src/preview1.rs +464 -413
  114. data/crates/kobako-wasmtime/src/trap.rs +9 -9
  115. data/crates/kobako-wasmtime/src/wasi.rs +53 -37
  116. data/crates/kobako-wasmtime/tests/{guest_file.rs → integration/guest_file.rs} +232 -201
  117. data/crates/kobako-wasmtime/tests/integration/main.rs +29 -0
  118. data/crates/kobako-wasmtime/tests/{preamble_flags.rs → integration/preamble_flags.rs} +1 -1
  119. data/crates/kobako-wasmtime/tests/{snippet_compile.rs → integration/snippet_compile.rs} +1 -1
  120. data/crates/kobako-wasmtime/tests/{unanswered_call.rs → integration/unanswered_call.rs} +1 -1
  121. data/crates/kobako-wasmtime/tests/{unsupported_call.rs → integration/unsupported_call.rs} +1 -1
  122. data/crates/kobako-wasmtime/witx/typenames.witx +750 -0
  123. data/crates/kobako-wasmtime/witx/wasi_snapshot_preview1.witx +441 -0
  124. data/data/kobako.wasm +0 -0
  125. data/ext/kobako/Cargo.toml +1 -1
  126. data/ext/kobako/src/runtime/bridge.rs +1 -1
  127. data/ext/kobako/src/runtime/errors.rs +1 -1
  128. data/ext/kobako/src/runtime/escape.rs +28 -14
  129. data/ext/kobako/src/runtime/filesystem/grants.rs +21 -5
  130. data/ext/kobako/src/runtime/filesystem/ruby.rs +464 -156
  131. data/ext/kobako/src/runtime/filesystem.rs +109 -137
  132. data/ext/kobako/src/runtime/gvl.rs +43 -8
  133. data/ext/kobako/src/runtime/snapshot.rs +1 -1
  134. data/ext/kobako/src/runtime.rs +6 -1
  135. data/lib/kobako/catalog/extensions.rb +5 -5
  136. data/lib/kobako/catalog/flags.rb +2 -2
  137. data/lib/kobako/catalog/handles.rb +6 -6
  138. data/lib/kobako/catalog/mount.rb +2 -10
  139. data/lib/kobako/catalog/services.rb +2 -2
  140. data/lib/kobako/catalog/snippets.rb +7 -7
  141. data/lib/kobako/catalog.rb +1 -1
  142. data/lib/kobako/codec/decoder.rb +10 -10
  143. data/lib/kobako/codec/ext_types.rb +3 -5
  144. data/lib/kobako/codec/handle_walk.rb +4 -6
  145. data/lib/kobako/codec/nesting.rb +7 -7
  146. data/lib/kobako/codec/utils.rb +1 -1
  147. data/lib/kobako/codec/value_budget.rb +3 -3
  148. data/lib/kobako/context.rb +5 -5
  149. data/lib/kobako/filesystem.rb +44 -13
  150. data/lib/kobako/payload/arguments.rb +2 -2
  151. data/lib/kobako/pool.rb +4 -4
  152. data/lib/kobako/sandbox.rb +28 -28
  153. data/lib/kobako/sandbox_options.rb +2 -3
  154. data/lib/kobako/snippet/bytecode.rb +1 -1
  155. data/lib/kobako/snippet/source.rb +1 -1
  156. data/lib/kobako/transport/dispatcher.rb +5 -5
  157. data/lib/kobako/transport/exposure.rb +2 -2
  158. data/lib/kobako/transport/reflection.rb +7 -7
  159. data/lib/kobako/transport/run.rb +5 -5
  160. data/lib/kobako/transport/yielder.rb +1 -1
  161. data/lib/kobako/version.rb +1 -1
  162. data/lib/kobako.rb +34 -0
  163. data/release-please-config.json +1 -0
  164. data/sig/kobako/catalog/extensions.rbs +3 -3
  165. data/sig/kobako/catalog/handles.rbs +3 -3
  166. data/sig/kobako/catalog/mount.rbs +0 -4
  167. data/sig/kobako/catalog/services.rbs +1 -1
  168. data/sig/kobako/catalog/snippets.rbs +3 -3
  169. data/sig/kobako/catalog.rbs +1 -1
  170. data/sig/kobako/codec/decoder.rbs +1 -1
  171. data/sig/kobako/codec/handle_walk.rbs +1 -1
  172. data/sig/kobako/codec/nesting.rbs +2 -2
  173. data/sig/kobako/codec/utils.rbs +1 -1
  174. data/sig/kobako/codec/value_budget.rbs +1 -1
  175. data/sig/kobako/codec.rbs +1 -2
  176. data/sig/kobako/context.rbs +1 -1
  177. data/sig/kobako/filesystem.rbs +19 -14
  178. data/sig/kobako/guest_yielder.rbs +1 -1
  179. data/sig/kobako/payload/arguments.rbs +1 -1
  180. data/sig/kobako/pool.rbs +2 -2
  181. data/sig/kobako/runtime.rbs +1 -1
  182. data/sig/kobako/sandbox.rbs +3 -5
  183. data/sig/kobako/sandbox_options.rbs +1 -1
  184. data/sig/kobako/transport/dispatcher.rbs +2 -2
  185. data/sig/kobako/transport/exposure.rbs +1 -1
  186. data/sig/kobako/transport/reflection.rbs +2 -2
  187. data/sig/kobako/transport/run.rbs +3 -3
  188. data/sig/kobako/transport.rbs +1 -1
  189. metadata +67 -19
  190. data/crates/kobako-runtime/src/filesystem/directory.rs +0 -1082
  191. data/crates/kobako-runtime/src/filesystem/memory.rs +0 -1100
  192. data/crates/kobako-runtime/src/filesystem/overlay/exclusive.rs +0 -88
  193. data/crates/kobako-runtime/src/filesystem/temporary.rs +0 -167
  194. data/crates/kobako-wasmtime/src/capture.rs +0 -96
  195. data/crates/kobako-wasmtime/src/dispatch.rs +0 -125
  196. data/crates/kobako-wasmtime/src/guest_mem.rs +0 -271
  197. data/crates/kobako-wasmtime/src/io.rs +0 -89
  198. data/crates/kobako-wasmtime/src/preview1/args.rs +0 -60
  199. data/crates/kobako-wasmtime/src/preview1/beneath.rs +0 -188
  200. /data/crates/kobako-wasmtime/tests/{compile_posture.rs → integration/compile_posture.rs} +0 -0
  201. /data/crates/kobako-wasmtime/tests/{support → integration/support}/mod.rs +0 -0
@@ -0,0 +1,210 @@
1
+ //! Who is running and how it waits, beneath every lock, as Linux's
2
+ //! `kernel/sched` is: a lock asks only for its wait to happen, so nothing
3
+ //! above it knows what waiting means to the frontend. A run past its deadline,
4
+ //! or one its frontend interrupted, stops waiting, as a fatal signal ends
5
+ //! Linux's killable waits, so one Sandbox holding a lock never holds another's
6
+ //! thread past that one's own end.
7
+
8
+ use std::cell::RefCell;
9
+ use std::sync::atomic::{AtomicBool, Ordering};
10
+ use std::sync::{Arc, OnceLock};
11
+ use std::thread::{self, ThreadId};
12
+ use std::time::{Duration, Instant};
13
+
14
+ /// How a thread waits for a lock another thread holds. A frontend whose
15
+ /// threads share a lock of their own, as Ruby's threads share the GVL,
16
+ /// gives that lock up for the wait, or a holder that needs it to finish
17
+ /// never could.
18
+ pub trait Blocking: Send + Sync {
19
+ /// Run `wait`, which returns once the lock is taken.
20
+ fn wait(&self, wait: &mut dyn FnMut());
21
+ }
22
+
23
+ static BLOCKING: OnceLock<Box<dyn Blocking>> = OnceLock::new();
24
+
25
+ /// Have every thread of this process wait through `blocking`, as a frontend
26
+ /// says once at startup; a later one is handed back, as
27
+ /// `register_filesystem` refuses a second. Without one a thread just waits.
28
+ pub fn register_blocking(blocking: Box<dyn Blocking>) -> Result<(), Box<dyn Blocking>> {
29
+ BLOCKING.set(blocking)
30
+ }
31
+
32
+ /// Run `wait` as this process's threads wait, as a lock's slow path calls
33
+ /// `schedule`.
34
+ pub(crate) fn schedule(wait: &mut dyn FnMut()) {
35
+ match BLOCKING.get() {
36
+ Some(blocking) => blocking.wait(wait),
37
+ None => wait(),
38
+ }
39
+ }
40
+
41
+ thread_local! {
42
+ static CURRENT: ThreadId = thread::current().id();
43
+ }
44
+
45
+ /// Who is running, as Linux's `current`.
46
+ pub(crate) fn current() -> ThreadId {
47
+ CURRENT.with(|current| *current)
48
+ }
49
+
50
+ /// A thread as its frontend may interrupt it from another thread, as a signal
51
+ /// reaches a Linux task.
52
+ #[derive(Clone, Default)]
53
+ pub struct Task {
54
+ interrupted: Arc<AtomicBool>,
55
+ }
56
+
57
+ impl Task {
58
+ /// The calling thread.
59
+ pub fn current() -> Self {
60
+ TASK.with(|task| task.borrow().task.clone())
61
+ }
62
+
63
+ /// Have every wait of the run this thread carries out stop, as a fatal
64
+ /// signal ends a killable wait; it holds until that run ends.
65
+ pub fn interrupt(&self) {
66
+ self.interrupted.store(true, Ordering::SeqCst);
67
+ }
68
+ }
69
+
70
+ /// The task this thread carries out, as Linux's `current`, and when its run is
71
+ /// to stop.
72
+ #[derive(Default)]
73
+ struct Current {
74
+ task: Task,
75
+ deadline: Option<Instant>,
76
+ /// How many runs the thread is inside, one within another where a
77
+ /// Filesystem's own call starts another.
78
+ depth: usize,
79
+ }
80
+
81
+ thread_local! {
82
+ static TASK: RefCell<Current> = RefCell::default();
83
+ }
84
+
85
+ /// Run `run` as a run that stops waiting once `deadline` passes or its thread
86
+ /// is interrupted, as a Linux task stops a killable wait on a fatal signal. A
87
+ /// run within another keeps the outer one's interrupt.
88
+ pub fn run_task<T>(deadline: Option<Instant>, run: impl FnOnce() -> T) -> T {
89
+ /// Puts back the deadline of the run around this one, however `run` ends,
90
+ /// and lets an interrupt go once no run is left.
91
+ struct Outer(Option<Instant>);
92
+
93
+ impl Drop for Outer {
94
+ fn drop(&mut self) {
95
+ TASK.with(|task| {
96
+ let mut running = task.borrow_mut();
97
+ running.deadline = self.0;
98
+ running.depth -= 1;
99
+ if running.depth == 0 {
100
+ running.task.interrupted.store(false, Ordering::SeqCst);
101
+ }
102
+ });
103
+ }
104
+ }
105
+
106
+ let _outer = Outer(TASK.with(|task| {
107
+ let mut running = task.borrow_mut();
108
+ running.depth += 1;
109
+ std::mem::replace(&mut running.deadline, deadline)
110
+ }));
111
+ run()
112
+ }
113
+
114
+ /// Whether the calling thread's run is to stop waiting, as Linux's
115
+ /// `fatal_signal_pending(current)` answers.
116
+ pub(crate) fn fatal_signal_pending() -> bool {
117
+ TASK.with(|task| {
118
+ let running = task.borrow();
119
+ running.task.interrupted.load(Ordering::SeqCst)
120
+ || running.deadline.is_some_and(|at| Instant::now() >= at)
121
+ })
122
+ }
123
+
124
+ /// The most a wait sleeps before it looks again whether its run is to stop.
125
+ const MAX_SLEEP: Duration = Duration::from_millis(10);
126
+
127
+ /// How long a wait sleeps before it looks again whether its run is to stop:
128
+ /// never past its deadline.
129
+ pub(crate) fn sleep_slice() -> Duration {
130
+ TASK.with(|task| match task.borrow().deadline {
131
+ Some(at) => at.saturating_duration_since(Instant::now()).min(MAX_SLEEP),
132
+ None => MAX_SLEEP,
133
+ })
134
+ }
135
+
136
+ /// A process has one Blocking, so a test counts only the waits of the thread
137
+ /// that asked to.
138
+ #[cfg(test)]
139
+ pub(crate) mod testing {
140
+ use std::cell::RefCell;
141
+ use std::sync::atomic::{AtomicUsize, Ordering};
142
+ use std::sync::Arc;
143
+ use std::time::{Duration, Instant};
144
+
145
+ /// The longest a test waits for another thread, so what never arrives fails
146
+ /// the test instead of hanging the suite.
147
+ pub(crate) const MAX_WAIT: Duration = Duration::from_secs(10);
148
+
149
+ /// Wait until `arrived`, failing the test with `what` past `MAX_WAIT`.
150
+ pub(crate) fn wait_until(what: &str, mut arrived: impl FnMut() -> bool) {
151
+ let started = Instant::now();
152
+ while !arrived() {
153
+ assert!(
154
+ started.elapsed() < MAX_WAIT,
155
+ "{what} did not happen within {MAX_WAIT:?}"
156
+ );
157
+ std::thread::yield_now();
158
+ }
159
+ }
160
+
161
+ /// What the thread `handle` runs answers, failing the test with `what` past
162
+ /// `MAX_WAIT`, so a thread stuck on a lock fails rather than hangs.
163
+ pub(crate) fn join<T>(what: &str, handle: std::thread::JoinHandle<T>) -> T {
164
+ wait_until(what, || handle.is_finished());
165
+ handle
166
+ .join()
167
+ .unwrap_or_else(|_| panic!("{what} must not panic"))
168
+ }
169
+
170
+ /// What the scoped thread `handle` runs answers, as `join` waits for one.
171
+ pub(crate) fn join_scoped<T>(what: &str, handle: std::thread::ScopedJoinHandle<'_, T>) -> T {
172
+ wait_until(what, || handle.is_finished());
173
+ handle
174
+ .join()
175
+ .unwrap_or_else(|_| panic!("{what} must not panic"))
176
+ }
177
+
178
+ /// What `run` answers, each wait for a lock in it ending past `MAX_WAIT`, so a
179
+ /// test thread waiting on a lock itself fails rather than hangs.
180
+ pub(crate) fn within_max_wait<T>(run: impl FnOnce() -> T) -> T {
181
+ super::run_task(Some(Instant::now() + MAX_WAIT), run)
182
+ }
183
+
184
+ use super::{Blocking, BLOCKING};
185
+
186
+ thread_local! {
187
+ static COUNTS: RefCell<Option<Arc<AtomicUsize>>> = const { RefCell::new(None) };
188
+ }
189
+
190
+ struct CountsWaits;
191
+
192
+ impl Blocking for CountsWaits {
193
+ fn wait(&self, wait: &mut dyn FnMut()) {
194
+ COUNTS.with(|counts| {
195
+ if let Some(counts) = counts.borrow().as_ref() {
196
+ counts.fetch_add(1, Ordering::SeqCst);
197
+ }
198
+ });
199
+ wait();
200
+ }
201
+ }
202
+
203
+ /// Counts each wait this thread makes through the registered Blocking.
204
+ pub(crate) fn count_waits() -> Arc<AtomicUsize> {
205
+ BLOCKING.get_or_init(|| Box::new(CountsWaits));
206
+ let counts = Arc::<AtomicUsize>::default();
207
+ COUNTS.with(|held| *held.borrow_mut() = Some(counts.clone()));
208
+ counts
209
+ }
210
+ }