1use alloc::{
2 boxed::Box,
3 collections::{btree_map, BTreeMap},
4 sync::{Arc, Weak},
5};
6use tracing::info;
7
8use crate::{
9 io::NoDebug,
10 sync::{once::OnceLock, spin::rwlock::RwLock},
11};
12
13use super::{
14 path::{Component, Path, PathBuf},
15 EmptyFileSystem, FileSystem, FileSystemError,
16};
17
18static FILESYSTEM_MAPPING: OnceLock<FileSystemMapping> = OnceLock::new();
19
20pub fn get_mapping(path: &Path) -> Result<(PathBuf, &Path, Arc<MappingNode>), FileSystemError> {
34 FILESYSTEM_MAPPING.get().get_mapping(path)
35}
36
37pub fn mount(arg: &str, filesystem: Arc<dyn FileSystem>) -> Result<(), MappingError> {
62 let mapping = FILESYSTEM_MAPPING.get_or_init(FileSystemMapping::empty_root);
63
64 if arg == "/" {
65 mapping.set_root(filesystem)
66 } else {
67 mapping.mount(arg, filesystem)
68 }
69}
70
71pub fn unmount_all() {
75 FILESYSTEM_MAPPING.get().root.unmount_all(Path::new("/"));
77}
78
79pub fn on_all_matching_mappings(
102 inp_path: &Path,
103 handler: impl FnMut(&Path, Arc<dyn FileSystem>),
104) -> Result<(), FileSystemError> {
105 FILESYSTEM_MAPPING
106 .get()
107 .on_all_matching_mappings(inp_path, handler)
108}
109
110#[derive(Debug)]
111pub enum MappingError {
112 MustBeAbsolute,
113 InvalidPath,
114 PartOfParentNotMounted,
115 AlreadyMounted,
116}
117
118impl From<MappingError> for FileSystemError {
119 fn from(value: MappingError) -> Self {
120 Self::MappingError(value)
121 }
122}
123
124#[derive(Debug)]
125#[allow(dead_code)]
126pub struct MappingNode {
127 filesystem: NoDebug<RwLock<Arc<dyn FileSystem>>>,
128 parent: Weak<MappingNode>,
129 children: RwLock<BTreeMap<Box<str>, Arc<MappingNode>>>,
130}
131
132impl MappingNode {
133 fn check_and_traverse(
134 &self,
135 target: &Path,
136 this_component: Component<'_>,
137 handler: &mut dyn FnMut(&Path, Arc<dyn FileSystem>),
138 ) -> Result<(), FileSystemError> {
139 let mut components = target.components();
140
141 if components.next() != Some(this_component) {
143 return Ok(());
144 }
145
146 if components.peek().is_none() {
147 for (name, node) in self.children.read().iter() {
148 node.traverse(name.into(), handler);
149 }
150 } else {
151 for (name, node) in self.children.read().iter() {
152 node.check_and_traverse(
153 components.as_path(),
154 Component::Normal(name.as_ref()),
155 handler,
156 )?;
157 }
158 }
159
160 Ok(())
161 }
162
163 fn traverse(&self, current_path: PathBuf, handler: &mut dyn FnMut(&Path, Arc<dyn FileSystem>)) {
164 handler(¤t_path, self.filesystem());
165
166 for (name, node) in self.children.read().iter() {
167 node.traverse(current_path.join(name.as_ref()), handler);
168 }
169 }
170
171 pub fn try_find_child(&self, component_name: &str) -> Option<Arc<MappingNode>> {
172 self.children.read().get(component_name).cloned()
173 }
174
175 pub fn filesystem(&self) -> Arc<dyn FileSystem> {
176 self.filesystem.0.read().clone()
177 }
178
179 pub fn parent(&self) -> Option<Arc<MappingNode>> {
180 self.parent.upgrade()
181 }
182
183 fn unmount_all(&self, this_name: &Path) {
184 let mut children = self.children.write();
185 while let Some((name, node)) = children.pop_first() {
186 node.unmount_all(&this_name.join(name.as_ref()));
187 }
188
189 info!("Unmounting {}", this_name.display());
190 let fs = core::mem::replace(&mut *self.filesystem.0.write(), Arc::new(EmptyFileSystem));
191 assert_eq!(
192 Arc::strong_count(&fs),
193 1, "Filesystem still in use"
195 );
196 fs.unmount();
197 }
198}
199
200#[derive(Debug)]
201struct FileSystemMapping {
202 root: Arc<MappingNode>,
203}
204
205impl FileSystemMapping {
206 fn empty_root() -> Self {
207 Self {
208 root: Arc::new(MappingNode {
209 filesystem: NoDebug(RwLock::new(Arc::new(EmptyFileSystem))),
210 parent: Weak::new(),
211 children: RwLock::new(BTreeMap::new()),
212 }),
213 }
214 }
215
216 fn set_root(&self, filesystem: Arc<dyn FileSystem>) -> Result<(), MappingError> {
217 if let Err(FileSystemError::FileNotFound) = self.root.filesystem().open_root() {
219 *self.root.filesystem.0.write() = filesystem;
222 Ok(())
223 } else {
224 Err(MappingError::AlreadyMounted)
225 }
226 }
227
228 fn get_mapping<'p>(
229 &self,
230 path: &'p Path,
231 ) -> Result<(PathBuf, &'p Path, Arc<MappingNode>), FileSystemError> {
232 let mut current = self.root.clone();
233 let mut mapping_path = PathBuf::from("/");
235
236 let mut components = path.components();
237
238 if components.next() != Some(Component::RootDir) {
239 return Err(FileSystemError::MustBeAbsolute);
240 }
241
242 while let Some(component) = components.peek() {
243 match component {
244 Component::Normal(name) => {
245 if let Some(child) = current.try_find_child(name) {
246 mapping_path.push(name);
247 current = child;
248 } else {
249 break;
250 }
251 }
252 _ => {
253 break;
254 }
255 }
256
257 components.next();
259 }
260
261 Ok((mapping_path, components.as_path(), current))
262 }
263
264 fn on_all_matching_mappings(
265 &self,
266 path: &Path,
267 mut handler: impl FnMut(&Path, Arc<dyn FileSystem>),
268 ) -> Result<(), FileSystemError> {
269 self.root
270 .check_and_traverse(path, Component::RootDir, &mut handler)
271 }
272
273 fn mount<P: AsRef<Path>>(
274 &self,
275 arg: P,
276 filesystem: Arc<dyn FileSystem>,
277 ) -> Result<(), MappingError> {
278 let mut components: super::path::Components = arg.as_ref().components();
279
280 if components.next() != Some(Component::RootDir) {
281 return Err(MappingError::MustBeAbsolute);
282 }
283
284 {
285 if components
287 .clone()
288 .any(|c| !matches!(c, Component::Normal(_)))
289 {
290 return Err(MappingError::InvalidPath);
291 }
292 }
293
294 let mut current_element = self.root.clone();
295
296 let size = components.clone().count();
297
298 for (i, component) in components.enumerate() {
299 let Component::Normal(component_path) = component else {
300 unreachable!("Already checked all the components")
301 };
302 let is_last = i == size - 1;
303
304 match current_element
305 .clone()
306 .children
307 .write()
308 .entry(component_path.into())
309 {
310 btree_map::Entry::Vacant(entry) => {
311 if is_last {
312 entry.insert(Arc::new(MappingNode {
313 filesystem: NoDebug(RwLock::new(filesystem)),
314 parent: Arc::downgrade(¤t_element),
315 children: RwLock::new(BTreeMap::new()),
316 }));
317 return Ok(());
318 } else {
319 return Err(MappingError::PartOfParentNotMounted);
320 }
321 }
322 btree_map::Entry::Occupied(entry) => {
323 if is_last {
324 return Err(MappingError::AlreadyMounted);
325 } else {
326 current_element = entry.get().clone();
327 }
328 }
329 }
330 }
331
332 unreachable!("For some reason, it wasn't mounted")
333 }
334}