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