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,301 @@
|
|
|
1
|
+
//! The Layers of an Overlay and the rules that make them one view: which
|
|
2
|
+
//! Layer shows a name, how a change lands in the upper, and the marks that
|
|
3
|
+
//! hide what a lower Layer still holds. The calls on the view are split
|
|
4
|
+
//! as overlayfs splits them: finding a name in `namei`, copying up in
|
|
5
|
+
//! `copy_up`, listing in `readdir`, and changing a directory in `dir`.
|
|
6
|
+
//!
|
|
7
|
+
//! Every Layer is asked about a single name in a directory already held, as
|
|
8
|
+
//! overlayfs looks a name up in each layer's directory. A Layer's directory
|
|
9
|
+
//! merges into the view only beneath a directory it also holds.
|
|
10
|
+
//!
|
|
11
|
+
//! The upper Tmpfs holds the view's changes in `upper` and builds each one
|
|
12
|
+
//! in `work` first, as overlayfs's upperdir and workdir share one mount.
|
|
13
|
+
//! A change that replaces an entry is made whole in `work` and exchanged
|
|
14
|
+
//! into place, so a change that fails part way leaves the view as it was.
|
|
15
|
+
|
|
16
|
+
mod copy_up;
|
|
17
|
+
mod dir;
|
|
18
|
+
mod namei;
|
|
19
|
+
mod readdir;
|
|
20
|
+
|
|
21
|
+
use std::sync::atomic::{AtomicU64, Ordering};
|
|
22
|
+
use std::sync::Arc;
|
|
23
|
+
|
|
24
|
+
use super::inode::OverlayDirInode;
|
|
25
|
+
use super::path::OverlayPath;
|
|
26
|
+
|
|
27
|
+
use super::super::{FileOperations, FileType, Filesystem, FilesystemError, Kiocb};
|
|
28
|
+
use crate::vfs::{Cred, Dentry, Identity};
|
|
29
|
+
|
|
30
|
+
/// The directories of the upper Tmpfs holding the view's changes and the
|
|
31
|
+
/// changes being built, as overlayfs's `upperdir` and `workdir`.
|
|
32
|
+
const UPPERDIR: &str = "upper";
|
|
33
|
+
const WORKDIR: &str = "work";
|
|
34
|
+
|
|
35
|
+
/// Where the upper stands among the Layers, topmost first, holding nothing where
|
|
36
|
+
/// an Overlay has none, as `ovl_inode` keeps `__upperdentry` apart from its lower
|
|
37
|
+
/// stack. Every change lands there.
|
|
38
|
+
pub(super) const UPPER: usize = 0;
|
|
39
|
+
|
|
40
|
+
/// Each Layer's directory at one directory of the view, topmost first;
|
|
41
|
+
/// `None` where that Layer holds no directory the view merges there.
|
|
42
|
+
pub(super) type Level = Vec<Option<OverlayPath>>;
|
|
43
|
+
|
|
44
|
+
/// One directory of the view: the dentry the runtime holds for it and the
|
|
45
|
+
/// state kept there, as overlayfs reaches a directory by its dentry and
|
|
46
|
+
/// the `ovl_inode` behind it.
|
|
47
|
+
#[derive(Clone)]
|
|
48
|
+
pub(super) struct ViewDir {
|
|
49
|
+
dentry: Arc<Dentry>,
|
|
50
|
+
inode: Arc<OverlayDirInode>,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
impl ViewDir {
|
|
54
|
+
/// Each Layer's directory this one stands for.
|
|
55
|
+
pub(super) fn level(&self) -> Level {
|
|
56
|
+
self.inode.level()
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/// What a Layer shows at a name, as `ovl_lookup_single` tells a whiteout from an
|
|
61
|
+
/// entry.
|
|
62
|
+
enum Presence {
|
|
63
|
+
/// Nothing, and a name too long for that Layer to hold is nothing either, as
|
|
64
|
+
/// `ovl_lookup_single` answers ENOENT and ENAMETOOLONG by moving on.
|
|
65
|
+
Nothing,
|
|
66
|
+
/// A whiteout, which hides the Layers below.
|
|
67
|
+
Whiteout,
|
|
68
|
+
/// An entry, a link reported as itself.
|
|
69
|
+
Entry(LayerEntry),
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/// An entry a Layer holds: its name as the Layer stores it, and its dentry there.
|
|
73
|
+
pub(super) struct LayerEntry {
|
|
74
|
+
stored_name: String,
|
|
75
|
+
dentry: Arc<Dentry>,
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
impl LayerEntry {
|
|
79
|
+
/// The name as the Layer stores it.
|
|
80
|
+
pub(super) fn stored_name(&self) -> &str {
|
|
81
|
+
&self.stored_name
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/// What kind of entry it is, as its lookup recorded it.
|
|
85
|
+
fn kind(&self) -> Result<FileType, FilesystemError> {
|
|
86
|
+
self.dentry.kind()
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
impl Presence {
|
|
91
|
+
fn is_directory(&self) -> bool {
|
|
92
|
+
matches!(self, Self::Entry(found) if found.kind() == Ok(FileType::Dir))
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/// What `dir` shows at `name`, read from one lookup, as `ovl_lookup_single` reads
|
|
97
|
+
/// `ovl_is_whiteout` and `d_is_dir` from the dentry it found.
|
|
98
|
+
fn entry(dir: &OverlayPath, name: &str) -> Result<Presence, FilesystemError> {
|
|
99
|
+
match dir.lookup_single(name) {
|
|
100
|
+
Ok(Some((_, dentry))) if dir.is_whiteout_dentry(&dentry) => Ok(Presence::Whiteout),
|
|
101
|
+
Ok(Some((stored_name, dentry))) => Ok(Presence::Entry(LayerEntry {
|
|
102
|
+
stored_name,
|
|
103
|
+
dentry,
|
|
104
|
+
})),
|
|
105
|
+
Ok(None) | Err(FilesystemError::NotFound | FilesystemError::InvalidFilename) => {
|
|
106
|
+
Ok(Presence::Nothing)
|
|
107
|
+
}
|
|
108
|
+
Err(err) => Err(err),
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/// Whether a Layer below the upper shows `name` in `level`, as
|
|
113
|
+
/// `ovl_lower_positive` finds out whether a removal must leave a whiteout. A Layer
|
|
114
|
+
/// that cannot answer is taken to hold something, so nothing below shows through.
|
|
115
|
+
fn ovl_lower_positive(level: &Level, name: &str) -> bool {
|
|
116
|
+
for dir in level.iter().skip(UPPER + 1).flatten() {
|
|
117
|
+
match entry(dir, name) {
|
|
118
|
+
Ok(Presence::Nothing) => {}
|
|
119
|
+
Ok(Presence::Whiteout) => return false,
|
|
120
|
+
Ok(Presence::Entry(_)) | Err(_) => return true,
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
false
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
fn copy_whole(
|
|
127
|
+
source: &mut dyn FileOperations,
|
|
128
|
+
target: &mut dyn FileOperations,
|
|
129
|
+
) -> Result<(), FilesystemError> {
|
|
130
|
+
let mut buf = vec![0; 64 * 1024];
|
|
131
|
+
let (mut from, mut to) = (Kiocb::at(0), Kiocb::at(0));
|
|
132
|
+
loop {
|
|
133
|
+
let read = source.read_iter(&mut from, &mut buf)?;
|
|
134
|
+
if read == 0 {
|
|
135
|
+
return Ok(());
|
|
136
|
+
}
|
|
137
|
+
let mut written = 0;
|
|
138
|
+
while written < read {
|
|
139
|
+
match target.write_iter(&mut to, &buf[written..read])? {
|
|
140
|
+
0 => {
|
|
141
|
+
return Err(FilesystemError::Other(
|
|
142
|
+
"a copy up's write wrote nothing".to_string(),
|
|
143
|
+
))
|
|
144
|
+
}
|
|
145
|
+
wrote => written += wrote,
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/// The Layer's directory a level holds, which it holds wherever it shows
|
|
152
|
+
/// a name.
|
|
153
|
+
fn layer_dir(dir: &Option<OverlayPath>) -> Result<&OverlayPath, FilesystemError> {
|
|
154
|
+
dir.as_ref().ok_or(FilesystemError::NotFound)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/// The directory `name` in `root`, made there first if it stands nowhere, as
|
|
158
|
+
/// overlayfs's `ovl_workdir_create` makes its upperdir's sibling.
|
|
159
|
+
fn ovl_workdir_create(root: &OverlayPath, name: &str) -> Result<OverlayPath, FilesystemError> {
|
|
160
|
+
match root.do_mkdir(name) {
|
|
161
|
+
Ok(()) | Err(FilesystemError::AlreadyExists) => root.open_dir(name),
|
|
162
|
+
Err(err) => Err(err),
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/// Take `name` out of `dir`, a directory only once it is empty, as
|
|
167
|
+
/// overlayfs's `ovl_cleanup` takes away what a change left in its
|
|
168
|
+
/// workdir, so whatever another writer made in it meanwhile stays.
|
|
169
|
+
fn ovl_cleanup(dir: &OverlayPath, name: &str) -> Result<(), FilesystemError> {
|
|
170
|
+
match dir.getattr_at(name)?.file_type {
|
|
171
|
+
FileType::Dir => dir.do_rmdir(name),
|
|
172
|
+
_ => dir.do_unlink(name),
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/// Take the whiteouts out of the directory `name` in `dir`, and nothing
|
|
177
|
+
/// else, as overlayfs's `ovl_cleanup_whiteouts` does before it removes
|
|
178
|
+
/// one; anything else stands there leaves it holding that.
|
|
179
|
+
fn ovl_cleanup_whiteouts(dir: &OverlayPath, name: &str) -> Result<(), FilesystemError> {
|
|
180
|
+
if dir.getattr_at(name)?.file_type != FileType::Dir {
|
|
181
|
+
return Ok(());
|
|
182
|
+
}
|
|
183
|
+
let inner = dir.open_dir(name)?;
|
|
184
|
+
for entry in inner.entries()? {
|
|
185
|
+
if inner.is_whiteout(&entry.name) {
|
|
186
|
+
inner.do_unlink(&entry.name)?;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
Ok(())
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/// Take `name` out of the workdir with all it holds, as overlayfs's
|
|
193
|
+
/// `ovl_workdir_cleanup` empties it of what an earlier mount left.
|
|
194
|
+
fn ovl_workdir_cleanup(dir: &OverlayPath, name: &str) -> Result<(), FilesystemError> {
|
|
195
|
+
let mut pending = vec![(dir.clone(), name.to_string())];
|
|
196
|
+
while let Some((dir, name)) = pending.last().cloned() {
|
|
197
|
+
if dir.getattr_at(&name)?.file_type != FileType::Dir {
|
|
198
|
+
dir.do_unlink(&name)?;
|
|
199
|
+
pending.pop();
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
let inner = dir.open_dir(&name)?;
|
|
203
|
+
let entries = inner.entries()?;
|
|
204
|
+
if entries.is_empty() {
|
|
205
|
+
dir.do_rmdir(&name)?;
|
|
206
|
+
pending.pop();
|
|
207
|
+
}
|
|
208
|
+
pending.extend(entries.into_iter().map(|entry| (inner.clone(), entry.name)));
|
|
209
|
+
}
|
|
210
|
+
Ok(())
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/// Layers stacked into one view, topmost first, the upper among them on
|
|
214
|
+
/// top, as overlayfs's `ovl_fs` holds a mount's layers.
|
|
215
|
+
#[derive(Clone)]
|
|
216
|
+
pub(super) struct OverlayFs {
|
|
217
|
+
/// Each Layer's root, the upper's `upper` at `UPPER` where there is one, and
|
|
218
|
+
/// the lower Layers beneath it, topmost first.
|
|
219
|
+
roots: Level,
|
|
220
|
+
/// Where a change is built before it lands, with an upper only.
|
|
221
|
+
work: Option<OverlayPath>,
|
|
222
|
+
/// The next name a change built in `work` takes.
|
|
223
|
+
temp: Arc<AtomicU64>,
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
impl OverlayFs {
|
|
227
|
+
/// The view of `lower` under `upper`, its `upper` and `work` made where
|
|
228
|
+
/// they stand nowhere and `work` emptied of what a change left, as
|
|
229
|
+
/// overlayfs's mount does with its workdir.
|
|
230
|
+
pub(super) fn new(
|
|
231
|
+
lower: Vec<Arc<Filesystem>>,
|
|
232
|
+
upper: Option<Arc<Filesystem>>,
|
|
233
|
+
) -> Result<Self, FilesystemError> {
|
|
234
|
+
let mut roots = vec![None];
|
|
235
|
+
let mut work = None;
|
|
236
|
+
let creator_cred = Cred::new(Vec::new());
|
|
237
|
+
if let Some(upper) = upper {
|
|
238
|
+
let root = OverlayPath::root(&upper, &creator_cred);
|
|
239
|
+
roots[UPPER] = Some(ovl_workdir_create(&root, UPPERDIR)?);
|
|
240
|
+
let made = ovl_workdir_create(&root, WORKDIR)?;
|
|
241
|
+
for entry in made.entries()? {
|
|
242
|
+
ovl_workdir_cleanup(&made, &entry.name)?;
|
|
243
|
+
}
|
|
244
|
+
work = Some(made);
|
|
245
|
+
}
|
|
246
|
+
roots.extend(
|
|
247
|
+
lower
|
|
248
|
+
.iter()
|
|
249
|
+
.map(|layer| Some(OverlayPath::root(layer, &creator_cred))),
|
|
250
|
+
);
|
|
251
|
+
Ok(Self {
|
|
252
|
+
roots,
|
|
253
|
+
work,
|
|
254
|
+
temp: Arc::default(),
|
|
255
|
+
})
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/// Whether changes have an upper to land in, as `ovl_upper_mnt` answers.
|
|
259
|
+
pub(super) fn has_upper(&self) -> bool {
|
|
260
|
+
self.work.is_some()
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/// What the topmost Layer's root recorded it stands for.
|
|
264
|
+
pub(super) fn root_identity(&self) -> Option<Identity> {
|
|
265
|
+
let topmost = self.roots.iter().flatten().next()?;
|
|
266
|
+
topmost.dentry().inode()?.identity()
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/// The state of the root, which stands for every Layer's root, as overlayfs's
|
|
270
|
+
/// `ovl_get_root` keeps it on the root's inode as it mounts.
|
|
271
|
+
pub(super) fn root_dir(&self) -> OverlayDirInode {
|
|
272
|
+
OverlayDirInode::new(self.roots.clone())
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/// The directory of the view `dentry` names, its state found on the inode
|
|
276
|
+
/// its lookup, or the mount for the root, recorded.
|
|
277
|
+
pub(super) fn dir(&self, dentry: &Dentry) -> Result<ViewDir, FilesystemError> {
|
|
278
|
+
let inode = dentry.inode().ok_or(FilesystemError::NotFound)?;
|
|
279
|
+
let inode = OverlayDirInode::of(&inode).ok_or(FilesystemError::NotADirectory)?;
|
|
280
|
+
Ok(ViewDir {
|
|
281
|
+
dentry: dentry.get(),
|
|
282
|
+
inode,
|
|
283
|
+
})
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/// The directory holding the entry `dentry` names.
|
|
287
|
+
pub(super) fn parent(&self, dentry: &Dentry) -> Result<ViewDir, FilesystemError> {
|
|
288
|
+
self.dir(&dentry.parent())
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/// The workdir and a name in it no change has taken, as overlayfs's
|
|
292
|
+
/// `ovl_tempname` names one.
|
|
293
|
+
fn tempname(&self) -> Result<(&OverlayPath, String), FilesystemError> {
|
|
294
|
+
let work = self
|
|
295
|
+
.work
|
|
296
|
+
.as_ref()
|
|
297
|
+
.ok_or(FilesystemError::ReadOnlyFilesystem)?;
|
|
298
|
+
let next = self.temp.fetch_add(1, Ordering::Relaxed);
|
|
299
|
+
Ok((work, format!("#{next:x}")))
|
|
300
|
+
}
|
|
301
|
+
}
|