kobako 0.29.0 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (201) hide show
  1. checksums.yaml +4 -4
  2. data/.release-please-manifest.json +1 -1
  3. data/CHANGELOG.md +217 -10
  4. data/Cargo.lock +139 -141
  5. data/README.md +96 -574
  6. data/ROADMAP.md +2 -1
  7. data/crates/kobako-runtime/CHANGELOG.md +269 -1
  8. data/crates/kobako-runtime/Cargo.toml +25 -15
  9. data/crates/kobako-runtime/README.md +7 -3
  10. data/crates/kobako-runtime/src/dispatch.rs +1 -1
  11. data/crates/kobako-runtime/src/endpoint/buffer.rs +192 -0
  12. data/crates/kobako-runtime/src/endpoint/capture.rs +115 -0
  13. data/crates/kobako-runtime/src/endpoint/dispatch.rs +253 -0
  14. data/crates/kobako-runtime/src/endpoint/invoke.rs +194 -0
  15. data/crates/kobako-runtime/src/endpoint/memory_cap.rs +238 -0
  16. data/crates/kobako-runtime/src/endpoint.rs +166 -0
  17. data/crates/kobako-runtime/src/filesystem/bind/fid.rs +82 -0
  18. data/crates/kobako-runtime/src/filesystem/{directory/spelling.rs → bind/lookup.rs} +91 -49
  19. data/crates/kobako-runtime/src/filesystem/bind/walk.rs +249 -0
  20. data/crates/kobako-runtime/src/filesystem/bind.rs +2665 -0
  21. data/crates/kobako-runtime/src/filesystem/overlay/file.rs +89 -0
  22. data/crates/kobako-runtime/src/filesystem/overlay/inode.rs +106 -0
  23. data/crates/kobako-runtime/src/filesystem/overlay/path.rs +285 -0
  24. data/crates/kobako-runtime/src/filesystem/overlay/stack/copy_up.rs +135 -0
  25. data/crates/kobako-runtime/src/filesystem/overlay/stack/dir.rs +263 -0
  26. data/crates/kobako-runtime/src/filesystem/overlay/stack/namei.rs +135 -0
  27. data/crates/kobako-runtime/src/filesystem/overlay/stack/readdir.rs +57 -0
  28. data/crates/kobako-runtime/src/filesystem/overlay/stack.rs +227 -734
  29. data/crates/kobako-runtime/src/filesystem/overlay.rs +2649 -1135
  30. data/crates/kobako-runtime/src/filesystem/path.rs +2 -41
  31. data/crates/kobako-runtime/src/filesystem/pipe.rs +221 -0
  32. data/crates/kobako-runtime/src/filesystem/rdir.rs +101 -0
  33. data/crates/kobako-runtime/src/filesystem/tmpfs/tmpfile.rs +32 -0
  34. data/crates/kobako-runtime/src/filesystem/tmpfs/whiteout.rs +85 -0
  35. data/crates/kobako-runtime/src/filesystem/tmpfs/xattr.rs +22 -0
  36. data/crates/kobako-runtime/src/filesystem/tmpfs.rs +1985 -0
  37. data/crates/kobako-runtime/src/filesystem/wrap.rs +224 -0
  38. data/crates/kobako-runtime/src/filesystem.rs +21 -387
  39. data/crates/kobako-runtime/src/hashtable.rs +44 -0
  40. data/crates/kobako-runtime/src/lib.rs +11 -6
  41. data/crates/kobako-runtime/src/locking/seqlock.rs +66 -0
  42. data/crates/kobako-runtime/src/locking.rs +218 -0
  43. data/crates/kobako-runtime/src/mount.rs +31 -104
  44. data/crates/kobako-runtime/src/profile.rs +137 -1
  45. data/crates/kobako-runtime/src/sched.rs +210 -0
  46. data/crates/kobako-runtime/src/security.rs +696 -0
  47. data/crates/kobako-runtime/src/vfs/attr.rs +162 -0
  48. data/crates/kobako-runtime/src/vfs/cred.rs +334 -0
  49. data/crates/kobako-runtime/src/vfs/dentry.rs +1412 -0
  50. data/crates/kobako-runtime/src/vfs/errno.rs +153 -0
  51. data/crates/kobako-runtime/src/vfs/file.rs +1915 -0
  52. data/crates/kobako-runtime/src/vfs/inode.rs +429 -0
  53. data/crates/kobako-runtime/src/vfs/kern_path.rs +217 -0
  54. data/crates/kobako-runtime/src/vfs/mount.rs +59 -0
  55. data/crates/kobako-runtime/src/vfs/namei.rs +5708 -0
  56. data/crates/kobako-runtime/src/vfs/openat2.rs +116 -0
  57. data/crates/kobako-runtime/src/vfs/path.rs +64 -0
  58. data/crates/kobako-runtime/src/vfs/readdir.rs +163 -0
  59. data/crates/kobako-runtime/src/vfs/stat.rs +157 -0
  60. data/crates/kobako-runtime/src/vfs/super_block.rs +122 -0
  61. data/crates/kobako-runtime/src/vfs/utimes.rs +289 -0
  62. data/crates/kobako-runtime/src/vfs.rs +60 -0
  63. data/crates/kobako-runtime/tests/linux_baseline/calls.rs +171 -0
  64. data/crates/kobako-runtime/tests/linux_baseline/concurrent.rs +276 -0
  65. data/crates/kobako-runtime/tests/linux_baseline/kobako.rs +323 -0
  66. data/crates/kobako-runtime/tests/linux_baseline/linux-baseline-expected.txt +16 -0
  67. data/crates/kobako-runtime/tests/linux_baseline/linux.rs +449 -0
  68. data/crates/kobako-runtime/tests/linux_baseline/main.rs +611 -0
  69. data/crates/kobako-runtime/tests/linux_baseline/permissions.rs +236 -0
  70. data/crates/kobako-transport/CHANGELOG.md +8 -2
  71. data/crates/kobako-transport/Cargo.toml +3 -3
  72. data/crates/kobako-transport/README.md +6 -5
  73. data/crates/kobako-transport/src/envelope/bytes.rs +33 -25
  74. data/crates/kobako-transport/src/envelope/call.rs +5 -5
  75. data/crates/kobako-transport/src/envelope/compile_reply.rs +2 -2
  76. data/crates/kobako-transport/src/envelope/error_record.rs +3 -3
  77. data/crates/kobako-transport/src/envelope/fault.rs +2 -2
  78. data/crates/kobako-transport/src/envelope/outcome.rs +3 -3
  79. data/crates/kobako-transport/src/envelope/preamble.rs +12 -12
  80. data/crates/kobako-transport/src/envelope/reply.rs +2 -2
  81. data/crates/kobako-transport/src/envelope/request.rs +2 -2
  82. data/crates/kobako-transport/src/envelope/run.rs +1 -1
  83. data/crates/kobako-transport/src/envelope/unsupported.rs +2 -2
  84. data/crates/kobako-transport/src/envelope.rs +2 -2
  85. data/crates/kobako-wasmtime/CHANGELOG.md +154 -8
  86. data/crates/kobako-wasmtime/Cargo.toml +15 -14
  87. data/crates/kobako-wasmtime/README.md +9 -5
  88. data/crates/kobako-wasmtime/src/abi.rs +12 -20
  89. data/crates/kobako-wasmtime/src/ambient.rs +1 -2
  90. data/crates/kobako-wasmtime/src/cache.rs +5 -5
  91. data/crates/kobako-wasmtime/src/config.rs +22 -7
  92. data/crates/kobako-wasmtime/src/descriptors.rs +133 -100
  93. data/crates/kobako-wasmtime/src/driver.rs +29 -96
  94. data/crates/kobako-wasmtime/src/exports.rs +5 -16
  95. data/crates/kobako-wasmtime/src/guest.rs +38 -0
  96. data/crates/kobako-wasmtime/src/imports.rs +14 -8
  97. data/crates/kobako-wasmtime/src/instance_pre.rs +3 -3
  98. data/crates/kobako-wasmtime/src/invocation.rs +26 -46
  99. data/crates/kobako-wasmtime/src/lib.rs +1 -4
  100. data/crates/kobako-wasmtime/src/limiter.rs +6 -230
  101. data/crates/kobako-wasmtime/src/preview1/differential/mount.rs +69 -46
  102. data/crates/kobako-wasmtime/src/preview1/differential.rs +121 -7
  103. data/crates/kobako-wasmtime/src/preview1/errno.rs +74 -206
  104. data/crates/kobako-wasmtime/src/preview1/fd.rs +1248 -500
  105. data/crates/kobako-wasmtime/src/preview1/memory.rs +117 -150
  106. data/crates/kobako-wasmtime/src/preview1/mount_view/wrap.rs +211 -0
  107. data/crates/kobako-wasmtime/src/preview1/mount_view.rs +1939 -672
  108. data/crates/kobako-wasmtime/src/preview1/path.rs +230 -349
  109. data/crates/kobako-wasmtime/src/preview1/poll.rs +148 -100
  110. data/crates/kobako-wasmtime/src/preview1/sock.rs +6 -23
  111. data/crates/kobako-wasmtime/src/preview1/testsuite-expected.txt +6 -0
  112. data/crates/kobako-wasmtime/src/preview1/testsuite.rs +157 -0
  113. data/crates/kobako-wasmtime/src/preview1.rs +464 -413
  114. data/crates/kobako-wasmtime/src/trap.rs +9 -9
  115. data/crates/kobako-wasmtime/src/wasi.rs +53 -37
  116. data/crates/kobako-wasmtime/tests/{guest_file.rs → integration/guest_file.rs} +232 -201
  117. data/crates/kobako-wasmtime/tests/integration/main.rs +29 -0
  118. data/crates/kobako-wasmtime/tests/{preamble_flags.rs → integration/preamble_flags.rs} +1 -1
  119. data/crates/kobako-wasmtime/tests/{snippet_compile.rs → integration/snippet_compile.rs} +1 -1
  120. data/crates/kobako-wasmtime/tests/{unanswered_call.rs → integration/unanswered_call.rs} +1 -1
  121. data/crates/kobako-wasmtime/tests/{unsupported_call.rs → integration/unsupported_call.rs} +1 -1
  122. data/crates/kobako-wasmtime/witx/typenames.witx +750 -0
  123. data/crates/kobako-wasmtime/witx/wasi_snapshot_preview1.witx +441 -0
  124. data/data/kobako.wasm +0 -0
  125. data/ext/kobako/Cargo.toml +1 -1
  126. data/ext/kobako/src/runtime/bridge.rs +1 -1
  127. data/ext/kobako/src/runtime/errors.rs +1 -1
  128. data/ext/kobako/src/runtime/escape.rs +28 -14
  129. data/ext/kobako/src/runtime/filesystem/grants.rs +21 -5
  130. data/ext/kobako/src/runtime/filesystem/ruby.rs +464 -156
  131. data/ext/kobako/src/runtime/filesystem.rs +109 -137
  132. data/ext/kobako/src/runtime/gvl.rs +43 -8
  133. data/ext/kobako/src/runtime/snapshot.rs +1 -1
  134. data/ext/kobako/src/runtime.rs +6 -1
  135. data/lib/kobako/catalog/extensions.rb +5 -5
  136. data/lib/kobako/catalog/flags.rb +2 -2
  137. data/lib/kobako/catalog/handles.rb +6 -6
  138. data/lib/kobako/catalog/mount.rb +2 -10
  139. data/lib/kobako/catalog/services.rb +2 -2
  140. data/lib/kobako/catalog/snippets.rb +7 -7
  141. data/lib/kobako/catalog.rb +1 -1
  142. data/lib/kobako/codec/decoder.rb +10 -10
  143. data/lib/kobako/codec/ext_types.rb +3 -5
  144. data/lib/kobako/codec/handle_walk.rb +4 -6
  145. data/lib/kobako/codec/nesting.rb +7 -7
  146. data/lib/kobako/codec/utils.rb +1 -1
  147. data/lib/kobako/codec/value_budget.rb +3 -3
  148. data/lib/kobako/context.rb +5 -5
  149. data/lib/kobako/filesystem.rb +44 -13
  150. data/lib/kobako/payload/arguments.rb +2 -2
  151. data/lib/kobako/pool.rb +4 -4
  152. data/lib/kobako/sandbox.rb +28 -28
  153. data/lib/kobako/sandbox_options.rb +2 -3
  154. data/lib/kobako/snippet/bytecode.rb +1 -1
  155. data/lib/kobako/snippet/source.rb +1 -1
  156. data/lib/kobako/transport/dispatcher.rb +5 -5
  157. data/lib/kobako/transport/exposure.rb +2 -2
  158. data/lib/kobako/transport/reflection.rb +7 -7
  159. data/lib/kobako/transport/run.rb +5 -5
  160. data/lib/kobako/transport/yielder.rb +1 -1
  161. data/lib/kobako/version.rb +1 -1
  162. data/lib/kobako.rb +34 -0
  163. data/release-please-config.json +1 -0
  164. data/sig/kobako/catalog/extensions.rbs +3 -3
  165. data/sig/kobako/catalog/handles.rbs +3 -3
  166. data/sig/kobako/catalog/mount.rbs +0 -4
  167. data/sig/kobako/catalog/services.rbs +1 -1
  168. data/sig/kobako/catalog/snippets.rbs +3 -3
  169. data/sig/kobako/catalog.rbs +1 -1
  170. data/sig/kobako/codec/decoder.rbs +1 -1
  171. data/sig/kobako/codec/handle_walk.rbs +1 -1
  172. data/sig/kobako/codec/nesting.rbs +2 -2
  173. data/sig/kobako/codec/utils.rbs +1 -1
  174. data/sig/kobako/codec/value_budget.rbs +1 -1
  175. data/sig/kobako/codec.rbs +1 -2
  176. data/sig/kobako/context.rbs +1 -1
  177. data/sig/kobako/filesystem.rbs +19 -14
  178. data/sig/kobako/guest_yielder.rbs +1 -1
  179. data/sig/kobako/payload/arguments.rbs +1 -1
  180. data/sig/kobako/pool.rbs +2 -2
  181. data/sig/kobako/runtime.rbs +1 -1
  182. data/sig/kobako/sandbox.rbs +3 -5
  183. data/sig/kobako/sandbox_options.rbs +1 -1
  184. data/sig/kobako/transport/dispatcher.rbs +2 -2
  185. data/sig/kobako/transport/exposure.rbs +1 -1
  186. data/sig/kobako/transport/reflection.rbs +2 -2
  187. data/sig/kobako/transport/run.rbs +3 -3
  188. data/sig/kobako/transport.rbs +1 -1
  189. metadata +67 -19
  190. data/crates/kobako-runtime/src/filesystem/directory.rs +0 -1082
  191. data/crates/kobako-runtime/src/filesystem/memory.rs +0 -1100
  192. data/crates/kobako-runtime/src/filesystem/overlay/exclusive.rs +0 -88
  193. data/crates/kobako-runtime/src/filesystem/temporary.rs +0 -167
  194. data/crates/kobako-wasmtime/src/capture.rs +0 -96
  195. data/crates/kobako-wasmtime/src/dispatch.rs +0 -125
  196. data/crates/kobako-wasmtime/src/guest_mem.rs +0 -271
  197. data/crates/kobako-wasmtime/src/io.rs +0 -89
  198. data/crates/kobako-wasmtime/src/preview1/args.rs +0 -60
  199. data/crates/kobako-wasmtime/src/preview1/beneath.rs +0 -188
  200. /data/crates/kobako-wasmtime/tests/{compile_posture.rs → integration/compile_posture.rs} +0 -0
  201. /data/crates/kobako-wasmtime/tests/{support → integration/support}/mod.rs +0 -0
