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