diff --git a/easy-fs/.gitignore b/easy-fs/.gitignore new file mode 100644 index 00000000..79f5db68 --- /dev/null +++ b/easy-fs/.gitignore @@ -0,0 +1,3 @@ +.idea/ +target/ +Cargo.lock diff --git a/easy-fs/Cargo.toml b/easy-fs/Cargo.toml new file mode 100644 index 00000000..b441f2a1 --- /dev/null +++ b/easy-fs/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "easy-fs" +version = "0.1.0" +authors = ["Yifan Wu "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/easy-fs/src/bin/main.rs b/easy-fs/src/bin/main.rs new file mode 100644 index 00000000..f25aec75 --- /dev/null +++ b/easy-fs/src/bin/main.rs @@ -0,0 +1,53 @@ +extern crate easy_fs; +extern crate alloc; + +use easy_fs::{ + BlockDevice, + EasyFileSystem, +}; +use std::fs::{File, OpenOptions}; +use std::io::{Read, Write, Seek, SeekFrom}; +use std::sync::Mutex; +use alloc::sync::Arc; + +const BLOCK_SZ: usize = 512; + +struct BlockFile(Mutex); + +impl BlockDevice for BlockFile { + fn read_block(&self, block_id: usize, buf: &mut [u8]) { + let mut file = self.0.lock().unwrap(); + file.seek(SeekFrom::Start((block_id * BLOCK_SZ) as u64)) + .expect("Error when seeking!"); + assert_eq!(file.read(buf).unwrap(), BLOCK_SZ, "Not a complete block!"); + } + + fn write_block(&self, block_id: usize, buf: &[u8]) { + let mut file = self.0.lock().unwrap(); + file.seek(SeekFrom::Start((block_id * BLOCK_SZ) as u64)) + .expect("Error when seeking!"); + assert_eq!(file.write(buf).unwrap(), BLOCK_SZ, "Not a complete block!"); + } +} + +fn main() { + easy_fs_pack().expect("Error when packing easy-fs!"); +} + +fn easy_fs_pack() -> std::io::Result<()> { + let block_file = BlockFile(Mutex::new( + OpenOptions::new() + .read(true) + .write(true) + .open("target/fs.img")? + )); + /* + let _efs = EasyFileSystem::create( + Arc::new(block_file), + 4096, + 1, + ); + */ + let _efs = EasyFileSystem::open(Arc::new(block_file)); + Ok(()) +} \ No newline at end of file diff --git a/easy-fs/src/bitmap.rs b/easy-fs/src/bitmap.rs new file mode 100644 index 00000000..edda5909 --- /dev/null +++ b/easy-fs/src/bitmap.rs @@ -0,0 +1,67 @@ +use alloc::sync::Arc; +use super::BlockDevice; +use super::Dirty; +use super::BLOCK_SZ; + +type BitmapBlock = [u64; 64]; + +const BLOCK_BITS: usize = BLOCK_SZ * 8; + +pub struct Bitmap { + start_block_id: usize, + blocks: usize, +} + +/// Return (block_pos, bits64_pos, inner_pos) +fn decomposition(mut bit: usize) -> (usize, usize, usize) { + let block_pos = bit / BLOCK_BITS; + bit = bit % BLOCK_BITS; + (block_pos, bit/64, bit % 64) +} + +impl Bitmap { + pub fn new(start_block_id: usize, blocks: usize) -> Self { + Self { + start_block_id, + blocks, + } + } + pub fn alloc(&self, block_device: &Arc) -> Option { + for block_id in 0..self.blocks { + let mut dirty_bitmap_block: Dirty = Dirty::new( + block_id + self.start_block_id as usize, + 0, + block_device.clone() + ); + let bitmap_block = dirty_bitmap_block.get_mut(); + if let Some((bits64_pos, inner_pos)) = bitmap_block + .iter() + .enumerate() + .find(|(_, bits64)| **bits64 != u64::MAX) + .map(|(bits64_pos, bits64)| { + (bits64_pos, bits64.trailing_ones() as usize) + }) { + // modify cache + bitmap_block[bits64_pos] |= 1u64 << inner_pos; + return Some(block_id * BLOCK_BITS + bits64_pos * 64 + inner_pos as usize); + // after dirty is dropped, data will be written back automatically + } + } + None + } + pub fn dealloc(&self, block_device: &Arc, bit: usize) { + let (block_pos, bits64_pos, inner_pos) = decomposition(bit); + let mut dirty_bitmap_block: Dirty = Dirty::new( + block_pos, + 0, + block_device.clone(), + ); + dirty_bitmap_block.modify(|bitmap_block| { + assert!(bitmap_block[bits64_pos] & (1u64 << inner_pos) > 0); + bitmap_block[bits64_pos] -= 1u64 << inner_pos; + }); + } + pub fn maximum(&self) -> usize { + self.blocks * BLOCK_BITS + } +} \ No newline at end of file diff --git a/easy-fs/src/block_dev.rs b/easy-fs/src/block_dev.rs new file mode 100644 index 00000000..7a282751 --- /dev/null +++ b/easy-fs/src/block_dev.rs @@ -0,0 +1,6 @@ +use core::any::Any; + +pub trait BlockDevice : Send + Sync + Any { + fn read_block(&self, block_id: usize, buf: &mut [u8]); + fn write_block(&self, block_id: usize, buf: &[u8]); +} diff --git a/easy-fs/src/dirty.rs b/easy-fs/src/dirty.rs new file mode 100644 index 00000000..b1cfd33d --- /dev/null +++ b/easy-fs/src/dirty.rs @@ -0,0 +1,54 @@ +use super::BlockDevice; +use super::BLOCK_SZ; +use alloc::sync::Arc; +use core::marker::PhantomData; + +pub struct Dirty { + block_id: usize, + block_cache: [u8; BLOCK_SZ], + offset: usize, + block_device: Arc, + phantom: PhantomData, +} + +impl Dirty where T: Sized { + pub fn new(block_id: usize, offset: usize, block_device: Arc) -> Self { + Self { + block_id, + block_cache: { + let mut cache = [0u8; BLOCK_SZ]; + block_device.read_block(block_id as usize, &mut cache); + cache + }, + offset, + block_device, + phantom: PhantomData, + } + } + pub fn get_mut(&mut self) -> &mut 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 { &mut *(start_addr as *mut T) } + } + pub fn read(&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)) { + f(self.get_mut()); + } + pub fn write_back(&mut self) { + self.block_device.write_block(self.block_id as usize, &self.block_cache); + } +} + +impl Drop for Dirty { + fn drop(&mut self) { + self.write_back(); + } +} \ No newline at end of file diff --git a/easy-fs/src/efs.rs b/easy-fs/src/efs.rs new file mode 100644 index 00000000..51eef844 --- /dev/null +++ b/easy-fs/src/efs.rs @@ -0,0 +1,130 @@ +use alloc::sync::Arc; +use super::{ + BlockDevice, + Bitmap, + SuperBlock, + DiskInode, + DiskInodeType, + Dirty, +}; +use crate::BLOCK_SZ; + +pub struct EasyFileSystem { + pub block_device: Arc, + pub inode_bitmap: Bitmap, + pub data_bitmap: Bitmap, + inode_area_start_block: u32, + data_area_start_block: u32, +} + +type DataBlock = [u8; BLOCK_SZ]; + +impl EasyFileSystem { + pub fn create( + block_device: Arc, + total_blocks: u32, + inode_bitmap_blocks: u32, + ) -> Self { + // calculate block size of areas & create bitmaps + let inode_bitmap = Bitmap::new(1, inode_bitmap_blocks as usize); + let inode_num = inode_bitmap.maximum(); + let inode_area_blocks = + ((inode_num * core::mem::size_of::() + BLOCK_SZ - 1) / BLOCK_SZ) as u32; + let inode_total_blocks = inode_bitmap_blocks + inode_area_blocks; + let data_total_blocks = total_blocks - 1 - inode_total_blocks; + let data_bitmap_blocks = (data_total_blocks + 4096) / 4097; + let data_area_blocks = data_total_blocks - data_bitmap_blocks; + let data_bitmap = Bitmap::new( + (1 + inode_bitmap_blocks + inode_area_blocks) as usize, + data_bitmap_blocks as usize, + ); + let efs = Self { + block_device, + inode_bitmap, + data_bitmap, + inode_area_start_block: 1 + inode_bitmap_blocks, + data_area_start_block: 1 + inode_total_blocks + data_bitmap_blocks, + }; + // clear all blocks + for i in 0..total_blocks { + efs.get_block(i).modify(|data_block| { + for byte in data_block.iter_mut() { + *byte = 0; + } + }); + } + // initialize SuperBlock + efs.get_super_block().modify(|super_block| { + super_block.initialize( + total_blocks, + inode_bitmap_blocks, + inode_area_blocks, + data_bitmap_blocks, + data_area_blocks, + ); + }); + // write back immediately + // create a inode for root node "/" + assert_eq!(efs.inode_bitmap.alloc(&efs.block_device).unwrap(), 0); + efs.get_disk_inode(0).modify(|disk_inode| { + disk_inode.initialize(DiskInodeType::Directory); + }); + efs + } + + pub fn open(block_device: Arc) -> Self { + // read SuperBlock + let super_block_dirty: Dirty = Dirty::new(0, 0, block_device.clone()); + let super_block = super_block_dirty.read(); + assert!(super_block.is_valid(), "Error loading EFS!"); + println!("{:?}", super_block); + let inode_total_blocks = + super_block.inode_bitmap_blocks + super_block.inode_area_blocks; + let efs = Self { + block_device, + inode_bitmap: Bitmap::new( + 1, + super_block.inode_bitmap_blocks as usize + ), + data_bitmap: Bitmap::new( + (1 + inode_total_blocks) as usize, + super_block.data_bitmap_blocks as usize, + ), + inode_area_start_block: 1 + super_block.inode_bitmap_blocks, + data_area_start_block: 1 + inode_total_blocks + super_block.data_bitmap_blocks, + }; + efs + } + + fn get_super_block(&self) -> Dirty { + Dirty::new(0, 0, self.block_device.clone()) + } + + 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; + Dirty::new( + block_id as usize, + (inode_id % inodes_per_block) as usize * inode_size, + self.block_device.clone(), + ) + } + + 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(), + ) + } + + fn get_block(&self, block_id: u32) -> Dirty { + Dirty::new( + block_id as usize, + 0, + self.block_device.clone(), + ) + } + +} \ No newline at end of file diff --git a/easy-fs/src/layout.rs b/easy-fs/src/layout.rs new file mode 100644 index 00000000..75251250 --- /dev/null +++ b/easy-fs/src/layout.rs @@ -0,0 +1,80 @@ +use core::fmt::{Debug, Formatter, Result}; + +const EFS_MAGIC: u32 = 0x3b800001; +const INODE_DIRECT_COUNT: usize = 12; +const NAME_LENGTH_LIMIT: usize = 27; + +#[repr(C)] +pub struct SuperBlock { + magic: u32, + pub total_blocks: u32, + pub inode_bitmap_blocks: u32, + pub inode_area_blocks: u32, + pub data_bitmap_blocks: u32, + pub data_area_blocks: u32, +} + +impl Debug for SuperBlock { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + f.debug_struct("SuperBlock") + .field("total_blocks", &self.total_blocks) + .field("inode_bitmap_blocks", &self.inode_bitmap_blocks) + .field("inode_area_blocks", &self.inode_area_blocks) + .field("data_bitmap_blocks", &self.data_bitmap_blocks) + .field("data_area_blocks", &self.data_area_blocks) + .finish() + } +} + +impl SuperBlock { + pub fn initialize( + &mut self, + total_blocks: u32, + inode_bitmap_blocks: u32, + inode_area_blocks: u32, + data_bitmap_blocks: u32, + data_area_blocks: u32, + ) { + *self = Self { + magic: EFS_MAGIC, + total_blocks, + inode_bitmap_blocks, + inode_area_blocks, + data_bitmap_blocks, + data_area_blocks, + } + } + pub fn is_valid(&self) -> bool { + self.magic == EFS_MAGIC + } +} + +pub enum DiskInodeType { + File, + Directory, +} + +#[repr(C)] +pub struct DiskInode { + size: u32, + direct: [u32; INODE_DIRECT_COUNT], + indirect1: u32, + indirect2: u32, + type_: DiskInodeType, +} + +impl DiskInode { + pub fn initialize(&mut self, type_: DiskInodeType) { + self.size = 0; + self.direct.iter_mut().for_each(|v| *v = 0); + self.indirect1 = 0; + self.indirect2 = 0; + self.type_ = type_; + } +} + +#[repr(C)] +pub struct DirEntry { + name: [u8; NAME_LENGTH_LIMIT + 1], + inode_number: u32, +} \ No newline at end of file diff --git a/easy-fs/src/lib.rs b/easy-fs/src/lib.rs new file mode 100644 index 00000000..25abab63 --- /dev/null +++ b/easy-fs/src/lib.rs @@ -0,0 +1,15 @@ +extern crate alloc; + +mod block_dev; +mod layout; +mod efs; +mod dirty; +mod bitmap; +mod vfs; + +pub const BLOCK_SZ: usize = 512; +pub use block_dev::BlockDevice; +pub use efs::EasyFileSystem; +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 new file mode 100644 index 00000000..488bb7a1 --- /dev/null +++ b/easy-fs/src/vfs.rs @@ -0,0 +1,3 @@ +pub struct Inode { + +} \ No newline at end of file