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