@@ -0,0 +1,89 @@
1
+ //! A lower Layer's file open to read, which reads the upper's once a copy up
2
+ //! makes one, as overlayfs's `ovl_real_file` looks where the data is at each
3
+ //! call and opens the upper file the first time it finds it there.
4
+
5
+ use std::sync::Arc;
6
+
7
+ use super::super::{FileOperations, FilesystemError, Kiocb, OpenHow};
8
+ use super::inode::OverlayFileInode;
9
+ use crate::vfs::{generic_file_llseek, Dentry, Whence};
10
+
11
+ /// `file`, opened on `real` for the entry `inode` keeps, as `ovl_open` hands out
12
+ /// a file over the real one.
13
+ pub(super) fn ovl_file(
14
+ file: Box<dyn FileOperations>,
15
+ real: Arc<Dentry>,
16
+ inode: Arc<OverlayFileInode>,
17
+ ) -> Box<dyn FileOperations> {
18
+ Box::new(OverlayFile {
19
+ file,
20
+ real,
21
+ inode,
22
+ upper: None,
23
+ })
24
+ }
25
+
26
+ /// A lower Layer's file open to read, as overlayfs's `struct ovl_file`.
27
+ struct OverlayFile {
28
+ file: Box<dyn FileOperations>,
29
+ /// The Layer's dentry `file` was opened on.
30
+ real: Arc<Dentry>,
31
+ /// The entry this file reads, which says where its data is now.
32
+ inode: Arc<OverlayFileInode>,
33
+ /// The upper's file, once a copy up made one and a call found it.
34
+ upper: Option<Box<dyn FileOperations>>,
35
+ }
36
+
37
+ impl OverlayFile {
38
+ /// The file holding the data now, as `ovl_real_file_path` finds it.
39
+ fn real_file(&mut self) -> Result<&mut dyn FileOperations, FilesystemError> {
40
+ if self.upper.is_none() {
41
+ let real = self.inode.path_real();
42
+ if !Arc::ptr_eq(real.dentry(), &self.real) {
43
+ let read = OpenHow {
44
+ read: true,
45
+ ..OpenHow::default()
46
+ };
47
+ self.upper = Some(real.open_realfile(&read)?);
48
+ }
49
+ }
50
+ Ok(match &mut self.upper {
51
+ Some(upper) => upper.as_mut(),
52
+ None => self.file.as_mut(),
53
+ })
54
+ }
55
+ }
56
+
57
+ impl FileOperations for OverlayFile {
58
+ /// The view's size answers an end, which is the real file's, as
59
+ /// `ovl_llseek` seeks the real file.
60
+ fn llseek(
61
+ &mut self,
62
+ dentry: &Dentry,
63
+ pos: u64,
64
+ offset: i64,
65
+ whence: Whence,
66
+ ) -> Result<u64, FilesystemError> {
67
+ generic_file_llseek(dentry, pos, offset, whence)
68
+ }
69
+
70
+ fn read_iter(&mut self, iocb: &mut Kiocb, buf: &mut [u8]) -> Result<usize, FilesystemError> {
71
+ self.real_file()?.read_iter(iocb, buf)
72
+ }
73
+
74
+ /// A file open to read flushes what it reads, as `ovl_fsync` syncs the
75
+ /// real file.
76
+ fn fsync(&mut self, datasync: bool) -> Result<(), FilesystemError> {
77
+ self.real_file()?.fsync(datasync)
78
+ }
79
+
80
+ fn flush(&mut self) -> Result<(), FilesystemError> {
81
+ self.file.flush()
82
+ }
83
+
84
+ /// A stream stays one through the view, as overlayfs hands a special file's
85
+ /// open to its real inode.
86
+ fn is_stream(&self) -> bool {
87
+ self.file.is_stream()
88
+ }
89
+ }
@@ -0,0 +1,106 @@
1
+ //! The entries of an Overlay's view as it keeps them on their inodes, as
2
+ //! overlayfs keeps an `ovl_inode` for each: a directory's Layers' directories, or
3
+ //! where anything else stands in a Layer, with the lock a copy up of the entry
4
+ //! holds. A directory and a file keep apart what `ovl_inode` holds together, so
5
+ //! each answers only what it has.
6
+ //!
7
+ //! The runtime records one inode for each dentry it reaches, so every handle
8
+ //! of an entry reaches the same state, and a removal or a replacement
9
+ //! through the runtime leaves the next lookup an inode, and so a state, of
10
+ //! its own.
11
+
12
+ use std::any::Any;
13
+ use std::sync::{Arc, Mutex, MutexGuard, PoisonError};
14
+
15
+ use super::path::OverlayPath;
16
+ use super::stack::{Level, UPPER};
17
+ use crate::locking::RwSemaphore;
18
+ use crate::vfs::{Dentry, Inode};
19
+
20
+ /// A directory of the view.
21
+ pub(super) struct OverlayDirInode {
22
+ /// Each Layer's directory it stands for.
23
+ level: Mutex<Level>,
24
+ /// The lock a copy up of it holds, as overlayfs's `ovl_inode_lock`, so two
25
+ /// calls never copy it up at once.
26
+ pub(super) lock: Arc<RwSemaphore>,
27
+ }
28
+
29
+ /// A file of the view, or anything else that is no directory.
30
+ pub(super) struct OverlayFileInode {
31
+ /// The Layer and its dentry a lookup found it at, or a copy up made, as
32
+ /// `struct ovl_entry` holds each real dentry with its layer, so it answers
33
+ /// from there even once its name is gone from the view.
34
+ real: Mutex<OverlayPath>,
35
+ /// The lock a copy up of it holds, as overlayfs's `ovl_inode_lock`.
36
+ pub(super) lock: Arc<RwSemaphore>,
37
+ }
38
+
39
+ fn lock<T>(held: &Mutex<T>) -> MutexGuard<'_, T> {
40
+ held.lock().unwrap_or_else(PoisonError::into_inner)
41
+ }
42
+
43
+ impl OverlayDirInode {
44
+ /// A directory standing for `level`, as `ovl_inode_params` carries it.
45
+ pub(super) fn new(level: Level) -> Self {
46
+ Self {
47
+ level: Mutex::new(level),
48
+ lock: Arc::default(),
49
+ }
50
+ }
51
+
52
+ /// What `inode` keeps, where it is a directory's.
53
+ pub(super) fn of(inode: &Inode) -> Option<Arc<Self>> {
54
+ inode.private().get()?.clone().downcast().ok()
55
+ }
56
+
57
+ pub(super) fn level(&self) -> Level {
58
+ lock(&self.level).clone()
59
+ }
60
+
61
+ /// The upper now holds `dir` as this directory, made or found there by a
62
+ /// copy up, as overlayfs's `ovl_inode_update` records the upper dentry, so
63
+ /// every handle of it asks the upper too.
64
+ pub(super) fn inode_update(&self, dir: &OverlayPath) {
65
+ lock(&self.level)[UPPER] = Some(dir.clone());
66
+ }
67
+ }
68
+
69
+ impl OverlayFileInode {
70
+ /// An entry answering through `real`, as `ovl_inode_params` carries it.
71
+ pub(super) fn new(real: OverlayPath) -> Self {
72
+ Self {
73
+ real: Mutex::new(real),
74
+ lock: Arc::default(),
75
+ }
76
+ }
77
+
78
+ /// What `inode` keeps, where it is no directory's.
79
+ pub(super) fn of(inode: &Inode) -> Option<Arc<Self>> {
80
+ inode.private().get()?.clone().downcast().ok()
81
+ }
82
+
83
+ /// The Layer's dentry this answers through.
84
+ pub(super) fn real(&self) -> Arc<Dentry> {
85
+ lock(&self.real).dentry().clone()
86
+ }
87
+
88
+ /// The Layer and its dentry this answers through, as `ovl_path_real` finds
89
+ /// the real path.
90
+ pub(super) fn path_real(&self) -> OverlayPath {
91
+ lock(&self.real).clone()
92
+ }
93
+
94
+ /// This file's data now stands at `real`, in the upper, as
95
+ /// `ovl_dentry_set_upper_alias` records a copy up.
96
+ pub(super) fn set_real(&self, real: OverlayPath) {
97
+ *lock(&self.real) = real;
98
+ }
99
+ }
100
+
101
+ /// Keep on `inode` what its lookup found, as `ovl_inode_init` takes it from
102
+ /// `ovl_lookup` under the directory's lock, so a name moved meanwhile cannot
103
+ /// have the entry stand for another.
104
+ pub(super) fn ovl_inode_init<T: Any + Send + Sync>(inode: &Inode, kept: T) {
105
+ inode.private().get_or_init(|| Arc::new(kept));
106
+ }
@@ -0,0 +1,285 @@
1
+ //! One Layer's directory as an Overlay holds it: the directory and its
2
+ //! dentry in the Layer's Filesystem, as overlayfs's `ovl_path` pairs a
3
+ //! layer with its dentry. A name is found, and the directory held while one
4
+ //! is made, removed, or moved, through the VFS's `lookup_one_unlocked`,
5
+ //! `start_creating`, `start_removing`, and `__start_renaming`, and an entry
6
+ //! made, removed, or moved through its `vfs_mkdir`, `vfs_create`, `vfs_rmdir`,
7
+ //! `vfs_unlink`, and `vfs_rename`, as overlayfs's are, so the VFS checks what
8
+ //! stands there and the Layer's dentries follow each change. Each Layer is
9
+ //! asked its own permission as the Overlay's creator, as overlayfs asks
10
+ //! `inode_permission` of the real inode before it searches or opens one.
11
+
12
+ use std::any::Any;
13
+ use std::sync::Arc;
14
+
15
+ use super::super::tmpfs::TmpfsFile;
16
+ use super::super::{
17
+ DirEntry, FileOperations, FileType, Filesystem, FilesystemError, Iattr, InodeOperations, Kstat,
18
+ Lookup, OpenHow, SetTime, Tmpfs,
19
+ };
20
+ use crate::locking::RwSemaphoreGuard;
21
+ use crate::vfs::{
22
+ __start_renaming, inode_permission, lookup_one_unlocked, may_create_dentry, notify_change,
23
+ start_creating, start_removing, vfs_create, vfs_mkdir, vfs_rename, vfs_rmdir, vfs_unlink, Cred,
24
+ Dentry, DirContext, DirEmit, Mask, RenameFlag,
25
+ };
26
+
27
+ /// The change setting what `stat` reports of its times, as
28
+ /// `ovl_set_timestamps` sets `ATTR_ATIME_SET` and `ATTR_MTIME_SET`.
29
+ pub(super) fn timestamps(stat: &Kstat) -> Iattr<'static> {
30
+ Iattr::times(stat.atime.map(SetTime::At), stat.mtime.map(SetTime::At))
31
+ }
32
+
33
+ /// The attribute marking a directory opaque, and its value, as
34
+ /// overlayfs's `OVL_XATTR_OPAQUE`.
35
+ const OPAQUE_XATTR: &str = "trusted.overlay.opaque";
36
+ const OPAQUE: &[u8] = b"y";
37
+
38
+ #[derive(Clone)]
39
+ pub(super) struct OverlayPath {
40
+ filesystem: Arc<Filesystem>,
41
+ dentry: Arc<Dentry>,
42
+ /// Who every call on a Layer is made as, whoever asked the view, as overlayfs
43
+ /// overrides the caller's creds with `ofs->creator_cred`.
44
+ creator_cred: Arc<Cred>,
45
+ }
46
+
47
+ impl OverlayPath {
48
+ /// The root of the Layer `filesystem` stands for.
49
+ pub(super) fn root(filesystem: &Arc<Filesystem>, creator_cred: &Arc<Cred>) -> Self {
50
+ Self {
51
+ dentry: filesystem.s_root(),
52
+ filesystem: filesystem.clone(),
53
+ creator_cred: creator_cred.clone(),
54
+ }
55
+ }
56
+
57
+ /// Where this directory stands in its Layer.
58
+ pub(super) fn dentry(&self) -> &Arc<Dentry> {
59
+ &self.dentry
60
+ }
61
+
62
+ /// Whether what this Layer holds at `name`, if it holds a dentry there, is
63
+ /// `real`, as `ovl_matches_upper` compares what stands in the upper.
64
+ pub(super) fn matches_real(&self, name: &str, real: &Arc<Dentry>) -> bool {
65
+ let held = self.filesystem.dentries().lookup(&self.dentry, name);
66
+ held.is_none_or(|held| Arc::ptr_eq(&held, real))
67
+ }
68
+
69
+ /// The dentry of `name` as this directory stores it, what stands there
70
+ /// recorded on it.
71
+ fn lookup_real(&self, name: &str) -> Result<Arc<Dentry>, FilesystemError> {
72
+ lookup_one_unlocked(&self.creator_cred, &self.filesystem, &self.dentry, name)
73
+ }
74
+
75
+ /// This directory held for making `name`, and its dentry.
76
+ fn start_creating(
77
+ &self,
78
+ name: &str,
79
+ ) -> Result<(RwSemaphoreGuard, Arc<Dentry>), FilesystemError> {
80
+ start_creating(&self.creator_cred, &self.filesystem, &self.dentry, name)
81
+ }
82
+
83
+ /// This directory held for removing what stands at `name`, and its dentry.
84
+ fn start_removing(
85
+ &self,
86
+ name: &str,
87
+ ) -> Result<(RwSemaphoreGuard, Arc<Dentry>), FilesystemError> {
88
+ start_removing(&self.creator_cred, &self.filesystem, &self.dentry, name)
89
+ }
90
+
91
+ fn inode_lock_shared(&self) -> Result<RwSemaphoreGuard, FilesystemError> {
92
+ self.dentry.inode_lock_shared()
93
+ }
94
+
95
+ fn inode_lock(&self) -> Result<RwSemaphoreGuard, FilesystemError> {
96
+ self.dentry.inode_lock()
97
+ }
98
+
99
+ /// This directory as a Tmpfs, the one kind holding an Overlay's marks.
100
+ fn tmpfs(&self) -> Option<Arc<Tmpfs>> {
101
+ let ops: Arc<dyn Any + Send + Sync> = self.dentry.inode_ops().ok()?;
102
+ ops.downcast().ok()
103
+ }
104
+
105
+ /// This directory of the upper, which is always a Tmpfs.
106
+ fn upper(&self) -> Result<Arc<Tmpfs>, FilesystemError> {
107
+ self.tmpfs().ok_or(FilesystemError::ReadOnlyFilesystem)
108
+ }
109
+
110
+ /// Whether a whiteout stands at `name`, as `ovl_is_whiteout` reads the
111
+ /// entry; a Layer that is no Tmpfs holds none.
112
+ pub(super) fn is_whiteout(&self, name: &str) -> bool {
113
+ self.tmpfs().is_some_and(|tmpfs| tmpfs.is_whiteout(name))
114
+ }
115
+
116
+ /// Whether `dentry`, found in this directory, stands for a whiteout, as
117
+ /// `ovl_is_whiteout` reads a dentry, a character device being one only in a
118
+ /// Tmpfs, whose marks an Overlay reads.
119
+ pub(super) fn is_whiteout_dentry(&self, dentry: &Dentry) -> bool {
120
+ dentry.kind() == Ok(FileType::Chrdev) && self.tmpfs().is_some()
121
+ }
122
+
123
+ /// Whether this directory hides the Layers below it.
124
+ pub(super) fn is_opaque(&self) -> bool {
125
+ self.tmpfs()
126
+ .and_then(|tmpfs| tmpfs.getxattr(OPAQUE_XATTR))
127
+ .is_some_and(|value| value == OPAQUE)
128
+ }
129
+
130
+ pub(super) fn set_opaque(&self) -> Result<(), FilesystemError> {
131
+ let _held = self.inode_lock()?;
132
+ self.upper()?.setxattr(OPAQUE_XATTR, OPAQUE)
133
+ }
134
+
135
+ pub(super) fn do_whiteout(&self, name: &str) -> Result<(), FilesystemError> {
136
+ let (_held, dentry) = self.start_creating(name)?;
137
+ may_create_dentry(&self.creator_cred, &self.filesystem, &self.dentry, &dentry)?;
138
+ self.upper()?.whiteout(name)
139
+ }
140
+
141
+ /// Move `from` here to `to` in `target` as `renameat2` does with
142
+ /// `flag`.
143
+ pub(super) fn do_rename(
144
+ &self,
145
+ from: &str,
146
+ target: &OverlayPath,
147
+ to: &str,
148
+ flag: Option<RenameFlag>,
149
+ ) -> Result<(), FilesystemError> {
150
+ let rd = __start_renaming(
151
+ &self.filesystem,
152
+ (&self.dentry, from),
153
+ (&target.dentry, to),
154
+ flag,
155
+ )?;
156
+ vfs_rename(&self.creator_cred, &self.filesystem, &rd)
157
+ }
158
+
159
+ pub(super) fn do_tmpfile(&self) -> Result<TmpfsFile, FilesystemError> {
160
+ self.upper()?.tmpfile()
161
+ }
162
+
163
+ /// Name `file` here, answering the dentry of what was linked, as
164
+ /// `ovl_do_link` leaves it on the dentry it was handed, so nothing landing at
165
+ /// the name afterwards is taken for it.
166
+ pub(super) fn do_link(
167
+ &self,
168
+ file: &TmpfsFile,
169
+ name: &str,
170
+ ) -> Result<Arc<Dentry>, FilesystemError> {
171
+ let (_held, dentry) = self.start_creating(name)?;
172
+ may_create_dentry(&self.creator_cred, &self.filesystem, &self.dentry, &dentry)?;
173
+ let upper = self.upper()?;
174
+ upper.link(file, name)?;
175
+ if let Lookup::Found(inode) = upper.lookup(&dentry)? {
176
+ dentry.add(Some(inode));
177
+ }
178
+ Ok(dentry)
179
+ }
180
+
181
+ /// Set this directory's times to those `stat` reports, as
182
+ /// `ovl_set_timestamps` hands `notify_change` the upper dentry.
183
+ pub(super) fn set_timestamps(&self, stat: &Kstat) -> Result<(), FilesystemError> {
184
+ let held = self.dentry.inode_lock()?;
185
+ notify_change(&self.dentry, &held, &timestamps(stat))
186
+ }
187
+
188
+ /// `name` as this directory stores it and the dentry standing there, `None`
189
+ /// where nothing does, as `ovl_lookup_single` asks a layer once for a name.
190
+ pub(super) fn lookup_single(
191
+ &self,
192
+ name: &str,
193
+ ) -> Result<Option<(String, Arc<Dentry>)>, FilesystemError> {
194
+ let dentry = self.lookup_real(name)?;
195
+ Ok(dentry.inode().is_some().then(|| (dentry.name(), dentry)))
196
+ }
197
+
198
+ /// The directory `dentry` names beneath this one.
199
+ pub(super) fn beneath(&self, dentry: Arc<Dentry>) -> Self {
200
+ Self {
201
+ dentry,
202
+ ..self.clone()
203
+ }
204
+ }
205
+
206
+ /// Make the file `name`, holding the directory, as `ovl_do_create` does.
207
+ pub(super) fn do_create(&self, name: &str) -> Result<(), FilesystemError> {
208
+ let (_held, child) = self.start_creating(name)?;
209
+ vfs_create(&self.creator_cred, &self.filesystem, &self.dentry, &child)
210
+ }
211
+
212
+ /// The directory standing at `name`, a link there refused as a loop, as
213
+ /// `ovl_lookup` follows no link in a Layer.
214
+ pub(super) fn open_dir(&self, name: &str) -> Result<Self, FilesystemError> {
215
+ let dentry = self.lookup_real(name)?;
216
+ let inode = dentry.inode().ok_or(FilesystemError::NotFound)?;
217
+ match inode.kind() {
218
+ FileType::Dir => Ok(self.beneath(dentry)),
219
+ FileType::Symlink => Err(FilesystemError::FilesystemLoop),
220
+ _ => Err(FilesystemError::NotADirectory),
221
+ }
222
+ }
223
+
224
+ pub(super) fn getattr(&self) -> Result<Kstat, FilesystemError> {
225
+ self.dentry.getattr()
226
+ }
227
+
228
+ pub(super) fn getattr_at(&self, name: &str) -> Result<Kstat, FilesystemError> {
229
+ self.lookup_real(name)?.getattr()
230
+ }
231
+
232
+ /// This directory opened, kept open for whoever holds it, as overlayfs's
233
+ /// `ovl_dir_open` opens the real directory.
234
+ pub(super) fn open_real(&self) -> Result<Box<dyn FileOperations>, FilesystemError> {
235
+ self.open_realfile(&OpenHow {
236
+ read: true,
237
+ ..OpenHow::default()
238
+ })
239
+ }
240
+
241
+ /// What this dentry names opened as `how` asks, once its Layer lets the
242
+ /// Overlay's creator open it so, as `ovl_open_realfile` asks
243
+ /// `inode_permission` for `MAY_OPEN` and the access before it opens.
244
+ pub(super) fn open_realfile(
245
+ &self,
246
+ how: &OpenHow,
247
+ ) -> Result<Box<dyn FileOperations>, FilesystemError> {
248
+ let mut mask = Mask::OPEN;
249
+ if how.read {
250
+ mask = mask | Mask::READ;
251
+ }
252
+ if how.write {
253
+ mask = mask | Mask::WRITE;
254
+ }
255
+ inode_permission(&self.creator_cred, &self.filesystem, &self.dentry, mask)?;
256
+ self.dentry.open(how)
257
+ }
258
+
259
+ pub(super) fn entries(&self) -> Result<Vec<DirEntry>, FilesystemError> {
260
+ let _held = self.inode_lock_shared()?;
261
+ let mut entries = Vec::new();
262
+ let mut actor = |emit: &DirEmit<'_>| {
263
+ entries.push(emit.to_dir_entry());
264
+ true
265
+ };
266
+ self.open_real()?
267
+ .iterate_shared(&self.dentry, &mut DirContext::new(2, &mut actor))?;
268
+ Ok(entries)
269
+ }
270
+
271
+ pub(super) fn do_mkdir(&self, name: &str) -> Result<(), FilesystemError> {
272
+ let (_held, child) = self.start_creating(name)?;
273
+ vfs_mkdir(&self.creator_cred, &self.filesystem, &self.dentry, &child)
274
+ }
275
+
276
+ pub(super) fn do_rmdir(&self, name: &str) -> Result<(), FilesystemError> {
277
+ let (_held, victim) = self.start_removing(name)?;
278
+ vfs_rmdir(&self.creator_cred, &self.filesystem, &self.dentry, &victim)
279
+ }
280
+
281
+ pub(super) fn do_unlink(&self, name: &str) -> Result<(), FilesystemError> {
282
+ let (_held, victim) = self.start_removing(name)?;
283
+ vfs_unlink(&self.creator_cred, &self.filesystem, &self.dentry, &victim)
284
+ }
285
+ }
@@ -0,0 +1,135 @@
1
+ //! Making what a change needs in the upper from what a lower Layer shows,
2
+ //! as overlayfs's `copy_up.c` copies a file or a directory up: whole and
3
+ //! with the lower one's times before it is named, and leaving the times
4
+ //! of the directory it lands in as they were.
5
+
6
+ use std::sync::Arc;
7
+
8
+ use super::super::super::{FileType, FilesystemError, OpenHow};
9
+ use super::super::inode::OverlayFileInode;
10
+ use super::super::path::{timestamps, OverlayPath};
11
+ use super::{copy_whole, OverlayFs, ViewDir, UPPER};
12
+ use crate::vfs::{Dentry, RenameFlag};
13
+
14
+ impl OverlayFs {
15
+ /// The directory `dir` in the upper, made there along with each directory
16
+ /// above it that so far shows only from another Layer, as overlayfs's
17
+ /// `ovl_copy_up_flags` climbs the dentry's parents in a loop to copy up the
18
+ /// topmost first, however deep the Layer's tree is.
19
+ pub(in super::super) fn upper_dir(
20
+ &self,
21
+ dir: &ViewDir,
22
+ ) -> Result<OverlayPath, FilesystemError> {
23
+ let mut unmade = Vec::new();
24
+ let mut at = dir.clone();
25
+ let mut made = loop {
26
+ if let Some(held) = at.level()[UPPER].clone() {
27
+ break held;
28
+ }
29
+ let next = self.dir(&at.dentry.parent())?;
30
+ unmade.push(at);
31
+ at = next;
32
+ };
33
+ while let Some(dir) = unmade.pop() {
34
+ made = self.copy_up_dir(&dir, &made)?;
35
+ }
36
+ Ok(made)
37
+ }
38
+
39
+ /// The directory `dir` in the upper, beneath `parent` there. It is made in
40
+ /// the workdir with the lower one's times and moved into place, as
41
+ /// `ovl_copy_up_workdir` does. Another call may have copied it up while this
42
+ /// one waited, as `ovl_already_copied_up_locked` checks; what else stands at
43
+ /// the name is no copy of this directory, and refuses the move.
44
+ fn copy_up_dir(
45
+ &self,
46
+ dir: &ViewDir,
47
+ parent: &OverlayPath,
48
+ ) -> Result<OverlayPath, FilesystemError> {
49
+ let name = dir.dentry.name();
50
+ let _held = dir.inode.lock.down_write()?;
51
+ let level = dir.level();
52
+ if let Some(held) = &level[UPPER] {
53
+ return Ok(held.clone());
54
+ }
55
+ let lower = level
56
+ .iter()
57
+ .flatten()
58
+ .next()
59
+ .ok_or(FilesystemError::NotFound)?
60
+ .getattr()?;
61
+ let (work, temp) = self.tempname()?;
62
+ work.do_mkdir(&temp)?;
63
+ let made = work
64
+ .open_dir(&temp)
65
+ .and_then(|made| made.set_timestamps(&lower));
66
+ let parent_times = parent.getattr()?;
67
+ let moved =
68
+ made.and_then(|()| work.do_rename(&temp, parent, &name, Some(RenameFlag::NoReplace)));
69
+ if let Err(err) = moved {
70
+ let _ = work.do_rmdir(&temp);
71
+ return Err(err);
72
+ }
73
+ let _ = parent.set_timestamps(&parent_times);
74
+ let made = parent.open_dir(&name)?;
75
+ dir.inode.inode_update(&made);
76
+ Ok(made)
77
+ }
78
+
79
+ /// Whether `real`, a Layer's dentry, stands in the upper Tmpfs, in its
80
+ /// workdir too, where a removal leaves a file something still holds.
81
+ pub(in super::super) fn is_in_upper(&self, real: &Arc<Dentry>) -> bool {
82
+ self.work
83
+ .as_ref()
84
+ .is_some_and(|work| real.is_subdir(&work.dentry().parent()))
85
+ }
86
+
87
+ /// Copy the file `child` names in `dir` up from the Layer its inode keeps,
88
+ /// as overlayfs's `ovl_copy_up_tmpfile` does: the copy is made with no
89
+ /// name, filled, given the lower file's times, and only then named, so a
90
+ /// copy that fails part way is never seen. A `truncate` copy up leaves the
91
+ /// bytes behind, as `ovl_copy_up_one` sets its size to zero for `O_TRUNC`,
92
+ /// since the open is about to empty it. Only a file copies up, as
93
+ /// overlayfs's `ovl_copy_up_data` only reads a regular file, so a special
94
+ /// file is never opened. A file whose name is gone from the view has no
95
+ /// place to land.
96
+ pub(in super::super) fn copy_up_file(
97
+ &self,
98
+ dir: &ViewDir,
99
+ child: &Dentry,
100
+ truncate: bool,
101
+ ) -> Result<(), FilesystemError> {
102
+ let name = child.name();
103
+ let target_dir = self.upper_dir(dir)?;
104
+ let inode = child
105
+ .inode()
106
+ .as_deref()
107
+ .and_then(OverlayFileInode::of)
108
+ .ok_or(FilesystemError::NotFound)?;
109
+ let _held = inode.lock.down_write()?;
110
+ let real = inode.path_real();
111
+ // Another call may have copied it up while this one waited, as
112
+ // overlayfs's `ovl_already_copied_up_locked` checks.
113
+ if self.is_in_upper(real.dentry()) {
114
+ return Ok(());
115
+ }
116
+ let lower = real.getattr()?;
117
+ if lower.file_type != FileType::RegFile {
118
+ return Err(FilesystemError::PermissionDenied);
119
+ }
120
+ let mut copy = target_dir.do_tmpfile()?;
121
+ if !truncate {
122
+ let mut source = real.open_realfile(&OpenHow {
123
+ read: true,
124
+ ..OpenHow::default()
125
+ })?;
126
+ copy_whole(source.as_mut(), &mut copy)?;
127
+ }
128
+ copy.setattr(&timestamps(&lower));
129
+ let parent_times = target_dir.getattr()?;
130
+ let linked = target_dir.do_link(&copy, &name)?;
131
+ let _ = target_dir.set_timestamps(&parent_times);
132
+ inode.set_real(target_dir.beneath(linked));
133
+ Ok(())
134
+ }
135
+ }