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,172 @@
1
+ //! The guest's linear memory as one preview1 call reads what its generated
2
+ //! glue left as pointers. An address outside the memory traps, as preview1
3
+ //! requires.
4
+
5
+ use std::cell::Cell;
6
+
7
+ use wiggle::{GuestPtr, GuestType};
8
+
9
+ use super::errno::{Answer, Errno};
10
+ use super::types;
11
+
12
+ /// A memory shared between threads hands out no slice to read or write, and
13
+ /// no Guest Binary declares one.
14
+ fn require_unshared<T>(slice: Option<T>) -> Answer<T> {
15
+ slice.ok_or_else(|| {
16
+ types::Error::trap(wasmtime::Error::msg(
17
+ "a preview1 call needs the guest's memory unshared",
18
+ ))
19
+ })
20
+ }
21
+
22
+ pub(crate) struct GuestMemory<'a, 'm> {
23
+ memory: &'a mut wiggle::GuestMemory<'m>,
24
+ fuel: Cell<u64>,
25
+ }
26
+
27
+ impl<'a, 'm> GuestMemory<'a, 'm> {
28
+ pub(crate) fn new(memory: &'a mut wiggle::GuestMemory<'m>, fuel: usize) -> Self {
29
+ Self {
30
+ memory,
31
+ fuel: Cell::new(fuel as u64),
32
+ }
33
+ }
34
+
35
+ /// Spend `count` elements of `size` bytes of this call's fuel, as
36
+ /// wasmtime-wasi spends it before it copies an array.
37
+ pub(crate) fn consume(&self, count: u32, size: u32) -> Answer {
38
+ let cost = u64::from(count) * u64::from(size);
39
+ let left = self.fuel.get().checked_sub(cost).ok_or(Errno::Nomem)?;
40
+ self.fuel.set(left);
41
+ Ok(())
42
+ }
43
+
44
+ pub(crate) fn bytes(&self, ptr: GuestPtr<[u8]>) -> Answer<&[u8]> {
45
+ require_unshared(self.memory.as_slice(ptr)?)
46
+ }
47
+
48
+ pub(crate) fn bytes_mut(&mut self, ptr: GuestPtr<[u8]>) -> Answer<&mut [u8]> {
49
+ require_unshared(self.memory.as_slice_mut(ptr)?)
50
+ }
51
+
52
+ pub(crate) fn read<T: GuestType>(&self, ptr: GuestPtr<T>) -> Answer<T> {
53
+ Ok(self.memory.read(ptr)?)
54
+ }
55
+
56
+ pub(crate) fn write<T: GuestType>(&mut self, ptr: GuestPtr<T>, value: T) -> Answer {
57
+ Ok(self.memory.write(ptr, value)?)
58
+ }
59
+
60
+ /// Copy `bytes` into guest memory at `ptr`.
61
+ pub(crate) fn write_bytes(&mut self, ptr: GuestPtr<u8>, bytes: &[u8]) -> Answer {
62
+ Ok(self
63
+ .memory
64
+ .copy_from_slice(bytes, ptr.as_array(bytes.len() as u32))?)
65
+ }
66
+
67
+ /// A guest path, which preview1 requires to be UTF-8, charged to the
68
+ /// call's fuel before it is read, as wasmtime-wasi charges it.
69
+ pub(crate) fn path(&self, ptr: GuestPtr<str>) -> Answer<String> {
70
+ self.consume(ptr.len(), 1)?;
71
+ let bytes = self.memory.as_cow(ptr.as_bytes())?;
72
+ String::from_utf8(bytes.into_owned()).map_err(|_| Errno::Ilseq.into())
73
+ }
74
+
75
+ /// The first non-empty buffer of `iovs`, or an empty one when every
76
+ /// buffer is empty. Like wasmtime-wasi, kobako moves one buffer per call,
77
+ /// and the buffer itself is bounded only once the call has found its
78
+ /// descriptor.
79
+ pub(crate) fn first_non_empty_iovec(&self, iovs: types::IovecArray) -> Answer<GuestPtr<[u8]>> {
80
+ self.first_non_empty(iovs, |iov: types::Iovec| (iov.buf, iov.buf_len))
81
+ }
82
+
83
+ /// The first non-empty buffer of `ciovs`, as `first_non_empty_iovec`.
84
+ pub(crate) fn first_non_empty_ciovec(
85
+ &self,
86
+ ciovs: types::CiovecArray,
87
+ ) -> Answer<GuestPtr<[u8]>> {
88
+ self.first_non_empty(ciovs, |ciov: types::Ciovec| (ciov.buf, ciov.buf_len))
89
+ }
90
+
91
+ fn first_non_empty<T: GuestType>(
92
+ &self,
93
+ array: GuestPtr<[T]>,
94
+ buffer: impl Fn(T) -> (GuestPtr<u8>, u32),
95
+ ) -> Answer<GuestPtr<[u8]>> {
96
+ self.consume(array.len(), T::guest_size())?;
97
+ for entry in array.iter() {
98
+ let (ptr, len) = buffer(self.memory.read(entry?)?);
99
+ if len != 0 {
100
+ self.consume(len, 1)?;
101
+ return Ok(ptr.as_array(len));
102
+ }
103
+ }
104
+ Ok(GuestPtr::new((0, 0)))
105
+ }
106
+ }
107
+
108
+ #[cfg(test)]
109
+ mod tests {
110
+ use super::*;
111
+
112
+ /// The most of a guest's memory one call may have the host copy or walk,
113
+ /// which wasmtime hands each call by default.
114
+ const HOSTCALL_FUEL: usize = 128 << 20;
115
+
116
+ // Linux's `readv` would move all three buffers, so only the first with room
117
+ // being the one handed back shows the encoding moves one per call.
118
+ // @behavior RT-284
119
+ #[test]
120
+ fn a_read_or_write_moves_its_first_buffer_with_room() {
121
+ #[repr(align(8))]
122
+ struct AlignedBytes([u8; 64]);
123
+
124
+ // Three iovecs at 0: empty at 32, three bytes at 40, two at 48.
125
+ let mut bytes = AlignedBytes([0; 64]);
126
+ for (at, (buf, len)) in [(32u32, 0u32), (40, 3), (48, 2)].into_iter().enumerate() {
127
+ bytes.0[at * 8..at * 8 + 4].copy_from_slice(&buf.to_le_bytes());
128
+ bytes.0[at * 8 + 4..at * 8 + 8].copy_from_slice(&len.to_le_bytes());
129
+ }
130
+ let mut memory = wiggle::GuestMemory::Unshared(&mut bytes.0);
131
+ let taken = GuestMemory::new(&mut memory, HOSTCALL_FUEL)
132
+ .first_non_empty_iovec(GuestPtr::new((0, 3)))
133
+ .unwrap_or_else(|_| panic!("the iovecs must be read"));
134
+ assert_eq!(
135
+ (taken.offset_base(), taken.len()),
136
+ (40, 3),
137
+ "a preview1 read handed an empty buffer, then three bytes, then two, must move only \
138
+ the three bytes, as wasmtime-wasi does"
139
+ );
140
+ }
141
+
142
+ // Neither the array nor the buffer exists in this small memory, so the
143
+ // budget alone, spent before anything is read, refuses each.
144
+ // @behavior RT-096
145
+ #[test]
146
+ fn a_call_past_its_hostcall_fuel_answers_nomem() {
147
+ // A guest's memory starts aligned, and wiggle refuses an `iovec` at a
148
+ // host address that is not.
149
+ #[repr(align(8))]
150
+ struct AlignedBytes([u8; 64]);
151
+
152
+ let mut array_bytes = AlignedBytes([0; 64]);
153
+ let mut buffer_bytes = AlignedBytes([0; 64]);
154
+ buffer_bytes.0[4..8].copy_from_slice(&(128u32 << 20).wrapping_add(1).to_le_bytes());
155
+ let answer = |bytes: &mut [u8], count: u32| {
156
+ let mut memory = wiggle::GuestMemory::Unshared(bytes);
157
+ GuestMemory::new(&mut memory, HOSTCALL_FUEL)
158
+ .first_non_empty_iovec(GuestPtr::new((0, count)))
159
+ .err()
160
+ .and_then(|err| err.downcast().ok())
161
+ };
162
+ assert_eq!(
163
+ (
164
+ answer(&mut array_bytes.0, (16 << 20) + 1),
165
+ answer(&mut buffer_bytes.0, 1)
166
+ ),
167
+ (Some(Errno::Nomem), Some(Errno::Nomem)),
168
+ "an iovec array or a buffer past 128 MiB through one preview1 call must answer \
169
+ NOMEM before the host copies any of it"
170
+ );
171
+ }
172
+ }
@@ -0,0 +1,211 @@
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::marker::PhantomData;
12
+ use std::sync::Arc;
13
+
14
+ use kobako_runtime::filesystem::{
15
+ DentryOperations, FileOperations, FileSystemType, Filesystem, FilesystemError, InodeOperations,
16
+ Kstat, Lookup, OpenHow, RenameFlag,
17
+ };
18
+ use kobako_runtime::vfs::{Dentry, Inode};
19
+
20
+ /// What a test changes of the Filesystem beneath it.
21
+ pub(super) trait Hook: Send + Sync + 'static {
22
+ fn getattr(
23
+ &self,
24
+ inner: &dyn InodeOperations,
25
+ dentry: &Dentry,
26
+ ) -> Result<Kstat, FilesystemError> {
27
+ inner.getattr(dentry)
28
+ }
29
+
30
+ fn lookup(
31
+ &self,
32
+ inner: &dyn InodeOperations,
33
+ dentry: &Dentry,
34
+ ) -> Result<Lookup, FilesystemError> {
35
+ inner.lookup(dentry)
36
+ }
37
+
38
+ fn fill_super(
39
+ &self,
40
+ fs: Arc<dyn FileSystemType>,
41
+ sb: &mut Filesystem,
42
+ ) -> Result<(), FilesystemError> {
43
+ fs.fill_super(sb)
44
+ }
45
+
46
+ fn open(
47
+ &self,
48
+ inner: &dyn InodeOperations,
49
+ dentry: &Dentry,
50
+ how: &OpenHow,
51
+ ) -> Result<Box<dyn FileOperations>, FilesystemError> {
52
+ inner.open(dentry, how)
53
+ }
54
+
55
+ fn mkdir(&self, inner: &dyn InodeOperations, dentry: &Dentry) -> Result<(), FilesystemError> {
56
+ inner.mkdir(dentry)
57
+ }
58
+
59
+ fn rmdir(&self, inner: &dyn InodeOperations, dentry: &Dentry) -> Result<(), FilesystemError> {
60
+ inner.rmdir(dentry)
61
+ }
62
+
63
+ fn unlink(&self, inner: &dyn InodeOperations, dentry: &Dentry) -> Result<(), FilesystemError> {
64
+ inner.unlink(dentry)
65
+ }
66
+
67
+ fn rename(
68
+ &self,
69
+ inner: &dyn InodeOperations,
70
+ from: &Dentry,
71
+ to_dir: &dyn InodeOperations,
72
+ to: &Dentry,
73
+ flag: Option<RenameFlag>,
74
+ ) -> Result<(), FilesystemError> {
75
+ inner.rename(from, to_dir, to, flag)
76
+ }
77
+ }
78
+
79
+ /// A Filesystem over the one `fs` makes, with `hook` stepping in.
80
+ pub(super) struct Wrap<H> {
81
+ fs: Arc<dyn FileSystemType>,
82
+ hook: Arc<H>,
83
+ }
84
+
85
+ impl<H: Hook> Wrap<H> {
86
+ pub(super) fn new(fs: Arc<dyn FileSystemType>, hook: Arc<H>) -> Arc<Self> {
87
+ Arc::new(Self { fs, hook })
88
+ }
89
+ }
90
+
91
+ impl<H: Hook> FileSystemType for Wrap<H> {
92
+ fn fill_super(self: Arc<Self>, sb: &mut Filesystem) -> Result<(), FilesystemError> {
93
+ self.hook.fill_super(self.fs.clone(), sb)?;
94
+ if let Some(ops) = sb.d_op().cloned() {
95
+ sb.set_default_d_op(Arc::new(WrapDentry::<H> {
96
+ ops,
97
+ hook: PhantomData,
98
+ }));
99
+ }
100
+ if let Some(root) = sb.root().and_then(|root| root.inode()) {
101
+ let wrapped = WrapInode {
102
+ inner: root.ops().clone(),
103
+ hook: self.hook.clone(),
104
+ };
105
+ sb.d_make_root(root.with_ops(Arc::new(wrapped)));
106
+ }
107
+ Ok(())
108
+ }
109
+ }
110
+
111
+ /// `inner`, an entry of the Filesystem beneath, with `hook` stepping in.
112
+ struct WrapInode<H> {
113
+ inner: Arc<dyn InodeOperations>,
114
+ hook: Arc<H>,
115
+ }
116
+
117
+ impl<H: Hook> WrapInode<H> {
118
+ /// `found` with the entry it names wrapped too.
119
+ fn wrap(&self, found: Lookup) -> Lookup {
120
+ let wrapped = |inode: Arc<Inode>| {
121
+ let ops = Self {
122
+ inner: inode.ops().clone(),
123
+ hook: self.hook.clone(),
124
+ };
125
+ inode.with_ops(Arc::new(ops))
126
+ };
127
+ match found {
128
+ Lookup::Found(inode) => Lookup::Found(wrapped(inode)),
129
+ Lookup::Spelled { name, inode } => Lookup::Spelled {
130
+ name,
131
+ inode: wrapped(inode),
132
+ },
133
+ Lookup::Absent => Lookup::Absent,
134
+ }
135
+ }
136
+ }
137
+
138
+ /// `ops`, handed the directory a `WrapInode<H>` wraps.
139
+ struct WrapDentry<H> {
140
+ ops: Arc<dyn DentryOperations>,
141
+ hook: PhantomData<H>,
142
+ }
143
+
144
+ impl<H: Hook> DentryOperations for WrapDentry<H> {
145
+ fn revalidate(
146
+ &self,
147
+ dir: &dyn InodeOperations,
148
+ dentry: &Dentry,
149
+ ) -> Result<bool, FilesystemError> {
150
+ let dir = (dir as &dyn Any)
151
+ .downcast_ref::<WrapInode<H>>()
152
+ .map_or(dir, |wrap| wrap.inner.as_ref());
153
+ self.ops.revalidate(dir, dentry)
154
+ }
155
+ }
156
+
157
+ impl<H: Hook> InodeOperations for WrapInode<H> {
158
+ fn getattr(&self, dentry: &Dentry) -> Result<Kstat, FilesystemError> {
159
+ self.hook.getattr(self.inner.as_ref(), dentry)
160
+ }
161
+
162
+ fn setattr(
163
+ &self,
164
+ dentry: &Dentry,
165
+ attr: &kobako_runtime::filesystem::Iattr,
166
+ ) -> Result<(), FilesystemError> {
167
+ self.inner.setattr(dentry, attr)
168
+ }
169
+
170
+ fn lookup(&self, dentry: &Dentry) -> Result<Lookup, FilesystemError> {
171
+ Ok(self.wrap(self.hook.lookup(self.inner.as_ref(), dentry)?))
172
+ }
173
+
174
+ fn open(
175
+ &self,
176
+ dentry: &Dentry,
177
+ how: &OpenHow,
178
+ ) -> Result<Box<dyn FileOperations>, FilesystemError> {
179
+ self.hook.open(self.inner.as_ref(), dentry, how)
180
+ }
181
+
182
+ fn create(&self, dentry: &Dentry) -> Result<(), FilesystemError> {
183
+ self.inner.create(dentry)
184
+ }
185
+
186
+ fn mkdir(&self, dentry: &Dentry) -> Result<(), FilesystemError> {
187
+ self.hook.mkdir(self.inner.as_ref(), dentry)
188
+ }
189
+
190
+ fn rmdir(&self, dentry: &Dentry) -> Result<(), FilesystemError> {
191
+ self.hook.rmdir(self.inner.as_ref(), dentry)
192
+ }
193
+
194
+ fn unlink(&self, dentry: &Dentry) -> Result<(), FilesystemError> {
195
+ self.hook.unlink(self.inner.as_ref(), dentry)
196
+ }
197
+
198
+ fn rename(
199
+ &self,
200
+ from: &Dentry,
201
+ to_dir: &dyn InodeOperations,
202
+ to: &Dentry,
203
+ flag: Option<RenameFlag>,
204
+ ) -> Result<(), FilesystemError> {
205
+ let target = (to_dir as &dyn Any)
206
+ .downcast_ref::<Self>()
207
+ .ok_or(FilesystemError::CrossesDevices)?;
208
+ self.hook
209
+ .rename(self.inner.as_ref(), from, target.inner.as_ref(), to, flag)
210
+ }
211
+ }