1#![recursion_limit = "512"]
6
7use starnix_core::task::{CurrentTask, Kernel};
8use starnix_core::vfs::{
9 CacheMode, DirectoryEntryType, DirentSink, FileHandle, FileObject, FileOps, FileSystem,
10 FileSystemHandle, FileSystemOps, FsNode, FsNodeHandle, FsNodeOps, FsStr, FsString, MountInfo,
11 SeekTarget, ValueOrSize, WhatToMount, XattrOp, fileops_impl_directory, fileops_impl_noop_sync,
12 fs_node_impl_dir_readonly, unbounded_seek,
13};
14
15use starnix_uapi::errors::Errno;
16use starnix_uapi::mount_flags::{FileSystemFlags, MountpointFlags};
17use starnix_uapi::open_flags::OpenFlags;
18use starnix_uapi::{errno, ino_t, off_t, statfs};
19use std::collections::BTreeMap;
20use std::sync::Arc;
21use std::sync::atomic::Ordering;
22
23struct LayeredMountAction {
24 path: FsString,
25 fs: FileSystemHandle,
26}
27
28pub type LayeredFsMounts = Box<dyn FnOnce(&CurrentTask) -> Result<(), Errno>>;
34
35pub struct LayeredFsBuilder {
42 fs: FileSystemHandle,
43 subdirs: BTreeMap<FsString, LayeredFsBuilder>,
44}
45
46fn split_path(path: &FsStr) -> Vec<&FsStr> {
47 path.split(|c| *c == b'/').map(<&FsStr>::from).collect()
48}
49
50impl LayeredFsBuilder {
51 pub fn new(root_fs: FileSystemHandle) -> Self {
53 Self { fs: root_fs, subdirs: Default::default() }
54 }
55
56 pub fn add(&mut self, path: &str, fs: FileSystemHandle) {
63 let path = FsStr::new(path);
64 assert_eq!(path[0], b'/');
65 let parts = split_path(&path[1..]);
66 assert!(!parts.is_empty());
67 let final_part = parts.len() - 1;
68
69 let mut parent = self;
70 for i in 0..final_part {
71 parent = parent.subdirs.get_mut(parts[i]).unwrap();
72 }
73
74 parent.subdirs.insert(parts[parts.len() - 1].into(), Self::new(fs));
75 }
76
77 pub fn build(self, kernel: &Kernel) -> (FileSystemHandle, LayeredFsMounts) {
84 let (fs, actions) = self.build_internal(kernel, Default::default());
85 let cb = Box::new(move |current_task: &CurrentTask| {
86 for action in actions {
87 let mount_point =
88 current_task.lookup_path_from_root(action.path.as_ref()).map_err(|e| {
89 Errno::with_context(
90 e.code,
91 format!("lookup path from root: {}", action.path),
92 )
93 })?;
94 mount_point.mount(WhatToMount::Fs(action.fs), MountpointFlags::empty()).map_err(
95 |e| {
96 Errno::with_context(e.code, format!("mount layered fs at: {}", action.path))
97 },
98 )?;
99 }
100 Ok(())
101 });
102 (fs, cb)
103 }
104
105 fn build_internal(
106 self,
107 kernel: &Kernel,
108 prefix: &FsStr,
109 ) -> (FileSystemHandle, Vec<LayeredMountAction>) {
110 if self.subdirs.is_empty() {
111 return (self.fs, Vec::new());
112 }
113
114 let names =
115 self.subdirs.iter().map(|(name, entry)| (name.clone(), entry.fs.clone())).collect();
116 let fs = LayeredFs::new_fs(kernel, self.fs, names);
117
118 let mut mount_actions = Vec::new();
119 for (subpath, builder) in self.subdirs {
120 let path = FsString::from(format!("{}/{}", prefix, subpath));
121 let (fs, subdir_actions) = builder.build_internal(kernel, path.as_ref());
122 mount_actions.push(LayeredMountAction { path, fs });
123 mount_actions.extend(subdir_actions.into_iter());
124 }
125
126 (fs, mount_actions)
127 }
128}
129
130struct LayeredFs {
133 base_fs: FileSystemHandle,
134 mappings: BTreeMap<FsString, FileSystemHandle>,
135}
136
137impl LayeredFs {
138 fn new_fs(
144 kernel: &Kernel,
145 base_fs: FileSystemHandle,
146 mappings: BTreeMap<FsString, FileSystemHandle>,
147 ) -> FileSystemHandle {
148 let options = base_fs.options.clone();
149 let layered_fs = Arc::new(LayeredFs { base_fs, mappings });
150 let fs = FileSystem::new(
151 kernel,
152 CacheMode::Uncached,
153 LayeredFileSystemOps { fs: layered_fs.clone() },
154 options,
155 )
156 .expect("layeredfs constructed with valid options");
157 let root_ino = fs.allocate_ino();
158 fs.create_root(root_ino, LayeredNodeOps { fs: layered_fs });
159 fs
160 }
161}
162
163struct LayeredFileSystemOps {
164 fs: Arc<LayeredFs>,
165}
166
167impl FileSystemOps for LayeredFileSystemOps {
168 fn statfs(&self, _fs: &FileSystem, current_task: &CurrentTask) -> Result<statfs, Errno> {
169 self.fs.base_fs.statfs(current_task)
170 }
171 fn name(&self) -> &'static FsStr {
172 self.fs.base_fs.name()
173 }
174 fn update_flags(
175 &self,
176 fs: &FileSystem,
177 current_task: &CurrentTask,
178 new_flags: FileSystemFlags,
179 ) -> Result<(), Errno> {
180 self.fs.base_fs.update_flags(current_task, new_flags)?;
181 let flags = self.fs.base_fs.options.flags.load(Ordering::Relaxed);
182 fs.options.flags.store(flags, Ordering::Relaxed);
183 Ok(())
184 }
185}
186
187struct LayeredNodeOps {
188 fs: Arc<LayeredFs>,
189}
190
191impl FsNodeOps for LayeredNodeOps {
192 fs_node_impl_dir_readonly!();
193
194 fn create_file_ops(
195 &self,
196 _node: &FsNode,
197 current_task: &CurrentTask,
198 flags: OpenFlags,
199 ) -> Result<Box<dyn FileOps>, Errno> {
200 Ok(Box::new(LayeredFileOps {
201 fs: self.fs.clone(),
202 root_file: self.fs.base_fs.root().open_anonymous(current_task, flags)?,
203 }))
204 }
205
206 fn lookup(
207 &self,
208 _node: &FsNode,
209 current_task: &CurrentTask,
210 name: &FsStr,
211 ) -> Result<FsNodeHandle, Errno> {
212 if let Some(fs) = self.fs.mappings.get(name) {
213 Ok(fs.root().node.clone())
214 } else {
215 self.fs.base_fs.root().node.lookup(current_task, &MountInfo::detached(), name)
216 }
217 }
218
219 fn get_xattr(
220 &self,
221 _node: &FsNode,
222 current_task: &CurrentTask,
223 name: &FsStr,
224 max_size: usize,
225 ) -> Result<ValueOrSize<FsString>, Errno> {
226 self.fs.base_fs.root().node.ops().get_xattr(
227 &*self.fs.base_fs.root().node,
228 current_task,
229 name,
230 max_size,
231 )
232 }
233
234 fn set_xattr(
236 &self,
237 _node: &FsNode,
238 current_task: &CurrentTask,
239 name: &FsStr,
240 value: &FsStr,
241 op: XattrOp,
242 ) -> Result<(), Errno> {
243 self.fs.base_fs.root().node.set_xattr(current_task, &MountInfo::detached(), name, value, op)
244 }
245
246 fn remove_xattr(
247 &self,
248 _node: &FsNode,
249 current_task: &CurrentTask,
250 name: &FsStr,
251 ) -> Result<(), Errno> {
252 self.fs.base_fs.root().node.remove_xattr(current_task, &MountInfo::detached(), name)
253 }
254
255 fn list_xattrs(
256 &self,
257 _node: &FsNode,
258 current_task: &CurrentTask,
259 max_size: usize,
260 ) -> Result<ValueOrSize<Vec<FsString>>, Errno> {
261 self.fs.base_fs.root().node.list_xattrs(current_task, max_size)
262 }
263}
264
265struct LayeredFileOps {
266 fs: Arc<LayeredFs>,
267 root_file: FileHandle,
268}
269
270impl FileOps for LayeredFileOps {
271 fileops_impl_directory!();
272 fileops_impl_noop_sync!();
273
274 fn seek(
275 &self,
276 _file: &FileObject,
277 current_task: &CurrentTask,
278 current_offset: off_t,
279 target: SeekTarget,
280 ) -> Result<off_t, Errno> {
281 let mut new_offset = unbounded_seek(current_offset, target)?;
282 if new_offset >= self.fs.mappings.len() as off_t {
283 new_offset = self
284 .root_file
285 .seek(current_task, SeekTarget::Set(new_offset - self.fs.mappings.len() as off_t))?
286 .checked_add(self.fs.mappings.len() as off_t)
287 .ok_or_else(|| errno!(EINVAL))?;
288 }
289 Ok(new_offset)
290 }
291
292 fn readdir(
293 &self,
294 _file: &FileObject,
295 current_task: &CurrentTask,
296 sink: &mut dyn DirentSink,
297 ) -> Result<(), Errno> {
298 for (key, fs) in self.fs.mappings.iter().skip(sink.offset() as usize) {
299 sink.add(fs.root().node.ino, sink.offset() + 1, DirectoryEntryType::DIR, key.as_ref())?;
300 }
301
302 struct DirentSinkWrapper<'a> {
303 sink: &'a mut dyn DirentSink,
304 mappings: &'a BTreeMap<FsString, FileSystemHandle>,
305 offset: &'a mut off_t,
306 }
307
308 impl<'a> DirentSink for DirentSinkWrapper<'a> {
309 fn add(
310 &mut self,
311 inode_num: ino_t,
312 offset: off_t,
313 entry_type: DirectoryEntryType,
314 name: &FsStr,
315 ) -> Result<(), Errno> {
316 if !self.mappings.contains_key(name) {
317 self.sink.add(
318 inode_num,
319 offset + (self.mappings.len() as off_t),
320 entry_type,
321 name,
322 )?;
323 }
324 *self.offset = offset;
325 Ok(())
326 }
327 fn offset(&self) -> off_t {
328 *self.offset
329 }
330 }
331
332 let _token = starnix_sync::allow_subclass();
338 let mut root_file_offset = self.root_file.offset.copy();
339 let mut wrapper =
340 DirentSinkWrapper { sink, mappings: &self.fs.mappings, offset: &mut *root_file_offset };
341
342 self.root_file.readdir(current_task, &mut wrapper)?;
343 root_file_offset.update();
344 Ok(())
345 }
346}
347
348#[cfg(test)]
349mod test {
350 use super::*;
351 use starnix_core::fs::tmpfs::TmpFs;
352 use starnix_core::testing::*;
353
354 fn get_root_entry_names(current_task: &CurrentTask, fs: &FileSystem) -> Vec<Vec<u8>> {
355 struct DirentNameCapturer {
356 pub names: Vec<Vec<u8>>,
357 offset: off_t,
358 }
359 impl DirentSink for DirentNameCapturer {
360 fn add(
361 &mut self,
362 _inode_num: ino_t,
363 offset: off_t,
364 _entry_type: DirectoryEntryType,
365 name: &FsStr,
366 ) -> Result<(), Errno> {
367 self.names.push(name.to_vec());
368 self.offset = offset;
369 Ok(())
370 }
371 fn offset(&self) -> off_t {
372 self.offset
373 }
374 }
375 let mut sink = DirentNameCapturer { names: vec![], offset: 0 };
376 fs.root()
377 .open_anonymous(current_task, OpenFlags::RDONLY)
378 .expect("open")
379 .readdir(current_task, &mut sink)
380 .expect("readdir");
381 std::mem::take(&mut sink.names)
382 }
383
384 #[::fuchsia::test]
385 async fn test_remove_duplicates() {
386 spawn_kernel_and_run(async move |current_task| {
387 let kernel = current_task.kernel();
388 let base = TmpFs::new_fs(kernel);
389 base.root().create_dir_for_testing(current_task, "d1".into()).expect("create_dir");
390 base.root().create_dir_for_testing(current_task, "d2".into()).expect("create_dir");
391 let base_entries = get_root_entry_names(current_task, &base);
392 assert_eq!(base_entries.len(), 4);
393 assert!(base_entries.contains(&b".".to_vec()));
394 assert!(base_entries.contains(&b"..".to_vec()));
395 assert!(base_entries.contains(&b"d1".to_vec()));
396 assert!(base_entries.contains(&b"d2".to_vec()));
397
398 let tmpfs1 = TmpFs::new_fs(kernel);
399 let tmpfs2 = TmpFs::new_fs(kernel);
400 let layered_fs = LayeredFs::new_fs(
401 kernel,
402 base,
403 BTreeMap::from([("d1".into(), tmpfs1), ("d3".into(), tmpfs2)]),
404 );
405 let layered_fs_entries = get_root_entry_names(current_task, &layered_fs);
406 assert_eq!(layered_fs_entries.len(), 5);
407 assert!(layered_fs_entries.contains(&b".".to_vec()));
408 assert!(layered_fs_entries.contains(&b"..".to_vec()));
409 assert!(layered_fs_entries.contains(&b"d1".to_vec()));
410 assert!(layered_fs_entries.contains(&b"d2".to_vec()));
411 assert!(layered_fs_entries.contains(&b"d3".to_vec()));
412 })
413 .await;
414 }
415}