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,224 @@
1
+ //! A test's Filesystem over another: every call forwards to the entry it
2
+ //! wraps unless its `Hook` steps in, and every entry a lookup finds is
3
+ //! wrapped too, as a Filesystem wrapping another must. It makes a file
4
+ //! through `create` and leaves the open to the VFS, as overlayfs does, so an
5
+ //! open's lookup reaches the `Hook` as any lookup does. It hands the
6
+ //! Filesystem beneath the dentry it is handed, where overlayfs hands a real
7
+ //! one, so what that Filesystem makes is recorded unwrapped and later calls
8
+ //! on it reach that Filesystem past the `Hook`.
9
+
10
+ use std::any::Any;
11
+ use std::sync::Arc;
12
+
13
+ use super::{
14
+ DentryOperations, FileOperations, FileSystemType, Filesystem, FilesystemError, Iattr,
15
+ InodeOperations, Kstat, Lookup, Mask, OpenHow, RenameFlag,
16
+ };
17
+ use crate::vfs::{Dentry, Inode};
18
+
19
+ /// What a test changes of the Filesystem beneath it.
20
+ pub(crate) trait Hook: Send + Sync + 'static {
21
+ fn permission(
22
+ &self,
23
+ inner: &dyn InodeOperations,
24
+ dentry: &Dentry,
25
+ mask: Mask,
26
+ ) -> Result<(), FilesystemError> {
27
+ inner.permission(dentry, mask)
28
+ }
29
+
30
+ fn revalidate(
31
+ &self,
32
+ ops: &dyn DentryOperations,
33
+ dir: &dyn InodeOperations,
34
+ dentry: &Dentry,
35
+ ) -> Result<bool, FilesystemError> {
36
+ ops.revalidate(dir, dentry)
37
+ }
38
+
39
+ fn getattr(
40
+ &self,
41
+ inner: &dyn InodeOperations,
42
+ dentry: &Dentry,
43
+ ) -> Result<Kstat, FilesystemError> {
44
+ inner.getattr(dentry)
45
+ }
46
+
47
+ fn lookup(
48
+ &self,
49
+ inner: &dyn InodeOperations,
50
+ dentry: &Dentry,
51
+ ) -> Result<Lookup, FilesystemError> {
52
+ inner.lookup(dentry)
53
+ }
54
+
55
+ fn open(
56
+ &self,
57
+ inner: &dyn InodeOperations,
58
+ dentry: &Dentry,
59
+ how: &OpenHow,
60
+ ) -> Result<Box<dyn FileOperations>, FilesystemError> {
61
+ inner.open(dentry, how)
62
+ }
63
+
64
+ fn mkdir(&self, inner: &dyn InodeOperations, dentry: &Dentry) -> Result<(), FilesystemError> {
65
+ inner.mkdir(dentry)
66
+ }
67
+
68
+ fn rmdir(&self, inner: &dyn InodeOperations, dentry: &Dentry) -> Result<(), FilesystemError> {
69
+ inner.rmdir(dentry)
70
+ }
71
+
72
+ fn unlink(&self, inner: &dyn InodeOperations, dentry: &Dentry) -> Result<(), FilesystemError> {
73
+ inner.unlink(dentry)
74
+ }
75
+
76
+ fn rename(
77
+ &self,
78
+ inner: &dyn InodeOperations,
79
+ from: &Dentry,
80
+ to_dir: &dyn InodeOperations,
81
+ to: &Dentry,
82
+ flag: Option<RenameFlag>,
83
+ ) -> Result<(), FilesystemError> {
84
+ inner.rename(from, to_dir, to, flag)
85
+ }
86
+ }
87
+
88
+ /// A Filesystem over the one `fs` makes, with `hook` stepping in.
89
+ pub(crate) struct Wrap<H> {
90
+ fs: Arc<dyn FileSystemType>,
91
+ hook: Arc<H>,
92
+ }
93
+
94
+ impl<H: Hook> Wrap<H> {
95
+ pub(crate) fn new(fs: Arc<dyn FileSystemType>, hook: Arc<H>) -> Self {
96
+ Self { fs, hook }
97
+ }
98
+ }
99
+
100
+ impl<H: Hook> FileSystemType for Wrap<H> {
101
+ fn fill_super(self: Arc<Self>, sb: &mut Filesystem) -> Result<(), FilesystemError> {
102
+ self.fs.clone().fill_super(sb)?;
103
+ if let Some(ops) = sb.d_op().cloned() {
104
+ sb.set_default_d_op(Arc::new(WrapDentry {
105
+ ops,
106
+ hook: self.hook.clone(),
107
+ }));
108
+ }
109
+ if let Some(root) = sb.root().and_then(|root| root.inode()) {
110
+ let wrapped = WrapInode {
111
+ inner: root.ops().clone(),
112
+ hook: self.hook.clone(),
113
+ };
114
+ sb.d_make_root(root.with_ops(Arc::new(wrapped)));
115
+ }
116
+ Ok(())
117
+ }
118
+ }
119
+
120
+ /// `inner`, an entry of the Filesystem beneath, with `hook` stepping in.
121
+ struct WrapInode<H> {
122
+ inner: Arc<dyn InodeOperations>,
123
+ hook: Arc<H>,
124
+ }
125
+
126
+ impl<H: Hook> WrapInode<H> {
127
+ /// `found` with the entry it names wrapped too.
128
+ fn wrap(&self, found: Lookup) -> Lookup {
129
+ let wrapped = |inode: Arc<Inode>| {
130
+ let ops = Self {
131
+ inner: inode.ops().clone(),
132
+ hook: self.hook.clone(),
133
+ };
134
+ inode.with_ops(Arc::new(ops))
135
+ };
136
+ match found {
137
+ Lookup::Found(inode) => Lookup::Found(wrapped(inode)),
138
+ Lookup::Spelled { name, inode } => Lookup::Spelled {
139
+ name,
140
+ inode: wrapped(inode),
141
+ },
142
+ Lookup::Absent => Lookup::Absent,
143
+ }
144
+ }
145
+ }
146
+
147
+ /// `ops`, handed the directory a `WrapInode<H>` wraps.
148
+ struct WrapDentry<H> {
149
+ ops: Arc<dyn DentryOperations>,
150
+ hook: Arc<H>,
151
+ }
152
+
153
+ impl<H: Hook> DentryOperations for WrapDentry<H> {
154
+ fn revalidate(
155
+ &self,
156
+ dir: &dyn InodeOperations,
157
+ dentry: &Dentry,
158
+ ) -> Result<bool, FilesystemError> {
159
+ let dir = (dir as &dyn Any)
160
+ .downcast_ref::<WrapInode<H>>()
161
+ .map_or(dir, |wrap| wrap.inner.as_ref());
162
+ self.hook.revalidate(self.ops.as_ref(), dir, dentry)
163
+ }
164
+ }
165
+
166
+ impl<H: Hook> InodeOperations for WrapInode<H> {
167
+ fn permission(&self, dentry: &Dentry, mask: Mask) -> Result<(), FilesystemError> {
168
+ self.hook.permission(self.inner.as_ref(), dentry, mask)
169
+ }
170
+
171
+ fn getattr(&self, dentry: &Dentry) -> Result<Kstat, FilesystemError> {
172
+ self.hook.getattr(self.inner.as_ref(), dentry)
173
+ }
174
+
175
+ fn lookup(&self, dentry: &Dentry) -> Result<Lookup, FilesystemError> {
176
+ Ok(self.wrap(self.hook.lookup(self.inner.as_ref(), dentry)?))
177
+ }
178
+
179
+ fn setattr(&self, dentry: &Dentry, attr: &Iattr) -> Result<(), FilesystemError> {
180
+ self.inner.setattr(dentry, attr)
181
+ }
182
+
183
+ fn get_link(&self, dentry: &Dentry) -> Result<String, FilesystemError> {
184
+ self.inner.get_link(dentry)
185
+ }
186
+
187
+ fn open(
188
+ &self,
189
+ dentry: &Dentry,
190
+ how: &OpenHow,
191
+ ) -> Result<Box<dyn FileOperations>, FilesystemError> {
192
+ self.hook.open(self.inner.as_ref(), dentry, how)
193
+ }
194
+
195
+ fn create(&self, dentry: &Dentry) -> Result<(), FilesystemError> {
196
+ self.inner.create(dentry)
197
+ }
198
+
199
+ fn mkdir(&self, dentry: &Dentry) -> Result<(), FilesystemError> {
200
+ self.hook.mkdir(self.inner.as_ref(), dentry)
201
+ }
202
+
203
+ fn rmdir(&self, dentry: &Dentry) -> Result<(), FilesystemError> {
204
+ self.hook.rmdir(self.inner.as_ref(), dentry)
205
+ }
206
+
207
+ fn unlink(&self, dentry: &Dentry) -> Result<(), FilesystemError> {
208
+ self.hook.unlink(self.inner.as_ref(), dentry)
209
+ }
210
+
211
+ fn rename(
212
+ &self,
213
+ from: &Dentry,
214
+ to_dir: &dyn InodeOperations,
215
+ to: &Dentry,
216
+ flag: Option<RenameFlag>,
217
+ ) -> Result<(), FilesystemError> {
218
+ let target = (to_dir as &dyn Any)
219
+ .downcast_ref::<Self>()
220
+ .ok_or(FilesystemError::CrossesDevices)?;
221
+ self.hook
222
+ .rename(self.inner.as_ref(), from, target.inner.as_ref(), to, flag)
223
+ }
224
+ }
@@ -0,0 +1,26 @@
1
+ //! The Filesystem kinds kobako ships, each built on the VFS as a Linux
2
+ //! filesystem is: what answers every call a guest makes beneath a Mount.
3
+ //!
4
+ //! A third party implements the same tables to bring its own, so they are
5
+ //! named here too; the VFS, which calls them, owns them.
6
+
7
+ mod bind;
8
+ mod overlay;
9
+ mod path;
10
+ mod pipe;
11
+ mod rdir;
12
+ mod tmpfs;
13
+ #[cfg(test)]
14
+ pub(crate) mod wrap;
15
+
16
+ pub use bind::Bind;
17
+ pub use overlay::Overlay;
18
+ pub use pipe::{create_pipe_files, PipeInodeInfo};
19
+ pub use rdir::Rdir;
20
+ pub use tmpfs::{SeedError, Tmpfs};
21
+
22
+ pub use crate::vfs::{
23
+ Dentry, DentryOperations, DirContext, DirEntry, FMode, FileOperations, FileSystemType,
24
+ FileType, FilesystemError, Finish, Iattr, Inode, InodeOperations, Kiocb, Kstat, Lookup, Mask,
25
+ OpenHow, PollMask, RenameFlag, SetTime, SuperBlock as Filesystem, Whence,
26
+ };
@@ -0,0 +1,20 @@
1
+ //! A Grant: a host resource the Host App hands the runtime instead of
2
+ //! binding it by name. The Sandbox holds it and each Invocation receives it
3
+ //! afresh; the guest reaches it through its system interface.
4
+
5
+ use crate::mount::Mount;
6
+
7
+ /// One resource handed over for an Invocation. Each kind carries what it
8
+ /// is and decides its own access.
9
+ #[derive(Clone)]
10
+ #[non_exhaustive]
11
+ pub enum Grant {
12
+ Mount(Mount),
13
+ }
14
+
15
+ /// The kinds of Grant a runtime may support.
16
+ #[derive(Clone, Copy, Debug, PartialEq, Eq)]
17
+ #[non_exhaustive]
18
+ pub enum GrantKind {
19
+ Mount,
20
+ }
@@ -0,0 +1,44 @@
1
+ //! A table of objects held elsewhere, by key, as Linux's `hashtable.h`
2
+ //! keeps the dentries and inodes in use.
3
+
4
+ use std::collections::HashMap;
5
+ use std::hash::Hash;
6
+ use std::sync::{Arc, Weak};
7
+
8
+ /// Each object by its key while something else holds it. Entries no longer
9
+ /// held are swept out once the table reaches twice what the last sweep
10
+ /// left, so each insertion pays a constant share of the sweeps.
11
+ pub(crate) struct Hashtable<K, V> {
12
+ entries: HashMap<K, Weak<V>>,
13
+ sweep_at: usize,
14
+ }
15
+
16
+ impl<K, V> Default for Hashtable<K, V> {
17
+ fn default() -> Self {
18
+ Self {
19
+ entries: HashMap::new(),
20
+ sweep_at: 0,
21
+ }
22
+ }
23
+ }
24
+
25
+ impl<K: Hash + Eq, V> Hashtable<K, V> {
26
+ /// The object at `key`, while something holds it.
27
+ pub(crate) fn get(&self, key: &K) -> Option<Arc<V>> {
28
+ self.entries.get(key).and_then(Weak::upgrade)
29
+ }
30
+
31
+ pub(crate) fn insert(&mut self, key: K, value: &Arc<V>) {
32
+ if self.entries.len() >= self.sweep_at {
33
+ self.entries.retain(|_, held| held.strong_count() > 0);
34
+ self.sweep_at = (self.entries.len() * 2).max(16);
35
+ }
36
+ self.entries.insert(key, Arc::downgrade(value));
37
+ }
38
+
39
+ /// Take the object at `key` out of the table, answering it while
40
+ /// something holds it.
41
+ pub(crate) fn remove(&mut self, key: &K) -> Option<Arc<V>> {
42
+ self.entries.remove(key).and_then(|held| held.upgrade())
43
+ }
44
+ }
@@ -2,7 +2,8 @@
2
2
  //!
