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