1use super::*;
4use crate::{DirEntry, FileType, FsError, Metadata, OpenOptions, ReadDir, Result};
5use slab::Slab;
6use std::convert::identity;
7use std::ffi::OsString;
8use std::fmt;
9use std::path::{Component, Path, PathBuf};
10use std::sync::{Arc, RwLock};
11
12#[derive(Clone, Default)]
18pub struct FileSystem {
19 pub(super) inner: Arc<RwLock<FileSystemInner>>,
20}
21
22impl crate::FileSystem for FileSystem {
23 fn read_dir(&self, path: &Path) -> Result<ReadDir> {
24 let fs = self.inner.try_read().map_err(|_| FsError::Lock)?;
26
27 let (path, inode_of_directory) = fs.canonicalize(path)?;
29
30 let inode = fs.storage.get(inode_of_directory);
32 let children = match inode {
33 Some(Node::Directory { children, .. }) => children
34 .iter()
35 .filter_map(|inode| fs.storage.get(*inode))
36 .map(|node| DirEntry {
37 path: {
38 let mut entry_path = path.to_path_buf();
39 entry_path.push(node.name());
40
41 entry_path
42 },
43 metadata: Ok(node.metadata().clone()),
44 })
45 .collect(),
46
47 _ => return Err(FsError::InvalidInput),
48 };
49
50 Ok(ReadDir::new(children))
51 }
52
53 fn create_dir(&self, path: &Path) -> Result<()> {
54 let (inode_of_parent, name_of_directory) = {
55 let fs = self.inner.try_read().map_err(|_| FsError::Lock)?;
57
58 let path = fs.canonicalize_without_inode(path)?;
61
62 let parent_of_path = path.parent().ok_or(FsError::BaseNotDirectory)?;
64
65 let name_of_directory = path
67 .file_name()
68 .ok_or(FsError::InvalidInput)?
69 .to_os_string();
70
71 let inode_of_parent = fs.inode_of_parent(parent_of_path)?;
73
74 (inode_of_parent, name_of_directory)
75 };
76
77 {
78 let mut fs = self.inner.try_write().map_err(|_| FsError::Lock)?;
80
81 let inode_of_directory = fs.storage.vacant_entry().key();
83 let real_inode_of_directory = fs.storage.insert(Node::Directory {
84 inode: inode_of_directory,
85 name: name_of_directory,
86 children: Vec::new(),
87 metadata: {
88 let time = time();
89
90 Metadata {
91 ft: FileType {
92 dir: true,
93 ..Default::default()
94 },
95 accessed: time,
96 created: time,
97 modified: time,
98 len: 0,
99 }
100 },
101 });
102
103 assert_eq!(
104 inode_of_directory, real_inode_of_directory,
105 "new directory inode should have been correctly calculated",
106 );
107
108 fs.add_child_to_node(inode_of_parent, inode_of_directory)?;
110 }
111
112 Ok(())
113 }
114
115 fn remove_dir(&self, path: &Path) -> Result<()> {
116 let (inode_of_parent, position, inode_of_directory) = {
117 let fs = self.inner.try_read().map_err(|_| FsError::Lock)?;
119
120 let (path, _) = fs.canonicalize(path)?;
122
123 let parent_of_path = path.parent().ok_or(FsError::BaseNotDirectory)?;
125
126 let name_of_directory = path
128 .file_name()
129 .ok_or(FsError::InvalidInput)?
130 .to_os_string();
131
132 let inode_of_parent = fs.inode_of_parent(parent_of_path)?;
134
135 let (position, inode_of_directory) = fs.as_parent_get_position_and_inode_of_directory(
138 inode_of_parent,
139 &name_of_directory,
140 DirectoryMustBeEmpty::Yes,
141 )?;
142
143 (inode_of_parent, position, inode_of_directory)
144 };
145
146 {
147 let mut fs = self.inner.try_write().map_err(|_| FsError::Lock)?;
149
150 fs.storage.remove(inode_of_directory);
152
153 fs.remove_child_from_node(inode_of_parent, position)?;
155 }
156
157 Ok(())
158 }
159
160 fn rename(&self, from: &Path, to: &Path) -> Result<()> {
161 let (
162 (position_of_from, inode, inode_of_from_parent),
163 (inode_of_to_parent, name_of_to),
164 inode_dest,
165 ) = {
166 let fs = self.inner.try_read().map_err(|_| FsError::Lock)?;
168
169 let from = fs.canonicalize_without_inode(from)?;
170 let to = fs.canonicalize_without_inode(to)?;
171
172 let parent_of_from = from.parent().ok_or(FsError::BaseNotDirectory)?;
174 let parent_of_to = to.parent().ok_or(FsError::BaseNotDirectory)?;
175
176 let name_of_from = from
178 .file_name()
179 .ok_or(FsError::InvalidInput)?
180 .to_os_string();
181 let name_of_to = to.file_name().ok_or(FsError::InvalidInput)?.to_os_string();
182
183 let inode_of_from_parent = fs.inode_of_parent(parent_of_from)?;
185 let inode_of_to_parent = fs.inode_of_parent(parent_of_to)?;
186
187 let maybe_position_and_inode_of_file =
189 fs.as_parent_get_position_and_inode_of_file(inode_of_to_parent, &name_of_to)?;
190
191 let (position_of_from, inode) = fs
194 .as_parent_get_position_and_inode(inode_of_from_parent, &name_of_from)?
195 .ok_or(FsError::NotAFile)?;
196
197 (
198 (position_of_from, inode, inode_of_from_parent),
199 (inode_of_to_parent, name_of_to),
200 maybe_position_and_inode_of_file,
201 )
202 };
203
204 {
205 let mut fs = self.inner.try_write().map_err(|_| FsError::Lock)?;
207
208 if let Some((position, inode_of_file)) = inode_dest {
209 fs.storage.remove(inode_of_file);
211
212 fs.remove_child_from_node(inode_of_to_parent, position)?;
214 }
215
216 fs.update_node_name(inode, name_of_to)?;
218
219 if inode_of_from_parent != inode_of_to_parent {
221 fs.remove_child_from_node(inode_of_from_parent, position_of_from)?;
224
225 fs.add_child_to_node(inode_of_to_parent, inode)?;
228 }
229 else {
231 let inode = fs.storage.get_mut(inode_of_from_parent);
232 match inode {
233 Some(Node::Directory {
234 metadata: Metadata { modified, .. },
235 ..
236 }) => *modified = time(),
237 _ => return Err(FsError::UnknownError),
238 }
239 }
240 }
241
242 Ok(())
243 }
244
245 fn metadata(&self, path: &Path) -> Result<Metadata> {
246 let fs = self.inner.try_read().map_err(|_| FsError::Lock)?;
248
249 Ok(fs
250 .storage
251 .get(fs.inode_of(path)?)
252 .ok_or(FsError::UnknownError)?
253 .metadata()
254 .clone())
255 }
256
257 fn remove_file(&self, path: &Path) -> Result<()> {
258 let (inode_of_parent, position, inode_of_file) = {
259 let fs = self.inner.try_read().map_err(|_| FsError::Lock)?;
261
262 let path = fs.canonicalize_without_inode(path)?;
264
265 let parent_of_path = path.parent().ok_or(FsError::BaseNotDirectory)?;
267
268 let name_of_file = path
270 .file_name()
271 .ok_or(FsError::InvalidInput)?
272 .to_os_string();
273
274 let inode_of_parent = fs.inode_of_parent(parent_of_path)?;
276
277 let maybe_position_and_inode_of_file =
279 fs.as_parent_get_position_and_inode_of_file(inode_of_parent, &name_of_file)?;
280
281 match maybe_position_and_inode_of_file {
282 Some((position, inode_of_file)) => (inode_of_parent, position, inode_of_file),
283 None => return Err(FsError::NotAFile),
284 }
285 };
286
287 {
288 let mut fs = self.inner.try_write().map_err(|_| FsError::Lock)?;
290
291 fs.storage.remove(inode_of_file);
293
294 fs.remove_child_from_node(inode_of_parent, position)?;
296 }
297
298 Ok(())
299 }
300
301 fn new_open_options(&self) -> OpenOptions {
302 OpenOptions::new(Box::new(FileOpener {
303 filesystem: self.clone(),
304 }))
305 }
306}
307
308impl fmt::Debug for FileSystem {
309 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
310 let fs: &FileSystemInner = &self.inner.read().unwrap();
311
312 fs.fmt(formatter)
313 }
314}
315
316pub(super) struct FileSystemInner {
319 pub(super) storage: Slab<Node>,
320}
321
322impl FileSystemInner {
323 pub(super) fn inode_of(&self, path: &Path) -> Result<Inode> {
325 let mut node = self.storage.get(ROOT_INODE).unwrap();
327 let mut components = path.components();
328
329 match components.next() {
330 Some(Component::RootDir) | None => {}
331 _ => return Err(FsError::BaseNotDirectory),
332 }
333
334 for component in components {
335 node = match node {
336 Node::Directory { children, .. } => children
337 .iter()
338 .filter_map(|inode| self.storage.get(*inode))
339 .find(|node| node.name() == component.as_os_str())
340 .ok_or(FsError::NotAFile)?,
341 _ => return Err(FsError::BaseNotDirectory),
342 };
343 }
344
345 Ok(node.inode())
346 }
347
348 pub(super) fn inode_of_parent(&self, parent_path: &Path) -> Result<Inode> {
351 let inode_of_parent = self.inode_of(parent_path)?;
352
353 match self.storage.get(inode_of_parent) {
355 Some(Node::Directory { .. }) => Ok(inode_of_parent),
356 _ => Err(FsError::BaseNotDirectory),
357 }
358 }
359
360 pub(super) fn as_parent_get_position_and_inode_of_directory(
363 &self,
364 inode_of_parent: Inode,
365 name_of_directory: &OsString,
366 directory_must_be_empty: DirectoryMustBeEmpty,
367 ) -> Result<(usize, Inode)> {
368 match self.storage.get(inode_of_parent) {
369 Some(Node::Directory { children, .. }) => children
370 .iter()
371 .enumerate()
372 .filter_map(|(nth, inode)| self.storage.get(*inode).map(|node| (nth, node)))
373 .find_map(|(nth, node)| match node {
374 Node::Directory {
375 inode,
376 name,
377 children,
378 ..
379 } if name.as_os_str() == name_of_directory => {
380 if directory_must_be_empty.no() || children.is_empty() {
381 Some(Ok((nth, *inode)))
382 } else {
383 Some(Err(FsError::DirectoryNotEmpty))
384 }
385 }
386
387 _ => None,
388 })
389 .ok_or(FsError::InvalidInput)
390 .and_then(identity), _ => Err(FsError::BaseNotDirectory),
392 }
393 }
394
395 pub(super) fn as_parent_get_position_and_inode_of_file(
398 &self,
399 inode_of_parent: Inode,
400 name_of_file: &OsString,
401 ) -> Result<Option<(usize, Inode)>> {
402 match self.storage.get(inode_of_parent) {
403 Some(Node::Directory { children, .. }) => children
404 .iter()
405 .enumerate()
406 .filter_map(|(nth, inode)| self.storage.get(*inode).map(|node| (nth, node)))
407 .find_map(|(nth, node)| match node {
408 Node::File { inode, name, .. } if name.as_os_str() == name_of_file => {
409 Some(Some((nth, *inode)))
410 }
411
412 _ => None,
413 })
414 .or(Some(None))
415 .ok_or(FsError::InvalidInput),
416
417 _ => Err(FsError::BaseNotDirectory),
418 }
419 }
420
421 fn as_parent_get_position_and_inode(
425 &self,
426 inode_of_parent: Inode,
427 name_of: &OsString,
428 ) -> Result<Option<(usize, Inode)>> {
429 match self.storage.get(inode_of_parent) {
430 Some(Node::Directory { children, .. }) => children
431 .iter()
432 .enumerate()
433 .filter_map(|(nth, inode)| self.storage.get(*inode).map(|node| (nth, node)))
434 .find_map(|(nth, node)| match node {
435 Node::File { inode, name, .. } | Node::Directory { inode, name, .. }
436 if name.as_os_str() == name_of =>
437 {
438 Some(Some((nth, *inode)))
439 }
440
441 _ => None,
442 })
443 .or(Some(None))
444 .ok_or(FsError::InvalidInput),
445
446 _ => Err(FsError::BaseNotDirectory),
447 }
448 }
449
450 pub(super) fn update_node_name(&mut self, inode: Inode, new_name: OsString) -> Result<()> {
452 let node = self.storage.get_mut(inode).ok_or(FsError::UnknownError)?;
453
454 node.set_name(new_name);
455 node.metadata_mut().modified = time();
456
457 Ok(())
458 }
459
460 pub(super) fn add_child_to_node(&mut self, inode: Inode, new_child: Inode) -> Result<()> {
468 match self.storage.get_mut(inode) {
469 Some(Node::Directory {
470 children,
471 metadata: Metadata { modified, .. },
472 ..
473 }) => {
474 children.push(new_child);
475 *modified = time();
476
477 Ok(())
478 }
479 _ => Err(FsError::UnknownError),
480 }
481 }
482
483 pub(super) fn remove_child_from_node(&mut self, inode: Inode, position: usize) -> Result<()> {
492 match self.storage.get_mut(inode) {
493 Some(Node::Directory {
494 children,
495 metadata: Metadata { modified, .. },
496 ..
497 }) => {
498 children.remove(position);
499 *modified = time();
500
501 Ok(())
502 }
503 _ => Err(FsError::UnknownError),
504 }
505 }
506
507 pub(super) fn canonicalize(&self, path: &Path) -> Result<(PathBuf, Inode)> {
516 let new_path = self.canonicalize_without_inode(path)?;
517 let inode = self.inode_of(&new_path)?;
518
519 Ok((new_path, inode))
520 }
521
522 pub(super) fn canonicalize_without_inode(&self, path: &Path) -> Result<PathBuf> {
526 let mut components = path.components();
527
528 match components.next() {
529 Some(Component::RootDir) => {}
530 _ => return Err(FsError::InvalidInput),
531 }
532
533 let mut new_path = PathBuf::with_capacity(path.as_os_str().len());
534 new_path.push("/");
535
536 for component in components {
537 match component {
538 Component::RootDir => return Err(FsError::UnknownError),
540
541 Component::CurDir => (),
543
544 Component::ParentDir => {
547 if !new_path.pop() {
548 return Err(FsError::InvalidInput);
549 }
550 }
551
552 Component::Normal(name) => {
554 new_path.push(name);
555 }
556
557 Component::Prefix(_) => return Err(FsError::InvalidInput),
559 }
560 }
561
562 Ok(new_path)
563 }
564}
565
566impl fmt::Debug for FileSystemInner {
567 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
568 writeln!(
569 formatter,
570 "\n{inode:<8} {ty:<4} name",
571 inode = "inode",
572 ty = "type",
573 )?;
574
575 fn debug(
576 nodes: Vec<&Node>,
577 slf: &FileSystemInner,
578 formatter: &mut fmt::Formatter<'_>,
579 indentation: usize,
580 ) -> fmt::Result {
581 for node in nodes {
582 writeln!(
583 formatter,
584 "{inode:<8} {ty:<4} {indentation_symbol:indentation_width$}{name}",
585 inode = node.inode(),
586 ty = match node {
587 Node::File { .. } => "file",
588 Node::Directory { .. } => "dir",
589 },
590 name = node.name().to_string_lossy(),
591 indentation_symbol = " ",
592 indentation_width = indentation * 2 + 1,
593 )?;
594
595 if let Node::Directory { children, .. } = node {
596 debug(
597 children
598 .iter()
599 .filter_map(|inode| slf.storage.get(*inode))
600 .collect(),
601 slf,
602 formatter,
603 indentation + 1,
604 )?;
605 }
606 }
607
608 Ok(())
609 }
610
611 debug(
612 vec![self.storage.get(ROOT_INODE).unwrap()],
613 self,
614 formatter,
615 0,
616 )
617 }
618}
619
620impl Default for FileSystemInner {
621 fn default() -> Self {
622 let time = time();
623
624 let mut slab = Slab::new();
625 slab.insert(Node::Directory {
626 inode: ROOT_INODE,
627 name: OsString::from("/"),
628 children: Vec::new(),
629 metadata: Metadata {
630 ft: FileType {
631 dir: true,
632 ..Default::default()
633 },
634 accessed: time,
635 created: time,
636 modified: time,
637 len: 0,
638 },
639 });
640
641 Self { storage: slab }
642 }
643}
644
645#[cfg(test)]
646mod test_filesystem {
647 use crate::{mem_fs::*, DirEntry, FileSystem as FS, FileType, FsError};
648
649 macro_rules! path {
650 ($path:expr) => {
651 std::path::Path::new($path)
652 };
653
654 (buf $path:expr) => {
655 std::path::PathBuf::from($path)
656 };
657 }
658
659 #[test]
660 fn test_new_filesystem() {
661 let fs = FileSystem::default();
662 let fs_inner = fs.inner.read().unwrap();
663
664 assert_eq!(fs_inner.storage.len(), 1, "storage has a root");
665 assert!(
666 matches!(
667 fs_inner.storage.get(ROOT_INODE),
668 Some(Node::Directory {
669 inode: ROOT_INODE,
670 name,
671 children,
672 ..
673 }) if name == "/" && children.is_empty(),
674 ),
675 "storage has a well-defined root",
676 );
677 }
678
679 #[test]
680 fn test_create_dir() {
681 let fs = FileSystem::default();
682
683 assert_eq!(
684 fs.create_dir(path!("/")),
685 Err(FsError::BaseNotDirectory),
686 "creating a directory that has no parent",
687 );
688
689 assert_eq!(fs.create_dir(path!("/foo")), Ok(()), "creating a directory",);
690
691 {
692 let fs_inner = fs.inner.read().unwrap();
693 assert_eq!(
694 fs_inner.storage.len(),
695 2,
696 "storage contains the new directory"
697 );
698 assert!(
699 matches!(
700 fs_inner.storage.get(ROOT_INODE),
701 Some(Node::Directory {
702 inode: ROOT_INODE,
703 name,
704 children,
705 ..
706 }) if name == "/" && children == &[1]
707 ),
708 "the root is updated and well-defined",
709 );
710 assert!(
711 matches!(
712 fs_inner.storage.get(1),
713 Some(Node::Directory {
714 inode: 1,
715 name,
716 children,
717 ..
718 }) if name == "foo" && children.is_empty(),
719 ),
720 "the new directory is well-defined",
721 );
722 }
723
724 assert_eq!(
725 fs.create_dir(path!("/foo/bar")),
726 Ok(()),
727 "creating a sub-directory",
728 );
729
730 {
731 let fs_inner = fs.inner.read().unwrap();
732 assert_eq!(
733 fs_inner.storage.len(),
734 3,
735 "storage contains the new sub-directory",
736 );
737 assert!(
738 matches!(
739 fs_inner.storage.get(ROOT_INODE),
740 Some(Node::Directory {
741 inode: ROOT_INODE,
742 name,
743 children,
744 ..
745 }) if name == "/" && children == &[1]
746 ),
747 "the root is updated again and well-defined",
748 );
749 assert!(
750 matches!(
751 fs_inner.storage.get(1),
752 Some(Node::Directory {
753 inode: 1,
754 name,
755 children,
756 ..
757 }) if name == "foo" && children == &[2]
758 ),
759 "the new directory is updated and well-defined",
760 );
761 assert!(
762 matches!(
763 fs_inner.storage.get(2),
764 Some(Node::Directory {
765 inode: 2,
766 name,
767 children,
768 ..
769 }) if name == "bar" && children.is_empty()
770 ),
771 "the new directory is well-defined",
772 );
773 }
774 }
775
776 #[test]
777 fn test_remove_dir() {
778 let fs = FileSystem::default();
779
780 assert_eq!(
781 fs.remove_dir(path!("/")),
782 Err(FsError::BaseNotDirectory),
783 "removing a directory that has no parent",
784 );
785
786 assert_eq!(
787 fs.remove_dir(path!("/foo")),
788 Err(FsError::NotAFile),
789 "cannot remove a directory that doesn't exist",
790 );
791
792 assert_eq!(fs.create_dir(path!("/foo")), Ok(()), "creating a directory",);
793
794 assert_eq!(
795 fs.create_dir(path!("/foo/bar")),
796 Ok(()),
797 "creating a sub-directory",
798 );
799
800 {
801 let fs_inner = fs.inner.read().unwrap();
802 assert_eq!(
803 fs_inner.storage.len(),
804 3,
805 "storage contains all the directories",
806 );
807 }
808
809 assert_eq!(
810 fs.remove_dir(path!("/foo")),
811 Err(FsError::DirectoryNotEmpty),
812 "removing a directory that has children",
813 );
814
815 assert_eq!(
816 fs.remove_dir(path!("/foo/bar")),
817 Ok(()),
818 "removing a sub-directory",
819 );
820
821 assert_eq!(fs.remove_dir(path!("/foo")), Ok(()), "removing a directory",);
822
823 {
824 let fs_inner = fs.inner.read().unwrap();
825 assert_eq!(
826 fs_inner.storage.len(),
827 1,
828 "storage contains all the directories",
829 );
830 }
831 }
832
833 #[test]
834 fn test_rename() {
835 let fs = FileSystem::default();
836
837 assert_eq!(
838 fs.rename(path!("/"), path!("/bar")),
839 Err(FsError::BaseNotDirectory),
840 "renaming a directory that has no parent",
841 );
842 assert_eq!(
843 fs.rename(path!("/foo"), path!("/")),
844 Err(FsError::BaseNotDirectory),
845 "renaming to a directory that has no parent",
846 );
847
848 assert_eq!(fs.create_dir(path!("/foo")), Ok(()));
849 assert_eq!(fs.create_dir(path!("/foo/qux")), Ok(()));
850
851 assert_eq!(
852 fs.rename(path!("/foo"), path!("/bar/baz")),
853 Err(FsError::NotAFile),
854 "renaming to a directory that has parent that doesn't exist",
855 );
856
857 assert_eq!(fs.create_dir(path!("/bar")), Ok(()));
858
859 assert!(
860 matches!(
861 fs.new_open_options()
862 .write(true)
863 .create_new(true)
864 .open(path!("/bar/hello1.txt")),
865 Ok(_),
866 ),
867 "creating a new file (`hello1.txt`)",
868 );
869 assert!(
870 matches!(
871 fs.new_open_options()
872 .write(true)
873 .create_new(true)
874 .open(path!("/bar/hello2.txt")),
875 Ok(_),
876 ),
877 "creating a new file (`hello2.txt`)",
878 );
879
880 {
881 let fs_inner = fs.inner.read().unwrap();
882
883 assert_eq!(fs_inner.storage.len(), 6, "storage has all files");
884 assert!(
885 matches!(
886 fs_inner.storage.get(ROOT_INODE),
887 Some(Node::Directory {
888 inode: ROOT_INODE,
889 name,
890 children,
891 ..
892 }) if name == "/" && children == &[1, 3]
893 ),
894 "`/` contains `foo` and `bar`",
895 );
896 assert!(
897 matches!(
898 fs_inner.storage.get(1),
899 Some(Node::Directory {
900 inode: 1,
901 name,
902 children,
903 ..
904 }) if name == "foo" && children == &[2]
905 ),
906 "`foo` contains `qux`",
907 );
908 assert!(
909 matches!(
910 fs_inner.storage.get(2),
911 Some(Node::Directory {
912 inode: 2,
913 name,
914 children,
915 ..
916 }) if name == "qux" && children.is_empty()
917 ),
918 "`qux` is empty",
919 );
920 assert!(
921 matches!(
922 fs_inner.storage.get(3),
923 Some(Node::Directory {
924 inode: 3,
925 name,
926 children,
927 ..
928 }) if name == "bar" && children == &[4, 5]
929 ),
930 "`bar` is contains `hello.txt`",
931 );
932 assert!(
933 matches!(
934 fs_inner.storage.get(4),
935 Some(Node::File {
936 inode: 4,
937 name,
938 ..
939 }) if name == "hello1.txt"
940 ),
941 "`hello1.txt` exists",
942 );
943 assert!(
944 matches!(
945 fs_inner.storage.get(5),
946 Some(Node::File {
947 inode: 5,
948 name,
949 ..
950 }) if name == "hello2.txt"
951 ),
952 "`hello2.txt` exists",
953 );
954 }
955
956 assert_eq!(
957 fs.rename(path!("/bar/hello2.txt"), path!("/foo/world2.txt")),
958 Ok(()),
959 "renaming (and moving) a file",
960 );
961
962 assert_eq!(
963 fs.rename(path!("/foo"), path!("/bar/baz")),
964 Ok(()),
965 "renaming a directory",
966 );
967
968 assert_eq!(
969 fs.rename(path!("/bar/hello1.txt"), path!("/bar/world1.txt")),
970 Ok(()),
971 "renaming a file (in the same directory)",
972 );
973
974 {
975 let fs_inner = fs.inner.read().unwrap();
976
977 dbg!(&fs_inner);
978
979 assert_eq!(
980 fs_inner.storage.len(),
981 6,
982 "storage has still all directories"
983 );
984 assert!(
985 matches!(
986 fs_inner.storage.get(ROOT_INODE),
987 Some(Node::Directory {
988 inode: ROOT_INODE,
989 name,
990 children,
991 ..
992 }) if name == "/" && children == &[3]
993 ),
994 "`/` contains `bar`",
995 );
996 assert!(
997 matches!(
998 fs_inner.storage.get(1),
999 Some(Node::Directory {
1000 inode: 1,
1001 name,
1002 children,
1003 ..
1004 }) if name == "baz" && children == &[2, 5]
1005 ),
1006 "`foo` has been renamed to `baz` and contains `qux` and `world2.txt`",
1007 );
1008 assert!(
1009 matches!(
1010 fs_inner.storage.get(2),
1011 Some(Node::Directory {
1012 inode: 2,
1013 name,
1014 children,
1015 ..
1016 }) if name == "qux" && children.is_empty()
1017 ),
1018 "`qux` is empty",
1019 );
1020 assert!(
1021 matches!(
1022 fs_inner.storage.get(3),
1023 Some(Node::Directory {
1024 inode: 3,
1025 name,
1026 children,
1027 ..
1028 }) if name == "bar" && children == &[4, 1]
1029 ),
1030 "`bar` contains `bar` (ex `foo`) and `world1.txt` (ex `hello1`)",
1031 );
1032 assert!(
1033 matches!(
1034 fs_inner.storage.get(4),
1035 Some(Node::File {
1036 inode: 4,
1037 name,
1038 ..
1039 }) if name == "world1.txt"
1040 ),
1041 "`hello1.txt` has been renamed to `world1.txt`",
1042 );
1043 assert!(
1044 matches!(
1045 fs_inner.storage.get(5),
1046 Some(Node::File {
1047 inode: 5,
1048 name,
1049 ..
1050 }) if name == "world2.txt"
1051 ),
1052 "`hello2.txt` has been renamed to `world2.txt`",
1053 );
1054 }
1055 }
1056
1057 #[test]
1058 fn test_metadata() {
1059 use std::thread::sleep;
1060 use std::time::Duration;
1061
1062 let fs = FileSystem::default();
1063 let root_metadata = fs.metadata(path!("/"));
1064
1065 assert!(matches!(
1066 root_metadata,
1067 Ok(Metadata {
1068 ft: FileType { dir: true, .. },
1069 accessed,
1070 created,
1071 modified,
1072 len: 0
1073 }) if accessed == created && created == modified && modified > 0
1074 ));
1075
1076 assert_eq!(fs.create_dir(path!("/foo")), Ok(()));
1077
1078 let foo_metadata = fs.metadata(path!("/foo"));
1079 assert!(foo_metadata.is_ok());
1080 let foo_metadata = foo_metadata.unwrap();
1081
1082 assert!(matches!(
1083 foo_metadata,
1084 Metadata {
1085 ft: FileType { dir: true, .. },
1086 accessed,
1087 created,
1088 modified,
1089 len: 0
1090 } if accessed == created && created == modified && modified > 0
1091 ));
1092
1093 sleep(Duration::from_secs(3));
1094
1095 assert_eq!(fs.rename(path!("/foo"), path!("/bar")), Ok(()));
1096
1097 assert!(
1098 matches!(
1099 fs.metadata(path!("/bar")),
1100 Ok(Metadata {
1101 ft: FileType { dir: true, .. },
1102 accessed,
1103 created,
1104 modified,
1105 len: 0
1106 }) if
1107 accessed == foo_metadata.accessed &&
1108 created == foo_metadata.created &&
1109 modified > foo_metadata.modified
1110 ),
1111 "the modified time is updated when file is renamed",
1112 );
1113 assert!(
1114 matches!(
1115 fs.metadata(path!("/")),
1116 Ok(Metadata {
1117 ft: FileType { dir: true, .. },
1118 accessed,
1119 created,
1120 modified,
1121 len: 0
1122 }) if
1123 accessed == foo_metadata.accessed &&
1124 created == foo_metadata.created &&
1125 modified > foo_metadata.modified
1126 ),
1127 "the modified time of the parent is updated when file is renamed",
1128 );
1129 }
1130
1131 #[test]
1132 fn test_remove_file() {
1133 let fs = FileSystem::default();
1134
1135 assert!(
1136 matches!(
1137 fs.new_open_options()
1138 .write(true)
1139 .create_new(true)
1140 .open(path!("/foo.txt")),
1141 Ok(_)
1142 ),
1143 "creating a new file",
1144 );
1145
1146 {
1147 let fs_inner = fs.inner.read().unwrap();
1148
1149 assert_eq!(fs_inner.storage.len(), 2, "storage has all files");
1150 assert!(
1151 matches!(
1152 fs_inner.storage.get(ROOT_INODE),
1153 Some(Node::Directory {
1154 inode: ROOT_INODE,
1155 name,
1156 children,
1157 ..
1158 }) if name == "/" && children == &[1]
1159 ),
1160 "`/` contains `foo.txt`",
1161 );
1162 assert!(
1163 matches!(
1164 fs_inner.storage.get(1),
1165 Some(Node::File {
1166 inode: 1,
1167 name,
1168 ..
1169 }) if name == "foo.txt"
1170 ),
1171 "`foo.txt` exists and is a file",
1172 );
1173 }
1174
1175 assert_eq!(
1176 fs.remove_file(path!("/foo.txt")),
1177 Ok(()),
1178 "removing a file that exists",
1179 );
1180
1181 {
1182 let fs_inner = fs.inner.read().unwrap();
1183
1184 assert_eq!(fs_inner.storage.len(), 1, "storage no longer has the file");
1185 assert!(
1186 matches!(
1187 fs_inner.storage.get(ROOT_INODE),
1188 Some(Node::Directory {
1189 inode: ROOT_INODE,
1190 name,
1191 children,
1192 ..
1193 }) if name == "/" && children.is_empty()
1194 ),
1195 "`/` is empty",
1196 );
1197 }
1198
1199 assert_eq!(
1200 fs.remove_file(path!("/foo.txt")),
1201 Err(FsError::NotAFile),
1202 "removing a file that exists",
1203 );
1204 }
1205
1206 #[test]
1207 fn test_readdir() {
1208 let fs = FileSystem::default();
1209
1210 assert_eq!(fs.create_dir(path!("/foo")), Ok(()), "creating `foo`");
1211 assert_eq!(fs.create_dir(path!("/foo/sub")), Ok(()), "creating `sub`");
1212 assert_eq!(fs.create_dir(path!("/bar")), Ok(()), "creating `bar`");
1213 assert_eq!(fs.create_dir(path!("/baz")), Ok(()), "creating `bar`");
1214 assert!(
1215 matches!(
1216 fs.new_open_options()
1217 .write(true)
1218 .create_new(true)
1219 .open(path!("/a.txt")),
1220 Ok(_)
1221 ),
1222 "creating `a.txt`",
1223 );
1224 assert!(
1225 matches!(
1226 fs.new_open_options()
1227 .write(true)
1228 .create_new(true)
1229 .open(path!("/b.txt")),
1230 Ok(_)
1231 ),
1232 "creating `b.txt`",
1233 );
1234
1235 let readdir = fs.read_dir(path!("/"));
1236
1237 assert!(readdir.is_ok(), "reading the directory `/`");
1238
1239 let mut readdir = readdir.unwrap();
1240
1241 assert!(
1242 matches!(
1243 readdir.next(),
1244 Some(Ok(DirEntry {
1245 path,
1246 metadata: Ok(Metadata { ft, .. }),
1247 }))
1248 if path == path!(buf "/foo") && ft.is_dir()
1249 ),
1250 "checking entry #1",
1251 );
1252 assert!(
1253 matches!(
1254 readdir.next(),
1255 Some(Ok(DirEntry {
1256 path,
1257 metadata: Ok(Metadata { ft, .. }),
1258 }))
1259 if path == path!(buf "/bar") && ft.is_dir()
1260 ),
1261 "checking entry #2",
1262 );
1263 assert!(
1264 matches!(
1265 readdir.next(),
1266 Some(Ok(DirEntry {
1267 path,
1268 metadata: Ok(Metadata { ft, .. }),
1269 }))
1270 if path == path!(buf "/baz") && ft.is_dir()
1271 ),
1272 "checking entry #3",
1273 );
1274 assert!(
1275 matches!(
1276 readdir.next(),
1277 Some(Ok(DirEntry {
1278 path,
1279 metadata: Ok(Metadata { ft, .. }),
1280 }))
1281 if path == path!(buf "/a.txt") && ft.is_file()
1282 ),
1283 "checking entry #4",
1284 );
1285 assert!(
1286 matches!(
1287 readdir.next(),
1288 Some(Ok(DirEntry {
1289 path,
1290 metadata: Ok(Metadata { ft, .. }),
1291 }))
1292 if path == path!(buf "/b.txt") && ft.is_file()
1293 ),
1294 "checking entry #5",
1295 );
1296 assert!(matches!(readdir.next(), None), "no more entries");
1297 }
1298
1299 #[test]
1300 fn test_canonicalize() {
1301 let fs = FileSystem::default();
1302
1303 assert_eq!(fs.create_dir(path!("/foo")), Ok(()), "creating `foo`");
1304 assert_eq!(fs.create_dir(path!("/foo/bar")), Ok(()), "creating `bar`");
1305 assert_eq!(
1306 fs.create_dir(path!("/foo/bar/baz")),
1307 Ok(()),
1308 "creating `baz`",
1309 );
1310 assert_eq!(
1311 fs.create_dir(path!("/foo/bar/baz/qux")),
1312 Ok(()),
1313 "creating `qux`",
1314 );
1315 assert!(
1316 matches!(
1317 fs.new_open_options()
1318 .write(true)
1319 .create_new(true)
1320 .open(path!("/foo/bar/baz/qux/hello.txt")),
1321 Ok(_)
1322 ),
1323 "creating `hello.txt`",
1324 );
1325
1326 let fs_inner = fs.inner.read().unwrap();
1327
1328 assert_eq!(
1329 fs_inner.canonicalize(path!("/")),
1330 Ok((path!(buf "/"), ROOT_INODE)),
1331 "canonicalizing `/`",
1332 );
1333 assert_eq!(
1334 fs_inner.canonicalize(path!("foo")),
1335 Err(FsError::InvalidInput),
1336 "canonicalizing `foo`",
1337 );
1338 assert_eq!(
1339 fs_inner.canonicalize(path!("/././././foo/")),
1340 Ok((path!(buf "/foo"), 1)),
1341 "canonicalizing `/././././foo/`",
1342 );
1343 assert_eq!(
1344 fs_inner.canonicalize(path!("/foo/bar//")),
1345 Ok((path!(buf "/foo/bar"), 2)),
1346 "canonicalizing `/foo/bar//`",
1347 );
1348 assert_eq!(
1349 fs_inner.canonicalize(path!("/foo/bar/../bar")),
1350 Ok((path!(buf "/foo/bar"), 2)),
1351 "canonicalizing `/foo/bar/../bar`",
1352 );
1353 assert_eq!(
1354 fs_inner.canonicalize(path!("/foo/bar/../..")),
1355 Ok((path!(buf "/"), ROOT_INODE)),
1356 "canonicalizing `/foo/bar/../..`",
1357 );
1358 assert_eq!(
1359 fs_inner.canonicalize(path!("/foo/bar/../../..")),
1360 Err(FsError::InvalidInput),
1361 "canonicalizing `/foo/bar/../../..`",
1362 );
1363 assert_eq!(
1364 fs_inner.canonicalize(path!("C:/foo/")),
1365 Err(FsError::InvalidInput),
1366 "canonicalizing `C:/foo/`",
1367 );
1368 assert_eq!(
1369 fs_inner.canonicalize(path!(
1370 "/foo/./../foo/bar/../../foo/bar/./baz/./../baz/qux/../../baz/./qux/hello.txt"
1371 )),
1372 Ok((path!(buf "/foo/bar/baz/qux/hello.txt"), 5)),
1373 "canonicalizing a crazily stupid path name",
1374 );
1375 }
1376}
1377
1378#[allow(dead_code)] pub(super) enum DirectoryMustBeEmpty {
1380 Yes,
1381 No,
1382}
1383
1384impl DirectoryMustBeEmpty {
1385 pub(super) fn yes(&self) -> bool {
1386 matches!(self, Self::Yes)
1387 }
1388
1389 pub(super) fn no(&self) -> bool {
1390 !self.yes()
1391 }
1392}