feat(vfs): add cow tests for memfs
This commit is contained in:
+96
-9
@@ -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;
|
||||
|
||||
+16
-17
@@ -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<A: BlockAllocator + Copy + 'static> {
|
||||
root: RefCell<Option<VnodeRef>>,
|
||||
@@ -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<usize, Errno> {
|
||||
fn open(&mut self, _node: VnodeRef) -> Result<usize, Errno> {
|
||||
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<isize, Errno> {
|
||||
fn ioctl(&mut self, _node: VnodeRef, cmd: u64, value: *mut c_void) -> Result<isize, Errno> {
|
||||
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<usize, Errno> {
|
||||
fn open(&mut self, _node: VnodeRef) -> Result<usize, Errno> {
|
||||
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<usize, Errno> {
|
||||
fn read(&mut self, _node: VnodeRef, _pos: usize, _data: &mut [u8]) -> Result<usize, Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn write(&mut self, node: VnodeRef, pos: usize, data: &[u8]) -> Result<usize, Errno> {
|
||||
fn write(&mut self, _node: VnodeRef, _pos: usize, _data: &[u8]) -> Result<usize, Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ impl VnodeImpl for DirInode {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn ioctl(&mut self, node: VnodeRef, cmd: u64, value: *mut c_void) -> Result<isize, Errno> {
|
||||
fn ioctl(&mut self, _node: VnodeRef, _cmd: u64, _value: *mut c_void) -> Result<isize, Errno> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
@@ -134,7 +134,7 @@ impl<A: BlockAllocator + Copy + 'static> Filesystem for Ramfs<A> {
|
||||
}
|
||||
|
||||
fn create_node(self: Rc<Self>, name: &str, kind: VnodeKind) -> Result<VnodeRef, Errno> {
|
||||
let mut node = Vnode::new(name, kind, Vnode::SEEKABLE);
|
||||
let node = Vnode::new(name, kind, Vnode::SEEKABLE);
|
||||
let data: Box<dyn VnodeImpl> = match kind {
|
||||
VnodeKind::Regular => Box::new(FileInode {
|
||||
data: Bvec::new(self.alloc),
|
||||
@@ -151,7 +151,7 @@ impl<A: BlockAllocator + Copy + 'static> Filesystem for Ramfs<A> {
|
||||
|
||||
impl<A: BlockAllocator + Copy + 'static> Ramfs<A> {
|
||||
pub fn open(base: *const u8, size: usize, alloc: A) -> Result<Rc<Self>, Errno> {
|
||||
let mut res = Rc::new(Self {
|
||||
let res = Rc::new(Self {
|
||||
root: RefCell::new(None),
|
||||
alloc,
|
||||
});
|
||||
@@ -220,9 +220,7 @@ impl<A: BlockAllocator + Copy + 'static> Ramfs<A> {
|
||||
|
||||
#[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<A: BlockAllocator + Copy + 'static> Ramfs<A> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use alloc::boxed::Box;
|
||||
use libcommon::Read;
|
||||
use vfs::Ioctx;
|
||||
|
||||
#[test]
|
||||
|
||||
+15
-1
@@ -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<usize, Errno> {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
|
||||
fn ioctl(
|
||||
&mut self,
|
||||
_node: VnodeRef,
|
||||
_cmd: u64,
|
||||
_value: *mut c_void,
|
||||
) -> Result<isize, Errno> {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
impl Filesystem for DummyFs {
|
||||
@@ -115,7 +129,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn create_node(self: Rc<Self>, name: &str, kind: VnodeKind) -> Result<VnodeRef, Errno> {
|
||||
let node = Vnode::new(name, kind);
|
||||
let node = Vnode::new(name, kind, 0);
|
||||
node.set_data(VnodeData {
|
||||
node: Box::new(DummyInode {}),
|
||||
fs: self,
|
||||
|
||||
+25
-11
@@ -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<usize, Errno> {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
|
||||
fn ioctl(
|
||||
&mut self,
|
||||
_node: VnodeRef,
|
||||
_cmd: u64,
|
||||
_value: *mut c_void,
|
||||
) -> Result<isize, Errno> {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
impl Filesystem for DummyFs {
|
||||
@@ -115,7 +129,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn create_node(self: Rc<Self>, name: &str, kind: VnodeKind) -> Result<VnodeRef, Errno> {
|
||||
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 {
|
||||
|
||||
+11
-16
@@ -291,6 +291,10 @@ mod tests {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
|
||||
fn size(&mut self, _node: VnodeRef) -> Result<usize, Errno> {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
|
||||
fn ioctl(
|
||||
&mut self,
|
||||
_node: VnodeRef,
|
||||
@@ -299,15 +303,6 @@ mod tests {
|
||||
) -> Result<isize, Errno> {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
|
||||
fn seek(
|
||||
&mut self,
|
||||
_node: VnodeRef,
|
||||
_pos: usize,
|
||||
_whence: SeekWhence,
|
||||
) -> Result<usize, Errno> {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
impl Filesystem for DummyFs {
|
||||
@@ -316,7 +311,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn create_node(self: Rc<Self>, name: &str, kind: VnodeKind) -> Result<VnodeRef, Errno> {
|
||||
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());
|
||||
|
||||
Reference in New Issue
Block a user