diff --git a/.travis.yml b/.travis.yml index 613564f..0f33674 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1 +1,3 @@ -language: rust \ No newline at end of file +language: rust +rust: + - nightly \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index bb5692b..d76f4dd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ #![feature(alloc)] +#![feature(const_fn)] #![no_std] #[cfg(test)] diff --git a/src/sfs.rs b/src/sfs.rs index 14c2e9e..6029e8b 100644 --- a/src/sfs.rs +++ b/src/sfs.rs @@ -17,14 +17,14 @@ pub trait Device { // Helper functions - fn read_block(&mut self, id: BlockId, offset: usize, buf: &mut [u8]) -> Result<(), ()> { + fn read_block(&mut self, id: BlockId, offset: usize, buf: &mut [u8]) -> vfs::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<(), ()> { + fn write_block(&mut self, id: BlockId, offset: usize, buf: &[u8]) -> vfs::Result<()> { debug_assert!(offset + buf.len() <= BLKSIZE); match self.write_at(id * BLKSIZE + offset, buf) { Some(len) if len == buf.len() => Ok(()), @@ -33,13 +33,15 @@ pub trait Device { } } -/// Load struct `T` from given block in device -/// Workaround: It should be inside the trait `Device`. But that would cause compile error. -fn load_struct(device: &mut Device, id: BlockId) -> T { - let mut s: T = unsafe { uninitialized() }; - device.read_block(id, 0, s.as_buf_mut()).unwrap(); - s +trait DeviceExt: Device { + /// Load struct `T` from given block in device + fn load_struct(&mut self, id: BlockId) -> T { + let mut s: T = unsafe { uninitialized() }; + self.read_block(id, 0, s.as_buf_mut()).unwrap(); + s + } } +impl DeviceExt for Device {} type Ptr = Rc>; type WeakPtr = Weak>; @@ -81,7 +83,7 @@ impl INode { id => unimplemented!("double indirect blocks is not supported"), } } - fn set_disk_block_id(&mut self, file_block_id: BlockId, disk_block_id: BlockId) -> Result<(),()> { + fn set_disk_block_id(&mut self, file_block_id: BlockId, disk_block_id: BlockId) -> vfs::Result<()> { match file_block_id { id if id >= self.disk_inode.blocks as BlockId => Err(()), @@ -102,17 +104,56 @@ impl INode { id => unimplemented!("double indirect blocks is not supported"), } } + /// Only for Dir + fn get_file_inode_id(&mut self, name: &'static str) -> Option { + (0 .. self.disk_inode.blocks) + .map(|i| { + use vfs::INode; + let mut entry: DiskEntry = unsafe {uninitialized()}; + self.read_at(i as usize * BLKSIZE, entry.as_buf_mut()).unwrap(); + entry + }) + .find(|entry| entry.name.as_ref() == name) + .map(|entry| entry.id as INodeId) + } + /// Init dir content. Insert 2 init entries. + fn init_dir(&mut self, parent: INodeId) -> vfs::Result<()> { + use vfs::INode; + // Insert entries: '.' '..' + self.resize(BLKSIZE * 2).unwrap(); + self.write_at(0, DiskEntry { + id: parent as u32, + name: Str256::from(".."), + }.as_buf()).unwrap(); + let id = self.id as u32; + self.write_at(0, DiskEntry { + id, + name: Str256::from("."), + }.as_buf()).unwrap(); + Ok(()) + } + fn clean_at(&mut self, begin: usize, end: usize) -> vfs::Result<()> { + let fs = self.fs.upgrade().unwrap(); + + let iter = BlockIter { begin, end }; + for BlockRange { block, begin, end } in iter { + static ZEROS: [u8; BLKSIZE] = [0; BLKSIZE]; + let disk_block_id = self.get_disk_block_id(block).unwrap(); + fs.borrow_mut().device.write_block(disk_block_id, begin, &ZEROS[begin..end]).unwrap(); + } + Ok(()) + } } impl vfs::INode for INode { - fn open(&mut self, flags: u32) -> Result<(), ()> { + fn open(&mut self, flags: u32) -> vfs::Result<()> { // Do nothing Ok(()) } - fn close(&mut self) -> Result<(), ()> { + fn close(&mut self) -> vfs::Result<()> { self.sync() } - fn read_at(&mut self, offset: usize, buf: &mut [u8]) -> Option { + fn read_at(&mut self, offset: usize, buf: &mut [u8]) -> vfs::Result { let fs = self.fs.upgrade().unwrap(); let iter = BlockIter { @@ -123,18 +164,14 @@ impl vfs::INode for INode { // Read for each block let mut buf_offset = 0usize; for BlockRange { block, begin, end } in iter { - if let Some(disk_block_id) = self.get_disk_block_id(block) { - let len = end - begin; - fs.borrow_mut().device.read_block(disk_block_id, begin, &mut buf[buf_offset..buf_offset + len]).unwrap(); - buf_offset += len; - } else { - // Failed this time - break; - } + let disk_block_id = self.get_disk_block_id(block).unwrap(); + let len = end - begin; + fs.borrow_mut().device.read_block(disk_block_id, begin, &mut buf[buf_offset..buf_offset + len]).unwrap(); + buf_offset += len; } - Some(buf_offset) + Ok(buf_offset) } - fn write_at(&mut self, offset: usize, buf: &[u8]) -> Option { + fn write_at(&mut self, offset: usize, buf: &[u8]) -> vfs::Result { let fs = self.fs.upgrade().unwrap(); let iter = BlockIter { @@ -142,43 +179,33 @@ impl vfs::INode for INode { end: offset + buf.len(), }; - // Read for each block + // Write for each block let mut buf_offset = 0usize; for BlockRange { block, begin, end } in iter { - if let Some(disk_block_id) = self.get_disk_block_id(block) { - let len = end - begin; - fs.borrow_mut().device.write_block(disk_block_id, begin, &buf[buf_offset..buf_offset + len]).unwrap(); - buf_offset += len; - } else { - // Failed this time - break; - } + let disk_block_id = self.get_disk_block_id(block).unwrap(); + let len = end - begin; + fs.borrow_mut().device.write_block(disk_block_id, begin, &buf[buf_offset..buf_offset + len]).unwrap(); + buf_offset += len; } - Some(buf_offset) + Ok(buf_offset) } - fn info(&mut self) -> Result { - if self.disk_inode.type_ != FileType::File { - return Err(()); - } + fn info(&mut self) -> vfs::Result { Ok(vfs::FileInfo { size: self.disk_inode.size as usize, mode: 0, + type_: vfs::FileType::from(self.disk_inode.type_.clone()), }) } - fn sync(&mut self) -> Result<(), ()> { + fn sync(&mut self) -> vfs::Result<()> { if self.disk_inode.dirty() { - let fs0 = self.fs.upgrade().unwrap(); - let mut fs = fs0.borrow_mut(); - fs.device.write_block(self.id, 0, self.disk_inode.as_buf())?; + let fs = self.fs.upgrade().unwrap(); + fs.borrow_mut().device.write_block(self.id, 0, self.disk_inode.as_buf()).unwrap(); self.disk_inode.sync(); } Ok(()) } - fn type_(&self) -> Result { - Ok(self.disk_inode.type_.clone() as u32) - } - fn resize(&mut self, len: usize) -> Result<(), ()> { - if self.disk_inode.type_ != FileType::File || len > MAX_FILE_SIZE { + fn resize(&mut self, len: usize) -> vfs::Result<()> { + if len > MAX_FILE_SIZE { return Err(()); } let blocks = ((len + BLKSIZE - 1) / BLKSIZE) as u32; @@ -189,11 +216,18 @@ impl vfs::INode for INode { let fs = self.fs.upgrade().unwrap(); let old_blocks = self.disk_inode.blocks; self.disk_inode.blocks = blocks; + // allocate indirect block if need + if old_blocks < NDIRECT as u32 && blocks >= NDIRECT as u32 { + self.disk_inode.indirect = fs.borrow_mut().alloc_block().unwrap() as u32; + } // allocate extra blocks for i in old_blocks .. blocks { let disk_block_id = fs.borrow_mut().alloc_block().expect("no more space"); self.set_disk_block_id(i as usize, disk_block_id).unwrap(); } + // clean up + let old_size = self.disk_inode.size as usize; + self.clean_at(old_size, len).unwrap(); }, Ordering::Less => { let fs = self.fs.upgrade().unwrap(); @@ -202,12 +236,70 @@ impl vfs::INode for INode { let disk_block_id = self.get_disk_block_id(i as usize).unwrap(); fs.borrow_mut().free_block(disk_block_id); } + // free indirect block if need + if blocks < NDIRECT as u32 && self.disk_inode.blocks >= NDIRECT as u32 { + fs.borrow_mut().free_block(self.disk_inode.indirect as usize); + self.disk_inode.indirect = 0; + } self.disk_inode.blocks = blocks; }, } self.disk_inode.size = len as u32; Ok(()) } + fn create(&mut self, name: &'static str) -> vfs::Result> { + let fs = self.fs.upgrade().unwrap(); + let info = self.info().unwrap(); + assert_eq!(info.type_, vfs::FileType::Dir); + assert_eq!(info.size % BLKSIZE, 0); + + // Ensure the name is not exist + assert!(self.get_file_inode_id(name).is_none(), "file name exist"); + + // Create new INode + let inode = fs.borrow_mut().new_inode_file().unwrap(); + + // Write new entry + let entry = DiskEntry { + id: inode.borrow().id as u32, + name: Str256::from(name), + }; + self.resize(info.size + BLKSIZE).unwrap(); + self.write_at(info.size, entry.as_buf()).unwrap(); + + Ok(inode) + } + fn loopup(&mut self, path: &'static str) -> vfs::Result> { + let fs = self.fs.upgrade().unwrap(); + let info = self.info().unwrap(); + assert_eq!(info.type_, vfs::FileType::Dir); + assert_eq!(info.size % BLKSIZE, 0); + + let (name, rest_path) = match path.find('/') { + None => (path, ""), + Some(pos) => (&path[0..pos], &path[pos+1..]), + }; + let inode_id = self.get_file_inode_id(name); + if inode_id.is_none() { + return Err(()); + } + let inode = fs.borrow_mut().get_inode(inode_id.unwrap()); + + let type_ = inode.borrow().disk_inode.type_; + match type_ { + FileType::File => if rest_path == "" {Ok(inode)} else {Err(())}, + FileType::Dir => inode.borrow_mut().loopup(rest_path), + _ => unimplemented!(), + } + } +} + +impl Drop for INode { + /// Auto sync when drop + fn drop(&mut self) { + use vfs::INode; + self.sync().expect("failed to sync"); + } } /// Given a range and iterate sub-range for each block @@ -256,11 +348,11 @@ pub struct SimpleFileSystem { impl SimpleFileSystem { /// Load SFS from device pub fn open(mut device: Box) -> Option> { - let super_block = load_struct::(device.as_mut(), BLKN_SUPER); + let super_block = device.load_struct::(BLKN_SUPER); if super_block.check() == false { return None; } - let free_map = load_struct::<[u8; BLKSIZE]>(device.as_mut(), BLKN_FREEMAP); + let free_map = device.load_struct::<[u8; BLKSIZE]>(BLKN_FREEMAP); Some(SimpleFileSystem { super_block: Dirty::new(super_block), @@ -279,19 +371,38 @@ impl SimpleFileSystem { magic: MAGIC, blocks: blocks as u32, unused_blocks: blocks as u32 - 3, - info: Str32::from_slice(b"simple file system"), + info: Str32::from("simple file system"), }; - let mut free_map = BitSet::with_capacity(BLKBITS); - for i in 3 .. blocks { - free_map.insert(i); - } - SimpleFileSystem { + let free_map = { + let mut bitset = BitSet::with_capacity(BLKBITS); + for i in 3 .. blocks { + bitset.insert(i); + } + bitset + }; + + let sfs = SimpleFileSystem { super_block: Dirty::new_dirty(super_block), free_map: Dirty::new_dirty(free_map), inodes: BTreeMap::>::new(), device, self_ptr: Weak::default(), - }.wrap() + }.wrap(); + + // Init root INode + let inode = Rc::new(RefCell::new(INode { + disk_inode: Dirty::new_dirty(DiskINode::new_dir()), + id: BLKN_ROOT, + fs: Rc::downgrade(&sfs), + })); + inode.borrow_mut().init_dir(BLKN_ROOT).unwrap(); + { + use vfs::INode; + inode.borrow_mut().sync().unwrap(); + } + sfs.borrow_mut().inodes.insert(BLKN_ROOT, inode); + + sfs } /// Wrap pure SimpleFileSystem with Rc> /// Used in constructors @@ -322,7 +433,7 @@ impl SimpleFileSystem { // Load if not in memory. if !self.inodes.contains_key(&id) { - let disk_inode = load_struct::(self.device.as_mut(), id); + let disk_inode = self.device.load_struct::(id); let inode = Rc::new(RefCell::new(INode { disk_inode: Dirty::new(disk_inode), id, @@ -334,17 +445,39 @@ impl SimpleFileSystem { self.inodes.get(&id).unwrap().clone() } } + /// Create a new INode file + fn new_inode_file(&mut self) -> vfs::Result> { + let id = self.alloc_block().unwrap(); + Ok(Rc::new(RefCell::new(INode { + disk_inode: Dirty::new_dirty(DiskINode::new_file()), + id, + fs: self.self_ptr.clone(), + }))) + } + /// Create a new INode dir + fn new_inode_dir(&mut self, parent: INodeId) -> vfs::Result> { + let id = self.alloc_block().unwrap(); + let mut inode = INode { + disk_inode: Dirty::new_dirty(DiskINode::new_dir()), + id, + fs: self.self_ptr.clone(), + }; + inode.init_dir(parent).unwrap(); + + Ok(Rc::new(RefCell::new(inode))) + } } impl vfs::FileSystem for SimpleFileSystem { type INode = INode; /// Write back super block if dirty - fn sync(&mut self) -> Result<(), ()> { + fn sync(&mut self) -> vfs::Result<()> { let SimpleFileSystem { ref mut super_block, ref mut device, ref mut free_map, + ref mut inodes, .. } = self; @@ -356,6 +489,10 @@ impl vfs::FileSystem for SimpleFileSystem { device.write_at(BLKSIZE * BLKN_FREEMAP, free_map.as_buf()).unwrap(); free_map.sync(); } + for inode in inodes.values() { + use vfs::INode; + inode.borrow_mut().sync().unwrap(); + } Ok(()) } @@ -363,7 +500,7 @@ impl vfs::FileSystem for SimpleFileSystem { self.get_inode(BLKN_ROOT) } - fn unmount(&mut self) -> Result<(), ()> { + fn unmount(&mut self) -> vfs::Result<()> { unimplemented!() } @@ -408,6 +545,16 @@ impl AsBuf for BitSet { impl AsBuf for [u8; BLKSIZE] {} +impl From for vfs::FileType { + fn from(t: FileType) -> Self { + match t { + FileType::File => vfs::FileType::File, + FileType::Dir => vfs::FileType::Dir, + _ => panic!("unknown file type"), + } + } +} + #[cfg(test)] mod test { use super::*; diff --git a/src/structs.rs b/src/structs.rs index 11df346..16f65ca 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -3,6 +3,7 @@ use core::slice; use core::mem::size_of_val; use core::fmt::{Debug, Formatter, Error}; +use alloc::str; /// On-disk superblock #[repr(C, packed)] @@ -58,24 +59,39 @@ pub struct Str256(pub [u8; 256]); #[repr(C)] pub struct Str32(pub [u8; 32]); +impl AsRef for Str256 { + fn as_ref(&self) -> &str { + let len = self.0.iter().enumerate().find(|(_, &b)| b == 0).unwrap().0; + str::from_utf8(&self.0[0..len]).unwrap() + } +} +impl AsRef for Str32 { + fn as_ref(&self) -> &str { + let len = self.0.iter().enumerate().find(|(_, &b)| b == 0).unwrap().0; + str::from_utf8(&self.0[0..len]).unwrap() + } +} impl Debug for Str256 { fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { - use alloc::String; - let len = self.0.iter().enumerate().find(|(i, &b)| b == 0).unwrap().0; - write!(f, "{}", String::from_utf8_lossy(&self.0[0 .. len])) + write!(f, "{}", self.as_ref()) } } impl Debug for Str32 { fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { - use alloc::String; - let len = self.0.iter().enumerate().find(|(i, &b)| b == 0).unwrap().0; - write!(f, "{}", String::from_utf8_lossy(&self.0[0 .. len])) + write!(f, "{}", self.as_ref()) + } +} +impl From<&'static str> for Str256 { + fn from(s: &'static str) -> Self { + let mut ret = [0u8; 256]; + ret[0..s.len()].copy_from_slice(s.as_ref()); + Str256(ret) } } -impl Str32 { - pub fn from_slice(s: &[u8]) -> Self { +impl From<&'static str> for Str32 { + fn from(s: &'static str) -> Self { let mut ret = [0u8; 32]; - ret[0..s.len()].copy_from_slice(s); + ret[0..s.len()].copy_from_slice(s.as_ref()); Str32(ret) } } @@ -86,6 +102,31 @@ impl SuperBlock { } } +impl DiskINode { + pub const fn new_file() -> Self { + DiskINode { + size: 0, + type_: FileType::File, + nlinks: 0, + blocks: 0, + direct: [0; NDIRECT], + indirect: 0, + db_indirect: 0, + } + } + pub const fn new_dir() -> Self { + DiskINode { + size: 0, + type_: FileType::Dir, + nlinks: 0, + blocks: 0, + direct: [0; NDIRECT], + indirect: 0, + db_indirect: 0, + } + } +} + /// Convert structs to [u8] slice pub trait AsBuf { fn as_buf(&self) -> &[u8] { diff --git a/src/tests.rs b/src/tests.rs index 3c04e49..f6a5cb4 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -3,6 +3,7 @@ use std::io::{Read, Write, Seek, SeekFrom}; use std::boxed::Box; use super::sfs::*; use super::vfs::*; +use super::vfs::INode; impl Device for File { fn read_at(&mut self, offset: usize, buf: &mut [u8]) -> Option { @@ -32,11 +33,10 @@ fn test() { println!("{:?}", root); use super::structs::{DiskEntry, AsBuf}; - use super::vfs::INode; use std::mem::uninitialized; let mut entry: DiskEntry = unsafe{uninitialized()}; for i in 0 .. 23 { - root.borrow_mut().read_at(i * 4096, entry.as_buf_mut()); + root.borrow_mut().read_at(i * 4096, entry.as_buf_mut()).unwrap(); println!("{:?}", entry); } } @@ -47,6 +47,7 @@ fn create() { .read(true).write(true).create(true).open("test.img") .expect("failed to create file"); let sfs = SimpleFileSystem::create(Box::new(file), 16 * 4096); - - + let root = sfs.borrow_mut().root_inode(); + let file1 = root.borrow_mut().create("file1").unwrap(); + sfs.borrow_mut().sync().unwrap(); } \ No newline at end of file diff --git a/src/vfs.rs b/src/vfs.rs index 74b91e5..6f83a4f 100644 --- a/src/vfs.rs +++ b/src/vfs.rs @@ -1,34 +1,44 @@ use alloc::rc::{Rc, Weak}; use core::cell::RefCell; +use core::mem::size_of; +use core; /// Abstract operations on a inode. pub trait INode { - fn open(&mut self, flags: u32) -> Result<(), ()>; - fn close(&mut self) -> Result<(), ()>; - fn read_at(&mut self, offset: usize, buf: &mut [u8]) -> Option; - fn write_at(&mut self, offset: usize, buf: &[u8]) -> Option; - fn info(&mut self) -> Result; - fn sync(&mut self) -> Result<(), ()>; -// fn name_file(&mut self) -> Result<(), ()>; -// fn reclaim(&mut self) -> Result<(), ()>; - fn type_(&self) -> Result; -// fn try_seek(&mut self, offset: u64) -> Result<(), ()>; - fn resize(&mut self, len: usize) -> 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<(), ()>; + fn open(&mut self, flags: u32) -> Result<()>; + fn close(&mut self) -> Result<()>; + fn read_at(&mut self, offset: usize, buf: &mut [u8]) -> Result; + fn write_at(&mut self, offset: usize, buf: &[u8]) -> Result; + fn info(&mut self) -> Result; + fn sync(&mut self) -> Result<()>; +// fn name_file(&mut self) -> Result<()>; +// fn reclaim(&mut self) -> Result<()>; +// fn try_seek(&mut self, offset: u64) -> Result<()>; + fn resize(&mut self, len: usize) -> Result<()>; + fn create(&mut self, name: &'static str) -> Result>>; + fn loopup(&mut self, path: &'static str) -> Result>>; +// fn io_ctrl(&mut self, op: u32, data: &[u8]) -> Result<()>; } +#[derive(Debug)] pub struct FileInfo { pub size: usize, pub mode: u32, + pub type_: FileType, } +#[derive(Debug, Eq, PartialEq)] +pub enum FileType { + File, Dir, +} + +pub type Result = core::result::Result; + /// Abstract filesystem pub trait FileSystem { type INode: INode; - fn sync(&mut self) -> Result<(), ()>; + fn sync(&mut self) -> Result<()>; fn root_inode(&mut self) -> Rc>; - fn unmount(&mut self) -> Result<(), ()>; + fn unmount(&mut self) -> Result<()>; fn cleanup(&mut self); } \ No newline at end of file