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.
- checksums.yaml +4 -4
- data/.release-please-manifest.json +1 -1
- data/CHANGELOG.md +377 -0
- data/Cargo.lock +181 -154
- data/README.md +99 -459
- data/ROADMAP.md +7 -1
- data/SECURITY.md +1 -1
- data/crates/kobako-runtime/CHANGELOG.md +354 -0
- data/crates/kobako-runtime/Cargo.toml +38 -7
- data/crates/kobako-runtime/README.md +17 -8
- data/crates/kobako-runtime/src/dispatch.rs +9 -1
- data/crates/kobako-runtime/src/endpoint/buffer.rs +192 -0
- data/crates/kobako-runtime/src/endpoint/capture.rs +115 -0
- data/crates/kobako-runtime/src/endpoint/dispatch.rs +253 -0
- data/crates/kobako-runtime/src/endpoint/invoke.rs +194 -0
- data/crates/kobako-runtime/src/endpoint/memory_cap.rs +238 -0
- data/crates/kobako-runtime/src/endpoint.rs +166 -0
- data/crates/kobako-runtime/src/filesystem/bind/fid.rs +82 -0
- data/crates/kobako-runtime/src/filesystem/bind/lookup.rs +236 -0
- data/crates/kobako-runtime/src/filesystem/bind/walk.rs +249 -0
- data/crates/kobako-runtime/src/filesystem/bind.rs +2665 -0
- data/crates/kobako-runtime/src/filesystem/overlay/file.rs +89 -0
- data/crates/kobako-runtime/src/filesystem/overlay/inode.rs +106 -0
- data/crates/kobako-runtime/src/filesystem/overlay/path.rs +285 -0
- data/crates/kobako-runtime/src/filesystem/overlay/stack/copy_up.rs +135 -0
- data/crates/kobako-runtime/src/filesystem/overlay/stack/dir.rs +263 -0
- data/crates/kobako-runtime/src/filesystem/overlay/stack/namei.rs +135 -0
- data/crates/kobako-runtime/src/filesystem/overlay/stack/readdir.rs +57 -0
- data/crates/kobako-runtime/src/filesystem/overlay/stack.rs +301 -0
- data/crates/kobako-runtime/src/filesystem/overlay.rs +3469 -0
- data/crates/kobako-runtime/src/filesystem/path.rs +21 -0
- data/crates/kobako-runtime/src/filesystem/pipe.rs +221 -0
- data/crates/kobako-runtime/src/filesystem/rdir.rs +101 -0
- data/crates/kobako-runtime/src/filesystem/tmpfs/tmpfile.rs +32 -0
- data/crates/kobako-runtime/src/filesystem/tmpfs/whiteout.rs +85 -0
- data/crates/kobako-runtime/src/filesystem/tmpfs/xattr.rs +22 -0
- data/crates/kobako-runtime/src/filesystem/tmpfs.rs +1985 -0
- data/crates/kobako-runtime/src/filesystem/wrap.rs +224 -0
- data/crates/kobako-runtime/src/filesystem.rs +26 -0
- data/crates/kobako-runtime/src/grant.rs +20 -0
- data/crates/kobako-runtime/src/hashtable.rs +44 -0
- data/crates/kobako-runtime/src/lib.rs +16 -1
- data/crates/kobako-runtime/src/locking/seqlock.rs +66 -0
- data/crates/kobako-runtime/src/locking.rs +218 -0
- data/crates/kobako-runtime/src/mount.rs +105 -0
- data/crates/kobako-runtime/src/profile.rs +140 -4
- data/crates/kobako-runtime/src/runtime.rs +12 -0
- data/crates/kobako-runtime/src/sched.rs +210 -0
- data/crates/kobako-runtime/src/security.rs +696 -0
- data/crates/kobako-runtime/src/snapshot.rs +20 -5
- data/crates/kobako-runtime/src/vfs/attr.rs +162 -0
- data/crates/kobako-runtime/src/vfs/cred.rs +334 -0
- data/crates/kobako-runtime/src/vfs/dentry.rs +1412 -0
- data/crates/kobako-runtime/src/vfs/errno.rs +153 -0
- data/crates/kobako-runtime/src/vfs/file.rs +1915 -0
- data/crates/kobako-runtime/src/vfs/inode.rs +429 -0
- data/crates/kobako-runtime/src/vfs/kern_path.rs +217 -0
- data/crates/kobako-runtime/src/vfs/mount.rs +59 -0
- data/crates/kobako-runtime/src/vfs/namei.rs +5708 -0
- data/crates/kobako-runtime/src/vfs/openat2.rs +116 -0
- data/crates/kobako-runtime/src/vfs/path.rs +64 -0
- data/crates/kobako-runtime/src/vfs/readdir.rs +163 -0
- data/crates/kobako-runtime/src/vfs/stat.rs +157 -0
- data/crates/kobako-runtime/src/vfs/super_block.rs +122 -0
- data/crates/kobako-runtime/src/vfs/utimes.rs +289 -0
- data/crates/kobako-runtime/src/vfs.rs +60 -0
- data/crates/kobako-runtime/src/yielder.rs +11 -6
- data/crates/kobako-runtime/tests/linux_baseline/calls.rs +171 -0
- data/crates/kobako-runtime/tests/linux_baseline/concurrent.rs +276 -0
- data/crates/kobako-runtime/tests/linux_baseline/kobako.rs +323 -0
- data/crates/kobako-runtime/tests/linux_baseline/linux-baseline-expected.txt +16 -0
- data/crates/kobako-runtime/tests/linux_baseline/linux.rs +449 -0
- data/crates/kobako-runtime/tests/linux_baseline/main.rs +611 -0
- data/crates/kobako-runtime/tests/linux_baseline/permissions.rs +236 -0
- data/crates/kobako-transport/CHANGELOG.md +23 -0
- data/crates/kobako-transport/Cargo.toml +3 -3
- data/crates/kobako-transport/README.md +6 -5
- data/crates/kobako-transport/src/abi.rs +1 -1
- data/crates/kobako-transport/src/envelope/bytes.rs +33 -25
- data/crates/kobako-transport/src/envelope/call.rs +5 -5
- data/crates/kobako-transport/src/envelope/compile_reply.rs +2 -2
- data/crates/kobako-transport/src/envelope/error_record.rs +3 -3
- data/crates/kobako-transport/src/envelope/fault.rs +4 -3
- data/crates/kobako-transport/src/envelope/outcome.rs +3 -3
- data/crates/kobako-transport/src/envelope/preamble.rs +24 -24
- data/crates/kobako-transport/src/envelope/reply.rs +34 -16
- data/crates/kobako-transport/src/envelope/request.rs +19 -9
- data/crates/kobako-transport/src/envelope/run.rs +1 -1
- data/crates/kobako-transport/src/envelope/unsupported.rs +2 -2
- data/crates/kobako-transport/src/envelope.rs +2 -2
- data/crates/kobako-wasmtime/CHANGELOG.md +265 -0
- data/crates/kobako-wasmtime/Cargo.toml +25 -19
- data/crates/kobako-wasmtime/README.md +13 -7
- data/crates/kobako-wasmtime/src/abi.rs +75 -49
- data/crates/kobako-wasmtime/src/ambient.rs +64 -6
- data/crates/kobako-wasmtime/src/cache.rs +188 -77
- data/crates/kobako-wasmtime/src/config.rs +77 -5
- data/crates/kobako-wasmtime/src/descriptors.rs +200 -0
- data/crates/kobako-wasmtime/src/driver.rs +70 -135
- data/crates/kobako-wasmtime/src/exports.rs +5 -16
- data/crates/kobako-wasmtime/src/guest.rs +38 -0
- data/crates/kobako-wasmtime/src/imports.rs +52 -0
- data/crates/kobako-wasmtime/src/instance_pre.rs +18 -46
- data/crates/kobako-wasmtime/src/invocation.rs +43 -74
- data/crates/kobako-wasmtime/src/lib.rs +5 -4
- data/crates/kobako-wasmtime/src/limiter.rs +6 -230
- data/crates/kobako-wasmtime/src/preview1/differential/mount.rs +632 -0
- data/crates/kobako-wasmtime/src/preview1/differential.rs +486 -0
- data/crates/kobako-wasmtime/src/preview1/errno.rs +135 -0
- data/crates/kobako-wasmtime/src/preview1/fd.rs +1420 -0
- data/crates/kobako-wasmtime/src/preview1/memory.rs +172 -0
- data/crates/kobako-wasmtime/src/preview1/mount_view/wrap.rs +211 -0
- data/crates/kobako-wasmtime/src/preview1/mount_view.rs +3766 -0
- data/crates/kobako-wasmtime/src/preview1/path.rs +302 -0
- data/crates/kobako-wasmtime/src/preview1/poll.rs +381 -0
- data/crates/kobako-wasmtime/src/preview1/sock.rs +13 -0
- data/crates/kobako-wasmtime/src/preview1/testsuite-expected.txt +6 -0
- data/crates/kobako-wasmtime/src/preview1/testsuite.rs +157 -0
- data/crates/kobako-wasmtime/src/preview1.rs +512 -0
- data/crates/kobako-wasmtime/src/trap.rs +45 -25
- data/crates/kobako-wasmtime/src/wasi.rs +333 -0
- data/crates/kobako-wasmtime/tests/integration/compile_posture.rs +43 -0
- data/crates/kobako-wasmtime/tests/integration/guest_file.rs +1085 -0
- data/crates/kobako-wasmtime/tests/integration/main.rs +29 -0
- data/crates/kobako-wasmtime/tests/{preamble_flags.rs → integration/preamble_flags.rs} +4 -3
- data/crates/kobako-wasmtime/tests/{snippet_compile.rs → integration/snippet_compile.rs} +5 -4
- data/crates/kobako-wasmtime/tests/{support → integration/support}/mod.rs +9 -13
- data/crates/kobako-wasmtime/tests/integration/unanswered_call.rs +86 -0
- data/crates/kobako-wasmtime/tests/{unsupported_call.rs → integration/unsupported_call.rs} +2 -1
- data/crates/kobako-wasmtime/witx/typenames.witx +750 -0
- data/crates/kobako-wasmtime/witx/wasi_snapshot_preview1.witx +441 -0
- data/data/kobako.wasm +0 -0
- data/ext/kobako/Cargo.toml +4 -2
- data/ext/kobako/src/runtime/bridge.rs +154 -81
- data/ext/kobako/src/runtime/errors.rs +1 -1
- data/ext/kobako/src/runtime/escape.rs +67 -0
- data/ext/kobako/src/runtime/filesystem/grants.rs +107 -0
- data/ext/kobako/src/runtime/filesystem/ruby.rs +708 -0
- data/ext/kobako/src/runtime/filesystem.rs +208 -0
- data/ext/kobako/src/runtime/gvl.rs +49 -8
- data/ext/kobako/src/runtime/pinned_value.rs +31 -0
- data/ext/kobako/src/runtime/snapshot.rs +127 -0
- data/ext/kobako/src/runtime.rs +125 -199
- data/lib/kobako/catalog/extensions.rb +5 -5
- data/lib/kobako/catalog/flags.rb +2 -14
- data/lib/kobako/catalog/grants.rb +30 -0
- data/lib/kobako/catalog/handles.rb +6 -6
- data/lib/kobako/catalog/mount.rb +19 -0
- data/lib/kobako/catalog/services.rb +13 -2
- data/lib/kobako/catalog/snippets.rb +11 -15
- data/lib/kobako/catalog.rb +11 -0
- data/lib/kobako/codec/decoder.rb +10 -9
- data/lib/kobako/codec/ext_types.rb +4 -6
- data/lib/kobako/codec/handle_walk.rb +4 -6
- data/lib/kobako/codec/nesting.rb +7 -7
- data/lib/kobako/codec/state.rb +19 -3
- data/lib/kobako/codec/utils.rb +1 -1
- data/lib/kobako/codec/value_budget.rb +86 -0
- data/lib/kobako/codec.rb +7 -0
- data/lib/kobako/context.rb +40 -13
- data/lib/kobako/errors.rb +15 -14
- data/lib/kobako/filesystem.rb +69 -0
- data/lib/kobako/payload/arguments.rb +2 -2
- data/lib/kobako/pool.rb +6 -6
- data/lib/kobako/sandbox.rb +55 -27
- data/lib/kobako/sandbox_options.rb +39 -13
- data/lib/kobako/snippet/{binary.rb → bytecode.rb} +2 -2
- data/lib/kobako/snippet/source.rb +1 -1
- data/lib/kobako/snippet.rb +1 -1
- data/lib/kobako/transport/block_failure.rb +35 -0
- data/lib/kobako/transport/dispatcher.rb +33 -29
- data/lib/kobako/transport/exposure.rb +7 -9
- data/lib/kobako/transport/reflection.rb +79 -23
- data/lib/kobako/transport/run.rb +5 -5
- data/lib/kobako/transport/yielder.rb +9 -31
- data/lib/kobako/transport.rb +1 -0
- data/lib/kobako/version.rb +1 -1
- data/lib/kobako.rb +35 -0
- data/release-please-config.json +1 -0
- data/sig/kobako/catalog/extensions.rbs +3 -3
- data/sig/kobako/catalog/flags.rbs +0 -4
- data/sig/kobako/catalog/grants.rbs +13 -0
- data/sig/kobako/catalog/handles.rbs +3 -3
- data/sig/kobako/catalog/mount.rbs +24 -0
- data/sig/kobako/catalog/services.rbs +5 -1
- data/sig/kobako/catalog/snippets.rbs +4 -4
- data/sig/kobako/catalog.rbs +1 -0
- data/sig/kobako/codec/decoder.rbs +1 -1
- data/sig/kobako/codec/handle_walk.rbs +1 -1
- data/sig/kobako/codec/nesting.rbs +2 -2
- data/sig/kobako/codec/state.rbs +7 -1
- data/sig/kobako/codec/utils.rbs +1 -1
- data/sig/kobako/codec/value_budget.rbs +23 -0
- data/sig/kobako/codec.rbs +3 -2
- data/sig/kobako/context.rbs +14 -6
- data/sig/kobako/errors.rbs +4 -4
- data/sig/kobako/filesystem.rbs +93 -0
- data/sig/kobako/guest_yielder.rbs +9 -0
- data/sig/kobako/payload/arguments.rbs +1 -1
- data/sig/kobako/pool.rbs +3 -3
- data/sig/kobako/runtime.rbs +21 -49
- data/sig/kobako/sandbox.rbs +9 -5
- data/sig/kobako/sandbox_options.rbs +25 -3
- data/sig/kobako/snapshot.rbs +20 -0
- data/sig/kobako/snippet/bytecode.rbs +12 -0
- data/sig/kobako/transport/block_failure.rbs +13 -0
- data/sig/kobako/transport/dispatcher.rbs +4 -4
- data/sig/kobako/transport/exposure.rbs +2 -2
- data/sig/kobako/transport/reflection.rbs +14 -1
- data/sig/kobako/transport/run.rbs +3 -3
- data/sig/kobako/transport/yielder.rbs +3 -7
- data/sig/kobako/transport.rbs +3 -3
- metadata +116 -15
- data/crates/kobako-wasmtime/src/capture.rs +0 -96
- data/crates/kobako-wasmtime/src/dispatch.rs +0 -107
- data/crates/kobako-wasmtime/src/guest_mem.rs +0 -265
- data/crates/kobako-wasmtime/src/io.rs +0 -286
- 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
|
+
}
|