Merge pull request #3 from benpigchu/ucore-fs-enhance

Ucore fs enhance
master
Wang Runji 6 years ago committed by GitHub
commit 075ea8469f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,3 +1,5 @@
language: rust
rust:
- nightly
script:
- cargo test --verbose -- --test-threads=1

@ -3,9 +3,15 @@ name = "simple-filesystem"
version = "0.0.1"
authors = ["WangRunji <wangrunji0408@163.com>"]
[[bin]]
name = "mksfs"
path = "src/bin/mksfs.rs"
required-features = ["std"]
[dependencies]
bit-vec = { default-features = false, git = "https://github.com/AltSysrq/bit-vec.git" } # default-features contains 'std'
static_assertions = "0.2.5"
[features]
debug_print = []
std = []

@ -0,0 +1,95 @@
extern crate simple_filesystem;
use std::env;
use std::fs;
use std::io::{Read, Write, Result};
use std::path::Path;
use std::mem::uninitialized;
use simple_filesystem::*;
fn main() -> Result<()> {
let args: Vec<_> = env::args().collect();
let cmd = &args[1];
let dir_path = Path::new(&args[2]);
let img_path = Path::new(&args[3]);
match cmd.as_str() {
"zip" => zip(dir_path, img_path),
"unzip" => unzip(dir_path, img_path),
_ => {
println!("USAGE: <zip|unzip> <PATH> <IMG>");
panic!("Invalid command: {}", cmd);
},
}
}
fn zip(path: &Path, img_path: &Path) -> Result<()> {
let img = fs::OpenOptions::new().read(true).write(true).create(true).open(img_path)?;
let sfs = SimpleFileSystem::create(Box::new(img), 0x1000000);
let inode = sfs.root_inode();
zip_dir(path, inode)?;
sfs.sync().expect("Failed to sync");
Ok(())
}
fn zip_dir(path: &Path, inode: INodePtr) -> Result<()> {
let dir = fs::read_dir(path).expect("Failed to open dir");
for entry in dir {
let entry = entry?;
let name_ = entry.file_name();
let name = name_.to_str().unwrap();
let type_ = entry.file_type()?;
if type_.is_file() {
let inode = inode.borrow_mut().create(name, FileType::File).expect("Failed to create INode");
let mut file = fs::File::open(entry.path())?;
inode.borrow_mut().resize(file.metadata().unwrap().len() as usize).expect("Failed to resize INode");
let mut buf: [u8; 4096] = unsafe { uninitialized() };
let mut offset = 0usize;
let mut len = 4096;
while len == 4096 {
len = file.read(&mut buf)?;
inode.borrow().write_at(offset, &buf).expect("Failed to write image");
offset += len;
}
} else if type_.is_dir() {
let inode = inode.borrow_mut().create(name, FileType::Dir).expect("Failed to create INode");
zip_dir(entry.path().as_path(), inode)?;
}
}
Ok(())
}
fn unzip(path: &Path, img_path: &Path) -> Result<()> {
let img = fs::File::open(img_path)?;
let sfs = SimpleFileSystem::open(Box::new(img)).expect("Failed to open sfs");
let inode = sfs.root_inode();
fs::create_dir(&path)?;
unzip_dir(path, inode)
}
fn unzip_dir(path: &Path, inode: INodePtr) -> Result<()> {
let files = inode.borrow().list().expect("Failed to list files from INode");
for name in files.iter().skip(2) {
let inode = inode.borrow().lookup(name.as_str()).expect("Failed to lookup");
let mut path = path.to_path_buf();
path.push(name);
let info = inode.borrow().info().expect("Failed to get file info");
match info.type_ {
FileType::File => {
let mut file = fs::File::create(&path)?;
let mut buf: [u8; 4096] = unsafe { uninitialized() };
let mut offset = 0usize;
let mut len = 4096;
while len == 4096 {
len = inode.borrow().read_at(offset, buf.as_mut()).expect("Failed to read from INode");
file.write(&buf[..len])?;
offset += len;
}
}
FileType::Dir => {
fs::create_dir(&path)?;
unzip_dir(path.as_path(), inode)?;
}
}
}
Ok(())
}

