diff --git a/src/dirty.rs b/src/dirty.rs new file mode 100644 index 0000000..667c9b7 --- /dev/null +++ b/src/dirty.rs @@ -0,0 +1,47 @@ +use core::ops::{Deref, DerefMut}; + +/// Dirty wraps a value of type T with functions similiar to that of a Read/Write +/// lock but simply sets a dirty flag on write(), reset on read() +pub struct Dirty { + value: T, + dirty: bool, +} + +impl Dirty { + /// Create a new Dirty + pub fn new(val: T) -> Dirty { + Dirty { + value: val, + dirty: false, + } + } + + /// Returns true if dirty, false otherwise + #[allow(dead_code)] + pub fn dirty(&self) -> bool { + self.dirty + } + + /// Reset dirty + pub fn sync(&mut self) { + self.dirty = false; + } +} + +impl Deref for Dirty { + type Target = T; + + /// Read the value + fn deref(&self) -> &T { + &self.value + } +} + +impl DerefMut for Dirty { + /// Writable value return, sets the dirty flag + fn deref_mut(&mut self) -> &mut T { + self.dirty = true; + &mut self.value + } +} + diff --git a/src/lib.rs b/src/lib.rs index 34b1939..c0735cf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,8 @@ extern crate spin; extern crate alloc; extern crate bit_set; +mod dirty; +mod vfs; mod sfs; mod structs; #[cfg(test)] diff --git a/src/sfs.rs b/src/sfs.rs index a8497da..af49cf1 100644 --- a/src/sfs.rs +++ b/src/sfs.rs @@ -1,6 +1,7 @@ use spin::Mutex; use bit_set::BitSet; -use alloc::{boxed::Box, Vec}; +use alloc::{boxed::Box, Vec, BTreeMap, rc::Rc}; +use dirty::Dirty; use super::structs::*; use core::mem::{uninitialized, size_of}; use core::slice; @@ -13,46 +14,32 @@ pub trait Device { } /// inode for sfs -pub struct Inode { +pub struct INode { /// on-disk inode - disk_inode: *mut DiskInode, + disk_inode: Dirty, /// inode number - id: u32, - /// true if inode modified - dirty: bool, - /// kill inode if it hits zero - reclaim_count: u32, - /// semaphore for din - mutex: Mutex<()>, + id: INodeId, } +type INodeId = usize; + /// filesystem for sfs pub struct SimpleFileSystem { /// on-disk superblock - super_block: SuperBlock, + super_block: Dirty, /// blocks in use are mared 0 free_map: BitSet, - /// true if super/freemap modified - super_dirty: bool, - /// buffer for non-block aligned io -// buffer: u8, - /// semaphore for fs -// fs_mutex: Mutex<()>, - /// semaphore for link/unlink and rename -// link_mutex: Mutex<()>, /// inode list -// inodes: Vec, + inodes: BTreeMap>, /// device - device: Mutex>, + device: Box, } impl SimpleFileSystem { /// Create a new SFS with device pub fn new(mut device: Box) -> Option { let mut super_block: SuperBlock = unsafe{ uninitialized() }; - let slice = unsafe{ slice::from_raw_parts_mut( - &mut super_block as *mut SuperBlock as *mut u8, size_of::()) }; - if device.read_at(0, slice).is_none() { + if device.read_at(0, super_block.as_buf_mut()).is_none() { return None; } if super_block.check() == false { @@ -60,10 +47,61 @@ impl SimpleFileSystem { } Some(SimpleFileSystem { - super_block, + super_block: Dirty::new(super_block), free_map: BitSet::new(), - super_dirty: false, - device: Mutex::new(device), + inodes: BTreeMap::>::new(), + device, }) } + /// Allocate a block, return block id + fn alloc_block(&mut self) -> Option { + let id = self.free_map.alloc(); + if id.is_some() { + self.super_block.unused_blocks -= 1; // will panic if underflow + } + id + } + /// Free a block + fn free_block(&mut self, block_id: usize) { + assert!(!self.free_map.contains(block_id)); + self.free_map.insert(block_id); + self.super_block.unused_blocks += 1; + } + /// Get inode by id + fn get_inode(&self, id: INodeId) -> Option> { + self.inodes.get(&id).map(|rc| rc.clone()) + } + /// Write back super block if dirty + fn sync(&mut self) { + let SimpleFileSystem { + ref mut super_block, + ref mut device, + .. + } = self; + + if super_block.dirty() { + device.write_at(0, super_block.as_buf()); + super_block.sync(); + } + } +} + +trait BitsetAlloc { + fn alloc(&mut self) -> Option; +} + +impl BitsetAlloc for BitSet { + fn alloc(&mut self) -> Option { + // TODO: more efficient + let id = (0 .. self.len()).find(|&i| self.contains(i)); + if let Some(id) = id { + self.remove(id); + } + id + } +} + +#[cfg(test)] +mod test { + use super::*; } \ No newline at end of file diff --git a/src/structs.rs b/src/structs.rs index 8f23d4c..7b4dca3 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -1,3 +1,6 @@ +use core::slice; +use core::mem::size_of_val; + /// On-disk superblock #[repr(C, packed)] pub struct SuperBlock { @@ -14,7 +17,7 @@ pub struct SuperBlock { /// inode (on disk) #[repr(C, packed)] #[derive(Debug)] -pub struct DiskInode { +pub struct DiskINode { /// size of the file (in bytes) pub size: u32, /// one of SYS_TYPE_* above @@ -35,7 +38,7 @@ pub struct DiskInode { #[repr(C, packed)] pub struct DiskEntry { /// inode number - pub inode_number: u32, + pub id: u32, /// file name pub name: [u8; MAX_FNAME_LEN + 1], } @@ -46,6 +49,19 @@ impl SuperBlock { } } +/// Convert structs to [u8] slice +pub trait AsBuf { + fn as_buf(&self) -> &[u8] { + unsafe{ slice::from_raw_parts(self as *const _ as *const u8, size_of_val(self)) } + } + fn as_buf_mut(&mut self) -> &mut [u8] { + unsafe{ slice::from_raw_parts_mut(self as *mut _ as *mut u8, size_of_val(self)) } + } +} + +impl AsBuf for SuperBlock {} +impl AsBuf for DiskINode {} + /* * Simple FS (SFS) definitions visible to ucore. This covers the on-disk format * and is used by tools that work on SFS volumes, such as mksfs. @@ -76,4 +92,17 @@ pub const BLK_NENTRY: usize = BLKSIZE / 4; /// file types pub enum FileType { Invalid = 0, File = 1, Dir = 2, Link = 3, +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn struct_size() { + use core::mem::size_of; + assert!(size_of::() <= BLKSIZE); + assert!(size_of::() <= BLKSIZE); + assert!(size_of::() <= BLKSIZE); + } } \ No newline at end of file diff --git a/src/vfs.rs b/src/vfs.rs new file mode 100644 index 0000000..fd5c96b --- /dev/null +++ b/src/vfs.rs @@ -0,0 +1,17 @@ +/// Abstract operations on a inode. +pub trait INodeOps { + fn open(&mut self, flags: u32) -> Result<(), ()>; + fn close(&mut self) -> Result<(), ()>; + fn read(&mut self, buf: &mut [u8]) -> Result<(), ()>; + fn write(&mut self, buf: &[u8]) -> Result<(), ()>; +// fn fstat(&mut self, buf: &[u8]) -> Result<(), ()>; +// fn fsync(&mut self) -> Result<(), ()>; +// fn name_file(&mut self) -> Result<(), ()>; +// fn reclaim(&mut self) -> Result<(), ()>; +// fn get_type(&mut self) -> Result; +// fn try_seek(&mut self, offset: u64) -> Result<(), ()>; +// fn truncate(&mut self, len: u64) -> Result<(), ()>; +// fn create(&mut self, name: &'static str, excl: bool) -> Result<(), ()>; +// fn loopup(&mut self, path: &'static str) -> Result<(), ()>; +// fn io_ctrl(&mut self, op: u32, data: &[u8]) -> Result<(), ()>; +} \ No newline at end of file