virtual_fs/mem_fs/
filesystem.rs

1//! This module contains the [`FileSystem`] type itself.
2
3use self::offloaded_file::OffloadBackingStore;
4
5use super::*;
6use crate::{DirEntry, FileType, FsError, Metadata, OpenOptions, ReadDir, Result};
7use futures::future::{BoxFuture, Either};
8use slab::Slab;
9use std::collections::VecDeque;
10use std::convert::identity;
11use std::ffi::OsString;
12use std::fmt;
13use std::path::{Component, Path, PathBuf};
14use std::sync::{Arc, RwLock};
15
16/// The in-memory file system!
17///
18/// This `FileSystem` type can be cloned, it's a light copy of the
19/// `FileSystemInner` (which is behind a `Arc` + `RwLock`).
20#[derive(Clone, Default)]
21pub struct FileSystem {
22    pub(super) inner: Arc<RwLock<FileSystemInner>>,
23}
24
25impl FileSystem {
26    pub fn set_memory_limiter(&self, limiter: crate::limiter::DynFsMemoryLimiter) {
27        self.inner.write().unwrap().limiter = Some(limiter);
28    }
29
30    pub fn new_open_options_ext(&self) -> &FileSystem {
31        self
32    }
33
34    /// Uses a mmap'ed file as a cache for file data thus removing the
35    /// need to copy the data into memory.
36    ///
37    /// This is especially important for journals as it means that the
38    /// data stored within the journals does not need to be copied
39    /// into memory, for very large journals this would otherwise be
40    /// a problem.
41    pub fn with_backing_offload(self, buffer: OffloadBackingStore) -> Result<Self> {
42        let mut lock = self.inner.write().map_err(|_| FsError::Lock)?;
43        lock.backing_offload.replace(buffer);
44        drop(lock);
45        Ok(self)
46    }
47
48    /// Canonicalize a path without validating that it actually exists.
49    pub fn canonicalize_unchecked(&self, path: &Path) -> Result<PathBuf> {
50        let lock = self.inner.read().map_err(|_| FsError::Lock)?;
51        lock.canonicalize_without_inode(path)
52    }
53
54    /// Merge all items from a given source path (directory) of a different file
55    /// system into this file system.
56    ///
57    /// Individual files and directories of the given path are mounted.
58    ///
59    /// This function is not recursive, only the items in the source_path are
60    /// mounted.
61    ///
62    /// See [`Self::union`] for mounting all inodes recursively.
63    pub fn mount_directory_entries(
64        &self,
65        target_path: &Path,
66        other: &Arc<dyn crate::FileSystem + Send + Sync>,
67        mut source_path: &Path,
68    ) -> Result<()> {
69        let fs_lock = self.inner.read().map_err(|_| FsError::Lock)?;
70
71        if cfg!(windows) {
72            // We need to take some care here because
73            // canonicalize_without_inode() doesn't accept Windows paths that
74            // start with a prefix (drive letters, UNC paths, etc.). If we
75            // somehow get one of those paths, we'll automatically trim it away.
76            let mut components = source_path.components();
77
78            if let Some(Component::Prefix(_)) = components.next() {
79                source_path = components.as_path();
80            }
81        }
82
83        let (_target_path, root_inode) = match fs_lock.canonicalize(target_path) {
84            Ok((p, InodeResolution::Found(inode))) => (p, inode),
85            Ok((_p, InodeResolution::Redirect(..))) => {
86                return Err(FsError::AlreadyExists);
87            }
88            Err(_) => {
89                // Root directory does not exist, so we can just mount.
90                return self.mount(target_path.to_path_buf(), other, source_path.to_path_buf());
91            }
92        };
93
94        let _root_node = match fs_lock.storage.get(root_inode).unwrap() {
95            Node::Directory(dir) => dir,
96            _ => {
97                return Err(FsError::AlreadyExists);
98            }
99        };
100
101        let source_path = fs_lock.canonicalize_without_inode(source_path)?;
102
103        std::mem::drop(fs_lock);
104
105        let source = other.read_dir(&source_path)?;
106        for entry in source.data {
107            let meta = entry.metadata?;
108
109            let entry_target_path = target_path.join(entry.path.file_name().unwrap());
110
111            if meta.is_file() {
112                self.insert_arc_file_at(entry_target_path, other.clone(), entry.path)?;
113            } else if meta.is_dir() {
114                self.insert_arc_directory_at(entry_target_path, other.clone(), entry.path)?;
115            }
116        }
117
118        Ok(())
119    }
120
121    pub fn union(&self, other: &Arc<dyn crate::FileSystem + Send + Sync>) {
122        // Iterate all the directories and files in the other filesystem
123        // and create references back to them in this filesystem
124        let mut remaining = VecDeque::new();
125        remaining.push_back(PathBuf::from("/"));
126        while let Some(next) = remaining.pop_back() {
127            if next
128                .file_name()
129                .map(|n| n.to_string_lossy().starts_with(".wh."))
130                .unwrap_or(false)
131            {
132                let rm = next.to_string_lossy();
133                let rm = &rm[".wh.".len()..];
134                let rm = PathBuf::from(rm);
135                let _ = crate::FileSystem::remove_dir(self, rm.as_path());
136                let _ = crate::FileSystem::remove_file(self, rm.as_path());
137                continue;
138            }
139            let _ = crate::FileSystem::create_dir(self, next.as_path());
140
141            let dir = match other.read_dir(next.as_path()) {
142                Ok(dir) => dir,
143                Err(_) => {
144                    // TODO: propagate errors (except NotFound)
145                    continue;
146                }
147            };
148
149            for sub_dir_res in dir {
150                let sub_dir = match sub_dir_res {
151                    Ok(sub_dir) => sub_dir,
152                    Err(_) => {
153                        // TODO: propagate errors (except NotFound)
154                        continue;
155                    }
156                };
157
158                match sub_dir.file_type() {
159                    Ok(t) if t.is_dir() => {
160                        remaining.push_back(sub_dir.path());
161                    }
162                    Ok(t) if t.is_file() => {
163                        if sub_dir.file_name().to_string_lossy().starts_with(".wh.") {
164                            let rm = next.to_string_lossy();
165                            let rm = &rm[".wh.".len()..];
166                            let rm = PathBuf::from(rm);
167                            let _ = crate::FileSystem::remove_dir(self, rm.as_path());
168                            let _ = crate::FileSystem::remove_file(self, rm.as_path());
169                            continue;
170                        }
171                        let _ = self
172                            .new_open_options_ext()
173                            .insert_arc_file(sub_dir.path(), other.clone());
174                    }
175                    _ => {}
176                }
177            }
178        }
179    }
180
181    pub fn mount(
182        &self,
183        target_path: PathBuf,
184        other: &Arc<dyn crate::FileSystem + Send + Sync>,
185        source_path: PathBuf,
186    ) -> Result<()> {
187        if crate::FileSystem::read_dir(self, target_path.as_path()).is_ok() {
188            return Err(FsError::AlreadyExists);
189        }
190
191        let (inode_of_parent, name_of_directory) = {
192            // Read lock.
193            let guard = self.inner.read().map_err(|_| FsError::Lock)?;
194
195            // Canonicalize the path without checking the path exists,
196            // because it's about to be created.
197            let path = guard.canonicalize_without_inode(target_path.as_path())?;
198
199            // Check the path has a parent.
200            let parent_of_path = path.parent().ok_or(FsError::BaseNotDirectory)?;
201
202            // Check the directory name.
203            let name_of_directory = path
204                .file_name()
205                .ok_or(FsError::InvalidInput)?
206                .to_os_string();
207
208            // Find the parent inode.
209            let inode_of_parent = match guard.inode_of_parent(parent_of_path)? {
210                InodeResolution::Found(a) => a,
211                InodeResolution::Redirect(..) => {
212                    return Err(FsError::AlreadyExists);
213                }
214            };
215
216            (inode_of_parent, name_of_directory)
217        };
218
219        {
220            // Write lock.
221            let mut fs = self.inner.write().map_err(|_| FsError::Lock)?;
222
223            // Creating the directory in the storage.
224            let inode_of_directory = fs.storage.vacant_entry().key();
225            let real_inode_of_directory = fs.storage.insert(Node::ArcDirectory(ArcDirectoryNode {
226                inode: inode_of_directory,
227                name: name_of_directory,
228                fs: other.clone(),
229                path: source_path,
230                metadata: {
231                    let time = time();
232
233                    Metadata {
234                        ft: FileType {
235                            dir: true,
236                            ..Default::default()
237                        },
238                        accessed: time,
239                        created: time,
240                        modified: time,
241                        len: 0,
242                    }
243                },
244            }));
245
246            assert_eq!(
247                inode_of_directory, real_inode_of_directory,
248                "new directory inode should have been correctly calculated",
249            );
250
251            // Adding the new directory to its parent.
252            fs.add_child_to_node(inode_of_parent, inode_of_directory)?;
253        }
254
255        Ok(())
256    }
257
258    pub fn create_symlink(&self, source: &Path, target: &Path) -> Result<()> {
259        let (inode_of_parent, name_of_symlink) = {
260            let guard = self.inner.read().map_err(|_| FsError::Lock)?;
261            let path = guard.canonicalize_without_inode(target)?;
262            let parent_of_path = path.parent().ok_or(FsError::BaseNotDirectory)?;
263            let name_of_symlink = path
264                .file_name()
265                .ok_or(FsError::InvalidInput)?
266                .to_os_string();
267            let inode_of_parent = match guard.inode_of_parent(parent_of_path)? {
268                InodeResolution::Found(a) => a,
269                InodeResolution::Redirect(fs, mut redirected_path) => {
270                    redirected_path.push(&name_of_symlink);
271                    drop(guard);
272                    let fs_ref: &dyn crate::FileSystem = fs.as_ref();
273                    if let Some(mem_fs) = fs_ref.downcast_ref::<Self>() {
274                        return mem_fs.create_symlink(source, redirected_path.as_path());
275                    }
276                    return Err(FsError::Unsupported);
277                }
278            };
279            (inode_of_parent, name_of_symlink)
280        };
281
282        let mut fs = self.inner.write().map_err(|_| FsError::Lock)?;
283        if fs.canonicalize(target).is_ok() {
284            return Err(FsError::AlreadyExists);
285        }
286
287        let inode_of_symlink = fs.storage.vacant_entry().key();
288        let real_inode_of_symlink = fs.storage.insert(Node::Symlink(SymlinkNode {
289            inode: inode_of_symlink,
290            name: name_of_symlink,
291            target: source.to_path_buf(),
292            metadata: {
293                let time = time();
294                Metadata {
295                    ft: FileType {
296                        symlink: true,
297                        ..Default::default()
298                    },
299                    accessed: time,
300                    created: time,
301                    modified: time,
302                    len: source.as_os_str().len() as u64,
303                }
304            },
305        }));
306
307        assert_eq!(
308            inode_of_symlink, real_inode_of_symlink,
309            "new symlink inode should have been correctly calculated",
310        );
311
312        fs.add_child_to_node(inode_of_parent, inode_of_symlink)?;
313        Ok(())
314    }
315}
316
317impl crate::FileSystem for FileSystem {
318    fn readlink(&self, path: &Path) -> Result<PathBuf> {
319        // Read lock.
320        let guard = self.inner.read().map_err(|_| FsError::Lock)?;
321
322        // Canonicalize the path.
323        let (_, inode_of_directory) = guard.canonicalize(path)?;
324        match inode_of_directory {
325            InodeResolution::Found(inode) => match guard.storage.get(inode) {
326                Some(Node::Symlink(SymlinkNode { target, .. })) => Ok(target.clone()),
327                _ => Err(FsError::InvalidInput),
328            },
329            InodeResolution::Redirect(fs, path) => fs.readlink(path.as_path()),
330        }
331    }
332
333    fn read_dir(&self, path: &Path) -> Result<ReadDir> {
334        // Read lock.
335        let guard = self.inner.read().map_err(|_| FsError::Lock)?;
336
337        fn rebase_entries(entries: &mut ReadDir, base: &Path) {
338            for entry in &mut entries.data {
339                let name = entry.file_name();
340                entry.path = base.join(name);
341            }
342        }
343
344        // Canonicalize the path.
345        let (guest_path, inode_of_directory) = guard.canonicalize(path)?;
346        let inode_of_directory = match inode_of_directory {
347            InodeResolution::Found(a) => a,
348            InodeResolution::Redirect(fs, redirect_path) => {
349                let mut entries = fs.read_dir(redirect_path.as_path())?;
350                rebase_entries(&mut entries, &guest_path);
351                return Ok(entries);
352            }
353        };
354
355        // Check it's a directory and fetch the immediate children as `DirEntry`.
356        let inode = guard.storage.get(inode_of_directory);
357        let children = match inode {
358            Some(Node::Directory(DirectoryNode { children, .. })) => children
359                .iter()
360                .filter_map(|inode| guard.storage.get(*inode))
361                .map(|node| DirEntry {
362                    path: {
363                        let mut entry_path = path.to_path_buf();
364                        entry_path.push(node.name());
365
366                        entry_path
367                    },
368                    metadata: Ok(node.metadata().clone()),
369                })
370                .collect(),
371
372            Some(Node::ArcDirectory(ArcDirectoryNode {
373                fs, path: fs_path, ..
374            })) => {
375                let mut entries = fs.read_dir(fs_path.as_path())?;
376                rebase_entries(&mut entries, &guest_path);
377                return Ok(entries);
378            }
379
380            _ => return Err(FsError::InvalidInput),
381        };
382
383        Ok(ReadDir::new(children))
384    }
385
386    fn create_dir(&self, path: &Path) -> Result<()> {
387        if self.read_dir(path).is_ok() {
388            return Err(FsError::AlreadyExists);
389        }
390
391        let (inode_of_parent, name_of_directory) = {
392            // Read lock.
393            let guard = self.inner.read().map_err(|_| FsError::Lock)?;
394
395            // Canonicalize the path without checking the path exists,
396            // because it's about to be created.
397            let path = guard.canonicalize_without_inode(path)?;
398
399            // Check the path has a parent.
400            let parent_of_path = path.parent().ok_or(FsError::BaseNotDirectory)?;
401
402            // Check the directory name.
403            let name_of_directory = path
404                .file_name()
405                .ok_or(FsError::InvalidInput)?
406                .to_os_string();
407
408            // Find the parent inode.
409            let inode_of_parent = match guard.inode_of_parent(parent_of_path)? {
410                InodeResolution::Found(a) => a,
411                InodeResolution::Redirect(fs, mut path) => {
412                    drop(guard);
413                    path.push(name_of_directory);
414                    return fs.create_dir(path.as_path());
415                }
416            };
417
418            (inode_of_parent, name_of_directory)
419        };
420
421        if self.read_dir(path).is_ok() {
422            return Err(FsError::AlreadyExists);
423        }
424
425        {
426            // Write lock.
427            let mut fs = self.inner.write().map_err(|_| FsError::Lock)?;
428
429            // Creating the directory in the storage.
430            let inode_of_directory = fs.storage.vacant_entry().key();
431            let real_inode_of_directory = fs.storage.insert(Node::Directory(DirectoryNode {
432                inode: inode_of_directory,
433                name: name_of_directory,
434                children: Vec::new(),
435                metadata: {
436                    let time = time();
437
438                    Metadata {
439                        ft: FileType {
440                            dir: true,
441                            ..Default::default()
442                        },
443                        accessed: time,
444                        created: time,
445                        modified: time,
446                        len: 0,
447                    }
448                },
449            }));
450
451            assert_eq!(
452                inode_of_directory, real_inode_of_directory,
453                "new directory inode should have been correctly calculated",
454            );
455
456            // Adding the new directory to its parent.
457            fs.add_child_to_node(inode_of_parent, inode_of_directory)?;
458        }
459
460        Ok(())
461    }
462
463    fn remove_dir(&self, path: &Path) -> Result<()> {
464        let (inode_of_parent, position, inode_of_directory) = {
465            // Read lock.
466            let guard = self.inner.read().map_err(|_| FsError::Lock)?;
467
468            // Canonicalize the path.
469            let (path, _) = guard.canonicalize(path)?;
470
471            // Check the path has a parent.
472            let parent_of_path = path.parent().ok_or(FsError::BaseNotDirectory)?;
473
474            // Check the directory name.
475            let name_of_directory = path
476                .file_name()
477                .ok_or(FsError::InvalidInput)?
478                .to_os_string();
479
480            // Find the parent inode.
481            let inode_of_parent = match guard.inode_of_parent(parent_of_path)? {
482                InodeResolution::Found(a) => a,
483                InodeResolution::Redirect(fs, mut parent_path) => {
484                    drop(guard);
485                    parent_path.push(name_of_directory);
486                    return fs.remove_dir(parent_path.as_path());
487                }
488            };
489
490            // Get the child index to remove in the parent node, in
491            // addition to the inode of the directory to remove.
492            let (position, inode_of_directory) = guard
493                .as_parent_get_position_and_inode_of_directory(
494                    inode_of_parent,
495                    &name_of_directory,
496                    DirectoryMustBeEmpty::Yes,
497                )?;
498
499            (inode_of_parent, position, inode_of_directory)
500        };
501
502        let inode_of_directory = match inode_of_directory {
503            InodeResolution::Found(a) => a,
504            InodeResolution::Redirect(fs, path) => {
505                return fs.remove_dir(path.as_path());
506            }
507        };
508
509        {
510            // Write lock.
511            let mut fs = self.inner.write().map_err(|_| FsError::Lock)?;
512
513            // Remove the directory from the storage.
514            fs.storage.remove(inode_of_directory);
515
516            // Remove the child from the parent directory.
517            fs.remove_child_from_node(inode_of_parent, position)?;
518        }
519
520        Ok(())
521    }
522
523    fn rename<'a>(&'a self, from: &'a Path, to: &'a Path) -> BoxFuture<'a, Result<()>> {
524        Box::pin(async move {
525            let name_of_to;
526
527            // Read lock.
528            let (name_of_from, inode_of_from_parent, name_of_to, inode_of_to_parent) = {
529                let fs = self.inner.read().map_err(|_| FsError::Lock)?;
530
531                let from = fs.canonicalize_without_inode(from)?;
532                let to = fs.canonicalize_without_inode(to)?;
533
534                // Check the paths have parents.
535                let parent_of_from = from.parent().ok_or(FsError::BaseNotDirectory)?;
536                let parent_of_to = to.parent().ok_or(FsError::BaseNotDirectory)?;
537
538                // Check the names.
539                let name_of_from = from
540                    .file_name()
541                    .ok_or(FsError::InvalidInput)?
542                    .to_os_string();
543                name_of_to = to.file_name().ok_or(FsError::InvalidInput)?.to_os_string();
544
545                // Find the parent inodes.
546                let inode_of_from_parent = match fs.inode_of_parent(parent_of_from)? {
547                    InodeResolution::Found(a) => Either::Left(a),
548                    InodeResolution::Redirect(fs, mut path) => {
549                        path.push(&name_of_from);
550                        Either::Right((fs, path))
551                    }
552                };
553                let inode_of_to_parent = match fs.inode_of_parent(parent_of_to)? {
554                    InodeResolution::Found(a) => Either::Left(a),
555                    InodeResolution::Redirect(fs, mut path) => {
556                        path.push(&name_of_to);
557                        Either::Right((fs, path))
558                    }
559                };
560
561                (
562                    name_of_from,
563                    inode_of_from_parent,
564                    name_of_to,
565                    inode_of_to_parent,
566                )
567            };
568
569            match (inode_of_from_parent, inode_of_to_parent) {
570                // Rename within this MemFS instance
571                (Either::Left(inode_of_from_parent), Either::Left(inode_of_to_parent)) => {
572                    let fs = self.inner.read().map_err(|_| FsError::Lock)?;
573
574                    // Find the inode of the dest file if it exists
575                    let maybe_position_and_inode_of_file = fs
576                        .as_parent_get_position_and_inode_of_file(
577                            inode_of_to_parent,
578                            &name_of_to,
579                        )?;
580
581                    // Get the child indexes to update in the parent nodes, in
582                    // addition to the inode of the directory to update.
583                    let (position_of_from, inode) = fs
584                        .as_parent_get_position_and_inode(inode_of_from_parent, &name_of_from)?
585                        .ok_or(FsError::EntryNotFound)?;
586
587                    let (
588                        (position_of_from, inode, inode_of_from_parent),
589                        (inode_of_to_parent, name_of_to),
590                        inode_dest,
591                    ) = (
592                        (position_of_from, inode, inode_of_from_parent),
593                        (inode_of_to_parent, name_of_to),
594                        maybe_position_and_inode_of_file,
595                    );
596
597                    let inode = match inode {
598                        InodeResolution::Found(a) => a,
599                        InodeResolution::Redirect(..) => {
600                            return Err(FsError::InvalidInput);
601                        }
602                    };
603
604                    drop(fs);
605
606                    {
607                        // Write lock.
608                        let mut fs = self.inner.write().map_err(|_| FsError::Lock)?;
609
610                        if let Some((position, inode_of_file)) = inode_dest {
611                            // Remove the file from the storage.
612                            match inode_of_file {
613                                InodeResolution::Found(inode_of_file) => {
614                                    fs.storage.remove(inode_of_file);
615                                }
616                                InodeResolution::Redirect(..) => {
617                                    return Err(FsError::InvalidInput);
618                                }
619                            }
620
621                            fs.remove_child_from_node(inode_of_to_parent, position)?;
622                        }
623
624                        // Update the file name, and update the modified time.
625                        fs.update_node_name(inode, name_of_to)?;
626
627                        // The parents are different. Let's update them.
628                        if inode_of_from_parent != inode_of_to_parent {
629                            // Remove the file from its parent, and update the
630                            // modified time.
631                            fs.remove_child_from_node(inode_of_from_parent, position_of_from)?;
632
633                            // Add the file to its new parent, and update the modified
634                            // time.
635                            fs.add_child_to_node(inode_of_to_parent, inode)?;
636                        }
637                        // Otherwise, we need to at least update the modified time of the parent.
638                        else {
639                            let mut inode = fs.storage.get_mut(inode_of_from_parent);
640                            match inode.as_mut() {
641                                Some(Node::Directory(node)) => node.metadata.modified = time(),
642                                Some(Node::ArcDirectory(node)) => node.metadata.modified = time(),
643                                _ => return Err(FsError::UnknownError),
644                            }
645                        }
646                    }
647
648                    Ok(())
649                }
650
651                // Rename within the same mounted FS instance
652                (Either::Right((from_fs, from_path)), Either::Right((to_fs, to_path)))
653                    if Arc::ptr_eq(&from_fs, &to_fs) =>
654                {
655                    let same_fs = from_fs;
656                    same_fs.rename(&from_path, &to_path).await
657                }
658
659                // Rename across file systems; we need to do a create and a delete
660                _ => {
661                    let mut from_file = self.new_open_options().read(true).open(from)?;
662                    let mut to_file = self
663                        .new_open_options()
664                        .create_new(true)
665                        .write(true)
666                        .open(to)?;
667                    tokio::io::copy(from_file.as_mut(), to_file.as_mut()).await?;
668                    if let Err(error) = self.remove_file(from) {
669                        tracing::warn!(
670                            ?from,
671                            ?to,
672                            ?error,
673                            "Failed to remove file after cross-FS rename"
674                        );
675                    }
676                    Ok(())
677                }
678            }
679        })
680    }
681
682    fn metadata(&self, path: &Path) -> Result<Metadata> {
683        // Read lock.
684        let guard = self.inner.read().map_err(|_| FsError::Lock)?;
685        match guard.inode_of(path)? {
686            InodeResolution::Found(inode) => Ok(guard
687                .storage
688                .get(inode)
689                .ok_or(FsError::UnknownError)?
690                .metadata()
691                .clone()),
692            InodeResolution::Redirect(fs, path) => {
693                drop(guard);
694                fs.metadata(path.as_path())
695            }
696        }
697    }
698
699    fn symlink_metadata(&self, path: &Path) -> Result<Metadata> {
700        // Read lock.
701        let guard = self.inner.read().map_err(|_| FsError::Lock)?;
702        match guard.inode_of(path)? {
703            InodeResolution::Found(inode) => Ok(guard
704                .storage
705                .get(inode)
706                .ok_or(FsError::UnknownError)?
707                .metadata()
708                .clone()),
709            InodeResolution::Redirect(fs, path) => {
710                drop(guard);
711                fs.symlink_metadata(path.as_path())
712            }
713        }
714    }
715
716    fn remove_file(&self, path: &Path) -> Result<()> {
717        let (inode_of_parent, position, inode_of_file) = {
718            // Read lock.
719            let guard = self.inner.read().map_err(|_| FsError::Lock)?;
720
721            // Canonicalize the path.
722            let path = guard.canonicalize_without_inode(path)?;
723
724            // Check the path has a parent.
725            let parent_of_path = path.parent().ok_or(FsError::BaseNotDirectory)?;
726
727            // Check the file name.
728            let name_of_file = path
729                .file_name()
730                .ok_or(FsError::InvalidInput)?
731                .to_os_string();
732
733            // Find the parent inode.
734            let inode_of_parent = match guard.inode_of_parent(parent_of_path)? {
735                InodeResolution::Found(a) => a,
736                InodeResolution::Redirect(fs, mut parent_path) => {
737                    parent_path.push(name_of_file);
738                    return fs.remove_file(parent_path.as_path());
739                }
740            };
741
742            // Find the inode of the file if it exists, along with its position.
743            let maybe_position_and_inode_of_file =
744                guard.as_parent_get_position_and_inode_of_file(inode_of_parent, &name_of_file)?;
745
746            match maybe_position_and_inode_of_file {
747                Some((position, inode_of_file)) => (inode_of_parent, position, inode_of_file),
748                None => return Err(FsError::EntryNotFound),
749            }
750        };
751
752        let inode_of_file = match inode_of_file {
753            InodeResolution::Found(a) => a,
754            InodeResolution::Redirect(fs, path) => {
755                return fs.remove_file(path.as_path());
756            }
757        };
758
759        {
760            // Write lock.
761            let mut fs = self.inner.write().map_err(|_| FsError::Lock)?;
762
763            // Remove the file from the storage.
764            fs.storage.remove(inode_of_file);
765
766            // Remove the child from the parent directory.
767            fs.remove_child_from_node(inode_of_parent, position)?;
768        }
769
770        Ok(())
771    }
772
773    fn new_open_options(&self) -> OpenOptions<'_> {
774        OpenOptions::new(self)
775    }
776
777    fn mount(
778        &self,
779        _name: String,
780        path: &Path,
781        fs: Box<dyn crate::FileSystem + Send + Sync>,
782    ) -> Result<()> {
783        let fs: Arc<dyn crate::FileSystem + Send + Sync> = Arc::new(fs);
784        self.mount(path.to_owned(), &fs, PathBuf::from("/"))
785    }
786}
787
788impl fmt::Debug for FileSystem {
789    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
790        let fs: &FileSystemInner = &self.inner.read().unwrap();
791
792        fs.fmt(formatter)
793    }
794}
795
796/// The core of the file system. It contains a collection of `Node`s,
797/// indexed by their respective `Inode` in a slab.
798pub(super) struct FileSystemInner {
799    pub(super) storage: Slab<Node>,
800    pub(super) backing_offload: Option<OffloadBackingStore>,
801    pub(super) limiter: Option<crate::limiter::DynFsMemoryLimiter>,
802}
803
804#[derive(Debug)]
805pub(super) enum InodeResolution {
806    Found(Inode),
807    Redirect(Arc<dyn crate::FileSystem + Send + Sync + 'static>, PathBuf),
808}
809
810impl InodeResolution {
811    #[allow(dead_code)]
812    pub fn unwrap(&self) -> Inode {
813        match self {
814            Self::Found(a) => *a,
815            Self::Redirect(..) => {
816                panic!("failed to unwrap the inode as the resolution is a redirect");
817            }
818        }
819    }
820}
821
822impl FileSystemInner {
823    /// Get the inode associated to a path if it exists.
824    pub(super) fn inode_of(&self, path: &Path) -> Result<InodeResolution> {
825        // SAFETY: The root node always exists, so it's safe to unwrap here.
826        let mut node = self.storage.get(ROOT_INODE).unwrap();
827        let mut components = path.components();
828
829        match components.next() {
830            Some(Component::RootDir) => {}
831            _ => return Err(FsError::BaseNotDirectory),
832        }
833
834        while let Some(component) = components.next() {
835            node = match node {
836                Node::Directory(DirectoryNode { children, .. }) => children
837                    .iter()
838                    .filter_map(|inode| self.storage.get(*inode))
839                    .find(|node| node.name() == component.as_os_str())
840                    .ok_or(FsError::EntryNotFound)?,
841                Node::ArcDirectory(ArcDirectoryNode {
842                    fs, path: fs_path, ..
843                }) => {
844                    let mut path = fs_path.clone();
845                    path.push(PathBuf::from(component.as_os_str()));
846                    for component in components.by_ref() {
847                        path.push(PathBuf::from(component.as_os_str()));
848                    }
849                    return Ok(InodeResolution::Redirect(fs.clone(), path));
850                }
851                _ => return Err(FsError::BaseNotDirectory),
852            };
853        }
854
855        Ok(InodeResolution::Found(node.inode()))
856    }
857
858    /// Get the inode associated to a “parent path”. The returned
859    /// inode necessarily represents a directory.
860    pub(super) fn inode_of_parent(&self, parent_path: &Path) -> Result<InodeResolution> {
861        match self.inode_of(parent_path)? {
862            InodeResolution::Found(inode_of_parent) => {
863                // Ensure it is a directory.
864                match self.storage.get(inode_of_parent) {
865                    Some(Node::Directory(DirectoryNode { .. })) => {
866                        Ok(InodeResolution::Found(inode_of_parent))
867                    }
868                    Some(Node::ArcDirectory(ArcDirectoryNode { fs, path, .. })) => {
869                        Ok(InodeResolution::Redirect(fs.clone(), path.clone()))
870                    }
871                    _ => Err(FsError::BaseNotDirectory),
872                }
873            }
874            InodeResolution::Redirect(fs, path) => Ok(InodeResolution::Redirect(fs, path)),
875        }
876    }
877
878    /// From the inode of a parent node (so, a directory), returns the
879    /// child index of `name_of_directory` along with its inode.
880    pub(super) fn as_parent_get_position_and_inode_of_directory(
881        &self,
882        inode_of_parent: Inode,
883        name_of_directory: &OsString,
884        directory_must_be_empty: DirectoryMustBeEmpty,
885    ) -> Result<(usize, InodeResolution)> {
886        match self.storage.get(inode_of_parent) {
887            Some(Node::Directory(DirectoryNode { children, .. })) => children
888                .iter()
889                .enumerate()
890                .filter_map(|(nth, inode)| self.storage.get(*inode).map(|node| (nth, node)))
891                .find_map(|(nth, node)| match node {
892                    Node::Directory(DirectoryNode {
893                        inode,
894                        name,
895                        children,
896                        ..
897                    }) if name.as_os_str() == name_of_directory => {
898                        if directory_must_be_empty.no() || children.is_empty() {
899                            Some(Ok((nth, InodeResolution::Found(*inode))))
900                        } else {
901                            Some(Err(FsError::DirectoryNotEmpty))
902                        }
903                    }
904                    Node::ArcDirectory(ArcDirectoryNode { name, fs, path, .. })
905                        if name.as_os_str() == name_of_directory =>
906                    {
907                        Some(Ok((0, InodeResolution::Redirect(fs.clone(), path.clone()))))
908                    }
909                    _ => None,
910                })
911                .ok_or(FsError::InvalidInput)
912                .and_then(identity), // flatten
913
914            Some(Node::ArcDirectory(ArcDirectoryNode {
915                fs, path: fs_path, ..
916            })) => {
917                let mut path = fs_path.clone();
918                path.push(name_of_directory);
919                Ok((0, InodeResolution::Redirect(fs.clone(), path)))
920            }
921
922            _ => Err(FsError::BaseNotDirectory),
923        }
924    }
925
926    /// From the inode of a parent node (so, a directory), returns the
927    /// child index of `name_of_file` along with its inode.
928    pub(super) fn as_parent_get_position_and_inode_of_file(
929        &self,
930        inode_of_parent: Inode,
931        name_of_file: &OsString,
932    ) -> Result<Option<(usize, InodeResolution)>> {
933        match self.storage.get(inode_of_parent) {
934            Some(Node::Directory(DirectoryNode { children, .. })) => children
935                .iter()
936                .enumerate()
937                .filter_map(|(nth, inode)| self.storage.get(*inode).map(|node| (nth, node)))
938                .find_map(|(nth, node)| match node {
939                    Node::File(FileNode { inode, name, .. })
940                    | Node::OffloadedFile(OffloadedFileNode { inode, name, .. })
941                    | Node::ReadOnlyFile(ReadOnlyFileNode { inode, name, .. })
942                    | Node::CustomFile(CustomFileNode { inode, name, .. })
943                    | Node::ArcFile(ArcFileNode { inode, name, .. })
944                    | Node::Symlink(SymlinkNode { inode, name, .. })
945                        if name.as_os_str() == name_of_file =>
946                    {
947                        Some(Some((nth, InodeResolution::Found(*inode))))
948                    }
949                    _ => None,
950                })
951                .or(Some(None))
952                .ok_or(FsError::InvalidInput),
953
954            Some(Node::ArcDirectory(ArcDirectoryNode {
955                fs, path: fs_path, ..
956            })) => {
957                let mut path = fs_path.clone();
958                path.push(name_of_file);
959                Ok(Some((0, InodeResolution::Redirect(fs.clone(), path))))
960            }
961
962            _ => Err(FsError::BaseNotDirectory),
963        }
964    }
965
966    /// From the inode of a parent node (so, a directory), returns the
967    /// child index of `name_of` along with its inode, whatever the
968    /// type of inode is (directory or file).
969    fn as_parent_get_position_and_inode(
970        &self,
971        inode_of_parent: Inode,
972        name_of: &OsString,
973    ) -> Result<Option<(usize, InodeResolution)>> {
974        match self.storage.get(inode_of_parent) {
975            Some(Node::Directory(DirectoryNode { children, .. })) => children
976                .iter()
977                .enumerate()
978                .filter_map(|(nth, inode)| self.storage.get(*inode).map(|node| (nth, node)))
979                .find_map(|(nth, node)| match node {
980                    Node::File(FileNode { inode, name, .. })
981                    | Node::OffloadedFile(OffloadedFileNode { inode, name, .. })
982                    | Node::Directory(DirectoryNode { inode, name, .. })
983                    | Node::ReadOnlyFile(ReadOnlyFileNode { inode, name, .. })
984                    | Node::CustomFile(CustomFileNode { inode, name, .. })
985                    | Node::ArcFile(ArcFileNode { inode, name, .. })
986                        if name.as_os_str() == name_of =>
987                    {
988                        Some(Some((nth, InodeResolution::Found(*inode))))
989                    }
990                    _ => None,
991                })
992                .or(Some(None))
993                .ok_or(FsError::InvalidInput),
994
995            Some(Node::ArcDirectory(ArcDirectoryNode {
996                fs, path: fs_path, ..
997            })) => {
998                let mut path = fs_path.clone();
999                path.push(name_of);
1000                Ok(Some((0, InodeResolution::Redirect(fs.clone(), path))))
1001            }
1002
1003            _ => Err(FsError::BaseNotDirectory),
1004        }
1005    }
1006
1007    /// Set a new name for the node represented by `inode`.
1008    pub(super) fn update_node_name(&mut self, inode: Inode, new_name: OsString) -> Result<()> {
1009        let node = self.storage.get_mut(inode).ok_or(FsError::UnknownError)?;
1010
1011        node.set_name(new_name);
1012        node.metadata_mut().modified = time();
1013
1014        Ok(())
1015    }
1016
1017    /// Add a child to a directory node represented by `inode`.
1018    ///
1019    /// This function also updates the modified time of the directory.
1020    ///
1021    /// # Safety
1022    ///
1023    /// `inode` must represents an existing directory.
1024    pub(super) fn add_child_to_node(&mut self, inode: Inode, new_child: Inode) -> Result<()> {
1025        match self.storage.get_mut(inode) {
1026            Some(Node::Directory(DirectoryNode {
1027                children,
1028                metadata: Metadata { modified, .. },
1029                ..
1030            })) => {
1031                children.push(new_child);
1032                *modified = time();
1033
1034                Ok(())
1035            }
1036            _ => Err(FsError::UnknownError),
1037        }
1038    }
1039
1040    /// Remove the child at position `position` of a directory node
1041    /// represented by `inode`.
1042    ///
1043    /// This function also updates the modified time of the directory.
1044    ///
1045    /// # Safety
1046    ///
1047    /// `inode` must represents an existing directory.
1048    pub(super) fn remove_child_from_node(&mut self, inode: Inode, position: usize) -> Result<()> {
1049        match self.storage.get_mut(inode) {
1050            Some(Node::Directory(DirectoryNode {
1051                children,
1052                metadata: Metadata { modified, .. },
1053                ..
1054            })) => {
1055                children.remove(position);
1056                *modified = time();
1057
1058                Ok(())
1059            }
1060            _ => Err(FsError::UnknownError),
1061        }
1062    }
1063
1064    /// Canonicalize a path, i.e. try to resolve to a canonical,
1065    /// absolute form of the path with all intermediate components
1066    /// normalized:
1067    ///
1068    /// * A path must starts with a root (`/`),
1069    /// * A path can contain `..` or `.` components,
1070    /// * A path must not contain a Windows prefix (`C:` or `\\server`),
1071    /// * A normalized path exists in the file system.
1072    pub(super) fn canonicalize(&self, path: &Path) -> Result<(PathBuf, InodeResolution)> {
1073        let new_path = self.canonicalize_without_inode(path)?;
1074        let inode = self.inode_of(&new_path)?;
1075
1076        Ok((new_path, inode))
1077    }
1078
1079    /// Like `Self::canonicalize` but without returning the inode of
1080    /// the path, which means that there is no guarantee that the path
1081    /// exists in the file system.
1082    pub(super) fn canonicalize_without_inode(&self, path: &Path) -> Result<PathBuf> {
1083        let mut components = path.components();
1084
1085        match components.next() {
1086            Some(Component::RootDir) => {}
1087            _ => return Err(FsError::InvalidInput),
1088        }
1089
1090        let mut new_path = PathBuf::with_capacity(path.as_os_str().len());
1091        new_path.push("/");
1092
1093        for component in components {
1094            match component {
1095                // That's an error to get a `RootDir` a second time.
1096                Component::RootDir => return Err(FsError::UnknownError),
1097
1098                // Nothing to do on `new_path`.
1099                Component::CurDir => (),
1100
1101                // Pop the lastly inserted component on `new_path` if
1102                // any, otherwise it's an error.
1103                Component::ParentDir => {
1104                    if !new_path.pop() {
1105                        return Err(FsError::InvalidInput);
1106                    }
1107                }
1108
1109                // A normal
1110                Component::Normal(name) => {
1111                    new_path.push(name);
1112                }
1113
1114                // We don't support Windows path prefix.
1115                Component::Prefix(_) => return Err(FsError::InvalidInput),
1116            }
1117        }
1118
1119        Ok(new_path)
1120    }
1121}
1122
1123impl fmt::Debug for FileSystemInner {
1124    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1125        writeln!(
1126            formatter,
1127            "\n{inode:<8}    {ty:<4}    name",
1128            inode = "inode",
1129            ty = "type",
1130        )?;
1131
1132        fn debug(
1133            nodes: Vec<&Node>,
1134            slf: &FileSystemInner,
1135            formatter: &mut fmt::Formatter<'_>,
1136            indentation: usize,
1137        ) -> fmt::Result {
1138            for node in nodes {
1139                writeln!(
1140                    formatter,
1141                    "{inode:<8}    {ty:<4}   {indentation_symbol:indentation_width$}{name}",
1142                    inode = node.inode(),
1143                    ty = match node {
1144                        Node::File { .. } => "file",
1145                        Node::OffloadedFile { .. } => "offloaded-file",
1146                        Node::ReadOnlyFile { .. } => "ro-file",
1147                        Node::ArcFile { .. } => "arc-file",
1148                        Node::CustomFile { .. } => "custom-file",
1149                        Node::Symlink { .. } => "symlink",
1150                        Node::Directory { .. } => "dir",
1151                        Node::ArcDirectory { .. } => "arc-dir",
1152                    },
1153                    name = node.name().to_string_lossy(),
1154                    indentation_symbol = " ",
1155                    indentation_width = indentation * 2 + 1,
1156                )?;
1157
1158                if let Node::Directory(DirectoryNode { children, .. }) = node {
1159                    debug(
1160                        children
1161                            .iter()
1162                            .filter_map(|inode| slf.storage.get(*inode))
1163                            .collect(),
1164                        slf,
1165                        formatter,
1166                        indentation + 1,
1167                    )?;
1168                }
1169            }
1170
1171            Ok(())
1172        }
1173
1174        debug(
1175            vec![self.storage.get(ROOT_INODE).unwrap()],
1176            self,
1177            formatter,
1178            0,
1179        )
1180    }
1181}
1182
1183impl Default for FileSystemInner {
1184    fn default() -> Self {
1185        let time = time();
1186
1187        let mut slab = Slab::new();
1188        slab.insert(Node::Directory(DirectoryNode {
1189            inode: ROOT_INODE,
1190            name: OsString::from("/"),
1191            children: Vec::new(),
1192            metadata: Metadata {
1193                ft: FileType {
1194                    dir: true,
1195                    ..Default::default()
1196                },
1197                accessed: time,
1198                created: time,
1199                modified: time,
1200                len: 0,
1201            },
1202        }));
1203
1204        Self {
1205            storage: slab,
1206            backing_offload: None,
1207            limiter: None,
1208        }
1209    }
1210}
1211
1212#[allow(dead_code)] // The `No` variant.
1213pub(super) enum DirectoryMustBeEmpty {
1214    Yes,
1215    No,
1216}
1217
1218impl DirectoryMustBeEmpty {
1219    pub(super) fn yes(&self) -> bool {
1220        matches!(self, Self::Yes)
1221    }
1222
1223    pub(super) fn no(&self) -> bool {
1224        !self.yes()
1225    }
1226}
1227
1228#[cfg(test)]
1229mod test_filesystem {
1230    use std::{path::Path, sync::Arc};
1231
1232    use shared_buffer::OwnedBuffer;
1233    use tokio::io::AsyncReadExt;
1234
1235    use crate::{DirEntry, FileSystem as FS, FileType, FsError, mem_fs::*, ops};
1236
1237    macro_rules! path {
1238        ($path:expr) => {
1239            std::path::Path::new($path)
1240        };
1241
1242        (buf $path:expr) => {
1243            std::path::PathBuf::from($path)
1244        };
1245    }
1246
1247    #[tokio::test]
1248    async fn test_new_filesystem() {
1249        let fs = FileSystem::default();
1250        let fs_inner = fs.inner.read().unwrap();
1251
1252        assert_eq!(fs_inner.storage.len(), 1, "storage has a root");
1253        assert!(
1254            matches!(
1255                fs_inner.storage.get(ROOT_INODE),
1256                Some(Node::Directory(DirectoryNode {
1257                    inode: ROOT_INODE,
1258                    name,
1259                    children,
1260                    ..
1261                })) if name == "/" && children.is_empty(),
1262            ),
1263            "storage has a well-defined root",
1264        );
1265    }
1266
1267    #[tokio::test]
1268    async fn test_create_dir() {
1269        let fs = FileSystem::default();
1270
1271        assert_eq!(
1272            fs.create_dir(path!("/")),
1273            Err(FsError::AlreadyExists),
1274            "creating the root which already exists",
1275        );
1276
1277        assert_eq!(fs.create_dir(path!("/foo")), Ok(()), "creating a directory");
1278
1279        {
1280            let fs_inner = fs.inner.read().unwrap();
1281            assert_eq!(
1282                fs_inner.storage.len(),
1283                2,
1284                "storage contains the new directory"
1285            );
1286            assert!(
1287                matches!(
1288                    fs_inner.storage.get(ROOT_INODE),
1289                    Some(Node::Directory(DirectoryNode {
1290                        inode: ROOT_INODE,
1291                        name,
1292                        children,
1293                        ..
1294                    })) if name == "/" && children == &[1]
1295                ),
1296                "the root is updated and well-defined",
1297            );
1298            assert!(
1299                matches!(
1300                    fs_inner.storage.get(1),
1301                    Some(Node::Directory(DirectoryNode {
1302                        inode: 1,
1303                        name,
1304                        children,
1305                        ..
1306                    })) if name == "foo" && children.is_empty(),
1307                ),
1308                "the new directory is well-defined",
1309            );
1310        }
1311
1312        assert_eq!(
1313            fs.create_dir(path!("/foo/bar")),
1314            Ok(()),
1315            "creating a sub-directory",
1316        );
1317
1318        {
1319            let fs_inner = fs.inner.read().unwrap();
1320            assert_eq!(
1321                fs_inner.storage.len(),
1322                3,
1323                "storage contains the new sub-directory",
1324            );
1325            assert!(
1326                matches!(
1327                    fs_inner.storage.get(ROOT_INODE),
1328                    Some(Node::Directory(DirectoryNode {
1329                        inode: ROOT_INODE,
1330                        name,
1331                        children,
1332                        ..
1333                    })) if name == "/" && children == &[1]
1334                ),
1335                "the root is updated again and well-defined",
1336            );
1337            assert!(
1338                matches!(
1339                    fs_inner.storage.get(1),
1340                    Some(Node::Directory(DirectoryNode {
1341                        inode: 1,
1342                        name,
1343                        children,
1344                        ..
1345                    })) if name == "foo" && children == &[2]
1346                ),
1347                "the new directory is updated and well-defined",
1348            );
1349            assert!(
1350                matches!(
1351                    fs_inner.storage.get(2),
1352                    Some(Node::Directory(DirectoryNode {
1353                        inode: 2,
1354                        name,
1355                        children,
1356                        ..
1357                    })) if name == "bar" && children.is_empty()
1358                ),
1359                "the new directory is well-defined",
1360            );
1361        }
1362    }
1363
1364    #[tokio::test]
1365    async fn test_remove_dir() {
1366        let fs = FileSystem::default();
1367
1368        assert_eq!(
1369            fs.remove_dir(path!("/")),
1370            Err(FsError::BaseNotDirectory),
1371            "removing a directory that has no parent",
1372        );
1373
1374        assert_eq!(
1375            fs.remove_dir(path!("/foo")),
1376            Err(FsError::EntryNotFound),
1377            "cannot remove a directory that doesn't exist",
1378        );
1379
1380        assert_eq!(fs.create_dir(path!("/foo")), Ok(()), "creating a directory");
1381
1382        assert_eq!(
1383            fs.create_dir(path!("/foo/bar")),
1384            Ok(()),
1385            "creating a sub-directory",
1386        );
1387
1388        {
1389            let fs_inner = fs.inner.read().unwrap();
1390            assert_eq!(
1391                fs_inner.storage.len(),
1392                3,
1393                "storage contains all the directories",
1394            );
1395        }
1396
1397        assert_eq!(
1398            fs.remove_dir(path!("/foo")),
1399            Err(FsError::DirectoryNotEmpty),
1400            "removing a directory that has children",
1401        );
1402
1403        assert_eq!(
1404            fs.remove_dir(path!("/foo/bar")),
1405            Ok(()),
1406            "removing a sub-directory",
1407        );
1408
1409        assert_eq!(fs.remove_dir(path!("/foo")), Ok(()), "removing a directory");
1410
1411        {
1412            let fs_inner = fs.inner.read().unwrap();
1413            assert_eq!(
1414                fs_inner.storage.len(),
1415                1,
1416                "storage contains all the directories",
1417            );
1418        }
1419    }
1420
1421    #[tokio::test]
1422    async fn test_rename() {
1423        let fs = FileSystem::default();
1424
1425        let fs2 = FileSystem::default();
1426        fs2.create_dir(path!("/boz")).unwrap();
1427        let arc_fs2: Arc<dyn crate::FileSystem + Send + Sync> = Arc::new(fs2);
1428
1429        assert_eq!(
1430            fs.rename(path!("/"), path!("/bar")).await,
1431            Err(FsError::BaseNotDirectory),
1432            "renaming a directory that has no parent",
1433        );
1434        assert_eq!(
1435            fs.rename(path!("/foo"), path!("/")).await,
1436            Err(FsError::BaseNotDirectory),
1437            "renaming to a directory that has no parent",
1438        );
1439
1440        assert_eq!(fs.create_dir(path!("/foo")), Ok(()));
1441        assert_eq!(fs.create_dir(path!("/foo/qux")), Ok(()));
1442
1443        assert_eq!(
1444            fs.rename(path!("/foo"), path!("/bar/baz")).await,
1445            Err(FsError::EntryNotFound),
1446            "renaming to a directory that has parent that doesn't exist",
1447        );
1448
1449        assert_eq!(fs.create_dir(path!("/bar")), Ok(()));
1450
1451        assert!(
1452            fs.new_open_options()
1453                .write(true)
1454                .create_new(true)
1455                .open(path!("/bar/hello1.txt"))
1456                .is_ok(),
1457            "creating a new file (`hello1.txt`)",
1458        );
1459        assert!(
1460            fs.new_open_options()
1461                .write(true)
1462                .create_new(true)
1463                .open(path!("/bar/hello2.txt"))
1464                .is_ok(),
1465            "creating a new file (`hello2.txt`)",
1466        );
1467
1468        fs.mount(path!(buf "/mnt"), &arc_fs2, path!(buf "/"))
1469            .unwrap();
1470
1471        {
1472            let fs_inner = fs.inner.read().unwrap();
1473
1474            assert_eq!(fs_inner.storage.len(), 7, "storage has all files");
1475            assert!(
1476                matches!(
1477                    fs_inner.storage.get(ROOT_INODE),
1478                    Some(Node::Directory(DirectoryNode {
1479                        inode: ROOT_INODE,
1480                        name,
1481                        children,
1482                        ..
1483                    })) if name == "/" && children == &[1, 3, 6]
1484                ),
1485                "`/` contains `foo` and `bar` and `mnt`",
1486            );
1487            assert!(
1488                matches!(
1489                    fs_inner.storage.get(1),
1490                    Some(Node::Directory(DirectoryNode {
1491                        inode: 1,
1492                        name,
1493                        children,
1494                        ..
1495                    })) if name == "foo" && children == &[2]
1496                ),
1497                "`foo` contains `qux`",
1498            );
1499            assert!(
1500                matches!(
1501                    fs_inner.storage.get(2),
1502                    Some(Node::Directory(DirectoryNode {
1503                        inode: 2,
1504                        name,
1505                        children,
1506                        ..
1507                    })) if name == "qux" && children.is_empty()
1508                ),
1509                "`qux` is empty",
1510            );
1511            assert!(
1512                matches!(
1513                    fs_inner.storage.get(3),
1514                    Some(Node::Directory(DirectoryNode {
1515                        inode: 3,
1516                        name,
1517                        children,
1518                        ..
1519                    })) if name == "bar" && children == &[4, 5]
1520                ),
1521                "`bar` is contains `hello.txt`",
1522            );
1523            assert!(
1524                matches!(
1525                    fs_inner.storage.get(4),
1526                    Some(Node::File(FileNode {
1527                        inode: 4,
1528                        name,
1529                        ..
1530                    })) if name == "hello1.txt"
1531                ),
1532                "`hello1.txt` exists",
1533            );
1534            assert!(
1535                matches!(
1536                    fs_inner.storage.get(5),
1537                    Some(Node::File(FileNode {
1538                        inode: 5,
1539                        name,
1540                        ..
1541                    })) if name == "hello2.txt"
1542                ),
1543                "`hello2.txt` exists",
1544            );
1545        }
1546
1547        assert_eq!(
1548            fs.rename(path!("/bar/hello2.txt"), path!("/foo/world2.txt"))
1549                .await,
1550            Ok(()),
1551            "renaming (and moving) a file",
1552        );
1553
1554        assert_eq!(
1555            fs.rename(path!("/foo"), path!("/bar/baz")).await,
1556            Ok(()),
1557            "renaming a directory",
1558        );
1559
1560        assert_eq!(
1561            fs.rename(path!("/mnt/boz"), path!("/mnt/cat")).await,
1562            Ok(()),
1563            "renaming a directory within a mounted FS"
1564        );
1565
1566        assert!(
1567            arc_fs2.read_dir(path!("/cat")).is_ok(),
1568            "The directory was renamed in the inner FS"
1569        );
1570
1571        assert_eq!(
1572            fs.rename(path!("/bar/hello1.txt"), path!("/bar/world1.txt"))
1573                .await,
1574            Ok(()),
1575            "renaming a file (in the same directory)",
1576        );
1577
1578        {
1579            let fs_inner = fs.inner.read().unwrap();
1580
1581            dbg!(&fs_inner);
1582
1583            assert_eq!(
1584                fs_inner.storage.len(),
1585                7,
1586                "storage has still all directories"
1587            );
1588            assert!(
1589                matches!(
1590                    fs_inner.storage.get(ROOT_INODE),
1591                    Some(Node::Directory(DirectoryNode {
1592                        inode: ROOT_INODE,
1593                        name,
1594                        children,
1595                        ..
1596                    })) if name == "/" && children == &[3, 6]
1597                ),
1598                "`/` contains `bar` and `mnt`",
1599            );
1600            assert!(
1601                matches!(
1602                    fs_inner.storage.get(1),
1603                    Some(Node::Directory(DirectoryNode {
1604                        inode: 1,
1605                        name,
1606                        children,
1607                        ..
1608                    })) if name == "baz" && children == &[2, 5]
1609                ),
1610                "`foo` has been renamed to `baz` and contains `qux` and `world2.txt`",
1611            );
1612            assert!(
1613                matches!(
1614                    fs_inner.storage.get(2),
1615                    Some(Node::Directory(DirectoryNode {
1616                        inode: 2,
1617                        name,
1618                        children,
1619                        ..
1620                    })) if name == "qux" && children.is_empty()
1621                ),
1622                "`qux` is empty",
1623            );
1624            assert!(
1625                matches!(
1626                    fs_inner.storage.get(3),
1627                    Some(Node::Directory(DirectoryNode {
1628                        inode: 3,
1629                        name,
1630                        children,
1631                        ..
1632                    })) if name == "bar" && children == &[4, 1]
1633                ),
1634                "`bar` contains `bar` (ex `foo`)  and `world1.txt` (ex `hello1`)",
1635            );
1636            assert!(
1637                matches!(
1638                    fs_inner.storage.get(4),
1639                    Some(Node::File(FileNode {
1640                        inode: 4,
1641                        name,
1642                        ..
1643                    })) if name == "world1.txt"
1644                ),
1645                "`hello1.txt` has been renamed to `world1.txt`",
1646            );
1647            assert!(
1648                matches!(
1649                    fs_inner.storage.get(5),
1650                    Some(Node::File(FileNode {
1651                        inode: 5,
1652                        name,
1653                        ..
1654                    })) if name == "world2.txt"
1655                ),
1656                "`hello2.txt` has been renamed to `world2.txt`",
1657            );
1658        }
1659    }
1660
1661    #[tokio::test]
1662    async fn test_metadata() {
1663        use std::thread::sleep;
1664        use std::time::Duration;
1665
1666        let fs = FileSystem::default();
1667        let root_metadata = fs.metadata(path!("/"));
1668
1669        assert!(matches!(
1670            root_metadata,
1671            Ok(Metadata {
1672                ft: FileType { dir: true, .. },
1673                accessed,
1674                created,
1675                modified,
1676                len: 0
1677            }) if accessed == created && created == modified && modified > 0
1678        ));
1679
1680        assert_eq!(fs.create_dir(path!("/foo")), Ok(()));
1681
1682        let foo_metadata = fs.metadata(path!("/foo"));
1683        assert!(foo_metadata.is_ok());
1684        let foo_metadata = foo_metadata.unwrap();
1685
1686        assert!(matches!(
1687            foo_metadata,
1688            Metadata {
1689                ft: FileType { dir: true, .. },
1690                accessed,
1691                created,
1692                modified,
1693                len: 0
1694            } if accessed == created && created == modified && modified > 0
1695        ));
1696
1697        sleep(Duration::from_secs(3));
1698
1699        assert_eq!(fs.rename(path!("/foo"), path!("/bar")).await, Ok(()));
1700
1701        assert!(
1702            matches!(
1703                fs.metadata(path!("/bar")),
1704                Ok(Metadata {
1705                    ft: FileType { dir: true, .. },
1706                    accessed,
1707                    created,
1708                    modified,
1709                    len: 0
1710                }) if
1711                    accessed == foo_metadata.accessed &&
1712                    created == foo_metadata.created &&
1713                    modified > foo_metadata.modified
1714            ),
1715            "the modified time is updated when file is renamed",
1716        );
1717        assert!(
1718            matches!(
1719                fs.metadata(path!("/")),
1720                Ok(Metadata {
1721                    ft: FileType { dir: true, .. },
1722                    accessed,
1723                    created,
1724                    modified,
1725                    len: 0
1726                }) if
1727                    accessed <= foo_metadata.accessed &&
1728                    created <= foo_metadata.created &&
1729                    modified > foo_metadata.modified
1730            ),
1731            "the modified time of the parent is updated when file is renamed \n{:?}\n{:?}",
1732            fs.metadata(path!("/")),
1733            foo_metadata,
1734        );
1735    }
1736
1737    #[tokio::test]
1738    async fn test_remove_file() {
1739        let fs = FileSystem::default();
1740
1741        assert!(
1742            fs.new_open_options()
1743                .write(true)
1744                .create_new(true)
1745                .open(path!("/foo.txt"))
1746                .is_ok(),
1747            "creating a new file",
1748        );
1749
1750        {
1751            let fs_inner = fs.inner.read().unwrap();
1752
1753            assert_eq!(fs_inner.storage.len(), 2, "storage has all files");
1754            assert!(
1755                matches!(
1756                    fs_inner.storage.get(ROOT_INODE),
1757                    Some(Node::Directory(DirectoryNode {
1758                        inode: ROOT_INODE,
1759                        name,
1760                        children,
1761                        ..
1762                    })) if name == "/" && children == &[1]
1763                ),
1764                "`/` contains `foo.txt`",
1765            );
1766            assert!(
1767                matches!(
1768                    fs_inner.storage.get(1),
1769                    Some(Node::File(FileNode {
1770                        inode: 1,
1771                        name,
1772                        ..
1773                    })) if name == "foo.txt"
1774                ),
1775                "`foo.txt` exists and is a file",
1776            );
1777        }
1778
1779        assert_eq!(
1780            fs.remove_file(path!("/foo.txt")),
1781            Ok(()),
1782            "removing a file that exists",
1783        );
1784
1785        {
1786            let fs_inner = fs.inner.read().unwrap();
1787
1788            assert_eq!(fs_inner.storage.len(), 1, "storage no longer has the file");
1789            assert!(
1790                matches!(
1791                    fs_inner.storage.get(ROOT_INODE),
1792                    Some(Node::Directory(DirectoryNode {
1793                        inode: ROOT_INODE,
1794                        name,
1795                        children,
1796                        ..
1797                    })) if name == "/" && children.is_empty()
1798                ),
1799                "`/` is empty",
1800            );
1801        }
1802
1803        assert_eq!(
1804            fs.remove_file(path!("/foo.txt")),
1805            Err(FsError::EntryNotFound),
1806            "removing a file that exists",
1807        );
1808    }
1809
1810    #[tokio::test]
1811    async fn test_readdir() {
1812        let fs = FileSystem::default();
1813
1814        assert_eq!(fs.create_dir(path!("/foo")), Ok(()), "creating `foo`");
1815        assert_eq!(fs.create_dir(path!("/foo/sub")), Ok(()), "creating `sub`");
1816        assert_eq!(fs.create_dir(path!("/bar")), Ok(()), "creating `bar`");
1817        assert_eq!(fs.create_dir(path!("/baz")), Ok(()), "creating `bar`");
1818        assert!(
1819            fs.new_open_options()
1820                .write(true)
1821                .create_new(true)
1822                .open(path!("/a.txt"))
1823                .is_ok(),
1824            "creating `a.txt`",
1825        );
1826        assert!(
1827            fs.new_open_options()
1828                .write(true)
1829                .create_new(true)
1830                .open(path!("/b.txt"))
1831                .is_ok(),
1832            "creating `b.txt`",
1833        );
1834
1835        let readdir = fs.read_dir(path!("/"));
1836
1837        assert!(readdir.is_ok(), "reading the directory `/`");
1838
1839        let mut readdir = readdir.unwrap();
1840
1841        assert!(
1842            matches!(
1843                readdir.next(),
1844                Some(Ok(DirEntry {
1845                    path,
1846                    metadata: Ok(Metadata { ft, .. }),
1847                }))
1848                    if path.as_path() == path!("/foo") && ft.is_dir()
1849            ),
1850            "checking entry #1",
1851        );
1852        assert!(
1853            matches!(
1854                readdir.next(),
1855                Some(Ok(DirEntry {
1856                    path,
1857                    metadata: Ok(Metadata { ft, .. }),
1858                }))
1859                    if path.as_path() == path!("/bar") && ft.is_dir()
1860            ),
1861            "checking entry #2",
1862        );
1863        assert!(
1864            matches!(
1865                readdir.next(),
1866                Some(Ok(DirEntry {
1867                    path,
1868                    metadata: Ok(Metadata { ft, .. }),
1869                }))
1870                    if path.as_path() == path!("/baz") && ft.is_dir()
1871            ),
1872            "checking entry #3",
1873        );
1874        assert!(
1875            matches!(
1876                readdir.next(),
1877                Some(Ok(DirEntry {
1878                    path,
1879                    metadata: Ok(Metadata { ft, .. }),
1880                }))
1881                    if path.as_path() == path!("/a.txt") && ft.is_file()
1882            ),
1883            "checking entry #4",
1884        );
1885        assert!(
1886            matches!(
1887                readdir.next(),
1888                Some(Ok(DirEntry {
1889                    path,
1890                    metadata: Ok(Metadata { ft, .. }),
1891                }))
1892                    if path.as_path() == path!("/b.txt") && ft.is_file()
1893            ),
1894            "checking entry #5",
1895        );
1896        assert!(readdir.next().is_none(), "no more entries");
1897    }
1898
1899    #[tokio::test]
1900    async fn test_canonicalize() {
1901        let fs = FileSystem::default();
1902
1903        assert_eq!(fs.create_dir(path!("/foo")), Ok(()), "creating `foo`");
1904        assert_eq!(fs.create_dir(path!("/foo/bar")), Ok(()), "creating `bar`");
1905        assert_eq!(
1906            fs.create_dir(path!("/foo/bar/baz")),
1907            Ok(()),
1908            "creating `baz`",
1909        );
1910        assert_eq!(
1911            fs.create_dir(path!("/foo/bar/baz/qux")),
1912            Ok(()),
1913            "creating `qux`",
1914        );
1915        assert!(
1916            fs.new_open_options()
1917                .write(true)
1918                .create_new(true)
1919                .open(path!("/foo/bar/baz/qux/hello.txt"))
1920                .is_ok(),
1921            "creating `hello.txt`",
1922        );
1923
1924        let fs_inner = fs.inner.read().unwrap();
1925
1926        assert_eq!(
1927            fs_inner
1928                .canonicalize(path!("/"))
1929                .map(|(a, b)| (a, b.unwrap())),
1930            Ok((path!(buf "/"), ROOT_INODE)),
1931            "canonicalizing `/`",
1932        );
1933        assert_eq!(
1934            fs_inner
1935                .canonicalize(path!("foo"))
1936                .map(|(a, b)| (a, b.unwrap())),
1937            Err(FsError::InvalidInput),
1938            "canonicalizing `foo`",
1939        );
1940        assert_eq!(
1941            fs_inner
1942                .canonicalize(path!("/././././foo/"))
1943                .map(|(a, b)| (a, b.unwrap())),
1944            Ok((path!(buf "/foo"), 1)),
1945            "canonicalizing `/././././foo/`",
1946        );
1947        assert_eq!(
1948            fs_inner
1949                .canonicalize(path!("/foo/bar//"))
1950                .map(|(a, b)| (a, b.unwrap())),
1951            Ok((path!(buf "/foo/bar"), 2)),
1952            "canonicalizing `/foo/bar//`",
1953        );
1954        assert_eq!(
1955            fs_inner
1956                .canonicalize(path!("/foo/bar/../bar"))
1957                .map(|(a, b)| (a, b.unwrap())),
1958            Ok((path!(buf "/foo/bar"), 2)),
1959            "canonicalizing `/foo/bar/../bar`",
1960        );
1961        assert_eq!(
1962            fs_inner
1963                .canonicalize(path!("/foo/bar/../.."))
1964                .map(|(a, b)| (a, b.unwrap())),
1965            Ok((path!(buf "/"), ROOT_INODE)),
1966            "canonicalizing `/foo/bar/../..`",
1967        );
1968        assert_eq!(
1969            fs_inner
1970                .canonicalize(path!("/foo/bar/../../.."))
1971                .map(|(a, b)| (a, b.unwrap())),
1972            Err(FsError::InvalidInput),
1973            "canonicalizing `/foo/bar/../../..`",
1974        );
1975        assert_eq!(
1976            fs_inner
1977                .canonicalize(path!("C:/foo/"))
1978                .map(|(a, b)| (a, b.unwrap())),
1979            Err(FsError::InvalidInput),
1980            "canonicalizing `C:/foo/`",
1981        );
1982        assert_eq!(
1983            fs_inner
1984                .canonicalize(path!(
1985                    "/foo/./../foo/bar/../../foo/bar/./baz/./../baz/qux/../../baz/./qux/hello.txt"
1986                ))
1987                .map(|(a, b)| (a, b.unwrap())),
1988            Ok((path!(buf "/foo/bar/baz/qux/hello.txt"), 5)),
1989            "canonicalizing a crazily stupid path name",
1990        );
1991    }
1992
1993    #[tokio::test]
1994    #[ignore = "Not yet supported. See https://github.com/wasmerio/wasmer/issues/3678"]
1995    async fn mount_to_overlapping_directories() {
1996        let top_level = FileSystem::default();
1997        ops::touch(&top_level, "/file.txt").unwrap();
1998        let nested = FileSystem::default();
1999        ops::touch(&nested, "/another-file.txt").unwrap();
2000        let top_level: Arc<dyn crate::FileSystem + Send + Sync> = Arc::new(top_level);
2001        let nested: Arc<dyn crate::FileSystem + Send + Sync> = Arc::new(nested);
2002
2003        let fs = FileSystem::default();
2004        fs.mount("/top-level".into(), &top_level, "/".into())
2005            .unwrap();
2006        fs.mount("/top-level/nested".into(), &nested, "/".into())
2007            .unwrap();
2008
2009        assert!(ops::is_dir(&fs, "/top-level"));
2010        assert!(ops::is_file(&fs, "/top-level/file.txt"));
2011        assert!(ops::is_dir(&fs, "/top-level/nested"));
2012        assert!(ops::is_file(&fs, "/top-level/nested/another-file.txt"));
2013    }
2014
2015    #[tokio::test]
2016    async fn read_dir_rebases_mount_paths() {
2017        let mounted = FileSystem::default();
2018        ops::touch(&mounted, "/file.txt").unwrap();
2019        let mounted: Arc<dyn crate::FileSystem + Send + Sync> = Arc::new(mounted);
2020
2021        let fs = FileSystem::default();
2022        fs.mount("/mnt".into(), &mounted, "/".into()).unwrap();
2023
2024        let entries: Vec<_> = fs
2025            .read_dir(Path::new("/mnt"))
2026            .unwrap()
2027            .map(|e| e.unwrap().path)
2028            .collect();
2029
2030        assert_eq!(entries, vec![Path::new("/mnt/file.txt").to_path_buf()]);
2031    }
2032
2033    #[tokio::test]
2034    async fn test_merge_flat() {
2035        let main = FileSystem::default();
2036
2037        let other = FileSystem::default();
2038        crate::ops::create_dir_all(&other, "/a/x").unwrap();
2039        other
2040            .insert_ro_file(Path::new("/a/x/a.txt"), OwnedBuffer::from_static(b"a"))
2041            .unwrap();
2042        other
2043            .insert_ro_file(Path::new("/a/x/b.txt"), OwnedBuffer::from_static(b"b"))
2044            .unwrap();
2045        other
2046            .insert_ro_file(Path::new("/a/x/c.txt"), OwnedBuffer::from_static(b"c"))
2047            .unwrap();
2048
2049        let out = other.read_dir(Path::new("/")).unwrap();
2050        dbg!(&out);
2051
2052        let other: Arc<dyn crate::FileSystem + Send + Sync> = Arc::new(other);
2053
2054        main.mount_directory_entries(Path::new("/"), &other, Path::new("/a"))
2055            .unwrap();
2056
2057        let mut buf = Vec::new();
2058
2059        let mut f = main
2060            .new_open_options()
2061            .read(true)
2062            .open(Path::new("/x/a.txt"))
2063            .unwrap();
2064        f.read_to_end(&mut buf).await.unwrap();
2065
2066        assert_eq!(buf, b"a");
2067    }
2068}