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.
Files changed (218) hide show
  1. checksums.yaml +4 -4
  2. data/.release-please-manifest.json +1 -1
  3. data/CHANGELOG.md +377 -0
  4. data/Cargo.lock +181 -154
  5. data/README.md +99 -459
  6. data/ROADMAP.md +7 -1
  7. data/SECURITY.md +1 -1
  8. data/crates/kobako-runtime/CHANGELOG.md +354 -0
  9. data/crates/kobako-runtime/Cargo.toml +38 -7
  10. data/crates/kobako-runtime/README.md +17 -8
  11. data/crates/kobako-runtime/src/dispatch.rs +9 -1
  12. data/crates/kobako-runtime/src/endpoint/buffer.rs +192 -0
  13. data/crates/kobako-runtime/src/endpoint/capture.rs +115 -0
  14. data/crates/kobako-runtime/src/endpoint/dispatch.rs +253 -0
  15. data/crates/kobako-runtime/src/endpoint/invoke.rs +194 -0
  16. data/crates/kobako-runtime/src/endpoint/memory_cap.rs +238 -0
  17. data/crates/kobako-runtime/src/endpoint.rs +166 -0
  18. data/crates/kobako-runtime/src/filesystem/bind/fid.rs +82 -0
  19. data/crates/kobako-runtime/src/filesystem/bind/lookup.rs +236 -0
  20. data/crates/kobako-runtime/src/filesystem/bind/walk.rs +249 -0
  21. data/crates/kobako-runtime/src/filesystem/bind.rs +2665 -0
  22. data/crates/kobako-runtime/src/filesystem/overlay/file.rs +89 -0
  23. data/crates/kobako-runtime/src/filesystem/overlay/inode.rs +106 -0
  24. data/crates/kobako-runtime/src/filesystem/overlay/path.rs +285 -0
  25. data/crates/kobako-runtime/src/filesystem/overlay/stack/copy_up.rs +135 -0
  26. data/crates/kobako-runtime/src/filesystem/overlay/stack/dir.rs +263 -0
  27. data/crates/kobako-runtime/src/filesystem/overlay/stack/namei.rs +135 -0
  28. data/crates/kobako-runtime/src/filesystem/overlay/stack/readdir.rs +57 -0
  29. data/crates/kobako-runtime/src/filesystem/overlay/stack.rs +301 -0
  30. data/crates/kobako-runtime/src/filesystem/overlay.rs +3469 -0
  31. data/crates/kobako-runtime/src/filesystem/path.rs +21 -0
  32. data/crates/kobako-runtime/src/filesystem/pipe.rs +221 -0
  33. data/crates/kobako-runtime/src/filesystem/rdir.rs +101 -0
  34. data/crates/kobako-runtime/src/filesystem/tmpfs/tmpfile.rs +32 -0
  35. data/crates/kobako-runtime/src/filesystem/tmpfs/whiteout.rs +85 -0
  36. data/crates/kobako-runtime/src/filesystem/tmpfs/xattr.rs +22 -0
  37. data/crates/kobako-runtime/src/filesystem/tmpfs.rs +1985 -0
  38. data/crates/kobako-runtime/src/filesystem/wrap.rs +224 -0
  39. data/crates/kobako-runtime/src/filesystem.rs +26 -0
  40. data/crates/kobako-runtime/src/grant.rs +20 -0
  41. data/crates/kobako-runtime/src/hashtable.rs +44 -0
  42. data/crates/kobako-runtime/src/lib.rs +16 -1
  43. data/crates/kobako-runtime/src/locking/seqlock.rs +66 -0
  44. data/crates/kobako-runtime/src/locking.rs +218 -0
  45. data/crates/kobako-runtime/src/mount.rs +105 -0
  46. data/crates/kobako-runtime/src/profile.rs +140 -4
  47. data/crates/kobako-runtime/src/runtime.rs +12 -0
  48. data/crates/kobako-runtime/src/sched.rs +210 -0
  49. data/crates/kobako-runtime/src/security.rs +696 -0
  50. data/crates/kobako-runtime/src/snapshot.rs +20 -5
  51. data/crates/kobako-runtime/src/vfs/attr.rs +162 -0
  52. data/crates/kobako-runtime/src/vfs/cred.rs +334 -0
  53. data/crates/kobako-runtime/src/vfs/dentry.rs +1412 -0
  54. data/crates/kobako-runtime/src/vfs/errno.rs +153 -0
  55. data/crates/kobako-runtime/src/vfs/file.rs +1915 -0
  56. data/crates/kobako-runtime/src/vfs/inode.rs +429 -0
  57. data/crates/kobako-runtime/src/vfs/kern_path.rs +217 -0
  58. data/crates/kobako-runtime/src/vfs/mount.rs +59 -0
  59. data/crates/kobako-runtime/src/vfs/namei.rs +5708 -0
  60. data/crates/kobako-runtime/src/vfs/openat2.rs +116 -0
  61. data/crates/kobako-runtime/src/vfs/path.rs +64 -0
  62. data/crates/kobako-runtime/src/vfs/readdir.rs +163 -0
  63. data/crates/kobako-runtime/src/vfs/stat.rs +157 -0
  64. data/crates/kobako-runtime/src/vfs/super_block.rs +122 -0
  65. data/crates/kobako-runtime/src/vfs/utimes.rs +289 -0
  66. data/crates/kobako-runtime/src/vfs.rs +60 -0
  67. data/crates/kobako-runtime/src/yielder.rs +11 -6
  68. data/crates/kobako-runtime/tests/linux_baseline/calls.rs +171 -0
  69. data/crates/kobako-runtime/tests/linux_baseline/concurrent.rs +276 -0
  70. data/crates/kobako-runtime/tests/linux_baseline/kobako.rs +323 -0
  71. data/crates/kobako-runtime/tests/linux_baseline/linux-baseline-expected.txt +16 -0
  72. data/crates/kobako-runtime/tests/linux_baseline/linux.rs +449 -0
  73. data/crates/kobako-runtime/tests/linux_baseline/main.rs +611 -0
  74. data/crates/kobako-runtime/tests/linux_baseline/permissions.rs +236 -0
  75. data/crates/kobako-transport/CHANGELOG.md +23 -0
  76. data/crates/kobako-transport/Cargo.toml +3 -3
  77. data/crates/kobako-transport/README.md +6 -5
  78. data/crates/kobako-transport/src/abi.rs +1 -1
  79. data/crates/kobako-transport/src/envelope/bytes.rs +33 -25
  80. data/crates/kobako-transport/src/envelope/call.rs +5 -5
  81. data/crates/kobako-transport/src/envelope/compile_reply.rs +2 -2
  82. data/crates/kobako-transport/src/envelope/error_record.rs +3 -3
  83. data/crates/kobako-transport/src/envelope/fault.rs +4 -3
  84. data/crates/kobako-transport/src/envelope/outcome.rs +3 -3
  85. data/crates/kobako-transport/src/envelope/preamble.rs +24 -24
  86. data/crates/kobako-transport/src/envelope/reply.rs +34 -16
  87. data/crates/kobako-transport/src/envelope/request.rs +19 -9
  88. data/crates/kobako-transport/src/envelope/run.rs +1 -1
  89. data/crates/kobako-transport/src/envelope/unsupported.rs +2 -2
  90. data/crates/kobako-transport/src/envelope.rs +2 -2
  91. data/crates/kobako-wasmtime/CHANGELOG.md +265 -0
  92. data/crates/kobako-wasmtime/Cargo.toml +25 -19
  93. data/crates/kobako-wasmtime/README.md +13 -7
  94. data/crates/kobako-wasmtime/src/abi.rs +75 -49
  95. data/crates/kobako-wasmtime/src/ambient.rs +64 -6
  96. data/crates/kobako-wasmtime/src/cache.rs +188 -77
  97. data/crates/kobako-wasmtime/src/config.rs +77 -5
  98. data/crates/kobako-wasmtime/src/descriptors.rs +200 -0
  99. data/crates/kobako-wasmtime/src/driver.rs +70 -135
  100. data/crates/kobako-wasmtime/src/exports.rs +5 -16
  101. data/crates/kobako-wasmtime/src/guest.rs +38 -0
  102. data/crates/kobako-wasmtime/src/imports.rs +52 -0
  103. data/crates/kobako-wasmtime/src/instance_pre.rs +18 -46
  104. data/crates/kobako-wasmtime/src/invocation.rs +43 -74
  105. data/crates/kobako-wasmtime/src/lib.rs +5 -4
  106. data/crates/kobako-wasmtime/src/limiter.rs +6 -230
  107. data/crates/kobako-wasmtime/src/preview1/differential/mount.rs +632 -0
  108. data/crates/kobako-wasmtime/src/preview1/differential.rs +486 -0
  109. data/crates/kobako-wasmtime/src/preview1/errno.rs +135 -0
  110. data/crates/kobako-wasmtime/src/preview1/fd.rs +1420 -0
  111. data/crates/kobako-wasmtime/src/preview1/memory.rs +172 -0
  112. data/crates/kobako-wasmtime/src/preview1/mount_view/wrap.rs +211 -0
  113. data/crates/kobako-wasmtime/src/preview1/mount_view.rs +3766 -0
  114. data/crates/kobako-wasmtime/src/preview1/path.rs +302 -0
  115. data/crates/kobako-wasmtime/src/preview1/poll.rs +381 -0
  116. data/crates/kobako-wasmtime/src/preview1/sock.rs +13 -0
  117. data/crates/kobako-wasmtime/src/preview1/testsuite-expected.txt +6 -0
  118. data/crates/kobako-wasmtime/src/preview1/testsuite.rs +157 -0
  119. data/crates/kobako-wasmtime/src/preview1.rs +512 -0
  120. data/crates/kobako-wasmtime/src/trap.rs +45 -25
  121. data/crates/kobako-wasmtime/src/wasi.rs +333 -0
  122. data/crates/kobako-wasmtime/tests/integration/compile_posture.rs +43 -0
  123. data/crates/kobako-wasmtime/tests/integration/guest_file.rs +1085 -0
  124. data/crates/kobako-wasmtime/tests/integration/main.rs +29 -0
  125. data/crates/kobako-wasmtime/tests/{preamble_flags.rs → integration/preamble_flags.rs} +4 -3
  126. data/crates/kobako-wasmtime/tests/{snippet_compile.rs → integration/snippet_compile.rs} +5 -4
  127. data/crates/kobako-wasmtime/tests/{support → integration/support}/mod.rs +9 -13
  128. data/crates/kobako-wasmtime/tests/integration/unanswered_call.rs +86 -0
  129. data/crates/kobako-wasmtime/tests/{unsupported_call.rs → integration/unsupported_call.rs} +2 -1
  130. data/crates/kobako-wasmtime/witx/typenames.witx +750 -0
  131. data/crates/kobako-wasmtime/witx/wasi_snapshot_preview1.witx +441 -0
  132. data/data/kobako.wasm +0 -0
  133. data/ext/kobako/Cargo.toml +4 -2
  134. data/ext/kobako/src/runtime/bridge.rs +154 -81
  135. data/ext/kobako/src/runtime/errors.rs +1 -1
  136. data/ext/kobako/src/runtime/escape.rs +67 -0
  137. data/ext/kobako/src/runtime/filesystem/grants.rs +107 -0
  138. data/ext/kobako/src/runtime/filesystem/ruby.rs +708 -0
  139. data/ext/kobako/src/runtime/filesystem.rs +208 -0
  140. data/ext/kobako/src/runtime/gvl.rs +49 -8
  141. data/ext/kobako/src/runtime/pinned_value.rs +31 -0
  142. data/ext/kobako/src/runtime/snapshot.rs +127 -0
  143. data/ext/kobako/src/runtime.rs +125 -199
  144. data/lib/kobako/catalog/extensions.rb +5 -5
  145. data/lib/kobako/catalog/flags.rb +2 -14
  146. data/lib/kobako/catalog/grants.rb +30 -0
  147. data/lib/kobako/catalog/handles.rb +6 -6
  148. data/lib/kobako/catalog/mount.rb +19 -0
  149. data/lib/kobako/catalog/services.rb +13 -2
  150. data/lib/kobako/catalog/snippets.rb +11 -15
  151. data/lib/kobako/catalog.rb +11 -0
  152. data/lib/kobako/codec/decoder.rb +10 -9
  153. data/lib/kobako/codec/ext_types.rb +4 -6
  154. data/lib/kobako/codec/handle_walk.rb +4 -6
  155. data/lib/kobako/codec/nesting.rb +7 -7
  156. data/lib/kobako/codec/state.rb +19 -3
  157. data/lib/kobako/codec/utils.rb +1 -1
  158. data/lib/kobako/codec/value_budget.rb +86 -0
  159. data/lib/kobako/codec.rb +7 -0
  160. data/lib/kobako/context.rb +40 -13
  161. data/lib/kobako/errors.rb +15 -14
  162. data/lib/kobako/filesystem.rb +69 -0
  163. data/lib/kobako/payload/arguments.rb +2 -2
  164. data/lib/kobako/pool.rb +6 -6
  165. data/lib/kobako/sandbox.rb +55 -27
  166. data/lib/kobako/sandbox_options.rb +39 -13
  167. data/lib/kobako/snippet/{binary.rb → bytecode.rb} +2 -2
  168. data/lib/kobako/snippet/source.rb +1 -1
  169. data/lib/kobako/snippet.rb +1 -1
  170. data/lib/kobako/transport/block_failure.rb +35 -0
  171. data/lib/kobako/transport/dispatcher.rb +33 -29
  172. data/lib/kobako/transport/exposure.rb +7 -9
  173. data/lib/kobako/transport/reflection.rb +79 -23
  174. data/lib/kobako/transport/run.rb +5 -5
  175. data/lib/kobako/transport/yielder.rb +9 -31
  176. data/lib/kobako/transport.rb +1 -0
  177. data/lib/kobako/version.rb +1 -1
  178. data/lib/kobako.rb +35 -0
  179. data/release-please-config.json +1 -0
  180. data/sig/kobako/catalog/extensions.rbs +3 -3
  181. data/sig/kobako/catalog/flags.rbs +0 -4
  182. data/sig/kobako/catalog/grants.rbs +13 -0
  183. data/sig/kobako/catalog/handles.rbs +3 -3
  184. data/sig/kobako/catalog/mount.rbs +24 -0
  185. data/sig/kobako/catalog/services.rbs +5 -1
  186. data/sig/kobako/catalog/snippets.rbs +4 -4
  187. data/sig/kobako/catalog.rbs +1 -0
  188. data/sig/kobako/codec/decoder.rbs +1 -1
  189. data/sig/kobako/codec/handle_walk.rbs +1 -1
  190. data/sig/kobako/codec/nesting.rbs +2 -2
  191. data/sig/kobako/codec/state.rbs +7 -1
  192. data/sig/kobako/codec/utils.rbs +1 -1
  193. data/sig/kobako/codec/value_budget.rbs +23 -0
  194. data/sig/kobako/codec.rbs +3 -2
  195. data/sig/kobako/context.rbs +14 -6
  196. data/sig/kobako/errors.rbs +4 -4
  197. data/sig/kobako/filesystem.rbs +93 -0
  198. data/sig/kobako/guest_yielder.rbs +9 -0
  199. data/sig/kobako/payload/arguments.rbs +1 -1
  200. data/sig/kobako/pool.rbs +3 -3
  201. data/sig/kobako/runtime.rbs +21 -49
  202. data/sig/kobako/sandbox.rbs +9 -5
  203. data/sig/kobako/sandbox_options.rbs +25 -3
  204. data/sig/kobako/snapshot.rbs +20 -0
  205. data/sig/kobako/snippet/bytecode.rbs +12 -0
  206. data/sig/kobako/transport/block_failure.rbs +13 -0
  207. data/sig/kobako/transport/dispatcher.rbs +4 -4
  208. data/sig/kobako/transport/exposure.rbs +2 -2
  209. data/sig/kobako/transport/reflection.rbs +14 -1
  210. data/sig/kobako/transport/run.rbs +3 -3
  211. data/sig/kobako/transport/yielder.rbs +3 -7
  212. data/sig/kobako/transport.rbs +3 -3
  213. metadata +116 -15
  214. data/crates/kobako-wasmtime/src/capture.rs +0 -96
  215. data/crates/kobako-wasmtime/src/dispatch.rs +0 -107
  216. data/crates/kobako-wasmtime/src/guest_mem.rs +0 -265
  217. data/crates/kobako-wasmtime/src/io.rs +0 -286
  218. 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
+ }