diff --git a/easy-fs/Cargo.toml b/easy-fs/Cargo.toml index b441f2a1..3beb3ff2 100644 --- a/easy-fs/Cargo.toml +++ b/easy-fs/Cargo.toml @@ -7,3 +7,4 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +spin = "0.7.0" \ No newline at end of file diff --git a/easy-fs/src/bin/main.rs b/easy-fs/src/bin/main.rs index f25aec75..3af832aa 100644 --- a/easy-fs/src/bin/main.rs +++ b/easy-fs/src/bin/main.rs @@ -4,6 +4,7 @@ extern crate alloc; use easy_fs::{ BlockDevice, EasyFileSystem, + Inode, }; use std::fs::{File, OpenOptions}; use std::io::{Read, Write, Seek, SeekFrom}; @@ -16,6 +17,7 @@ struct BlockFile(Mutex); impl BlockDevice for BlockFile { fn read_block(&self, block_id: usize, buf: &mut [u8]) { + println!("reading block {}", block_id); let mut file = self.0.lock().unwrap(); file.seek(SeekFrom::Start((block_id * BLOCK_SZ) as u64)) .expect("Error when seeking!"); @@ -23,6 +25,7 @@ impl BlockDevice for BlockFile { } fn write_block(&self, block_id: usize, buf: &[u8]) { + println!("writing block {}", block_id); let mut file = self.0.lock().unwrap(); file.seek(SeekFrom::Start((block_id * BLOCK_SZ) as u64)) .expect("Error when seeking!"); @@ -35,19 +38,23 @@ fn main() { } fn easy_fs_pack() -> std::io::Result<()> { - let block_file = BlockFile(Mutex::new( + let block_file = Arc::new(BlockFile(Mutex::new( OpenOptions::new() .read(true) .write(true) .open("target/fs.img")? - )); - /* - let _efs = EasyFileSystem::create( - Arc::new(block_file), + ))); + EasyFileSystem::create( + block_file.clone(), 4096, 1, ); - */ - let _efs = EasyFileSystem::open(Arc::new(block_file)); + let efs = EasyFileSystem::open(block_file.clone()); + let mut root_inode = EasyFileSystem::root_inode(&efs); + root_inode.create("filea"); + root_inode.create("fileb"); + for name in root_inode.ls() { + println!("name"); + } Ok(()) } \ No newline at end of file diff --git a/easy-fs/src/dirty.rs b/easy-fs/src/dirty.rs index b1cfd33d..8fa7a895 100644 --- a/easy-fs/src/dirty.rs +++ b/easy-fs/src/dirty.rs @@ -7,6 +7,7 @@ pub struct Dirty { block_id: usize, block_cache: [u8; BLOCK_SZ], offset: usize, + dirty: bool, block_device: Arc, phantom: PhantomData, } @@ -21,29 +22,37 @@ impl Dirty where T: Sized { cache }, offset, + dirty: false, block_device, phantom: PhantomData, } } pub fn get_mut(&mut self) -> &mut T { + self.dirty = true; let type_size = core::mem::size_of::(); // assert that the struct is inside a block assert!(self.offset + type_size <= BLOCK_SZ); let start_addr = &self.block_cache[self.offset] as *const _ as usize; unsafe { &mut *(start_addr as *mut T) } } - pub fn read(&self) -> &T { + pub fn get_ref(&self) -> &T { let type_size = core::mem::size_of::(); // assert that the struct is inside a block assert!(self.offset + type_size <= BLOCK_SZ); let start_addr = &self.block_cache[self.offset] as *const _ as usize; unsafe { &*(start_addr as *const T) } } - pub fn modify(&mut self, f: impl Fn(&mut T)) { + pub fn read(&self, f: impl FnOnce(&T) -> V) -> V { + f(self.get_ref()) + } + pub fn modify(&mut self, f: impl FnOnce(&mut T)) { f(self.get_mut()); } pub fn write_back(&mut self) { - self.block_device.write_block(self.block_id as usize, &self.block_cache); + if self.dirty { + self.block_device + .write_block(self.block_id as usize, &self.block_cache); + } } } diff --git a/easy-fs/src/efs.rs b/easy-fs/src/efs.rs index 51eef844..2a8af448 100644 --- a/easy-fs/src/efs.rs +++ b/easy-fs/src/efs.rs @@ -1,4 +1,5 @@ use alloc::sync::Arc; +use spin::Mutex; use super::{ BlockDevice, Bitmap, @@ -6,6 +7,7 @@ use super::{ DiskInode, DiskInodeType, Dirty, + Inode, }; use crate::BLOCK_SZ; @@ -24,7 +26,7 @@ impl EasyFileSystem { block_device: Arc, total_blocks: u32, inode_bitmap_blocks: u32, - ) -> Self { + ) -> Arc> { // calculate block size of areas & create bitmaps let inode_bitmap = Bitmap::new(1, inode_bitmap_blocks as usize); let inode_num = inode_bitmap.maximum(); @@ -38,7 +40,7 @@ impl EasyFileSystem { (1 + inode_bitmap_blocks + inode_area_blocks) as usize, data_bitmap_blocks as usize, ); - let efs = Self { + let mut efs = Self { block_device, inode_bitmap, data_bitmap, @@ -65,17 +67,20 @@ impl EasyFileSystem { }); // write back immediately // create a inode for root node "/" - assert_eq!(efs.inode_bitmap.alloc(&efs.block_device).unwrap(), 0); + assert_eq!(efs.alloc_inode(), 0); efs.get_disk_inode(0).modify(|disk_inode| { - disk_inode.initialize(DiskInodeType::Directory); + disk_inode.initialize( + DiskInodeType::Directory, + efs.alloc_data(), + ); }); - efs + Arc::new(Mutex::new(efs)) } - pub fn open(block_device: Arc) -> Self { + pub fn open(block_device: Arc) -> Arc> { // read SuperBlock let super_block_dirty: Dirty = Dirty::new(0, 0, block_device.clone()); - let super_block = super_block_dirty.read(); + let super_block = super_block_dirty.get_ref(); assert!(super_block.is_valid(), "Error loading EFS!"); println!("{:?}", super_block); let inode_total_blocks = @@ -93,14 +98,22 @@ impl EasyFileSystem { inode_area_start_block: 1 + super_block.inode_bitmap_blocks, data_area_start_block: 1 + inode_total_blocks + super_block.data_bitmap_blocks, }; - efs + Arc::new(Mutex::new(efs)) + } + + pub fn root_inode(efs: &Arc>) -> Inode { + Inode::new( + 0, + efs.clone(), + efs.lock().block_device.clone(), + ) } fn get_super_block(&self) -> Dirty { Dirty::new(0, 0, self.block_device.clone()) } - fn get_disk_inode(&self, inode_id: u32) -> Dirty { + pub fn get_disk_inode(&self, inode_id: u32) -> Dirty { let inode_size = core::mem::size_of::(); let inodes_per_block = (BLOCK_SZ / inode_size) as u32; let block_id = self.inode_area_start_block + inode_id / inodes_per_block; @@ -111,12 +124,8 @@ impl EasyFileSystem { ) } - fn get_data_block(&self, data_block_id: u32) -> Dirty { - Dirty::new( - (self.data_area_start_block + data_block_id) as usize, - 0, - self.block_device.clone(), - ) + pub fn get_data_block(&self, data_block_id: u32) -> Dirty { + self.get_block(self.data_area_start_block + data_block_id) } fn get_block(&self, block_id: u32) -> Dirty { @@ -127,4 +136,20 @@ impl EasyFileSystem { ) } + pub fn alloc_inode(&mut self) -> u32 { + self.inode_bitmap.alloc(&self.block_device).unwrap() as u32 + } + + /// Return a block ID not ID in the data area. + pub fn alloc_data(&mut self) -> u32 { + self.data_bitmap.alloc(&self.block_device).unwrap() as u32 + self.data_area_start_block + } + + fn dealloc_data(&mut self, block_id: u32) { + self.data_bitmap.dealloc( + &self.block_device, + (block_id - self.data_area_start_block) as usize + ) + } + } \ No newline at end of file diff --git a/easy-fs/src/layout.rs b/easy-fs/src/layout.rs index 75251250..174d64cf 100644 --- a/easy-fs/src/layout.rs +++ b/easy-fs/src/layout.rs @@ -1,4 +1,11 @@ use core::fmt::{Debug, Formatter, Result}; +use super::{ + BLOCK_SZ, + BlockDevice, + Dirty, +}; +use alloc::sync::Arc; +use alloc::vec::Vec; const EFS_MAGIC: u32 = 0x3b800001; const INODE_DIRECT_COUNT: usize = 12; @@ -49,32 +56,224 @@ impl SuperBlock { } } +#[derive(PartialEq)] pub enum DiskInodeType { File, Directory, } +type IndirectBlock = [u32; BLOCK_SZ / 4]; +type DataBlock = [u8; BLOCK_SZ]; + #[repr(C)] +/// Only support level-1 indirect now, **indirect2** field is always 0. pub struct DiskInode { - size: u32, - direct: [u32; INODE_DIRECT_COUNT], - indirect1: u32, - indirect2: u32, + pub size: u32, + pub direct: [u32; INODE_DIRECT_COUNT], + pub indirect1: u32, + pub indirect2: u32, type_: DiskInodeType, } impl DiskInode { - pub fn initialize(&mut self, type_: DiskInodeType) { + /// indirect1 block is allocated when the file is created. + pub fn initialize(&mut self, type_: DiskInodeType, indirect1: u32) { self.size = 0; self.direct.iter_mut().for_each(|v| *v = 0); - self.indirect1 = 0; + self.indirect1 = indirect1; self.indirect2 = 0; self.type_ = type_; } + pub fn is_dir(&self) -> bool { + self.type_ == DiskInodeType::Directory + } + pub fn is_file(&self) -> bool { + self.type_ == DiskInodeType::File + } + pub fn blocks(&self) -> u32 { + Self::_blocks(self.size) + } + fn _blocks(size: u32) -> u32 { + (size + BLOCK_SZ as u32 - 1) / BLOCK_SZ as u32 + } + pub fn get_block_id(&self, inner_id: u32, block_device: &Arc) -> u32 { + let inner_id = inner_id as usize; + if inner_id < INODE_DIRECT_COUNT { + self.direct[inner_id] + } else { + // only support indirect1 now + Dirty::::new( + self.indirect1 as usize, + 0, + block_device.clone() + ).read(|indirect_block| { + // it will panic if file is too large + indirect_block[inner_id - INODE_DIRECT_COUNT] + }) + } + } + pub fn blocks_num_needed(&self, new_size: u32) -> u32 { + assert!(new_size >= self.size); + Self::_blocks(new_size) - self.blocks() + } + pub fn increase_size( + &mut self, + new_size: u32, + new_blocks: Vec, + block_device: &Arc, + ) { + println!("increase_size new_size={}, new_blocks={:?}", new_size, new_blocks); + assert_eq!(new_blocks.len() as u32, self.blocks_num_needed(new_size)); + let last_blocks = self.blocks(); + self.size = new_size; + let current_blocks = self.blocks(); + Dirty::::new( + self.indirect1 as usize, + 0, + block_device.clone() + ).modify(|indirect_block| { + for i in 0..current_blocks - last_blocks { + let inner_id = (last_blocks + i) as usize; + let new_block = new_blocks[i as usize]; + if inner_id < INODE_DIRECT_COUNT { + self.direct[inner_id] = new_block; + } else { + indirect_block[inner_id - INODE_DIRECT_COUNT] = new_block; + } + } + }); + } + /// Clear size to zero and return blocks that should be deallocated. + pub fn clear_size(&mut self, block_device: &Arc) -> Vec { + let mut v: Vec = Vec::new(); + let blocks = self.blocks() as usize; + self.size = 0; + for i in 0..blocks.min(INODE_DIRECT_COUNT) { + v.push(self.direct[i]); + self.direct[i] = 0; + } + if blocks > INODE_DIRECT_COUNT { + Dirty::::new( + self.indirect1 as usize, + 0, + block_device.clone(), + ).modify(|indirect_block| { + for i in 0..blocks - INODE_DIRECT_COUNT { + v.push(indirect_block[i]); + indirect_block[i] = 0; + } + }); + } + v + } + pub fn read_at( + &self, + offset: usize, + buf: &mut [u8], + block_device: &Arc, + ) -> usize { + let mut start = offset; + let end = (offset + buf.len()).min(self.size as usize); + if start >= end { + return 0; + } + let mut start_block = start / BLOCK_SZ; + let mut read_size = 0usize; + loop { + // calculate end of current block + let mut end_current_block = (start / BLOCK_SZ + 1) * BLOCK_SZ; + end_current_block = end_current_block.min(end); + // read and update read size + let block_read_size = end_current_block - start; + let dst = &mut buf[read_size..read_size + block_read_size]; + Dirty::::new( + self.get_block_id(start_block as u32, block_device) as usize, + 0, + block_device.clone() + ).read(|data_block| { + let src = &data_block[start % BLOCK_SZ..start % BLOCK_SZ + block_read_size]; + dst.copy_from_slice(src); + }); + read_size += block_read_size; + // move to next block + if end_current_block == end { break; } + start_block += 1; + start = end_current_block; + } + read_size + } + /// File size must be adjusted before. + pub fn write_at( + &mut self, + offset: usize, + buf: &[u8], + block_device: &Arc, + ) -> usize { + let mut start = offset; + let end = (offset + buf.len()).min(self.size as usize); + assert!(start <= end); + let mut start_block = start / BLOCK_SZ; + let mut write_size = 0usize; + loop { + // calculate end of current block + let mut end_current_block = (start / BLOCK_SZ + 1) * BLOCK_SZ; + end_current_block = end_current_block.min(end); + // write and update write size + let block_write_size = end_current_block - start; + Dirty::::new( + self.get_block_id(start_block as u32, block_device) as usize, + 0, + block_device.clone() + ).modify(|data_block| { + let src = &buf[write_size..write_size + block_write_size]; + let dst = &mut data_block[start % BLOCK_SZ..start % BLOCK_SZ + block_write_size]; + dst.copy_from_slice(src); + }); + write_size += block_write_size; + // move to next block + if end_current_block == end { break; } + start_block += 1; + start = end_current_block; + } + write_size + } } #[repr(C)] pub struct DirEntry { name: [u8; NAME_LENGTH_LIMIT + 1], inode_number: u32, +} + +pub const DIRENT_SZ: usize = 32; + +pub type DirentBlock = [DirEntry; BLOCK_SZ / DIRENT_SZ]; +pub type DirentBytes = [u8; DIRENT_SZ]; + +impl DirEntry { + pub fn new(name: &str, inode_number: u32) -> Self { + let mut bytes = [0u8; NAME_LENGTH_LIMIT + 1]; + &mut bytes[..name.len()].copy_from_slice(name.as_bytes()); + Self { + name: bytes, + inode_number, + } + } + pub fn into_bytes(&self) -> &DirentBytes { + unsafe { + &*(self as *const Self as usize as *const DirentBytes) + } + } + pub fn from_bytes(bytes: &DirentBytes) -> &Self { + unsafe { &*(bytes.as_ptr() as usize as *const Self) } + } + pub fn from_bytes_mut(bytes: &mut DirentBytes) -> &mut Self { + unsafe { + &mut *(bytes.as_mut_ptr() as usize as *mut Self) + } + } + pub fn name(&self) -> &str { + let len = (0usize..).find(|i| self.name[*i] == 0).unwrap(); + core::str::from_utf8(&self.name[..len]).unwrap() + } } \ No newline at end of file diff --git a/easy-fs/src/lib.rs b/easy-fs/src/lib.rs index 25abab63..4202fb4b 100644 --- a/easy-fs/src/lib.rs +++ b/easy-fs/src/lib.rs @@ -10,6 +10,7 @@ mod vfs; pub const BLOCK_SZ: usize = 512; pub use block_dev::BlockDevice; pub use efs::EasyFileSystem; +pub use vfs::Inode; use layout::*; use dirty::Dirty; use bitmap::Bitmap; \ No newline at end of file diff --git a/easy-fs/src/vfs.rs b/easy-fs/src/vfs.rs index 488bb7a1..2a989f99 100644 --- a/easy-fs/src/vfs.rs +++ b/easy-fs/src/vfs.rs @@ -1,3 +1,156 @@ +use super::{ + BlockDevice, + Dirty, + DiskInode, + DiskInodeType, + DirEntry, + DirentBytes, + EasyFileSystem, + BLOCK_SZ, + DIRENT_SZ, +}; +use alloc::sync::Arc; +use alloc::string::String; +use alloc::vec::Vec; +use crate::layout::DiskInodeType::Directory; +use spin::{Mutex, MutexGuard}; + pub struct Inode { + inode_id: u32, + fs: Arc>, + block_device: Arc, +} + +impl Inode { + pub fn new( + inode_id: u32, + fs: Arc>, + block_device: Arc, + ) -> Self { + Self { + inode_id, + fs, + block_device, + } + } + fn get_disk_inode(&self) -> Dirty { + self.fs.lock().get_disk_inode(self.inode_id) + } + + fn find_inode_id(&self, name: &str) -> Option { + let inode = self.get_disk_inode(); + // assert it is a directory + assert!(inode.read(|inode| inode.is_dir())); + let file_count = inode.read(|inode| { + inode.size as usize + }) / DIRENT_SZ; + let mut dirent_space: DirentBytes = Default::default(); + for i in 0..file_count { + assert_eq!( + inode.read(|inode| { + inode.read_at( + DIRENT_SZ * i, + &mut dirent_space, + &self.block_device, + ) + }), + DIRENT_SZ, + ); + if DirEntry::from_bytes(&dirent_space).name() == name { + return Some(i as u32); + } + } + None + } + + fn increase_size(&self, new_size: u32, fs: &mut MutexGuard) { + let mut inode = fs.get_disk_inode(self.inode_id); + let blocks_needed = inode.read(|inode| { + inode.blocks_num_needed(new_size) + }); + println!("blocks_num_needed = {}", blocks_needed); + let mut v: Vec = Vec::new(); + for _ in 0..blocks_needed { + v.push(fs.alloc_data()); + } + inode.modify(|inode| { + inode.increase_size(new_size, v, &self.block_device); + }); + } + + pub fn create(&mut self, name: &str) -> Option> { + println!("creating name {}", name); + let mut inode = self.get_disk_inode(); + // assert it is a directory + assert!(inode.read(|inode| inode.is_dir())); + // has the file been created? + if let Some(_) = self.find_inode_id(name) { + return None; + } + println!("stop1"); + + // create a new file + // alloc a inode with an indirect block + let mut fs = self.fs.lock(); + let new_inode_id = fs.alloc_inode(); + let indirect1 = fs.alloc_data(); + println!("creating new file, new_inode_id={}, indirect={}", new_inode_id, indirect1); + // initialize inode + fs.get_disk_inode(new_inode_id).modify(|inode| { + inode.initialize( + DiskInodeType::File, + indirect1, + ) + }); + + // append file in the dirent + let file_count = + inode.read(|inode| inode.size as usize) / DIRENT_SZ; + let new_size = (file_count + 1) * DIRENT_SZ; + println!("expected new_size={}", new_size); + // increase size + self.increase_size(new_size as u32, &mut fs); + // write dirent + let dirent = DirEntry::new(name, new_inode_id); + inode.modify(|inode| { + inode.write_at( + file_count * DIRENT_SZ, + dirent.into_bytes(), + &self.block_device, + ); + }); + + // return inode + Some(Arc::new(Self::new( + new_inode_id, + self.fs.clone(), + self.block_device.clone(), + ))) + } -} \ No newline at end of file + pub fn ls(&self) -> Vec { + println!("into ls!"); + let fs = self.fs.lock(); + let inode = fs.get_disk_inode(self.inode_id); + let file_count = inode.read(|inode| { + (inode.size as usize) / DIRENT_SZ + }); + println!("file_count = {}", file_count); + let mut v: Vec = Vec::new(); + for i in 0..file_count { + let mut dirent_bytes: DirentBytes = Default::default(); + assert_eq!( + inode.read(|inode| { + inode.read_at( + i * DIRENT_SZ, + &mut dirent_bytes, + &self.block_device, + ) + }), + DIRENT_SZ, + ); + v.push(String::from(DirEntry::from_bytes(&dirent_bytes).name())); + } + v + } +}