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