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 create_symlink(&self, source: &Path, target: &Path) -> Result<()> {
464        self.create_symlink(source, target)
465    }
466
467    fn remove_dir(&self, path: &Path) -> Result<()> {
468        let (inode_of_parent, position, inode_of_directory) = {
469            // Read lock.
470            let guard = self.inner.read().map_err(|_| FsError::Lock)?;
471
472            // Canonicalize the path.
473            let (path, _) = guard.canonicalize(path)?;
474
475            // Check the path has a parent.
476            let parent_of_path = path.parent().ok_or(FsError::BaseNotDirectory)?;
477
478            // Check the directory name.
479            let name_of_directory = path
480                .file_name()
481                .ok_or(FsError::InvalidInput)?
482                .to_os_string();
483
484            // Find the parent inode.
485            let inode_of_parent = match guard.inode_of_parent(parent_of_path)? {
486                InodeResolution::Found(a) => a,
487                InodeResolution::Redirect(fs, mut parent_path) => {
488                    drop(guard);
489                    parent_path.push(name_of_directory);
490                    return fs.remove_dir(parent_path.as_path());
491                }
492            };
493
494            // Get the child index to remove in the parent node, in
495            // addition to the inode of the directory to remove.
496            let (position, inode_of_directory) = guard
497                .as_parent_get_position_and_inode_of_directory(
498                    inode_of_parent,
499                    &name_of_directory,
500                    DirectoryMustBeEmpty::Yes,
501                )?;
502
503            (inode_of_parent, position, inode_of_directory)
504        };
505
506        let inode_of_directory = match inode_of_directory {
507            InodeResolution::Found(a) => a,
508            InodeResolution::Redirect(fs, path) => {
509                return fs.remove_dir(path.as_path());
510            }
511        };
512
513        {
514            // Write lock.
515            let mut fs = self.inner.write().map_err(|_| FsError::Lock)?;
516
517            // Remove the directory from the storage.
518            fs.storage.remove(inode_of_directory);
519
520            // Remove the child from the parent directory.
521            fs.remove_child_from_node(inode_of_parent, position)?;
522        }
523
524        Ok(())
525    }
526
527    fn rename<'a>(&'a self, from: &'a Path, to: &'a Path) -> BoxFuture<'a, Result<()>> {
528        Box::pin(async move {
529            let name_of_to;
530
531            // Read lock.
532            let (name_of_from, inode_of_from_parent, name_of_to, inode_of_to_parent) = {
533                let fs = self.inner.read().map_err(|_| FsError::Lock)?;
534
535                let from = fs.canonicalize_without_inode(from)?;
536                let to = fs.canonicalize_without_inode(to)?;
537
538                // Check the paths have parents.
539                let parent_of_from = from.parent().ok_or(FsError::BaseNotDirectory)?;
540                let parent_of_to = to.parent().ok_or(FsError::BaseNotDirectory)?;
541
542                // Check the names.
543                let name_of_from = from
544                    .file_name()
545                    .ok_or(FsError::InvalidInput)?
546                    .to_os_string();
547                name_of_to = to.file_name().ok_or(FsError::InvalidInput)?.to_os_string();
548
549                // Find the parent inodes.
550                let inode_of_from_parent = match fs.inode_of_parent(parent_of_from)? {
551                    InodeResolution::Found(a) => Either::Left(a),
552                    InodeResolution::Redirect(fs, mut path) => {
553                        path.push(&name_of_from);
554                        Either::Right((fs, path))
555                    }
556                };
557                let inode_of_to_parent = match fs.inode_of_parent(parent_of_to)? {
558                    InodeResolution::Found(a) => Either::Left(a),
559                    InodeResolution::Redirect(fs, mut path) => {
560                        path.push(&name_of_to);
561                        Either::Right((fs, path))
562                    }
563                };
564
565                (
566                    name_of_from,
567                    inode_of_from_parent,
568                    name_of_to,
569                    inode_of_to_parent,
570                )
571            };
572
573            match (inode_of_from_parent, inode_of_to_parent) {
574                // Rename within this MemFS instance
575                (Either::Left(inode_of_from_parent), Either::Left(inode_of_to_parent)) => {
576                    let fs = self.inner.read().map_err(|_| FsError::Lock)?;
577
578                    // Find the inode of the dest file if it exists
579                    let maybe_position_and_inode_of_file = fs
580                        .as_parent_get_position_and_inode_of_file(
581                            inode_of_to_parent,
582                            &name_of_to,
583                        )?;
584
585                    // Get the child indexes to update in the parent nodes, in
586                    // addition to the inode of the directory to update.
587                    let (position_of_from, inode) = fs
588                        .as_parent_get_position_and_inode(inode_of_from_parent, &name_of_from)?
589                        .ok_or(FsError::EntryNotFound)?;
590
591                    let (
592                        (position_of_from, inode, inode_of_from_parent),
593                        (inode_of_to_parent, name_of_to),
594                        inode_dest,
595                    ) = (
596                        (position_of_from, inode, inode_of_from_parent),
597                        (inode_of_to_parent, name_of_to),
598                        maybe_position_and_inode_of_file,
599                    );
600
601                    let inode = match inode {
602                        InodeResolution::Found(a) => a,
603                        InodeResolution::Redirect(..) => {
604                            return Err(FsError::InvalidInput);
605                        }
606                    };
607
608                    drop(fs);
609
610                    {
611                        // Write lock.
612                        let mut fs = self.inner.write().map_err(|_| FsError::Lock)?;
613
614                        if let Some((position, inode_of_file)) = inode_dest {
615                            // Remove the file from the storage.
616                            match inode_of_file {
617                                InodeResolution::Found(inode_of_file) => {
618                                    fs.storage.remove(inode_of_file);
619                                }
620                                InodeResolution::Redirect(..) => {
621                                    return Err(FsError::InvalidInput);
622                                }
623                            }
624
625                            fs.remove_child_from_node(inode_of_to_parent, position)?;
626                        }
627
628                        // Update the file name, and update the modified time.
629                        fs.update_node_name(inode, name_of_to)?;
630
631                        // The parents are different. Let's update them.
632                        if inode_of_from_parent != inode_of_to_parent {
633                            // Remove the file from its parent, and update the
634                            // modified time.
635                            fs.remove_child_from_node(inode_of_from_parent, position_of_from)?;
636
637                            // Add the file to its new parent, and update the modified
638                            // time.
639                            fs.add_child_to_node(inode_of_to_parent, inode)?;
640                        }
641                        // Otherwise, we need to at least update the modified time of the parent.
642                        else {
643                            let mut inode = fs.storage.get_mut(inode_of_from_parent);
644                            match inode.as_mut() {
645                                Some(Node::Directory(node)) => node.metadata.modified = time(),
646                                Some(Node::ArcDirectory(node)) => node.metadata.modified = time(),
647                                _ => return Err(FsError::UnknownError),
648                            }
649                        }
650                    }
651
652                    Ok(())
653                }
654
655                // Rename within the same mounted FS instance
656                (Either::Right((from_fs, from_path)), Either::Right((to_fs, to_path)))
657                    if Arc::ptr_eq(&from_fs, &to_fs) =>
658                {
659                    let same_fs = from_fs;
660                    same_fs.rename(&from_path, &to_path).await
661                }
662
663                // Rename across file systems; we need to do a create and a delete
664                _ => crate::ops::move_across_filesystems(self, self, from, to).await,
665            }
666        })
667    }
668
669    fn metadata(&self, path: &Path) -> Result<Metadata> {
670        // Read lock.
671        let guard = self.inner.read().map_err(|_| FsError::Lock)?;
672        match guard.inode_of(path)? {
673            InodeResolution::Found(inode) => Ok(guard
674                .storage
675                .get(inode)
676                .ok_or(FsError::UnknownError)?
677                .metadata()
678                .clone()),
679            InodeResolution::Redirect(fs, path) => {
680                drop(guard);
681                fs.metadata(path.as_path())
682            }
683        }
684    }
685
686    fn symlink_metadata(&self, path: &Path) -> Result<Metadata> {
687        // Read lock.
688        let guard = self.inner.read().map_err(|_| FsError::Lock)?;
689        match guard.inode_of(path)? {
690            InodeResolution::Found(inode) => Ok(guard
691                .storage
692                .get(inode)
693                .ok_or(FsError::UnknownError)?
694                .metadata()
695                .clone()),
696            InodeResolution::Redirect(fs, path) => {
697                drop(guard);
698                fs.symlink_metadata(path.as_path())
699            }
700        }
701    }
702
703    fn remove_file(&self, path: &Path) -> Result<()> {
704        let (inode_of_parent, position, inode_of_file) = {
705            // Read lock.
706            let guard = self.inner.read().map_err(|_| FsError::Lock)?;
707
708            // Canonicalize the path.
709            let path = guard.canonicalize_without_inode(path)?;
710
711            // Check the path has a parent.
712            let parent_of_path = path.parent().ok_or(FsError::BaseNotDirectory)?;
713
714            // Check the file name.
715            let name_of_file = path
716                .file_name()
717                .ok_or(FsError::InvalidInput)?
718                .to_os_string();
719
720            // Find the parent inode.
721            let inode_of_parent = match guard.inode_of_parent(parent_of_path)? {
722                InodeResolution::Found(a) => a,
723                InodeResolution::Redirect(fs, mut parent_path) => {
724                    parent_path.push(name_of_file);
725                    return fs.remove_file(parent_path.as_path());
726                }
727            };
728
729            // Find the inode of the file if it exists, along with its position.
730            let maybe_position_and_inode_of_file =
731                guard.as_parent_get_position_and_inode_of_file(inode_of_parent, &name_of_file)?;
732
733            match maybe_position_and_inode_of_file {
734                Some((position, inode_of_file)) => (inode_of_parent, position, inode_of_file),
735                None => return Err(FsError::EntryNotFound),
736            }
737        };
738
739        let inode_of_file = match inode_of_file {
740            InodeResolution::Found(a) => a,
741            InodeResolution::Redirect(fs, path) => {
742                return fs.remove_file(path.as_path());
743            }
744        };
745
746        {
747            // Write lock.
748            let mut fs = self.inner.write().map_err(|_| FsError::Lock)?;
749
750            // Remove the file from the storage.
751            fs.storage.remove(inode_of_file);
752
753            // Remove the child from the parent directory.
754            fs.remove_child_from_node(inode_of_parent, position)?;
755        }
756
757        Ok(())
758    }
759
760    fn new_open_options(&self) -> OpenOptions<'_> {
761        OpenOptions::new(self)
762    }
763}
764
765impl fmt::Debug for FileSystem {
766    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
767        let fs: &FileSystemInner = &self.inner.read().unwrap();
768
769        fs.fmt(formatter)
770    }
771}
772
773/// The core of the file system. It contains a collection of `Node`s,
774/// indexed by their respective `Inode` in a slab.
775pub(super) struct FileSystemInner {
776    pub(super) storage: Slab<Node>,
777    pub(super) backing_offload: Option<OffloadBackingStore>,
778    pub(super) limiter: Option<crate::limiter::DynFsMemoryLimiter>,
779}
780
781#[derive(Debug)]
782pub(super) enum InodeResolution {
783    Found(Inode),
784    Redirect(Arc<dyn crate::FileSystem + Send + Sync + 'static>, PathBuf),
785}
786
787impl InodeResolution {
788    #[allow(dead_code)]
789    pub fn unwrap(&self) -> Inode {
790        match self {
791            Self::Found(a) => *a,
792            Self::Redirect(..) => {
793                panic!("failed to unwrap the inode as the resolution is a redirect");
794            }
795        }
796    }
797}
798
799impl FileSystemInner {
800    /// Get the inode associated to a path if it exists.
801    pub(super) fn inode_of(&self, path: &Path) -> Result<InodeResolution> {
802        // SAFETY: The root node always exists, so it's safe to unwrap here.
803        let mut node = self.storage.get(ROOT_INODE).unwrap();
804        let mut components = path.components();
805
806        match components.next() {
807            Some(Component::RootDir) => {}
808            _ => return Err(FsError::BaseNotDirectory),
809        }
810
811        while let Some(component) = components.next() {
812            node = match node {
813                Node::Directory(DirectoryNode { children, .. }) => children
814                    .iter()
815                    .filter_map(|inode| self.storage.get(*inode))
816                    .find(|node| node.name() == component.as_os_str())
817                    .ok_or(FsError::EntryNotFound)?,
818                Node::ArcDirectory(ArcDirectoryNode {
819                    fs, path: fs_path, ..
820                }) => {
821                    let mut path = fs_path.clone();
822                    path.push(PathBuf::from(component.as_os_str()));
823                    for component in components.by_ref() {
824                        path.push(PathBuf::from(component.as_os_str()));
825                    }
826                    return Ok(InodeResolution::Redirect(fs.clone(), path));
827                }
828                _ => return Err(FsError::BaseNotDirectory),
829            };
830        }
831
832        Ok(InodeResolution::Found(node.inode()))
833    }
834
835    /// Get the inode associated to a “parent path”. The returned
836    /// inode necessarily represents a directory.
837    pub(super) fn inode_of_parent(&self, parent_path: &Path) -> Result<InodeResolution> {
838        match self.inode_of(parent_path)? {
839            InodeResolution::Found(inode_of_parent) => {
840                // Ensure it is a directory.
841                match self.storage.get(inode_of_parent) {
842                    Some(Node::Directory(DirectoryNode { .. })) => {
843                        Ok(InodeResolution::Found(inode_of_parent))
844                    }
845                    Some(Node::ArcDirectory(ArcDirectoryNode { fs, path, .. })) => {
846                        Ok(InodeResolution::Redirect(fs.clone(), path.clone()))
847                    }
848                    _ => Err(FsError::BaseNotDirectory),
849                }
850            }
851            InodeResolution::Redirect(fs, path) => Ok(InodeResolution::Redirect(fs, path)),
852        }
853    }
854
855    /// From the inode of a parent node (so, a directory), returns the
856    /// child index of `name_of_directory` along with its inode.
857    pub(super) fn as_parent_get_position_and_inode_of_directory(
858        &self,
859        inode_of_parent: Inode,
860        name_of_directory: &OsString,
861        directory_must_be_empty: DirectoryMustBeEmpty,
862    ) -> Result<(usize, InodeResolution)> {
863        match self.storage.get(inode_of_parent) {
864            Some(Node::Directory(DirectoryNode { children, .. })) => children
865                .iter()
866                .enumerate()
867                .filter_map(|(nth, inode)| self.storage.get(*inode).map(|node| (nth, node)))
868                .find_map(|(nth, node)| match node {
869                    Node::Directory(DirectoryNode {
870                        inode,
871                        name,
872                        children,
873                        ..
874                    }) if name.as_os_str() == name_of_directory => {
875                        if directory_must_be_empty.no() || children.is_empty() {
876                            Some(Ok((nth, InodeResolution::Found(*inode))))
877                        } else {
878                            Some(Err(FsError::DirectoryNotEmpty))
879                        }
880                    }
881                    Node::ArcDirectory(ArcDirectoryNode { name, fs, path, .. })
882                        if name.as_os_str() == name_of_directory =>
883                    {
884                        Some(Ok((0, InodeResolution::Redirect(fs.clone(), path.clone()))))
885                    }
886                    _ => None,
887                })
888                .ok_or(FsError::InvalidInput)
889                .and_then(identity), // flatten
890
891            Some(Node::ArcDirectory(ArcDirectoryNode {
892                fs, path: fs_path, ..
893            })) => {
894                let mut path = fs_path.clone();
895                path.push(name_of_directory);
896                Ok((0, InodeResolution::Redirect(fs.clone(), path)))
897            }
898
899            _ => Err(FsError::BaseNotDirectory),
900        }
901    }
902
903    /// From the inode of a parent node (so, a directory), returns the
904    /// child index of `name_of_file` along with its inode.
905    pub(super) fn as_parent_get_position_and_inode_of_file(
906        &self,
907        inode_of_parent: Inode,
908        name_of_file: &OsString,
909    ) -> Result<Option<(usize, InodeResolution)>> {
910        match self.storage.get(inode_of_parent) {
911            Some(Node::Directory(DirectoryNode { children, .. })) => children
912                .iter()
913                .enumerate()
914                .filter_map(|(nth, inode)| self.storage.get(*inode).map(|node| (nth, node)))
915                .find_map(|(nth, node)| match node {
916                    Node::File(FileNode { inode, name, .. })
917                    | Node::OffloadedFile(OffloadedFileNode { inode, name, .. })
918                    | Node::ReadOnlyFile(ReadOnlyFileNode { inode, name, .. })
919                    | Node::CustomFile(CustomFileNode { inode, name, .. })
920                    | Node::ArcFile(ArcFileNode { inode, name, .. })
921                    | Node::Symlink(SymlinkNode { inode, name, .. })
922                        if name.as_os_str() == name_of_file =>
923                    {
924                        Some(Some((nth, InodeResolution::Found(*inode))))
925                    }
926                    _ => None,
927                })
928                .or(Some(None))
929                .ok_or(FsError::InvalidInput),
930
931            Some(Node::ArcDirectory(ArcDirectoryNode {
932                fs, path: fs_path, ..
933            })) => {
934                let mut path = fs_path.clone();
935                path.push(name_of_file);
936                Ok(Some((0, InodeResolution::Redirect(fs.clone(), path))))
937            }
938
939            _ => Err(FsError::BaseNotDirectory),
940        }
941    }
942
943    /// From the inode of a parent node (so, a directory), returns the
944    /// child index of `name_of` along with its inode, whatever the
945    /// type of inode is (directory or file).
946    fn as_parent_get_position_and_inode(
947        &self,
948        inode_of_parent: Inode,
949        name_of: &OsString,
950    ) -> Result<Option<(usize, InodeResolution)>> {
951        match self.storage.get(inode_of_parent) {
952            Some(Node::Directory(DirectoryNode { children, .. })) => children
953                .iter()
954                .enumerate()
955                .filter_map(|(nth, inode)| self.storage.get(*inode).map(|node| (nth, node)))
956                .find_map(|(nth, node)| match node {
957                    Node::File(FileNode { inode, name, .. })
958                    | Node::OffloadedFile(OffloadedFileNode { inode, name, .. })
959                    | Node::Directory(DirectoryNode { inode, name, .. })
960                    | Node::ReadOnlyFile(ReadOnlyFileNode { inode, name, .. })
961                    | Node::CustomFile(CustomFileNode { inode, name, .. })
962                    | Node::ArcFile(ArcFileNode { inode, name, .. })
963                        if name.as_os_str() == name_of =>
964                    {
965                        Some(Some((nth, InodeResolution::Found(*inode))))
966                    }
967                    _ => None,
968                })
969                .or(Some(None))
970                .ok_or(FsError::InvalidInput),
971
972            Some(Node::ArcDirectory(ArcDirectoryNode {
973                fs, path: fs_path, ..
974            })) => {
975                let mut path = fs_path.clone();
976                path.push(name_of);
977                Ok(Some((0, InodeResolution::Redirect(fs.clone(), path))))
978            }
979
980            _ => Err(FsError::BaseNotDirectory),
981        }
982    }
983
984    /// Set a new name for the node represented by `inode`.
985    pub(super) fn update_node_name(&mut self, inode: Inode, new_name: OsString) -> Result<()> {
986        let node = self.storage.get_mut(inode).ok_or(FsError::UnknownError)?;
987
988        node.set_name(new_name);
989        node.metadata_mut().modified = time();
990
991        Ok(())
992    }
993
994    /// Add a child to a directory node represented by `inode`.
995    ///
996    /// This function also updates the modified time of the directory.
997    ///
998    /// # Safety
999    ///
1000    /// `inode` must represents an existing directory.
1001    pub(super) fn add_child_to_node(&mut self, inode: Inode, new_child: Inode) -> Result<()> {
1002        match self.storage.get_mut(inode) {
1003            Some(Node::Directory(DirectoryNode {
1004                children,
1005                metadata: Metadata { modified, .. },
1006                ..
1007            })) => {
1008                children.push(new_child);
1009                *modified = time();
1010
1011                Ok(())
1012            }
1013            _ => Err(FsError::UnknownError),
1014        }
1015    }
1016
1017    /// Remove the child at position `position` of a directory node
1018    /// represented by `inode`.
1019    ///
1020    /// This function also updates the modified time of the directory.
1021    ///
1022    /// # Safety
1023    ///
1024    /// `inode` must represents an existing directory.
1025    pub(super) fn remove_child_from_node(&mut self, inode: Inode, position: usize) -> Result<()> {
1026        match self.storage.get_mut(inode) {
1027            Some(Node::Directory(DirectoryNode {
1028                children,
1029                metadata: Metadata { modified, .. },
1030                ..
1031            })) => {
1032                children.remove(position);
1033                *modified = time();
1034
1035                Ok(())
1036            }
1037            _ => Err(FsError::UnknownError),
1038        }
1039    }
1040
1041    /// Canonicalize a path, i.e. try to resolve to a canonical,
1042    /// absolute form of the path with all intermediate components
1043    /// normalized:
1044    ///
1045    /// * A path must starts with a root (`/`),
1046    /// * A path can contain `..` or `.` components,
1047    /// * A path must not contain a Windows prefix (`C:` or `\\server`),
1048    /// * A normalized path exists in the file system.
1049    pub(super) fn canonicalize(&self, path: &Path) -> Result<(PathBuf, InodeResolution)> {
1050        let new_path = self.canonicalize_without_inode(path)?;
1051        let inode = self.inode_of(&new_path)?;
1052
1053        Ok((new_path, inode))
1054    }
1055
1056    /// Like `Self::canonicalize` but without returning the inode of
1057    /// the path, which means that there is no guarantee that the path
1058    /// exists in the file system.
1059    pub(super) fn canonicalize_without_inode(&self, path: &Path) -> Result<PathBuf> {
1060        let mut components = path.components();
1061
1062        match components.next() {
1063            Some(Component::RootDir) => {}
1064            _ => return Err(FsError::InvalidInput),
1065        }
1066
1067        let mut new_path = PathBuf::with_capacity(path.as_os_str().len());
1068        new_path.push("/");
1069
1070        for component in components {
1071            match component {
1072                // That's an error to get a `RootDir` a second time.
1073                Component::RootDir => return Err(FsError::UnknownError),
1074
1075                // Nothing to do on `new_path`.
1076                Component::CurDir => (),
1077
1078                // Pop the lastly inserted component on `new_path` if
1079                // any, otherwise it's an error.
1080                Component::ParentDir => {
1081                    if !new_path.pop() {
1082                        return Err(FsError::InvalidInput);
1083                    }
1084                }
1085
1086                // A normal
1087                Component::Normal(name) => {
1088                    new_path.push(name);
1089                }
1090
1091                // We don't support Windows path prefix.
1092                Component::Prefix(_) => return Err(FsError::InvalidInput),
1093            }
1094        }
1095
1096        Ok(new_path)
1097    }
1098}
1099
1100impl fmt::Debug for FileSystemInner {
1101    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1102        writeln!(
1103            formatter,
1104            "\n{inode:<8}    {ty:<4}    name",
1105            inode = "inode",
1106            ty = "type",
1107        )?;
1108
1109        fn debug(
1110            nodes: Vec<&Node>,
1111            slf: &FileSystemInner,
1112            formatter: &mut fmt::Formatter<'_>,
1113            indentation: usize,
1114        ) -> fmt::Result {
1115            for node in nodes {
1116                writeln!(
1117                    formatter,
1118                    "{inode:<8}    {ty:<4}   {indentation_symbol:indentation_width$}{name}",
1119                    inode = node.inode(),
1120                    ty = match node {
1121                        Node::File { .. } => "file",
1122                        Node::OffloadedFile { .. } => "offloaded-file",
1123                        Node::ReadOnlyFile { .. } => "ro-file",
1124                        Node::ArcFile { .. } => "arc-file",
1125                        Node::CustomFile { .. } => "custom-file",
1126                        Node::Symlink { .. } => "symlink",
1127                        Node::Directory { .. } => "dir",
1128                        Node::ArcDirectory { .. } => "arc-dir",
1129                    },
1130                    name = node.name().to_string_lossy(),
1131                    indentation_symbol = " ",
1132                    indentation_width = indentation * 2 + 1,
1133                )?;
1134
1135                if let Node::Directory(DirectoryNode { children, .. }) = node {
1136                    debug(
1137                        children
1138                            .iter()
1139                            .filter_map(|inode| slf.storage.get(*inode))
1140                            .collect(),
1141                        slf,
1142                        formatter,
1143                        indentation + 1,
1144                    )?;
1145                }
1146            }
1147
1148            Ok(())
1149        }
1150
1151        debug(
1152            vec![self.storage.get(ROOT_INODE).unwrap()],
1153            self,
1154            formatter,
1155            0,
1156        )
1157    }
1158}
1159
1160impl Default for FileSystemInner {
1161    fn default() -> Self {
1162        let time = time();
1163
1164        let mut slab = Slab::new();
1165        slab.insert(Node::Directory(DirectoryNode {
1166            inode: ROOT_INODE,
1167            name: OsString::from("/"),
1168            children: Vec::new(),
1169            metadata: Metadata {
1170                ft: FileType {
1171                    dir: true,
1172                    ..Default::default()
1173                },
1174                accessed: time,
1175                created: time,
1176                modified: time,
1177                len: 0,
1178            },
1179        }));
1180
1181        Self {
1182            storage: slab,
1183            backing_offload: None,
1184            limiter: None,
1185        }
1186    }
1187}
1188
1189#[allow(dead_code)] // The `No` variant.
1190pub(super) enum DirectoryMustBeEmpty {
1191    Yes,
1192    No,
1193}
1194
1195impl DirectoryMustBeEmpty {
1196    pub(super) fn yes(&self) -> bool {
1197        matches!(self, Self::Yes)
1198    }
1199
1200    pub(super) fn no(&self) -> bool {
1201        !self.yes()
1202    }
1203}
1204
1205#[cfg(test)]
1206mod test_filesystem {
1207    use std::{path::Path, sync::Arc};
1208
1209    use shared_buffer::OwnedBuffer;
1210    use tokio::io::AsyncReadExt;
1211
1212    use crate::{DirEntry, FileSystem as FS, FileType, FsError, mem_fs::*, ops};
1213
1214    macro_rules! path {
1215        ($path:expr) => {
1216            std::path::Path::new($path)
1217        };
1218
1219        (buf $path:expr) => {
1220            std::path::PathBuf::from($path)
1221        };
1222    }
1223
1224    #[tokio::test]
1225    async fn test_new_filesystem() {
1226        let fs = FileSystem::default();
1227        let fs_inner = fs.inner.read().unwrap();
1228
1229        assert_eq!(fs_inner.storage.len(), 1, "storage has a root");
1230        assert!(
1231            matches!(
1232                fs_inner.storage.get(ROOT_INODE),
1233                Some(Node::Directory(DirectoryNode {
1234                    inode: ROOT_INODE,
1235                    name,
1236                    children,
1237                    ..
1238                })) if name == "/" && children.is_empty(),
1239            ),
1240            "storage has a well-defined root",
1241        );
1242    }
1243
1244    #[tokio::test]
1245    async fn test_create_dir() {
1246        let fs = FileSystem::default();
1247
1248        assert_eq!(
1249            fs.create_dir(path!("/")),
1250            Err(FsError::AlreadyExists),
1251            "creating the root which already exists",
1252        );
1253
1254        assert_eq!(fs.create_dir(path!("/foo")), Ok(()), "creating a directory");
1255
1256        {
1257            let fs_inner = fs.inner.read().unwrap();
1258            assert_eq!(
1259                fs_inner.storage.len(),
1260                2,
1261                "storage contains the new directory"
1262            );
1263            assert!(
1264                matches!(
1265                    fs_inner.storage.get(ROOT_INODE),
1266                    Some(Node::Directory(DirectoryNode {
1267                        inode: ROOT_INODE,
1268                        name,
1269                        children,
1270                        ..
1271                    })) if name == "/" && children == &[1]
1272                ),
1273                "the root is updated and well-defined",
1274            );
1275            assert!(
1276                matches!(
1277                    fs_inner.storage.get(1),
1278                    Some(Node::Directory(DirectoryNode {
1279                        inode: 1,
1280                        name,
1281                        children,
1282                        ..
1283                    })) if name == "foo" && children.is_empty(),
1284                ),
1285                "the new directory is well-defined",
1286            );
1287        }
1288
1289        assert_eq!(
1290            fs.create_dir(path!("/foo/bar")),
1291            Ok(()),
1292            "creating a sub-directory",
1293        );
1294
1295        {
1296            let fs_inner = fs.inner.read().unwrap();
1297            assert_eq!(
1298                fs_inner.storage.len(),
1299                3,
1300                "storage contains the new sub-directory",
1301            );
1302            assert!(
1303                matches!(
1304                    fs_inner.storage.get(ROOT_INODE),
1305                    Some(Node::Directory(DirectoryNode {
1306                        inode: ROOT_INODE,
1307                        name,
1308                        children,
1309                        ..
1310                    })) if name == "/" && children == &[1]
1311                ),
1312                "the root is updated again and well-defined",
1313            );
1314            assert!(
1315                matches!(
1316                    fs_inner.storage.get(1),
1317                    Some(Node::Directory(DirectoryNode {
1318                        inode: 1,
1319                        name,
1320                        children,
1321                        ..
1322                    })) if name == "foo" && children == &[2]
1323                ),
1324                "the new directory is updated and well-defined",
1325            );
1326            assert!(
1327                matches!(
1328                    fs_inner.storage.get(2),
1329                    Some(Node::Directory(DirectoryNode {
1330                        inode: 2,
1331                        name,
1332                        children,
1333                        ..
1334                    })) if name == "bar" && children.is_empty()
1335                ),
1336                "the new directory is well-defined",
1337            );
1338        }
1339    }
1340
1341    #[tokio::test]
1342    async fn test_remove_dir() {
1343        let fs = FileSystem::default();
1344
1345        assert_eq!(
1346            fs.remove_dir(path!("/")),
1347            Err(FsError::BaseNotDirectory),
1348            "removing a directory that has no parent",
1349        );
1350
1351        assert_eq!(
1352            fs.remove_dir(path!("/foo")),
1353            Err(FsError::EntryNotFound),
1354            "cannot remove a directory that doesn't exist",
1355        );
1356
1357        assert_eq!(fs.create_dir(path!("/foo")), Ok(()), "creating a directory");
1358
1359        assert_eq!(
1360            fs.create_dir(path!("/foo/bar")),
1361            Ok(()),
1362            "creating a sub-directory",
1363        );
1364
1365        {
1366            let fs_inner = fs.inner.read().unwrap();
1367            assert_eq!(
1368                fs_inner.storage.len(),
1369                3,
1370                "storage contains all the directories",
1371            );
1372        }
1373
1374        assert_eq!(
1375            fs.remove_dir(path!("/foo")),
1376            Err(FsError::DirectoryNotEmpty),
1377            "removing a directory that has children",
1378        );
1379
1380        assert_eq!(
1381            fs.remove_dir(path!("/foo/bar")),
1382            Ok(()),
1383            "removing a sub-directory",
1384        );
1385
1386        assert_eq!(fs.remove_dir(path!("/foo")), Ok(()), "removing a directory");
1387
1388        {
1389            let fs_inner = fs.inner.read().unwrap();
1390            assert_eq!(
1391                fs_inner.storage.len(),
1392                1,
1393                "storage contains all the directories",
1394            );
1395        }
1396    }
1397
1398    #[tokio::test]
1399    async fn test_rename() {
1400        let fs = FileSystem::default();
1401
1402        let fs2 = FileSystem::default();
1403        fs2.create_dir(path!("/boz")).unwrap();
1404        let arc_fs2: Arc<dyn crate::FileSystem + Send + Sync> = Arc::new(fs2);
1405
1406        assert_eq!(
1407            fs.rename(path!("/"), path!("/bar")).await,
1408            Err(FsError::BaseNotDirectory),
1409            "renaming a directory that has no parent",
1410        );
1411        assert_eq!(
1412            fs.rename(path!("/foo"), path!("/")).await,
1413            Err(FsError::BaseNotDirectory),
1414            "renaming to a directory that has no parent",
1415        );
1416
1417        assert_eq!(fs.create_dir(path!("/foo")), Ok(()));
1418        assert_eq!(fs.create_dir(path!("/foo/qux")), Ok(()));
1419
1420        assert_eq!(
1421            fs.rename(path!("/foo"), path!("/bar/baz")).await,
1422            Err(FsError::EntryNotFound),
1423            "renaming to a directory that has parent that doesn't exist",
1424        );
1425
1426        assert_eq!(fs.create_dir(path!("/bar")), Ok(()));
1427
1428        assert!(
1429            fs.new_open_options()
1430                .write(true)
1431                .create_new(true)
1432                .open(path!("/bar/hello1.txt"))
1433                .is_ok(),
1434            "creating a new file (`hello1.txt`)",
1435        );
1436        assert!(
1437            fs.new_open_options()
1438                .write(true)
1439                .create_new(true)
1440                .open(path!("/bar/hello2.txt"))
1441                .is_ok(),
1442            "creating a new file (`hello2.txt`)",
1443        );
1444
1445        fs.mount(path!(buf "/mnt"), &arc_fs2, path!(buf "/"))
1446            .unwrap();
1447
1448        {
1449            let fs_inner = fs.inner.read().unwrap();
1450
1451            assert_eq!(fs_inner.storage.len(), 7, "storage has all files");
1452            assert!(
1453                matches!(
1454                    fs_inner.storage.get(ROOT_INODE),
1455                    Some(Node::Directory(DirectoryNode {
1456                        inode: ROOT_INODE,
1457                        name,
1458                        children,
1459                        ..
1460                    })) if name == "/" && children == &[1, 3, 6]
1461                ),
1462                "`/` contains `foo` and `bar` and `mnt`",
1463            );
1464            assert!(
1465                matches!(
1466                    fs_inner.storage.get(1),
1467                    Some(Node::Directory(DirectoryNode {
1468                        inode: 1,
1469                        name,
1470                        children,
1471                        ..
1472                    })) if name == "foo" && children == &[2]
1473                ),
1474                "`foo` contains `qux`",
1475            );
1476            assert!(
1477                matches!(
1478                    fs_inner.storage.get(2),
1479                    Some(Node::Directory(DirectoryNode {
1480                        inode: 2,
1481                        name,
1482                        children,
1483                        ..
1484                    })) if name == "qux" && children.is_empty()
1485                ),
1486                "`qux` is empty",
1487            );
1488            assert!(
1489                matches!(
1490                    fs_inner.storage.get(3),
1491                    Some(Node::Directory(DirectoryNode {
1492                        inode: 3,
1493                        name,
1494                        children,
1495                        ..
1496                    })) if name == "bar" && children == &[4, 5]
1497                ),
1498                "`bar` is contains `hello.txt`",
1499            );
1500            assert!(
1501                matches!(
1502                    fs_inner.storage.get(4),
1503                    Some(Node::File(FileNode {
1504                        inode: 4,
1505                        name,
1506                        ..
1507                    })) if name == "hello1.txt"
1508                ),
1509                "`hello1.txt` exists",
1510            );
1511            assert!(
1512                matches!(
1513                    fs_inner.storage.get(5),
1514                    Some(Node::File(FileNode {
1515                        inode: 5,
1516                        name,
1517                        ..
1518                    })) if name == "hello2.txt"
1519                ),
1520                "`hello2.txt` exists",
1521            );
1522        }
1523
1524        assert_eq!(
1525            fs.rename(path!("/bar/hello2.txt"), path!("/foo/world2.txt"))
1526                .await,
1527            Ok(()),
1528            "renaming (and moving) a file",
1529        );
1530
1531        assert_eq!(
1532            fs.rename(path!("/foo"), path!("/bar/baz")).await,
1533            Ok(()),
1534            "renaming a directory",
1535        );
1536
1537        assert_eq!(
1538            fs.rename(path!("/mnt/boz"), path!("/mnt/cat")).await,
1539            Ok(()),
1540            "renaming a directory within a mounted FS"
1541        );
1542
1543        assert!(
1544            arc_fs2.read_dir(path!("/cat")).is_ok(),
1545            "The directory was renamed in the inner FS"
1546        );
1547
1548        assert_eq!(
1549            fs.rename(path!("/bar/hello1.txt"), path!("/bar/world1.txt"))
1550                .await,
1551            Ok(()),
1552            "renaming a file (in the same directory)",
1553        );
1554
1555        {
1556            let fs_inner = fs.inner.read().unwrap();
1557
1558            dbg!(&fs_inner);
1559
1560            assert_eq!(
1561                fs_inner.storage.len(),
1562                7,
1563                "storage has still all directories"
1564            );
1565            assert!(
1566                matches!(
1567                    fs_inner.storage.get(ROOT_INODE),
1568                    Some(Node::Directory(DirectoryNode {
1569                        inode: ROOT_INODE,
1570                        name,
1571                        children,
1572                        ..
1573                    })) if name == "/" && children == &[3, 6]
1574                ),
1575                "`/` contains `bar` and `mnt`",
1576            );
1577            assert!(
1578                matches!(
1579                    fs_inner.storage.get(1),
1580                    Some(Node::Directory(DirectoryNode {
1581                        inode: 1,
1582                        name,
1583                        children,
1584                        ..
1585                    })) if name == "baz" && children == &[2, 5]
1586                ),
1587                "`foo` has been renamed to `baz` and contains `qux` and `world2.txt`",
1588            );
1589            assert!(
1590                matches!(
1591                    fs_inner.storage.get(2),
1592                    Some(Node::Directory(DirectoryNode {
1593                        inode: 2,
1594                        name,
1595                        children,
1596                        ..
1597                    })) if name == "qux" && children.is_empty()
1598                ),
1599                "`qux` is empty",
1600            );
1601            assert!(
1602                matches!(
1603                    fs_inner.storage.get(3),
1604                    Some(Node::Directory(DirectoryNode {
1605                        inode: 3,
1606                        name,
1607                        children,
1608                        ..
1609                    })) if name == "bar" && children == &[4, 1]
1610                ),
1611                "`bar` contains `bar` (ex `foo`)  and `world1.txt` (ex `hello1`)",
1612            );
1613            assert!(
1614                matches!(
1615                    fs_inner.storage.get(4),
1616                    Some(Node::File(FileNode {
1617                        inode: 4,
1618                        name,
1619                        ..
1620                    })) if name == "world1.txt"
1621                ),
1622                "`hello1.txt` has been renamed to `world1.txt`",
1623            );
1624            assert!(
1625                matches!(
1626                    fs_inner.storage.get(5),
1627                    Some(Node::File(FileNode {
1628                        inode: 5,
1629                        name,
1630                        ..
1631                    })) if name == "world2.txt"
1632                ),
1633                "`hello2.txt` has been renamed to `world2.txt`",
1634            );
1635        }
1636    }
1637
1638    #[tokio::test]
1639    async fn test_metadata() {
1640        use std::thread::sleep;
1641        use std::time::Duration;
1642
1643        let fs = FileSystem::default();
1644        let root_metadata = fs.metadata(path!("/"));
1645
1646        assert!(matches!(
1647            root_metadata,
1648            Ok(Metadata {
1649                ft: FileType { dir: true, .. },
1650                accessed,
1651                created,
1652                modified,
1653                len: 0
1654            }) if accessed == created && created == modified && modified > 0
1655        ));
1656
1657        assert_eq!(fs.create_dir(path!("/foo")), Ok(()));
1658
1659        let foo_metadata = fs.metadata(path!("/foo"));
1660        assert!(foo_metadata.is_ok());
1661        let foo_metadata = foo_metadata.unwrap();
1662
1663        assert!(matches!(
1664            foo_metadata,
1665            Metadata {
1666                ft: FileType { dir: true, .. },
1667                accessed,
1668                created,
1669                modified,
1670                len: 0
1671            } if accessed == created && created == modified && modified > 0
1672        ));
1673
1674        sleep(Duration::from_secs(3));
1675
1676        assert_eq!(fs.rename(path!("/foo"), path!("/bar")).await, Ok(()));
1677
1678        assert!(
1679            matches!(
1680                fs.metadata(path!("/bar")),
1681                Ok(Metadata {
1682                    ft: FileType { dir: true, .. },
1683                    accessed,
1684                    created,
1685                    modified,
1686                    len: 0
1687                }) if
1688                    accessed == foo_metadata.accessed &&
1689                    created == foo_metadata.created &&
1690                    modified > foo_metadata.modified
1691            ),
1692            "the modified time is updated when file is renamed",
1693        );
1694        assert!(
1695            matches!(
1696                fs.metadata(path!("/")),
1697                Ok(Metadata {
1698                    ft: FileType { dir: true, .. },
1699                    accessed,
1700                    created,
1701                    modified,
1702                    len: 0
1703                }) if
1704                    accessed <= foo_metadata.accessed &&
1705                    created <= foo_metadata.created &&
1706                    modified > foo_metadata.modified
1707            ),
1708            "the modified time of the parent is updated when file is renamed \n{:?}\n{:?}",
1709            fs.metadata(path!("/")),
1710            foo_metadata,
1711        );
1712    }
1713
1714    #[tokio::test]
1715    async fn test_remove_file() {
1716        let fs = FileSystem::default();
1717
1718        assert!(
1719            fs.new_open_options()
1720                .write(true)
1721                .create_new(true)
1722                .open(path!("/foo.txt"))
1723                .is_ok(),
1724            "creating a new file",
1725        );
1726
1727        {
1728            let fs_inner = fs.inner.read().unwrap();
1729
1730            assert_eq!(fs_inner.storage.len(), 2, "storage has all files");
1731            assert!(
1732                matches!(
1733                    fs_inner.storage.get(ROOT_INODE),
1734                    Some(Node::Directory(DirectoryNode {
1735                        inode: ROOT_INODE,
1736                        name,
1737                        children,
1738                        ..
1739                    })) if name == "/" && children == &[1]
1740                ),
1741                "`/` contains `foo.txt`",
1742            );
1743            assert!(
1744                matches!(
1745                    fs_inner.storage.get(1),
1746                    Some(Node::File(FileNode {
1747                        inode: 1,
1748                        name,
1749                        ..
1750                    })) if name == "foo.txt"
1751                ),
1752                "`foo.txt` exists and is a file",
1753            );
1754        }
1755
1756        assert_eq!(
1757            fs.remove_file(path!("/foo.txt")),
1758            Ok(()),
1759            "removing a file that exists",
1760        );
1761
1762        {
1763            let fs_inner = fs.inner.read().unwrap();
1764
1765            assert_eq!(fs_inner.storage.len(), 1, "storage no longer has the file");
1766            assert!(
1767                matches!(
1768                    fs_inner.storage.get(ROOT_INODE),
1769                    Some(Node::Directory(DirectoryNode {
1770                        inode: ROOT_INODE,
1771                        name,
1772                        children,
1773                        ..
1774                    })) if name == "/" && children.is_empty()
1775                ),
1776                "`/` is empty",
1777            );
1778        }
1779
1780        assert_eq!(
1781            fs.remove_file(path!("/foo.txt")),
1782            Err(FsError::EntryNotFound),
1783            "removing a file that exists",
1784        );
1785    }
1786
1787    #[tokio::test]
1788    async fn test_readdir() {
1789        let fs = FileSystem::default();
1790
1791        assert_eq!(fs.create_dir(path!("/foo")), Ok(()), "creating `foo`");
1792        assert_eq!(fs.create_dir(path!("/foo/sub")), Ok(()), "creating `sub`");
1793        assert_eq!(fs.create_dir(path!("/bar")), Ok(()), "creating `bar`");
1794        assert_eq!(fs.create_dir(path!("/baz")), Ok(()), "creating `bar`");
1795        assert!(
1796            fs.new_open_options()
1797                .write(true)
1798                .create_new(true)
1799                .open(path!("/a.txt"))
1800                .is_ok(),
1801            "creating `a.txt`",
1802        );
1803        assert!(
1804            fs.new_open_options()
1805                .write(true)
1806                .create_new(true)
1807                .open(path!("/b.txt"))
1808                .is_ok(),
1809            "creating `b.txt`",
1810        );
1811
1812        let readdir = fs.read_dir(path!("/"));
1813
1814        assert!(readdir.is_ok(), "reading the directory `/`");
1815
1816        let mut readdir = readdir.unwrap();
1817
1818        assert!(
1819            matches!(
1820                readdir.next(),
1821                Some(Ok(DirEntry {
1822                    path,
1823                    metadata: Ok(Metadata { ft, .. }),
1824                }))
1825                    if path.as_path() == path!("/foo") && ft.is_dir()
1826            ),
1827            "checking entry #1",
1828        );
1829        assert!(
1830            matches!(
1831                readdir.next(),
1832                Some(Ok(DirEntry {
1833                    path,
1834                    metadata: Ok(Metadata { ft, .. }),
1835                }))
1836                    if path.as_path() == path!("/bar") && ft.is_dir()
1837            ),
1838            "checking entry #2",
1839        );
1840        assert!(
1841            matches!(
1842                readdir.next(),
1843                Some(Ok(DirEntry {
1844                    path,
1845                    metadata: Ok(Metadata { ft, .. }),
1846                }))
1847                    if path.as_path() == path!("/baz") && ft.is_dir()
1848            ),
1849            "checking entry #3",
1850        );
1851        assert!(
1852            matches!(
1853                readdir.next(),
1854                Some(Ok(DirEntry {
1855                    path,
1856                    metadata: Ok(Metadata { ft, .. }),
1857                }))
1858                    if path.as_path() == path!("/a.txt") && ft.is_file()
1859            ),
1860            "checking entry #4",
1861        );
1862        assert!(
1863            matches!(
1864                readdir.next(),
1865                Some(Ok(DirEntry {
1866                    path,
1867                    metadata: Ok(Metadata { ft, .. }),
1868                }))
1869                    if path.as_path() == path!("/b.txt") && ft.is_file()
1870            ),
1871            "checking entry #5",
1872        );
1873        assert!(readdir.next().is_none(), "no more entries");
1874    }
1875
1876    #[tokio::test]
1877    async fn test_canonicalize() {
1878        let fs = FileSystem::default();
1879
1880        assert_eq!(fs.create_dir(path!("/foo")), Ok(()), "creating `foo`");
1881        assert_eq!(fs.create_dir(path!("/foo/bar")), Ok(()), "creating `bar`");
1882        assert_eq!(
1883            fs.create_dir(path!("/foo/bar/baz")),
1884            Ok(()),
1885            "creating `baz`",
1886        );
1887        assert_eq!(
1888            fs.create_dir(path!("/foo/bar/baz/qux")),
1889            Ok(()),
1890            "creating `qux`",
1891        );
1892        assert!(
1893            fs.new_open_options()
1894                .write(true)
1895                .create_new(true)
1896                .open(path!("/foo/bar/baz/qux/hello.txt"))
1897                .is_ok(),
1898            "creating `hello.txt`",
1899        );
1900
1901        let fs_inner = fs.inner.read().unwrap();
1902
1903        assert_eq!(
1904            fs_inner
1905                .canonicalize(path!("/"))
1906                .map(|(a, b)| (a, b.unwrap())),
1907            Ok((path!(buf "/"), ROOT_INODE)),
1908            "canonicalizing `/`",
1909        );
1910        assert_eq!(
1911            fs_inner
1912                .canonicalize(path!("foo"))
1913                .map(|(a, b)| (a, b.unwrap())),
1914            Err(FsError::InvalidInput),
1915            "canonicalizing `foo`",
1916        );
1917        assert_eq!(
1918            fs_inner
1919                .canonicalize(path!("/././././foo/"))
1920                .map(|(a, b)| (a, b.unwrap())),
1921            Ok((path!(buf "/foo"), 1)),
1922            "canonicalizing `/././././foo/`",
1923        );
1924        assert_eq!(
1925            fs_inner
1926                .canonicalize(path!("/foo/bar//"))
1927                .map(|(a, b)| (a, b.unwrap())),
1928            Ok((path!(buf "/foo/bar"), 2)),
1929            "canonicalizing `/foo/bar//`",
1930        );
1931        assert_eq!(
1932            fs_inner
1933                .canonicalize(path!("/foo/bar/../bar"))
1934                .map(|(a, b)| (a, b.unwrap())),
1935            Ok((path!(buf "/foo/bar"), 2)),
1936            "canonicalizing `/foo/bar/../bar`",
1937        );
1938        assert_eq!(
1939            fs_inner
1940                .canonicalize(path!("/foo/bar/../.."))
1941                .map(|(a, b)| (a, b.unwrap())),
1942            Ok((path!(buf "/"), ROOT_INODE)),
1943            "canonicalizing `/foo/bar/../..`",
1944        );
1945        assert_eq!(
1946            fs_inner
1947                .canonicalize(path!("/foo/bar/../../.."))
1948                .map(|(a, b)| (a, b.unwrap())),
1949            Err(FsError::InvalidInput),
1950            "canonicalizing `/foo/bar/../../..`",
1951        );
1952        assert_eq!(
1953            fs_inner
1954                .canonicalize(path!("C:/foo/"))
1955                .map(|(a, b)| (a, b.unwrap())),
1956            Err(FsError::InvalidInput),
1957            "canonicalizing `C:/foo/`",
1958        );
1959        assert_eq!(
1960            fs_inner
1961                .canonicalize(path!(
1962                    "/foo/./../foo/bar/../../foo/bar/./baz/./../baz/qux/../../baz/./qux/hello.txt"
1963                ))
1964                .map(|(a, b)| (a, b.unwrap())),
1965            Ok((path!(buf "/foo/bar/baz/qux/hello.txt"), 5)),
1966            "canonicalizing a crazily stupid path name",
1967        );
1968    }
1969
1970    #[tokio::test]
1971    #[ignore = "Not yet supported. See https://github.com/wasmerio/wasmer/issues/3678"]
1972    async fn mount_to_overlapping_directories() {
1973        let top_level = FileSystem::default();
1974        ops::touch(&top_level, "/file.txt").unwrap();
1975        let nested = FileSystem::default();
1976        ops::touch(&nested, "/another-file.txt").unwrap();
1977        let top_level: Arc<dyn crate::FileSystem + Send + Sync> = Arc::new(top_level);
1978        let nested: Arc<dyn crate::FileSystem + Send + Sync> = Arc::new(nested);
1979
1980        let fs = FileSystem::default();
1981        fs.mount("/top-level".into(), &top_level, "/".into())
1982            .unwrap();
1983        fs.mount("/top-level/nested".into(), &nested, "/".into())
1984            .unwrap();
1985
1986        assert!(ops::is_dir(&fs, "/top-level"));
1987        assert!(ops::is_file(&fs, "/top-level/file.txt"));
1988        assert!(ops::is_dir(&fs, "/top-level/nested"));
1989        assert!(ops::is_file(&fs, "/top-level/nested/another-file.txt"));
1990    }
1991
1992    #[tokio::test]
1993    async fn read_dir_rebases_mount_paths() {
1994        let mounted = FileSystem::default();
1995        ops::touch(&mounted, "/file.txt").unwrap();
1996        let mounted: Arc<dyn crate::FileSystem + Send + Sync> = Arc::new(mounted);
1997
1998        let fs = FileSystem::default();
1999        fs.mount("/mnt".into(), &mounted, "/".into()).unwrap();
2000
2001        let entries: Vec<_> = fs
2002            .read_dir(Path::new("/mnt"))
2003            .unwrap()
2004            .map(|e| e.unwrap().path)
2005            .collect();
2006
2007        assert_eq!(entries, vec![Path::new("/mnt/file.txt").to_path_buf()]);
2008    }
2009
2010    #[tokio::test]
2011    async fn test_merge_flat() {
2012        let main = FileSystem::default();
2013
2014        let other = FileSystem::default();
2015        crate::ops::create_dir_all(&other, "/a/x").unwrap();
2016        other
2017            .insert_ro_file(Path::new("/a/x/a.txt"), OwnedBuffer::from_static(b"a"))
2018            .unwrap();
2019        other
2020            .insert_ro_file(Path::new("/a/x/b.txt"), OwnedBuffer::from_static(b"b"))
2021            .unwrap();
2022        other
2023            .insert_ro_file(Path::new("/a/x/c.txt"), OwnedBuffer::from_static(b"c"))
2024            .unwrap();
2025
2026        let out = other.read_dir(Path::new("/")).unwrap();
2027        dbg!(&out);
2028
2029        let other: Arc<dyn crate::FileSystem + Send + Sync> = Arc::new(other);
2030
2031        main.mount_directory_entries(Path::new("/"), &other, Path::new("/a"))
2032            .unwrap();
2033
2034        let mut buf = Vec::new();
2035
2036        let mut f = main
2037            .new_open_options()
2038            .read(true)
2039            .open(Path::new("/x/a.txt"))
2040            .unwrap();
2041        f.read_to_end(&mut buf).await.unwrap();
2042
2043        assert_eq!(buf, b"a");
2044    }
2045}