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
|
@@ -18,6 +18,7 @@ use kobako_transport::envelope::Outcome;
|
|
|
18
18
|
/// pre-invocation sentinel — no bytes, cap not reached — shared by every
|
|
19
19
|
/// frontend's before-first-invocation readout.
|
|
20
20
|
#[derive(Default)]
|
|
21
|
+
#[non_exhaustive]
|
|
21
22
|
pub struct Capture {
|
|
22
23
|
pub bytes: Vec<u8>,
|
|
23
24
|
pub truncated: bool,
|
|
@@ -39,12 +40,13 @@ pub enum Completion {
|
|
|
39
40
|
Trap(Trap),
|
|
40
41
|
}
|
|
41
42
|
|
|
42
|
-
/// Resource usage of one guest invocation, measured across the
|
|
43
|
-
///
|
|
44
|
-
///
|
|
45
|
-
/// `memory.grow` delta in bytes past the entry-time
|
|
46
|
-
/// (all zeros) is the pre-invocation sentinel.
|
|
43
|
+
/// Resource usage of one guest invocation, each measured across the span
|
|
44
|
+
/// its cap governs: `wall_time` is the seconds from setting the guest up to
|
|
45
|
+
/// its export call returning, the span the deadline covers; `memory_peak`
|
|
46
|
+
/// is the high-water `memory.grow` delta in bytes past the entry-time
|
|
47
|
+
/// baseline. `Default` (all zeros) is the pre-invocation sentinel.
|
|
47
48
|
#[derive(Clone, Copy, Default)]
|
|
49
|
+
#[non_exhaustive]
|
|
48
50
|
pub struct Usage {
|
|
49
51
|
pub wall_time: f64,
|
|
50
52
|
pub memory_peak: usize,
|
|
@@ -54,9 +56,22 @@ pub struct Usage {
|
|
|
54
56
|
/// kinds: captures and usage are collected on trap and outcome alike.
|
|
55
57
|
/// What a frontend exposes from the trap path is its own contract
|
|
56
58
|
/// decision.
|
|
59
|
+
#[non_exhaustive]
|
|
57
60
|
pub struct Snapshot {
|
|
58
61
|
pub completion: Completion,
|
|
59
62
|
pub stdout: Capture,
|
|
60
63
|
pub stderr: Capture,
|
|
61
64
|
pub usage: Usage,
|
|
62
65
|
}
|
|
66
|
+
|
|
67
|
+
impl Snapshot {
|
|
68
|
+
/// What an engine hands back from one invocation.
|
|
69
|
+
pub fn new(completion: Completion, stdout: Capture, stderr: Capture, usage: Usage) -> Self {
|
|
70
|
+
Self {
|
|
71
|
+
completion,
|
|
72
|
+
stdout,
|
|
73
|
+
stderr,
|
|
74
|
+
usage,
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
//! What a change sets of an entry, as Linux's `fs/attr.c` takes a
|
|
2
|
+
//! `struct iattr`.
|
|
3
|
+
|
|
4
|
+
use std::fmt;
|
|
5
|
+
use std::time::SystemTime;
|
|
6
|
+
|
|
7
|
+
use super::dentry::Dentry;
|
|
8
|
+
use super::file::FileOperations;
|
|
9
|
+
use super::FilesystemError;
|
|
10
|
+
use crate::locking::RwSemaphoreGuard;
|
|
11
|
+
|
|
12
|
+
/// Apply `attr` to what `dentry` names, as Linux's `notify_change` reaches
|
|
13
|
+
/// `->setattr`, the moment it lands read once, as `current_time` is, so every
|
|
14
|
+
/// time it sets to now reads the same. The caller holds the entry's lock and
|
|
15
|
+
/// hands its guard over, as `notify_change` warns unless `inode_is_locked`;
|
|
16
|
+
/// the lock was reached through the entry, which a dentry never drops.
|
|
17
|
+
pub(crate) fn notify_change(
|
|
18
|
+
dentry: &Dentry,
|
|
19
|
+
held: &RwSemaphoreGuard,
|
|
20
|
+
attr: &Iattr,
|
|
21
|
+
) -> Result<(), FilesystemError> {
|
|
22
|
+
let inode = dentry
|
|
23
|
+
.inode()
|
|
24
|
+
.expect("a dentry whose entry is locked names it");
|
|
25
|
+
debug_assert!(
|
|
26
|
+
inode.is_locked_by(held),
|
|
27
|
+
"notify_change takes the guard of the entry's own lock"
|
|
28
|
+
);
|
|
29
|
+
let attr = Iattr {
|
|
30
|
+
ctime: Some(SystemTime::now()),
|
|
31
|
+
..*attr
|
|
32
|
+
};
|
|
33
|
+
inode.ops().setattr(dentry, &attr)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/// What a change sets of an entry, as Linux's `struct iattr`; a field left
|
|
37
|
+
/// `None` is one `ia_valid` does not name.
|
|
38
|
+
#[derive(Clone, Copy)]
|
|
39
|
+
#[non_exhaustive]
|
|
40
|
+
pub struct Iattr<'a> {
|
|
41
|
+
/// The length to truncate or extend a file to, extending with zeros.
|
|
42
|
+
pub size: Option<u64>,
|
|
43
|
+
/// The access and modification times to set.
|
|
44
|
+
pub atime: Option<SetTime>,
|
|
45
|
+
pub mtime: Option<SetTime>,
|
|
46
|
+
/// Whether the size comes from an open asking to truncate, as `ATTR_OPEN`
|
|
47
|
+
/// says, so a Filesystem that truncated in `->open` has nothing left to do.
|
|
48
|
+
pub open: bool,
|
|
49
|
+
/// The moment the change lands, which `notify_change` sets, as Linux's
|
|
50
|
+
/// `ia_ctime`: what a time set to now reads, and the entry's change time.
|
|
51
|
+
pub ctime: Option<SystemTime>,
|
|
52
|
+
/// The open file a truncate is made through, as `ATTR_FILE` names
|
|
53
|
+
/// `ia_file`, so a Filesystem acts through what that open holds, as 9p
|
|
54
|
+
/// takes its fid.
|
|
55
|
+
pub file: Option<&'a dyn FileOperations>,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
impl Iattr<'static> {
|
|
59
|
+
/// A change truncating or extending a file to `len`, moving its modification
|
|
60
|
+
/// time to the moment it lands, as Linux's `do_truncate` names `ATTR_MTIME`
|
|
61
|
+
/// with `ATTR_SIZE`.
|
|
62
|
+
pub fn size(len: u64) -> Self {
|
|
63
|
+
Self {
|
|
64
|
+
size: Some(len),
|
|
65
|
+
atime: None,
|
|
66
|
+
mtime: Some(SetTime::Now),
|
|
67
|
+
open: false,
|
|
68
|
+
ctime: None,
|
|
69
|
+
file: None,
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/// A change emptying a file an open asked to truncate.
|
|
74
|
+
pub(crate) fn open_truncate() -> Self {
|
|
75
|
+
Self {
|
|
76
|
+
open: true,
|
|
77
|
+
..Self::size(0)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/// A change setting the times named, leaving any other as it stands.
|
|
82
|
+
pub fn times(atime: Option<SetTime>, mtime: Option<SetTime>) -> Self {
|
|
83
|
+
Self {
|
|
84
|
+
size: None,
|
|
85
|
+
atime,
|
|
86
|
+
mtime,
|
|
87
|
+
open: false,
|
|
88
|
+
ctime: None,
|
|
89
|
+
file: None,
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
impl<'a> Iattr<'a> {
|
|
95
|
+
/// This change made through the open file `file`, as `do_truncate` sets
|
|
96
|
+
/// `ATTR_FILE`.
|
|
97
|
+
pub(crate) fn through<'b>(self, file: &'b dyn FileOperations) -> Iattr<'b>
|
|
98
|
+
where
|
|
99
|
+
'a: 'b,
|
|
100
|
+
{
|
|
101
|
+
Iattr {
|
|
102
|
+
file: Some(file),
|
|
103
|
+
..self
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/// The time `set` names, now read as the moment this change lands.
|
|
108
|
+
pub fn time_of(&self, set: SetTime) -> SystemTime {
|
|
109
|
+
match set {
|
|
110
|
+
SetTime::Now => self.ctime.unwrap_or_else(SystemTime::now),
|
|
111
|
+
SetTime::At(time) => time,
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/// Two changes compare equal through the same open file.
|
|
117
|
+
impl PartialEq for Iattr<'_> {
|
|
118
|
+
fn eq(&self, other: &Self) -> bool {
|
|
119
|
+
let file = |attr: &Self| {
|
|
120
|
+
attr.file
|
|
121
|
+
.map(|file| file as *const dyn FileOperations as *const ())
|
|
122
|
+
};
|
|
123
|
+
(
|
|
124
|
+
self.size,
|
|
125
|
+
self.atime,
|
|
126
|
+
self.mtime,
|
|
127
|
+
self.open,
|
|
128
|
+
self.ctime,
|
|
129
|
+
file(self),
|
|
130
|
+
) == (
|
|
131
|
+
other.size,
|
|
132
|
+
other.atime,
|
|
133
|
+
other.mtime,
|
|
134
|
+
other.open,
|
|
135
|
+
other.ctime,
|
|
136
|
+
file(other),
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
impl Eq for Iattr<'_> {}
|
|
142
|
+
|
|
143
|
+
impl fmt::Debug for Iattr<'_> {
|
|
144
|
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
145
|
+
f.debug_struct("Iattr")
|
|
146
|
+
.field("size", &self.size)
|
|
147
|
+
.field("atime", &self.atime)
|
|
148
|
+
.field("mtime", &self.mtime)
|
|
149
|
+
.field("open", &self.open)
|
|
150
|
+
.field("ctime", &self.ctime)
|
|
151
|
+
.field("file", &self.file.is_some())
|
|
152
|
+
.finish()
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/// A time a change sets, as Linux's `ATTR_ATIME` names the moment it lands and
|
|
157
|
+
/// `ATTR_ATIME_SET` the time it carries.
|
|
158
|
+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
159
|
+
pub enum SetTime {
|
|
160
|
+
Now,
|
|
161
|
+
At(SystemTime),
|
|
162
|
+
}
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
//! Who a call is made for, as Linux's `struct cred`, and the hooks a security
|
|
2
|
+
//! module answers, as `security_*` call each registered LSM. The VFS asks them
|
|
3
|
+
//! where Linux does and knows nothing of what they decide.
|
|
4
|
+
|
|
5
|
+
use std::any::Any;
|
|
6
|
+
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
7
|
+
use std::sync::Arc;
|
|
8
|
+
|
|
9
|
+
use super::dentry::Dentry;
|
|
10
|
+
use super::file::FMode;
|
|
11
|
+
use super::inode::Mask;
|
|
12
|
+
use super::mount::VfsMount;
|
|
13
|
+
use super::openat2::OpenHow;
|
|
14
|
+
use super::{FileType, FilesystemError};
|
|
15
|
+
|
|
16
|
+
/// What one security module keeps on an open file, as Linux's `f_security`
|
|
17
|
+
/// blob holds each module's part; `None` where it keeps nothing.
|
|
18
|
+
pub type FileSecurity = Option<Arc<dyn Any + Send + Sync>>;
|
|
19
|
+
|
|
20
|
+
/// A path a security hook is asked about: the mount it stands on, and its name
|
|
21
|
+
/// from that mount's root, built only for a module that reads it, as `struct path`
|
|
22
|
+
/// leaves `d_path` to the module that needs one. The name is what
|
|
23
|
+
/// `Dentry::path_raw` answers for the entry when the module asks.
|
|
24
|
+
#[derive(Clone, Copy)]
|
|
25
|
+
pub struct SecurityPath<'a> {
|
|
26
|
+
pub mnt: &'a Arc<VfsMount>,
|
|
27
|
+
pub name: &'a dyn Fn() -> String,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/// Answer `hook` for `dentry` on `mnt`, its name read from where the dentry
|
|
31
|
+
/// stands when a module asks, as a module builds one from the `struct path` a
|
|
32
|
+
/// `security_*` hook is handed.
|
|
33
|
+
pub(crate) fn on_path<T>(
|
|
34
|
+
mnt: &Arc<VfsMount>,
|
|
35
|
+
dentry: &Dentry,
|
|
36
|
+
hook: impl FnOnce(SecurityPath<'_>) -> T,
|
|
37
|
+
) -> T {
|
|
38
|
+
hook(SecurityPath {
|
|
39
|
+
mnt,
|
|
40
|
+
name: &|| dentry.path_raw(),
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/// What a security module answers, as a hook in Linux's `lsm_hook_defs.h`. A hook
|
|
45
|
+
/// a module does not have allows the call, and a module can only refuse.
|
|
46
|
+
pub trait SecurityOperations: Send + Sync {
|
|
47
|
+
/// Whether a call may do what `mask` asks of what `dentry` names, as
|
|
48
|
+
/// `security_inode_permission` is asked once the entry itself allowed it.
|
|
49
|
+
fn inode_permission(&self, _dentry: &Dentry, _mask: Mask) -> Result<(), FilesystemError> {
|
|
50
|
+
Ok(())
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/// Whether a file may be made at `path`, as `security_path_mknod`.
|
|
54
|
+
fn path_mknod(&self, _path: SecurityPath<'_>) -> Result<(), FilesystemError> {
|
|
55
|
+
Ok(())
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/// Whether a directory may be made at `path`, as `security_path_mkdir`.
|
|
59
|
+
fn path_mkdir(&self, _path: SecurityPath<'_>) -> Result<(), FilesystemError> {
|
|
60
|
+
Ok(())
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/// Whether the directory at `path` may go, as `security_path_rmdir`.
|
|
64
|
+
fn path_rmdir(&self, _path: SecurityPath<'_>) -> Result<(), FilesystemError> {
|
|
65
|
+
Ok(())
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/// Whether the file at `path` may go, as `security_path_unlink`.
|
|
69
|
+
fn path_unlink(&self, _path: SecurityPath<'_>) -> Result<(), FilesystemError> {
|
|
70
|
+
Ok(())
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/// Whether a link may be made at `path`, as `security_path_symlink`.
|
|
74
|
+
fn path_symlink(&self, _path: SecurityPath<'_>) -> Result<(), FilesystemError> {
|
|
75
|
+
Ok(())
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/// Whether what `old` names may also go by `new`, as `security_path_link`.
|
|
79
|
+
fn path_link(
|
|
80
|
+
&self,
|
|
81
|
+
_old: SecurityPath<'_>,
|
|
82
|
+
_new: SecurityPath<'_>,
|
|
83
|
+
) -> Result<(), FilesystemError> {
|
|
84
|
+
Ok(())
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/// Whether what `old` names may move to `new`, the dentries of each given, as
|
|
88
|
+
/// `security_path_rename` is handed both.
|
|
89
|
+
fn path_rename(
|
|
90
|
+
&self,
|
|
91
|
+
_old: SecurityPath<'_>,
|
|
92
|
+
_new: SecurityPath<'_>,
|
|
93
|
+
_dentries: (&Arc<Dentry>, &Arc<Dentry>),
|
|
94
|
+
) -> Result<(), FilesystemError> {
|
|
95
|
+
Ok(())
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/// Whether a file of `kind` at `path` may be opened for `mode` as `how` asks,
|
|
99
|
+
/// as `security_file_open` reads `f_mode` and `f_flags` once `may_open`
|
|
100
|
+
/// allowed it, a file the open just made included, which `FMode::CREATED`
|
|
101
|
+
/// marks. What the module keeps on the open file answers its later hooks, as
|
|
102
|
+
/// Landlock keeps what it allowed in the file's security blob.
|
|
103
|
+
fn file_open(
|
|
104
|
+
&self,
|
|
105
|
+
_path: SecurityPath<'_>,
|
|
106
|
+
_kind: FileType,
|
|
107
|
+
_mode: FMode,
|
|
108
|
+
_how: &OpenHow,
|
|
109
|
+
) -> Result<FileSecurity, FilesystemError> {
|
|
110
|
+
Ok(None)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/// Whether the open file may be truncated, judged from what this module kept
|
|
114
|
+
/// on it as it opened, as Landlock's `hook_file_truncate` reads the file's
|
|
115
|
+
/// blob.
|
|
116
|
+
fn file_truncate(&self, _kept: &FileSecurity) -> Result<(), FilesystemError> {
|
|
117
|
+
Ok(())
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/// Whether times may be set on the entry at `path`. Linux has no path hook
|
|
121
|
+
/// for times, so this is kobako's own, beside `path_truncate` and
|
|
122
|
+
/// `path_chmod` in kind, letting a module judge the path a call names.
|
|
123
|
+
fn path_utimes(&self, _path: SecurityPath<'_>) -> Result<(), FilesystemError> {
|
|
124
|
+
Ok(())
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/// Whether where the link `dentry` names points may be read, as
|
|
128
|
+
/// `security_inode_readlink` is handed the link.
|
|
129
|
+
fn inode_readlink(&self, _dentry: &Dentry) -> Result<(), FilesystemError> {
|
|
130
|
+
Ok(())
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/// How many bytes Filesystems hold on one caller's behalf between calls, and the
|
|
135
|
+
/// most they may, as Linux's `__vm_enough_memory` weighs what `vm_committed_as`
|
|
136
|
+
/// counts against its limit, so a guest's descriptors cannot multiply a directory
|
|
137
|
+
/// past what it was allotted.
|
|
138
|
+
pub(crate) struct VmCommitted {
|
|
139
|
+
limit: usize,
|
|
140
|
+
used: AtomicUsize,
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
impl VmCommitted {
|
|
144
|
+
pub(crate) fn new(limit: usize) -> Arc<Self> {
|
|
145
|
+
Arc::new(Self {
|
|
146
|
+
limit,
|
|
147
|
+
used: AtomicUsize::new(0),
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/// Who a call is made for, holding the security modules registered for it, as
|
|
153
|
+
/// `cred->security` holds a Landlock domain, and the memory held on its behalf,
|
|
154
|
+
/// which every Cred restricted from it shares, as a task's memory cgroup does.
|
|
155
|
+
pub struct Cred {
|
|
156
|
+
security: Vec<Arc<dyn SecurityOperations>>,
|
|
157
|
+
committed: Option<Arc<VmCommitted>>,
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/// One no module is registered for, which this crate's tests make calls as.
|
|
161
|
+
#[cfg(test)]
|
|
162
|
+
impl Default for Cred {
|
|
163
|
+
fn default() -> Self {
|
|
164
|
+
Self {
|
|
165
|
+
security: Vec::new(),
|
|
166
|
+
committed: None,
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
impl Cred {
|
|
172
|
+
/// One with `security` registered.
|
|
173
|
+
pub(crate) fn new(security: Vec<Arc<dyn SecurityOperations>>) -> Arc<Self> {
|
|
174
|
+
Self::within(security, None)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/// One with `security` registered and the memory held on its behalf counted
|
|
178
|
+
/// in `committed`, without bound where none.
|
|
179
|
+
pub(crate) fn within(
|
|
180
|
+
security: Vec<Arc<dyn SecurityOperations>>,
|
|
181
|
+
committed: Option<Arc<VmCommitted>>,
|
|
182
|
+
) -> Arc<Self> {
|
|
183
|
+
Arc::new(Self {
|
|
184
|
+
security,
|
|
185
|
+
committed,
|
|
186
|
+
})
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/// This Cred with `more` modules registered after its own, sharing the
|
|
190
|
+
/// memory held on its behalf, as `landlock_restrict_self` adds a domain.
|
|
191
|
+
pub(crate) fn with_modules(&self, more: Vec<Arc<dyn SecurityOperations>>) -> Arc<Self> {
|
|
192
|
+
let mut security = self.security.clone();
|
|
193
|
+
security.extend(more);
|
|
194
|
+
Self::within(security, self.committed.clone())
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/// Ask each module in turn, the first refusal answering, as `call_int_hook`.
|
|
198
|
+
fn call_int_hook(
|
|
199
|
+
&self,
|
|
200
|
+
hook: impl Fn(&dyn SecurityOperations) -> Result<(), FilesystemError>,
|
|
201
|
+
) -> Result<(), FilesystemError> {
|
|
202
|
+
self.security
|
|
203
|
+
.iter()
|
|
204
|
+
.try_for_each(|module| hook(module.as_ref()))
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
pub(crate) fn security_inode_permission(
|
|
208
|
+
&self,
|
|
209
|
+
dentry: &Dentry,
|
|
210
|
+
mask: Mask,
|
|
211
|
+
) -> Result<(), FilesystemError> {
|
|
212
|
+
self.call_int_hook(|module| module.inode_permission(dentry, mask))
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
pub(crate) fn security_path_mknod(
|
|
216
|
+
&self,
|
|
217
|
+
path: SecurityPath<'_>,
|
|
218
|
+
) -> Result<(), FilesystemError> {
|
|
219
|
+
self.call_int_hook(|module| module.path_mknod(path))
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
pub(crate) fn security_path_mkdir(
|
|
223
|
+
&self,
|
|
224
|
+
path: SecurityPath<'_>,
|
|
225
|
+
) -> Result<(), FilesystemError> {
|
|
226
|
+
self.call_int_hook(|module| module.path_mkdir(path))
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
pub(crate) fn security_path_rmdir(
|
|
230
|
+
&self,
|
|
231
|
+
path: SecurityPath<'_>,
|
|
232
|
+
) -> Result<(), FilesystemError> {
|
|
233
|
+
self.call_int_hook(|module| module.path_rmdir(path))
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
pub(crate) fn security_path_unlink(
|
|
237
|
+
&self,
|
|
238
|
+
path: SecurityPath<'_>,
|
|
239
|
+
) -> Result<(), FilesystemError> {
|
|
240
|
+
self.call_int_hook(|module| module.path_unlink(path))
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
pub(crate) fn security_path_symlink(
|
|
244
|
+
&self,
|
|
245
|
+
path: SecurityPath<'_>,
|
|
246
|
+
) -> Result<(), FilesystemError> {
|
|
247
|
+
self.call_int_hook(|module| module.path_symlink(path))
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
pub(crate) fn security_path_link(
|
|
251
|
+
&self,
|
|
252
|
+
old: SecurityPath<'_>,
|
|
253
|
+
new: SecurityPath<'_>,
|
|
254
|
+
) -> Result<(), FilesystemError> {
|
|
255
|
+
self.call_int_hook(|module| module.path_link(old, new))
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
pub(crate) fn security_path_rename(
|
|
259
|
+
&self,
|
|
260
|
+
old: SecurityPath<'_>,
|
|
261
|
+
new: SecurityPath<'_>,
|
|
262
|
+
dentries: (&Arc<Dentry>, &Arc<Dentry>),
|
|
263
|
+
) -> Result<(), FilesystemError> {
|
|
264
|
+
self.call_int_hook(|module| module.path_rename(old, new, dentries))
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/// A file's room for what each module keeps on it, empty until an open
|
|
268
|
+
/// fills it, as `security_file_alloc` gives every file one blob per module.
|
|
269
|
+
pub(crate) fn security_file_alloc(&self) -> Vec<FileSecurity> {
|
|
270
|
+
vec![None; self.security.len()]
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/// Ask each module about the open, keeping what each keeps on the file in
|
|
274
|
+
/// the order the modules stand, as `security_file_open` fills `f_security`.
|
|
275
|
+
pub(crate) fn security_file_open(
|
|
276
|
+
&self,
|
|
277
|
+
path: SecurityPath<'_>,
|
|
278
|
+
kind: FileType,
|
|
279
|
+
mode: FMode,
|
|
280
|
+
how: &OpenHow,
|
|
281
|
+
) -> Result<Vec<FileSecurity>, FilesystemError> {
|
|
282
|
+
self.security
|
|
283
|
+
.iter()
|
|
284
|
+
.map(|module| module.file_open(path, kind, mode, how))
|
|
285
|
+
.collect()
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/// Ask each module whether the file may be truncated, handing each what it
|
|
289
|
+
/// kept on the file as it opened.
|
|
290
|
+
pub(crate) fn security_file_truncate(
|
|
291
|
+
&self,
|
|
292
|
+
kept: &[FileSecurity],
|
|
293
|
+
) -> Result<(), FilesystemError> {
|
|
294
|
+
self.security
|
|
295
|
+
.iter()
|
|
296
|
+
.zip(kept)
|
|
297
|
+
.try_for_each(|(module, kept)| module.file_truncate(kept))
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
pub(crate) fn security_path_utimes(
|
|
301
|
+
&self,
|
|
302
|
+
path: SecurityPath<'_>,
|
|
303
|
+
) -> Result<(), FilesystemError> {
|
|
304
|
+
self.call_int_hook(|module| module.path_utimes(path))
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
pub(crate) fn security_inode_readlink(&self, dentry: &Dentry) -> Result<(), FilesystemError> {
|
|
308
|
+
self.call_int_hook(|module| module.inode_readlink(dentry))
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/// Count `bytes` more held on this caller's behalf, refusing them past its
|
|
312
|
+
/// limit, as `security_vm_enough_memory_mm` ends in `__vm_enough_memory`.
|
|
313
|
+
pub(crate) fn security_vm_enough_memory(&self, bytes: usize) -> Result<(), FilesystemError> {
|
|
314
|
+
let Some(committed) = &self.committed else {
|
|
315
|
+
return Ok(());
|
|
316
|
+
};
|
|
317
|
+
committed
|
|
318
|
+
.used
|
|
319
|
+
.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |used| {
|
|
320
|
+
used.checked_add(bytes)
|
|
321
|
+
.filter(|&total| total <= committed.limit)
|
|
322
|
+
})
|
|
323
|
+
.map(|_| ())
|
|
324
|
+
.map_err(|_| FilesystemError::OutOfMemory)
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/// `bytes` counted earlier are held no more, as `vm_unacct_memory` gives
|
|
328
|
+
/// them back.
|
|
329
|
+
pub(crate) fn vm_unacct_memory(&self, bytes: usize) {
|
|
330
|
+
if let Some(committed) = &self.committed {
|
|
331
|
+
committed.used.fetch_sub(bytes, Ordering::Relaxed);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|