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,289 @@
|
|
|
1
|
+
//! Setting the times of an entry, as Linux's `utimensat` does. What answers is
|
|
2
|
+
//! the entry's own `setattr`, so a Filesystem decides whether it keeps a time
|
|
3
|
+
//! of the caller's choosing.
|
|
4
|
+
|
|
5
|
+
use std::sync::Arc;
|
|
6
|
+
|
|
7
|
+
use super::attr::notify_change;
|
|
8
|
+
use super::cred::{on_path, Cred};
|
|
9
|
+
use super::namei::{filename_lookup, retry_estale};
|
|
10
|
+
use super::path::{Path, Start};
|
|
11
|
+
use super::{FilesystemError, Iattr};
|
|
12
|
+
|
|
13
|
+
/// Set `times` on what the descriptor at `path` names, as Linux's `do_utimes_fd`.
|
|
14
|
+
pub fn do_utimes_fd(cred: &Arc<Cred>, path: &Path, times: &Iattr) -> Result<(), FilesystemError> {
|
|
15
|
+
vfs_utimes(cred, path, times)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/// Set `times` on what `path` names, as Linux's `vfs_utimes` asks the mount,
|
|
19
|
+
/// then the security hook, then `setattr` under the entry's lock. An entry
|
|
20
|
+
/// without `setattr` keeps no time to set, as this VFS holds none on an inode.
|
|
21
|
+
fn vfs_utimes(cred: &Arc<Cred>, path: &Path, times: &Iattr) -> Result<(), FilesystemError> {
|
|
22
|
+
path.mnt().mnt_want_write()?;
|
|
23
|
+
let dentry = path.dentry();
|
|
24
|
+
on_path(path.mnt(), dentry, |at| cred.security_path_utimes(at))?;
|
|
25
|
+
let held = dentry.inode_lock()?;
|
|
26
|
+
notify_change(dentry, &held, times)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/// Set `times` on what `path` beneath `start` names, a link at its end
|
|
30
|
+
/// followed where `follow` asks, as Linux's `do_utimes_path` walks to it first.
|
|
31
|
+
pub fn do_utimes_path(
|
|
32
|
+
cred: &Arc<Cred>,
|
|
33
|
+
start: &impl Start,
|
|
34
|
+
path: &str,
|
|
35
|
+
follow: bool,
|
|
36
|
+
times: &Iattr,
|
|
37
|
+
) -> Result<(), FilesystemError> {
|
|
38
|
+
retry_estale(
|
|
39
|
+
|| filename_lookup(cred, start, path, follow),
|
|
40
|
+
|nd| vfs_utimes(cred, &nd.path_to(nd.path()?), times),
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
#[cfg(test)]
|
|
45
|
+
mod tests {
|
|
46
|
+
use std::sync::Mutex;
|
|
47
|
+
use std::time::UNIX_EPOCH;
|
|
48
|
+
|
|
49
|
+
use super::*;
|
|
50
|
+
use crate::filesystem::{
|
|
51
|
+
FileSystemType, FileType, Filesystem, InodeOperations, Kstat, SetTime, Tmpfs,
|
|
52
|
+
};
|
|
53
|
+
use crate::mount::{Access as MountAccess, Mount, Policy};
|
|
54
|
+
use crate::security::{prepare_creds_within, restrict_self};
|
|
55
|
+
use crate::vfs::Dentry;
|
|
56
|
+
|
|
57
|
+
fn times() -> Iattr<'static> {
|
|
58
|
+
Iattr::times(Some(SetTime::Now), Some(SetTime::At(UNIX_EPOCH)))
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
fn mount_of(filesystem: Arc<dyn FileSystemType>, access: MountAccess) -> Mount {
|
|
62
|
+
let filesystem = Filesystem::new(filesystem).expect("the instance must stand");
|
|
63
|
+
Mount::new("/data", filesystem, access).expect("/data is a guest path")
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
fn tmpfs() -> Arc<Tmpfs> {
|
|
67
|
+
Arc::new([("file.txt", "x")].into_iter().collect())
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// A thread waiting on a lock it holds answers a deadlock, so only a change
|
|
71
|
+
// that takes the entry's lock answers one here.
|
|
72
|
+
// @behavior RT-236
|
|
73
|
+
#[test]
|
|
74
|
+
fn a_change_to_an_entry_holds_its_lock() {
|
|
75
|
+
let mount = mount_of(tmpfs(), MountAccess::ReadWrite);
|
|
76
|
+
let root = Path::root(mount.mnt());
|
|
77
|
+
let _held = root.dentry().inode_lock().expect("the lock must be free");
|
|
78
|
+
assert_eq!(
|
|
79
|
+
vfs_utimes(&Arc::default(), &root, ×()),
|
|
80
|
+
Err(FilesystemError::Deadlock),
|
|
81
|
+
"vfs::vfs_utimes on an entry whose lock its own thread holds must answer EDEADLK"
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// @behavior RT-236
|
|
86
|
+
#[test]
|
|
87
|
+
fn a_truncate_through_a_descriptor_holds_the_entry_lock() {
|
|
88
|
+
let mount = mount_of(tmpfs(), MountAccess::ReadWrite);
|
|
89
|
+
let write = crate::vfs::OpenHow {
|
|
90
|
+
write: true,
|
|
91
|
+
..Default::default()
|
|
92
|
+
};
|
|
93
|
+
let mut file = crate::vfs::kern_path::filp_open(
|
|
94
|
+
&Arc::default(),
|
|
95
|
+
&Path::root(mount.mnt()),
|
|
96
|
+
"file.txt",
|
|
97
|
+
&write,
|
|
98
|
+
)
|
|
99
|
+
.expect("the file opens to write");
|
|
100
|
+
let dentry = file.path().dentry().clone();
|
|
101
|
+
let _held = dentry.inode_lock().expect("the lock must be free");
|
|
102
|
+
assert_eq!(
|
|
103
|
+
file.truncate(0),
|
|
104
|
+
Err(FilesystemError::Deadlock),
|
|
105
|
+
"vfs::File::truncate on a file whose lock its own thread holds must answer EDEADLK, \
|
|
106
|
+
as do_ftruncate holds the inode's lock around notify_change"
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// @behavior RT-186
|
|
111
|
+
#[test]
|
|
112
|
+
fn setting_times_asks_the_mount_then_the_entry() {
|
|
113
|
+
let set = |access, path: &str| {
|
|
114
|
+
let mount = mount_of(tmpfs(), access);
|
|
115
|
+
do_utimes_path(
|
|
116
|
+
&Arc::default(),
|
|
117
|
+
&Path::root(mount.mnt()),
|
|
118
|
+
path,
|
|
119
|
+
false,
|
|
120
|
+
×(),
|
|
121
|
+
)
|
|
122
|
+
};
|
|
123
|
+
assert_eq!(
|
|
124
|
+
(
|
|
125
|
+
set(MountAccess::ReadWrite, "missing"),
|
|
126
|
+
set(MountAccess::Read, "file.txt"),
|
|
127
|
+
set(MountAccess::ReadWrite, "file.txt"),
|
|
128
|
+
),
|
|
129
|
+
(
|
|
130
|
+
Err(FilesystemError::NotFound),
|
|
131
|
+
Err(FilesystemError::ReadOnlyFilesystem),
|
|
132
|
+
Ok(()),
|
|
133
|
+
),
|
|
134
|
+
"vfs::do_utimes_path must answer what is not there ENOENT, a read-only Mount EROFS, and \
|
|
135
|
+
hand a Tmpfs entry the times"
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/// A directory answering every call but `setattr` by default.
|
|
140
|
+
struct NoSetattr;
|
|
141
|
+
|
|
142
|
+
impl FileSystemType for NoSetattr {
|
|
143
|
+
fn fill_super(self: Arc<Self>, sb: &mut Filesystem) -> Result<(), FilesystemError> {
|
|
144
|
+
sb.d_make_root(crate::vfs::Inode::new(
|
|
145
|
+
&crate::vfs::Kstat::new(crate::vfs::FileType::Dir, 0),
|
|
146
|
+
self,
|
|
147
|
+
));
|
|
148
|
+
Ok(())
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
impl InodeOperations for NoSetattr {
|
|
153
|
+
fn getattr(&self, _dentry: &Dentry) -> Result<Kstat, FilesystemError> {
|
|
154
|
+
Ok(Kstat::new(FileType::Dir, 0))
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// @behavior RT-186
|
|
159
|
+
#[test]
|
|
160
|
+
fn an_entry_without_setattr_answers_enotsup() {
|
|
161
|
+
let mount = mount_of(Arc::new(NoSetattr), MountAccess::ReadWrite);
|
|
162
|
+
assert_eq!(
|
|
163
|
+
vfs_utimes(&Arc::default(), &Path::root(mount.mnt()), ×()),
|
|
164
|
+
Err(FilesystemError::Unsupported),
|
|
165
|
+
"vfs::vfs_utimes on an entry without setattr must answer ENOTSUP"
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/// A Filesystem keeping what it was last asked to set.
|
|
170
|
+
#[derive(Default)]
|
|
171
|
+
struct KeepsTimes(Mutex<Option<Iattr<'static>>>);
|
|
172
|
+
|
|
173
|
+
impl FileSystemType for KeepsTimes {
|
|
174
|
+
fn fill_super(self: Arc<Self>, sb: &mut Filesystem) -> Result<(), FilesystemError> {
|
|
175
|
+
sb.d_make_root(crate::vfs::Inode::new(
|
|
176
|
+
&crate::vfs::Kstat::new(crate::vfs::FileType::Dir, 0),
|
|
177
|
+
self,
|
|
178
|
+
));
|
|
179
|
+
Ok(())
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
impl InodeOperations for KeepsTimes {
|
|
184
|
+
fn getattr(&self, _dentry: &Dentry) -> Result<Kstat, FilesystemError> {
|
|
185
|
+
Ok(Kstat::new(FileType::Dir, 0))
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
fn setattr(&self, _dentry: &Dentry, attr: &Iattr) -> Result<(), FilesystemError> {
|
|
189
|
+
*self.0.lock().unwrap() = Some(Iattr {
|
|
190
|
+
size: attr.size,
|
|
191
|
+
atime: attr.atime,
|
|
192
|
+
mtime: attr.mtime,
|
|
193
|
+
open: attr.open,
|
|
194
|
+
ctime: attr.ctime,
|
|
195
|
+
file: None,
|
|
196
|
+
});
|
|
197
|
+
Ok(())
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// @behavior RT-186
|
|
202
|
+
#[test]
|
|
203
|
+
fn a_filesystem_with_setattr_answers_for_the_times_itself() {
|
|
204
|
+
let filesystem = Arc::new(KeepsTimes::default());
|
|
205
|
+
let mount = mount_of(filesystem.clone(), MountAccess::ReadWrite);
|
|
206
|
+
let answer = vfs_utimes(&Arc::default(), &Path::root(mount.mnt()), ×());
|
|
207
|
+
let handed = filesystem
|
|
208
|
+
.0
|
|
209
|
+
.lock()
|
|
210
|
+
.unwrap()
|
|
211
|
+
.map(|attr| (attr.atime, attr.mtime, attr.ctime.is_some()));
|
|
212
|
+
assert_eq!(
|
|
213
|
+
(answer, handed),
|
|
214
|
+
(Ok(()), Some((times().atime, times().mtime, true))),
|
|
215
|
+
"vfs::vfs_utimes on a Filesystem with setattr must hand it the times and the moment \
|
|
216
|
+
the change lands, and answer what it answers"
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/// A Policy allowing everything, keeping each path it was asked about.
|
|
221
|
+
#[derive(Default)]
|
|
222
|
+
struct RecordsPaths(Mutex<Vec<String>>);
|
|
223
|
+
|
|
224
|
+
impl Policy for RecordsPaths {
|
|
225
|
+
fn allows(&self, path: &str, _access: MountAccess) -> bool {
|
|
226
|
+
self.0.lock().unwrap().push(path.to_string());
|
|
227
|
+
true
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// A file's descriptor outlives its name, so only the walk's own check
|
|
232
|
+
// keeps a Policy from judging a path it no longer stands at.
|
|
233
|
+
// @behavior RT-201
|
|
234
|
+
#[test]
|
|
235
|
+
fn a_time_set_through_a_descriptor_is_judged_where_it_stands() {
|
|
236
|
+
let policy = Arc::new(RecordsPaths::default());
|
|
237
|
+
let mount = mount_of(tmpfs(), MountAccess::ReadWrite).with_policy(policy.clone());
|
|
238
|
+
let cred = restrict_self(&prepare_creds_within(&[], None), [&mount]);
|
|
239
|
+
let root = Path::root(mount.mnt());
|
|
240
|
+
let how = crate::vfs::OpenHow {
|
|
241
|
+
read: true,
|
|
242
|
+
..Default::default()
|
|
243
|
+
};
|
|
244
|
+
let file = crate::vfs::kern_path::filp_open(&cred, &root, "file.txt", &how)
|
|
245
|
+
.unwrap_or_else(|_| panic!("the file must open"));
|
|
246
|
+
crate::vfs::filename_unlinkat(&cred, &root, "file.txt").expect("the name must go");
|
|
247
|
+
policy.0.lock().unwrap().clear();
|
|
248
|
+
assert_eq!(
|
|
249
|
+
(
|
|
250
|
+
crate::security::WalkGuard::new([&mount]).check(file.path()),
|
|
251
|
+
policy.0.lock().unwrap().clone()
|
|
252
|
+
),
|
|
253
|
+
(Err(FilesystemError::NotFound), Vec::new()),
|
|
254
|
+
"WalkGuard::check of a descriptor of a file whose name is gone, beneath a Policy, \
|
|
255
|
+
must answer ENOENT without asking the Policy about the path it stood at"
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
struct RefusesAll;
|
|
260
|
+
|
|
261
|
+
impl Policy for RefusesAll {
|
|
262
|
+
fn allows(&self, _path: &str, _access: MountAccess) -> bool {
|
|
263
|
+
false
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// A file opened to read reaches this with no write the Policy judged, so
|
|
268
|
+
// the call itself must ask.
|
|
269
|
+
// @behavior RT-186
|
|
270
|
+
#[test]
|
|
271
|
+
fn a_policy_is_asked_before_times_are_set() {
|
|
272
|
+
let filesystem = Arc::new(KeepsTimes::default());
|
|
273
|
+
let mount =
|
|
274
|
+
mount_of(filesystem.clone(), MountAccess::ReadWrite).with_policy(Arc::new(RefusesAll));
|
|
275
|
+
assert_eq!(
|
|
276
|
+
(
|
|
277
|
+
vfs_utimes(
|
|
278
|
+
&restrict_self(&prepare_creds_within(&[], None), [&mount]),
|
|
279
|
+
&Path::root(mount.mnt()),
|
|
280
|
+
×()
|
|
281
|
+
),
|
|
282
|
+
*filesystem.0.lock().unwrap(),
|
|
283
|
+
),
|
|
284
|
+
(Err(FilesystemError::PermissionDenied), None),
|
|
285
|
+
"vfs::vfs_utimes beneath a Policy refusing the path must answer EACCES and leave the \
|
|
286
|
+
Filesystem unasked"
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
//! The calls made on paths beneath a mount, answered as the VFS above a
|
|
2
|
+
//! filesystem answers them, so an ABI above only carries each call's arguments
|
|
3
|
+
//! in and its answer out.
|
|
4
|
+
//!
|
|
5
|
+
//! A path never climbs above the directory it is resolved beneath, as a
|
|
6
|
+
//! capability never widens, and a read-only mount and the security hooks
|
|
7
|
+
//! answer each open and each change before the Filesystem is asked, so a
|
|
8
|
+
//! Filesystem of a third party's checks neither.
|
|
9
|
+
|
|
10
|
+
mod attr;
|
|
11
|
+
mod cred;
|
|
12
|
+
mod dentry;
|
|
13
|
+
mod errno;
|
|
14
|
+
mod file;
|
|
15
|
+
mod inode;
|
|
16
|
+
#[cfg(test)]
|
|
17
|
+
pub(crate) mod kern_path;
|
|
18
|
+
mod mount;
|
|
19
|
+
mod namei;
|
|
20
|
+
mod openat2;
|
|
21
|
+
mod path;
|
|
22
|
+
mod readdir;
|
|
23
|
+
mod stat;
|
|
24
|
+
mod super_block;
|
|
25
|
+
mod utimes;
|
|
26
|
+
|
|
27
|
+
pub(crate) use namei::{
|
|
28
|
+
__start_renaming, d_revalidate, inode_permission, lookup_one_unlocked, may_create_dentry,
|
|
29
|
+
start_creating, start_removing, vfs_create, vfs_mkdir, vfs_rename, vfs_rmdir, vfs_unlink,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
pub(crate) use attr::notify_change;
|
|
33
|
+
pub use attr::{Iattr, SetTime};
|
|
34
|
+
pub(crate) use cred::VmCommitted;
|
|
35
|
+
pub use cred::{Cred, FileSecurity, SecurityOperations, SecurityPath};
|
|
36
|
+
pub use dentry::{Dentry, DentryOperations};
|
|
37
|
+
pub use errno::FilesystemError;
|
|
38
|
+
pub(crate) use file::alloc_file_pseudo;
|
|
39
|
+
pub use file::{
|
|
40
|
+
generic_file_llseek, generic_write_sync, FMode, File, FileOperations, Kiocb, PollMask, Whence,
|
|
41
|
+
};
|
|
42
|
+
pub use inode::{Finish, Identity, Inode, InodeOperations, Lookup, Mask, RenameFlag};
|
|
43
|
+
pub use mount::VfsMount;
|
|
44
|
+
pub use namei::{
|
|
45
|
+
do_file_open, filename_linkat, filename_mkdirat, filename_renameat2, filename_rmdir,
|
|
46
|
+
filename_symlinkat, filename_unlinkat,
|
|
47
|
+
};
|
|
48
|
+
pub use openat2::{build_open_flags, OpenFlags, OpenHow};
|
|
49
|
+
pub use path::{Path, Start};
|
|
50
|
+
pub use readdir::{
|
|
51
|
+
dir_emit_dots, generic_read_dir, verify_dirent_name, DirContext, DirEmit, DirEntry,
|
|
52
|
+
};
|
|
53
|
+
pub use stat::{do_readlinkat, vfs_fstatat, FileType, Kstat};
|
|
54
|
+
pub use super_block::{FileSystemType, SuperBlock};
|
|
55
|
+
pub use utimes::{do_utimes_fd, do_utimes_path};
|
|
56
|
+
|
|
57
|
+
/// Linux's limits on a path, its terminating NUL counted, and on one name
|
|
58
|
+
/// within it.
|
|
59
|
+
pub const PATH_MAX: usize = 4096;
|
|
60
|
+
pub const NAME_MAX: usize = 255;
|
|
@@ -15,11 +15,16 @@ use crate::error::Trap;
|
|
|
15
15
|
/// Host-initiated re-entry into the in-flight guest instance to run a
|
|
16
16
|
/// yielded block.
|
|
17
17
|
///
|
|
18
|
-
///
|
|
19
|
-
///
|
|
20
|
-
///
|
|
21
|
-
///
|
|
22
|
-
/// the
|
|
18
|
+
/// Each open Call has a `frame`, counted from `0` at the outermost.
|
|
19
|
+
/// `yield_to_block` ships `args` as a `yield` downcall to the block of the
|
|
20
|
+
/// open Call `frame` names, and returns the decoded Yield Reply,
|
|
21
|
+
/// `Unsupported` arm included, or a `Trap` — surfaced through the
|
|
22
|
+
/// frontend's trap-error mapping — when the re-entry traps, the guest
|
|
23
|
+
/// returns an empty or unframeable result, or a payload exceeds the
|
|
24
|
+
/// 16 MiB cap.
|
|
23
25
|
pub trait Yielder {
|
|
24
|
-
|
|
26
|
+
/// The frame of the Call this Yielder was lent for.
|
|
27
|
+
fn frame(&self) -> u32;
|
|
28
|
+
|
|
29
|
+
fn yield_to_block(&mut self, frame: u32, args: &[u8]) -> Result<YieldReply, Trap>;
|
|
25
30
|
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
//! The calls a sequence is made of, drawn from a seed so a run repeats and an
|
|
2
|
+
//! older commit can be held to the same one.
|
|
3
|
+
|
|
4
|
+
/// How many descriptors a sequence holds at once.
|
|
5
|
+
pub const SLOTS: usize = 4;
|
|
6
|
+
|
|
7
|
+
/// The modified time a call sets, seconds past the Unix epoch.
|
|
8
|
+
pub const UTIME_SECONDS: i64 = 1;
|
|
9
|
+
|
|
10
|
+
/// One call, made alike through the VFS and through Linux. A path call starts
|
|
11
|
+
/// from the root, or from the descriptor `base` names.
|
|
12
|
+
#[derive(Clone, Debug)]
|
|
13
|
+
pub enum Call {
|
|
14
|
+
Open(Open),
|
|
15
|
+
Close(usize),
|
|
16
|
+
Read(usize, usize),
|
|
17
|
+
Write(usize, usize),
|
|
18
|
+
Pread(usize, usize, u64),
|
|
19
|
+
Pwrite(usize, usize, u64),
|
|
20
|
+
Seek(usize, i64, u8),
|
|
21
|
+
Truncate(usize, u64),
|
|
22
|
+
Fallocate(usize, u64, u64),
|
|
23
|
+
Fstat(usize),
|
|
24
|
+
Fsync(usize),
|
|
25
|
+
Readdir(usize),
|
|
26
|
+
Stat {
|
|
27
|
+
base: Option<usize>,
|
|
28
|
+
path: String,
|
|
29
|
+
follow: bool,
|
|
30
|
+
},
|
|
31
|
+
Utimes {
|
|
32
|
+
base: Option<usize>,
|
|
33
|
+
path: String,
|
|
34
|
+
follow: bool,
|
|
35
|
+
},
|
|
36
|
+
Mkdir(Option<usize>, String),
|
|
37
|
+
Rmdir(Option<usize>, String),
|
|
38
|
+
Unlink(Option<usize>, String),
|
|
39
|
+
Rename(Option<usize>, String, String),
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/// What an open asks for, and the descriptor it lands in.
|
|
43
|
+
#[derive(Clone, Debug, Default)]
|
|
44
|
+
pub struct Open {
|
|
45
|
+
pub base: Option<usize>,
|
|
46
|
+
pub path: String,
|
|
47
|
+
pub read: bool,
|
|
48
|
+
pub write: bool,
|
|
49
|
+
pub create: bool,
|
|
50
|
+
pub exclusive: bool,
|
|
51
|
+
pub truncate: bool,
|
|
52
|
+
pub append: bool,
|
|
53
|
+
pub directory: bool,
|
|
54
|
+
pub nofollow: bool,
|
|
55
|
+
pub slot: usize,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
impl Call {
|
|
59
|
+
/// The call's name, which a difference and a coverage count are kept by.
|
|
60
|
+
pub fn kind(&self) -> &'static str {
|
|
61
|
+
match self {
|
|
62
|
+
Self::Open(_) => "Open",
|
|
63
|
+
Self::Close(_) => "Close",
|
|
64
|
+
Self::Read(..) => "Read",
|
|
65
|
+
Self::Write(..) => "Write",
|
|
66
|
+
Self::Pread(..) => "Pread",
|
|
67
|
+
Self::Pwrite(..) => "Pwrite",
|
|
68
|
+
Self::Seek(..) => "Seek",
|
|
69
|
+
Self::Truncate(..) => "Truncate",
|
|
70
|
+
Self::Fallocate(..) => "Fallocate",
|
|
71
|
+
Self::Utimes { .. } => "Utimes",
|
|
72
|
+
Self::Fstat(_) => "Fstat",
|
|
73
|
+
Self::Fsync(_) => "Fsync",
|
|
74
|
+
Self::Readdir(_) => "Readdir",
|
|
75
|
+
Self::Stat { .. } => "Stat",
|
|
76
|
+
Self::Mkdir(..) => "Mkdir",
|
|
77
|
+
Self::Rmdir(..) => "Rmdir",
|
|
78
|
+
Self::Unlink(..) => "Unlink",
|
|
79
|
+
Self::Rename(..) => "Rename",
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/// The bytes a write of `len` carries.
|
|
85
|
+
pub fn payload(len: usize) -> &'static [u8] {
|
|
86
|
+
&b"WXYZ"[..len.min(4)]
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/// A small generator, so a seed names the same sequence on every toolchain.
|
|
90
|
+
pub struct Rng(u64);
|
|
91
|
+
|
|
92
|
+
impl Rng {
|
|
93
|
+
pub fn new(seed: u64) -> Self {
|
|
94
|
+
let mut rng = Self(seed.wrapping_mul(0x9E37_79B9_7F4A_7C15) | 1);
|
|
95
|
+
for _ in 0..4 {
|
|
96
|
+
rng.next();
|
|
97
|
+
}
|
|
98
|
+
rng
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
fn next(&mut self) -> u64 {
|
|
102
|
+
self.0 ^= self.0 << 13;
|
|
103
|
+
self.0 ^= self.0 >> 7;
|
|
104
|
+
self.0 ^= self.0 << 17;
|
|
105
|
+
self.0
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
pub fn random_below(&mut self, bound: u64) -> u64 {
|
|
109
|
+
self.next() % bound
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
fn random_ratio(&mut self, numerator: u64, denominator: u64) -> bool {
|
|
113
|
+
self.random_below(denominator) < numerator
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
fn choose_path(&mut self, paths: &[String]) -> String {
|
|
117
|
+
paths[self.random_below(paths.len() as u64) as usize].clone()
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/// The next call of a sequence over `paths`.
|
|
121
|
+
pub fn call(&mut self, paths: &[String]) -> Call {
|
|
122
|
+
let slot = self.random_below(SLOTS as u64) as usize;
|
|
123
|
+
let base = self
|
|
124
|
+
.random_ratio(25, 100)
|
|
125
|
+
.then(|| self.random_below(SLOTS as u64) as usize);
|
|
126
|
+
match self.random_below(100) {
|
|
127
|
+
0..=27 => Call::Open(Open {
|
|
128
|
+
base,
|
|
129
|
+
path: self.choose_path(paths),
|
|
130
|
+
read: self.random_ratio(70, 100),
|
|
131
|
+
write: self.random_ratio(45, 100),
|
|
132
|
+
create: self.random_ratio(30, 100),
|
|
133
|
+
exclusive: self.random_ratio(10, 100),
|
|
134
|
+
truncate: self.random_ratio(10, 100),
|
|
135
|
+
append: self.random_ratio(12, 100),
|
|
136
|
+
directory: self.random_ratio(12, 100),
|
|
137
|
+
nofollow: self.random_ratio(15, 100),
|
|
138
|
+
slot,
|
|
139
|
+
}),
|
|
140
|
+
28..=32 => Call::Close(slot),
|
|
141
|
+
33..=38 => Call::Read(slot, self.random_below(8) as usize),
|
|
142
|
+
39..=45 => Call::Write(slot, self.random_below(4) as usize),
|
|
143
|
+
46..=48 => Call::Pread(slot, self.random_below(8) as usize, self.random_below(10)),
|
|
144
|
+
49..=51 => Call::Pwrite(slot, self.random_below(4) as usize, self.random_below(10)),
|
|
145
|
+
52..=54 => Call::Seek(
|
|
146
|
+
slot,
|
|
147
|
+
self.random_below(14) as i64 - 4,
|
|
148
|
+
self.random_below(3) as u8,
|
|
149
|
+
),
|
|
150
|
+
55..=58 => Call::Truncate(slot, self.random_below(12)),
|
|
151
|
+
59..=62 => Call::Fstat(slot),
|
|
152
|
+
63..=64 => Call::Fsync(slot),
|
|
153
|
+
65..=68 => Call::Readdir(slot),
|
|
154
|
+
69..=73 => Call::Stat {
|
|
155
|
+
base,
|
|
156
|
+
path: self.choose_path(paths),
|
|
157
|
+
follow: self.random_ratio(50, 100),
|
|
158
|
+
},
|
|
159
|
+
74..=79 => Call::Mkdir(base, self.choose_path(paths)),
|
|
160
|
+
80..=85 => Call::Rmdir(base, self.choose_path(paths)),
|
|
161
|
+
86..=89 => Call::Unlink(base, self.choose_path(paths)),
|
|
162
|
+
90 => Call::Fallocate(slot, self.random_below(10), self.random_below(5)),
|
|
163
|
+
91 => Call::Utimes {
|
|
164
|
+
base,
|
|
165
|
+
path: self.choose_path(paths),
|
|
166
|
+
follow: self.random_ratio(50, 100),
|
|
167
|
+
},
|
|
168
|
+
_ => Call::Rename(base, self.choose_path(paths), self.choose_path(paths)),
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|