From 15632fd69b3e4bdceb0c612c09fb2cbc96163885 Mon Sep 17 00:00:00 2001 From: WangRunji Date: Mon, 11 Feb 2019 22:00:34 +0800 Subject: [PATCH] impl SEFS --- rcore-fs/src/lib.rs | 38 +- rcore-fs/src/sefs/mod.rs | 655 +++++++++++++++++++++++ rcore-fs/src/sefs/std_impl.rs | 84 +++ rcore-fs/src/sefs/structs.rs | 174 ++++++ rcore-fs/src/{ => sfs}/blocked_device.rs | 2 +- rcore-fs/src/{sfs.rs => sfs/mod.rs} | 16 +- rcore-fs/src/sfs/std_impl.rs | 23 + rcore-fs/src/{ => sfs}/structs.rs | 0 rcore-fs/src/tests.rs | 4 +- rcore-fs/src/vfs.rs | 11 +- 10 files changed, 954 insertions(+), 53 deletions(-) create mode 100644 rcore-fs/src/sefs/mod.rs create mode 100644 rcore-fs/src/sefs/std_impl.rs create mode 100644 rcore-fs/src/sefs/structs.rs rename rcore-fs/src/{ => sfs}/blocked_device.rs (99%) rename rcore-fs/src/{sfs.rs => sfs/mod.rs} (98%) create mode 100644 rcore-fs/src/sfs/std_impl.rs rename rcore-fs/src/{ => sfs}/structs.rs (100%) diff --git a/rcore-fs/src/lib.rs b/rcore-fs/src/lib.rs index d511088..e8938fc 100644 --- a/rcore-fs/src/lib.rs +++ b/rcore-fs/src/lib.rs @@ -1,50 +1,14 @@ #![cfg_attr(not(any(test, feature = "std")), no_std)] #![feature(alloc)] -#![feature(const_fn)] #![feature(const_str_len)] -#![feature(nll)] extern crate alloc; -#[cfg(not(test))] -#[allow(unused_macros)] -macro_rules! eprintln { - () => (); - ($fmt:expr) => (); - ($fmt:expr, $($arg:tt)*) => (); -} - mod dirty; mod util; -mod blocked_device; pub mod vfs; pub mod sfs; +pub mod sefs; pub mod file; -mod structs; #[cfg(test)] mod tests; - -#[cfg(any(test, feature = "std"))] -pub mod std_impl { - use std::fs::File; - use std::io::{Read, Write, Seek, SeekFrom}; - use super::vfs::Device; - - impl Device for File { - 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: 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, - } - } - } -} diff --git a/rcore-fs/src/sefs/mod.rs b/rcore-fs/src/sefs/mod.rs new file mode 100644 index 0000000..f2bb7c4 --- /dev/null +++ b/rcore-fs/src/sefs/mod.rs @@ -0,0 +1,655 @@ +use alloc::{boxed::Box, collections::BTreeMap, string::String, sync::{Arc, Weak}, vec::Vec}; +use core::any::Any; +use core::fmt::{Debug, Error, Formatter}; +use core::mem::uninitialized; + +use bitvec::BitVec; +use log::*; +use spin::{Mutex, RwLock}; + +use crate::dirty::Dirty; +use crate::vfs::{self, FileSystem, FsError, INode, Timespec}; + +use self::structs::*; + +mod structs; +mod std_impl; + +/// A file stores a normal file or directory. +/// +/// The interface is same as `std::fs::File`. +pub trait File: Send + Sync { + fn read_at(&self, buf: &mut [u8], offset: usize) -> DevResult; + fn write_at(&self, buf: &[u8], offset: usize) -> DevResult; + fn set_len(&self, len: usize) -> DevResult<()>; + fn flush(&self) -> DevResult<()>; + + fn read_exact_at(&self, buf: &mut [u8], offset: usize) -> DevResult<()> { + let len = self.read_at(buf, offset)?; + if len == buf.len() { Ok(()) } else { Err(DeviceError) } + } + fn write_all_at(&self, buf: &[u8], offset: usize) -> DevResult<()> { + let len = self.write_at(buf, offset)?; + if len == buf.len() { Ok(()) } else { Err(DeviceError) } + } +} + +/// The collection of all files in the FS. +pub trait Storage: Send + Sync { + fn open(&self, file_id: usize) -> DevResult>; + fn create(&self, file_id: usize) -> DevResult>; + fn remove(&self, file_id: usize) -> DevResult<()>; +} + +#[derive(Debug)] +pub struct DeviceError; + +pub type DevResult = Result; + +impl From for FsError { + fn from(_: DeviceError) -> Self { + FsError::DeviceError + } +} + +/// Helper methods for `File` +impl File { + fn read_block(&self, id: BlockId, buf: &mut [u8]) -> DevResult<()> { + assert!(buf.len() <= BLKSIZE); + self.read_exact_at(buf, id * BLKSIZE) + } + fn write_block(&self, id: BlockId, buf: &[u8]) -> DevResult<()> { + assert!(buf.len() <= BLKSIZE); + self.write_all_at(buf, id * BLKSIZE) + } + fn read_direntry(&self, id: usize) -> DevResult { + let mut direntry: DiskEntry = unsafe { uninitialized() }; + self.read_exact_at(direntry.as_buf_mut(), DIRENT_SIZE * id)?; + Ok(direntry) + } + fn write_direntry(&self, id: usize, direntry: &DiskEntry) -> DevResult<()> { + self.write_all_at(direntry.as_buf(), DIRENT_SIZE * id) + } + /// Load struct `T` from given block in device + fn load_struct(&self, id: BlockId) -> DevResult { + let mut s: T = unsafe { uninitialized() }; + self.read_block(id, s.as_buf_mut())?; + Ok(s) + } +} + +/// inode for SEFS +pub struct INodeImpl { + /// inode number + id: INodeId, + /// on-disk inode + disk_inode: RwLock>, + /// back file + file: Box, + /// Reference to FS + fs: Arc, +} + +impl Debug for INodeImpl { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + write!(f, "INode {{ id: {}, disk: {:?} }}", self.id, self.disk_inode) + } +} + +impl INodeImpl { + /// Only for Dir + fn get_file_inode_and_entry_id(&self, name: &str) -> Option<(INodeId, usize)> { + (0..self.disk_inode.read().blocks as usize) + .map(|i| { + let entry = self.file.read_direntry(i).unwrap(); + (entry, i) + }) + .find(|(entry, _)| entry.name.as_ref() == name) + .map(|(entry, id)| (entry.id as INodeId, id)) + } + fn get_file_inode_id(&self, name: &str) -> Option { + self.get_file_inode_and_entry_id(name).map(|(inode_id, _)| inode_id) + } + /// Init dir content. Insert 2 init entries. + /// This do not init nlinks, please modify the nlinks in the invoker. + fn dirent_init(&self, parent: INodeId) -> vfs::Result<()> { + self.disk_inode.write().blocks = 2; + // Insert entries: '.' '..' + self.file.write_direntry(0, &DiskEntry { + id: self.id as u32, + name: Str256::from("."), + })?; + self.file.write_direntry(1, &DiskEntry { + id: parent as u32, + name: Str256::from(".."), + })?; + Ok(()) + } + fn dirent_append(&self, entry: &DiskEntry) -> vfs::Result<()> { + let mut inode = self.disk_inode.write(); + let total = &mut inode.blocks; + self.file.write_direntry(*total as usize, entry)?; + *total += 1; + Ok(()) + } + /// remove a page in middle of file and insert the last page here, useful for dirent remove + /// should be only used in unlink + fn dirent_remove(&self, id: usize) -> vfs::Result<()> { + let total = self.disk_inode.read().blocks as usize; + debug_assert!(id < total); + let last_direntry = self.file.read_direntry(total - 1)?; + if id != total - 1 { + self.file.write_direntry(id, &last_direntry)?; + } + self.file.set_len((total - 1) * DIRENT_SIZE)?; + self.disk_inode.write().blocks -= 1; + Ok(()) + } + fn nlinks_inc(&self) { + self.disk_inode.write().nlinks += 1; + } + fn nlinks_dec(&self) { + let mut disk_inode = self.disk_inode.write(); + assert!(disk_inode.nlinks > 0); + disk_inode.nlinks -= 1; + } +} + +impl vfs::INode for INodeImpl { + fn read_at(&self, offset: usize, buf: &mut [u8]) -> vfs::Result { + if self.disk_inode.read().type_ != FileType::File { + return Err(FsError::NotFile); + } + let len = self.file.read_at(buf, offset)?; + Ok(len) + } + fn write_at(&self, offset: usize, buf: &[u8]) -> vfs::Result { + if self.disk_inode.read().type_ != FileType::File { + return Err(FsError::NotFile); + } + let len = self.file.write_at(buf, offset)?; + Ok(len) + } + /// the size returned here is logical size(entry num for directory), not the disk space used. + fn info(&self) -> vfs::Result { + let disk_inode = self.disk_inode.read(); + Ok(vfs::FileInfo { + inode: 0, + size: match disk_inode.type_ { + FileType::File => disk_inode.size as usize, + FileType::Dir => disk_inode.blocks as usize, + _ => panic!("Unknown file type"), + }, + mode: 0, + type_: vfs::FileType::from(disk_inode.type_.clone()), + blocks: disk_inode.blocks as usize, + atime: Timespec { sec: 0, nsec: 0 }, + mtime: Timespec { sec: 0, nsec: 0 }, + ctime: Timespec { sec: 0, nsec: 0 }, + nlinks: disk_inode.nlinks as usize, + uid: 0, + gid: 0 + }) + } + fn sync(&self) -> vfs::Result<()> { + let mut disk_inode = self.disk_inode.write(); + if disk_inode.dirty() { + self.fs.meta_file.write_block(self.id, disk_inode.as_buf())?; + disk_inode.sync(); + } + Ok(()) + } + fn resize(&self, len: usize) -> vfs::Result<()> { + if self.disk_inode.read().type_ != FileType::File { + return Err(FsError::NotFile); + } + self.file.set_len(len)?; + self.disk_inode.write().size = len as u32; + Ok(()) + } + fn create(&self, name: &str, type_: vfs::FileType) -> vfs::Result> { + let info = self.info()?; + if info.type_ != vfs::FileType::Dir { + return Err(FsError::NotDir); + } + if info.nlinks <= 0 { + return Err(FsError::DirRemoved); + } + + // Ensure the name is not exist + if !self.get_file_inode_id(name).is_none() { + return Err(FsError::EntryExist); + } + + // Create new INode + let inode = match type_ { + vfs::FileType::File => self.fs.new_inode_file()?, + vfs::FileType::Dir => self.fs.new_inode_dir(self.id)?, + }; + + // Write new entry + let entry = DiskEntry { + id: inode.id as u32, + name: Str256::from(name), + }; + self.dirent_append(&entry)?; + inode.nlinks_inc(); + if type_ == vfs::FileType::Dir { + inode.nlinks_inc(); //for . + self.nlinks_inc(); //for .. + } + + Ok(inode) + } + fn unlink(&self, name: &str) -> vfs::Result<()> { + let info = self.info()?; + if info.type_ != vfs::FileType::Dir { + return Err(FsError::NotDir) + } + if info.nlinks <= 0 { + return Err(FsError::DirRemoved) + } + if name == "." { + return Err(FsError::IsDir) + } + if name == ".." { + return Err(FsError::IsDir) + } + + let (inode_id, entry_id) = self.get_file_inode_and_entry_id(name).ok_or(FsError::EntryNotFound)?; + let inode = self.fs.get_inode(inode_id); + + let type_ = inode.disk_inode.read().type_; + if type_ == FileType::Dir { + // only . and .. + assert!(inode.disk_inode.read().blocks >= 2); + if inode.disk_inode.read().blocks > 2 { + return Err(FsError::DirNotEmpty) + } + } + inode.nlinks_dec(); + if type_ == FileType::Dir { + inode.nlinks_dec(); //for . + self.nlinks_dec(); //for .. + } + self.dirent_remove(entry_id)?; + + Ok(()) + } + fn link(&self, name: &str, other: &Arc) -> vfs::Result<()> { + let info = self.info()?; + if info.type_ != vfs::FileType::Dir { + return Err(FsError::NotDir) + } + if info.nlinks <= 0 { + return Err(FsError::DirRemoved) + } + if !self.get_file_inode_id(name).is_none() { + return Err(FsError::EntryExist); + } + let child = other.downcast_ref::().ok_or(FsError::NotSameFs)?; + if !Arc::ptr_eq(&self.fs, &child.fs) { + return Err(FsError::NotSameFs); + } + if child.info()?.type_ == vfs::FileType::Dir { + return Err(FsError::IsDir); + } + let entry = DiskEntry { + id: child.id as u32, + name: Str256::from(name), + }; + self.dirent_append(&entry)?; + child.nlinks_inc(); + Ok(()) + } + fn rename(&self, old_name: &str, new_name: &str) -> vfs::Result<()> { + let info = self.info()?; + if info.type_ != vfs::FileType::Dir { + return Err(FsError::NotDir) + } + if info.nlinks <= 0 { + return Err(FsError::DirRemoved) + } + if old_name == "." { + return Err(FsError::IsDir) + } + if old_name == ".." { + return Err(FsError::IsDir) + } + + if !self.get_file_inode_id(new_name).is_none() { + return Err(FsError::EntryExist); + } + + let (inode_id, entry_id) = self.get_file_inode_and_entry_id(old_name) + .ok_or(FsError::EntryNotFound)?; + + // in place modify name + let entry = DiskEntry { + id: inode_id as u32, + name: Str256::from(new_name), + }; + self.file.write_direntry(entry_id, &entry)?; + + Ok(()) + } + fn move_(&self, old_name: &str, target: &Arc, new_name: &str) -> vfs::Result<()> { + let info = self.info()?; + if info.type_ != vfs::FileType::Dir { + return Err(FsError::NotDir) + } + if info.nlinks <= 0 { + return Err(FsError::DirRemoved) + } + if old_name == "." { + return Err(FsError::IsDir) + } + if old_name == ".." { + return Err(FsError::IsDir) + } + + let dest = target.downcast_ref::().ok_or(FsError::NotSameFs)?; + if !Arc::ptr_eq(&self.fs, &dest.fs) { + return Err(FsError::NotSameFs); + } + if dest.info()?.type_ != vfs::FileType::Dir { + return Err(FsError::NotDir) + } + if dest.info()?.nlinks <= 0 { + return Err(FsError::DirRemoved) + } + + if !self.get_file_inode_id(new_name).is_none() { + return Err(FsError::EntryExist); + } + + let (inode_id, entry_id) = self.get_file_inode_and_entry_id(old_name).ok_or(FsError::EntryNotFound)?; + let inode = self.fs.get_inode(inode_id); + + let entry = DiskEntry { + id: inode_id as u32, + name: Str256::from(new_name), + }; + dest.dirent_append(&entry)?; + self.dirent_remove(entry_id)?; + + if inode.info()?.type_ == vfs::FileType::Dir { + self.nlinks_dec(); + dest.nlinks_inc(); + } + + Ok(()) + } + fn find(&self, name: &str) -> vfs::Result> { + let info = self.info()?; + if info.type_ != vfs::FileType::Dir { + return Err(FsError::NotDir) + } + let inode_id = self.get_file_inode_id(name).ok_or(FsError::EntryNotFound)?; + Ok(self.fs.get_inode(inode_id)) + } + fn get_entry(&self, id: usize) -> vfs::Result { + if self.disk_inode.read().type_ != FileType::Dir { + return Err(FsError::NotDir) + } + if id >= self.disk_inode.read().blocks as usize { + return Err(FsError::EntryNotFound) + }; + let entry = self.file.read_direntry(id)?; + Ok(String::from(entry.name.as_ref())) + } + fn fs(&self) -> Arc { + self.fs.clone() + } + fn as_any_ref(&self) -> &Any { + self + } +} + +impl Drop for INodeImpl { + /// Auto sync when drop + fn drop(&mut self) { + self.sync().expect("Failed to sync when dropping the SEFS Inode"); + if self.disk_inode.read().nlinks <= 0 { + self.disk_inode.write().sync(); + self.fs.free_block(self.id); + self.fs.device.remove(self.id); + } + } +} + + +/// Simple Encrypt File System +pub struct SEFS { + /// on-disk superblock + super_block: RwLock>, + /// blocks in use are marked 0 + free_map: RwLock>, + /// inode list + inodes: RwLock>>, + /// device + device: Box, + /// metadata file + meta_file: Box, + /// Pointer to self, used by INodes + self_ptr: Weak, +} + +impl SEFS { + /// Load SEFS + pub fn open(device: Box) -> vfs::Result> { + let meta_file = device.open(0)?; + let super_block = meta_file.load_struct::(BLKN_SUPER)?; + if !super_block.check() { + return Err(FsError::WrongFs); + } + let free_map = meta_file.load_struct::<[u8; BLKSIZE]>(BLKN_FREEMAP)?; + + Ok(SEFS { + super_block: RwLock::new(Dirty::new(super_block)), + free_map: RwLock::new(Dirty::new(BitVec::from(free_map.as_ref()))), + inodes: RwLock::new(BTreeMap::new()), + device, + meta_file, + self_ptr: Weak::default(), + }.wrap()) + } + /// Create a new SEFS + pub fn create(device: Box) -> vfs::Result> { + let blocks = BLKBITS; + + let super_block = SuperBlock { + magic: MAGIC, + blocks: blocks as u32, + unused_blocks: blocks as u32 - 3, + }; + let free_map = { + let mut bitset = BitVec::with_capacity(BLKBITS); + bitset.extend(core::iter::repeat(false).take(BLKBITS)); + for i in 3..blocks { + bitset.set(i, true); + } + bitset + }; + let meta_file = device.create(0)?; + + let sefs = SEFS { + super_block: RwLock::new(Dirty::new_dirty(super_block)), + free_map: RwLock::new(Dirty::new_dirty(free_map)), + inodes: RwLock::new(BTreeMap::new()), + device, + meta_file, + self_ptr: Weak::default(), + }.wrap(); + + // Init root INode + let root = sefs._new_inode(BLKN_ROOT, Dirty::new_dirty(DiskINode::new_dir())); + root.dirent_init(BLKN_ROOT)?; + root.nlinks_inc(); //for . + root.nlinks_inc(); //for ..(root's parent is itself) + root.sync()?; + + Ok(sefs) + } + /// Wrap pure SEFS with Arc + /// Used in constructors + fn wrap(self) -> Arc { + // Create a Arc, make a Weak from it, then put it into the struct. + // It's a little tricky. + let fs = Arc::new(self); + let weak = Arc::downgrade(&fs); + let ptr = Arc::into_raw(fs) as *mut Self; + unsafe { (*ptr).self_ptr = weak; } + unsafe { Arc::from_raw(ptr) } + } + + /// Allocate a block, return block id + fn alloc_block(&self) -> Option { + let mut free_map = self.free_map.write(); + let id = free_map.alloc(); + if let Some(block_id) = id { + let mut super_block = self.super_block.write(); + if super_block.unused_blocks == 0 { + free_map.set(block_id, true); + return None + } + super_block.unused_blocks -= 1; // will not underflow + } + id + } + /// Free a block + fn free_block(&self, block_id: usize) { + let mut free_map = self.free_map.write(); + assert!(!free_map[block_id]); + free_map.set(block_id, true); + self.super_block.write().unused_blocks += 1; + } + + /// Create a new INode struct, then insert it to self.inodes + /// Private used for load or create INode + fn _new_inode(&self, id: INodeId, disk_inode: Dirty) -> Arc { + let inode = Arc::new(INodeImpl { + id, + disk_inode: RwLock::new(disk_inode), + file: self.device.create(id).unwrap(), + fs: self.self_ptr.upgrade().unwrap(), + }); + self.inodes.write().insert(id, Arc::downgrade(&inode)); + inode + } + /// Get inode by id. Load if not in memory. + /// ** Must ensure it's a valid INode ** + fn get_inode(&self, id: INodeId) -> Arc { + assert!(!self.free_map.read()[id]); + + // In the BTreeSet and not weak. + if let Some(inode) = self.inodes.read().get(&id) { + if let Some(inode) = inode.upgrade() { + return inode; + } + } + // Load if not in set, or is weak ref. + let disk_inode = Dirty::new(self.meta_file.load_struct::(id).unwrap()); + self._new_inode(id, disk_inode) + } + /// Create a new INode file + fn new_inode_file(&self) -> vfs::Result> { + let id = self.alloc_block().ok_or(FsError::NoDeviceSpace)?; + let disk_inode = Dirty::new_dirty(DiskINode::new_file()); + Ok(self._new_inode(id, disk_inode)) + } + /// Create a new INode dir + fn new_inode_dir(&self, parent: INodeId) -> vfs::Result> { + let id = self.alloc_block().ok_or(FsError::NoDeviceSpace)?; + let disk_inode = Dirty::new_dirty(DiskINode::new_dir()); + let inode = self._new_inode(id, disk_inode); + inode.dirent_init(parent)?; + Ok(inode) + } + fn flush_weak_inodes(&self) { + let mut inodes = self.inodes.write(); + let remove_ids: Vec<_> = inodes.iter().filter(|(_, inode)| { + inode.upgrade().is_none() + }).map(|(&id, _)| id).collect(); + for id in remove_ids.iter() { + inodes.remove(&id); + } + } +} + +impl vfs::FileSystem for SEFS { + /// Write back super block if dirty + fn sync(&self) -> vfs::Result<()> { + let mut super_block = self.super_block.write(); + if super_block.dirty() { + self.meta_file.write_all_at(super_block.as_buf(), BLKSIZE * BLKN_SUPER)?; + super_block.sync(); + } + let mut free_map = self.free_map.write(); + if free_map.dirty() { + self.meta_file.write_all_at(free_map.as_buf(), BLKSIZE * BLKN_FREEMAP)?; + free_map.sync(); + } + self.flush_weak_inodes(); + for inode in self.inodes.read().values() { + if let Some(inode) = inode.upgrade() { + inode.sync()?; + } + } + Ok(()) + } + + fn root_inode(&self) -> Arc { + self.get_inode(BLKN_ROOT) + } + + fn info(&self) -> &'static vfs::FsInfo { + static INFO: vfs::FsInfo = vfs::FsInfo { + max_file_size: MAX_FILE_SIZE, + }; + &INFO + } +} + +impl Drop for SEFS { + /// Auto sync when drop + fn drop(&mut self) { + self.sync().expect("Failed to sync when dropping the SimpleFileSystem"); + } +} + +trait BitsetAlloc { + fn alloc(&mut self) -> Option; +} + +impl BitsetAlloc for BitVec { + fn alloc(&mut self) -> Option { + // TODO: more efficient + let id = (0..self.len()).find(|&i| self[i]); + if let Some(id) = id { + self.set(id, false); + } + id + } +} + +impl AsBuf for BitVec { + fn as_buf(&self) -> &[u8] { + self.as_ref() + } + fn as_buf_mut(&mut self) -> &mut [u8] { + self.as_mut() + } +} + +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"), + } + } +} diff --git a/rcore-fs/src/sefs/std_impl.rs b/rcore-fs/src/sefs/std_impl.rs new file mode 100644 index 0000000..b4fda09 --- /dev/null +++ b/rcore-fs/src/sefs/std_impl.rs @@ -0,0 +1,84 @@ +#![cfg(any(test, feature = "std"))] + +use std::path::{Path, PathBuf}; +use std::fs::{File, OpenOptions, remove_file}; +use std::io::{Read, Write, Seek, SeekFrom}; +use spin::Mutex; +use super::{DeviceError, DevResult}; + +pub struct StdStorage { + path: PathBuf, +} + +impl StdStorage { + pub fn new(path: impl AsRef) -> Self { + assert!(path.as_ref().is_dir()); + StdStorage { path: path.as_ref().to_path_buf() } + } +} + +impl super::Storage for StdStorage { + fn open(&self, file_id: usize) -> DevResult> { + let mut path = self.path.to_path_buf(); + path.push(format!("{}", file_id)); + let file = OpenOptions::new().read(true).write(true).open(path)?; + Ok(Box::new(Mutex::new(file))) + } + + fn create(&self, file_id: usize) -> DevResult> { + let mut path = self.path.to_path_buf(); + path.push(format!("{}", file_id)); + let file = OpenOptions::new().read(true).write(true).create(true).open(path)?; + Ok(Box::new(Mutex::new(file))) + } + + fn remove(&self, file_id: usize) -> DevResult<()> { + let mut path = self.path.to_path_buf(); + path.push(format!("{}", file_id)); + remove_file(path)?; + Ok(()) + } +} + +impl From for DeviceError { + fn from(e: std::io::Error) -> Self { + panic!("{:?}", e); + DeviceError + } +} + +impl super::File for Mutex { + fn read_at(&self, buf: &mut [u8], offset: usize) -> DevResult { + let mut file = self.lock(); + let offset = offset as u64; + let real_offset = file.seek(SeekFrom::Start(offset))?; + if real_offset != offset { + return Err(DeviceError); + } + let len = file.read(buf)?; + Ok(len) + } + + fn write_at(&self, buf: &[u8], offset: usize) -> DevResult { + let mut file = self.lock(); + let offset = offset as u64; + let real_offset = file.seek(SeekFrom::Start(offset))?; + if real_offset != offset { + return Err(DeviceError); + } + let len = file.write(buf)?; + Ok(len) + } + + fn set_len(&self, len: usize) -> DevResult<()> { + let mut file = self.lock(); + file.set_len(len as u64)?; + Ok(()) + } + + fn flush(&self) -> DevResult<()> { + let mut file = self.lock(); + file.sync_all()?; + Ok(()) + } +} diff --git a/rcore-fs/src/sefs/structs.rs b/rcore-fs/src/sefs/structs.rs new file mode 100644 index 0000000..69e2e64 --- /dev/null +++ b/rcore-fs/src/sefs/structs.rs @@ -0,0 +1,174 @@ +//! On-disk structures in SEFS + +use core::slice; +use core::mem::{size_of_val, size_of}; +use core::fmt::{Debug, Formatter, Error}; +use alloc::str; +use static_assertions::const_assert; + +/// On-disk superblock +#[repr(C)] +#[derive(Debug)] +pub struct SuperBlock { + /// magic number, should be SFS_MAGIC + pub magic: u32, + /// number of blocks in fs + pub blocks: u32, + /// number of unused blocks in fs + pub unused_blocks: u32, +} + +/// On-disk inode +#[repr(C)] +#[derive(Debug)] +pub struct DiskINode { + /// size of the file (in bytes) + pub size: u32, + /// one of SYS_TYPE_* above + pub type_: FileType, + /// number of hard links to this file + /// Note: "." and ".." is counted in this nlinks + pub nlinks: u16, + /// number of blocks + pub blocks: u32, + pub uid: u16, + pub gid: u8, + pub atime: u32, + pub mtime: u32, + pub ctime: u32, +} + +/// On-disk file entry +#[repr(C)] +#[derive(Debug)] +pub struct DiskEntry { + /// inode number + pub id: u32, + /// file name + pub name: Str256, +} + +#[repr(C)] +pub struct Str256(pub [u8; 256]); + + +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 Debug for Str256 { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + write!(f, "{}", self.as_ref()) + } +} + +impl<'a> From<&'a str> for Str256 { + fn from(s: &'a str) -> Self { + let mut ret = [0u8; 256]; + ret[0..s.len()].copy_from_slice(s.as_ref()); + Str256(ret) + } +} + +impl SuperBlock { + pub fn check(&self) -> bool { + self.magic == MAGIC + } +} + +impl DiskINode { + pub const fn new_file() -> Self { + DiskINode { + size: 0, + type_: FileType::File, + nlinks: 0, + blocks: 0, + uid: 0, + gid: 0, + atime: 0, + mtime: 0, + ctime: 0 + } + } + pub const fn new_dir() -> Self { + DiskINode { + size: 0, + type_: FileType::Dir, + nlinks: 0, + blocks: 0, + uid: 0, + gid: 0, + atime: 0, + mtime: 0, + ctime: 0 + } + } +} + +/// 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 {} + +impl AsBuf for DiskEntry {} + +impl AsBuf for u32 {} + +/* + * 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 +pub const BLKSIZE: usize = 1usize << BLKSIZE_LOG2; +/// log2( size of block ) +pub const BLKSIZE_LOG2: u8 = 7; +/// max length of information +pub const MAX_INFO_LEN: usize = 31; +/// max length of filename +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: BlockId = 0; +/// location of the root dir inode +pub const BLKN_ROOT: BlockId = 2; +/// 1st block of the freemap +pub const BLKN_FREEMAP: BlockId = 1; +/// number of bits in a block +pub const BLKBITS: usize = BLKSIZE * 8; +/// size of one entry +pub const ENTRY_SIZE: usize = 4; +/// number of entries in a block +pub const BLK_NENTRY: usize = BLKSIZE / ENTRY_SIZE; +/// size of a dirent used in the size field +pub const DIRENT_SIZE: usize = 260; + +/// file types +#[repr(u16)] +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +pub enum FileType { + Invalid = 0, + File = 1, + Dir = 2, + Link = 3, +} + +const_assert!(o1; size_of::() <= BLKSIZE); +const_assert!(o2; size_of::() <= BLKSIZE); diff --git a/rcore-fs/src/blocked_device.rs b/rcore-fs/src/sfs/blocked_device.rs similarity index 99% rename from rcore-fs/src/blocked_device.rs rename to rcore-fs/src/sfs/blocked_device.rs index 5e1ff0a..472b326 100644 --- a/rcore-fs/src/blocked_device.rs +++ b/rcore-fs/src/sfs/blocked_device.rs @@ -1,5 +1,5 @@ use crate::util::*; -use crate::vfs::Device; +use super::Device; /// Device which can only R/W in blocks pub trait BlockedDevice: Send { diff --git a/rcore-fs/src/sfs.rs b/rcore-fs/src/sfs/mod.rs similarity index 98% rename from rcore-fs/src/sfs.rs rename to rcore-fs/src/sfs/mod.rs index 15cedb7..511f787 100644 --- a/rcore-fs/src/sfs.rs +++ b/rcore-fs/src/sfs/mod.rs @@ -8,9 +8,21 @@ use log::*; use spin::{Mutex, RwLock}; use crate::dirty::Dirty; -use crate::structs::*; use crate::util::*; -use crate::vfs::{self, Device, FileSystem, FsError, INode, Timespec}; +use crate::vfs::{self, FileSystem, FsError, INode, Timespec}; + +use self::structs::*; + +mod structs; +mod std_impl; +mod blocked_device; + +/// Interface for FS to read & write +/// TODO: use std::io::{Read, Write} +pub trait Device: Send { + fn read_at(&mut self, offset: usize, buf: &mut [u8]) -> Option; + fn write_at(&mut self, offset: usize, buf: &[u8]) -> Option; +} impl Device { fn read_block(&mut self, id: BlockId, offset: usize, buf: &mut [u8]) -> vfs::Result<()> { diff --git a/rcore-fs/src/sfs/std_impl.rs b/rcore-fs/src/sfs/std_impl.rs new file mode 100644 index 0000000..0025ff9 --- /dev/null +++ b/rcore-fs/src/sfs/std_impl.rs @@ -0,0 +1,23 @@ +#![cfg(any(test, feature = "std"))] + +use std::fs::File; +use std::io::{Read, Write, Seek, SeekFrom}; +use super::Device; + +impl Device for File { + 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: 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, + } + } +} \ No newline at end of file diff --git a/rcore-fs/src/structs.rs b/rcore-fs/src/sfs/structs.rs similarity index 100% rename from rcore-fs/src/structs.rs rename to rcore-fs/src/sfs/structs.rs diff --git a/rcore-fs/src/tests.rs b/rcore-fs/src/tests.rs index 110d761..45787e9 100644 --- a/rcore-fs/src/tests.rs +++ b/rcore-fs/src/tests.rs @@ -4,7 +4,6 @@ use std::sync::Arc; use std::mem::uninitialized; use crate::sfs::*; use crate::vfs::*; -use crate::structs::AsBuf; fn _open_sample_file() -> Arc { fs::copy("sfs.img", "test.img").expect("failed to open sfs.img"); @@ -69,8 +68,7 @@ fn resize() -> Result<()> { file1.resize(SIZE1)?; assert_eq!(file1.info()?.size, SIZE1, "wrong size after resize"); let mut data1: [u8; SIZE2] = unsafe { uninitialized() }; - impl AsBuf for [u8; SIZE2] {} - let len = file1.read_at(0, data1.as_buf_mut())?; + let len = file1.read_at(0, data1.as_mut())?; assert_eq!(len, SIZE1, "wrong size returned by read_at()"); assert_eq!(&data1[..SIZE1], &[0u8; SIZE1][..], "expanded data should be 0"); diff --git a/rcore-fs/src/vfs.rs b/rcore-fs/src/vfs.rs index 7dbf331..22456f2 100644 --- a/rcore-fs/src/vfs.rs +++ b/rcore-fs/src/vfs.rs @@ -1,14 +1,6 @@ use alloc::{vec::Vec, string::String, sync::Arc}; use core::any::Any; use core::result; -pub use super::blocked_device::BlockedDevice; - -/// Interface for FS to read & write -/// TODO: use std::io::{Read, Write} -pub trait Device: Send { - fn read_at(&mut self, offset: usize, buf: &mut [u8]) -> Option; - fn write_at(&mut self, offset: usize, buf: &[u8]) -> Option; -} /// Abstract operations on a inode. pub trait INode: Any + Sync + Send { @@ -144,6 +136,7 @@ pub enum FsError { DirRemoved,//E_NOENT, when the current dir was remove by a previous unlink DirNotEmpty,//E_NOTEMPTY WrongFs,//E_INVAL, when we find the content on disk is wrong when opening the device + DeviceError, } pub type Result = result::Result; @@ -153,6 +146,4 @@ pub trait FileSystem: Sync { fn sync(&self) -> Result<()>; fn root_inode(&self) -> Arc; fn info(&self) -> &'static FsInfo; -// fn unmount(&self) -> Result<()>; -// fn cleanup(&self); }