#[path = "../../../os-vfs-traits/src/lib.rs"] mod traits; use traits::{Error, FileSystem, Kind, Metadata, Mount}; #[derive(Clone, Copy, Debug, PartialEq, Eq)] struct TinyFs { fault_read: bool, corrupt_file: bool } impl FileSystem for TinyFs { fn root(&self) -> u64 { 1 } fn lookup(&self, parent: u64, name: &[u8]) -> Result { if parent == 1 && name == b"hello" { Ok(2) } else { Err(Error::NoEntry) } } fn metadata(&self, inode: u64) -> Result { match inode { 1 => Ok(Metadata {inode: 1, kind: Kind::Directory, size: 0, allocated_bytes: 0, read_only: true}), 2 => Ok(Metadata {inode: 2, kind: Kind::Regular, size: if self.corrupt_file { 513 } else { 5 }, allocated_bytes: 512, read_only: true}), _ => Err(Error::NoEntry), } } fn read_at(&self, inode: u64, offset: u64, output: &mut [u8]) -> Result { if inode != 2 { return Err(Error::NoEntry); } if self.fault_read { output[0] = b'X'; return Err(Error::ReadFault); } let bytes = b"hello"; let begin = offset as usize; let actual = output.len().min(bytes.len().saturating_sub(begin)); output[..actual].copy_from_slice(&bytes[begin..begin + actual]); Ok(actual) } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] struct OtherFs; impl FileSystem for OtherFs { fn root(&self) -> u64 { 10 } fn lookup(&self, parent: u64, name: &[u8]) -> Result { if parent == 10 && name == b"world" { Ok(20) } else { Err(Error::NoEntry) } } fn metadata(&self, inode: u64) -> Result { match inode { 10 => Ok(Metadata {inode: 10, kind: Kind::Directory, size: 0, allocated_bytes: 0, read_only: true}), 20 => Ok(Metadata {inode: 20, kind: Kind::Regular, size: 5, allocated_bytes: 512, read_only: true}), _ => Err(Error::NoEntry), } } fn read_at(&self, inode: u64, offset: u64, output: &mut [u8]) -> Result { if inode != 20 { return Err(Error::NoEntry); } let bytes = b"world"; let begin = offset as usize; let actual = output.len().min(bytes.len().saturating_sub(begin)); output[..actual].copy_from_slice(&bytes[begin..begin + actual]); Ok(actual) } } #[test] fn os_vfs_traits_positive() { let mut mount = Mount::new(7, 9, TinyFs {fault_read: false, corrupt_file: false}).unwrap(); let root = mount.root(); let file = mount.lookup(root, b"hello").unwrap(); assert_eq!((file.volume(), file.generation(), file.inode()), (7, 9, 2)); assert_eq!(mount.metadata(file).unwrap().size, 5); let mut output = [b'!'; 8]; assert_eq!(mount.read_at(file, 0, &mut output), Ok(5)); assert_eq!(&output, b"hello!!!"); assert_eq!(mount.read_at(file, 5, &mut output), Ok(0)); assert_eq!(&output, b"hello!!!"); assert_eq!(mount.remount(), Ok(10)); assert_eq!(mount.metadata(file), Err(Error::Stale)); assert_eq!(mount.read_at(file, 0, &mut output), Err(Error::Stale)); let fresh = mount.lookup(mount.root(), b"hello").unwrap(); assert_eq!(fresh.generation(), 10); let other = Mount::new(8, 3, OtherFs).unwrap(); let other_file = other.lookup(other.root(), b"world").unwrap(); let mut other_output = [0u8; 5]; assert_eq!(other.read_at(other_file, 0, &mut other_output), Ok(5)); assert_eq!(&other_output, b"world"); } #[test] fn os_vfs_traits_negative() { let backend = TinyFs {fault_read: true, corrupt_file: false}; assert_eq!(Mount::new(0, 1, backend), Err(Error::BadMount)); let mount = Mount::new(1, 1, backend).unwrap(); let root = mount.root(); assert_eq!(mount.lookup(root, b"he/llo"), Err(Error::InvalidName)); assert_eq!(mount.lookup(root, b"missing"), Err(Error::NoEntry)); let file = mount.lookup(root, b"hello").unwrap(); let mut output = [b'!'; 8]; assert_eq!(mount.read_at(root, 0, &mut output), Err(Error::IsDirectory)); assert_eq!(mount.read_at(file, 0, &mut output), Err(Error::ReadFault)); assert_eq!(output, [b'!'; 8]); let mut oversized = [0u8; 33]; assert_eq!(mount.read_at(file, 0, &mut oversized), Err(Error::TooLarge)); assert_eq!(oversized, [0; 33]); let corrupt = Mount::new(1, 1, TinyFs {fault_read: false, corrupt_file: true}).unwrap(); assert_eq!(corrupt.lookup(corrupt.root(), b"hello"), Err(Error::CorruptMetadata)); }