1use std::any::Any;
19use std::collections::HashMap;
20use std::ffi::CStr;
21use std::fmt;
22use std::io;
23use std::io::{Error, 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::other(format!(
438 "Unsupported max inode number, requested {} supported {}",
439 ino, VFS_MAX_INO
440 )));
441 }
442
443 let _guard = self.lock.lock().unwrap();
444 self.insert_mount_locked(fs, entry, fs_idx, path)
445 }
446
447 pub fn umount(&self, path: &str) -> VfsResult<(u64, u64)> {
449 let _guard = self.lock.lock().unwrap();
451 let inode = self
452 .root
453 .path_walk(path)
454 .map_err(VfsError::PathWalk)?
455 .ok_or_else(|| VfsError::NotFound(path.to_string()))?;
456 let parent = self
457 .root
458 .get_parent_inode(inode)
459 .ok_or(VfsError::NotFound(format!(
460 "{}'s parent inode does not exist",
461 inode
462 )))?;
463 let mut mountpoints = self.mountpoints.load().deref().deref().clone();
464 let fs_idx = mountpoints
465 .get(&inode)
466 .cloned()
467 .map(|x| {
468 if self.remove_pseudo_root {
478 self.root.evict_inode(inode);
479 }
480 mountpoints.remove(&inode);
481 self.mountpoints.store(Arc::new(mountpoints));
482 x.fs_idx
483 })
484 .ok_or_else(|| {
485 error!("{} is not a mount point.", path);
486 VfsError::NotFound(path.to_string())
487 })?;
488
489 trace!("fs_idx {}", fs_idx);
490 let mut superblocks = self.superblocks.load().deref().deref().clone();
491 if let Some(fs) = superblocks[fs_idx as usize].take() {
492 fs.destroy();
493 }
494 self.superblocks.store(Arc::new(superblocks));
495
496 Ok((inode, parent))
497 }
498
499 pub fn get_rootfs(&self, path: &str) -> VfsResult<Option<(Arc<BackFileSystem>, u8)>> {
501 let _guard = self.lock.lock().unwrap();
503 let inode = match self.root.path_walk(path).map_err(VfsError::PathWalk)? {
504 Some(i) => i,
505 None => return Ok(None),
506 };
507
508 if let Some(mnt) = self.mountpoints.load().get(&inode) {
509 let fs = self
510 .get_fs_by_idx(mnt.fs_idx)
511 .map_err(|e| VfsError::NotFound(format!("fs index {}, {:?}", mnt.fs_idx, e)))?;
512 Ok(Some((fs, mnt.fs_idx)))
513 } else {
514 Ok(None)
517 }
518 }
519
520 pub fn get_root_pseudofs(&self) -> &PseudoFs {
522 &self.root
523 }
524
525 fn convert_inode(&self, fs_idx: VfsIndex, inode: u64) -> Result<u64> {
531 if inode == 0 {
533 return Ok(inode);
534 }
535 if inode > VFS_MAX_INO {
536 return Err(Error::other(format!(
537 "Inode number {inode} too large, max supported {VFS_MAX_INO}"
538 )));
539 }
540 let ino: u64 = ((fs_idx as u64) << VFS_INDEX_SHIFT) | inode;
541 trace!(
542 "fuse: vfs fs_idx {} inode {} fuse ino {:#x}",
543 fs_idx,
544 inode,
545 ino
546 );
547 Ok(ino)
548 }
549
550 fn convert_entry(&self, fs_idx: VfsIndex, inode: u64, entry: &mut Entry) -> Result<Entry> {
551 self.convert_inode(fs_idx, inode).map(|ino| {
552 entry.inode = ino;
553 entry.attr.st_ino = ino;
554 if let Some((internal_id, external_id, range)) = self.id_mapping {
556 if entry.attr.st_uid >= internal_id && entry.attr.st_uid < internal_id + range {
557 entry.attr.st_uid += external_id - internal_id;
558 }
559 if entry.attr.st_gid >= internal_id && entry.attr.st_gid < internal_id + range {
560 entry.attr.st_gid += external_id - internal_id;
561 }
562 }
563 *entry
564 })
565 }
566
567 fn remap_attr_id(&self, map_internal_to_external: bool, attr: &mut stat64) {
574 if let Some((internal_id, external_id, range)) = self.id_mapping {
575 if map_internal_to_external
576 && attr.st_uid >= internal_id
577 && attr.st_uid < internal_id + range
578 {
579 attr.st_uid += external_id - internal_id;
580 }
581 if map_internal_to_external
582 && attr.st_gid >= internal_id
583 && attr.st_gid < internal_id + range
584 {
585 attr.st_gid += external_id - internal_id;
586 }
587 if !map_internal_to_external
588 && attr.st_uid >= external_id
589 && attr.st_uid < external_id + range
590 {
591 attr.st_uid += internal_id - external_id;
592 }
593 if !map_internal_to_external
594 && attr.st_gid >= external_id
595 && attr.st_gid < external_id + range
596 {
597 attr.st_gid += internal_id - external_id;
598 }
599 }
600 }
601
602 fn allocate_fs_idx(&self) -> Result<VfsIndex> {
603 let superblocks = self.superblocks.load().deref().deref().clone();
604 let start = self.next_super.load(Ordering::SeqCst);
605 let mut found = false;
606
607 loop {
608 let index = self.next_super.fetch_add(1, Ordering::Relaxed);
609 if index == start {
610 if found {
611 break;
613 } else {
614 found = true;
615 }
616 }
617
618 if index == VFS_PSEUDO_FS_IDX {
619 continue;
621 }
622 if (index as usize) < superblocks.len() && superblocks[index as usize].is_some() {
623 continue;
625 } else {
626 return Ok(index);
627 }
628 }
629
630 Err(Error::other("vfs maximum mountpoints reached"))
631 }
632
633 fn get_fs_by_idx(&self, fs_idx: VfsIndex) -> Result<Arc<BackFileSystem>> {
634 let superblocks = self.superblocks.load();
635
636 if let Some(fs) = &superblocks[fs_idx as usize] {
637 return Ok(fs.clone());
638 }
639
640 Err(Error::from_raw_os_error(libc::ENOENT))
641 }
642
643 fn get_real_rootfs(&self, inode: VfsInode) -> Result<(VfsEitherFs<'_>, VfsInode)> {
644 if inode.is_pseudo_fs() {
645 if inode.ino() == ROOT_ID {
647 if let Some(mnt) = self.mountpoints.load().get(&inode.ino()).cloned() {
648 let fs = self.get_fs_by_idx(mnt.fs_idx)?;
649 return Ok((Right(fs), VfsInode::new(mnt.fs_idx, mnt.ino)));
650 }
651 }
652 Ok((Left(&self.root), inode))
653 } else {
654 let fs = self.get_fs_by_idx(inode.fs_idx())?;
655 Ok((Right(fs), inode))
656 }
657 }
658
659 fn lookup_pseudo(
660 &self,
661 fs: &PseudoFs,
662 idata: VfsInode,
663 ctx: &Context,
664 name: &CStr,
665 ) -> Result<Entry> {
666 trace!("lookup pseudo ino {} name {:?}", idata.ino(), name);
667 let mut entry = fs.lookup(ctx, idata.ino(), name)?;
668
669 match self.mountpoints.load().get(&entry.inode) {
670 Some(mnt) => {
671 entry = mnt.root_entry;
673 self.convert_entry(mnt.fs_idx, mnt.ino, &mut entry)?;
674 trace!(
675 "vfs lookup cross mountpoint, return new mount fs_idx {} inode 0x{:x} fuse inode 0x{:x}, attr inode 0x{:x}",
676 mnt.fs_idx,
677 mnt.ino,
678 entry.inode,
679 entry.attr.st_ino,
680 );
681 Ok(entry)
682 }
683 None => self.convert_entry(idata.fs_idx(), entry.inode, &mut entry),
684 }
685 }
686}
687
688#[cfg(feature = "persist")]
690pub mod persist {
691 use std::{
692 ops::Deref,
693 sync::{atomic::Ordering, Arc},
694 };
695
696 use dbs_snapshot::Snapshot;
697 use versionize::{VersionMap, Versionize, VersionizeResult};
698 use versionize_derive::Versionize;
699
700 use crate::api::{
701 filesystem::FsOptions,
702 pseudo_fs::persist::PseudoFsState,
703 vfs::{VfsError, VfsResult},
704 Vfs, VfsOptions,
705 };
706
707 #[derive(Versionize, Debug)]
709 struct VfsState {
710 options: VfsOptionsState,
712 root: Vec<u8>,
714 next_super: u8,
716 }
717
718 #[derive(Versionize, Debug, Default)]
719 struct VfsOptionsState {
720 in_opts: u64,
721 out_opts: u64,
722 no_readdir: bool,
723 seal_size: bool,
724 id_mapping_internal: u32,
725 id_mapping_external: u32,
726 id_mapping_range: u32,
727
728 #[cfg(target_os = "linux")]
729 no_open: bool,
730 #[cfg(target_os = "linux")]
731 no_opendir: bool,
732 #[cfg(target_os = "linux")]
733 no_writeback: bool,
734 #[cfg(target_os = "linux")]
735 killpriv_v2: bool,
736 }
737
738 impl VfsOptions {
739 fn save(&self) -> VfsOptionsState {
740 VfsOptionsState {
741 in_opts: self.in_opts.bits(),
742 out_opts: self.out_opts.bits(),
743 no_readdir: self.no_readdir,
744 seal_size: self.seal_size,
745 id_mapping_internal: self.id_mapping.0,
746 id_mapping_external: self.id_mapping.1,
747 id_mapping_range: self.id_mapping.2,
748
749 #[cfg(target_os = "linux")]
750 no_open: self.no_open,
751 #[cfg(target_os = "linux")]
752 no_opendir: self.no_opendir,
753 #[cfg(target_os = "linux")]
754 no_writeback: self.no_writeback,
755 #[cfg(target_os = "linux")]
756 killpriv_v2: self.killpriv_v2,
757 }
758 }
759
760 fn restore(state: &VfsOptionsState) -> VfsResult<VfsOptions> {
761 Ok(VfsOptions {
762 in_opts: FsOptions::from_bits(state.in_opts).ok_or(VfsError::Persist(
763 "Failed to restore VfsOptions.in_opts".to_owned(),
764 ))?,
765 out_opts: FsOptions::from_bits(state.out_opts).ok_or(VfsError::Persist(
766 "Failed to restore VfsOptions.out_opts".to_owned(),
767 ))?,
768 no_readdir: state.no_readdir,
769 seal_size: state.seal_size,
770 id_mapping: (
771 state.id_mapping_internal,
772 state.id_mapping_external,
773 state.id_mapping_range,
774 ),
775
776 #[cfg(target_os = "linux")]
777 no_open: state.no_open,
778 #[cfg(target_os = "linux")]
779 no_opendir: state.no_opendir,
780 #[cfg(target_os = "linux")]
781 no_writeback: state.no_writeback,
782 #[cfg(target_os = "linux")]
783 killpriv_v2: state.killpriv_v2,
784 })
785 }
786 }
787
788 impl Vfs {
789 fn get_version_map() -> versionize::VersionMap {
790 let mut version_map = VersionMap::new();
791 version_map
792 .set_type_version(VfsState::type_id(), 1)
793 .set_type_version(PseudoFsState::type_id(), 1)
794 .set_type_version(VfsOptionsState::type_id(), 1);
795
796 version_map
799 }
800
801 pub fn save_to_bytes(&self) -> VfsResult<Vec<u8>> {
858 let root_state = self
859 .root
860 .save_to_bytes()
861 .map_err(|e| VfsError::Persist(format!("Failed to save Vfs root: {:?}", e)))?;
862 let vfs_state = VfsState {
863 options: self.opts.load().deref().deref().save(),
864 root: root_state,
865 next_super: self.next_super.load(Ordering::SeqCst),
866 };
867
868 let vm = Vfs::get_version_map();
869 let target_version = vm.latest_version();
870 let mut s = Snapshot::new(vm, target_version);
871 let mut buf = Vec::new();
872 s.save(&mut buf, &vfs_state).map_err(|e| {
873 VfsError::Persist(format!("Failed to save Vfs using snapshot: {:?}", e))
874 })?;
875
876 Ok(buf)
877 }
878
879 pub fn restore_from_bytes(&self, buf: &mut Vec<u8>) -> VfsResult<()> {
882 let mut state: VfsState =
883 Snapshot::load(&mut buf.as_slice(), buf.len(), Vfs::get_version_map())
884 .map_err(|e| {
885 VfsError::Persist(format!("Failed to load Vfs using snapshot: {:?}", e))
886 })?
887 .0;
888 let opts = VfsOptions::restore(&state.options)?;
889 self.initialized
890 .store(!opts.in_opts.is_empty(), Ordering::Release);
891 self.opts.store(Arc::new(opts));
892
893 self.next_super.store(state.next_super, Ordering::SeqCst);
894 self.root
895 .restore_from_bytes(&mut state.root)
896 .map_err(|e| VfsError::Persist(format!("Failed to restore Vfs root: {:?}", e)))?;
897
898 Ok(())
899 }
900 }
901
902 mod test {
903
904 #[test]
906 fn test_vfs_save_restore_simple() {
907 use crate::api::{Vfs, VfsOptions};
908
909 let vfs = &Vfs::new(VfsOptions::default());
911
912 let mut buf = vfs.save_to_bytes().unwrap();
914
915 let vfs = &Vfs::new(VfsOptions::default());
917 vfs.restore_from_bytes(&mut buf).unwrap();
918 assert_eq!(vfs.next_super.load(std::sync::atomic::Ordering::SeqCst), 1);
919 }
920
921 #[test]
922 fn test_vfs_save_restore_with_backend_fs() {
923 use crate::api::{Vfs, VfsIndex, VfsOptions};
924 use crate::passthrough::{Config, PassthroughFs};
925
926 let new_backend_fs = || {
927 let fs_cfg = Config::default();
928 let fs = PassthroughFs::<()>::new(fs_cfg.clone()).unwrap();
929 fs.import().unwrap();
930 Box::new(fs)
931 };
932
933 let vfs = &Vfs::new(VfsOptions::default());
935 let paths = vec!["/a", "/a/b", "/a/b/c", "/b", "/b/a/c", "/d"];
936 let backend_fs_list: Vec<(&str, VfsIndex)> = paths
937 .iter()
938 .map(|path| {
939 let fs = new_backend_fs();
940 let idx = vfs.mount(fs, path).unwrap();
941
942 (path.to_owned(), idx)
943 })
944 .collect();
945
946 let mut buf = vfs.save_to_bytes().unwrap();
948
949 let restored_vfs = &Vfs::new(VfsOptions::default());
951 restored_vfs.restore_from_bytes(&mut buf).unwrap();
952 backend_fs_list.into_iter().for_each(|(path, idx)| {
954 let fs = new_backend_fs();
955 vfs.restore_mount(fs, idx, path).unwrap();
956 });
957
958 assert_eq!(
960 vfs.next_super.load(std::sync::atomic::Ordering::SeqCst),
961 restored_vfs
962 .next_super
963 .load(std::sync::atomic::Ordering::SeqCst)
964 );
965 assert_eq!(
966 vfs.initialized.load(std::sync::atomic::Ordering::SeqCst),
967 restored_vfs
968 .initialized
969 .load(std::sync::atomic::Ordering::SeqCst)
970 );
971 for path in paths.iter() {
972 let inode = vfs.root.path_walk(path).unwrap();
973 let restored_inode = restored_vfs.root.path_walk(path).unwrap();
974 assert_eq!(inode, restored_inode);
975 }
976 }
977
978 #[test]
979 fn test_vfs_save_restore_with_backend_fs_with_initialized() {
980 use crate::api::filesystem::{FileSystem, FsOptions};
981 use crate::api::{Vfs, VfsIndex, VfsOptions};
982 use crate::passthrough::{Config, PassthroughFs};
983 use std::sync::atomic::Ordering;
984
985 let new_backend_fs = || {
986 let fs_cfg = Config::default();
987 let fs = PassthroughFs::<()>::new(fs_cfg.clone()).unwrap();
988 fs.import().unwrap();
989 Box::new(fs)
990 };
991
992 let vfs = &Vfs::new(VfsOptions::default());
994 let paths = vec!["/a", "/a/b", "/a/b/c", "/b", "/b/a/c", "/d"];
995 let backend_fs_list: Vec<(&str, VfsIndex)> = paths
996 .iter()
997 .map(|path| {
998 let fs = new_backend_fs();
999 let idx = vfs.mount(fs, path).unwrap();
1000
1001 (path.to_owned(), idx)
1002 })
1003 .collect();
1004 vfs.init(FsOptions::ASYNC_READ).unwrap();
1005 assert!(vfs.initialized.load(Ordering::Acquire));
1006
1007 let mut buf = vfs.save_to_bytes().unwrap();
1009
1010 let restored_vfs = &Vfs::new(VfsOptions::default());
1012 restored_vfs.restore_from_bytes(&mut buf).unwrap();
1013
1014 backend_fs_list.into_iter().for_each(|(path, idx)| {
1016 let fs = new_backend_fs();
1017 vfs.restore_mount(fs, idx, path).unwrap();
1018 });
1019
1020 assert_eq!(
1022 vfs.next_super.load(std::sync::atomic::Ordering::SeqCst),
1023 restored_vfs
1024 .next_super
1025 .load(std::sync::atomic::Ordering::SeqCst)
1026 );
1027 assert_eq!(
1028 vfs.initialized.load(std::sync::atomic::Ordering::Acquire),
1029 restored_vfs
1030 .initialized
1031 .load(std::sync::atomic::Ordering::Acquire)
1032 );
1033 for path in paths.iter() {
1034 let inode = vfs.root.path_walk(path).unwrap();
1035 let restored_inode = restored_vfs.root.path_walk(path).unwrap();
1036 assert_eq!(inode, restored_inode);
1037 }
1038 }
1039 }
1040}
1041
1042#[cfg(test)]
1043mod tests {
1044 use super::*;
1045 use crate::api::Vfs;
1046 use std::ffi::CString;
1047 use std::io::{Error, ErrorKind};
1048
1049 pub(crate) struct FakeFileSystemOne {}
1050 impl FileSystem for FakeFileSystemOne {
1051 type Inode = u64;
1052 type Handle = u64;
1053 fn lookup(&self, _: &Context, _: Self::Inode, _: &CStr) -> Result<Entry> {
1054 Ok(Entry::default())
1055 }
1056 fn getattr(
1057 &self,
1058 _ctx: &Context,
1059 _inode: Self::Inode,
1060 _handle: Option<Self::Handle>,
1061 ) -> Result<(stat64, Duration)> {
1062 let mut attr = Attr {
1063 ..Default::default()
1064 };
1065 attr.ino = 1;
1066 Ok((attr.into(), Duration::from_secs(1)))
1067 }
1068 }
1069
1070 pub(crate) struct FakeFileSystemTwo {}
1071 impl FileSystem for FakeFileSystemTwo {
1072 type Inode = u64;
1073 type Handle = u64;
1074 fn lookup(&self, _: &Context, _: Self::Inode, _: &CStr) -> Result<Entry> {
1075 Ok(Entry {
1076 inode: 1,
1077 ..Default::default()
1078 })
1079 }
1080 }
1081
1082 #[test]
1083 fn test_is_safe_path_component() {
1084 let name = CStr::from_bytes_with_nul(b"normal\0").unwrap();
1085 assert!(is_safe_path_component(name), "\"{:?}\"", name);
1086
1087 let name = CStr::from_bytes_with_nul(b".a\0").unwrap();
1088 assert!(is_safe_path_component(name));
1089
1090 let name = CStr::from_bytes_with_nul(b"a.a\0").unwrap();
1091 assert!(is_safe_path_component(name));
1092
1093 let name = CStr::from_bytes_with_nul(b"a.a\0").unwrap();
1094 assert!(is_safe_path_component(name));
1095
1096 let name = CStr::from_bytes_with_nul(b"/\0").unwrap();
1097 assert!(!is_safe_path_component(name));
1098
1099 let name = CStr::from_bytes_with_nul(b"/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"..\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"a/b\0").unwrap();
1112 assert!(!is_safe_path_component(name));
1113
1114 let name = CStr::from_bytes_with_nul(b"./../a\0").unwrap();
1115 assert!(!is_safe_path_component(name));
1116 }
1117
1118 #[test]
1119 fn test_is_dot_or_dotdot() {
1120 let name = CStr::from_bytes_with_nul(b"..\0").unwrap();
1121 assert!(is_dot_or_dotdot(name));
1122
1123 let name = CStr::from_bytes_with_nul(b".\0").unwrap();
1124 assert!(is_dot_or_dotdot(name));
1125
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"a\0").unwrap();
1133 assert!(!is_dot_or_dotdot(name));
1134
1135 let name = CStr::from_bytes_with_nul(b"aa\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"a/\0").unwrap();
1142 assert!(!is_dot_or_dotdot(name));
1143 }
1144
1145 #[cfg(feature = "async-io")]
1146 mod async_io {
1147 use super::*;
1148 use crate::abi::fuse_abi::{OpenOptions, SetattrValid};
1149 use async_trait::async_trait;
1150
1151 #[allow(unused_variables)]
1152 #[async_trait]
1153 impl AsyncFileSystem for FakeFileSystemOne {
1154 async fn async_lookup(
1155 &self,
1156 ctx: &Context,
1157 parent: <Self as FileSystem>::Inode,
1158 name: &CStr,
1159 ) -> Result<Entry> {
1160 Ok(Entry::default())
1161 }
1162
1163 async fn async_getattr(
1164 &self,
1165 ctx: &Context,
1166 inode: <Self as FileSystem>::Inode,
1167 handle: Option<<Self as FileSystem>::Handle>,
1168 ) -> Result<(libc::stat64, Duration)> {
1169 unimplemented!()
1170 }
1171
1172 async fn async_setattr(
1173 &self,
1174 ctx: &Context,
1175 inode: <Self as FileSystem>::Inode,
1176 attr: libc::stat64,
1177 handle: Option<<Self as FileSystem>::Handle>,
1178 valid: SetattrValid,
1179 ) -> Result<(libc::stat64, Duration)> {
1180 unimplemented!()
1181 }
1182
1183 async fn async_open(
1184 &self,
1185 ctx: &Context,
1186 inode: <Self as FileSystem>::Inode,
1187 flags: u32,
1188 fuse_flags: u32,
1189 ) -> Result<(Option<<Self as FileSystem>::Handle>, OpenOptions)> {
1190 unimplemented!()
1191 }
1192
1193 async fn async_create(
1194 &self,
1195 ctx: &Context,
1196 parent: <Self as FileSystem>::Inode,
1197 name: &CStr,
1198 args: CreateIn,
1199 ) -> Result<(Entry, Option<<Self as FileSystem>::Handle>, OpenOptions)> {
1200 unimplemented!()
1201 }
1202
1203 async fn async_read(
1204 &self,
1205 ctx: &Context,
1206 inode: <Self as FileSystem>::Inode,
1207 handle: <Self as FileSystem>::Handle,
1208 w: &mut (dyn AsyncZeroCopyWriter + Send),
1209 size: u32,
1210 offset: u64,
1211 lock_owner: Option<u64>,
1212 flags: u32,
1213 ) -> Result<usize> {
1214 unimplemented!()
1215 }
1216
1217 async fn async_write(
1218 &self,
1219 ctx: &Context,
1220 inode: <Self as FileSystem>::Inode,
1221 handle: <Self as FileSystem>::Handle,
1222 r: &mut (dyn AsyncZeroCopyReader + Send),
1223 size: u32,
1224 offset: u64,
1225 lock_owner: Option<u64>,
1226 delayed_write: bool,
1227 flags: u32,
1228 fuse_flags: u32,
1229 ) -> Result<usize> {
1230 unimplemented!()
1231 }
1232
1233 async fn async_fsync(
1234 &self,
1235 ctx: &Context,
1236 inode: <Self as FileSystem>::Inode,
1237 datasync: bool,
1238 handle: <Self as FileSystem>::Handle,
1239 ) -> Result<()> {
1240 unimplemented!()
1241 }
1242
1243 async fn async_fallocate(
1244 &self,
1245 ctx: &Context,
1246 inode: <Self as FileSystem>::Inode,
1247 handle: <Self as FileSystem>::Handle,
1248 mode: u32,
1249 offset: u64,
1250 length: u64,
1251 ) -> Result<()> {
1252 unimplemented!()
1253 }
1254
1255 async fn async_fsyncdir(
1256 &self,
1257 ctx: &Context,
1258 inode: <Self as FileSystem>::Inode,
1259 datasync: bool,
1260 handle: <Self as FileSystem>::Handle,
1261 ) -> Result<()> {
1262 unimplemented!()
1263 }
1264 }
1265
1266 impl BackendFileSystem for FakeFileSystemOne {
1267 fn mount(&self) -> Result<(Entry, u64)> {
1268 Ok((
1269 Entry {
1270 inode: 1,
1271 ..Default::default()
1272 },
1273 0,
1274 ))
1275 }
1276
1277 fn as_any(&self) -> &dyn Any {
1278 self
1279 }
1280 }
1281
1282 #[allow(unused_variables)]
1283 #[async_trait]
1284 impl AsyncFileSystem for FakeFileSystemTwo {
1285 async fn async_lookup(
1286 &self,
1287 ctx: &Context,
1288 parent: <Self as FileSystem>::Inode,
1289 name: &CStr,
1290 ) -> Result<Entry> {
1291 Err(std::io::Error::from_raw_os_error(libc::EINVAL))
1292 }
1293
1294 async fn async_getattr(
1295 &self,
1296 ctx: &Context,
1297 inode: <Self as FileSystem>::Inode,
1298 handle: Option<<Self as FileSystem>::Handle>,
1299 ) -> Result<(libc::stat64, Duration)> {
1300 unimplemented!()
1301 }
1302
1303 async fn async_setattr(
1304 &self,
1305 ctx: &Context,
1306 inode: <Self as FileSystem>::Inode,
1307 attr: libc::stat64,
1308 handle: Option<<Self as FileSystem>::Handle>,
1309 valid: SetattrValid,
1310 ) -> Result<(libc::stat64, Duration)> {
1311 unimplemented!()
1312 }
1313
1314 async fn async_open(
1315 &self,
1316 ctx: &Context,
1317 inode: <Self as FileSystem>::Inode,
1318 flags: u32,
1319 fuse_flags: u32,
1320 ) -> Result<(Option<<Self as FileSystem>::Handle>, OpenOptions)> {
1321 unimplemented!()
1322 }
1323
1324 async fn async_create(
1325 &self,
1326 ctx: &Context,
1327 parent: <Self as FileSystem>::Inode,
1328 name: &CStr,
1329 args: CreateIn,
1330 ) -> Result<(Entry, Option<<Self as FileSystem>::Handle>, OpenOptions)> {
1331 unimplemented!()
1332 }
1333
1334 async fn async_read(
1335 &self,
1336 ctx: &Context,
1337 inode: <Self as FileSystem>::Inode,
1338 handle: <Self as FileSystem>::Handle,
1339 w: &mut (dyn AsyncZeroCopyWriter + Send),
1340 size: u32,
1341 offset: u64,
1342 lock_owner: Option<u64>,
1343 flags: u32,
1344 ) -> Result<usize> {
1345 unimplemented!()
1346 }
1347
1348 async fn async_write(
1349 &self,
1350 ctx: &Context,
1351 inode: <Self as FileSystem>::Inode,
1352 handle: <Self as FileSystem>::Handle,
1353 r: &mut (dyn AsyncZeroCopyReader + Send),
1354 size: u32,
1355 offset: u64,
1356 lock_owner: Option<u64>,
1357 delayed_write: bool,
1358 flags: u32,
1359 fuse_flags: u32,
1360 ) -> Result<usize> {
1361 unimplemented!()
1362 }
1363
1364 async fn async_fsync(
1365 &self,
1366 ctx: &Context,
1367 inode: <Self as FileSystem>::Inode,
1368 datasync: bool,
1369 handle: <Self as FileSystem>::Handle,
1370 ) -> Result<()> {
1371 unimplemented!()
1372 }
1373
1374 async fn async_fallocate(
1375 &self,
1376 ctx: &Context,
1377 inode: <Self as FileSystem>::Inode,
1378 handle: <Self as FileSystem>::Handle,
1379 mode: u32,
1380 offset: u64,
1381 length: u64,
1382 ) -> Result<()> {
1383 unimplemented!()
1384 }
1385
1386 async fn async_fsyncdir(
1387 &self,
1388 ctx: &Context,
1389 inode: <Self as FileSystem>::Inode,
1390 datasync: bool,
1391 handle: <Self as FileSystem>::Handle,
1392 ) -> Result<()> {
1393 unimplemented!()
1394 }
1395 }
1396
1397 impl BackendFileSystem for FakeFileSystemTwo {
1398 fn mount(&self) -> Result<(Entry, u64)> {
1399 Ok((
1400 Entry {
1401 inode: 1,
1402 ..Default::default()
1403 },
1404 0,
1405 ))
1406 }
1407 fn as_any(&self) -> &dyn Any {
1408 self
1409 }
1410 }
1411 }
1412
1413 #[cfg(not(feature = "async-io"))]
1414 impl BackendFileSystem for FakeFileSystemOne {
1415 fn mount(&self) -> Result<(Entry, u64)> {
1416 Ok((
1417 Entry {
1418 inode: 1,
1419 ..Default::default()
1420 },
1421 0,
1422 ))
1423 }
1424
1425 fn as_any(&self) -> &dyn Any {
1426 self
1427 }
1428 }
1429
1430 #[cfg(not(feature = "async-io"))]
1431 impl BackendFileSystem for FakeFileSystemTwo {
1432 fn mount(&self) -> Result<(Entry, u64)> {
1433 Ok((
1434 Entry {
1435 inode: 1,
1436 ..Default::default()
1437 },
1438 0,
1439 ))
1440 }
1441 fn as_any(&self) -> &dyn Any {
1442 self
1443 }
1444 }
1445
1446 #[test]
1447 fn test_vfs_init() {
1448 let vfs = Vfs::default();
1449 assert_eq!(vfs.initialized(), false);
1450
1451 let opts = vfs.opts.load();
1452 let out_opts = opts.out_opts;
1453
1454 #[cfg(target_os = "linux")]
1455 {
1456 assert_eq!(opts.no_open, true);
1457 assert_eq!(opts.no_opendir, true);
1458 assert_eq!(opts.no_writeback, false);
1459 assert_eq!(opts.killpriv_v2, false);
1460 }
1461 assert_eq!(opts.no_readdir, false);
1462 assert_eq!(opts.seal_size, false);
1463 assert_eq!(opts.in_opts.is_empty(), true);
1464
1465 vfs.init(FsOptions::ASYNC_READ).unwrap();
1466 assert_eq!(vfs.initialized(), true);
1467
1468 let opts = vfs.opts.load();
1469 #[cfg(target_os = "linux")]
1470 {
1471 assert_eq!(opts.no_open, false);
1472 assert_eq!(opts.no_opendir, false);
1473 assert_eq!(opts.no_writeback, false);
1474 assert_eq!(opts.killpriv_v2, false);
1475 }
1476 assert_eq!(opts.no_readdir, false);
1477 assert_eq!(opts.seal_size, false);
1478
1479 vfs.destroy();
1480 assert_eq!(vfs.initialized(), false);
1481
1482 let vfs = Vfs::default();
1483 #[cfg(target_os = "linux")]
1484 let in_opts =
1485 FsOptions::ASYNC_READ | FsOptions::ZERO_MESSAGE_OPEN | FsOptions::ZERO_MESSAGE_OPENDIR;
1486 #[cfg(target_os = "macos")]
1487 let in_opts = FsOptions::ASYNC_READ;
1488 vfs.init(in_opts).unwrap();
1489 let opts = vfs.opts.load();
1490 #[cfg(target_os = "linux")]
1491 {
1492 assert_eq!(opts.no_open, true);
1493 assert_eq!(opts.no_opendir, true);
1494 assert_eq!(opts.no_writeback, false);
1495 assert_eq!(opts.killpriv_v2, false);
1496 }
1497 assert_eq!(opts.out_opts, out_opts & in_opts);
1498 }
1499
1500 #[test]
1501 fn test_vfs_lookup() {
1502 let vfs = Vfs::new(VfsOptions::default());
1503 let fs = FakeFileSystemOne {};
1504 let ctx = Context::new();
1505
1506 assert!(vfs.mount(Box::new(fs), "/x/y").is_ok());
1507
1508 let entry1 = vfs
1510 .lookup(&ctx, ROOT_ID.into(), CString::new("x").unwrap().as_c_str())
1511 .unwrap();
1512 assert_eq!(entry1.inode, 0x2);
1513
1514 let entry2 = vfs
1516 .lookup(
1517 &ctx,
1518 entry1.inode.into(),
1519 CString::new("y").unwrap().as_c_str(),
1520 )
1521 .unwrap();
1522 assert_eq!(entry2.inode, 0x100_0000_0000_0001);
1523
1524 let entry3 = vfs
1526 .lookup(
1527 &ctx,
1528 entry2.inode.into(),
1529 CString::new("z").unwrap().as_c_str(),
1530 )
1531 .unwrap();
1532 assert_eq!(entry3.inode, 0);
1533
1534 let (stat, _) = vfs
1535 .getattr(&ctx, VfsInode(0x100_0000_0000_0001), None)
1536 .unwrap();
1537 assert_eq!(stat.st_ino, 0x100_0000_0000_0001);
1538 }
1539
1540 #[test]
1541 fn test_mount_different_fs_types() {
1542 let vfs = Vfs::new(VfsOptions::default());
1543 let fs1 = FakeFileSystemOne {};
1544 let fs2 = FakeFileSystemTwo {};
1545 assert!(vfs.mount(Box::new(fs1), "/foo").is_ok());
1546 assert!(vfs.mount(Box::new(fs2), "/bar").is_ok());
1547
1548 let ctx = Context::new();
1550 let entry1 = vfs
1551 .lookup(
1552 &ctx,
1553 ROOT_ID.into(),
1554 CString::new("bar").unwrap().as_c_str(),
1555 )
1556 .unwrap();
1557 assert_eq!(entry1.inode, 0x200_0000_0000_0001);
1558 assert_eq!(entry1.attr.st_ino, 0x200_0000_0000_0001);
1559 }
1560
1561 #[test]
1562 fn test_umount() {
1563 let vfs = Vfs::new(VfsOptions::default());
1564 let fs1 = FakeFileSystemOne {};
1565 let fs2 = FakeFileSystemOne {};
1566 assert!(vfs.mount(Box::new(fs1), "/foo").is_ok());
1567 assert!(vfs.umount("/foo").is_ok());
1568
1569 assert!(vfs.mount(Box::new(fs2), "/x/y").is_ok());
1570
1571 match vfs.umount("/x") {
1572 Err(VfsError::NotFound(_e)) => {}
1573 _ => panic!("expect VfsError::NotFound(/x)"),
1574 }
1575 }
1576
1577 #[test]
1578 fn test_umount_overlap() {
1579 let vfs = Vfs::new(VfsOptions::default());
1580 let fs1 = FakeFileSystemOne {};
1581 let fs2 = FakeFileSystemTwo {};
1582
1583 assert!(vfs.mount(Box::new(fs1), "/x/y/z").is_ok());
1584 assert!(vfs.mount(Box::new(fs2), "/x/y").is_ok());
1585
1586 let (m1, _) = vfs.get_rootfs("/x/y/z").unwrap().unwrap();
1587 assert!(m1.as_any().is::<FakeFileSystemOne>());
1588 let (m2, _) = vfs.get_rootfs("/x/y").unwrap().unwrap();
1589 assert!(m2.as_any().is::<FakeFileSystemTwo>());
1590
1591 assert!(vfs.umount("/x/y/z").is_ok());
1592 assert!(vfs.umount("/x/y").is_ok());
1593
1594 match vfs.umount("/x/y/z") {
1595 Err(VfsError::NotFound(_e)) => {}
1596 _ => panic!("expect VfsError::NotFound(/x/y/z)"),
1597 }
1598 }
1599
1600 #[test]
1601 fn test_umount_same() {
1602 let vfs = Vfs::new(VfsOptions::default());
1603 let fs1 = FakeFileSystemOne {};
1604 let fs2 = FakeFileSystemTwo {};
1605
1606 assert!(vfs.mount(Box::new(fs1), "/x/y").is_ok());
1607 assert!(vfs.mount(Box::new(fs2), "/x/y").is_ok());
1608
1609 let (m1, _) = vfs.get_rootfs("/x/y").unwrap().unwrap();
1610 assert!(m1.as_any().is::<FakeFileSystemTwo>());
1611
1612 assert!(vfs.umount("/x/y").is_ok());
1613
1614 match vfs.umount("/x/y") {
1615 Err(VfsError::NotFound(_e)) => {}
1616 _ => panic!("expect VfsError::NotFound(/x/y)"),
1617 }
1618 }
1619
1620 #[test]
1621 #[should_panic]
1622 fn test_invalid_inode() {
1623 let _ = VfsInode::new(1, VFS_MAX_INO + 1);
1624 }
1625
1626 #[test]
1627 fn test_inode() {
1628 let inode = VfsInode::new(2, VFS_MAX_INO);
1629
1630 assert_eq!(inode.fs_idx(), 2);
1631 assert_eq!(inode.ino(), VFS_MAX_INO);
1632 assert!(!inode.is_pseudo_fs());
1633 assert_eq!(u64::from(inode), 0x200_0000_0000_0000u64 + VFS_MAX_INO);
1634 }
1635
1636 #[test]
1637 fn test_allocate_fs_idx() {
1638 let vfs = Vfs::new(VfsOptions::default());
1639 let _guard = vfs.lock.lock().unwrap();
1640
1641 for _ in 0..255 {
1643 let fs = FakeFileSystemOne {};
1644 let index = vfs.allocate_fs_idx().unwrap();
1645 let mut superblocks = vfs.superblocks.load().deref().deref().clone();
1646
1647 superblocks[index as usize] = Some(Arc::new(Box::new(fs)));
1648 vfs.superblocks.store(Arc::new(superblocks));
1649 }
1650
1651 for _ in 0..=256 {
1653 vfs.allocate_fs_idx().unwrap_err();
1654 }
1655 }
1656
1657 #[test]
1658 fn test_fmt_vfs_error() {
1659 assert_eq!(
1660 format!("{}", VfsError::Unsupported),
1661 "Vfs operation not supported".to_string()
1662 );
1663 assert_eq!(
1664 format!(
1665 "{}",
1666 VfsError::Mount(Error::new(ErrorKind::Other, "mount".to_string()),)
1667 ),
1668 "Mount backend filesystem: mount".to_string()
1669 );
1670 assert_eq!(
1671 format!("{}", VfsError::InodeIndex("inode index".to_string())),
1672 "Illegal inode index: inode index".to_string()
1673 );
1674 assert_eq!(
1675 format!(
1676 "{}",
1677 VfsError::FsIndex(Error::new(ErrorKind::Other, "fs index".to_string()),)
1678 ),
1679 "Filesystem index error: fs index".to_string()
1680 );
1681 assert_eq!(
1682 format!(
1683 "{}",
1684 VfsError::PathWalk(Error::new(ErrorKind::Other, "path walk".to_string()),)
1685 ),
1686 "Walking path error: path walk".to_string()
1687 );
1688 assert_eq!(
1689 format!("{}", VfsError::NotFound("not found".to_string())),
1690 "Entry can't be found: not found".to_string()
1691 );
1692 assert_eq!(
1693 format!("{}", VfsError::Initialize("initialize".to_string())),
1694 "File system can't be initialized: initialize".to_string()
1695 );
1696 }
1697}