diff --git a/Cargo.lock b/Cargo.lock index 135cfbd..8880cd5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -108,7 +108,9 @@ dependencies = [ "cortex-a", "error", "fdt-rs", + "memfs", "tock-registers", + "vfs", ] [[package]] @@ -121,6 +123,14 @@ checksum = "7b2f96d100e1cf1929e7719b7edb3b90ab5298072638fccd77be9ce942ecdfce" name = "libusr" version = "0.1.0" +[[package]] +name = "memfs" +version = "0.1.0" +dependencies = [ + "error", + "vfs", +] + [[package]] name = "memoffset" version = "0.5.6" diff --git a/Cargo.toml b/Cargo.toml index a379754..c92c1ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ edition = "2018" [workspace] members = [ "fs/vfs", + "fs/memfs", "kernel", "libusr", "init", diff --git a/Makefile b/Makefile index 83253fc..18c42c3 100644 --- a/Makefile +++ b/Makefile @@ -60,7 +60,8 @@ all: kernel kernel: cd kernel && cargo build $(CARGO_BUILD_OPTS) cd init && cargo build --target=../etc/$(ARCH)-osdev5.json -Z build-std=core,alloc,compiler_builtins - cp target/$(ARCH)-osdev5/debug/init $(O)/initrd.img + echo "This is a test file" >$(O)/test.txt + cd $(O) && tar cf initrd.img test.txt ifeq ($(ARCH),aarch64) $(LLVM_BASE)/llvm-strip -o $(O)/kernel.strip $(O)/kernel $(LLVM_BASE)/llvm-size $(O)/kernel.strip @@ -79,6 +80,10 @@ ifeq ($(MACH),orangepi3) $(O)/uImage endif +test: + cd fs/vfs && cargo test + cd fs/memfs && cargo test + clean: cargo clean diff --git a/fs/memfs/Cargo.toml b/fs/memfs/Cargo.toml new file mode 100644 index 0000000..d261a18 --- /dev/null +++ b/fs/memfs/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "memfs" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +vfs = { path = "../vfs" } +error = { path = "../../error" } diff --git a/fs/memfs/src/block.rs b/fs/memfs/src/block.rs new file mode 100644 index 0000000..e02dd6c --- /dev/null +++ b/fs/memfs/src/block.rs @@ -0,0 +1,132 @@ +use core::mem::{size_of, MaybeUninit}; +use core::ops::{Deref, DerefMut}; +use error::Errno; + +pub const SIZE: usize = 4096; +pub const ENTRY_COUNT: usize = SIZE / size_of::(); + +// Should be the same as "usize" in layout +pub struct BlockRef<'a, A: BlockAllocator + Copy> { + inner: Option<&'a mut [u8; SIZE]>, + alloc: MaybeUninit, +} + +pub unsafe trait BlockAllocator { + fn alloc(&self) -> *mut u8; + unsafe fn dealloc(&self, block: *mut u8); +} + +impl<'a, A: BlockAllocator + Copy> BlockRef<'a, A> { + pub fn new(alloc: A) -> Result { + assert!(size_of::() == 0); + let ptr = alloc.alloc(); + if ptr.is_null() { + Err(Errno::OutOfMemory) + } else { + Ok(unsafe { Self::from_raw(alloc, ptr) }) + } + } + + pub fn new_indirect(alloc: A) -> Result { + let mut res = Self::new(alloc)?; + for it in res.as_mut_ref_array().iter_mut() { + it.write(BlockRef::null()); + } + Ok(res) + } + + pub const fn null() -> Self { + Self { + inner: None, + alloc: MaybeUninit::uninit(), + } + } + + pub unsafe fn from_raw(alloc: A, data: *mut u8) -> Self { + Self { + inner: Some(&mut *(data as *mut _)), + alloc: MaybeUninit::new(alloc), + } + } + + pub const fn is_null(&self) -> bool { + self.inner.is_none() + } + + pub fn as_mut_ref_array(&mut self) -> &mut [MaybeUninit>; ENTRY_COUNT] { + assert_eq!(size_of::(), 8); + unsafe { &mut *(self.deref_mut() as *mut _ as *mut _) } + } + + pub fn as_ref_array(&self) -> &[MaybeUninit>; ENTRY_COUNT] { + assert_eq!(size_of::(), 8); + unsafe { &*(self.deref() as *const _ as *const _) } + } +} + +impl<'a, A: BlockAllocator + Copy> Drop for BlockRef<'a, A> { + fn drop(&mut self) { + if let Some(inner) = self.inner.take() { + unsafe { + self.alloc + .assume_init_ref() + .dealloc(inner as *mut _ as *mut _); + } + } + } +} + +impl<'a, A: BlockAllocator + Copy> Deref for BlockRef<'a, A> { + type Target = [u8; SIZE]; + + fn deref(&self) -> &Self::Target { + self.inner.as_ref().unwrap() + } +} + +impl<'a, A: BlockAllocator + Copy> DerefMut for BlockRef<'a, A> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.inner.as_mut().unwrap() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::boxed::Box; + use std::sync::atomic::{AtomicUsize, Ordering}; + static A_COUNTER: AtomicUsize = AtomicUsize::new(0); + #[test] + fn block_allocator() { + #[derive(Clone, Copy)] + struct A; + unsafe impl BlockAllocator for A { + fn alloc(&self) -> *mut u8 { + let b = Box::leak(Box::new([0; SIZE])); + A_COUNTER.fetch_add(1, Ordering::SeqCst); + b.as_mut_ptr() as *mut _ + } + unsafe fn dealloc(&self, ptr: *mut u8) { + A_COUNTER.fetch_sub(1, Ordering::SeqCst); + drop(Box::from_raw(ptr as *mut [u8; SIZE])); + } + } + const N: usize = 13; + { + let mut s: [MaybeUninit>; N] = MaybeUninit::uninit_array(); + assert_eq!(A_COUNTER.load(Ordering::Acquire), 0); + for i in 0..N { + let mut block = BlockRef::new(A {}).unwrap(); + block.fill(1); + s[i].write(block); + } + assert_eq!(A_COUNTER.load(Ordering::Acquire), N); + for i in 0..N { + unsafe { + s[i].assume_init_drop(); + } + } + assert_eq!(A_COUNTER.load(Ordering::Acquire), 0); + } + } +} diff --git a/fs/memfs/src/bvec.rs b/fs/memfs/src/bvec.rs new file mode 100644 index 0000000..9354953 --- /dev/null +++ b/fs/memfs/src/bvec.rs @@ -0,0 +1,611 @@ +use crate::{block, BlockAllocator, BlockRef}; +use core::cmp::{max, min}; +use core::marker::PhantomData; +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, + l0: [MaybeUninit>; L0_BLOCKS], + l1: [MaybeUninit>; L1_BLOCKS], + l2: MaybeUninit>, + alloc: A, +} +impl<'a, A: BlockAllocator + Copy> Bvec<'a, A> { + pub fn new(alloc: A) -> Self { + let mut res = Self { + capacity: 0, + size: 0, + l0: MaybeUninit::uninit_array(), + l1: MaybeUninit::uninit_array(), + l2: MaybeUninit::uninit(), + alloc, + }; + for it in res.l0.iter_mut() { + it.write(BlockRef::null()); + } + for it in res.l1.iter_mut() { + it.write(BlockRef::null()); + } + res.l2.write(BlockRef::null()); + res + } + pub fn resize(&mut self, cap: usize) -> Result<(), Errno> { + if cap <= self.capacity { + let mut curr = self.capacity; + while curr != cap { + curr -= 1; + let mut index = curr; + if index >= L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT { + index -= L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT; + let l1i = index / block::ENTRY_COUNT; + let l0i = index % block::ENTRY_COUNT; + let l2r = unsafe { self.l2.assume_init_mut() }; + assert!(!l2r.is_null()); + let l1r = unsafe { l2r.as_mut_ref_array()[l1i].assume_init_mut() }; + assert!(!l1r.is_null()); + let l0r = unsafe { l1r.as_mut_ref_array()[l0i].assume_init_mut() }; + assert!(!l0r.is_null()); + *l0r = BlockRef::null(); + if l0i == 0 { + *l1r = BlockRef::null(); + } + if index == 0 { + *l2r = BlockRef::null(); + } + continue; + } + if index >= L0_BLOCKS { + index -= L0_BLOCKS; + let l1i = index / block::ENTRY_COUNT; + let l0i = index % block::ENTRY_COUNT; + let l1r = unsafe { self.l1[l1i].assume_init_mut() }; + assert!(!l1r.is_null()); + let l0r = unsafe { l1r.as_mut_ref_array()[l0i].assume_init_mut() }; + assert!(!l0r.is_null()); + *l0r = BlockRef::null(); + if l0i == 0 { + *l1r = BlockRef::null(); + } + continue; + } + let l0r = unsafe { self.l0[index].assume_init_mut() }; + assert!(!l0r.is_null()); + *l0r = BlockRef::null(); + continue; + unimplemented!(); + } + } else { + for mut index in self.capacity..cap { + if index < L0_BLOCKS { + let l0r = unsafe { self.l0[index].assume_init_mut() }; + assert!(l0r.is_null()); + *l0r = BlockRef::new(self.alloc)?; + continue; + } + index -= L0_BLOCKS; + if index < L1_BLOCKS * block::ENTRY_COUNT { + let l1i = index / block::ENTRY_COUNT; + let l0i = index % block::ENTRY_COUNT; + let l1r = unsafe { self.l1[l1i].assume_init_mut() }; + if l1r.is_null() { + *l1r = BlockRef::new_indirect(self.alloc)?; + } + let l0r = unsafe { l1r.as_mut_ref_array()[l0i].assume_init_mut() }; + assert!(l0r.is_null()); + *l0r = BlockRef::new(self.alloc)?; + continue; + } + index -= L1_BLOCKS * block::ENTRY_COUNT; + if index < block::ENTRY_COUNT * block::ENTRY_COUNT { + let l1i = index / block::ENTRY_COUNT; + let l0i = index % block::ENTRY_COUNT; + let l2r = unsafe { self.l2.assume_init_mut() }; + if l2r.is_null() { + *l2r = BlockRef::new_indirect(self.alloc)?; + } + let l1r = unsafe { l2r.as_mut_ref_array()[l1i].assume_init_mut() }; + if l1r.is_null() { + *l1r = BlockRef::new_indirect(self.alloc)?; + } + let l0r = unsafe { l1r.as_mut_ref_array()[l0i].assume_init_mut() }; + assert!(l0r.is_null()); + *l0r = BlockRef::new(self.alloc)?; + continue; + } + unimplemented!(); + } + } + self.capacity = cap; + Ok(()) + } + pub fn write(&mut self, mut pos: usize, data: &[u8]) -> Result { + if pos > self.size { + return Err(Errno::InvalidArgument); + } + let mut rem = data.len(); + let mut doff = 0usize; + if pos + rem > self.size { + self.size = pos + rem; + self.resize((pos + rem + block::SIZE - 1) / block::SIZE)?; + } + while rem > 0 { + let index = pos / block::SIZE; + let off = pos % block::SIZE; + let count = min(block::SIZE - off, rem); + let block = &mut self[index]; + let dst = &mut block[off..off + count]; + let src = &data[doff..doff + count]; + dst.copy_from_slice(src); + doff += count; + pos += count; + rem -= count; + } + Ok(doff) + } + pub fn read(&self, mut pos: usize, data: &mut [u8]) -> Result { + if pos > self.size { + return Err(Errno::InvalidArgument); + } + let mut rem = min(self.size - pos, data.len()); + let mut doff = 0usize; + while rem > 0 { + let index = pos / block::SIZE; + let off = pos % block::SIZE; + let count = min(block::SIZE - off, rem); + let block = &self[index]; + let src = &block[off..off + count]; + let dst = &mut data[doff..doff + count]; + dst.copy_from_slice(src); + doff += count; + pos += count; + rem -= count; + } + Ok(doff) + } +} +impl<'a, A: BlockAllocator + Copy> Index for Bvec<'a, A> { + type Output = BlockRef<'a, A>; + fn index(&self, mut index: usize) -> &Self::Output { + if index >= self.capacity { + panic!( + "Index exceeds bvec capacity ({} >= {})", + index, self.capacity + ); + } + if index < L0_BLOCKS { + return unsafe { self.l0[index].assume_init_ref() }; + } + index -= L0_BLOCKS; + if index < L1_BLOCKS * block::ENTRY_COUNT { + return unsafe { + let l1 = self.l1[index / block::ENTRY_COUNT].assume_init_ref(); + l1.as_ref_array()[index % block::ENTRY_COUNT].assume_init_ref() + }; + } + index -= L1_BLOCKS * block::ENTRY_COUNT; + if index < block::ENTRY_COUNT * block::ENTRY_COUNT { + return unsafe { + let l2 = self.l2.assume_init_ref(); + let l1 = l2.as_ref_array()[index / block::ENTRY_COUNT].assume_init_ref(); + l1.as_ref_array()[index % block::ENTRY_COUNT].assume_init_ref() + }; + } + unimplemented!(); + } +} +impl<'a, A: BlockAllocator + Copy> IndexMut for Bvec<'a, A> { + fn index_mut(&mut self, mut index: usize) -> &mut Self::Output { + if index >= self.capacity { + panic!( + "Index exceeds bvec capacity ({} >= {})", + index, self.capacity + ); + } + if index < L0_BLOCKS { + return unsafe { self.l0[index].assume_init_mut() }; + } + index -= L0_BLOCKS; + if index < L1_BLOCKS * block::ENTRY_COUNT { + return unsafe { + let l1 = self.l1[index / block::ENTRY_COUNT].assume_init_mut(); + l1.as_mut_ref_array()[index % block::ENTRY_COUNT].assume_init_mut() + }; + } + index -= L1_BLOCKS * block::ENTRY_COUNT; + if index < block::ENTRY_COUNT * block::ENTRY_COUNT { + return unsafe { + let l2 = self.l2.assume_init_mut(); + let l1 = l2.as_mut_ref_array()[index / block::ENTRY_COUNT].assume_init_mut(); + l1.as_mut_ref_array()[index % block::ENTRY_COUNT].assume_init_mut() + }; + } + unimplemented!(); + } +} +impl<'a, A: BlockAllocator + Copy> Drop for Bvec<'a, A> { + fn drop(&mut self) { + for i in 0..min(L0_BLOCKS, self.capacity) { + unsafe { + self.l0[i].assume_init_drop(); + } + } + if self.capacity > L0_BLOCKS {} + } +} + +#[cfg(feature = "test_bvec")] +#[cfg(test)] +mod tests { + use super::*; + use std::boxed::Box; + use std::mem::MaybeUninit; + use std::sync::atomic::{AtomicUsize, Ordering}; + static A_COUNTER: AtomicUsize = AtomicUsize::new(0); + #[derive(Clone, Copy)] + struct TestAlloc; + unsafe impl BlockAllocator for TestAlloc { + fn alloc(&self) -> *mut u8 { + let b = Box::leak(Box::new([0; block::SIZE])); + eprintln!("alloc {:p}", b); + b.as_mut_ptr() as *mut _ + } + unsafe fn dealloc(&self, ptr: *mut u8) { + eprintln!("drop {:p}", ptr); + drop(Box::from_raw(ptr as *mut [u8; block::SIZE])); + } + } + #[test] + fn bvec_allocation() { + #[derive(Clone, Copy)] + struct A; + unsafe impl BlockAllocator for A { + fn alloc(&self) -> *mut u8 { + let b = Box::leak(Box::new([0; block::SIZE])); + A_COUNTER.fetch_add(1, Ordering::SeqCst); + b.as_mut_ptr() as *mut _ + } + unsafe fn dealloc(&self, ptr: *mut u8) { + A_COUNTER.fetch_sub(1, Ordering::SeqCst); + drop(Box::from_raw(ptr as *mut [u8; block::SIZE])); + } + } + let mut bvec = Bvec::new(A {}); + assert_eq!(A_COUNTER.load(Ordering::Acquire), 0); + bvec.resize(123).unwrap(); + unsafe { + for i in 0..L0_BLOCKS { + assert!(!bvec.l0[i].assume_init_ref().is_null()); + } + let l1r = bvec.l1[0].assume_init_ref(); + assert!(!l1r.is_null()); + for i in 0..123 - L0_BLOCKS { + assert!(!l1r.as_ref_array()[i].assume_init_ref().is_null()); + } + } + assert_eq!(A_COUNTER.load(Ordering::Acquire), 123 + 1); + bvec.resize(123 + block::ENTRY_COUNT).unwrap(); + unsafe { + for i in 0..L0_BLOCKS { + assert!(!bvec.l0[i].assume_init_ref().is_null()); + } + for i in 0..(123 + block::ENTRY_COUNT) - L0_BLOCKS { + let l1i = i / block::ENTRY_COUNT; + let l0i = i % block::ENTRY_COUNT; + let l1r = bvec.l1[l1i].assume_init_ref(); + assert!(!l1r.is_null()); + assert!(!l1r.as_ref_array()[l0i].assume_init_ref().is_null()); + } + } + assert_eq!( + A_COUNTER.load(Ordering::Acquire), + 123 + block::ENTRY_COUNT + 2 + ); + bvec.resize(L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT) + .unwrap(); + unsafe { + for i in 0..L0_BLOCKS { + assert!(!bvec.l0[i].assume_init_ref().is_null()); + } + for i in 0..L1_BLOCKS * block::ENTRY_COUNT { + let l1i = i / block::ENTRY_COUNT; + let l0i = i % block::ENTRY_COUNT; + let l1r = bvec.l1[l1i].assume_init_ref(); + assert!(!l1r.is_null()); + assert!(!l1r.as_ref_array()[l0i].assume_init_ref().is_null()); + } + } + assert_eq!( + A_COUNTER.load(Ordering::Acquire), + L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + L1_BLOCKS + ); + bvec.resize(L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + block::ENTRY_COUNT * 4) + .unwrap(); + unsafe { + for i in 0..L0_BLOCKS { + assert!(!bvec.l0[i].assume_init_ref().is_null()); + } + for i in 0..L1_BLOCKS * block::ENTRY_COUNT { + let l1i = i / block::ENTRY_COUNT; + let l0i = i % block::ENTRY_COUNT; + let l1r = bvec.l1[l1i].assume_init_ref(); + assert!(!l1r.is_null()); + assert!(!l1r.as_ref_array()[l0i].assume_init_ref().is_null()); + } + let l2r = bvec.l2.assume_init_ref(); + assert!(!l2r.is_null()); + for i in 0..block::ENTRY_COUNT * 4 { + let l1i = i / block::ENTRY_COUNT; + let l0i = i % block::ENTRY_COUNT; + let l1r = l2r.as_ref_array()[l1i].assume_init_ref(); + assert!(!l1r.is_null()); + assert!(!l1r.as_ref_array()[l0i].assume_init_ref().is_null()); + } + } + assert_eq!( + A_COUNTER.load(Ordering::Acquire), + L0_BLOCKS + // L0 + L1_BLOCKS * block::ENTRY_COUNT + L1_BLOCKS + // L1 + block::ENTRY_COUNT * 4 + 4 + 1 + ); + bvec.resize(L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + block::ENTRY_COUNT * 3 + 1) + .unwrap(); + unsafe { + for i in 0..L0_BLOCKS { + assert!(!bvec.l0[i].assume_init_ref().is_null()); + } + for i in 0..L1_BLOCKS * block::ENTRY_COUNT { + let l1i = i / block::ENTRY_COUNT; + let l0i = i % block::ENTRY_COUNT; + let l1r = bvec.l1[l1i].assume_init_ref(); + assert!(!l1r.is_null()); + assert!(!l1r.as_ref_array()[l0i].assume_init_ref().is_null()); + } + let l2r = bvec.l2.assume_init_ref(); + assert!(!l2r.is_null()); + for i in 0..block::ENTRY_COUNT * 3 + 1 { + let l1i = i / block::ENTRY_COUNT; + let l0i = i % block::ENTRY_COUNT; + let l1r = l2r.as_ref_array()[l1i].assume_init_ref(); + assert!(!l1r.is_null()); + assert!(!l1r.as_ref_array()[l0i].assume_init_ref().is_null()); + } + } + assert_eq!( + A_COUNTER.load(Ordering::Acquire), + L0_BLOCKS + // L0 + L1_BLOCKS * block::ENTRY_COUNT + L1_BLOCKS + // L1 + block::ENTRY_COUNT * 3 + 1 + 4 + 1 + ); + bvec.resize(L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + block::ENTRY_COUNT * 2 + 1) + .unwrap(); + unsafe { + for i in 0..L0_BLOCKS { + assert!(!bvec.l0[i].assume_init_ref().is_null()); + } + for i in 0..L1_BLOCKS * block::ENTRY_COUNT { + let l1i = i / block::ENTRY_COUNT; + let l0i = i % block::ENTRY_COUNT; + let l1r = bvec.l1[l1i].assume_init_ref(); + assert!(!l1r.is_null()); + assert!(!l1r.as_ref_array()[l0i].assume_init_ref().is_null()); + } + let l2r = bvec.l2.assume_init_ref(); + assert!(!l2r.is_null()); + for i in 0..block::ENTRY_COUNT * 2 + 1 { + let l1i = i / block::ENTRY_COUNT; + let l0i = i % block::ENTRY_COUNT; + let l1r = l2r.as_ref_array()[l1i].assume_init_ref(); + assert!(!l1r.is_null()); + assert!(!l1r.as_ref_array()[l0i].assume_init_ref().is_null()); + } + } + assert_eq!( + A_COUNTER.load(Ordering::Acquire), + L0_BLOCKS + // L0 + L1_BLOCKS * block::ENTRY_COUNT + L1_BLOCKS + // L1 + block::ENTRY_COUNT * 2 + 1 + 3 + 1 + ); + bvec.resize(L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + 1) + .unwrap(); + unsafe { + for i in 0..L0_BLOCKS { + assert!(!bvec.l0[i].assume_init_ref().is_null()); + } + for i in 0..L1_BLOCKS * block::ENTRY_COUNT { + let l1i = i / block::ENTRY_COUNT; + let l0i = i % block::ENTRY_COUNT; + let l1r = bvec.l1[l1i].assume_init_ref(); + assert!(!l1r.is_null()); + assert!(!l1r.as_ref_array()[l0i].assume_init_ref().is_null()); + } + let l2r = bvec.l2.assume_init_ref(); + assert!(!l2r.is_null()); + let l1r = l2r.as_ref_array()[0].assume_init_ref(); + assert!(!l1r.is_null()); + assert!(!l1r.as_ref_array()[0].assume_init_ref().is_null()); + } + assert_eq!( + A_COUNTER.load(Ordering::Acquire), + L0_BLOCKS + // L0 + L1_BLOCKS * block::ENTRY_COUNT + L1_BLOCKS + // L1 + 1 + 1 + 1 + ); + bvec.resize(L0_BLOCKS + 3 * block::ENTRY_COUNT + 1).unwrap(); + unsafe { + for i in 0..L0_BLOCKS { + assert!(!bvec.l0[i].assume_init_ref().is_null()); + } + for i in 0..3 * block::ENTRY_COUNT + 1 { + let l1i = i / block::ENTRY_COUNT; + let l0i = i % block::ENTRY_COUNT; + let l1r = bvec.l1[l1i].assume_init_ref(); + assert!(!l1r.is_null()); + assert!(!l1r.as_ref_array()[l0i].assume_init_ref().is_null()); + } + let l2r = bvec.l2.assume_init_ref(); + assert!(l2r.is_null()); + } + assert_eq!( + A_COUNTER.load(Ordering::Acquire), + L0_BLOCKS + // L0 + 3 * block::ENTRY_COUNT + 1 + 4 + ); + bvec.resize(L0_BLOCKS).unwrap(); + unsafe { + for i in 0..L0_BLOCKS { + assert!(!bvec.l0[i].assume_init_ref().is_null()); + } + assert!(bvec.l1[0].assume_init_ref().is_null()); + } + assert_eq!(A_COUNTER.load(Ordering::Acquire), L0_BLOCKS); + bvec.resize(12).unwrap(); + unsafe { + for i in 0..12 { + assert!(!bvec.l0[i].assume_init_ref().is_null()); + } + } + assert_eq!(A_COUNTER.load(Ordering::Acquire), 12); + bvec.resize(0).unwrap(); + unsafe { + for i in 0..L0_BLOCKS { + assert!(bvec.l0[i].assume_init_ref().is_null()); + } + } + assert_eq!(A_COUNTER.load(Ordering::Acquire), 0); + } + #[test] + fn bvec_index_l0() { + let mut bvec = Bvec::new(TestAlloc {}); + bvec.resize(L0_BLOCKS).unwrap(); + for i in 0..L0_BLOCKS { + let block = &bvec[i]; + assert_eq!(block as *const _, bvec.l0[i].as_ptr()); + } + } + #[test] + fn bvec_index_l1() { + let mut bvec = Bvec::new(TestAlloc {}); + bvec.resize(L0_BLOCKS + block::ENTRY_COUNT * 2 + 3).unwrap(); + for i in 0..block::ENTRY_COUNT * 2 + 3 { + let l1i = i / block::ENTRY_COUNT; + let l0i = i % block::ENTRY_COUNT; + let block = &bvec[i + L0_BLOCKS]; + let l1r = unsafe { bvec.l1[l1i].assume_init_ref() }; + assert_eq!(block as *const _, l1r.as_ref_array()[l0i].as_ptr()); + } + } + #[test] + fn bvec_index_l2() { + let mut bvec = Bvec::new(TestAlloc {}); + bvec.resize(L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + 3) + .unwrap(); + for i in 0..3 { + let l1i = i / block::ENTRY_COUNT; + let l0i = i % block::ENTRY_COUNT; + let block = &bvec[i + L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT]; + let l2r = unsafe { bvec.l2.assume_init_ref() }; + let l1r = unsafe { l2r.as_ref_array()[l1i].assume_init_ref() }; + assert_eq!(block as *const _, l1r.as_ref_array()[l0i].as_ptr()); + } + } + #[test] + #[should_panic] + fn bvec_index_invalid_l0_0() { + let bvec = Bvec::new(TestAlloc {}); + let _block = &bvec[0]; + } + #[test] + #[should_panic] + fn bvec_index_invalid_l0_1() { + let mut bvec = Bvec::new(TestAlloc {}); + bvec.resize(13).unwrap(); + let _block = &bvec[15]; + } + #[test] + #[should_panic] + fn bvec_index_invalid_l1_0() { + let mut bvec = Bvec::new(TestAlloc {}); + bvec.resize(13).unwrap(); + let _block = &bvec[L0_BLOCKS + 2]; + } + #[test] + #[should_panic] + fn bvec_index_invalid_l1_1() { + let mut bvec = Bvec::new(TestAlloc {}); + bvec.resize(L0_BLOCKS + block::ENTRY_COUNT * 2 + 3).unwrap(); + let _block = &bvec[L0_BLOCKS + block::ENTRY_COUNT * 2 + 6]; + } + #[test] + #[should_panic] + fn bvec_index_invalid_l1_2() { + let mut bvec = Bvec::new(TestAlloc {}); + bvec.resize(L0_BLOCKS + block::ENTRY_COUNT * 2 + 3).unwrap(); + let _block = &bvec[L0_BLOCKS + block::ENTRY_COUNT * 3 + 1]; + } + #[test] + #[should_panic] + fn bvec_index_invalid_l2_0() { + let mut bvec = Bvec::new(TestAlloc {}); + bvec.resize(13).unwrap(); + let _block = &bvec[L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + 3]; + } + #[test] + #[should_panic] + fn bvec_index_invalid_l2_1() { + let mut bvec = Bvec::new(TestAlloc {}); + bvec.resize(L0_BLOCKS + block::ENTRY_COUNT * 3 + 13) + .unwrap(); + let _block = &bvec[L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + 3]; + } + #[test] + #[should_panic] + fn bvec_index_invalid_l2_2() { + let mut bvec = Bvec::new(TestAlloc {}); + bvec.resize(L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + 6) + .unwrap(); + let _block = &bvec[L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + 8]; + } + #[test] + #[should_panic] + fn bvec_index_invalid_l2_3() { + let mut bvec = Bvec::new(TestAlloc {}); + bvec.resize(L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + block::ENTRY_COUNT * 2 + 7) + .unwrap(); + let _block = + &bvec[L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + block::ENTRY_COUNT * 2 + 13]; + } + #[test] + #[should_panic] + fn bvec_index_invalid_l2_4() { + let mut bvec = Bvec::new(TestAlloc {}); + bvec.resize(L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + block::ENTRY_COUNT * 2 + 13) + .unwrap(); + let _block = &bvec[L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + block::ENTRY_COUNT * 3 + 2]; + } + #[test] + fn bvec_write_read() { + let mut bvec = Bvec::new(TestAlloc {}); + const N: usize = block::SIZE * (L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + 3); + let mut data = vec![0u8; N]; + for i in 0..N { + data[i] = (i & 0xFF) as u8; + } + assert_eq!(bvec.write(0, &data[..]), Ok(N)); + let mut buf = vec![0u8; 327]; + let mut off = 0usize; + let mut rem = N; + while rem != 0 { + let count = min(rem, buf.len()); + assert_eq!(bvec.read(off, &mut buf[..]), Ok(count)); + for i in 0..count { + assert_eq!(buf[i], ((i + off) & 0xFF) as u8); + } + rem -= count; + off += count; + } + } +} diff --git a/fs/memfs/src/lib.rs b/fs/memfs/src/lib.rs new file mode 100644 index 0000000..5529fb6 --- /dev/null +++ b/fs/memfs/src/lib.rs @@ -0,0 +1,237 @@ +#![feature( + const_fn_trait_bound, + const_mut_refs, + maybe_uninit_extra, + maybe_uninit_uninit_array +)] +#![no_std] + +extern crate alloc; +#[cfg(test)] +#[macro_use] +extern crate std; + +use alloc::{boxed::Box, rc::Rc}; +use core::cell::RefCell; +use error::Errno; +use vfs::{node::VnodeData, Filesystem, Vnode, VnodeImpl, VnodeKind, VnodeRef}; + +pub mod block; +pub use block::{BlockAllocator, BlockRef}; +pub mod bvec; +use bvec::Bvec; +pub mod tar; +use tar::{Tar, TarIterator}; + +pub struct Ramfs { + root: RefCell>, + alloc: A, +} + +pub struct FileInode<'a, A: BlockAllocator + Copy + 'static> { + data: Bvec<'a, A>, +} + +pub struct DirInode; + +impl<'a, A: BlockAllocator + Copy + 'static> VnodeImpl for FileInode<'a, A> { + fn create(&mut self, _parent: VnodeRef, _node: VnodeRef) -> Result<(), Errno> { + panic!() + } + + fn remove(&mut self, _parent: VnodeRef, _name: &str) -> Result<(), Errno> { + panic!() + } + + fn open(&mut self, node: VnodeRef) -> Result { + Ok(0) + } + + fn close(&mut self, node: VnodeRef) -> Result<(), Errno> { + Ok(()) + } + + fn read(&mut self, node: VnodeRef, pos: usize, data: &mut [u8]) -> Result { + self.data.read(pos, data) + } + + fn write(&mut self, node: VnodeRef, pos: usize, data: &[u8]) -> Result { + self.data.write(pos, data) + } + + fn truncate(&mut self, _node: VnodeRef, size: usize) -> Result<(), Errno> { + self.data.resize((size + 4095) / 4096) + } +} + +impl VnodeImpl for DirInode { + fn create(&mut self, _parent: VnodeRef, _node: VnodeRef) -> Result<(), Errno> { + Ok(()) + } + + fn remove(&mut self, _parent: VnodeRef, _name: &str) -> Result<(), Errno> { + Ok(()) + } + + fn open(&mut self, node: VnodeRef) -> Result { + todo!() + } + + fn close(&mut self, node: VnodeRef) -> Result<(), Errno> { + todo!() + } + + fn read(&mut self, node: VnodeRef, pos: usize, data: &mut [u8]) -> Result { + todo!() + } + + fn write(&mut self, node: VnodeRef, pos: usize, data: &[u8]) -> Result { + todo!() + } + + fn truncate(&mut self, _node: VnodeRef, _size: usize) -> Result<(), Errno> { + todo!() + } +} + +impl Filesystem for Ramfs { + fn root(self: Rc) -> Result { + self.root.borrow().clone().ok_or(Errno::DoesNotExist) + } + + fn create_node(self: Rc, name: &str, kind: VnodeKind) -> Result { + let mut node = Vnode::new(name, kind); + let data: Box = match kind { + VnodeKind::Regular => Box::new(FileInode { + data: Bvec::new(self.alloc), + }), + VnodeKind::Directory => Box::new(DirInode {}), + }; + node.set_data(VnodeData { + fs: self, + node: data, + }); + Ok(node) + } +} + +impl Ramfs { + pub fn open(base: *const u8, size: usize, alloc: A) -> Result, Errno> { + let mut res = Rc::new(Self { + root: RefCell::new(None), + alloc, + }); + unsafe { + *res.root.borrow_mut() = Some(res.clone().load_tar(base, size)?); + } + Ok(res) + } + + fn make_path( + self: Rc, + at: VnodeRef, + path: &str, + kind: VnodeKind, + do_create: bool, + ) -> Result { + let (element, rest) = vfs::util::path_component_left(path); + assert!(!element.is_empty()); + + let node_kind = if rest.is_empty() { + kind + } else { + VnodeKind::Directory + }; + + let node = at.lookup(element); + let node = match node { + Some(node) => node, + None => { + if !do_create { + return Err(Errno::DoesNotExist); + } + let node = self.clone().create_node(element, node_kind)?; + at.attach(node.clone()); + node + } + }; + + if rest.is_empty() { + Ok(node) + } else { + self.make_path(node, rest, kind, do_create) + } + } + + unsafe fn load_tar(self: Rc, base: *const u8, size: usize) -> Result { + let root = self.clone().create_node("", VnodeKind::Directory)?; + + // 1. Create all the paths in TAR + for block in TarIterator::new(base, base.add(size)) { + let node = + self.clone() + .make_path(root.clone(), block.path()?, block.node_kind(), true)?; + assert_eq!(node.kind(), block.node_kind()); + } + + // 2. Setup data blocks + for block in TarIterator::new(base, base.add(size)) { + if block.is_file() { + let size = block.size(); + let node = self.clone().make_path( + root.clone(), + block.path()?, + block.node_kind(), + false, + )?; + + node.truncate(size).unwrap(); + let res = node.write(0, block.data()).unwrap(); + if res != size { + panic!("Expected to write {}B, got {}B", size, res); + } + } + } + + Ok(root) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use alloc::boxed::Box; + use vfs::Ioctx; + + #[test] + fn ramfs_open() { + #[derive(Clone, Copy)] + struct A; + unsafe impl BlockAllocator for A { + 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])); + } + } + unsafe impl Sync for A {} + + let data = include_str!("../test/test1.tar"); + let fs = Ramfs::open(data.as_ptr(), data.bytes().len(), A {}).unwrap(); + + let root = fs.root().unwrap(); + let ioctx = Ioctx::new(root.clone()); + + assert!(Rc::ptr_eq(&ioctx.find(None, "/").unwrap(), &root)); + + let node = ioctx.find(None, "/test1.txt").unwrap(); + let mut file = node.open().unwrap(); + let mut buf = [0u8; 1024]; + + assert_eq!(file.read(&mut buf).unwrap(), 20); + let s = core::str::from_utf8(&buf[..20]).unwrap(); + assert_eq!(s, "This is a test file\n"); + } +} diff --git a/fs/memfs/src/tar.rs b/fs/memfs/src/tar.rs new file mode 100644 index 0000000..79419b2 --- /dev/null +++ b/fs/memfs/src/tar.rs @@ -0,0 +1,112 @@ +use vfs::VnodeKind; +use error::Errno; + +#[repr(packed)] +#[allow(dead_code)] +pub struct Tar { + name: [u8; 100], + mode: [u8; 8], + uid: [u8; 8], + gid: [u8; 8], + size: [u8; 12], + mtime: [u8; 12], + checksum: [u8; 8], + type_: u8, + link_name: [u8; 100], + magic: [u8; 8], + user: [u8; 32], + group: [u8; 32], + dev_major: [u8; 8], + dev_minor: [u8; 8], + prefix: [u8; 155], +} + +pub struct TarIterator { + address: *const u8, + limit: *const u8, + zero_blocks: usize, +} + +impl TarIterator { + pub const fn new( + address: *const u8, + limit: *const u8, + ) -> Self { + Self { + address, + limit, + zero_blocks: 0, + } + } +} + +impl Iterator for TarIterator { + type Item = &'static Tar; + + fn next(&mut self) -> Option { + if self.address >= self.limit || self.zero_blocks == 2 { + return None; + } + + let bytes: &[u8; 512] = unsafe { (self.address as *const [u8; 512]).as_ref() }.unwrap(); + if bytes.iter().all(|&x| x == 0) { + self.zero_blocks += 1; + self.address = unsafe { self.address.add(512) }; + self.next() + } else { + let block: &Tar = unsafe { (self.address as *const Tar).as_ref() }.unwrap(); + self.zero_blocks = 0; + self.address = unsafe { self.address.add(512 + align_up(block.size())) }; + Some(block) + } + } +} + +impl Tar { + pub fn is_file(&self) -> bool { + self.type_ == 0 || self.type_ == b'0' + } + + pub fn size(&self) -> usize { + from_octal(&self.size) + } + + pub fn path(&self) -> Result<&str, Errno> { + let zero_index = self.name.iter().position(|&c| c == 0).unwrap(); + core::str::from_utf8(&self.name[..zero_index]).map_err(|_| Errno::InvalidArgument) + } + + pub fn node_kind(&self) -> VnodeKind { + match self.type_ { + 0 | b'0' => VnodeKind::Regular, + b'5' => VnodeKind::Directory, + p => panic!("Unrecognized tar entry type: '{}'", p as char), + } + } + + pub fn data(&self) -> &[u8] { + unsafe { + core::slice::from_raw_parts( + ((self as *const _ as usize) + 512) as *const _, + self.size(), + ) + } + } +} + +fn from_octal(oct: &[u8]) -> usize { + let mut res = 0usize; + for &byte in oct { + if byte == 0 { + break; + } + + res <<= 3; + res |= (byte - b'0') as usize; + } + res +} + +const fn align_up(size: usize) -> usize { + (size + 511) & !511 +} diff --git a/fs/memfs/test/test1.tar b/fs/memfs/test/test1.tar new file mode 100644 index 0000000..b2f0f32 Binary files /dev/null and b/fs/memfs/test/test1.tar differ diff --git a/fs/memfs/test/test1.txt b/fs/memfs/test/test1.txt new file mode 100644 index 0000000..9f4b6d8 --- /dev/null +++ b/fs/memfs/test/test1.txt @@ -0,0 +1 @@ +This is a test file diff --git a/fs/vfs/src/file.rs b/fs/vfs/src/file.rs index 4388275..1fb1374 100644 --- a/fs/vfs/src/file.rs +++ b/fs/vfs/src/file.rs @@ -76,6 +76,10 @@ mod tests { fn write(&mut self, _node: VnodeRef, _pos: usize, _data: &[u8]) -> Result { Err(Errno::NotImplemented) } + + fn truncate(&mut self, _node: VnodeRef, _size: usize) -> Result<(), Errno> { + Err(Errno::NotImplemented) + } } impl Filesystem for DummyFs { diff --git a/fs/vfs/src/ioctx.rs b/fs/vfs/src/ioctx.rs index eca7810..672d549 100644 --- a/fs/vfs/src/ioctx.rs +++ b/fs/vfs/src/ioctx.rs @@ -102,6 +102,10 @@ mod tests { fn write(&mut self, _node: VnodeRef, _pos: usize, _data: &[u8]) -> Result { Err(Errno::NotImplemented) } + + fn truncate(&mut self, _node: VnodeRef, _size: usize) -> Result<(), Errno> { + Err(Errno::NotImplemented) + } } impl Filesystem for DummyFs { diff --git a/fs/vfs/src/node.rs b/fs/vfs/src/node.rs index bb886d8..70d4c69 100644 --- a/fs/vfs/src/node.rs +++ b/fs/vfs/src/node.rs @@ -45,6 +45,7 @@ pub trait VnodeImpl { fn open(&mut self, node: VnodeRef /* TODO open mode */) -> Result; fn close(&mut self, node: VnodeRef) -> Result<(), Errno>; + fn truncate(&mut self, node: VnodeRef, size: usize) -> Result<(), Errno>; fn read(&mut self, node: VnodeRef, pos: usize, data: &mut [u8]) -> Result; fn write(&mut self, node: VnodeRef, pos: usize, data: &[u8]) -> Result; } @@ -77,6 +78,11 @@ impl Vnode { self.kind == VnodeKind::Directory } + #[inline(always)] + pub const fn kind(&self) -> VnodeKind { + self.kind + } + // Tree operations pub fn attach(self: &VnodeRef, child: VnodeRef) { @@ -188,6 +194,30 @@ impl Vnode { Err(Errno::NotImplemented) } } + + pub fn write(self: &VnodeRef, pos: usize, buf: &[u8]) -> Result { + if self.kind != VnodeKind::Regular { + return Err(Errno::IsADirectory); + } + + if let Some(ref mut data) = *self.data.borrow_mut() { + data.node.write(self.clone(), pos, buf) + } else { + Err(Errno::NotImplemented) + } + } + + pub fn truncate(self: &VnodeRef, size: usize) -> Result<(), Errno> { + if self.kind != VnodeKind::Regular { + return Err(Errno::IsADirectory); + } + + if let Some(ref mut data) = *self.data.borrow_mut() { + data.node.truncate(self.clone(), size) + } else { + Err(Errno::NotImplemented) + } + } } impl fmt::Debug for Vnode { @@ -228,6 +258,10 @@ mod tests { fn write(&mut self, _node: VnodeRef, _pos: usize, _data: &[u8]) -> Result { Err(Errno::NotImplemented) } + + fn truncate(&mut self, _node: VnodeRef, _size: usize) -> Result<(), Errno> { + Err(Errno::NotImplemented) + } } impl Filesystem for DummyFs { diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 8204965..b506e17 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -11,6 +11,8 @@ test = false [dependencies] error = { path = "../error" } +vfs = { path = "../fs/vfs" } +memfs = { path = "../fs/memfs" } cfg-if = "1.x.x" tock-registers = "0.7.x" fdt-rs = { version = "0.x.x", default-features = false } diff --git a/kernel/src/fs/mod.rs b/kernel/src/fs/mod.rs new file mode 100644 index 0000000..0094ea8 --- /dev/null +++ b/kernel/src/fs/mod.rs @@ -0,0 +1,21 @@ +#![allow(missing_docs)] + +use crate::mem::{self, phys::{self, PageUsage}}; +use memfs::BlockAllocator; + +#[derive(Clone, Copy)] +pub struct MemfsBlockAlloc; + +unsafe impl BlockAllocator for MemfsBlockAlloc { + fn alloc(&self) -> *mut u8 { + if let Ok(page) = phys::alloc_page(PageUsage::Kernel) { + mem::virtualize(page) as *mut u8 + } else { + core::ptr::null_mut() + } + } + + unsafe fn dealloc(&self, data: *mut u8) { + todo!() + } +} diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 853d519..8610548 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -10,7 +10,7 @@ const_panic, panic_info_message, alloc_error_handler, - const_btree_new, + const_btree_new )] #![no_std] #![no_main] @@ -27,12 +27,13 @@ pub mod debug; pub mod arch; pub mod dev; +pub mod fs; pub mod mem; pub mod proc; pub mod sync; -pub mod util; #[allow(missing_docs)] pub mod syscall; +pub mod util; #[panic_handler] fn panic_handler(pi: &core::panic::PanicInfo) -> ! { diff --git a/kernel/src/proc/mod.rs b/kernel/src/proc/mod.rs index 5962ca8..0399c49 100644 --- a/kernel/src/proc/mod.rs +++ b/kernel/src/proc/mod.rs @@ -57,15 +57,31 @@ pub unsafe fn enter(initrd: Option<(usize, usize)>) -> ! { if let Some((start, end)) = initrd { let initrd = Box::into_raw(Box::new((mem::virtualize(start), mem::virtualize(end)))); - for _ in 0..4 { - spawn!(fn (initrd_ptr: usize) { - debugln!("Running kernel init process"); + spawn!(fn (initrd_ptr: usize) { + use memfs::Ramfs; + use vfs::{Filesystem, Ioctx}; + use crate::fs::MemfsBlockAlloc; + debugln!("Running kernel init process"); - let (start, _end) = unsafe { *(initrd_ptr as *const (usize, usize)) }; - Process::execve(|space| elf::load_elf(space, start as *const u8), 0).unwrap(); - panic!("This code should not run"); - }, initrd as usize); - } + let (start, end) = unsafe { *(initrd_ptr as *const (usize, usize)) }; + let size = end - start; + + let fs = Ramfs::open(start as *mut u8, size, MemfsBlockAlloc {}).unwrap(); + let root = fs.root().unwrap(); + let ioctx = Ioctx::new(root); + + // Open a test file + let node = ioctx.find(None, "/test.txt").unwrap(); + let mut file = node.open().unwrap(); + let mut buf = [0u8; 16]; + + while let Ok(count) = file.read(&mut buf) { + if count == 0 { + break; + } + debugln!("Read {} bytes: {:?}", count, &buf[0..count]); + } + }, initrd as usize); } SCHED.enter(); }