diff --git a/Cargo.toml b/Cargo.toml index 15e2841..8d882d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ members = [ "rcore-fs-sfs", "rcore-fs-sefs", "rcore-fs-fuse", - "rcore-fs-ext2" + "rcore-fs-ext2", + "rcore-fs-ramfs" ] exclude = ["sefs-fuse"] diff --git a/rcore-fs-ramfs/Cargo.toml b/rcore-fs-ramfs/Cargo.toml new file mode 100644 index 0000000..fcf4659 --- /dev/null +++ b/rcore-fs-ramfs/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "rcore-fs-ramfs" +version = "0.1.0" +authors = ["gjz010 ", "WangRunji "] +edition = "2018" + +[dependencies] +rcore-fs = { path = "../rcore-fs" } +spin = "0.4" +log = "0.4" diff --git a/rcore-fs-ramfs/src/lib.rs b/rcore-fs-ramfs/src/lib.rs new file mode 100644 index 0000000..e4db648 --- /dev/null +++ b/rcore-fs-ramfs/src/lib.rs @@ -0,0 +1,347 @@ +#![cfg_attr(not(any(test, feature = "std")), no_std)] +#![feature(alloc)] + +extern crate alloc; +#[macro_use] +extern crate log; + +use alloc::{ + string::{String, ToString}, + sync::{Arc, Weak}, + vec, + vec::Vec, +}; +use core::any::Any; +use rcore_fs::vfs::*; +use spin::{RwLock, RwLockWriteGuard}; + +pub struct RamFS { + root: Arc, +} + +impl FileSystem for RamFS { + fn sync(&self) -> Result<()> { + Ok(()) + } + + fn root_inode(&self) -> Arc { + Arc::clone(&self.root) + } + + fn info(&self) -> FsInfo { + FsInfo { + bsize: 0, + frsize: 0, + blocks: 0, + bfree: 0, + bavail: 0, + files: 0, + ffree: 0, + namemax: 0, + } + } +} + +struct RamFSINode { + parent: Option>, + this: Option>, + children: Vec<(String, Arc)>, + content: Vec, + extra: Metadata, + fs: Weak, +} + +struct LockedINode(RwLock); + +impl INode for LockedINode { + fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result { + let file = self.0.read(); + if file.extra.type_ == FileType::Dir { + return Err(FsError::IsDir); + } + let start = file.content.len().min(offset); + let end = file.content.len().min(offset + buf.len()); + let src = &file.content[start..end]; + buf[0..src.len()].copy_from_slice(src); + Ok(src.len()) + } + + fn write_at(&self, offset: usize, buf: &[u8]) -> Result { + let mut file = self.0.write(); + if file.extra.type_ == FileType::Dir { + return Err(FsError::IsDir); + } + let content = &mut file.content; + if offset + buf.len() > content.len() { + content.resize(offset + buf.len(), 0); + } + let target = &mut content[offset..offset + buf.len()]; + target.copy_from_slice(buf); + Ok(buf.len()) + } + + fn poll(&self) -> Result { + let file = self.0.read(); + if file.extra.type_ == FileType::Dir { + return Err(FsError::IsDir); + } + Ok(PollStatus { + read: true, + write: true, + error: false, + }) + } + + fn metadata(&self) -> Result { + let file = self.0.read(); + let extra = &file.extra; + let size = file.content.len(); + Ok(Metadata { + dev: 0, + inode: extra.inode, + size: size, + blk_size: 4096, + blocks: size / 4096, + atime: extra.atime, + mtime: extra.mtime, + ctime: extra.ctime, + type_: extra.type_, + mode: extra.mode, + nlinks: extra.nlinks, + uid: extra.uid, + gid: extra.gid, + rdev: extra.rdev, + }) + } + + fn set_metadata(&self, metadata: &Metadata) -> Result<()> { + let mut file = self.0.write(); + file.extra.atime = metadata.atime; + file.extra.mtime = metadata.mtime; + file.extra.ctime = metadata.ctime; + file.extra.mode = metadata.mode; + file.extra.uid = metadata.uid; + file.extra.gid = metadata.gid; + Ok(()) + } + + fn sync_all(&self) -> Result<()> { + Ok(()) + } + + fn sync_data(&self) -> Result<()> { + Ok(()) + } + + fn resize(&self, len: usize) -> Result<()> { + let mut file = self.0.write(); + if file.extra.type_ == FileType::File { + file.content.resize(len, 0); + Ok(()) + } else { + Err(FsError::NotFile) + } + } + + fn create2( + &self, + name: &str, + type_: FileType, + mode: u32, + data: usize, + ) -> Result> { + let mut file = self.0.write(); + if file.extra.type_ == FileType::Dir { + if name == "." { + return Err(FsError::EntryExist); + } + if name == ".." { + return Err(FsError::EntryExist); + } + for (n, _) in file.children.iter() { + if n == name { + return Err(FsError::EntryExist); + } + } + let temp_file: Arc = Arc::new(LockedINode(RwLock::new(RamFSINode { + parent: Some(Weak::clone(file.this.as_ref().unwrap())), + this: None, + children: Vec::new(), + content: Vec::new(), + extra: Metadata { + dev: 0, + inode: 0, + size: 0, + blk_size: 0, + blocks: 0, + atime: Timespec { sec: 0, nsec: 0 }, + mtime: Timespec { sec: 0, nsec: 0 }, + ctime: Timespec { sec: 0, nsec: 0 }, + type_: type_, + mode: mode as u16, + nlinks: 0, + uid: 0, + gid: 0, + rdev: data, + }, + fs: Weak::clone(&file.fs), + }))); + let mut root = downcast_inode(Arc::as_ref(&temp_file)).0.write(); + root.this = Some(Arc::downgrade(&temp_file)); + drop(root); + file.children + .push((String::from(name), Arc::clone(&temp_file))); + Ok(temp_file) + } else { + Err(FsError::NotDir) + } + } + + fn link(&self, name: &str, other: &Arc) -> Result<()> { + let other_lock = downcast_inode(Arc::as_ref(other)); + // to make sure locking order. + let mut locks = lockMultiple(&vec![&self.0, &other_lock.0]).into_iter(); + + let mut file = locks.next().unwrap(); + let mut other_l = locks.next().unwrap(); + + if file.extra.type_ != FileType::Dir { + return Err(FsError::NotDir); + } + if other_l.extra.type_ == FileType::Dir { + return Err(FsError::IsDir); + } + for (n, _) in file.children.iter() { + if n == name { + return Err(FsError::EntryExist); + } + } + + file.children.push((String::from(name), Arc::clone(other))); + other_l.extra.nlinks += 1; + Ok(()) + } + + fn unlink(&self, name: &str) -> Result<()> { + let mut file = self.0.write(); + if file.extra.type_ != FileType::Dir { + return Err(FsError::NotDir); + } + if name == "." || name == ".." { + return Err(FsError::DirNotEmpty); + } + let mut index: usize = 0; + for (n, f) in file.children.iter() { + if n == name { + let removal_inode = Arc::clone(f); + let other = downcast_inode(Arc::as_ref(&removal_inode)); + if other.0.read().children.len() > 0 { + return Err(FsError::DirNotEmpty); + } + file.children.remove(index); + drop(file); + other.0.write().extra.nlinks -= 1; + return Ok(()); + } else { + index += 1; + } + } + Ok(()) + } + + fn move_(&self, old_name: &str, target: &Arc, new_name: &str) -> Result<()> { + let elem = self.find(old_name)?; + let t = downcast_inode(Arc::as_ref(target)); + if let Err(err) = t.link(new_name, &elem) { + return Err(err); + } else { + if let Err(err) = self.unlink(old_name) { + t.unlink(new_name)?; + return Err(err); + } else { + return Ok(()); + } + } + } + + fn find(&self, name: &str) -> Result> { + let file = self.0.read(); + if file.extra.type_ != FileType::Dir { + return Err(FsError::NotDir); + } + //info!("find it: {} {}", name, file.parent.is_none()); + match name { + "." => Ok(file + .this + .as_ref() + .unwrap() + .upgrade() + .ok_or(FsError::EntryNotFound)?), + ".." => Ok(file + .parent + .as_ref() + .unwrap() + .upgrade() + .ok_or(FsError::EntryNotFound)?), + name => { + for (s, e) in file.children.iter() { + if s == name { + return Ok(Arc::clone(e)); + } + } + Err(FsError::EntryNotFound) + } + } + } + + fn get_entry(&self, id: usize) -> Result { + let file = self.0.read(); + if file.extra.type_ != FileType::Dir { + return Err(FsError::NotDir); + } + + match id { + 0 => Ok(String::from(".")), + 1 => Ok(String::from("..")), + i => { + if let Some((s, _)) = file.children.get(i - 2) { + Ok(s.to_string()) + } else { + Err(FsError::EntryNotFound) + } + } + } + } + + fn io_control(&self, _cmd: u32, _data: usize) -> Result<()> { + Err(FsError::NotSupported) + } + + fn fs(&self) -> Arc { + Weak::upgrade(&self.0.read().fs).unwrap() + } + + fn as_any_ref(&self) -> &dyn Any { + self + } +} + +fn lockMultiple<'a>(locks: &[&'a RwLock]) -> Vec> { + let mut v: Vec<(usize, &'a RwLock, usize)> = Vec::new(); + let mut index: usize = 0; + for l in locks { + v.push((index, *l, l.read().extra.inode)); + index += 1; + } + v.sort_by_key(|l| l.2); + let mut v2: Vec<(usize, RwLockWriteGuard<'a, RamFSINode>)> = v + .into_iter() + .map(|(index, lock, _inode)| (index, lock.write())) + .collect(); + v2.sort_by_key(|(index, _lock)| *index); + v2.into_iter().map(|(_index, lock)| lock).collect() +} + +fn downcast_inode<'a>(inode: &'a dyn INode) -> &'a LockedINode { + inode.as_any_ref().downcast_ref::().unwrap() +}