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