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,429 @@
|
|
|
1
|
+
//! What a name stands for, apart from where it stands, as Linux's
|
|
2
|
+
//! `struct inode` is what a dentry is instantiated with.
|
|
3
|
+
|
|
4
|
+
use std::any::Any;
|
|
5
|
+
use std::ops::BitOr;
|
|
6
|
+
use std::sync::atomic::{AtomicBool, Ordering};
|
|
7
|
+
use std::sync::{Arc, Mutex, MutexGuard, OnceLock, PoisonError, Weak};
|
|
8
|
+
|
|
9
|
+
use super::dentry::Dentry;
|
|
10
|
+
use super::file::FileOperations;
|
|
11
|
+
use super::{FileType, FilesystemError, Iattr, Kstat, OpenHow};
|
|
12
|
+
use crate::locking::{RwSemaphore, RwSemaphoreGuard};
|
|
13
|
+
|
|
14
|
+
/// What an `atomic_open` did with the name it was asked to open, as Linux's
|
|
15
|
+
/// `->atomic_open` ends in `finish_open` or `finish_no_open`. `created` says
|
|
16
|
+
/// whether it made the file, as `FMODE_CREATED` does: one it found standing is
|
|
17
|
+
/// opened as any open of an entry that stood, and one it made asks for nothing
|
|
18
|
+
/// but the open.
|
|
19
|
+
#[non_exhaustive]
|
|
20
|
+
pub enum Finish {
|
|
21
|
+
/// It opened the file too, as `finish_open` does.
|
|
22
|
+
Open {
|
|
23
|
+
file: Box<dyn FileOperations>,
|
|
24
|
+
created: bool,
|
|
25
|
+
},
|
|
26
|
+
/// It found the entry stored under the other spelling `name` and opened
|
|
27
|
+
/// it, as `finish_open` is handed the dentry `d_add_ci` returned, for the
|
|
28
|
+
/// VFS to record `inode` on that spelling's dentry.
|
|
29
|
+
Spelled {
|
|
30
|
+
file: Box<dyn FileOperations>,
|
|
31
|
+
name: String,
|
|
32
|
+
inode: Arc<Inode>,
|
|
33
|
+
},
|
|
34
|
+
/// It left the open to the VFS, as `finish_no_open` hands the dentry back.
|
|
35
|
+
NoOpen { created: bool },
|
|
36
|
+
/// It looked the name up and left the rest to the VFS, as `finish_no_open`
|
|
37
|
+
/// hands back the dentry a lookup returned, for the VFS to record as it
|
|
38
|
+
/// records what `lookup` answers.
|
|
39
|
+
Lookup(Lookup),
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/// What one name in a directory turned out to be, as `->lookup` answers.
|
|
43
|
+
pub enum Lookup {
|
|
44
|
+
/// The entry standing at the name, for the runtime to record on the
|
|
45
|
+
/// dentry, as `d_splice_alias` records it. A Filesystem naming one entry by
|
|
46
|
+
/// more than one name hands the same one, as `iget5_locked` finds it, and so
|
|
47
|
+
/// shares its lock.
|
|
48
|
+
Found(Arc<Inode>),
|
|
49
|
+
/// Nothing stands at the name, as a negative dentry records.
|
|
50
|
+
Absent,
|
|
51
|
+
/// The entry the name reaches, stored under the other spelling `name`, as
|
|
52
|
+
/// `d_add_ci` is handed the inode found and the case-exact name, so a name
|
|
53
|
+
/// a host reaches by more than one spelling has one dentry, one lock, and
|
|
54
|
+
/// one spelling a security hook judges.
|
|
55
|
+
Spelled { name: String, inode: Arc<Inode> },
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/// What one entry of a Filesystem answers: a directory for the names in it,
|
|
59
|
+
/// a link for where it points, and any entry for itself, as Linux's
|
|
60
|
+
/// `inode_operations` hang from each inode. An operation an entry does not
|
|
61
|
+
/// have answers by default as Linux's VFS answers a NULL in its table. One
|
|
62
|
+
/// Filesystem may wrap another and answer by
|
|
63
|
+
/// delegating, wrapping each entry its `lookup` finds, so it sees every call
|
|
64
|
+
/// on the way through. It forwards every operation the one beneath keeps, or
|
|
65
|
+
/// the default answers in that one's place. A directory keeps its identity its
|
|
66
|
+
/// own while it is open, as an open descriptor keeps a host's inode from being
|
|
67
|
+
/// reused.
|
|
68
|
+
///
|
|
69
|
+
/// The VFS walks a path one name at a time and follows a link
|
|
70
|
+
/// itself, as the VFS above a filesystem does, so a link never leads a call
|
|
71
|
+
/// out of the directory a descriptor opened. Every call hands the dentry it
|
|
72
|
+
/// is about; a Filesystem answering by path reads `Dentry::path_raw`, so a
|
|
73
|
+
/// directory moved through the runtime is asked at its new path. A call
|
|
74
|
+
/// making, removing, or moving a name runs under its directory's lock.
|
|
75
|
+
pub trait InodeOperations: Any + Send + Sync {
|
|
76
|
+
/// Whether a call may do what `mask` asks of this entry, which `dentry`
|
|
77
|
+
/// names, as `->permission` answers `inode_permission`. The VFS asks where
|
|
78
|
+
/// Linux does and holds no mode to judge by, so an entry without it allows
|
|
79
|
+
/// the call, and the backend answers once it is made, as FUSE leaves a
|
|
80
|
+
/// server to without `default_permissions`.
|
|
81
|
+
fn permission(&self, _dentry: &Dentry, _mask: Mask) -> Result<(), FilesystemError> {
|
|
82
|
+
Ok(())
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/// What this entry, which `dentry` names, reports of itself, as
|
|
86
|
+
/// `getattr` is handed the path it answers for, even once its name is
|
|
87
|
+
/// gone and only an open file holds it, as `fstat` asks the same. A
|
|
88
|
+
/// Filesystem keeps what that answer takes on the `Inode`. A link reports
|
|
89
|
+
/// itself.
|
|
90
|
+
fn getattr(&self, dentry: &Dentry) -> Result<Kstat, FilesystemError>;
|
|
91
|
+
|
|
92
|
+
/// Apply `attr` to this entry, even once its name is gone and only an
|
|
93
|
+
/// open file holds it, as `ftruncate`, `open` with `O_TRUNC`, and
|
|
94
|
+
/// `utimensat` reach `->setattr`. One whose `open` already truncated
|
|
95
|
+
/// answers success where `attr.open` says so. An entry without it refuses a
|
|
96
|
+
/// size as invalid input and times as unsupported.
|
|
97
|
+
fn setattr(&self, _dentry: &Dentry, attr: &Iattr) -> Result<(), FilesystemError> {
|
|
98
|
+
match attr.size {
|
|
99
|
+
Some(_) => Err(FilesystemError::InvalidInput),
|
|
100
|
+
None => Err(FilesystemError::Unsupported),
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/// What stands at the name `dentry` carries in this directory. The
|
|
105
|
+
/// runtime asks once for a dentry naming nothing yet, and an entry
|
|
106
|
+
/// without it is no directory to walk through. A directory found answers as
|
|
107
|
+
/// an `Inode::new` carrying its identity, or a Policy cannot tell it from
|
|
108
|
+
/// one made in its place.
|
|
109
|
+
fn lookup(&self, _dentry: &Dentry) -> Result<Lookup, FilesystemError> {
|
|
110
|
+
Err(FilesystemError::NotADirectory)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/// Where this link, which `dentry` names, points, as `get_link` answers,
|
|
114
|
+
/// for the VFS to follow. Anything else refuses as `readlinkat` does.
|
|
115
|
+
fn get_link(&self, _dentry: &Dentry) -> Result<String, FilesystemError> {
|
|
116
|
+
Err(FilesystemError::InvalidInput)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/// Open this entry, which `dentry` names, as `how` asks, as `f_op->open`
|
|
120
|
+
/// makes the open file reading its `f_flags`. The VFS empties a file an
|
|
121
|
+
/// open asks to truncate through `setattr` afterwards, and refuses a link
|
|
122
|
+
/// before it asks. An entry without it opens as Linux's opens with no
|
|
123
|
+
/// `f_op->open`, answering each call on the open file as `FileOperations`
|
|
124
|
+
/// does by default.
|
|
125
|
+
fn open(
|
|
126
|
+
&self,
|
|
127
|
+
_dentry: &Dentry,
|
|
128
|
+
_how: &OpenHow,
|
|
129
|
+
) -> Result<Box<dyn FileOperations>, FilesystemError> {
|
|
130
|
+
Ok(Box::new(EmptyFops))
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/// Make the file `dentry` names in this directory and record it on `dentry`,
|
|
134
|
+
/// as `->create` calls `d_instantiate`. A directory without it answers
|
|
135
|
+
/// EACCES, as Linux's VFS does.
|
|
136
|
+
fn create(&self, _dentry: &Dentry) -> Result<(), FilesystemError> {
|
|
137
|
+
Err(FilesystemError::PermissionDenied)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/// Look up, make, and open the last name of an open in one step, as
|
|
141
|
+
/// `->atomic_open` takes `open_flag` for a backend that does it at once. The
|
|
142
|
+
/// VFS asks it for a `dentry` still in lookup, which is looked up first, and
|
|
143
|
+
/// for a negative one an open may make; `how` asks to make a file only
|
|
144
|
+
/// where the open may, and for a directory where the name ends in a slash,
|
|
145
|
+
/// as Linux adds `O_DIRECTORY` for `LOOKUP_DIRECTORY`. A directory without it looks the name up, then makes
|
|
146
|
+
/// the file through `create` and leaves the open to the VFS, which asks
|
|
147
|
+
/// `may_open` and the security modules first, as Linux's `lookup_open` calls
|
|
148
|
+
/// `->lookup`, then `->create`, and `do_open` opens what it made.
|
|
149
|
+
fn atomic_open(&self, dentry: &Dentry, how: &OpenHow) -> Result<Finish, FilesystemError> {
|
|
150
|
+
if dentry.in_lookup() {
|
|
151
|
+
let found = self.lookup(dentry)?;
|
|
152
|
+
if !how.create || !matches!(found, Lookup::Absent) {
|
|
153
|
+
return Ok(Finish::Lookup(found));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if !how.create {
|
|
157
|
+
return Ok(Finish::NoOpen { created: false });
|
|
158
|
+
}
|
|
159
|
+
self.create(dentry)?;
|
|
160
|
+
Ok(Finish::NoOpen { created: true })
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/// Make the directory `dentry` names in this one, recording it on `dentry`
|
|
164
|
+
/// where it can, as `->mkdir` calls `d_instantiate`. One that records nothing
|
|
165
|
+
/// answers for the name at the next walk, through its `revalidate` or by
|
|
166
|
+
/// keeping no name, as Linux leaves a negative dentry to its Filesystem. A
|
|
167
|
+
/// directory without it, or without any change below, answers EPERM, as
|
|
168
|
+
/// Linux's VFS does.
|
|
169
|
+
fn mkdir(&self, _dentry: &Dentry) -> Result<(), FilesystemError> {
|
|
170
|
+
Err(FilesystemError::NotPermitted)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/// Give the entry `old` names another name, the one `dentry` carries in
|
|
174
|
+
/// this directory. A directory without it answers EPERM, as Linux's VFS
|
|
175
|
+
/// does.
|
|
176
|
+
fn link(&self, _old: &Dentry, _dentry: &Dentry) -> Result<(), FilesystemError> {
|
|
177
|
+
Err(FilesystemError::NotPermitted)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/// Make the link `dentry` names in this directory, pointing at `target`.
|
|
181
|
+
/// A directory without it answers EPERM, as Linux's VFS does.
|
|
182
|
+
fn symlink(&self, _dentry: &Dentry, _target: &str) -> Result<(), FilesystemError> {
|
|
183
|
+
Err(FilesystemError::NotPermitted)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/// Remove the empty directory `dentry` names in this one. A directory
|
|
187
|
+
/// without it answers EPERM, as Linux's VFS does.
|
|
188
|
+
fn rmdir(&self, _dentry: &Dentry) -> Result<(), FilesystemError> {
|
|
189
|
+
Err(FilesystemError::NotPermitted)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/// Remove the file `dentry` names in this directory. A directory without
|
|
193
|
+
/// it answers EPERM, as Linux's VFS does.
|
|
194
|
+
fn unlink(&self, _dentry: &Dentry) -> Result<(), FilesystemError> {
|
|
195
|
+
Err(FilesystemError::NotPermitted)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/// Move the entry `from` names in this directory to `to` in `to_dir`,
|
|
199
|
+
/// refusing what rename(2) refuses at `to`. The runtime has already
|
|
200
|
+
/// checked what stands at `from` and never hands a move onto its own
|
|
201
|
+
/// name, as Linux's VFS does before `inode_operations.rename`. It hands
|
|
202
|
+
/// only a `to_dir` of the same mount, so no two instances meet here, as
|
|
203
|
+
/// `filename_renameat2` answers EXDEV first. One that takes no `flag`
|
|
204
|
+
/// refuses any as invalid input, as Linux's filesystems refuse a `renameat2`
|
|
205
|
+
/// flag they do not know. A directory without it answers EPERM, as
|
|
206
|
+
/// Linux's VFS does.
|
|
207
|
+
fn rename(
|
|
208
|
+
&self,
|
|
209
|
+
_from: &Dentry,
|
|
210
|
+
_to_dir: &dyn InodeOperations,
|
|
211
|
+
_to: &Dentry,
|
|
212
|
+
_flag: Option<RenameFlag>,
|
|
213
|
+
) -> Result<(), FilesystemError> {
|
|
214
|
+
Err(FilesystemError::NotPermitted)
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/// What a call asks of an entry, as Linux's `MAY_*` bits in the mask
|
|
219
|
+
/// `inode_permission` takes.
|
|
220
|
+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
221
|
+
pub struct Mask(u8);
|
|
222
|
+
|
|
223
|
+
impl Mask {
|
|
224
|
+
/// Asking nothing, as an `acc_mode` of 0.
|
|
225
|
+
pub(crate) const NONE: Self = Self(0);
|
|
226
|
+
/// Passing through a directory, as `MAY_EXEC`.
|
|
227
|
+
pub const EXEC: Self = Self(1);
|
|
228
|
+
/// Changing it, as `MAY_WRITE`.
|
|
229
|
+
pub const WRITE: Self = Self(1 << 1);
|
|
230
|
+
/// Reading it, as `MAY_READ`.
|
|
231
|
+
pub const READ: Self = Self(1 << 2);
|
|
232
|
+
/// Opening it, as `MAY_OPEN`.
|
|
233
|
+
pub const OPEN: Self = Self(1 << 3);
|
|
234
|
+
/// Changing it only at its end, as `MAY_APPEND`, which comes with `WRITE`
|
|
235
|
+
/// so a module can tell an append from any other write.
|
|
236
|
+
pub const APPEND: Self = Self(1 << 4);
|
|
237
|
+
|
|
238
|
+
/// Whether this holds all of `mask`.
|
|
239
|
+
pub fn contains(self, mask: Self) -> bool {
|
|
240
|
+
self.0 & mask.0 == mask.0
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
impl BitOr for Mask {
|
|
245
|
+
type Output = Self;
|
|
246
|
+
|
|
247
|
+
fn bitor(self, other: Self) -> Self {
|
|
248
|
+
Self(self.0 | other.0)
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/// What a move asks of its target, as `renameat2`'s flags.
|
|
253
|
+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
254
|
+
#[non_exhaustive]
|
|
255
|
+
pub enum RenameFlag {
|
|
256
|
+
/// Refuse the move when anything stands at the target.
|
|
257
|
+
NoReplace,
|
|
258
|
+
/// Swap the source and the target, both of which must stand.
|
|
259
|
+
Exchange,
|
|
260
|
+
/// Leave a whiteout where the source stood.
|
|
261
|
+
Whiteout,
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/// What an entry without `open` opens as, as Linux's `empty_fops` is a file with
|
|
265
|
+
/// no operations.
|
|
266
|
+
pub(super) struct EmptyFops;
|
|
267
|
+
|
|
268
|
+
impl FileOperations for EmptyFops {}
|
|
269
|
+
|
|
270
|
+
/// What an entry is, apart from where it stands: the device and inode
|
|
271
|
+
/// numbers its Filesystem names it by, as Linux reads `i_ino` and `s_dev`.
|
|
272
|
+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
|
273
|
+
pub struct Identity {
|
|
274
|
+
dev: u64,
|
|
275
|
+
ino: u64,
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
impl Identity {
|
|
279
|
+
/// The identity `stat` names, `None` where its inode of zero reports none.
|
|
280
|
+
pub fn of(stat: &Kstat) -> Option<Self> {
|
|
281
|
+
Self::from_numbers(stat.dev, stat.ino)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/// The identity `dev` and `ino` name, `None` where an inode of zero
|
|
285
|
+
/// reports none.
|
|
286
|
+
pub(crate) fn from_numbers(dev: u64, ino: u64) -> Option<Self> {
|
|
287
|
+
(ino != 0).then_some(Self { dev, ino })
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/// `stat`, naming this identity.
|
|
291
|
+
pub fn onto(self, stat: Kstat) -> Kstat {
|
|
292
|
+
stat.with_identity(self.dev, self.ino)
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/// One entry of a Filesystem, as a lookup finds it for the runtime to record
|
|
297
|
+
/// on the dentry naming it: what it is, and what answers for it, as Linux's
|
|
298
|
+
/// inode carries `i_mode`, `i_ino`, and `i_op`.
|
|
299
|
+
pub struct Inode {
|
|
300
|
+
/// What kind of entry it is.
|
|
301
|
+
kind: FileType,
|
|
302
|
+
/// `None` where the Filesystem names no inode number.
|
|
303
|
+
identity: Option<Identity>,
|
|
304
|
+
ops: Arc<dyn InodeOperations>,
|
|
305
|
+
/// What the Filesystem keeps on the entry once it needs it, set once,
|
|
306
|
+
/// as Linux's `i_private`, reached the same however its operations are
|
|
307
|
+
/// wrapped.
|
|
308
|
+
private: OnceLock<Arc<dyn Any + Send + Sync>>,
|
|
309
|
+
/// The lock calls on the entry run under, as Linux's `i_rwsem`.
|
|
310
|
+
rwsem: Arc<RwSemaphore>,
|
|
311
|
+
/// Whether the directory was removed or replaced through the VFS, as
|
|
312
|
+
/// Linux sets `S_DEAD` in `i_flags`.
|
|
313
|
+
dead: AtomicBool,
|
|
314
|
+
/// The dentry a directory stands at, as Linux keeps a directory's one
|
|
315
|
+
/// dentry in `i_dentry`.
|
|
316
|
+
dentry: Mutex<Weak<Dentry>>,
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
impl Inode {
|
|
320
|
+
/// The entry `stat` reports, answered for by `ops`.
|
|
321
|
+
pub fn new(stat: &Kstat, ops: Arc<dyn InodeOperations>) -> Arc<Self> {
|
|
322
|
+
Self::of_kind(stat.file_type, Identity::of(stat), ops)
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/// This entry answered for by `ops`, as a Filesystem wrapping another
|
|
326
|
+
/// wraps each entry that one's lookup finds.
|
|
327
|
+
pub fn with_ops(&self, ops: Arc<dyn InodeOperations>) -> Arc<Self> {
|
|
328
|
+
Self::of_kind(self.kind, self.identity, ops)
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
fn of_kind(
|
|
332
|
+
kind: FileType,
|
|
333
|
+
identity: Option<Identity>,
|
|
334
|
+
ops: Arc<dyn InodeOperations>,
|
|
335
|
+
) -> Arc<Self> {
|
|
336
|
+
Arc::new(Self {
|
|
337
|
+
kind,
|
|
338
|
+
identity,
|
|
339
|
+
ops,
|
|
340
|
+
private: OnceLock::new(),
|
|
341
|
+
rwsem: Arc::default(),
|
|
342
|
+
dead: AtomicBool::new(false),
|
|
343
|
+
dentry: Mutex::default(),
|
|
344
|
+
})
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/// The dentry other than `dentry` this directory already stands at, as
|
|
348
|
+
/// `__d_find_dir_alias` finds it, or none with this entry recorded on
|
|
349
|
+
/// `dentry`, as `__d_add` records it. Both run under one lock, as
|
|
350
|
+
/// `d_splice_alias` holds `i_lock`, so two lookups of one directory never
|
|
351
|
+
/// each find none, and none finds `dentry` before a walk can.
|
|
352
|
+
pub(super) fn find_alias_or_add(self: &Arc<Self>, dentry: &Dentry) -> Option<Arc<Dentry>> {
|
|
353
|
+
let mut held = self.dentry();
|
|
354
|
+
if let Some(found) = held.upgrade() {
|
|
355
|
+
if !std::ptr::eq(found.as_ref(), dentry) {
|
|
356
|
+
return Some(found);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
dentry.__instantiate(self.clone(), &mut held);
|
|
360
|
+
dentry.rehash();
|
|
361
|
+
None
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/// Record this entry on `dentry` and let a walk find it by its name, as
|
|
365
|
+
/// `d_add` does under `i_lock`.
|
|
366
|
+
pub(super) fn add_hashed(self: &Arc<Self>, dentry: &Dentry) {
|
|
367
|
+
let mut held = self.dentry();
|
|
368
|
+
dentry.__instantiate(self.clone(), &mut held);
|
|
369
|
+
dentry.rehash();
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
pub(super) fn dentry(&self) -> MutexGuard<'_, Weak<Dentry>> {
|
|
373
|
+
self.dentry.lock().unwrap_or_else(PoisonError::into_inner)
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/// Hold it shared where nothing holds it exclusive, without waiting, as
|
|
377
|
+
/// Linux's `inode_trylock_shared`.
|
|
378
|
+
pub(super) fn trylock_shared(&self) -> Option<RwSemaphoreGuard> {
|
|
379
|
+
self.rwsem.down_read_trylock()
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/// Hold it shared, as Linux's `inode_lock_shared` holds the directory it
|
|
383
|
+
/// looks a name up in, so no change through the VFS lands between the
|
|
384
|
+
/// name's dentry and what the Filesystem answers for it.
|
|
385
|
+
pub(super) fn lock_shared(&self) -> Result<RwSemaphoreGuard, FilesystemError> {
|
|
386
|
+
Ok(self.rwsem.down_read()?)
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/// Hold it exclusive, as Linux's `inode_lock` holds a directory while a
|
|
390
|
+
/// name in it is made, removed, or moved.
|
|
391
|
+
pub(super) fn lock(&self) -> Result<RwSemaphoreGuard, FilesystemError> {
|
|
392
|
+
Ok(self.rwsem.down_write()?)
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/// Whether `held` holds its lock, as Linux's `inode_is_locked` asks.
|
|
396
|
+
pub(super) fn is_locked_by(&self, held: &RwSemaphoreGuard) -> bool {
|
|
397
|
+
held.holds(&self.rwsem)
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/// Whether the directory was removed or replaced, as `IS_DEADDIR`.
|
|
401
|
+
pub(super) fn is_deaddir(&self) -> bool {
|
|
402
|
+
self.dead.load(Ordering::SeqCst)
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/// The directory was removed or replaced, so nothing is made in it any
|
|
406
|
+
/// more.
|
|
407
|
+
pub(super) fn mark_dead(&self) {
|
|
408
|
+
self.dead.store(true, Ordering::SeqCst);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
pub fn kind(&self) -> FileType {
|
|
412
|
+
self.kind
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
pub fn identity(&self) -> Option<Identity> {
|
|
416
|
+
self.identity
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/// What answers for the entry, as Linux's `i_op`.
|
|
420
|
+
pub fn ops(&self) -> &Arc<dyn InodeOperations> {
|
|
421
|
+
&self.ops
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/// What the Filesystem keeps on the entry, set once, as Linux's
|
|
425
|
+
/// `i_private`.
|
|
426
|
+
pub fn private(&self) -> &OnceLock<Arc<dyn Any + Send + Sync>> {
|
|
427
|
+
&self.private
|
|
428
|
+
}
|
|
429
|
+
}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
//! A test's way to ask a Filesystem about a path, as kernel code reaches a
|
|
2
|
+
//! file by name through `kern_path`: the VFS resolves the path to a
|
|
3
|
+
//! dentry, as it does for every call, and the Filesystem is asked about
|
|
4
|
+
//! that dentry, a change going through the VFS's own `vfs_*` helpers as
|
|
5
|
+
//! kernel code's does, so a test sees what the Filesystem answers beneath
|
|
6
|
+
//! the VFS. It takes no directory's lock, so a test holding one can
|
|
7
|
+
//! still reach the Filesystem through it.
|
|
8
|
+
|
|
9
|
+
use std::sync::Arc;
|
|
10
|
+
|
|
11
|
+
use super::cred::Cred;
|
|
12
|
+
use super::dentry::Dentry;
|
|
13
|
+
use super::namei::{
|
|
14
|
+
atomic_open, do_file_open, filename_lookup, filename_parentat, lookup_one_qstr_excl,
|
|
15
|
+
may_create_dentry, vfs_mkdir, vfs_rename, vfs_rmdir, vfs_unlink, Nameidata, RenameData,
|
|
16
|
+
};
|
|
17
|
+
use super::path::{Path, Start};
|
|
18
|
+
use super::{
|
|
19
|
+
build_open_flags, DirEntry, File, FileOperations, FileSystemType, FileType, FilesystemError,
|
|
20
|
+
Iattr, Kstat, OpenHow, SuperBlock,
|
|
21
|
+
};
|
|
22
|
+
use crate::mount::{Access, Mount};
|
|
23
|
+
|
|
24
|
+
/// Open `path` beneath `start` for `cred` as `how` asks, as kernel code opens a
|
|
25
|
+
/// file by name through `filp_open`, which builds the flags `do_file_open`
|
|
26
|
+
/// takes.
|
|
27
|
+
pub(crate) fn filp_open(
|
|
28
|
+
cred: &Arc<Cred>,
|
|
29
|
+
start: &impl Start,
|
|
30
|
+
path: &str,
|
|
31
|
+
how: &OpenHow,
|
|
32
|
+
) -> Result<File, FilesystemError> {
|
|
33
|
+
do_file_open(cred, start, path, &build_open_flags(how)?)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/// A directory of a Filesystem reached through the VFS.
|
|
37
|
+
#[derive(Clone)]
|
|
38
|
+
pub(crate) struct KernPath(Path);
|
|
39
|
+
|
|
40
|
+
/// The root of `filesystem`, mounted read-write.
|
|
41
|
+
pub(crate) fn kern_path(filesystem: Arc<dyn FileSystemType>) -> KernPath {
|
|
42
|
+
let filesystem = SuperBlock::new(filesystem).expect("the instance must stand");
|
|
43
|
+
let mount = Mount::new("/data", filesystem, Access::ReadWrite).expect("/data is a guest path");
|
|
44
|
+
KernPath(Path::root(mount.mnt()))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
impl KernPath {
|
|
48
|
+
/// `path` resolved beneath this directory, a link at its end left as
|
|
49
|
+
/// it stands.
|
|
50
|
+
fn resolve(&self, path: &str) -> Result<Nameidata, FilesystemError> {
|
|
51
|
+
filename_lookup(&Arc::default(), &self.0, path, false)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/// The directory holding the last name of `path`, and that name's dentry,
|
|
55
|
+
/// whether or not anything stands there.
|
|
56
|
+
fn parent(&self, path: &str) -> Result<(Nameidata, Arc<Dentry>), FilesystemError> {
|
|
57
|
+
let nd = filename_parentat(&Arc::default(), &self.0, path)?;
|
|
58
|
+
let child = lookup_one_qstr_excl(nd.mnt.sb(), nd.dentry(), &nd.last)?;
|
|
59
|
+
Ok((nd, child))
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
pub(crate) fn open(
|
|
63
|
+
&self,
|
|
64
|
+
path: &str,
|
|
65
|
+
how: &OpenHow,
|
|
66
|
+
) -> Result<Box<dyn FileOperations>, FilesystemError> {
|
|
67
|
+
let child = self.resolve(path)?.path()?;
|
|
68
|
+
// A link opens as itself nowhere, as `may_open` refuses one first.
|
|
69
|
+
if child.kind() == Ok(FileType::Symlink) {
|
|
70
|
+
return Err(FilesystemError::FilesystemLoop);
|
|
71
|
+
}
|
|
72
|
+
let file = child.open(how)?;
|
|
73
|
+
if how.truncate {
|
|
74
|
+
child
|
|
75
|
+
.inode()
|
|
76
|
+
.ok_or(FilesystemError::NotFound)?
|
|
77
|
+
.ops()
|
|
78
|
+
.setattr(&child, &Iattr::open_truncate())?;
|
|
79
|
+
}
|
|
80
|
+
Ok(file)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/// Open the file at `path`, made first where nothing stands.
|
|
84
|
+
pub(crate) fn open_or_create(
|
|
85
|
+
&self,
|
|
86
|
+
path: &str,
|
|
87
|
+
how: &OpenHow,
|
|
88
|
+
) -> Result<Box<dyn FileOperations>, FilesystemError> {
|
|
89
|
+
match self.vfs_lstat(path) {
|
|
90
|
+
Err(FilesystemError::NotFound) => self.create(path, how),
|
|
91
|
+
_ => self.open(path, how),
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/// Make the file at `path` and open it, as `->atomic_open` does.
|
|
96
|
+
pub(crate) fn create(
|
|
97
|
+
&self,
|
|
98
|
+
path: &str,
|
|
99
|
+
how: &OpenHow,
|
|
100
|
+
) -> Result<Box<dyn FileOperations>, FilesystemError> {
|
|
101
|
+
let (nd, child) = self.parent(path)?;
|
|
102
|
+
may_create_dentry(&nd.cred, nd.mnt.sb(), nd.dentry(), &child)?;
|
|
103
|
+
let how = OpenHow {
|
|
104
|
+
create: true,
|
|
105
|
+
..*how
|
|
106
|
+
};
|
|
107
|
+
let opened = atomic_open(nd.mnt.sb(), nd.dentry(), &child, &how)?;
|
|
108
|
+
match opened.file {
|
|
109
|
+
Some(file) => Ok(file),
|
|
110
|
+
None => opened.spliced.unwrap_or(child).open(&how),
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/// Truncate or extend the file at `path`, as `->setattr` is asked.
|
|
115
|
+
pub(crate) fn truncate(&self, path: &str, len: u64) -> Result<(), FilesystemError> {
|
|
116
|
+
self.setattr(path, &Iattr::size(len))
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/// Hand `attr` to what `path` names, as `->setattr` is asked.
|
|
120
|
+
pub(crate) fn setattr(&self, path: &str, attr: &Iattr) -> Result<(), FilesystemError> {
|
|
121
|
+
let child = self.resolve(path)?.path()?;
|
|
122
|
+
child
|
|
123
|
+
.inode()
|
|
124
|
+
.ok_or(FilesystemError::NotFound)?
|
|
125
|
+
.ops()
|
|
126
|
+
.setattr(&child, attr)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
pub(crate) fn open_dir(&self, path: &str) -> Result<Self, FilesystemError> {
|
|
130
|
+
let dentry = self.resolve(path)?.path()?;
|
|
131
|
+
match dentry.kind()? {
|
|
132
|
+
FileType::Symlink => Err(FilesystemError::FilesystemLoop),
|
|
133
|
+
FileType::Dir => {
|
|
134
|
+
dentry.getattr()?;
|
|
135
|
+
Ok(Self(self.0.with_dentry(dentry)))
|
|
136
|
+
}
|
|
137
|
+
_ => Err(FilesystemError::NotADirectory),
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/// What the directory reports of itself, as `vfs_getattr`.
|
|
142
|
+
pub(crate) fn vfs_getattr(&self) -> Result<Kstat, FilesystemError> {
|
|
143
|
+
self.0.dentry().getattr()
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/// What `path` names, a link reported as itself, as `vfs_lstat`; the
|
|
147
|
+
/// directory itself for the empty path.
|
|
148
|
+
pub(crate) fn vfs_lstat(&self, path: &str) -> Result<Kstat, FilesystemError> {
|
|
149
|
+
if path.is_empty() {
|
|
150
|
+
return self.vfs_getattr();
|
|
151
|
+
}
|
|
152
|
+
self.resolve(path)?.getattr()
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
pub(crate) fn lookup(&self, path: &str) -> Result<String, FilesystemError> {
|
|
156
|
+
let nd = filename_parentat(&Arc::default(), &self.0, path)?;
|
|
157
|
+
Ok(lookup_one_qstr_excl(nd.mnt.sb(), nd.dentry(), &nd.last)?.name())
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
#[cfg(unix)]
|
|
161
|
+
pub(crate) fn vfs_readlink(&self, path: &str) -> Result<String, FilesystemError> {
|
|
162
|
+
self.resolve(path)?.path()?.get_link()
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
pub(crate) fn iterate_dir(&self) -> Result<Vec<DirEntry>, FilesystemError> {
|
|
166
|
+
let read = OpenHow {
|
|
167
|
+
read: true,
|
|
168
|
+
..OpenHow::default()
|
|
169
|
+
};
|
|
170
|
+
File::dentry_open(self.0.clone(), &read, &Arc::default())?.iterate_shared()
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
pub(crate) fn vfs_mkdir(&self, path: &str) -> Result<(), FilesystemError> {
|
|
174
|
+
let (nd, child) = self.parent(path)?;
|
|
175
|
+
vfs_mkdir(&nd.cred, nd.mnt.sb(), nd.dentry(), &child)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
pub(crate) fn vfs_rmdir(&self, path: &str) -> Result<(), FilesystemError> {
|
|
179
|
+
let (nd, child) = self.parent(path)?;
|
|
180
|
+
vfs_rmdir(&nd.cred, nd.mnt.sb(), nd.dentry(), &child)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
pub(crate) fn vfs_unlink(&self, path: &str) -> Result<(), FilesystemError> {
|
|
184
|
+
let (nd, child) = self.parent(path)?;
|
|
185
|
+
vfs_unlink(&nd.cred, nd.mnt.sb(), nd.dentry(), &child)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/// Move `from` beneath this directory to `to` beneath `to_dir`,
|
|
189
|
+
/// refused where the source stands above the target's directory, as
|
|
190
|
+
/// `start_renaming` refuses it.
|
|
191
|
+
pub(crate) fn rename(
|
|
192
|
+
&self,
|
|
193
|
+
from: &str,
|
|
194
|
+
to_dir: &Self,
|
|
195
|
+
to: &str,
|
|
196
|
+
) -> Result<(), FilesystemError> {
|
|
197
|
+
let ((source, old), (target, new)) = (self.parent(from)?, to_dir.parent(to)?);
|
|
198
|
+
if target.dentry().is_subdir(&old) {
|
|
199
|
+
return Err(FilesystemError::InvalidInput);
|
|
200
|
+
}
|
|
201
|
+
if source.dentry().is_subdir(&new) {
|
|
202
|
+
return Err(FilesystemError::DirectoryNotEmpty);
|
|
203
|
+
}
|
|
204
|
+
vfs_rename(
|
|
205
|
+
&source.cred,
|
|
206
|
+
source.mnt.sb(),
|
|
207
|
+
&RenameData {
|
|
208
|
+
_held: Vec::new(),
|
|
209
|
+
old_parent: source.dentry().clone(),
|
|
210
|
+
old_dentry: old,
|
|
211
|
+
new_parent: target.dentry().clone(),
|
|
212
|
+
new_dentry: new,
|
|
213
|
+
flag: None,
|
|
214
|
+
},
|
|
215
|
+
)
|
|
216
|
+
}
|
|
217
|
+
}
|