1use std::any::Any;
19use std::collections::HashMap;
20use std::ffi::CStr;
21use std::fmt;
22use std::io;
23use std::io::{Error, ErrorKind, Result};
24use std::ops::Deref;
25use std::sync::atomic::{AtomicBool, AtomicU8, Ordering};
26use std::sync::{Arc, Mutex};
27use std::time::Duration;
28
29use arc_swap::ArcSwap;
30
31use crate::abi::fuse_abi::*;
32use crate::api::filesystem::*;
33use crate::api::pseudo_fs::PseudoFs;
34
35#[cfg(feature = "async-io")]
36mod async_io;
37mod sync_io;
38
39pub const CURRENT_DIR_CSTR: &[u8] = b".\0";
41pub const PARENT_DIR_CSTR: &[u8] = b"..\0";
43pub const EMPTY_CSTR: &[u8] = b"\0";
45pub const PROC_SELF_FD_CSTR: &[u8] = b"/proc/self/fd\0";
47pub const SLASH_ASCII: u8 = 47;
49
50pub const VFS_MAX_INO: u64 = 0xff_ffff_ffff_ffff;
52
53const VFS_INDEX_SHIFT: u8 = 56;
57const VFS_PSEUDO_FS_IDX: VfsIndex = 0;
58
59type ArcBackFs = Arc<BackFileSystem>;
60type ArcSuperBlock = ArcSwap<Vec<Option<Arc<BackFileSystem>>>>;
61type VfsEitherFs<'a> = Either<&'a PseudoFs, ArcBackFs>;
62
63type VfsHandle = u64;
64pub type VfsIndex = u8;
66
67const MAX_VFS_INDEX: usize = 256;
69
70#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)]
72pub struct VfsInode(u64);
73
74#[derive(Debug)]
76pub enum VfsError {
77 Unsupported,
79 Mount(Error),
81 RestoreMount(Error),
83 InodeIndex(String),
85 FsIndex(Error),
87 PathWalk(Error),
89 NotFound(String),
91 Initialize(String),
93 Persist(String),
95}
96
97impl fmt::Display for VfsError {
98 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
99 use self::VfsError::*;
100 match self {
101 Unsupported => write!(f, "Vfs operation not supported"),
102 Mount(e) => write!(f, "Mount backend filesystem: {e}"),
103 RestoreMount(e) => write!(f, "Restore mount backend filesystem: {e}"),
104 InodeIndex(s) => write!(f, "Illegal inode index: {s}"),
105 FsIndex(e) => write!(f, "Filesystem index error: {e}"),
106 PathWalk(e) => write!(f, "Walking path error: {e}"),
107 NotFound(s) => write!(f, "Entry can't be found: {s}"),
108 Initialize(s) => write!(f, "File system can't be initialized: {s}"),
109 Persist(e) => write!(f, "Error serializing: {e}"),
110 }
111 }
112}
113
114impl std::error::Error for VfsError {}
115
116pub type VfsResult<T> = std::result::Result<T, VfsError>;
118
119#[inline]
120fn is_dot_or_dotdot(name: &CStr) -> bool {
121 let bytes = name.to_bytes_with_nul();
122 bytes.starts_with(CURRENT_DIR_CSTR) || bytes.starts_with(PARENT_DIR_CSTR)
123}
124
125fn is_safe_path_component(name: &CStr) -> bool {
127 let bytes = name.to_bytes_with_nul();
128
129 if bytes.contains(&SLASH_ASCII) {
130 return false;
131 }
132 !is_dot_or_dotdot(name)
133}
134
135#[inline]
139pub fn validate_path_component(name: &CStr) -> io::Result<()> {
140 match is_safe_path_component(name) {
141 true => Ok(()),
142 false => Err(io::Error::from_raw_os_error(libc::EINVAL)),
143 }
144}
145
146impl VfsInode {
147 fn new(fs_idx: VfsIndex, ino: u64) -> Self {
148 assert_eq!(ino & !VFS_MAX_INO, 0);
149 VfsInode(((fs_idx as u64) << VFS_INDEX_SHIFT) | ino)
150 }
151
152 fn is_pseudo_fs(&self) -> bool {
153 (self.0 >> VFS_INDEX_SHIFT) as VfsIndex == VFS_PSEUDO_FS_IDX
154 }
155
156 fn fs_idx(&self) -> VfsIndex {
157 (self.0 >> VFS_INDEX_SHIFT) as VfsIndex
158 }
159
160 fn ino(&self) -> u64 {
161 self.0 & VFS_MAX_INO
162 }
163}
164
165impl From<u64> for VfsInode {
166 fn from(val: u64) -> Self {
167 VfsInode(val)
168 }
169}
170
171impl From<VfsInode> for u64 {
172 fn from(val: VfsInode) -> Self {
173 val.0
174 }
175}
176
177#[derive(Debug, Clone)]
178enum Either<A, B> {
179 Left(A),
181 Right(B),
183}
184use Either::*;
185
186pub type BackFileSystem = Box<dyn BackendFileSystem<Inode = u64, Handle = u64> + Sync + Send>;
188
189#[cfg(not(feature = "async-io"))]
190pub trait BackendFileSystem: FileSystem {
192 fn mount(&self) -> Result<(Entry, u64)> {
195 Err(Error::from_raw_os_error(libc::ENOSYS))
196 }
197
198 fn as_any(&self) -> &dyn Any;
202}
203
204#[cfg(feature = "async-io")]
205pub trait BackendFileSystem: AsyncFileSystem {
207 fn mount(&self) -> Result<(Entry, u64)> {
210 Err(Error::from_raw_os_error(libc::ENOSYS))
211 }
212
213 fn as_any(&self) -> &dyn Any;
217}
218
219struct MountPointData {
220 fs_idx: VfsIndex,
221 ino: u64,
222 root_entry: Entry,
223 _path: String,
224}
225
226#[derive(Debug, Copy, Clone)]
227pub struct VfsOptions {
229 pub no_readdir: bool,
231 pub seal_size: bool,
234 pub in_opts: FsOptions,
236 pub out_opts: FsOptions,
238 pub id_mapping: (u32, u32, u32),
242
243 #[cfg(target_os = "linux")]
246 pub no_open: bool,
247 #[cfg(target_os = "linux")]
250 pub no_opendir: bool,
251 #[cfg(target_os = "linux")]
254 pub no_writeback: bool,
255 #[cfg(target_os = "linux")]
259 pub killpriv_v2: bool,
260}
261
262impl VfsOptions {
263 fn new() -> Self {
264 VfsOptions::default()
265 }
266}
267
268impl Default for VfsOptions {
269 #[cfg(target_os = "linux")]
270 fn default() -> Self {
271 let out_opts = FsOptions::ASYNC_READ
272 | FsOptions::PARALLEL_DIROPS
273 | FsOptions::BIG_WRITES
274 | FsOptions::ASYNC_DIO
275 | FsOptions::AUTO_INVAL_DATA
276 | FsOptions::HAS_IOCTL_DIR
277 | FsOptions::WRITEBACK_CACHE
278 | FsOptions::ZERO_MESSAGE_OPEN
279 | FsOptions::MAX_PAGES
280 | FsOptions::ATOMIC_O_TRUNC
281 | FsOptions::CACHE_SYMLINKS
282 | FsOptions::DO_READDIRPLUS
283 | FsOptions::READDIRPLUS_AUTO
284 | FsOptions::EXPLICIT_INVAL_DATA
285 | FsOptions::ZERO_MESSAGE_OPENDIR
286 | FsOptions::HANDLE_KILLPRIV_V2
287 | FsOptions::PERFILE_DAX;
288 VfsOptions {
289 no_open: true,
290 no_opendir: true,
291 no_writeback: false,
292 no_readdir: false,
293 seal_size: false,
294 killpriv_v2: false,
295 in_opts: FsOptions::empty(),
296 out_opts,
297 id_mapping: (0, 0, 0),
298 }
299 }
300
301 #[cfg(target_os = "macos")]
302 fn default() -> Self {
303 let out_opts = FsOptions::ASYNC_READ | FsOptions::BIG_WRITES | FsOptions::ATOMIC_O_TRUNC;
304 VfsOptions {
305 no_readdir: false,
306 seal_size: false,
307 in_opts: FsOptions::empty(),
308 out_opts,
309 id_mapping: (0, 0, 0),
310 }
311 }
312}
313
314pub struct Vfs {
316 next_super: AtomicU8,
317 root: PseudoFs,
318 mountpoints: ArcSwap<HashMap<u64, Arc<MountPointData>>>,
320 superblocks: ArcSuperBlock,
322 opts: ArcSwap<VfsOptions>,
323 initialized: AtomicBool,
324 lock: Mutex<()>,
325 remove_pseudo_root: bool,
326 id_mapping: Option<(u32, u32, u32)>,
327}
328
329impl Default for Vfs {
330 fn default() -> Self {
331 Self::new(VfsOptions::new())
332 }
333}
334
335impl Vfs {
336 pub fn new(opts: VfsOptions) -> Self {
338 Vfs {
339 next_super: AtomicU8::new(VFS_PSEUDO_FS_IDX + 1),
340 mountpoints: ArcSwap::new(Arc::new(HashMap::new())),
341 superblocks: ArcSwap::new(Arc::new(vec![None; MAX_VFS_INDEX])),
342 root: PseudoFs::new(),
343 opts: ArcSwap::new(Arc::new(opts)),
344 lock: Mutex::new(()),
345 initialized: AtomicBool::new(false),
346 remove_pseudo_root: false,
347 id_mapping: match opts.id_mapping.2 {
348 0 => None,
349 _ => Some(opts.id_mapping),
350 },
351 }
352 }
353
354 pub fn set_remove_pseudo_root(&mut self) {
356 self.remove_pseudo_root = true;
357 }
358
359 pub fn initialized(&self) -> bool {
362 self.initialized.load(Ordering::Acquire)
363 }
364
365 pub fn options(&self) -> VfsOptions {
367 *self.opts.load_full()
368 }
369
370 fn insert_mount_locked(
371 &self,
372 fs: BackFileSystem,
373 mut entry: Entry,
374 fs_idx: VfsIndex,
375 path: &str,
376 ) -> Result<()> {
377 let mut superblocks = self.superblocks.load().deref().deref().clone();
381 let mut mountpoints = self.mountpoints.load().deref().deref().clone();
382 let inode = self.root.mount(path)?;
383 let real_root_ino = entry.inode;
384
385 self.convert_entry(fs_idx, entry.inode, &mut entry)?;
386
387 if let Some(mnt) = mountpoints.get(&inode) {
389 superblocks[mnt.fs_idx as usize] = None;
390 }
391 superblocks[fs_idx as usize] = Some(Arc::new(fs));
392 self.superblocks.store(Arc::new(superblocks));
393 trace!("fs_idx {} inode {}", fs_idx, inode);
394
395 let mountpoint = Arc::new(MountPointData {
396 fs_idx,
397 ino: real_root_ino,
398 root_entry: entry,
399 _path: path.to_string(),
400 });
401 mountpoints.insert(inode, mountpoint);
402 self.mountpoints.store(Arc::new(mountpoints));
403
404 Ok(())
405 }
406
407 pub fn mount(&self, fs: BackFileSystem, path: &str) -> VfsResult<VfsIndex> {
409 let (entry, ino) = fs.mount().map_err(VfsError::Mount)?;
410 if ino > VFS_MAX_INO {
411 fs.destroy();
412 return Err(VfsError::InodeIndex(format!(
413 "Unsupported max inode number, requested {ino} supported {VFS_MAX_INO}"
414 )));
415 }
416
417 let _guard = self.lock.lock().unwrap();
419 if self.initialized() {
420 let opts = self.opts.load().deref().out_opts;
421 fs.init(opts).map_err(|e| {
422 VfsError::Initialize(format!("Can't initialize with opts {opts:?}, {e:?}"))
423 })?;
424 }
425 let index = self.allocate_fs_idx().map_err(VfsError::FsIndex)?;
426 self.insert_mount_locked(fs, entry, index, path)
427 .map_err(VfsError::Mount)?;
428
429 Ok(index)
430 }
431
432 #[cfg(feature = "persist")]
434 pub fn restore_mount(&self, fs: BackFileSystem, fs_idx: VfsIndex, path: &str) -> Result<()> {
435 let (entry, ino) = fs.mount()?;
436 if ino > VFS_MAX_INO {
437 return Err(Error::new(
438 ErrorKind::Other,
439 format!(
440 "Unsupported max inode number, requested {} supported {}",
441 ino, VFS_MAX_INO
442 ),
443 ));
444 }
445
446 let _guard = self.lock.lock().unwrap();
447 self.insert_mount_locked(fs, entry, fs_idx, path)
448 }
449
450 pub fn umount(&self, path: &str) -> VfsResult<(u64, u64)> {
452 let _guard = self.lock.lock().unwrap();
454 let inode = self
455 .root
456 .path_walk(path)
457 .map_err(VfsError::PathWalk)?
458 .ok_or_else(|| VfsError::NotFound(path.to_string()))?;
459 let parent = self
460 .root
461 .get_parent_inode(inode)
462 .ok_or(VfsError::NotFound(format!(
463 "{}'s parent inode does not exist",
464 inode
465 )))?;
466 let mut mountpoints = self.mountpoints.load().deref().deref().clone();
467 let fs_idx = mountpoints
468 .get(&inode)
469 .map(Arc::clone)
470 .map(|x| {
471 if self.remove_pseudo_root {
481 self.root.evict_inode(inode);
482 }
483 mountpoints.remove(&inode);
484 self.mountpoints.store(Arc::new(mountpoints));
485 x.fs_idx
486 })
487 .ok_or_else(|| {
488 error!("{} is not a mount point.", path);
489 VfsError::NotFound(path.to_string())
490 })?;
491
492 trace!("fs_idx {}", fs_idx);
493 let mut superblocks = self.superblocks.load().deref().deref().clone();
494 if let Some(fs) = superblocks[fs_idx as usize].take() {
495 fs.destroy();
496 }
497 self.superblocks.store(Arc::new(superblocks));
498
499 Ok((inode, parent))
500 }
501
502 pub fn get_rootfs(&self, path: &str) -> VfsResult<Option<Arc<BackFileSystem>>> {
504 let _guard = self.lock.lock().unwrap();
506 let inode = match self.root.path_walk(path).map_err(VfsError::PathWalk)? {
507 Some(i) => i,
508 None => return Ok(None),
509 };
510
511 if let Some(mnt) = self.mountpoints.load().get(&inode) {
512 Ok(Some(self.get_fs_by_idx(mnt.fs_idx).map_err(|e| {
513 VfsError::NotFound(format!("fs index {}, {:?}", mnt.fs_idx, e))
514 })?))
515 } else {
516 Ok(None)
519 }
520 }
521
522 pub fn get_root_pseudofs(&self) -> &PseudoFs {
524 &self.root
525 }
526
527 fn convert_inode(&self, fs_idx: VfsIndex, inode: u64) -> Result<u64> {
533 if inode == 0 {
535 return Ok(inode);
536 }
537 if inode > VFS_MAX_INO {
538 return Err(Error::new(
539 ErrorKind::Other,
540 format!("Inode number {inode} too large, max supported {VFS_MAX_INO}"),
541 ));
542 }
543 let ino: u64 = ((fs_idx as u64) << VFS_INDEX_SHIFT) | inode;
544 trace!(
545 "fuse: vfs fs_idx {} inode {} fuse ino {:#x}",
546 fs_idx,
547 inode,
548 ino
549 );
550 Ok(ino)
551 }
552
553 fn convert_entry(&self, fs_idx: VfsIndex, inode: u64, entry: &mut Entry) -> Result<Entry> {
554 self.convert_inode(fs_idx, inode).map(|ino| {
555 entry.inode = ino;
556 entry.attr.st_ino = ino;
557 if let Some((internal_id, external_id, range)) = self.id_mapping {
559 if entry.attr.st_uid >= internal_id && entry.attr.st_uid < internal_id + range {
560 entry.attr.st_uid += external_id - internal_id;
561 }
562 if entry.attr.st_gid >= internal_id && entry.attr.st_gid < internal_id + range {
563 entry.attr.st_gid += external_id - internal_id;
564 }
565 }
566 *entry
567 })
568 }
569
570 fn remap_attr_id(&self, map_internal_to_external: bool, attr: &mut stat64) {
577 if let Some((internal_id, external_id, range)) = self.id_mapping {
578 if map_internal_to_external
579 && attr.st_uid >= internal_id
580 && attr.st_uid < internal_id + range
581 {
582 attr.st_uid += external_id - internal_id;
583 }
584 if map_internal_to_external
585 && attr.st_gid >= internal_id
586 && attr.st_gid < internal_id + range
587 {
588 attr.st_gid += external_id - internal_id;
589 }
590 if !map_internal_to_external
591 && attr.st_uid >= external_id
592 && attr.st_uid < external_id + range
593 {
594 attr.st_uid += internal_id - external_id;
595 }
596 if !map_internal_to_external
597 && attr.st_gid >= external_id
598 && attr.st_gid < external_id + range
599 {
600 attr.st_gid += internal_id - external_id;
601 }
602 }
603 }
604
605 fn allocate_fs_idx(&self) -> Result<VfsIndex> {
606 let superblocks = self.superblocks.load().deref().deref().clone();
607 let start = self.next_super.load(Ordering::SeqCst);
608 let mut found = false;
609
610 loop {
611 let index = self.next_super.fetch_add(1, Ordering::Relaxed);
612 if index == start {
613 if found {
614 break;
616 } else {
617 found = true;
618 }
619 }
620
621 if index == VFS_PSEUDO_FS_IDX {
622 continue;
624 }
625 if (index as usize) < superblocks.len() && superblocks[index as usize].is_some() {
626 continue;
628 } else {
629 return Ok(index);
630 }
631 }
632
633 Err(Error::new(
634 ErrorKind::Other,
635 "vfs maximum mountpoints reached",
636 ))
637 }
638
639 fn get_fs_by_idx(&self, fs_idx: VfsIndex) -> Result<Arc<BackFileSystem>> {
640 let superblocks = self.superblocks.load();
641
642 if let Some(fs) = &superblocks[fs_idx as usize] {
643 return Ok(fs.clone());
644 }
645
646 Err(Error::from_raw_os_error(libc::ENOENT))
647 }
648
649 fn get_real_rootfs(&self, inode: VfsInode) -> Result<(VfsEitherFs<'_>, VfsInode)> {
650 if inode.is_pseudo_fs() {
651 if inode.ino() == ROOT_ID {
653 if let Some(mnt) = self.mountpoints.load().get(&inode.ino()).map(Arc::clone) {
654 let fs = self.get_fs_by_idx(mnt.fs_idx)?;
655 return Ok((Right(fs), VfsInode::new(mnt.fs_idx, mnt.ino)));
656 }
657 }
658 Ok((Left(&self.root), inode))
659 } else {
660 let fs = self.get_fs_by_idx(inode.fs_idx())?;
661 Ok((Right(fs), inode))
662 }
663 }
664
665 fn lookup_pseudo(
666 &self,
667 fs: &PseudoFs,
668 idata: VfsInode,
669 ctx: &Context,
670 name: &CStr,
671 ) -> Result<Entry> {
672 trace!("lookup pseudo ino {} name {:?}", idata.ino(), name);
673 let mut entry = fs.lookup(ctx, idata.ino(), name)?;
674
675 match self.mountpoints.load().get(&entry.inode) {
676 Some(mnt) => {
677 entry = mnt.root_entry;
679 self.convert_entry(mnt.fs_idx, mnt.ino, &mut entry)?;
680 trace!(
681 "vfs lookup cross mountpoint, return new mount fs_idx {} inode 0x{:x} fuse inode 0x{:x}, attr inode 0x{:x}",
682 mnt.fs_idx,
683 mnt.ino,
684 entry.inode,
685 entry.attr.st_ino,
686 );
687 Ok(entry)
688 }
689 None => self.convert_entry(idata.fs_idx(), entry.inode, &mut entry),
690 }
691 }
692}
693
694#[cfg(feature = "persist")]
696pub mod persist {
697 use std::{
698 ops::Deref,
699 sync::{atomic::Ordering, Arc},
700 };
701
702 use dbs_snapshot::Snapshot;
703 use versionize::{VersionMap, Versionize, VersionizeResult};
704 use versionize_derive::Versionize;
705
706 use crate::api::{
707 filesystem::FsOptions,
708 pseudo_fs::persist::PseudoFsState,
709 vfs::{VfsError, VfsResult},
710 Vfs, VfsOptions,
711 };
712
713 #[derive(Versionize, Debug)]
715 struct VfsState {
716 options: VfsOptionsState,
718 root: Vec<u8>,
720 next_super: u8,
722 }
723
724 #[derive(Versionize, Debug, Default)]
725 struct VfsOptionsState {
726 in_opts: u64,
727 out_opts: u64,
728 no_readdir: bool,
729 seal_size: bool,
730 id_mapping_internal: u32,
731 id_mapping_external: u32,
732 id_mapping_range: u32,
733
734 #[cfg(target_os = "linux")]
735 no_open: bool,
736 #[cfg(target_os = "linux")]
737 no_opendir: bool,
738 #[cfg(target_os = "linux")]
739 no_writeback: bool,
740 #[cfg(target_os = "linux")]
741 killpriv_v2: bool,
742 }
743
744 impl VfsOptions {
745 fn save(&self) -> VfsOptionsState {
746 VfsOptionsState {
747 in_opts: self.in_opts.bits(),
748 out_opts: self.out_opts.bits(),
749 no_readdir: self.no_readdir,
750 seal_size: self.seal_size,
751 id_mapping_internal: self.id_mapping.0,
752 id_mapping_external: self.id_mapping.1,
753 id_mapping_range: self.id_mapping.2,
754
755 #[cfg(target_os = "linux")]
756 no_open: self.no_open,
757 #[cfg(target_os = "linux")]
758 no_opendir: self.no_opendir,
759 #[cfg(target_os = "linux")]
760 no_writeback: self.no_writeback,
761 #[cfg(target_os = "linux")]
762 killpriv_v2: self.killpriv_v2,
763 }
764 }
765
766 fn restore(state: &VfsOptionsState) -> VfsResult<VfsOptions> {
767 Ok(VfsOptions {
768 in_opts: FsOptions::from_bits(state.in_opts).ok_or(VfsError::Persist(
769 "Failed to restore VfsOptions.in_opts".to_owned(),
770 ))?,
771 out_opts: FsOptions::from_bits(state.out_opts).ok_or(VfsError::Persist(
772 "Failed to restore VfsOptions.out_opts".to_owned(),
773 ))?,
774 no_readdir: state.no_readdir,
775 seal_size: state.seal_size,
776 id_mapping: (
777 state.id_mapping_internal,
778 state.id_mapping_external,
779 state.id_mapping_range,
780 ),
781
782 #[cfg(target_os = "linux")]
783 no_open: state.no_open,
784 #[cfg(target_os = "linux")]
785 no_opendir: state.no_opendir,
786 #[cfg(target_os = "linux")]
787 no_writeback: state.no_writeback,
788 #[cfg(target_os = "linux")]
789 killpriv_v2: state.killpriv_v2,
790 })
791 }
792 }
793
794 impl Vfs {
795 fn get_version_map() -> versionize::VersionMap {
796 let mut version_map = VersionMap::new();
797 version_map
798 .set_type_version(VfsState::type_id(), 1)
799 .set_type_version(PseudoFsState::type_id(), 1)
800 .set_type_version(VfsOptionsState::type_id(), 1);
801
802 version_map
805 }
806
807 pub fn save_to_bytes(&self) -> VfsResult<Vec<u8>> {
864 let root_state = self
865 .root
866 .save_to_bytes()
867 .map_err(|e| VfsError::Persist(format!("Failed to save Vfs root: {:?}", e)))?;
868 let vfs_state = VfsState {
869 options: self.opts.load().deref().deref().save(),
870 root: root_state,
871 next_super: self.next_super.load(Ordering::SeqCst),
872 };
873
874 let vm = Vfs::get_version_map();
875 let target_version = vm.latest_version();
876 let mut s = Snapshot::new(vm, target_version);
877 let mut buf = Vec::new();
878 s.save(&mut buf, &vfs_state).map_err(|e| {
879 VfsError::Persist(format!("Failed to save Vfs using snapshot: {:?}", e))
880 })?;
881
882 Ok(buf)
883 }
884
885 pub fn restore_from_bytes(&self, buf: &mut Vec<u8>) -> VfsResult<()> {
888 let mut state: VfsState =
889 Snapshot::load(&mut buf.as_slice(), buf.len(), Vfs::get_version_map())
890 .map_err(|e| {
891 VfsError::Persist(format!("Failed to load Vfs using snapshot: {:?}", e))
892 })?
893 .0;
894 let opts = VfsOptions::restore(&state.options)?;
895 self.initialized
896 .store(!opts.in_opts.is_empty(), Ordering::Release);
897 self.opts.store(Arc::new(opts));
898
899 self.next_super.store(state.next_super, Ordering::SeqCst);
900 self.root
901 .restore_from_bytes(&mut state.root)
902 .map_err(|e| VfsError::Persist(format!("Failed to restore Vfs root: {:?}", e)))?;
903
904 Ok(())
905 }
906 }
907
908 mod test {
909
910 #[test]
912 fn test_vfs_save_restore_simple() {
913 use crate::api::{Vfs, VfsOptions};
914
915 let vfs = &Vfs::new(VfsOptions::default());
917
918 let mut buf = vfs.save_to_bytes().unwrap();
920
921 let vfs = &Vfs::new(VfsOptions::default());
923 vfs.restore_from_bytes(&mut buf).unwrap();
924 assert_eq!(vfs.next_super.load(std::sync::atomic::Ordering::SeqCst), 1);
925 }
926
927 #[test]
928 fn test_vfs_save_restore_with_backend_fs() {
929 use crate::api::{Vfs, VfsIndex, VfsOptions};
930 use crate::passthrough::{Config, PassthroughFs};
931
932 let new_backend_fs = || {
933 let fs_cfg = Config::default();
934 let fs = PassthroughFs::<()>::new(fs_cfg.clone()).unwrap();
935 fs.import().unwrap();
936 Box::new(fs)
937 };
938
939 let vfs = &Vfs::new(VfsOptions::default());
941 let paths = vec!["/a", "/a/b", "/a/b/c", "/b", "/b/a/c", "/d"];
942 let backend_fs_list: Vec<(&str, VfsIndex)> = paths
943 .iter()
944 .map(|path| {
945 let fs = new_backend_fs();
946 let idx = vfs.mount(fs, path).unwrap();
947
948 (path.to_owned(), idx)
949 })
950 .collect();
951
952 let mut buf = vfs.save_to_bytes().unwrap();
954
955 let restored_vfs = &Vfs::new(VfsOptions::default());
957 restored_vfs.restore_from_bytes(&mut buf).unwrap();
958 backend_fs_list.into_iter().for_each(|(path, idx)| {
960 let fs = new_backend_fs();
961 vfs.restore_mount(fs, idx, path).unwrap();
962 });
963
964 assert_eq!(
966 vfs.next_super.load(std::sync::atomic::Ordering::SeqCst),
967 restored_vfs
968 .next_super
969 .load(std::sync::atomic::Ordering::SeqCst)
970 );
971 assert_eq!(
972 vfs.initialized.load(std::sync::atomic::Ordering::SeqCst),
973 restored_vfs
974 .initialized
975 .load(std::sync::atomic::Ordering::SeqCst)
976 );
977 for path in paths.iter() {
978 let inode = vfs.root.path_walk(path).unwrap();
979 let restored_inode = restored_vfs.root.path_walk(path).unwrap();
980 assert_eq!(inode, restored_inode);
981 }
982 }
983
984 #[test]
985 fn test_vfs_save_restore_with_backend_fs_with_initialized() {
986 use crate::api::filesystem::{FileSystem, FsOptions};
987 use crate::api::{Vfs, VfsIndex, VfsOptions};
988 use crate::passthrough::{Config, PassthroughFs};
989 use std::sync::atomic::Ordering;
990
991 let new_backend_fs = || {
992 let fs_cfg = Config::default();
993 let fs = PassthroughFs::<()>::new(fs_cfg.clone()).unwrap();
994 fs.import().unwrap();
995 Box::new(fs)
996 };
997
998 let vfs = &Vfs::new(VfsOptions::default());
1000 let paths = vec!["/a", "/a/b", "/a/b/c", "/b", "/b/a/c", "/d"];
1001 let backend_fs_list: Vec<(&str, VfsIndex)> = paths
1002 .iter()
1003 .map(|path| {
1004 let fs = new_backend_fs();
1005 let idx = vfs.mount(fs, path).unwrap();
1006
1007 (path.to_owned(), idx)
1008 })
1009 .collect();
1010 vfs.init(FsOptions::ASYNC_READ).unwrap();
1011 assert!(vfs.initialized.load(Ordering::Acquire));
1012
1013 let mut buf = vfs.save_to_bytes().unwrap();
1015
1016 let restored_vfs = &Vfs::new(VfsOptions::default());
1018 restored_vfs.restore_from_bytes(&mut buf).unwrap();
1019
1020 backend_fs_list.into_iter().for_each(|(path, idx)| {
1022 let fs = new_backend_fs();
1023 vfs.restore_mount(fs, idx, path).unwrap();
1024 });
1025
1026 assert_eq!(
1028 vfs.next_super.load(std::sync::atomic::Ordering::SeqCst),
1029 restored_vfs
1030 .next_super
1031 .load(std::sync::atomic::Ordering::SeqCst)
1032 );
1033 assert_eq!(
1034 vfs.initialized.load(std::sync::atomic::Ordering::Acquire),
1035 restored_vfs
1036 .initialized
1037 .load(std::sync::atomic::Ordering::Acquire)
1038 );
1039 for path in paths.iter() {
1040 let inode = vfs.root.path_walk(path).unwrap();
1041 let restored_inode = restored_vfs.root.path_walk(path).unwrap();
1042 assert_eq!(inode, restored_inode);
1043 }
1044 }
1045 }
1046}
1047
1048#[cfg(test)]
1049mod tests {
1050 use super::*;
1051 use crate::api::Vfs;
1052 use std::ffi::CString;
1053 use std::io::{Error, ErrorKind};
1054
1055 pub(crate) struct FakeFileSystemOne {}
1056 impl FileSystem for FakeFileSystemOne {
1057 type Inode = u64;
1058 type Handle = u64;
1059 fn lookup(&self, _: &Context, _: Self::Inode, _: &CStr) -> Result<Entry> {
1060 Ok(Entry::default())
1061 }
1062 fn getattr(
1063 &self,
1064 _ctx: &Context,
1065 _inode: Self::Inode,
1066 _handle: Option<Self::Handle>,
1067 ) -> Result<(stat64, Duration)> {
1068 let mut attr = Attr {
1069 ..Default::default()
1070 };
1071 attr.ino = 1;
1072 Ok((attr.into(), Duration::from_secs(1)))
1073 }
1074 }
1075
1076 pub(crate) struct FakeFileSystemTwo {}
1077 impl FileSystem for FakeFileSystemTwo {
1078 type Inode = u64;
1079 type Handle = u64;
1080 fn lookup(&self, _: &Context, _: Self::Inode, _: &CStr) -> Result<Entry> {
1081 Ok(Entry {
1082 inode: 1,
1083 ..Default::default()
1084 })
1085 }
1086 }
1087
1088 #[test]
1089 fn test_is_safe_path_component() {
1090 let name = CStr::from_bytes_with_nul(b"normal\0").unwrap();
1091 assert!(is_safe_path_component(name), "\"{:?}\"", name);
1092
1093 let name = CStr::from_bytes_with_nul(b".a\0").unwrap();
1094 assert!(is_safe_path_component(name));
1095
1096 let name = CStr::from_bytes_with_nul(b"a.a\0").unwrap();
1097 assert!(is_safe_path_component(name));
1098
1099 let name = CStr::from_bytes_with_nul(b"a.a\0").unwrap();
1100 assert!(is_safe_path_component(name));
1101
1102 let name = CStr::from_bytes_with_nul(b"/\0").unwrap();
1103 assert!(!is_safe_path_component(name));
1104
1105 let name = CStr::from_bytes_with_nul(b"/a\0").unwrap();
1106 assert!(!is_safe_path_component(name));
1107
1108 let name = CStr::from_bytes_with_nul(b".\0").unwrap();
1109 assert!(!is_safe_path_component(name));
1110
1111 let name = CStr::from_bytes_with_nul(b"..\0").unwrap();
1112 assert!(!is_safe_path_component(name));
1113
1114 let name = CStr::from_bytes_with_nul(b"../.\0").unwrap();
1115 assert!(!is_safe_path_component(name));
1116
1117 let name = CStr::from_bytes_with_nul(b"a/b\0").unwrap();
1118 assert!(!is_safe_path_component(name));
1119
1120 let name = CStr::from_bytes_with_nul(b"./../a\0").unwrap();
1121 assert!(!is_safe_path_component(name));
1122 }
1123
1124 #[test]
1125 fn test_is_dot_or_dotdot() {
1126 let name = CStr::from_bytes_with_nul(b"..\0").unwrap();
1127 assert!(is_dot_or_dotdot(name));
1128
1129 let name = CStr::from_bytes_with_nul(b".\0").unwrap();
1130 assert!(is_dot_or_dotdot(name));
1131
1132 let name = CStr::from_bytes_with_nul(b"...\0").unwrap();
1133 assert!(!is_dot_or_dotdot(name));
1134
1135 let name = CStr::from_bytes_with_nul(b"./.\0").unwrap();
1136 assert!(!is_dot_or_dotdot(name));
1137
1138 let name = CStr::from_bytes_with_nul(b"a\0").unwrap();
1139 assert!(!is_dot_or_dotdot(name));
1140
1141 let name = CStr::from_bytes_with_nul(b"aa\0").unwrap();
1142 assert!(!is_dot_or_dotdot(name));
1143
1144 let name = CStr::from_bytes_with_nul(b"/a\0").unwrap();
1145 assert!(!is_dot_or_dotdot(name));
1146
1147 let name = CStr::from_bytes_with_nul(b"a/\0").unwrap();
1148 assert!(!is_dot_or_dotdot(name));
1149 }
1150
1151 #[cfg(feature = "async-io")]
1152 mod async_io {
1153 use super::*;
1154 use crate::abi::fuse_abi::{OpenOptions, SetattrValid};
1155 use async_trait::async_trait;
1156
1157 #[allow(unused_variables)]
1158 #[async_trait]
1159 impl AsyncFileSystem for FakeFileSystemOne {
1160 async fn async_lookup(
1161 &self,
1162 ctx: &Context,
1163 parent: <Self as FileSystem>::Inode,
1164 name: &CStr,
1165 ) -> Result<Entry> {
1166 Ok(Entry::default())
1167 }
1168
1169 async fn async_getattr(
1170 &self,
1171 ctx: &Context,
1172 inode: <Self as FileSystem>::Inode,
1173 handle: Option<<Self as FileSystem>::Handle>,
1174 ) -> Result<(libc::stat64, Duration)> {
1175 unimplemented!()
1176 }
1177
1178 async fn async_setattr(
1179 &self,
1180 ctx: &Context,
1181 inode: <Self as FileSystem>::Inode,
1182 attr: libc::stat64,
1183 handle: Option<<Self as FileSystem>::Handle>,
1184 valid: SetattrValid,
1185 ) -> Result<(libc::stat64, Duration)> {
1186 unimplemented!()
1187 }
1188
1189 async fn async_open(
1190 &self,
1191 ctx: &Context,
1192 inode: <Self as FileSystem>::Inode,
1193 flags: u32,
1194 fuse_flags: u32,
1195 ) -> Result<(Option<<Self as FileSystem>::Handle>, OpenOptions)> {
1196 unimplemented!()
1197 }
1198
1199 async fn async_create(
1200 &self,
1201 ctx: &Context,
1202 parent: <Self as FileSystem>::Inode,
1203 name: &CStr,
1204 args: CreateIn,
1205 ) -> Result<(Entry, Option<<Self as FileSystem>::Handle>, OpenOptions)> {
1206 unimplemented!()
1207 }
1208
1209 async fn async_read(
1210 &self,
1211 ctx: &Context,
1212 inode: <Self as FileSystem>::Inode,
1213 handle: <Self as FileSystem>::Handle,
1214 w: &mut (dyn AsyncZeroCopyWriter + Send),
1215 size: u32,
1216 offset: u64,
1217 lock_owner: Option<u64>,
1218 flags: u32,
1219 ) -> Result<usize> {
1220 unimplemented!()
1221 }
1222
1223 async fn async_write(
1224 &self,
1225 ctx: &Context,
1226 inode: <Self as FileSystem>::Inode,
1227 handle: <Self as FileSystem>::Handle,
1228 r: &mut (dyn AsyncZeroCopyReader + Send),
1229 size: u32,
1230 offset: u64,
1231 lock_owner: Option<u64>,
1232 delayed_write: bool,
1233 flags: u32,
1234 fuse_flags: u32,
1235 ) -> Result<usize> {
1236 unimplemented!()
1237 }
1238
1239 async fn async_fsync(
1240 &self,
1241 ctx: &Context,
1242 inode: <Self as FileSystem>::Inode,
1243 datasync: bool,
1244 handle: <Self as FileSystem>::Handle,
1245 ) -> Result<()> {
1246 unimplemented!()
1247 }
1248
1249 async fn async_fallocate(
1250 &self,
1251 ctx: &Context,
1252 inode: <Self as FileSystem>::Inode,
1253 handle: <Self as FileSystem>::Handle,
1254 mode: u32,
1255 offset: u64,
1256 length: u64,
1257 ) -> Result<()> {
1258 unimplemented!()
1259 }
1260
1261 async fn async_fsyncdir(
1262 &self,
1263 ctx: &Context,
1264 inode: <Self as FileSystem>::Inode,
1265 datasync: bool,
1266 handle: <Self as FileSystem>::Handle,
1267 ) -> Result<()> {
1268 unimplemented!()
1269 }
1270 }
1271
1272 impl BackendFileSystem for FakeFileSystemOne {
1273 fn mount(&self) -> Result<(Entry, u64)> {
1274 Ok((
1275 Entry {
1276 inode: 1,
1277 ..Default::default()
1278 },
1279 0,
1280 ))
1281 }
1282
1283 fn as_any(&self) -> &dyn Any {
1284 self
1285 }
1286 }
1287
1288 #[allow(unused_variables)]
1289 #[async_trait]
1290 impl AsyncFileSystem for FakeFileSystemTwo {
1291 async fn async_lookup(
1292 &self,
1293 ctx: &Context,
1294 parent: <Self as FileSystem>::Inode,
1295 name: &CStr,
1296 ) -> Result<Entry> {
1297 Err(std::io::Error::from_raw_os_error(libc::EINVAL))
1298 }
1299
1300 async fn async_getattr(
1301 &self,
1302 ctx: &Context,
1303 inode: <Self as FileSystem>::Inode,
1304 handle: Option<<Self as FileSystem>::Handle>,
1305 ) -> Result<(libc::stat64, Duration)> {
1306 unimplemented!()
1307 }
1308
1309 async fn async_setattr(
1310 &self,
1311 ctx: &Context,
1312 inode: <Self as FileSystem>::Inode,
1313 attr: libc::stat64,
1314 handle: Option<<Self as FileSystem>::Handle>,
1315 valid: SetattrValid,
1316 ) -> Result<(libc::stat64, Duration)> {
1317 unimplemented!()
1318 }
1319
1320 async fn async_open(
1321 &self,
1322 ctx: &Context,
1323 inode: <Self as FileSystem>::Inode,
1324 flags: u32,
1325 fuse_flags: u32,
1326 ) -> Result<(Option<<Self as FileSystem>::Handle>, OpenOptions)> {
1327 unimplemented!()
1328 }
1329
1330 async fn async_create(
1331 &self,
1332 ctx: &Context,
1333 parent: <Self as FileSystem>::Inode,
1334 name: &CStr,
1335 args: CreateIn,
1336 ) -> Result<(Entry, Option<<Self as FileSystem>::Handle>, OpenOptions)> {
1337 unimplemented!()
1338 }
1339
1340 async fn async_read(
1341 &self,
1342 ctx: &Context,
1343 inode: <Self as FileSystem>::Inode,
1344 handle: <Self as FileSystem>::Handle,
1345 w: &mut (dyn AsyncZeroCopyWriter + Send),
1346 size: u32,
1347 offset: u64,
1348 lock_owner: Option<u64>,
1349 flags: u32,
1350 ) -> Result<usize> {
1351 unimplemented!()
1352 }
1353
1354 async fn async_write(
1355 &self,
1356 ctx: &Context,
1357 inode: <Self as FileSystem>::Inode,
1358 handle: <Self as FileSystem>::Handle,
1359 r: &mut (dyn AsyncZeroCopyReader + Send),
1360 size: u32,
1361 offset: u64,
1362 lock_owner: Option<u64>,
1363 delayed_write: bool,
1364 flags: u32,
1365 fuse_flags: u32,
1366 ) -> Result<usize> {
1367 unimplemented!()
1368 }
1369
1370 async fn async_fsync(
1371 &self,
1372 ctx: &Context,
1373 inode: <Self as FileSystem>::Inode,
1374 datasync: bool,
1375 handle: <Self as FileSystem>::Handle,
1376 ) -> Result<()> {
1377 unimplemented!()
1378 }
1379
1380 async fn async_fallocate(
1381 &self,
1382 ctx: &Context,
1383 inode: <Self as FileSystem>::Inode,
1384 handle: <Self as FileSystem>::Handle,
1385 mode: u32,
1386 offset: u64,
1387 length: u64,
1388 ) -> Result<()> {
1389 unimplemented!()
1390 }
1391
1392 async fn async_fsyncdir(
1393 &self,
1394 ctx: &Context,
1395 inode: <Self as FileSystem>::Inode,
1396 datasync: bool,
1397 handle: <Self as FileSystem>::Handle,
1398 ) -> Result<()> {
1399 unimplemented!()
1400 }
1401 }
1402
1403 impl BackendFileSystem for FakeFileSystemTwo {
1404 fn mount(&self) -> Result<(Entry, u64)> {
1405 Ok((
1406 Entry {
1407 inode: 1,
1408 ..Default::default()
1409 },
1410 0,
1411 ))
1412 }
1413 fn as_any(&self) -> &dyn Any {
1414 self
1415 }
1416 }
1417 }
1418
1419 #[cfg(not(feature = "async-io"))]
1420 impl BackendFileSystem for FakeFileSystemOne {
1421 fn mount(&self) -> Result<(Entry, u64)> {
1422 Ok((
1423 Entry {
1424 inode: 1,
1425 ..Default::default()
1426 },
1427 0,
1428 ))
1429 }
1430
1431 fn as_any(&self) -> &dyn Any {
1432 self
1433 }
1434 }
1435
1436 #[cfg(not(feature = "async-io"))]
1437 impl BackendFileSystem for FakeFileSystemTwo {
1438 fn mount(&self) -> Result<(Entry, u64)> {
1439 Ok((
1440 Entry {
1441 inode: 1,
1442 ..Default::default()
1443 },
1444 0,
1445 ))
1446 }
1447 fn as_any(&self) -> &dyn Any {
1448 self
1449 }
1450 }
1451
1452 #[test]
1453 fn test_vfs_init() {
1454 let vfs = Vfs::default();
1455 assert_eq!(vfs.initialized(), false);
1456
1457 let opts = vfs.opts.load();
1458 let out_opts = opts.out_opts;
1459
1460 #[cfg(target_os = "linux")]
1461 {
1462 assert_eq!(opts.no_open, true);
1463 assert_eq!(opts.no_opendir, true);
1464 assert_eq!(opts.no_writeback, false);
1465 assert_eq!(opts.killpriv_v2, false);
1466 }
1467 assert_eq!(opts.no_readdir, false);
1468 assert_eq!(opts.seal_size, false);
1469 assert_eq!(opts.in_opts.is_empty(), true);
1470
1471 vfs.init(FsOptions::ASYNC_READ).unwrap();
1472 assert_eq!(vfs.initialized(), true);
1473
1474 let opts = vfs.opts.load();
1475 #[cfg(target_os = "linux")]
1476 {
1477 assert_eq!(opts.no_open, false);
1478 assert_eq!(opts.no_opendir, false);
1479 assert_eq!(opts.no_writeback, false);
1480 assert_eq!(opts.killpriv_v2, false);
1481 }
1482 assert_eq!(opts.no_readdir, false);
1483 assert_eq!(opts.seal_size, false);
1484
1485 vfs.destroy();
1486 assert_eq!(vfs.initialized(), false);
1487
1488 let vfs = Vfs::default();
1489 #[cfg(target_os = "linux")]
1490 let in_opts =
1491 FsOptions::ASYNC_READ | FsOptions::ZERO_MESSAGE_OPEN | FsOptions::ZERO_MESSAGE_OPENDIR;
1492 #[cfg(target_os = "macos")]
1493 let in_opts = FsOptions::ASYNC_READ;
1494 vfs.init(in_opts).unwrap();
1495 let opts = vfs.opts.load();
1496 #[cfg(target_os = "linux")]
1497 {
1498 assert_eq!(opts.no_open, true);
1499 assert_eq!(opts.no_opendir, true);
1500 assert_eq!(opts.no_writeback, false);
1501 assert_eq!(opts.killpriv_v2, false);
1502 }
1503 assert_eq!(opts.out_opts, out_opts & in_opts);
1504 }
1505
1506 #[test]
1507 fn test_vfs_lookup() {
1508 let vfs = Vfs::new(VfsOptions::default());
1509 let fs = FakeFileSystemOne {};
1510 let ctx = Context::new();
1511
1512 assert!(vfs.mount(Box::new(fs), "/x/y").is_ok());
1513
1514 let entry1 = vfs
1516 .lookup(&ctx, ROOT_ID.into(), CString::new("x").unwrap().as_c_str())
1517 .unwrap();
1518 assert_eq!(entry1.inode, 0x2);
1519
1520 let entry2 = vfs
1522 .lookup(
1523 &ctx,
1524 entry1.inode.into(),
1525 CString::new("y").unwrap().as_c_str(),
1526 )
1527 .unwrap();
1528 assert_eq!(entry2.inode, 0x100_0000_0000_0001);
1529
1530 let entry3 = vfs
1532 .lookup(
1533 &ctx,
1534 entry2.inode.into(),
1535 CString::new("z").unwrap().as_c_str(),
1536 )
1537 .unwrap();
1538 assert_eq!(entry3.inode, 0);
1539
1540 let (stat, _) = vfs
1541 .getattr(&ctx, VfsInode(0x100_0000_0000_0001), None)
1542 .unwrap();
1543 assert_eq!(stat.st_ino, 0x100_0000_0000_0001);
1544 }
1545
1546 #[test]
1547 fn test_mount_different_fs_types() {
1548 let vfs = Vfs::new(VfsOptions::default());
1549 let fs1 = FakeFileSystemOne {};
1550 let fs2 = FakeFileSystemTwo {};
1551 assert!(vfs.mount(Box::new(fs1), "/foo").is_ok());
1552 assert!(vfs.mount(Box::new(fs2), "/bar").is_ok());
1553
1554 let ctx = Context::new();
1556 let entry1 = vfs
1557 .lookup(
1558 &ctx,
1559 ROOT_ID.into(),
1560 CString::new("bar").unwrap().as_c_str(),
1561 )
1562 .unwrap();
1563 assert_eq!(entry1.inode, 0x200_0000_0000_0001);
1564 assert_eq!(entry1.attr.st_ino, 0x200_0000_0000_0001);
1565 }
1566
1567 #[test]
1568 fn test_umount() {
1569 let vfs = Vfs::new(VfsOptions::default());
1570 let fs1 = FakeFileSystemOne {};
1571 let fs2 = FakeFileSystemOne {};
1572 assert!(vfs.mount(Box::new(fs1), "/foo").is_ok());
1573 assert!(vfs.umount("/foo").is_ok());
1574
1575 assert!(vfs.mount(Box::new(fs2), "/x/y").is_ok());
1576
1577 match vfs.umount("/x") {
1578 Err(VfsError::NotFound(_e)) => {}
1579 _ => panic!("expect VfsError::NotFound(/x)"),
1580 }
1581 }
1582
1583 #[test]
1584 fn test_umount_overlap() {
1585 let vfs = Vfs::new(VfsOptions::default());
1586 let fs1 = FakeFileSystemOne {};
1587 let fs2 = FakeFileSystemTwo {};
1588
1589 assert!(vfs.mount(Box::new(fs1), "/x/y/z").is_ok());
1590 assert!(vfs.mount(Box::new(fs2), "/x/y").is_ok());
1591
1592 let m1 = vfs.get_rootfs("/x/y/z").unwrap().unwrap();
1593 assert!(m1.as_any().is::<FakeFileSystemOne>());
1594 let m2 = vfs.get_rootfs("/x/y").unwrap().unwrap();
1595 assert!(m2.as_any().is::<FakeFileSystemTwo>());
1596
1597 assert!(vfs.umount("/x/y/z").is_ok());
1598 assert!(vfs.umount("/x/y").is_ok());
1599
1600 match vfs.umount("/x/y/z") {
1601 Err(VfsError::NotFound(_e)) => {}
1602 _ => panic!("expect VfsError::NotFound(/x/y/z)"),
1603 }
1604 }
1605
1606 #[test]
1607 fn test_umount_same() {
1608 let vfs = Vfs::new(VfsOptions::default());
1609 let fs1 = FakeFileSystemOne {};
1610 let fs2 = FakeFileSystemTwo {};
1611
1612 assert!(vfs.mount(Box::new(fs1), "/x/y").is_ok());
1613 assert!(vfs.mount(Box::new(fs2), "/x/y").is_ok());
1614
1615 let m1 = vfs.get_rootfs("/x/y").unwrap().unwrap();
1616 assert!(m1.as_any().is::<FakeFileSystemTwo>());
1617
1618 assert!(vfs.umount("/x/y").is_ok());
1619
1620 match vfs.umount("/x/y") {
1621 Err(VfsError::NotFound(_e)) => {}
1622 _ => panic!("expect VfsError::NotFound(/x/y)"),
1623 }
1624 }
1625
1626 #[test]
1627 #[should_panic]
1628 fn test_invalid_inode() {
1629 let _ = VfsInode::new(1, VFS_MAX_INO + 1);
1630 }
1631
1632 #[test]
1633 fn test_inode() {
1634 let inode = VfsInode::new(2, VFS_MAX_INO);
1635
1636 assert_eq!(inode.fs_idx(), 2);
1637 assert_eq!(inode.ino(), VFS_MAX_INO);
1638 assert!(!inode.is_pseudo_fs());
1639 assert_eq!(u64::from(inode), 0x200_0000_0000_0000u64 + VFS_MAX_INO);
1640 }
1641
1642 #[test]
1643 fn test_allocate_fs_idx() {
1644 let vfs = Vfs::new(VfsOptions::default());
1645 let _guard = vfs.lock.lock().unwrap();
1646
1647 for _ in 0..255 {
1649 let fs = FakeFileSystemOne {};
1650 let index = vfs.allocate_fs_idx().unwrap();
1651 let mut superblocks = vfs.superblocks.load().deref().deref().clone();
1652
1653 superblocks[index as usize] = Some(Arc::new(Box::new(fs)));
1654 vfs.superblocks.store(Arc::new(superblocks));
1655 }
1656
1657 for _ in 0..=256 {
1659 vfs.allocate_fs_idx().unwrap_err();
1660 }
1661 }
1662
1663 #[test]
1664 fn test_fmt_vfs_error() {
1665 assert_eq!(
1666 format!("{}", VfsError::Unsupported),
1667 "Vfs operation not supported".to_string()
1668 );
1669 assert_eq!(
1670 format!(
1671 "{}",
1672 VfsError::Mount(Error::new(ErrorKind::Other, "mount".to_string()),)
1673 ),
1674 "Mount backend filesystem: mount".to_string()
1675 );
1676 assert_eq!(
1677 format!("{}", VfsError::InodeIndex("inode index".to_string())),
1678 "Illegal inode index: inode index".to_string()
1679 );
1680 assert_eq!(
1681 format!(
1682 "{}",
1683 VfsError::FsIndex(Error::new(ErrorKind::Other, "fs index".to_string()),)
1684 ),
1685 "Filesystem index error: fs index".to_string()
1686 );
1687 assert_eq!(
1688 format!(
1689 "{}",
1690 VfsError::PathWalk(Error::new(ErrorKind::Other, "path walk".to_string()),)
1691 ),
1692 "Walking path error: path walk".to_string()
1693 );
1694 assert_eq!(
1695 format!("{}", VfsError::NotFound("not found".to_string())),
1696 "Entry can't be found: not found".to_string()
1697 );
1698 assert_eq!(
1699 format!("{}", VfsError::Initialize("initialize".to_string())),
1700 "File system can't be initialized: initialize".to_string()
1701 );
1702 }
1703}