diff --git a/src/sfs.rs b/src/sfs.rs index af49cf1..0bdbf1b 100644 --- a/src/sfs.rs +++ b/src/sfs.rs @@ -1,16 +1,35 @@ use spin::Mutex; use bit_set::BitSet; -use alloc::{boxed::Box, Vec, BTreeMap, rc::Rc}; +use alloc::{boxed::Box, Vec, BTreeMap, rc::{Rc, Weak}}; +use core::cell::{RefCell, RefMut}; use dirty::Dirty; use super::structs::*; +use super::vfs; use core::mem::{uninitialized, size_of}; use core::slice; /// Interface for SFS to read & write /// TODO: use std::io::{Read, Write} pub trait Device { - fn read_at(&mut self, offset: u64, buf: &mut [u8]) -> Option; - fn write_at(&mut self, offset: u64, buf: &[u8]) -> Option; + fn read_at(&mut self, offset: usize, buf: &mut [u8]) -> Option; + fn write_at(&mut self, offset: usize, buf: &[u8]) -> Option; + + // Helper functions + + fn read_block(&mut self, id: BlockId, offset: usize, buf: &mut [u8]) -> Result<(),()> { + debug_assert!(offset + buf.len() <= BLKSIZE); + match self.read_at(id * BLKSIZE + offset, buf) { + Some(len) if len == buf.len() => Ok(()), + _ => Err(()), + } + } + fn write_block(&mut self, id: BlockId, offset: usize, buf: &[u8]) -> Result<(),()> { + debug_assert!(offset + buf.len() <= BLKSIZE); + match self.write_at(id * BLKSIZE + offset, buf) { + Some(len) if len == buf.len() => Ok(()), + _ => Err(()), + } + } } /// inode for sfs @@ -19,9 +38,121 @@ pub struct INode { disk_inode: Dirty, /// inode number id: INodeId, + /// Reference to SFS, used by almost all operations + fs: Rc>, +} + +impl INode { + /// Map file block id to disk block id + fn disk_block_id(&self, file_block_id: BlockId) -> Option { + match file_block_id { + id if id >= self.disk_inode.blocks as BlockId => + None, + id if id < NDIRECT => + Some(self.disk_inode.direct[id] as BlockId), + id if id < NDIRECT + BLK_NENTRY => { + let mut disk_block_id: BlockId = 0; + self.fs.borrow_mut().device.read_block( + self.disk_inode.indirect as usize, + ENTRY_SIZE * (id - NDIRECT), + disk_block_id.as_buf_mut() + ).unwrap(); + Some(disk_block_id as BlockId) + }, + id => unimplemented!("double indirect blocks is not supported"), + } + } +} + +impl vfs::INode for INode { + fn open(&mut self, flags: u32) -> Result<(), ()> { + // Do nothing + Ok(()) + } + fn close(&mut self) -> Result<(), ()> { + self.sync() + } + fn read_at(&mut self, offset: usize, buf: &mut [u8]) -> Option { + let mut fs = self.fs.borrow_mut(); + + let iter = BlockIter { + begin: offset, + end: offset + buf.len(), + }; + + // Read for each block + let mut buf_offset = 0usize; + for BlockRange{block, begin, end} in iter { + if let Some(disk_block_id) = self.disk_block_id(block) { + let len = end - begin; + fs.device.read_block(disk_block_id, begin, &mut buf[buf_offset .. buf_offset + len]); + buf_offset += len; + } else { + // Failed this time + break; + } + } + Some(buf_offset) + } + fn write_at(&mut self, offset: usize, buf: &[u8]) -> Option { + let mut fs = self.fs.borrow_mut(); + + let iter = BlockIter { + begin: offset, + end: offset + buf.len(), + }; + + // Read for each block + let mut buf_offset = 0usize; + for BlockRange{block, begin, end} in iter { + if let Some(disk_block_id) = self.disk_block_id(block) { + let len = end - begin; + fs.device.write_block(disk_block_id, begin, &buf[buf_offset .. buf_offset + len]); + buf_offset += len; + } else { + // Failed this time + break; + } + } + Some(buf_offset) + } + fn sync(&mut self) -> Result<(), ()> { + if self.disk_inode.dirty() { + let mut fs = self.fs.borrow_mut(); + fs.device.write_block(self.id, 0, self.disk_inode.as_buf())?; + self.disk_inode.sync(); + } + Ok(()) + } +} + +/// Given a range and iterate sub-range for each block +struct BlockIter { + begin: usize, + end: usize, +} + +struct BlockRange { + block: BlockId, + begin: usize, + end: usize, +} + +impl Iterator for BlockIter { + type Item = BlockRange; + + fn next(&mut self) -> Option<::Item> { + if self.begin >= self.end { + return None; + } + let block = self.begin / BLKSIZE; + let begin = self.begin % BLKSIZE; + let end = if block == self.end / BLKSIZE {self.end % BLKSIZE} else {BLKSIZE}; + self.begin += end - begin; + Some(BlockRange {block, begin, end}) + } } -type INodeId = usize; /// filesystem for sfs pub struct SimpleFileSystem { @@ -39,7 +170,7 @@ impl SimpleFileSystem { /// Create a new SFS with device pub fn new(mut device: Box) -> Option { let mut super_block: SuperBlock = unsafe{ uninitialized() }; - if device.read_at(0, super_block.as_buf_mut()).is_none() { + if device.read_at(BLKN_SUPER * BLKSIZE, super_block.as_buf_mut()).is_none() { return None; } if super_block.check() == false { @@ -86,6 +217,26 @@ impl SimpleFileSystem { } } +impl vfs::FileSystem for SimpleFileSystem { + type INode = INode; + + fn sync(&mut self) -> Result<(), ()> { + unimplemented!() + } + + fn root_inode(&mut self) -> Rc { + unimplemented!() + } + + fn unmount(&mut self) -> Result<(), ()> { + unimplemented!() + } + + fn cleanup(&mut self) { + unimplemented!() + } +} + trait BitsetAlloc { fn alloc(&mut self) -> Option; } diff --git a/src/structs.rs b/src/structs.rs index 1bd5927..f0fca53 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -12,7 +12,7 @@ pub struct SuperBlock { pub blocks: u32, /// number of unused blocks in fs pub unused_blocks: u32, - /// infomation for sfs + /// information for sfs pub info: [u8; MAX_INFO_LEN + 1], } @@ -23,7 +23,7 @@ pub struct DiskINode { /// size of the file (in bytes) pub size: u32, /// one of SYS_TYPE_* above - pub type_: u16, + pub type_: FileType, /// number of hard links to this file pub nlinks: u16, /// number of blocks @@ -36,6 +36,11 @@ pub struct DiskINode { pub db_indirect: u32, } +#[repr(C, packed)] +pub struct IndirectBlock { + pub entrys: [u32; BLK_NENTRY], +} + /// file entry (on disk) #[repr(C, packed)] pub struct DiskEntry { @@ -63,11 +68,15 @@ pub trait AsBuf { impl AsBuf for SuperBlock {} impl AsBuf for DiskINode {} +impl AsBuf for BlockId {} /* * 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. */ +pub type BlockId = usize; +pub type INodeId = BlockId; + /// magic number for sfs pub const MAGIC: u32 = 0x2f8dbe2a; /// size of block @@ -81,17 +90,21 @@ pub const MAX_FNAME_LEN: usize = 255; /// max file size (128M) pub const MAX_FILE_SIZE: usize = 1024 * 1024 * 128; /// block the superblock lives in -pub const BLKN_SUPER: usize = 0; +pub const BLKN_SUPER: BlockId = 0; /// location of the root dir inode -pub const BLKN_ROOT: usize = 1; +pub const BLKN_ROOT: BlockId = 1; /// 1st block of the freemap -pub const BLKN_FREEMAP: usize = 2; +pub const BLKN_FREEMAP: BlockId = 2; /// number of bits in a block pub const BLKBITS: usize = BLKSIZE * 8; +/// +pub const ENTRY_SIZE: usize = 4; /// number of entries in a block -pub const BLK_NENTRY: usize = BLKSIZE / 4; +pub const BLK_NENTRY: usize = BLKSIZE / ENTRY_SIZE; /// file types +#[repr(u16)] +#[derive(Debug)] pub enum FileType { Invalid = 0, File = 1, Dir = 2, Link = 3, } @@ -106,5 +119,6 @@ mod test { assert!(size_of::() <= BLKSIZE); assert!(size_of::() <= BLKSIZE); assert!(size_of::() <= BLKSIZE); + assert_eq!(size_of::(), BLKSIZE); } } \ No newline at end of file diff --git a/src/tests.rs b/src/tests.rs index 3d6a79d..c72078d 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -2,16 +2,19 @@ use std::fs::File; use std::io::{Read, Write, Seek, SeekFrom}; use std::boxed::Box; use super::sfs::*; +use super::vfs::*; impl Device for File { - fn read_at(&mut self, offset: u64, buf: &mut [u8]) -> Option { + fn read_at(&mut self, offset: usize, buf: &mut [u8]) -> Option { + let offset = offset as u64; match self.seek(SeekFrom::Start(offset)) { Ok(real_offset) if real_offset == offset => self.read(buf).ok(), _ => None, } } - fn write_at(&mut self, offset: u64, buf: &[u8]) -> Option { + fn write_at(&mut self, offset: usize, buf: &[u8]) -> Option { + let offset = offset as u64; match self.seek(SeekFrom::Start(offset)) { Ok(real_offset) if real_offset == offset => self.write(buf).ok(), _ => None, @@ -23,6 +26,7 @@ impl Device for File { fn test() { let file = File::open("sfs.img") .expect("failed to open sfs.img"); - let sfs = SimpleFileSystem::new(Box::new(file)) + let mut sfs = SimpleFileSystem::new(Box::new(file)) .expect("failed to create SFS"); +// let root = sfs.root_inode(); } \ No newline at end of file diff --git a/src/vfs.rs b/src/vfs.rs index fd5c96b..989f417 100644 --- a/src/vfs.rs +++ b/src/vfs.rs @@ -1,11 +1,13 @@ +use alloc::rc::{Rc, Weak}; + /// Abstract operations on a inode. -pub trait INodeOps { +pub trait INode { 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 read_at(&mut self, offset: usize, buf: &mut [u8]) -> Option; + fn write_at(&mut self, offset: usize, buf: &[u8]) -> Option; // fn fstat(&mut self, buf: &[u8]) -> Result<(), ()>; -// fn fsync(&mut self) -> Result<(), ()>; + fn sync(&mut self) -> Result<(), ()>; // fn name_file(&mut self) -> Result<(), ()>; // fn reclaim(&mut self) -> Result<(), ()>; // fn get_type(&mut self) -> Result; @@ -14,4 +16,13 @@ pub trait INodeOps { // 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<(), ()>; +} + +/// Abstract filesystem +pub trait FileSystem { + type INode: INode; + fn sync(&mut self) -> Result<(), ()>; + fn root_inode(&mut self) -> Rc; + fn unmount(&mut self) -> Result<(), ()>; + fn cleanup(&mut self); } \ No newline at end of file