From 8274abbda1e5dd9a47bca88c68f891731e93d8c8 Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Tue, 26 Oct 2021 13:26:47 +0300 Subject: [PATCH] feat(vfs): add cow tests for memfs --- fs/memfs/src/bvec.rs | 105 +++++++++++++++++++++++++++++++++++++++---- fs/memfs/src/lib.rs | 33 +++++++------- fs/vfs/src/file.rs | 16 ++++++- fs/vfs/src/ioctx.rs | 36 ++++++++++----- fs/vfs/src/node.rs | 27 +++++------ 5 files changed, 163 insertions(+), 54 deletions(-) diff --git a/fs/memfs/src/bvec.rs b/fs/memfs/src/bvec.rs index 6653009..cfaef9c 100644 --- a/fs/memfs/src/bvec.rs +++ b/fs/memfs/src/bvec.rs @@ -1,11 +1,12 @@ use crate::{block, BlockAllocator, BlockRef}; -use core::cmp::{max, min}; -use core::marker::PhantomData; +use core::cmp::min; use core::mem::MaybeUninit; use core::ops::{Index, IndexMut}; use error::Errno; + const L0_BLOCKS: usize = 32; // 128K const L1_BLOCKS: usize = 8; // 16M + pub struct Bvec<'a, A: BlockAllocator + Copy> { capacity: usize, size: usize, @@ -26,7 +27,7 @@ impl<'a, A: BlockAllocator + Copy> Bvec<'a, A> { l2: MaybeUninit::uninit(), alloc, #[cfg(feature = "cow")] - cow_source: core::ptr::null_mut() + cow_source: core::ptr::null_mut(), }; for it in res.l0.iter_mut() { it.write(BlockRef::null()); @@ -38,6 +39,11 @@ impl<'a, A: BlockAllocator + Copy> Bvec<'a, A> { res } + #[cfg(feature = "cow")] + pub fn is_cow(&self) -> bool { + !self.cow_source.is_null() + } + #[cfg(feature = "cow")] pub unsafe fn setup_cow(&mut self, src: *const u8, size: usize) { self.cow_source = src; @@ -50,7 +56,7 @@ impl<'a, A: BlockAllocator + Copy> Bvec<'a, A> { #[cfg(feature = "cow")] pub fn drop_cow(&mut self) { - assert!(!self.cow_source.is_null()); + assert!(self.is_cow()); let src_slice = unsafe { core::slice::from_raw_parts(self.cow_source, self.size) }; self.cow_source = core::ptr::null_mut(); @@ -60,7 +66,7 @@ impl<'a, A: BlockAllocator + Copy> Bvec<'a, A> { pub fn resize(&mut self, cap: usize) -> Result<(), Errno> { #[cfg(feature = "cow")] - assert!(self.cow_source.is_null()); + assert!(!self.is_cow()); if cap <= self.capacity { let mut curr = self.capacity; @@ -104,7 +110,6 @@ impl<'a, A: BlockAllocator + Copy> Bvec<'a, A> { assert!(!l0r.is_null()); *l0r = BlockRef::null(); continue; - unimplemented!(); } } else { for mut index in self.capacity..cap { @@ -156,7 +161,7 @@ impl<'a, A: BlockAllocator + Copy> Bvec<'a, A> { } #[cfg(feature = "cow")] - if !self.cow_source.is_null() { + if self.is_cow() { self.drop_cow(); } @@ -188,7 +193,7 @@ impl<'a, A: BlockAllocator + Copy> Bvec<'a, A> { let mut rem = min(self.size - pos, data.len()); #[cfg(feature = "cow")] - if !self.cow_source.is_null() { + if self.is_cow() { let cow_data = unsafe { core::slice::from_raw_parts(self.cow_source, self.size) }; data[..rem].copy_from_slice(&cow_data[pos..pos + rem]); return Ok(rem); @@ -280,9 +285,91 @@ impl<'a, A: BlockAllocator + Copy> Drop for Bvec<'a, A> { } } +#[cfg(feature = "cow")] +#[cfg(test)] +mod cow_tests { + use super::*; + use std::boxed::Box; + + #[derive(Clone, Copy)] + struct TestAlloc; + unsafe impl BlockAllocator for TestAlloc { + fn alloc(&self) -> *mut u8 { + let b = Box::leak(Box::new([0; block::SIZE])); + b.as_mut_ptr() as *mut _ + } + unsafe fn dealloc(&self, ptr: *mut u8) { + drop(Box::from_raw(ptr as *mut [u8; block::SIZE])); + } + } + + #[test] + fn bvec_write_copy_simple() { + let mut bvec = Bvec::new(TestAlloc {}); + let mut buf = [0u8; 512]; + let source_data = b"This is initial data\n"; + unsafe { + bvec.setup_cow(source_data.as_ptr(), source_data.len()); + } + assert!(bvec.is_cow()); + assert_eq!(bvec.size(), source_data.len()); + assert_eq!(bvec.capacity, 0); + + bvec.write(8, b"testing").unwrap(); + + assert!(!bvec.is_cow()); + assert_eq!(bvec.size(), source_data.len()); + assert_eq!(bvec.capacity, 1); + + assert_eq!(bvec.read(0, &mut buf).unwrap(), source_data.len()); + assert_eq!(&mut buf[..source_data.len()], b"This is testing data\n"); + } + + #[test] + fn bvec_write_copy_l0() { + let mut bvec = Bvec::new(TestAlloc {}); + let mut source_data = [0u8; 4096 * 2 - 2]; + let mut buf = [0u8; 512]; + for i in 0..source_data.len() { + source_data[i] = (i & 0xFF) as u8; + } + unsafe { + bvec.setup_cow(source_data.as_ptr(), source_data.len()); + } + assert!(bvec.is_cow()); + assert_eq!(bvec.size(), source_data.len()); + assert_eq!(bvec.capacity, 0); + + bvec.write(0, b"test").unwrap(); + + assert!(!bvec.is_cow()); + assert_eq!(bvec.size(), source_data.len()); + assert_eq!(bvec.capacity, 2); + + assert_eq!(bvec.read(0, &mut buf).unwrap(), 512); + assert_eq!(&buf[..4], b"test"); + for i in 4..512 { + assert_eq!(buf[i], (i & 0xFF) as u8); + } + assert_eq!(bvec.read(512, &mut buf).unwrap(), 512); + for i in 0..512 { + assert_eq!(buf[i], ((i + 512) & 0xFF) as u8); + } + + bvec.write(source_data.len(), b"test"); + assert_eq!(bvec.size(), 4096 * 2 + 2); + assert_eq!(bvec.capacity, 3); + + assert_eq!(bvec.read(4096 * 2, &mut buf).unwrap(), 2); + assert_eq!(&buf[..2], b"st"); + assert_eq!(bvec.read(4096 * 2 - 2, &mut buf).unwrap(), 4); + assert_eq!(&buf[..4], b"test"); + } +} + #[cfg(feature = "test_bvec")] #[cfg(test)] -mod tests { +mod bvec_tests { use super::*; use std::boxed::Box; use std::mem::MaybeUninit; diff --git a/fs/memfs/src/lib.rs b/fs/memfs/src/lib.rs index 6b275f8..e52a409 100644 --- a/fs/memfs/src/lib.rs +++ b/fs/memfs/src/lib.rs @@ -23,7 +23,7 @@ pub use block::{BlockAllocator, BlockRef}; pub mod bvec; use bvec::Bvec; pub mod tar; -use tar::{Tar, TarIterator}; +use tar::TarIterator; pub struct Ramfs { root: RefCell>, @@ -37,7 +37,7 @@ pub struct FileInode<'a, A: BlockAllocator + Copy + 'static> { pub struct DirInode; struct SetupFileParam { - data: &'static [u8] + data: &'static [u8], } const FILE_SETUP_COW: u64 = 0x1001; @@ -51,11 +51,11 @@ impl<'a, A: BlockAllocator + Copy + 'static> VnodeImpl for FileInode<'a, A> { panic!() } - fn open(&mut self, node: VnodeRef) -> Result { + fn open(&mut self, _node: VnodeRef) -> Result { Ok(0) } - fn close(&mut self, node: VnodeRef) -> Result<(), Errno> { + fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> { Ok(()) } @@ -75,7 +75,7 @@ impl<'a, A: BlockAllocator + Copy + 'static> VnodeImpl for FileInode<'a, A> { Ok(self.data.size()) } - fn ioctl(&mut self, node: VnodeRef, cmd: u64, value: *mut c_void) -> Result { + fn ioctl(&mut self, _node: VnodeRef, cmd: u64, value: *mut c_void) -> Result { match cmd { #[cfg(feature = "cow")] FILE_SETUP_COW => { @@ -84,8 +84,8 @@ impl<'a, A: BlockAllocator + Copy + 'static> VnodeImpl for FileInode<'a, A> { self.data.setup_cow(data.data.as_ptr(), data.data.len()); } Ok(0) - }, - _ => Err(Errno::InvalidArgument) + } + _ => Err(Errno::InvalidArgument), } } } @@ -99,19 +99,19 @@ impl VnodeImpl for DirInode { Ok(()) } - fn open(&mut self, node: VnodeRef) -> Result { + fn open(&mut self, _node: VnodeRef) -> Result { todo!() } - fn close(&mut self, node: VnodeRef) -> Result<(), Errno> { + fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> { todo!() } - fn read(&mut self, node: VnodeRef, pos: usize, data: &mut [u8]) -> Result { + fn read(&mut self, _node: VnodeRef, _pos: usize, _data: &mut [u8]) -> Result { todo!() } - fn write(&mut self, node: VnodeRef, pos: usize, data: &[u8]) -> Result { + fn write(&mut self, _node: VnodeRef, _pos: usize, _data: &[u8]) -> Result { todo!() } @@ -123,7 +123,7 @@ impl VnodeImpl for DirInode { todo!() } - fn ioctl(&mut self, node: VnodeRef, cmd: u64, value: *mut c_void) -> Result { + fn ioctl(&mut self, _node: VnodeRef, _cmd: u64, _value: *mut c_void) -> Result { todo!() } } @@ -134,7 +134,7 @@ impl Filesystem for Ramfs { } fn create_node(self: Rc, name: &str, kind: VnodeKind) -> Result { - let mut node = Vnode::new(name, kind, Vnode::SEEKABLE); + let node = Vnode::new(name, kind, Vnode::SEEKABLE); let data: Box = match kind { VnodeKind::Regular => Box::new(FileInode { data: Bvec::new(self.alloc), @@ -151,7 +151,7 @@ impl Filesystem for Ramfs { impl Ramfs { pub fn open(base: *const u8, size: usize, alloc: A) -> Result, Errno> { - let mut res = Rc::new(Self { + let res = Rc::new(Self { root: RefCell::new(None), alloc, }); @@ -220,9 +220,7 @@ impl Ramfs { #[cfg(feature = "cow")] { - let mut param = SetupFileParam { - data: block.data() - }; + let mut param = SetupFileParam { data: block.data() }; node.ioctl(FILE_SETUP_COW, &mut param as *mut _ as *mut _)?; } #[cfg(not(feature = "cow"))] @@ -244,6 +242,7 @@ impl Ramfs { mod tests { use super::*; use alloc::boxed::Box; + use libcommon::Read; use vfs::Ioctx; #[test] diff --git a/fs/vfs/src/file.rs b/fs/vfs/src/file.rs index 0c35b52..e7bb35f 100644 --- a/fs/vfs/src/file.rs +++ b/fs/vfs/src/file.rs @@ -67,6 +67,7 @@ mod tests { use super::*; use crate::{node::VnodeData, Filesystem, Ioctx, Vnode, VnodeImpl, VnodeKind, VnodeRef}; use alloc::boxed::Box; + use core::ffi::c_void; pub struct DummyInode; pub struct DummyFs; @@ -107,6 +108,19 @@ mod tests { fn truncate(&mut self, _node: VnodeRef, _size: usize) -> Result<(), Errno> { Err(Errno::NotImplemented) } + + fn size(&mut self, _node: VnodeRef) -> Result { + Err(Errno::NotImplemented) + } + + fn ioctl( + &mut self, + _node: VnodeRef, + _cmd: u64, + _value: *mut c_void, + ) -> Result { + Err(Errno::NotImplemented) + } } impl Filesystem for DummyFs { @@ -115,7 +129,7 @@ mod tests { } fn create_node(self: Rc, name: &str, kind: VnodeKind) -> Result { - let node = Vnode::new(name, kind); + let node = Vnode::new(name, kind, 0); node.set_data(VnodeData { node: Box::new(DummyInode {}), fs: self, diff --git a/fs/vfs/src/ioctx.rs b/fs/vfs/src/ioctx.rs index a4985df..63a1b28 100644 --- a/fs/vfs/src/ioctx.rs +++ b/fs/vfs/src/ioctx.rs @@ -75,6 +75,7 @@ mod tests { use super::*; use crate::{node::VnodeData, Filesystem, Vnode, VnodeImpl, VnodeKind}; use alloc::{boxed::Box, rc::Rc}; + use core::ffi::c_void; pub struct DummyInode; pub struct DummyFs; @@ -107,6 +108,19 @@ mod tests { fn truncate(&mut self, _node: VnodeRef, _size: usize) -> Result<(), Errno> { Err(Errno::NotImplemented) } + + fn size(&mut self, _node: VnodeRef) -> Result { + Err(Errno::NotImplemented) + } + + fn ioctl( + &mut self, + _node: VnodeRef, + _cmd: u64, + _value: *mut c_void, + ) -> Result { + Err(Errno::NotImplemented) + } } impl Filesystem for DummyFs { @@ -115,7 +129,7 @@ mod tests { } fn create_node(self: Rc, name: &str, kind: VnodeKind) -> Result { - let node = Vnode::new(name, kind); + let node = Vnode::new(name, kind, 0); node.set_data(VnodeData { node: Box::new(DummyInode {}), fs: self, @@ -126,12 +140,12 @@ mod tests { #[test] fn test_find_existing_absolute() { - let root = Vnode::new("", VnodeKind::Directory); - let d0 = Vnode::new("dir0", VnodeKind::Directory); - let d1 = Vnode::new("dir1", VnodeKind::Directory); - let d0d0 = Vnode::new("dir0", VnodeKind::Directory); - let d0f0 = Vnode::new("file0", VnodeKind::Regular); - let d1f0 = Vnode::new("file0", VnodeKind::Regular); + let root = Vnode::new("", VnodeKind::Directory, 0); + let d0 = Vnode::new("dir0", VnodeKind::Directory, 0); + let d1 = Vnode::new("dir1", VnodeKind::Directory, 0); + let d0d0 = Vnode::new("dir0", VnodeKind::Directory, 0); + let d0f0 = Vnode::new("file0", VnodeKind::Regular, 0); + let d1f0 = Vnode::new("file0", VnodeKind::Regular, 0); root.attach(d0.clone()); root.attach(d1.clone()); @@ -182,9 +196,9 @@ mod tests { #[test] fn test_find_rejects_file_dots() { - let root = Vnode::new("", VnodeKind::Directory); - let d0 = Vnode::new("dir0", VnodeKind::Directory); - let d0f0 = Vnode::new("file0", VnodeKind::Regular); + let root = Vnode::new("", VnodeKind::Directory, 0); + let d0 = Vnode::new("dir0", VnodeKind::Directory, 0); + let d0f0 = Vnode::new("file0", VnodeKind::Regular, 0); root.attach(d0.clone()); d0.attach(d0f0.clone()); @@ -207,7 +221,7 @@ mod tests { #[test] fn test_mkdir() { let fs = Rc::new(DummyFs {}); - let root = Vnode::new("", VnodeKind::Directory); + let root = Vnode::new("", VnodeKind::Directory, 0); let ioctx = Ioctx::new(root.clone()); root.set_data(VnodeData { diff --git a/fs/vfs/src/node.rs b/fs/vfs/src/node.rs index d85b5d8..f35fb0a 100644 --- a/fs/vfs/src/node.rs +++ b/fs/vfs/src/node.rs @@ -291,6 +291,10 @@ mod tests { Err(Errno::NotImplemented) } + fn size(&mut self, _node: VnodeRef) -> Result { + Err(Errno::NotImplemented) + } + fn ioctl( &mut self, _node: VnodeRef, @@ -299,15 +303,6 @@ mod tests { ) -> Result { Err(Errno::NotImplemented) } - - fn seek( - &mut self, - _node: VnodeRef, - _pos: usize, - _whence: SeekWhence, - ) -> Result { - Err(Errno::NotImplemented) - } } impl Filesystem for DummyFs { @@ -316,7 +311,7 @@ mod tests { } fn create_node(self: Rc, name: &str, kind: VnodeKind) -> Result { - let node = Vnode::new(name, kind); + let node = Vnode::new(name, kind, 0); node.set_data(VnodeData { node: Box::new(DummyInode {}), fs: self, @@ -327,8 +322,8 @@ mod tests { #[test] fn test_parent() { - let root = Vnode::new("", VnodeKind::Directory); - let node = Vnode::new("dir0", VnodeKind::Directory); + let root = Vnode::new("", VnodeKind::Directory, 0); + let node = Vnode::new("dir0", VnodeKind::Directory, 0); root.attach(node.clone()); @@ -339,7 +334,7 @@ mod tests { #[test] fn test_mkdir_unlink() { let fs = Rc::new(DummyFs {}); - let root = Vnode::new("", VnodeKind::Directory); + let root = Vnode::new("", VnodeKind::Directory, 0); root.set_data(VnodeData { node: Box::new(DummyInode {}), @@ -364,9 +359,9 @@ mod tests { #[test] fn test_lookup_attach_detach() { - let root = Vnode::new("", VnodeKind::Directory); - let dir0 = Vnode::new("dir0", VnodeKind::Directory); - let dir1 = Vnode::new("dir1", VnodeKind::Directory); + let root = Vnode::new("", VnodeKind::Directory, 0); + let dir0 = Vnode::new("dir0", VnodeKind::Directory, 0); + let dir1 = Vnode::new("dir1", VnodeKind::Directory, 0); root.attach(dir0.clone()); root.attach(dir1.clone());