@ -31,3 +31,28 @@ mod tests;
pub use sfs::*;
pub use vfs::*;
pub use blocked_device::BlockedDevice;
#[cfg(any(test, feature = "std"))]
pub mod std_impl {
use std::fs::{File, OpenOptions};
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<usize> {
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<usize> {
let offset = offset as u64;
match self.seek(SeekFrom::Start(offset)) {
Ok(real_offset) if real_offset == offset => self.write(buf).ok(),
_ => None,
}
}
}
}

@ -4,6 +4,7 @@ use core::cell::{RefCell, RefMut};
use core::mem::{uninitialized, size_of};
use core::slice;
use core::fmt::{Debug, Formatter, Error};
use core::any::Any;
use dirty::Dirty;
use structs::*;
use vfs::{self, Device};
@ -96,33 +97,51 @@ impl INode {
}
}
/// Only for Dir
fn get_file_inode_id(&self, name: &str) -> Option<INodeId> {
fn get_file_inode_and_entry_id(&self, name: &str) -> Option<(INodeId,usize)> {
(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
(entry,i)
})
.find(|entry| entry.name.as_ref() == name)
.map(|entry| entry.id as INodeId)
.find(|(entry,id)| entry.name.as_ref() == name)
.map(|(entry,id)| (entry.id as INodeId,id as usize))
}
fn get_file_inode_id(&self, name: &str) -> Option<INodeId> {
self.get_file_inode_and_entry_id(name).map(|(inode_id,entry_id)|{inode_id})
}
/// Init dir content. Insert 2 init entries.
fn init_dir(&mut self, parent: INodeId) -> vfs::Result<()> {
/// This do not init nlinks, please modify the nlinks in the invoker.
fn init_dir_entry(&mut self, parent: INodeId) -> vfs::Result<()> {
use vfs::INode;
let fs = self.fs.upgrade().unwrap();
// Insert entries: '.' '..'
self._resize(BLKSIZE * 2).unwrap();
self._write_at(BLKSIZE * 1, DiskEntry {
id: parent as u32,
name: Str256::from(".."),
}.as_buf()).unwrap();
let id = self.id as u32;
self._write_at(BLKSIZE * 0, DiskEntry {
id,
id: self.id as u32,
name: Str256::from("."),
}.as_buf()).unwrap();
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 remove_dirent_page(&mut self, id: usize) -> vfs::Result<()> {
assert!(id < self.disk_inode.blocks as usize);
let fs = self.fs.upgrade().unwrap();
let to_remove=self.get_disk_block_id(id).unwrap();
let current_last=self.get_disk_block_id(self.disk_inode.blocks as usize -1).unwrap();
self.set_disk_block_id(id,current_last).unwrap();
self.disk_inode.blocks -= 1;
let new_size=self.disk_inode.blocks as usize * BLKSIZE;
self._set_size(new_size);
fs.free_block(to_remove);
Ok(())
}
/// Resize content size, no matter what type it is.
fn _resize(&mut self, len: usize) -> vfs::Result<()> {
assert!(len <= MAX_FILE_SIZE, "file size exceed limit");
@ -145,7 +164,7 @@ impl INode {
}
// clean up
let old_size = self._size();
self.disk_inode.size = len as u32;
self._set_size(len);
self._clean_at(old_size, len).unwrap();
}
Ordering::Less => {
@ -163,7 +182,7 @@ impl INode {
self.disk_inode.blocks = blocks;
}
}
self.disk_inode.size = len as u32;
self._set_size(len);
Ok(())
}
/// Get the actual size of this inode,
@ -175,6 +194,15 @@ impl INode {
_ => unimplemented!(),
}
}
/// Set the ucore compat size of this inode,
/// Size in inode for dir is size of entries
fn _set_size(&mut self,len: usize) {
self.disk_inode.size=match self.disk_inode.type_ {
FileType::Dir => self.disk_inode.blocks as usize * DIRENT_SIZE,
FileType::File => len,
_ => unimplemented!(),
} as u32
}
/// Read/Write content, no matter what type it is
fn _io_at<F>(&self, begin: usize, end: usize, mut f: F) -> vfs::Result<usize>
where F: FnMut(RefMut<Box<Device>>, &BlockRange, usize)
@ -217,6 +245,13 @@ impl INode {
}).unwrap();
Ok(())
}
fn nlinks_inc(&mut self) {
self.disk_inode.nlinks+=1;
}
fn nlinks_dec(&mut self) {
assert!(self.disk_inode.nlinks>0);
self.disk_inode.nlinks-=1;
}
}
impl vfs::INode for INode {
@ -235,12 +270,18 @@ impl vfs::INode for INode {
assert_eq!(self.disk_inode.type_, FileType::File, "write_at is only available on file");
self._write_at(offset, buf)
}
/// the size returned here is logical size(entry num for directory), not the disk space used.
fn info(&self) -> vfs::Result<vfs::FileInfo> {
Ok(vfs::FileInfo {
size: self._size(),
size: match self.disk_inode.type_ {
FileType::File => self.disk_inode.size as usize,
FileType::Dir => self.disk_inode.blocks as usize,
_ => unimplemented!(),
},
mode: 0,
type_: vfs::FileType::from(self.disk_inode.type_.clone()),
blocks: self.disk_inode.blocks as usize,
nlinks: self.disk_inode.nlinks as usize,
})
}
fn sync(&mut self) -> vfs::Result<()> {
@ -259,6 +300,7 @@ impl vfs::INode for INode {
let fs = self.fs.upgrade().unwrap();
let info = self.info().unwrap();
assert_eq!(info.type_, vfs::FileType::Dir);
assert!(info.nlinks>0);
// Ensure the name is not exist
assert!(self.get_file_inode_id(name).is_none(), "file name exist");
@ -274,47 +316,150 @@ impl vfs::INode for INode {
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();
let old_size=self._size();
self._resize(old_size + BLKSIZE).unwrap();
self._write_at(old_size, entry.as_buf()).unwrap();
inode.borrow_mut().nlinks_inc();
if(type_==vfs::FileType::Dir){
inode.borrow_mut().nlinks_inc();//for .
self.nlinks_inc();//for ..
}
Ok(inode)
}
fn lookup(&self, path: &str) -> vfs::Result<Ptr<vfs::INode>> {
fn unlink(&mut self, name: &str) -> vfs::Result<()> {
assert!(name!=".");
assert!(name!="..");
let fs = self.fs.upgrade().unwrap();
let info = self.info().unwrap();
assert_eq!(info.type_, vfs::FileType::Dir);
let inode_and_entry_id = self.get_file_inode_and_entry_id(name);
if inode_and_entry_id.is_none() {
return Err(());
}
let (inode_id,entry_id)=inode_and_entry_id.unwrap();
let inode = fs.get_inode(inode_id);
let type_ = inode.borrow().disk_inode.type_;
if(type_==FileType::Dir){
// only . and ..
assert!(inode.borrow().disk_inode.blocks==2);
}
inode.borrow_mut().nlinks_dec();
if(type_==FileType::Dir){
inode.borrow_mut().nlinks_dec();//for .
self.nlinks_dec();//for ..
}
self.remove_dirent_page(entry_id);
Ok(())
}
fn link(&mut self, name: &str, other:&mut vfs::INode) -> vfs::Result<()> {
let fs = self.fs.upgrade().unwrap();
let info = self.info().unwrap();
assert_eq!(info.type_, vfs::FileType::Dir);
assert!(info.nlinks>0);
assert!(self.get_file_inode_id(name).is_none(), "file name exist");
let child = other.downcast_mut::<INode>().unwrap();
assert!(Rc::ptr_eq(&fs,&child.fs.upgrade().unwrap()));
assert!(child.info().unwrap().type_!=vfs::FileType::Dir);
let entry = DiskEntry {
id: child.id as u32,
name: Str256::from(name),
};
let old_size=self._size();
self._resize(old_size + BLKSIZE).unwrap();
self._write_at(old_size, entry.as_buf()).unwrap();
child.nlinks_inc();
Ok(())
}
fn rename(&mut self, old_name: &str, new_name: &str) -> vfs::Result<()>{
let info = self.info().unwrap();
assert_eq!(info.type_, vfs::FileType::Dir);
assert!(info.nlinks>0);
assert!(self.get_file_inode_id(new_name).is_none(), "file name exist");
let inode_and_entry_id = self.get_file_inode_and_entry_id(old_name);
if inode_and_entry_id.is_none() {
return Err(());
}
let (_,entry_id)=inode_and_entry_id.unwrap();
// in place modify name
let mut entry: DiskEntry = unsafe { uninitialized() };
let entry_pos=entry_id as usize * BLKSIZE;
self._read_at(entry_pos, entry.as_buf_mut()).unwrap();
entry.name=Str256::from(new_name);
self._write_at(entry_pos, entry.as_buf()).unwrap();
Ok(())
}
fn move_(&mut self, old_name: &str,target:&mut vfs::INode, new_name: &str) -> vfs::Result<()>{
let fs = self.fs.upgrade().unwrap();
let info = self.info().unwrap();
assert_eq!(info.type_, vfs::FileType::Dir);
assert!(info.nlinks>0);
let dest = target.downcast_mut::<INode>().unwrap();
assert!(Rc::ptr_eq(&fs,&dest.fs.upgrade().unwrap()));
assert!(dest.info().unwrap().type_==vfs::FileType::Dir);
assert!(dest.info().unwrap().nlinks>0);
assert!(dest.get_file_inode_id(new_name).is_none(), "file name exist");
let (name, rest_path) = match path.find('/') {
None => (path, ""),
Some(pos) => (&path[0..pos], &path[pos + 1..]),
let inode_and_entry_id = self.get_file_inode_and_entry_id(old_name);
if inode_and_entry_id.is_none() {
return Err(());
}
let (inode_id,entry_id)=inode_and_entry_id.unwrap();
let inode = fs.get_inode(inode_id);
let entry = DiskEntry {
id: inode_id as u32,
name: Str256::from(new_name),
};
let old_size=dest._size();
dest._resize(old_size + BLKSIZE).unwrap();
dest._write_at(old_size, entry.as_buf()).unwrap();
self.remove_dirent_page(entry_id);
if(inode.borrow().info().unwrap().type_==vfs::FileType::Dir){
self.nlinks_dec();
dest.nlinks_inc();
}
Ok(())
}
fn find(&self, name: &str) -> vfs::Result<Ptr<vfs::INode>> {
let fs = self.fs.upgrade().unwrap();
let info = self.info().unwrap();
assert_eq!(info.type_, vfs::FileType::Dir);
let inode_id = self.get_file_inode_id(name);
if inode_id.is_none() {
return Err(());
}
let inode = fs.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 => if rest_path == "" { Ok(inode) } else { inode.borrow().lookup(rest_path) },
_ => unimplemented!(),
}
Ok(inode)
}
fn list(&self) -> vfs::Result<Vec<String>> {
fn get_entry(&self,id: usize) -> vfs::Result<String> {
assert_eq!(self.disk_inode.type_, FileType::Dir);
Ok((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();
String::from(entry.name.as_ref())
}).collect())
assert!(id<self.disk_inode.blocks as usize);
use vfs::INode;
let mut entry: DiskEntry = unsafe { uninitialized() };
self._read_at(id as usize * BLKSIZE, entry.as_buf_mut()).unwrap();
Ok(String::from(entry.name.as_ref()))
}
fn fs(&self) -> Weak<vfs::FileSystem> {
self.fs.clone()
}
fn as_any_ref(&self) -> &Any {
self
}
fn as_any_mut(&mut self) -> &mut Any {
self
}
}
impl Drop for INode {
@ -322,6 +467,12 @@ impl Drop for INode {
fn drop(&mut self) {
use vfs::INode;
self.sync().expect("failed to sync");
if(self.disk_inode.nlinks<=0){
let fs = self.fs.upgrade().unwrap();
self._resize(0);
self.disk_inode.sync();
fs.free_block(self.id);
}
}
}
@ -393,7 +544,9 @@ impl SimpleFileSystem {
{
use vfs::INode;
let root = sfs._new_inode(BLKN_ROOT, Dirty::new_dirty(DiskINode::new_dir()));
root.borrow_mut().init_dir(BLKN_ROOT).unwrap();
root.borrow_mut().init_dir_entry(BLKN_ROOT).unwrap();
root.borrow_mut().nlinks_inc();//for .
root.borrow_mut().nlinks_inc();//for ..(root's parent is itself)
root.borrow_mut().sync().unwrap();
}
@ -416,8 +569,15 @@ impl SimpleFileSystem {
let id = self.free_map.borrow_mut().alloc();
if id.is_some() {
self.super_block.borrow_mut().unused_blocks -= 1; // will panic if underflow
id
}else{
self.flush_unreachable_inodes();
let id = self.free_map.borrow_mut().alloc();
if id.is_some() {
self.super_block.borrow_mut().unused_blocks -= 1;
}
id
}
id
}
/// Free a block
fn free_block(&self, block_id: usize) {
@ -462,9 +622,29 @@ impl SimpleFileSystem {
let id = self.alloc_block().unwrap();
let disk_inode = Dirty::new_dirty(DiskINode::new_dir());
let inode = self._new_inode(id, disk_inode);
inode.borrow_mut().init_dir(parent).unwrap();
inode.borrow_mut().init_dir_entry(parent).unwrap();
Ok(inode)
}
fn flush_unreachable_inodes(&self){
let mut inodes = self.inodes.borrow_mut();
let ids: Vec<_> = inodes.keys().cloned().collect();
for id in ids.iter() {
let mut should_remove=false;
// Since non-lexical-lifetime is not stable...
{
let inodeRc=inodes.get(&id).unwrap();
if Rc::strong_count(inodeRc)<=1 {
use vfs::INode;
if inodeRc.borrow().info().unwrap().nlinks==0 {
should_remove=true;
}
}
}
if(should_remove){
inodes.remove(&id);
}
}
}
}
impl vfs::FileSystem for SimpleFileSystem {
@ -488,6 +668,7 @@ impl vfs::FileSystem for SimpleFileSystem {
use vfs::INode;
inode.borrow_mut().sync().unwrap();
}
self.flush_unreachable_inodes();
Ok(())
}

@ -29,6 +29,7 @@ pub struct DiskINode {
/// 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,
@ -176,6 +177,8 @@ pub const BLKBITS: usize = BLKSIZE * 8;
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 = MAX_FNAME_LEN + 1;
/// file types
#[repr(u16)]

@ -9,24 +9,6 @@ use std::rc::Rc;
use std::mem::uninitialized;
use super::structs::{DiskEntry, AsBuf};
impl Device for File {
fn read_at(&mut self, offset: usize, buf: &mut [u8]) -> Option<usize> {
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<usize> {
let offset = offset as u64;
match self.seek(SeekFrom::Start(offset)) {
Ok(real_offset) if real_offset == offset => self.write(buf).ok(),
_ => None,
}
}
}
fn _open_sample_file() -> Rc<SimpleFileSystem> {
fs::copy("sfs.img","test.img").expect("failed to open sfs.img");
let file = OpenOptions::new()
@ -51,9 +33,10 @@ fn open_sample_file() {
#[test]
fn create_new_sfs() {
let sfs = _create_new_sfs();
let root = sfs.root_inode();
}
//#[test]
// #[test]
fn print_root() {
let sfs = _open_sample_file();
let root = sfs.root_inode();
@ -61,6 +44,9 @@ fn print_root() {
let files = root.borrow().list().unwrap();
println!("{:?}", files);
assert_eq!(files[3],root.borrow().get_entry(3).unwrap());
sfs.sync().unwrap();
}
#[test]
@ -74,6 +60,7 @@ fn create_file() {
type_: FileType::File,
mode: 0,
blocks: 0,
nlinks: 1,
});
sfs.sync().unwrap();
@ -123,23 +110,24 @@ fn resize() {
#[test]
fn create_then_lookup() {
let sfs = _create_new_sfs();
let root = sfs.root_inode();
assert!(Rc::ptr_eq(&root.borrow().lookup(".").unwrap(), &root), "failed to find .");
assert!(Rc::ptr_eq(&root.borrow().lookup("..").unwrap(), &root), "failed to find ..");
{
let root = sfs.root_inode();
let file1 = root.borrow_mut().create("file1", FileType::File)
.expect("failed to create file1");
assert!(Rc::ptr_eq(&root.borrow().lookup("file1").unwrap(), &file1), "failed to find file1");
assert!(root.borrow().lookup("file2").is_err(), "found non-existent file");
assert!(Rc::ptr_eq(&root.borrow().lookup(".").unwrap(), &root), "failed to find .");
assert!(Rc::ptr_eq(&root.borrow().lookup("..").unwrap(), &root), "failed to find ..");
let dir1 = root.borrow_mut().create("dir1", FileType::Dir)
.expect("failed to create dir1");
let file2 = dir1.borrow_mut().create("file2", FileType::File)
.expect("failed to create /dir1/file2");
assert!(Rc::ptr_eq(&root.borrow().lookup("dir1/file2").unwrap(), &file2), "failed to find dir1/file1");
assert!(Rc::ptr_eq(&dir1.borrow().lookup("..").unwrap(), &root), "failed to find .. from dir1");
let file1 = root.borrow_mut().create("file1", FileType::File)
.expect("failed to create file1");
assert!(Rc::ptr_eq(&root.borrow().lookup("file1").unwrap(), &file1), "failed to find file1");
assert!(root.borrow().lookup("file2").is_err(), "found non-existent file");
let dir1 = root.borrow_mut().create("dir1", FileType::Dir)
.expect("failed to create dir1");
let file2 = dir1.borrow_mut().create("file2", FileType::File)
.expect("failed to create /dir1/file2");
assert!(Rc::ptr_eq(&root.borrow().lookup("dir1/file2").unwrap(), &file2), "failed to find dir1/file1");
assert!(Rc::ptr_eq(&dir1.borrow().lookup("..").unwrap(), &root), "failed to find .. from dir1");
}
sfs.sync().unwrap();
}
@ -158,10 +146,190 @@ fn rc_layout() {
fn kernel_image_file_create(){
let sfs = _open_sample_file();
let root = sfs.root_inode();
let files_count_before = root.borrow().list().unwrap();
let files_count_before = root.borrow().list().unwrap().len();
root.borrow_mut().create("hello2",FileType::File).unwrap();
let files_count_after = root.borrow().list().unwrap();
assert_eq!(files_count_before+1, files_count_after)
let files_count_after = root.borrow().list().unwrap().len();
assert_eq!(files_count_before+1, files_count_after);
assert!(root.borrow().lookup("hello2").is_ok());
sfs.sync().unwrap();
}
// #[test]
fn kernel_image_file_unlink(){
let sfs = _open_sample_file();
let root = sfs.root_inode();
let files_count_before = root.borrow().list().unwrap().len();
root.borrow_mut().unlink("hello").unwrap();
let files_count_after = root.borrow().list().unwrap().len();
assert_eq!(files_count_before, files_count_after+1);
assert!(root.borrow().lookup("hello").is_err());
sfs.sync().unwrap();
}
// #[test]
fn kernel_image_file_rename(){
let sfs = _open_sample_file();
let root = sfs.root_inode();
let files_count_before = root.borrow().list().unwrap().len();
root.borrow_mut().rename("hello","hello2").unwrap();
let files_count_after = root.borrow().list().unwrap().len();
assert_eq!(files_count_before, files_count_after);
assert!(root.borrow().lookup("hello").is_err());
assert!(root.borrow().lookup("hello2").is_ok());
sfs.sync().unwrap();
}
// #[test]
fn kernel_image_file_move(){
use core::ops::DerefMut;
let sfs = _open_sample_file();
let root = sfs.root_inode();
let files_count_before = root.borrow().list().unwrap().len();
root.borrow_mut().unlink("divzero").unwrap();
let rust_dir = root.borrow_mut().create("rust", FileType::Dir).unwrap();
root.borrow_mut().move_("hello",rust_dir.borrow_mut().deref_mut(),"hello_world").unwrap();
let files_count_after = root.borrow().list().unwrap().len();
assert_eq!(files_count_before, files_count_after+1);
assert!(root.borrow().lookup("hello").is_err());
assert!(root.borrow().lookup("divzero").is_err());
assert!(root.borrow().lookup("rust").is_ok());
assert!(rust_dir.borrow().lookup("hello_world").is_ok());
sfs.sync().unwrap();
}
#[test]
fn hard_link(){
let sfs = _create_new_sfs();
let root = sfs.root_inode();
let file1 = root.borrow_mut().create("file1", FileType::File).unwrap();
use core::ops::DerefMut;
root.borrow_mut().link("file2",file1.borrow_mut().deref_mut()).unwrap();
let file2 = root.borrow().lookup("file2").unwrap();
file1.borrow_mut().resize(100);
assert_eq!(file2.borrow().info().unwrap().size,100);
sfs.sync().unwrap();
}
#[test]
fn nlinks(){
use core::ops::DerefMut;
let sfs = _create_new_sfs();
let root = sfs.root_inode();
// -root
assert_eq!(root.borrow().info().unwrap().nlinks,2);
let file1 = root.borrow_mut().create("file1", FileType::File).unwrap();
// -root
// `-file1 <f1>
assert_eq!(file1.borrow().info().unwrap().nlinks,1);
assert_eq!(root.borrow().info().unwrap().nlinks,2);
let dir1 = root.borrow_mut().create("dir1", FileType::Dir).unwrap();
// -root
// +-dir1
// `-file1 <f1>
assert_eq!(dir1.borrow().info().unwrap().nlinks,2);
assert_eq!(root.borrow().info().unwrap().nlinks,3);
root.borrow_mut().rename("dir1", "dir_1").unwrap();
// -root
// +-dir_1
// `-file1 <f1>
assert_eq!(dir1.borrow().info().unwrap().nlinks,2);
assert_eq!(root.borrow().info().unwrap().nlinks,3);
dir1.borrow_mut().link("file1_",file1.borrow_mut().deref_mut()).unwrap();
// -root
// +-dir_1
// | `-file1_ <f1>
// `-file1 <f1>
assert_eq!(dir1.borrow().info().unwrap().nlinks,2);
assert_eq!(root.borrow().info().unwrap().nlinks,3);
assert_eq!(file1.borrow().info().unwrap().nlinks,2);
let dir2 = root.borrow_mut().create("dir2", FileType::Dir).unwrap();
// -root
// +-dir_1
// | `-file1_ <f1>
// +-dir2
// `-file1 <f1>
assert_eq!(dir1.borrow().info().unwrap().nlinks,2);
assert_eq!(dir2.borrow().info().unwrap().nlinks,2);
assert_eq!(root.borrow().info().unwrap().nlinks,4);
assert_eq!(file1.borrow().info().unwrap().nlinks,2);
root.borrow_mut().rename("file1","file_1").unwrap();
// -root
// +-dir_1
// | `-file1_ <f1>
// +-dir2
// `-file_1 <f1>
assert_eq!(dir1.borrow().info().unwrap().nlinks,2);
assert_eq!(dir2.borrow().info().unwrap().nlinks,2);
assert_eq!(root.borrow().info().unwrap().nlinks,4);
assert_eq!(file1.borrow().info().unwrap().nlinks,2);
root.borrow_mut().move_("file_1",dir2.borrow_mut().deref_mut(),"file__1").unwrap();
// -root
// +-dir_1
// | `-file1_ <f1>
// `-dir2
// `-file__1 <f1>
assert_eq!(dir1.borrow().info().unwrap().nlinks,2);
assert_eq!(dir2.borrow().info().unwrap().nlinks,2);
assert_eq!(root.borrow().info().unwrap().nlinks,4);
assert_eq!(file1.borrow().info().unwrap().nlinks,2);
root.borrow_mut().move_("dir_1",dir2.borrow_mut().deref_mut(),"dir__1").unwrap();
// -root
// `-dir2
// +-dir__1
// | `-file1_ <f1>
// `-file__1 <f1>
assert_eq!(dir1.borrow().info().unwrap().nlinks,2);
assert_eq!(dir2.borrow().info().unwrap().nlinks,3);
assert_eq!(root.borrow().info().unwrap().nlinks,3);
assert_eq!(file1.borrow().info().unwrap().nlinks,2);
dir2.borrow_mut().unlink("file__1").unwrap();
// -root
// `-dir2
// `-dir__1
// `-file1_ <f1>
assert_eq!(file1.borrow().info().unwrap().nlinks,1);
assert_eq!(dir1.borrow().info().unwrap().nlinks,2);
assert_eq!(dir2.borrow().info().unwrap().nlinks,3);
assert_eq!(root.borrow().info().unwrap().nlinks,3);
dir1.borrow_mut().unlink("file1_").unwrap();
// -root
// `-dir2
// `-dir__1
assert_eq!(file1.borrow().info().unwrap().nlinks,0);
assert_eq!(dir1.borrow().info().unwrap().nlinks,2);
assert_eq!(dir2.borrow().info().unwrap().nlinks,3);
assert_eq!(root.borrow().info().unwrap().nlinks,3);
dir2.borrow_mut().unlink("dir__1").unwrap();
// -root
// `-dir2
assert_eq!(file1.borrow().info().unwrap().nlinks,0);
assert_eq!(dir1.borrow().info().unwrap().nlinks,0);
assert_eq!(root.borrow().info().unwrap().nlinks,3);
assert_eq!(dir2.borrow().info().unwrap().nlinks,2);
root.borrow_mut().unlink("dir2").unwrap();
// -root
assert_eq!(file1.borrow().info().unwrap().nlinks,0);
assert_eq!(dir1.borrow().info().unwrap().nlinks,0);
assert_eq!(root.borrow().info().unwrap().nlinks,2);
assert_eq!(dir2.borrow().info().unwrap().nlinks,0);
sfs.sync().unwrap();
}

@ -3,6 +3,7 @@ use core::cell::RefCell;
use core::mem::size_of;
use core;
use core::fmt::Debug;
use core::any::Any;
/// Interface for FS to read & write
/// TODO: use std::io::{Read, Write}
@ -12,29 +13,86 @@ pub trait Device {
}
/// Abstract operations on a inode.
pub trait INode: Debug {
pub trait INode: Debug + Any {
fn open(&mut self, flags: u32) -> Result<()>;
fn close(&mut self) -> Result<()>;
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize>;
fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize>;
fn info(&self) -> Result<FileInfo>;
fn sync(&mut self) -> Result<()>;
// fn name_file(&mut self) -> Result<()>;
// fn reclaim(&mut self) -> Result<()>;
fn resize(&mut self, len: usize) -> Result<()>;
fn create(&mut self, name: &str, type_: FileType) -> Result<INodePtr>;
fn lookup(&self, path: &str) -> Result<INodePtr>;
fn list(&self) -> Result<Vec<String>>;
fn unlink(&mut self, name: &str) -> Result<()>;
/// user of the vfs api should call borrow_mut by itself
fn link(&mut self, name: &str, other:&mut INode) -> Result<()>;
fn rename(&mut self, old_name: &str, new_name: &str) -> Result<()>;
// when self==target use rename instead since it's not possible to have two mut_ref at the same time.
fn move_(&mut self, old_name: &str,target:&mut INode, new_name: &str) -> Result<()>;
/// lookup with only one layer
fn find(&self, name: &str) -> Result<INodePtr>;
/// like list()[id]
/// only get one item in list, often faster than list
fn get_entry(&self,id: usize) -> Result<String>;
// fn io_ctrl(&mut self, op: u32, data: &[u8]) -> Result<()>;
fn fs(&self) -> Weak<FileSystem>;
/// this is used to implement dynamics cast
/// simply return self in the implement of the function
fn as_any_ref(&self) -> &Any;
/// this is used to implement dynamics cast
/// simply return self in the implement of the function
fn as_any_mut(&mut self) -> &mut Any;
}
impl INode{
pub fn downcast_ref<T:INode>(&self) -> Option<&T> {
self.as_any_ref().downcast_ref::<T>()
}
pub fn downcast_mut<T:INode>(&mut self) -> Option<&mut T> {
self.as_any_mut().downcast_mut::<T>()
}
pub fn list(&self) -> Result<Vec<String>> {
let info=self.info().unwrap();
assert_eq!(info.type_, FileType::Dir);
Ok((0..info.size).map(|i|{
self.get_entry(i).unwrap()
}).collect())
}
pub fn lookup(&self, path: &str) -> Result<INodePtr> {
if(self.info().unwrap().type_ != FileType::Dir){
return Err(())
}
let mut result=self.find(".").unwrap();
let mut rest_path=path;
while rest_path != "" {
if(result.borrow().info().unwrap().type_ != FileType::Dir){
return Err(())
}
let mut name;
match rest_path.find('/') {
None => {name=rest_path; rest_path=""},
Some(pos) => {name=&rest_path[0..pos]; rest_path=&rest_path[pos + 1..]},
};
let found=result.borrow().find(name);
match found {
Err(_) => return Err(()),
Ok(inode) => result=inode,
};
}
Ok(result)
}
}
#[derive(Debug, Eq, PartialEq)]
pub struct FileInfo {
// Note: for normal file size is the actuate file size
// for directory this is count of dirent.
pub size: usize,
pub mode: u32,
pub type_: FileType,
pub blocks: usize,
// Note: different from linux, "." and ".." count in nlinks
// this is same as original ucore.
pub nlinks: usize,
}
#[derive(Debug, Eq, PartialEq)]

Loading…
Cancel
Save