3
3
  //! The surface where a wasm engine implementation and a host frontend
4
4
  //! meet: the `Runtime` trait, the isolation `Profile` a runtime
5
- //! declares, the neutral per-invocation value types, and the dispatch /
5
+ //! declares, the Grants it presents to the guest and the Filesystem a
6
+ //! Mount places, the neutral per-invocation value types, and the dispatch /
6
7
  //! yield re-entry traits a frontend supplies. The messages these carry
7
8
  //! are `kobako-transport`'s, which this crate takes as given.
8
9
  //! Nothing here depends on an engine or a frontend type — each engine
@@ -12,18 +13,32 @@
12
13
  //! so the engine stays swappable.
13
14
 
14
15
  pub mod dispatch;
16
+ pub mod endpoint;
15
17
  pub mod error;
18
+ pub mod filesystem;
19
+ pub mod grant;
20
+ mod hashtable;
21
+ mod locking;
22
+ pub mod mount;
16
23
  pub mod profile;
17
24
  pub mod runtime;
25
+ pub mod sched;
26
+ pub mod security;
18
27
  pub mod snapshot;
28
+ pub mod vfs;
19
29
  pub mod yielder;
20
30
 
21
31
  // Every name an engine implementation writes into its own signatures, at
22
32
  // the crate root: the module path repeats the crate name and adds nothing
