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.
- checksums.yaml +4 -4
- data/.release-please-manifest.json +1 -1
- data/CHANGELOG.md +217 -10
- data/Cargo.lock +139 -141
- data/README.md +96 -574
- data/ROADMAP.md +2 -1
- data/crates/kobako-runtime/CHANGELOG.md +269 -1
- data/crates/kobako-runtime/Cargo.toml +25 -15
- data/crates/kobako-runtime/README.md +7 -3
- data/crates/kobako-runtime/src/dispatch.rs +1 -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/{directory/spelling.rs → bind/lookup.rs} +91 -49
- 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 +227 -734
- data/crates/kobako-runtime/src/filesystem/overlay.rs +2649 -1135
- data/crates/kobako-runtime/src/filesystem/path.rs +2 -41
- 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 +21 -387
- data/crates/kobako-runtime/src/hashtable.rs +44 -0
- data/crates/kobako-runtime/src/lib.rs +11 -6
- 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 +31 -104
- data/crates/kobako-runtime/src/profile.rs +137 -1
- data/crates/kobako-runtime/src/sched.rs +210 -0
- data/crates/kobako-runtime/src/security.rs +696 -0
- 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/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 +8 -2
- data/crates/kobako-transport/Cargo.toml +3 -3
- data/crates/kobako-transport/README.md +6 -5
- 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 +2 -2
- data/crates/kobako-transport/src/envelope/outcome.rs +3 -3
- data/crates/kobako-transport/src/envelope/preamble.rs +12 -12
- data/crates/kobako-transport/src/envelope/reply.rs +2 -2
- data/crates/kobako-transport/src/envelope/request.rs +2 -2
- 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 +154 -8
- data/crates/kobako-wasmtime/Cargo.toml +15 -14
- data/crates/kobako-wasmtime/README.md +9 -5
- data/crates/kobako-wasmtime/src/abi.rs +12 -20
- data/crates/kobako-wasmtime/src/ambient.rs +1 -2
- data/crates/kobako-wasmtime/src/cache.rs +5 -5
- data/crates/kobako-wasmtime/src/config.rs +22 -7
- data/crates/kobako-wasmtime/src/descriptors.rs +133 -100
- data/crates/kobako-wasmtime/src/driver.rs +29 -96
- 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 +14 -8
- data/crates/kobako-wasmtime/src/instance_pre.rs +3 -3
- data/crates/kobako-wasmtime/src/invocation.rs +26 -46
- data/crates/kobako-wasmtime/src/lib.rs +1 -4
- data/crates/kobako-wasmtime/src/limiter.rs +6 -230
- data/crates/kobako-wasmtime/src/preview1/differential/mount.rs +69 -46
- data/crates/kobako-wasmtime/src/preview1/differential.rs +121 -7
- data/crates/kobako-wasmtime/src/preview1/errno.rs +74 -206
- data/crates/kobako-wasmtime/src/preview1/fd.rs +1248 -500
- data/crates/kobako-wasmtime/src/preview1/memory.rs +117 -150
- data/crates/kobako-wasmtime/src/preview1/mount_view/wrap.rs +211 -0
- data/crates/kobako-wasmtime/src/preview1/mount_view.rs +1939 -672
- data/crates/kobako-wasmtime/src/preview1/path.rs +230 -349
- data/crates/kobako-wasmtime/src/preview1/poll.rs +148 -100
- data/crates/kobako-wasmtime/src/preview1/sock.rs +6 -23
- 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 +464 -413
- data/crates/kobako-wasmtime/src/trap.rs +9 -9
- data/crates/kobako-wasmtime/src/wasi.rs +53 -37
- data/crates/kobako-wasmtime/tests/{guest_file.rs → integration/guest_file.rs} +232 -201
- data/crates/kobako-wasmtime/tests/integration/main.rs +29 -0
- data/crates/kobako-wasmtime/tests/{preamble_flags.rs → integration/preamble_flags.rs} +1 -1
- data/crates/kobako-wasmtime/tests/{snippet_compile.rs → integration/snippet_compile.rs} +1 -1
- data/crates/kobako-wasmtime/tests/{unanswered_call.rs → integration/unanswered_call.rs} +1 -1
- data/crates/kobako-wasmtime/tests/{unsupported_call.rs → integration/unsupported_call.rs} +1 -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 +1 -1
- data/ext/kobako/src/runtime/bridge.rs +1 -1
- data/ext/kobako/src/runtime/errors.rs +1 -1
- data/ext/kobako/src/runtime/escape.rs +28 -14
- data/ext/kobako/src/runtime/filesystem/grants.rs +21 -5
- data/ext/kobako/src/runtime/filesystem/ruby.rs +464 -156
- data/ext/kobako/src/runtime/filesystem.rs +109 -137
- data/ext/kobako/src/runtime/gvl.rs +43 -8
- data/ext/kobako/src/runtime/snapshot.rs +1 -1
- data/ext/kobako/src/runtime.rs +6 -1
- data/lib/kobako/catalog/extensions.rb +5 -5
- data/lib/kobako/catalog/flags.rb +2 -2
- data/lib/kobako/catalog/handles.rb +6 -6
- data/lib/kobako/catalog/mount.rb +2 -10
- data/lib/kobako/catalog/services.rb +2 -2
- data/lib/kobako/catalog/snippets.rb +7 -7
- data/lib/kobako/catalog.rb +1 -1
- data/lib/kobako/codec/decoder.rb +10 -10
- data/lib/kobako/codec/ext_types.rb +3 -5
- data/lib/kobako/codec/handle_walk.rb +4 -6
- data/lib/kobako/codec/nesting.rb +7 -7
- data/lib/kobako/codec/utils.rb +1 -1
- data/lib/kobako/codec/value_budget.rb +3 -3
- data/lib/kobako/context.rb +5 -5
- data/lib/kobako/filesystem.rb +44 -13
- data/lib/kobako/payload/arguments.rb +2 -2
- data/lib/kobako/pool.rb +4 -4
- data/lib/kobako/sandbox.rb +28 -28
- data/lib/kobako/sandbox_options.rb +2 -3
- data/lib/kobako/snippet/bytecode.rb +1 -1
- data/lib/kobako/snippet/source.rb +1 -1
- data/lib/kobako/transport/dispatcher.rb +5 -5
- data/lib/kobako/transport/exposure.rb +2 -2
- data/lib/kobako/transport/reflection.rb +7 -7
- data/lib/kobako/transport/run.rb +5 -5
- data/lib/kobako/transport/yielder.rb +1 -1
- data/lib/kobako/version.rb +1 -1
- data/lib/kobako.rb +34 -0
- data/release-please-config.json +1 -0
- data/sig/kobako/catalog/extensions.rbs +3 -3
- data/sig/kobako/catalog/handles.rbs +3 -3
- data/sig/kobako/catalog/mount.rbs +0 -4
- data/sig/kobako/catalog/services.rbs +1 -1
- data/sig/kobako/catalog/snippets.rbs +3 -3
- data/sig/kobako/catalog.rbs +1 -1
- 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/utils.rbs +1 -1
- data/sig/kobako/codec/value_budget.rbs +1 -1
- data/sig/kobako/codec.rbs +1 -2
- data/sig/kobako/context.rbs +1 -1
- data/sig/kobako/filesystem.rbs +19 -14
- data/sig/kobako/guest_yielder.rbs +1 -1
- data/sig/kobako/payload/arguments.rbs +1 -1
- data/sig/kobako/pool.rbs +2 -2
- data/sig/kobako/runtime.rbs +1 -1
- data/sig/kobako/sandbox.rbs +3 -5
- data/sig/kobako/sandbox_options.rbs +1 -1
- data/sig/kobako/transport/dispatcher.rbs +2 -2
- data/sig/kobako/transport/exposure.rbs +1 -1
- data/sig/kobako/transport/reflection.rbs +2 -2
- data/sig/kobako/transport/run.rbs +3 -3
- data/sig/kobako/transport.rbs +1 -1
- metadata +67 -19
- data/crates/kobako-runtime/src/filesystem/directory.rs +0 -1082
- data/crates/kobako-runtime/src/filesystem/memory.rs +0 -1100
- data/crates/kobako-runtime/src/filesystem/overlay/exclusive.rs +0 -88
- data/crates/kobako-runtime/src/filesystem/temporary.rs +0 -167
- data/crates/kobako-wasmtime/src/capture.rs +0 -96
- data/crates/kobako-wasmtime/src/dispatch.rs +0 -125
- data/crates/kobako-wasmtime/src/guest_mem.rs +0 -271
- data/crates/kobako-wasmtime/src/io.rs +0 -89
- data/crates/kobako-wasmtime/src/preview1/args.rs +0 -60
- data/crates/kobako-wasmtime/src/preview1/beneath.rs +0 -188
- /data/crates/kobako-wasmtime/tests/{compile_posture.rs → integration/compile_posture.rs} +0 -0
- /data/crates/kobako-wasmtime/tests/{support → integration/support}/mod.rs +0 -0
|
@@ -1,32 +1,34 @@
|
|
|
1
|
-
//! The guest's linear memory as one preview1 call
|
|
2
|
-
//!
|
|
3
|
-
//!
|
|
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
4
|
|
|
5
5
|
use std::cell::Cell;
|
|
6
|
-
use std::ops::Range;
|
|
7
6
|
|
|
8
|
-
use
|
|
9
|
-
use super::errno::{Answer, Errno, Failure};
|
|
7
|
+
use wiggle::{GuestPtr, GuestType};
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
9
|
+
use super::errno::{Answer, Errno};
|
|
10
|
+
use super::types;
|
|
14
11
|
|
|
15
|
-
///
|
|
16
|
-
///
|
|
17
|
-
|
|
18
|
-
|
|
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
|
+
}
|
|
19
21
|
|
|
20
|
-
pub(crate) struct GuestMemory<'a> {
|
|
21
|
-
|
|
22
|
+
pub(crate) struct GuestMemory<'a, 'm> {
|
|
23
|
+
memory: &'a mut wiggle::GuestMemory<'m>,
|
|
22
24
|
fuel: Cell<u64>,
|
|
23
25
|
}
|
|
24
26
|
|
|
25
|
-
impl<'a> GuestMemory<'a> {
|
|
26
|
-
pub(crate) fn new(
|
|
27
|
+
impl<'a, 'm> GuestMemory<'a, 'm> {
|
|
28
|
+
pub(crate) fn new(memory: &'a mut wiggle::GuestMemory<'m>, fuel: usize) -> Self {
|
|
27
29
|
Self {
|
|
28
|
-
|
|
29
|
-
fuel: Cell::new(
|
|
30
|
+
memory,
|
|
31
|
+
fuel: Cell::new(fuel as u64),
|
|
30
32
|
}
|
|
31
33
|
}
|
|
32
34
|
|
|
@@ -39,129 +41,67 @@ impl<'a> GuestMemory<'a> {
|
|
|
39
41
|
Ok(())
|
|
40
42
|
}
|
|
41
43
|
|
|
42
|
-
fn
|
|
43
|
-
|
|
44
|
-
match start.checked_add(len as usize) {
|
|
45
|
-
Some(end) if end <= self.bytes.len() => Ok(start..end),
|
|
46
|
-
_ => Err(out_of_bounds()),
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/// Hold `ptr` to the alignment of the struct or integer it points at.
|
|
51
|
-
pub(crate) fn check_aligned(ptr: u32, align: u32) -> Answer {
|
|
52
|
-
if !ptr.is_multiple_of(align) {
|
|
53
|
-
return Err(Failure::trap("pointer not aligned for what it points at"));
|
|
54
|
-
}
|
|
55
|
-
Ok(())
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/// The address of element `index` of an array of `size`-byte elements
|
|
59
|
-
/// at `base`.
|
|
60
|
-
pub(crate) fn element(base: u32, index: u32, size: u32) -> Answer<u32> {
|
|
61
|
-
Self::field(base, index.checked_mul(size).ok_or_else(out_of_bounds)?)
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/// The address `offset` bytes past `base`, which traps rather than
|
|
65
|
-
/// wrapping past the end of the address space.
|
|
66
|
-
pub(crate) fn field(base: u32, offset: u32) -> Answer<u32> {
|
|
67
|
-
base.checked_add(offset).ok_or_else(out_of_bounds)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
pub(crate) fn bytes(&self, ptr: u32, len: u32) -> Answer<&[u8]> {
|
|
71
|
-
let range = self.range(ptr, len)?;
|
|
72
|
-
Ok(&self.bytes[range])
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
pub(crate) fn bytes_mut(&mut self, ptr: u32, len: u32) -> Answer<&mut [u8]> {
|
|
76
|
-
let range = self.range(ptr, len)?;
|
|
77
|
-
Ok(&mut self.bytes[range])
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
fn read<const N: usize>(&self, ptr: u32) -> Answer<[u8; N]> {
|
|
81
|
-
Self::check_aligned(ptr, N as u32)?;
|
|
82
|
-
let mut out = [0; N];
|
|
83
|
-
out.copy_from_slice(self.bytes(ptr, N as u32)?);
|
|
84
|
-
Ok(out)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
pub(crate) fn read_u8(&self, ptr: u32) -> Answer<u8> {
|
|
88
|
-
self.read::<1>(ptr).map(|b| b[0])
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
pub(crate) fn read_u16(&self, ptr: u32) -> Answer<u16> {
|
|
92
|
-
self.read(ptr).map(u16::from_le_bytes)
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
pub(crate) fn read_u32(&self, ptr: u32) -> Answer<u32> {
|
|
96
|
-
self.read(ptr).map(u32::from_le_bytes)
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
pub(crate) fn read_u64(&self, ptr: u32) -> Answer<u64> {
|
|
100
|
-
self.read(ptr).map(u64::from_le_bytes)
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/// Copy `bytes` into guest memory at `ptr`, which needs no alignment.
|
|
104
|
-
pub(crate) fn write(&mut self, ptr: u32, bytes: &[u8]) -> Answer {
|
|
105
|
-
self.bytes_mut(ptr, bytes.len() as u32)?
|
|
106
|
-
.copy_from_slice(bytes);
|
|
107
|
-
Ok(())
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
fn write_aligned<const N: usize>(&mut self, ptr: u32, bytes: [u8; N]) -> Answer {
|
|
111
|
-
Self::check_aligned(ptr, N as u32)?;
|
|
112
|
-
self.write(ptr, &bytes)
|
|
44
|
+
pub(crate) fn bytes(&self, ptr: GuestPtr<[u8]>) -> Answer<&[u8]> {
|
|
45
|
+
require_unshared(self.memory.as_slice(ptr)?)
|
|
113
46
|
}
|
|
114
47
|
|
|
115
|
-
pub(crate) fn
|
|
116
|
-
self.
|
|
48
|
+
pub(crate) fn bytes_mut(&mut self, ptr: GuestPtr<[u8]>) -> Answer<&mut [u8]> {
|
|
49
|
+
require_unshared(self.memory.as_slice_mut(ptr)?)
|
|
117
50
|
}
|
|
118
51
|
|
|
119
|
-
pub(crate) fn
|
|
120
|
-
self.
|
|
52
|
+
pub(crate) fn read<T: GuestType>(&self, ptr: GuestPtr<T>) -> Answer<T> {
|
|
53
|
+
Ok(self.memory.read(ptr)?)
|
|
121
54
|
}
|
|
122
55
|
|
|
123
|
-
pub(crate) fn
|
|
124
|
-
self.
|
|
56
|
+
pub(crate) fn write<T: GuestType>(&mut self, ptr: GuestPtr<T>, value: T) -> Answer {
|
|
57
|
+
Ok(self.memory.write(ptr, value)?)
|
|
125
58
|
}
|
|
126
59
|
|
|
127
|
-
|
|
128
|
-
|
|
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))?)
|
|
129
65
|
}
|
|
130
66
|
|
|
131
67
|
/// A guest path, which preview1 requires to be UTF-8, charged to the
|
|
132
|
-
/// call's fuel before it is read, as wasmtime-wasi charges it.
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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?)?);
|
|
159
99
|
if len != 0 {
|
|
160
100
|
self.consume(len, 1)?;
|
|
161
|
-
return Ok((
|
|
101
|
+
return Ok(ptr.as_array(len));
|
|
162
102
|
}
|
|
163
103
|
}
|
|
164
|
-
Ok((0, 0))
|
|
104
|
+
Ok(GuestPtr::new((0, 0)))
|
|
165
105
|
}
|
|
166
106
|
}
|
|
167
107
|
|
|
@@ -169,37 +109,64 @@ impl<'a> GuestMemory<'a> {
|
|
|
169
109
|
mod tests {
|
|
170
110
|
use super::*;
|
|
171
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
|
+
|
|
172
142
|
// Neither the array nor the buffer exists in this small memory, so the
|
|
173
143
|
// budget alone, spent before anything is read, refuses each.
|
|
174
144
|
// @behavior RT-096
|
|
175
145
|
#[test]
|
|
176
146
|
fn a_call_past_its_hostcall_fuel_answers_nomem() {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
)
|
|
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)
|
|
190
166
|
),
|
|
167
|
+
(Some(Errno::Nomem), Some(Errno::Nomem)),
|
|
191
168
|
"an iovec array or a buffer past 128 MiB through one preview1 call must answer \
|
|
192
169
|
NOMEM before the host copies any of it"
|
|
193
170
|
);
|
|
194
171
|
}
|
|
195
|
-
|
|
196
|
-
// A guest memory of the full 4 GiB lets a field's address pass the end
|
|
197
|
-
// of the address space, which only the arithmetic itself can refuse.
|
|
198
|
-
#[test]
|
|
199
|
-
fn a_field_past_the_address_space_traps_rather_than_wrapping() {
|
|
200
|
-
assert!(
|
|
201
|
-
matches!(GuestMemory::field(u32::MAX - 4, 8), Err(Failure::Trap(_))),
|
|
202
|
-
"a field address past u32::MAX through GuestMemory::field must trap rather than wrap"
|
|
203
|
-
);
|
|
204
|
-
}
|
|
205
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
|
+
}
|