From 075e416a7be6f2e6ec30222271036beb73aef0d7 Mon Sep 17 00:00:00 2001 From: WangRunji Date: Wed, 23 May 2018 21:56:29 +0800 Subject: [PATCH 01/14] Some code for 'mksfs'. Move 'impl Device for std::fs::File' out of test mod. --- Cargo.toml | 10 ++++++-- src/bin/mksfs.rs | 61 ++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 30 +++++++++++++++++++++--- src/sfs.rs | 1 - src/tests.rs | 18 -------------- 5 files changed, 96 insertions(+), 24 deletions(-) create mode 100644 src/bin/mksfs.rs diff --git a/Cargo.toml b/Cargo.toml index c64817d..8c256d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,13 +4,18 @@ version = "0.0.1" authors = ["WangRunji "] [lib] +name = "lib" #crate-type = ["staticlib"] +[[bin]] +name = "mksfs" +path = "src/bin/mksfs.rs" +required-features = ["std"] + [profile.dev] panic = 'abort' # prevent `_Unwind_Resume` link error [dependencies] -spin = "0.4" bit-set = { default-features = false, version = "0.5" } # default-features contains 'std' static_assertions = "0.2.5" @@ -19,4 +24,5 @@ bitflags = "1.0" [features] debug_print = [] -ucore = [] \ No newline at end of file +ucore = [] +std = [] \ No newline at end of file diff --git a/src/bin/mksfs.rs b/src/bin/mksfs.rs new file mode 100644 index 0000000..17dc102 --- /dev/null +++ b/src/bin/mksfs.rs @@ -0,0 +1,61 @@ +extern crate lib; + +use std::env; +use std::fs; +use std::io::Read; +use std::path::Path; +use lib::*; + +fn main () { + println!("USAGE: "); + 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), + _ => panic!("Invalid command: {}", cmd), + } +} + +fn zip(path: &Path, img_path: &Path) { + use lib::std_impl; + + let mut img = fs::File::create(img_path) + .expect(format!("Failed to create file: {:?}", img_path).as_str()); + let sfs = SimpleFileSystem::create(Box::new(img), 0x1000000); + let inode = sfs.root_inode(); + zip_dir(path, inode); +} + +fn zip_dir(path: &Path, inode: INodePtr) { + println!("{:?}", path); + let dir = fs::read_dir(path).expect("Failed to open dir"); + for entry in dir { + let entry = entry.unwrap(); + let name_ = entry.file_name(); + let name = name_.to_str().unwrap(); + let type_ = entry.file_type().unwrap(); + 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()) + .expect("Failed to open file"); + let mut buf = Vec::::new(); + file.read_to_end(&mut buf) + .expect("Failed to read file"); + inode.borrow_mut().write_at(0, buf.as_ref()) + .expect("Failed to write image"); + println!("{:?}", entry.path()); + } 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); + } + } +} + +fn unzip(path: &Path, img_path: &Path) { + +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index ffef679..2a504cd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,10 +3,9 @@ #![cfg_attr(feature = "ucore", feature(allocator_api, global_allocator, lang_items))] #![no_std] -#[cfg(test)] +#[cfg(any(test, feature = "std"))] #[macro_use] extern crate std; -extern crate spin; #[macro_use] extern crate alloc; extern crate bit_set; @@ -37,4 +36,29 @@ pub use vfs::*; #[cfg(feature = "ucore")] #[global_allocator] -pub static UCORE_ALLOCATOR: c_interface::UcoreAllocator = c_interface::UcoreAllocator{}; \ No newline at end of file +pub static UCORE_ALLOCATOR: c_interface::UcoreAllocator = c_interface::UcoreAllocator{}; + +#[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 { + 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/src/sfs.rs b/src/sfs.rs index cbf7c89..dddabef 100644 --- a/src/sfs.rs +++ b/src/sfs.rs @@ -1,4 +1,3 @@ -use spin::Mutex; use bit_set::BitSet; use alloc::{boxed::Box, Vec, BTreeMap, rc::{Rc, Weak}, String}; use core::cell::{RefCell, RefMut}; diff --git a/src/tests.rs b/src/tests.rs index ce19b5a..3772d12 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -8,24 +8,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 { - 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, - } - } -} - fn _open_sample_file() -> Rc { let file = File::open("sfs.img") .expect("failed to open sfs.img"); From 8d111eb80071e80f5880d209e77207021dbdbe67 Mon Sep 17 00:00:00 2001 From: WangRunji Date: Sun, 27 May 2018 22:46:12 +0800 Subject: [PATCH 02/14] Basically finish mksfs. But still bugs. --- src/bin/mksfs.rs | 90 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 62 insertions(+), 28 deletions(-) diff --git a/src/bin/mksfs.rs b/src/bin/mksfs.rs index 17dc102..a959f42 100644 --- a/src/bin/mksfs.rs +++ b/src/bin/mksfs.rs @@ -2,12 +2,12 @@ extern crate lib; use std::env; use std::fs; -use std::io::Read; +use std::io::{Read, Write, Result}; use std::path::Path; +use std::mem::uninitialized; use lib::*; -fn main () { - println!("USAGE: "); +fn main() -> Result<()> { let args: Vec<_> = env::args().collect(); let cmd = &args[1]; let dir_path = Path::new(&args[2]); @@ -15,47 +15,81 @@ fn main () { match cmd.as_str() { "zip" => zip(dir_path, img_path), "unzip" => unzip(dir_path, img_path), - _ => panic!("Invalid command: {}", cmd), + _ => { + println!("USAGE: "); + panic!("Invalid command: {}", cmd); + }, } } -fn zip(path: &Path, img_path: &Path) { - use lib::std_impl; - - let mut img = fs::File::create(img_path) - .expect(format!("Failed to create file: {:?}", img_path).as_str()); +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); + zip_dir(path, inode)?; + sfs.sync().expect("Failed to sync"); + Ok(()) } -fn zip_dir(path: &Path, inode: INodePtr) { - println!("{:?}", path); +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.unwrap(); + let entry = entry?; let name_ = entry.file_name(); let name = name_.to_str().unwrap(); - let type_ = entry.file_type().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()) - .expect("Failed to open file"); - let mut buf = Vec::::new(); - file.read_to_end(&mut buf) - .expect("Failed to read file"); - inode.borrow_mut().write_at(0, buf.as_ref()) - .expect("Failed to write image"); - println!("{:?}", entry.path()); + 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); + 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) { +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(()) } \ No newline at end of file From 289f627be8f56a99e243d26ef9b8b5b84cb3bbe2 Mon Sep 17 00:00:00 2001 From: Ben Pig Chu Date: Mon, 22 Oct 2018 20:43:39 +0800 Subject: [PATCH 03/14] add unlink interface --- src/sfs.rs | 37 +++++++++++++++++++++++++++++++++---- src/vfs.rs | 2 ++ 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/sfs.rs b/src/sfs.rs index 5982280..8823159 100644 --- a/src/sfs.rs +++ b/src/sfs.rs @@ -96,16 +96,19 @@ impl INode { } } /// Only for Dir - fn get_file_inode_id(&self, name: &str) -> Option { + 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 { + 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<()> { @@ -279,6 +282,30 @@ impl vfs::INode for INode { Ok(inode) } + fn unlink(&mut self, name: &str) -> vfs::Result<()> { + 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); + } + + //todo: actually unlink: + //1. move the last entry to the original place + //2. free the disc space + + unimplemented!(); + } fn lookup(&self, path: &str) -> vfs::Result> { let fs = self.fs.upgrade().unwrap(); let info = self.info().unwrap(); @@ -322,6 +349,7 @@ impl Drop for INode { fn drop(&mut self) { use vfs::INode; self.sync().expect("failed to sync"); + //TODO: check nlink and atually remove } } @@ -488,6 +516,7 @@ impl vfs::FileSystem for SimpleFileSystem { use vfs::INode; inode.borrow_mut().sync().unwrap(); } + //TODO: drop no ref inode Ok(()) } diff --git a/src/vfs.rs b/src/vfs.rs index ad2b560..14812dd 100644 --- a/src/vfs.rs +++ b/src/vfs.rs @@ -23,6 +23,8 @@ pub trait INode: Debug { // fn reclaim(&mut self) -> Result<()>; fn resize(&mut self, len: usize) -> Result<()>; fn create(&mut self, name: &str, type_: FileType) -> Result; + // since we do not have link, unlink==remove + fn unlink(&mut self, name: &str) -> Result<()>; fn lookup(&self, path: &str) -> Result; fn list(&self) -> Result>; // fn io_ctrl(&mut self, op: u32, data: &[u8]) -> Result<()>; From 77cea66f7e7ab9fdcc61b70bb89b3af6cbb70421 Mon Sep 17 00:00:00 2001 From: Ben Pig Chu Date: Tue, 23 Oct 2018 16:28:59 +0800 Subject: [PATCH 04/14] get the nlinks correct when creating inodes. --- src/sfs.rs | 26 +++++++++++++++++++++----- src/tests.rs | 23 ++++++++++++++++++++--- src/vfs.rs | 1 + 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/sfs.rs b/src/sfs.rs index 8823159..e7eda15 100644 --- a/src/sfs.rs +++ b/src/sfs.rs @@ -111,17 +111,18 @@ impl INode { 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(()) @@ -220,6 +221,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 { @@ -244,6 +252,7 @@ impl vfs::INode for INode { 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<()> { @@ -279,6 +288,11 @@ impl vfs::INode for INode { }; self._resize(info.size + BLKSIZE).unwrap(); self._write_at(info.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) } @@ -421,7 +435,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(); } @@ -490,7 +506,7 @@ 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) } } diff --git a/src/tests.rs b/src/tests.rs index f09e89f..6abffb1 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -51,6 +51,7 @@ fn open_sample_file() { #[test] fn create_new_sfs() { let sfs = _create_new_sfs(); + let root = sfs.root_inode(); } //#[test] @@ -74,6 +75,7 @@ fn create_file() { type_: FileType::File, mode: 0, blocks: 0, + nlinks: 1, }); sfs.sync().unwrap(); @@ -158,10 +160,25 @@ 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); + + sfs.sync().unwrap(); +} + +#[test] +fn nlinks(){ + let sfs = _create_new_sfs(); + let root = sfs.root_inode(); + assert_eq!(root.borrow().info().unwrap().nlinks,2); + let file1 = root.borrow_mut().create("file1", FileType::File).unwrap(); + 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(); + assert_eq!(dir1.borrow().info().unwrap().nlinks,2); + assert_eq!(root.borrow().info().unwrap().nlinks,3); sfs.sync().unwrap(); } \ No newline at end of file diff --git a/src/vfs.rs b/src/vfs.rs index 14812dd..e9b8375 100644 --- a/src/vfs.rs +++ b/src/vfs.rs @@ -37,6 +37,7 @@ pub struct FileInfo { pub mode: u32, pub type_: FileType, pub blocks: usize, + pub nlinks: usize, } #[derive(Debug, Eq, PartialEq)] From 36277c5769ff083ec4149f4cda9bbbe29b9ee77a Mon Sep 17 00:00:00 2001 From: Ben Pig Chu Date: Tue, 23 Oct 2018 19:28:23 +0800 Subject: [PATCH 05/14] basic unlink implement --- src/sfs.rs | 21 ++++++++++++++++----- src/structs.rs | 1 + src/tests.rs | 18 ++++++++++++++++++ src/vfs.rs | 2 ++ 4 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/sfs.rs b/src/sfs.rs index e7eda15..9d9e471 100644 --- a/src/sfs.rs +++ b/src/sfs.rs @@ -271,6 +271,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"); @@ -297,6 +298,8 @@ impl vfs::INode for INode { Ok(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); @@ -313,12 +316,20 @@ impl vfs::INode for INode { // 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 .. + } + let old_block_count=self.disk_inode.blocks as usize; + if(entry_id!=old_block_count - 1){//not last entry + let mut entry: DiskEntry = unsafe { uninitialized() }; + self._read_at((old_block_count - 1) * BLKSIZE, entry.as_buf_mut()).unwrap(); + self._write_at(entry_id * BLKSIZE, entry.as_buf()).unwrap(); + } + self._resize((old_block_count - 1) * BLKSIZE).unwrap(); - //todo: actually unlink: - //1. move the last entry to the original place - //2. free the disc space - - unimplemented!(); + Ok(()) } fn lookup(&self, path: &str) -> vfs::Result> { let fs = self.fs.upgrade().unwrap(); diff --git a/src/structs.rs b/src/structs.rs index 2b07667..f9596f5 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -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, diff --git a/src/tests.rs b/src/tests.rs index 6abffb1..a33294b 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -168,6 +168,18 @@ fn kernel_image_file_create(){ 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"); + let files_count_after = root.borrow().list().unwrap().len(); + assert_eq!(files_count_before, files_count_after+1); + + sfs.sync().unwrap(); +} + #[test] fn nlinks(){ let sfs = _create_new_sfs(); @@ -179,6 +191,12 @@ fn nlinks(){ let dir1 = root.borrow_mut().create("dir1", FileType::Dir).unwrap(); assert_eq!(dir1.borrow().info().unwrap().nlinks,2); assert_eq!(root.borrow().info().unwrap().nlinks,3); + root.borrow_mut().unlink("file1").unwrap(); + assert_eq!(file1.borrow().info().unwrap().nlinks,0); + assert_eq!(root.borrow().info().unwrap().nlinks,3); + root.borrow_mut().unlink("dir1").unwrap(); + assert_eq!(dir1.borrow().info().unwrap().nlinks,0); + assert_eq!(root.borrow().info().unwrap().nlinks,2); sfs.sync().unwrap(); } \ No newline at end of file diff --git a/src/vfs.rs b/src/vfs.rs index e9b8375..1b5396c 100644 --- a/src/vfs.rs +++ b/src/vfs.rs @@ -37,6 +37,8 @@ pub struct FileInfo { 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, } From 0e4f714d95e337bb5a4300dfdeb295c6c8127795 Mon Sep 17 00:00:00 2001 From: Ben Pig Chu Date: Wed, 24 Oct 2018 14:20:05 +0800 Subject: [PATCH 06/14] free disk space of unlinked no referenced inode on drop --- src/sfs.rs | 57 +++++++++++++++++++++++++++++++++++++++++++--------- src/tests.rs | 3 ++- 2 files changed, 49 insertions(+), 11 deletions(-) diff --git a/src/sfs.rs b/src/sfs.rs index 9d9e471..22c5dee 100644 --- a/src/sfs.rs +++ b/src/sfs.rs @@ -127,6 +127,19 @@ impl INode { }.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; + self.disk_inode.size -= BLKSIZE as u32; + 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"); @@ -321,13 +334,7 @@ impl vfs::INode for INode { inode.borrow_mut().nlinks_dec();//for . self.nlinks_dec();//for .. } - let old_block_count=self.disk_inode.blocks as usize; - if(entry_id!=old_block_count - 1){//not last entry - let mut entry: DiskEntry = unsafe { uninitialized() }; - self._read_at((old_block_count - 1) * BLKSIZE, entry.as_buf_mut()).unwrap(); - self._write_at(entry_id * BLKSIZE, entry.as_buf()).unwrap(); - } - self._resize((old_block_count - 1) * BLKSIZE).unwrap(); + self.remove_dirent_page(entry_id); Ok(()) } @@ -374,7 +381,12 @@ impl Drop for INode { fn drop(&mut self) { use vfs::INode; self.sync().expect("failed to sync"); - //TODO: check nlink and atually remove + if(self.disk_inode.nlinks<=0){ + let fs = self.fs.upgrade().unwrap(); + self._resize(0); + self.disk_inode.sync(); + fs.free_block(self.id); + } } } @@ -471,8 +483,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) { @@ -520,6 +539,24 @@ impl SimpleFileSystem { 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; + } + } + } + inodes.remove(&id); + } + } } impl vfs::FileSystem for SimpleFileSystem { @@ -543,7 +580,7 @@ impl vfs::FileSystem for SimpleFileSystem { use vfs::INode; inode.borrow_mut().sync().unwrap(); } - //TODO: drop no ref inode + self.flush_unreachable_inodes(); Ok(()) } diff --git a/src/tests.rs b/src/tests.rs index a33294b..c550f2d 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -173,9 +173,10 @@ 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"); + 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(); } From 9d0b3d1891330324bfaf3440c78a79d55009127b Mon Sep 17 00:00:00 2001 From: Ben Pig Chu Date: Wed, 24 Oct 2018 19:36:43 +0800 Subject: [PATCH 07/14] store and return ucore compatible size --- src/sfs.rs | 28 ++++++++++++++++++++++------ src/structs.rs | 2 ++ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/sfs.rs b/src/sfs.rs index 22c5dee..efd0dad 100644 --- a/src/sfs.rs +++ b/src/sfs.rs @@ -136,7 +136,8 @@ impl INode { 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; - self.disk_inode.size -= BLKSIZE as u32; + let new_size=self.disk_inode.blocks as usize * BLKSIZE; + self._set_size(new_size); fs.free_block(to_remove); Ok(()) } @@ -162,7 +163,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 => { @@ -180,7 +181,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, @@ -192,6 +193,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(&self, begin: usize, end: usize, mut f: F) -> vfs::Result where F: FnMut(RefMut>, &BlockRange, usize) @@ -259,9 +269,14 @@ 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 { 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, @@ -300,8 +315,9 @@ 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 . diff --git a/src/structs.rs b/src/structs.rs index f9596f5..9001498 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -177,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)] From 4a97d89b8f58ab8c8d4443db43baaf7074beb926 Mon Sep 17 00:00:00 2001 From: Ben Pig Chu Date: Fri, 26 Oct 2018 22:36:13 +0800 Subject: [PATCH 08/14] implement hard link --- src/sfs.rs | 26 ++++++++++++++++++++++++++ src/tests.rs | 24 ++++++++++++++++++++++++ src/vfs.rs | 21 +++++++++++++++++++-- 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/src/sfs.rs b/src/sfs.rs index efd0dad..4c4d5f2 100644 --- a/src/sfs.rs +++ b/src/sfs.rs @@ -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}; @@ -354,6 +355,25 @@ impl vfs::INode for INode { 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::().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 lookup(&self, path: &str) -> vfs::Result> { let fs = self.fs.upgrade().unwrap(); let info = self.info().unwrap(); @@ -390,6 +410,12 @@ impl vfs::INode for INode { fn fs(&self) -> Weak { self.fs.clone() } + fn as_any_ref(&self) -> &Any { + self + } + fn as_any_mut(&mut self) -> &mut Any { + self + } } impl Drop for INode { diff --git a/src/tests.rs b/src/tests.rs index c550f2d..f2ac9f6 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -181,6 +181,21 @@ fn kernel_image_file_unlink(){ 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_mut().lookup("file2").unwrap(); + file1.borrow_mut().resize(100); + assert_eq!(file2.borrow().info().unwrap().size,100); + + sfs.sync().unwrap(); +} + #[test] fn nlinks(){ let sfs = _create_new_sfs(); @@ -192,8 +207,17 @@ fn nlinks(){ let dir1 = root.borrow_mut().create("dir1", FileType::Dir).unwrap(); assert_eq!(dir1.borrow().info().unwrap().nlinks,2); assert_eq!(root.borrow().info().unwrap().nlinks,3); + use core::ops::DerefMut; + dir1.borrow_mut().link("file2",file1.borrow_mut().deref_mut()).unwrap(); + assert_eq!(dir1.borrow().info().unwrap().nlinks,2); + assert_eq!(root.borrow().info().unwrap().nlinks,3); + assert_eq!(file1.borrow().info().unwrap().nlinks,2); root.borrow_mut().unlink("file1").unwrap(); + assert_eq!(file1.borrow().info().unwrap().nlinks,1); + assert_eq!(root.borrow().info().unwrap().nlinks,3); + dir1.borrow_mut().unlink("file2").unwrap(); assert_eq!(file1.borrow().info().unwrap().nlinks,0); + assert_eq!(dir1.borrow().info().unwrap().nlinks,2); assert_eq!(root.borrow().info().unwrap().nlinks,3); root.borrow_mut().unlink("dir1").unwrap(); assert_eq!(dir1.borrow().info().unwrap().nlinks,0); diff --git a/src/vfs.rs b/src/vfs.rs index 1b5396c..84e7f9d 100644 --- a/src/vfs.rs +++ b/src/vfs.rs @@ -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,7 +13,7 @@ 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; @@ -23,12 +24,28 @@ pub trait INode: Debug { // fn reclaim(&mut self) -> Result<()>; fn resize(&mut self, len: usize) -> Result<()>; fn create(&mut self, name: &str, type_: FileType) -> Result; - // since we do not have link, unlink==remove 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 lookup(&self, path: &str) -> Result; fn list(&self) -> Result>; // fn io_ctrl(&mut self, op: u32, data: &[u8]) -> Result<()>; fn fs(&self) -> Weak; + /// 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(&self) -> Option<&T> { + self.as_any_ref().downcast_ref::() + } + pub fn downcast_mut(&mut self) -> Option<&mut T> { + self.as_any_mut().downcast_mut::() + } } #[derive(Debug, Eq, PartialEq)] From c744a3aea2cead8c53dc5e33d62ad74855ab128c Mon Sep 17 00:00:00 2001 From: Ben Pig Chu Date: Sat, 27 Oct 2018 01:16:38 +0800 Subject: [PATCH 09/14] get_entry(), as a different design from list() --- src/sfs.rs | 8 ++++++++ src/tests.rs | 5 ++++- src/vfs.rs | 3 +++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/sfs.rs b/src/sfs.rs index 4c4d5f2..abb201e 100644 --- a/src/sfs.rs +++ b/src/sfs.rs @@ -396,6 +396,14 @@ impl vfs::INode for INode { _ => unimplemented!(), } } + fn get_entry(&self,id: usize) -> vfs::Result { + assert_eq!(self.disk_inode.type_, FileType::Dir); + assert!(id vfs::Result> { assert_eq!(self.disk_inode.type_, FileType::Dir); diff --git a/src/tests.rs b/src/tests.rs index f2ac9f6..f0a6d9b 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -54,7 +54,7 @@ fn create_new_sfs() { let root = sfs.root_inode(); } -//#[test] +// #[test] fn print_root() { let sfs = _open_sample_file(); let root = sfs.root_inode(); @@ -62,6 +62,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] diff --git a/src/vfs.rs b/src/vfs.rs index 84e7f9d..eadd870 100644 --- a/src/vfs.rs +++ b/src/vfs.rs @@ -29,6 +29,9 @@ pub trait INode: Debug + Any { fn link(&mut self, name: &str, other:&mut INode) -> Result<()>; fn lookup(&self, path: &str) -> Result; fn list(&self) -> Result>; + /// like list()[id] + /// only get one item in list, often faster than list + fn get_entry(&self,id: usize) -> Result; // fn io_ctrl(&mut self, op: u32, data: &[u8]) -> Result<()>; fn fs(&self) -> Weak; /// this is used to implement dynamics cast From e640f7813468211a0e84416683bbf2f171936a6c Mon Sep 17 00:00:00 2001 From: Ben Pig Chu Date: Sat, 27 Oct 2018 01:53:15 +0800 Subject: [PATCH 10/14] untested single layer lookup named find --- src/sfs.rs | 11 +++++++++++ src/vfs.rs | 2 ++ 2 files changed, 13 insertions(+) diff --git a/src/sfs.rs b/src/sfs.rs index abb201e..e505f28 100644 --- a/src/sfs.rs +++ b/src/sfs.rs @@ -396,6 +396,17 @@ impl vfs::INode for INode { _ => unimplemented!(), } } + fn find(&self, name: &str) -> vfs::Result> { + 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()); + Ok(inode) + } fn get_entry(&self,id: usize) -> vfs::Result { assert_eq!(self.disk_inode.type_, FileType::Dir); assert!(id Result<()>; fn lookup(&self, path: &str) -> Result; + /// lookup with only one layer + fn find(&self, name: &str) -> Result; fn list(&self) -> Result>; /// like list()[id] /// only get one item in list, often faster than list From 9c0c3e0d8306a75c9697208e48e5fd5bd6645773 Mon Sep 17 00:00:00 2001 From: Ben Pig Chu Date: Mon, 29 Oct 2018 16:06:58 +0800 Subject: [PATCH 11/14] move the list and lookup implement to the vfs level --- src/sfs.rs | 33 --------------------------------- src/tests.rs | 37 +++++++++++++++++++------------------ src/vfs.rs | 35 +++++++++++++++++++++++++++++++++-- 3 files changed, 52 insertions(+), 53 deletions(-) diff --git a/src/sfs.rs b/src/sfs.rs index e505f28..d9dcabb 100644 --- a/src/sfs.rs +++ b/src/sfs.rs @@ -374,28 +374,6 @@ impl vfs::INode for INode { child.nlinks_inc(); Ok(()) } - fn lookup(&self, path: &str) -> vfs::Result> { - let fs = self.fs.upgrade().unwrap(); - let info = self.info().unwrap(); - assert_eq!(info.type_, vfs::FileType::Dir); - - 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.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!(), - } - } fn find(&self, name: &str) -> vfs::Result> { let fs = self.fs.upgrade().unwrap(); let info = self.info().unwrap(); @@ -415,17 +393,6 @@ impl vfs::INode for INode { self._read_at(id as usize * BLKSIZE, entry.as_buf_mut()).unwrap(); Ok(String::from(entry.name.as_ref())) } - fn list(&self) -> vfs::Result> { - 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()) - } fn fs(&self) -> Weak { self.fs.clone() } diff --git a/src/tests.rs b/src/tests.rs index f0a6d9b..f8f4a82 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -128,23 +128,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 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"); - + { + 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 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(); } @@ -192,7 +193,7 @@ fn hard_link(){ 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_mut().lookup("file2").unwrap(); + let file2 = root.borrow().lookup("file2").unwrap(); file1.borrow_mut().resize(100); assert_eq!(file2.borrow().info().unwrap().size,100); diff --git a/src/vfs.rs b/src/vfs.rs index f152941..15c4968 100644 --- a/src/vfs.rs +++ b/src/vfs.rs @@ -27,10 +27,9 @@ pub trait INode: Debug + Any { 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 lookup(&self, path: &str) -> Result; + // fn lookup(&self, path: &str) -> Result; /// lookup with only one layer fn find(&self, name: &str) -> Result; - fn list(&self) -> Result>; /// like list()[id] /// only get one item in list, often faster than list fn get_entry(&self,id: usize) -> Result; @@ -51,10 +50,42 @@ impl INode{ pub fn downcast_mut(&mut self) -> Option<&mut T> { self.as_any_mut().downcast_mut::() } + pub fn list(&self) -> Result> { + 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 { + 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, From 9026395afc670553deac765dc9a1d03c3f3ae035 Mon Sep 17 00:00:00 2001 From: Ben Pig Chu Date: Fri, 2 Nov 2018 11:22:47 +0800 Subject: [PATCH 12/14] rename dirent --- src/sfs.rs | 31 +++++++++++++++++++++++++++++++ src/tests.rs | 18 +++++++++++++++++- src/vfs.rs | 6 +++--- 3 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/sfs.rs b/src/sfs.rs index d9dcabb..e9ae51c 100644 --- a/src/sfs.rs +++ b/src/sfs.rs @@ -374,6 +374,37 @@ impl vfs::INode for INode { 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); + + 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::().unwrap(); + assert!(Rc::ptr_eq(&fs,&dest.fs.upgrade().unwrap())); + assert!(dest.info().unwrap().type_!=vfs::FileType::Dir); + assert!(dest.info().unwrap().nlinks>0); + unimplemented!() + } fn find(&self, name: &str) -> vfs::Result> { let fs = self.fs.upgrade().unwrap(); let info = self.info().unwrap(); diff --git a/src/tests.rs b/src/tests.rs index 00d45d5..d514e99 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -168,6 +168,19 @@ fn kernel_image_file_unlink(){ } +// #[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()); + + sfs.sync().unwrap(); +} + #[test] fn hard_link(){ let sfs = _create_new_sfs(); @@ -193,6 +206,9 @@ fn nlinks(){ let dir1 = root.borrow_mut().create("dir1", FileType::Dir).unwrap(); assert_eq!(dir1.borrow().info().unwrap().nlinks,2); assert_eq!(root.borrow().info().unwrap().nlinks,3); + root.borrow_mut().rename("dir1", "dir_1").unwrap(); + assert_eq!(dir1.borrow().info().unwrap().nlinks,2); + assert_eq!(root.borrow().info().unwrap().nlinks,3); use core::ops::DerefMut; dir1.borrow_mut().link("file2",file1.borrow_mut().deref_mut()).unwrap(); assert_eq!(dir1.borrow().info().unwrap().nlinks,2); @@ -205,7 +221,7 @@ fn nlinks(){ assert_eq!(file1.borrow().info().unwrap().nlinks,0); assert_eq!(dir1.borrow().info().unwrap().nlinks,2); assert_eq!(root.borrow().info().unwrap().nlinks,3); - root.borrow_mut().unlink("dir1").unwrap(); + root.borrow_mut().unlink("dir_1").unwrap(); assert_eq!(dir1.borrow().info().unwrap().nlinks,0); assert_eq!(root.borrow().info().unwrap().nlinks,2); diff --git a/src/vfs.rs b/src/vfs.rs index 15c4968..f04fb54 100644 --- a/src/vfs.rs +++ b/src/vfs.rs @@ -20,14 +20,14 @@ pub trait INode: Debug + Any { fn write_at(&self, offset: usize, buf: &[u8]) -> Result; fn info(&self) -> Result; 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; 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 lookup(&self, path: &str) -> 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; /// like list()[id] From 5462ccb300de138c99065c3ed5871da6f96288ec Mon Sep 17 00:00:00 2001 From: Ben Pig Chu Date: Fri, 2 Nov 2018 18:35:43 +0800 Subject: [PATCH 13/14] move file between dir --- src/sfs.rs | 35 ++++++++++++++-- src/tests.rs | 116 ++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 143 insertions(+), 8 deletions(-) diff --git a/src/sfs.rs b/src/sfs.rs index e9ae51c..5e6db36 100644 --- a/src/sfs.rs +++ b/src/sfs.rs @@ -379,6 +379,8 @@ impl vfs::INode for INode { 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(()); @@ -401,9 +403,34 @@ impl vfs::INode for INode { assert!(info.nlinks>0); let dest = target.downcast_mut::().unwrap(); assert!(Rc::ptr_eq(&fs,&dest.fs.upgrade().unwrap())); - assert!(dest.info().unwrap().type_!=vfs::FileType::Dir); + assert!(dest.info().unwrap().type_==vfs::FileType::Dir); assert!(dest.info().unwrap().nlinks>0); - unimplemented!() + + assert!(dest.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 (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> { let fs = self.fs.upgrade().unwrap(); @@ -613,7 +640,9 @@ impl SimpleFileSystem { } } } - inodes.remove(&id); + if(should_remove){ + inodes.remove(&id); + } } } } diff --git a/src/tests.rs b/src/tests.rs index d514e99..8a6b355 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -150,6 +150,7 @@ fn kernel_image_file_create(){ root.borrow_mut().create("hello2",FileType::File).unwrap(); 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(); } @@ -177,6 +178,26 @@ fn kernel_image_file_rename(){ 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(); } @@ -197,33 +218,118 @@ fn hard_link(){ #[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 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 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 assert_eq!(dir1.borrow().info().unwrap().nlinks,2); assert_eq!(root.borrow().info().unwrap().nlinks,3); - use core::ops::DerefMut; - dir1.borrow_mut().link("file2",file1.borrow_mut().deref_mut()).unwrap(); + + dir1.borrow_mut().link("file1_",file1.borrow_mut().deref_mut()).unwrap(); + // -root + // +-dir_1 + // | `-file1_ + // `-file1 + 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_ + // +-dir2 + // `-file1 + 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_ + // +-dir2 + // `-file_1 + 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_ + // `-dir2 + // `-file__1 + 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_ + // `-file__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); assert_eq!(file1.borrow().info().unwrap().nlinks,2); - root.borrow_mut().unlink("file1").unwrap(); + + dir2.borrow_mut().unlink("file__1").unwrap(); + // -root + // `-dir2 + // `-dir__1 + // `-file1_ 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("file2").unwrap(); + + 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); - root.borrow_mut().unlink("dir_1").unwrap(); + 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(); } \ No newline at end of file From 2de01e4f16d43223bd5d0dc023f91441790e780c Mon Sep 17 00:00:00 2001 From: Ben Pig Chu Date: Sun, 4 Nov 2018 00:21:56 +0800 Subject: [PATCH 14/14] make ci test only use one thread --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0f33674..52b1d96 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,5 @@ language: rust rust: - - nightly \ No newline at end of file + - nightly +script: + - cargo test --verbose -- --test-threads=1 \ No newline at end of file