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