23
33
  // a reader of `kobako_runtime::Runtime` was missing.
24
34
  pub use dispatch::DispatchHandler;
35
+ pub use endpoint::{Calls, GuestInstance};
25
36
  pub use error::{InvokeError, SetupError, Trap};
37
+ pub use filesystem::{Bind, DirEntry, FileType, FilesystemError, Kstat, Overlay, SeedError, Tmpfs};
38
+ pub use grant::{Grant, GrantKind};
39
+ pub use mount::{Access, Mount, MountPathError, Policy};
26
40
  pub use profile::Profile;
27
41
  pub use runtime::{Entry, Runtime};
42
+ pub use sched::Blocking;
28
43
  pub use snapshot::{Capture, Completion, Snapshot, Usage};
29
44
  pub use yielder::Yielder;
@@ -0,0 +1,66 @@
1
+ //! A lock its readers never wait on, as Linux's `seqlock_t`: a writer
2
+ //! counts each write, and a reader that a write overlapped reads again.
3
+
4
+ use std::sync::atomic::{AtomicUsize, Ordering};
5
+ use std::sync::{Mutex, PoisonError};
6
+
7
+ pub(crate) struct SeqLock {
8
+ /// Holds one writer at a time, as `write_seqlock` takes the lock's
9
+ /// spinlock.
10
+ writer: Mutex<()>,
11
+ /// How many times a write began or ended, odd while one is under way.
12
+ sequence: AtomicUsize,
13
+ }
14
+
15
+ impl SeqLock {
16
+ pub(crate) const fn new() -> Self {
17
+ Self {
18
+ writer: Mutex::new(()),
19
+ sequence: AtomicUsize::new(0),
20
+ }
21
+ }
22
+
23
+ /// Run `write` with every reader overlapping it told to read again, as
24
+ /// `write_seqlock` and `write_sequnlock` bracket a write.
25
+ pub(crate) fn write<T>(&self, write: impl FnOnce() -> T) -> T {
26
+ let _held = self.writer.lock().unwrap_or_else(PoisonError::into_inner);
27
+ self.sequence.fetch_add(1, Ordering::SeqCst);
28
+ let written = write();
29
+ self.sequence.fetch_add(1, Ordering::SeqCst);
30
+ written
31
+ }
32
+
33
+ /// Where the writes stand once none is under way, to ask `read_seqretry`
34
+ /// about later, as `__read_seqcount_begin` spins past an odd count. Holding
35
+ /// the writer's lock a moment waits out the one write it guards.
36
+ pub(crate) fn read_seqbegin(&self) -> usize {
37
+ loop {
38
+ let sequence = self.sequence.load(Ordering::SeqCst);
39
+ if sequence.is_multiple_of(2) {
40
+ return sequence;
41
+ }
42
+ drop(self.writer.lock().unwrap_or_else(PoisonError::into_inner));
43
+ }
44
+ }
45
+
46
+ /// Whether a write began since `read_seqbegin` answered `begun`.
47
+ pub(crate) fn read_seqretry(&self, begun: usize) -> bool {
48
+ self.sequence.load(Ordering::SeqCst) != begun
49
+ }
50
+
51
+ /// What `read` answers with no write overlapping it, as
52
+ /// `read_seqbegin_or_lock` reads: first without waiting, then, once a
53
+ /// write overlapped, holding writers off, so writes one after another
54
+ /// never keep a reader reading again.
55
+ pub(crate) fn read<T>(&self, mut read: impl FnMut() -> T) -> T {
56
+ let begun = self.sequence.load(Ordering::SeqCst);
57
+ if begun.is_multiple_of(2) {
58
+ let answer = read();
59
+ if self.sequence.load(Ordering::SeqCst) == begun {
60
+ return answer;
61
+ }
62
+ }
63
+ let _held = self.writer.lock().unwrap_or_else(PoisonError::into_inner);
64
+ read()
65
+ }
66
+ }
@@ -0,0 +1,218 @@
1
+ //! The lock a directory's calls run under, as Linux's `rw_semaphore` in
2
+ //! `kernel/locking`: shared while a name in it is looked up, exclusive while
3
+ //! one is made, removed, or moved. The other locks beneath the VFS sit
4
+ //! beside it.
5
+
6
+ pub(crate) mod seqlock;
7
+
8
+ use std::sync::{Arc, Condvar, Mutex, MutexGuard, PoisonError};
9
+ use std::thread::ThreadId;
10
+
11
+ use crate::sched;
12
+
13
+ /// Why a hold was not taken.
14
+ #[derive(Clone, Copy, Debug, PartialEq, Eq)]
15
+ pub(crate) enum DownError {
16
+ /// A wait no release could ever end, since the thread asking holds what it
17
+ /// asks for. A thread must never hang on itself, so it is answered, as
18
+ /// Linux's `rt_mutex` answers `-EDEADLK`.
19
+ Deadlock,
20
+ /// The wait's run was to stop, past its deadline or interrupted, as
21
+ /// `down_read_killable` answers `-EINTR` on a fatal signal.
22
+ Interrupted,
23
+ }
24
+
25
+ /// Held shared by any number of threads or exclusive by one. A thread
26
+ /// asking again for a hold it could only get once it let go of its own is
27
+ /// refused as a Deadlock; a thread already holding it shared may hold it
28
+ /// shared again, even past a waiting writer, which waits for that thread.
29
+ /// Every wait for it ends once its run is to stop, as each wait of Linux's
30
+ /// is `_killable`.
31
+ #[derive(Default)]
32
+ pub(crate) struct RwSemaphore {
33
+ holders: Mutex<Holders>,
34
+ freed: Condvar,
35
+ }
36
+
37
+ #[derive(Default)]
38
+ struct Holders {
39
+ writer: Option<ThreadId>,
40
+ /// One entry for each shared hold, a thread holding it shared twice
41
+ /// standing twice.
42
+ readers: Vec<ThreadId>,
43
+ /// How many threads wait for a release, so one nobody waits for wakes
44
+ /// no one.
45
+ waiting: usize,
46
+ /// How many threads wait to hold it exclusive, which a thread arriving to hold
47
+ /// it shared goes behind, as `rwsem_down_read_slowpath` queues behind a waiting
48
+ /// writer, so readers one after another never keep a writer out.
49
+ writers_waiting: usize,
50
+ }
51
+
52
+ /// The semaphore while one call holds it, freed as the call returns. It keeps
53
+ /// the semaphore alive itself, so whatever a lock was reached through may go
54
+ /// first.
55
+ pub(crate) struct RwSemaphoreGuard {
56
+ semaphore: Arc<RwSemaphore>,
57
+ holder: ThreadId,
58
+ exclusive: bool,
59
+ }
60
+
61
+ impl RwSemaphore {
62
+ fn holders(&self) -> MutexGuard<'_, Holders> {
63
+ self.holders.lock().unwrap_or_else(PoisonError::into_inner)
64
+ }
65
+
66
+ /// Hold it shared, waiting while a writer holds it.
67
+ pub(crate) fn down_read(self: &Arc<Self>) -> Result<RwSemaphoreGuard, DownError> {
68
+ let current = sched::current();
69
+ self.down(current, false, |holders| match holders.writer {
70
+ Some(writer) if writer == current => Err(DownError::Deadlock),
71
+ Some(_) => Ok(false),
72
+ None if holders.writers_waiting > 0 && !holders.readers.contains(&current) => Ok(false),
73
+ None => {
74
+ holders.readers.push(current);
75
+ Ok(true)
76
+ }
77
+ })
78
+ }
79
+
80
+ /// Hold it exclusive, waiting while anyone holds it.
81
+ pub(crate) fn down_write(self: &Arc<Self>) -> Result<RwSemaphoreGuard, DownError> {
82
+ let current = sched::current();
83
+ self.down(current, true, |holders| {
84
+ if holders.writer == Some(current) || holders.readers.contains(&current) {
85
+ return Err(DownError::Deadlock);
86
+ }
87
+ if holders.writer.is_some() || !holders.readers.is_empty() {
88
+ return Ok(false);
89
+ }
90
+ holders.writer = Some(current);
91
+ Ok(true)
92
+ })
93
+ }
94
+
95
+ /// Hold it shared where nobody holds or waits to hold it exclusive, without
96
+ /// waiting, as `down_read_trylock` fails on a writer or a waiter.
97
+ pub(crate) fn down_read_trylock(self: &Arc<Self>) -> Option<RwSemaphoreGuard> {
98
+ let current = sched::current();
99
+ let mut holders = self.holders();
100
+ if holders.writer.is_some() || holders.writers_waiting > 0 {
101
+ return None;
102
+ }
103
+ holders.readers.push(current);
104
+ Some(self.guard(current, false))
105
+ }
106
+
107
+ /// Hold it exclusive where nobody holds it, without waiting, as
108
+ /// `down_write_trylock`.
109
+ pub(crate) fn down_write_trylock(self: &Arc<Self>) -> Option<RwSemaphoreGuard> {
110
+ let current = sched::current();
111
+ let mut holders = self.holders();
112
+ if holders.writer.is_some() || !holders.readers.is_empty() {
113
+ return None;
114
+ }
115
+ holders.writer = Some(current);
116
+ Some(self.guard(current, true))
117
+ }
118
+
119
+ fn guard(self: &Arc<Self>, holder: ThreadId, exclusive: bool) -> RwSemaphoreGuard {
120
+ RwSemaphoreGuard {
121
+ semaphore: self.clone(),
122
+ holder,
123
+ exclusive,
124
+ }
125
+ }
126
+
127
+ /// Whether a thread waits to hold it exclusive, for a test to see one queued.
128
+ #[cfg(test)]
129
+ pub(crate) fn writer_waits(&self) -> bool {
130
+ self.holders().writers_waiting > 0
131
+ }
132
+
133
+ /// Take the hold `take` grants, or wait for it.
134
+ fn down(
135
+ self: &Arc<Self>,
136
+ holder: ThreadId,
137
+ exclusive: bool,
138
+ mut take: impl FnMut(&mut Holders) -> Result<bool, DownError>,
139
+ ) -> Result<RwSemaphoreGuard, DownError> {
140
+ let mut holders = self.holders();
141
+ if !take(&mut holders)? {
142
+ drop(holders);
143
+ self.down_slowpath(exclusive, &mut take)?;
144
+ }
145
+ Ok(self.guard(holder, exclusive))
146
+ }
147
+
148
+ /// Wait for a release and ask again, as `rwsem_down_read_slowpath` and
149
+ /// `rwsem_down_write_slowpath` call `schedule`, until the run is to stop, as
150
+ /// a killable wait looks at `fatal_signal_pending` each time it wakes.
151
+ fn down_slowpath(
152
+ &self,
153
+ exclusive: bool,
154
+ take: &mut impl FnMut(&mut Holders) -> Result<bool, DownError>,
155
+ ) -> Result<(), DownError> {
156
+ let mut taken = Ok(());
157
+ sched::schedule(&mut || {
158
+ let mut holders = self.holders();
159
+ holders.writers_waiting += usize::from(exclusive);
160
+ loop {
161
+ match take(&mut holders) {
162
+ Ok(true) => break,
163
+ Ok(false) if sched::fatal_signal_pending() => {
164
+ taken = Err(DownError::Interrupted);
165
+ break;
166
+ }
167
+ Ok(false) => {
168
+ holders.waiting += 1;
169
+ holders = self
170
+ .freed
171
+ .wait_timeout(holders, sched::sleep_slice())
172
+ .unwrap_or_else(PoisonError::into_inner)
173
+ .0;
174
+ holders.waiting -= 1;
175
+ }
176
+ Err(err) => {
177
+ taken = Err(err);
178
+ break;
179
+ }
180
+ }
181
+ }
182
+ holders.writers_waiting -= usize::from(exclusive);
183
+ // A reader may have stood behind this writer, which now takes it or
184
+ // gave up; one that took it wakes them again once it lets go.
185
+ if holders.waiting > 0 && taken.is_err() {
186
+ self.freed.notify_all();
187
+ }
188
+ });
189
+ taken
190
+ }
191
+ }
192
+
193
+ impl RwSemaphoreGuard {
194
+ /// Whether this holds `semaphore`.
195
+ pub(crate) fn holds(&self, semaphore: &Arc<RwSemaphore>) -> bool {
196
+ Arc::ptr_eq(&self.semaphore, semaphore)
197
+ }
198
+ }
199
+
200
+ impl Drop for RwSemaphoreGuard {
201
+ fn drop(&mut self) {
202
+ let mut holders = self.semaphore.holders();
203
+ if self.exclusive {
204
+ holders.writer = None;
205
+ } else if let Some(at) = holders
206
+ .readers
207
+ .iter()
208
+ .position(|reader| *reader == self.holder)
209
+ {
210
+ holders.readers.swap_remove(at);
211
+ }
212
+ let waiting = holders.waiting > 0;
213
+ drop(holders);
214
+ if waiting {
215
+ self.semaphore.freed.notify_all();
216
+ }
217
+ }
218
+ }