1use 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#[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 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 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 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 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 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 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 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 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 let guard = self.inner.read().map_err(|_| FsError::Lock)?;
194
195 let path = guard.canonicalize_without_inode(target_path.as_path())?;
198
199 let parent_of_path = path.parent().ok_or(FsError::BaseNotDirectory)?;
201
202 let name_of_directory = path
204 .file_name()
205 .ok_or(FsError::InvalidInput)?
206 .to_os_string();
207
208 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 let mut fs = self.inner.write().map_err(|_| FsError::Lock)?;
222
223 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 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 let guard = self.inner.read().map_err(|_| FsError::Lock)?;
263
264 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 let guard = self.inner.read().map_err(|_| FsError::Lock)?;
275
276 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 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 let guard = self.inner.read().map_err(|_| FsError::Lock)?;
320
321 let path = guard.canonicalize_without_inode(path)?;
324
325 let parent_of_path = path.parent().ok_or(FsError::BaseNotDirectory)?;
327
328 let name_of_directory = path
330 .file_name()
331 .ok_or(FsError::InvalidInput)?
332 .to_os_string();
333
334 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 let mut fs = self.inner.write().map_err(|_| FsError::Lock)?;
354
355 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 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 let guard = self.inner.read().map_err(|_| FsError::Lock)?;
393
394 let (path, _) = guard.canonicalize(path)?;
396
397 let parent_of_path = path.parent().ok_or(FsError::BaseNotDirectory)?;
399
400 let name_of_directory = path
402 .file_name()
403 .ok_or(FsError::InvalidInput)?
404 .to_os_string();
405
406 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 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 let mut fs = self.inner.write().map_err(|_| FsError::Lock)?;
438
439 fs.storage.remove(inode_of_directory);
441
442 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 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 let parent_of_from = from.parent().ok_or(FsError::BaseNotDirectory)?;
462 let parent_of_to = to.parent().ok_or(FsError::BaseNotDirectory)?;
463
464 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 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 (Either::Left(inode_of_from_parent), Either::Left(inode_of_to_parent)) => {
498 let fs = self.inner.read().map_err(|_| FsError::Lock)?;
499
500 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 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 let mut fs = self.inner.write().map_err(|_| FsError::Lock)?;
535
536 if let Some((position, inode_of_file)) = inode_dest {
537 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 fs.update_node_name(inode, name_of_to)?;
552
553 if inode_of_from_parent != inode_of_to_parent {
555 fs.remove_child_from_node(inode_of_from_parent, position_of_from)?;
558
559 fs.add_child_to_node(inode_of_to_parent, inode)?;
562 }
563 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 (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 _ => {
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 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 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 let guard = self.inner.read().map_err(|_| FsError::Lock)?;
646
647 let path = guard.canonicalize_without_inode(path)?;
649
650 let parent_of_path = path.parent().ok_or(FsError::BaseNotDirectory)?;
652
653 let name_of_file = path
655 .file_name()
656 .ok_or(FsError::InvalidInput)?
657 .to_os_string();
658
659 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 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 let mut fs = self.inner.write().map_err(|_| FsError::Lock)?;
688
689 fs.storage.remove(inode_of_file);
691
692 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
722pub(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 pub(super) fn inode_of(&self, path: &Path) -> Result<InodeResolution> {
751 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 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 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 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), 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 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 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 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 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 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 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 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 Component::RootDir => return Err(FsError::UnknownError),
1022
1023 Component::CurDir => (),
1025
1026 Component::ParentDir => {
1029 if !new_path.pop() {
1030 return Err(FsError::InvalidInput);
1031 }
1032 }
1033
1034 Component::Normal(name) => {
1036 new_path.push(name);
1037 }
1038
1039 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)] pub(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}