From 7a748cadbb2e2ce8c0e045cb8fbd77ccbd47459f Mon Sep 17 00:00:00 2001 From: Chuyan Zhang Date: Tue, 17 Oct 2023 23:07:21 -0700 Subject: initial commit --- src/main.rs | 225 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 225 insertions(+) create mode 100644 src/main.rs (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..414baaf --- /dev/null +++ b/src/main.rs @@ -0,0 +1,225 @@ +mod block_device; +mod disk; + +use clap::Parser; +use fuser::{Filesystem, MountOption, ReplyAttr, ReplyData, ReplyDirectory, ReplyEntry, Request}; +use std::ffi::OsStr; +use std::sync::Arc; +use std::time::Duration; +use log::debug; + +use block_device::{memory_disk::MemoryDisk, BlockDevice, BLOCK_SIZE}; +use disk::bitmap::Bitmap; +use disk::inode::{Inode, INODE_SIZE}; +use libc::ENOENT; + +#[derive(Parser, Debug)] +#[command(author, version, about)] +struct Args { + mount_point: Option, + #[arg(long)] + auto_unmount: bool, + #[arg(long)] + allow_root: bool, +} + +const TTL: Duration = Duration::from_secs(1); +const INODE_PER_BLOCK: usize = BLOCK_SIZE / INODE_SIZE; + +/// The design of MyFS is rather simple: +/// +-------------------+ +/// | Super Block | +/// +-------------------+ +/// | Inode Bitmap | +/// +-------------------+ +/// | ... | +/// +-------------------+ +/// | Data Block Bitmap | +/// +-------------------+ +/// | ... | +/// +-------------------+ +/// | Inode Block | +/// +-------------------+ +/// | ... | +/// +-------------------+ +/// | Data Block | +/// +-------------------+ +/// With each block 4KiB, each Inode entry 128B + +#[repr(C)] +struct MyFS { + device: Arc, + data_bitmap: Bitmap, + inode_bitmap: Bitmap, + inode_start_block: usize, + data_start_block: usize, +} + +impl MyFS { + fn new(device: Arc, total_block_number: usize) -> Self { + let max_inode_number: usize = 16384; // TODO: remove hard-coded magic number + let inode_block_number = max_inode_number / INODE_PER_BLOCK; // == 128 + let inode_bitmap_block_number = (inode_block_number + BLOCK_SIZE - 1) / BLOCK_SIZE; + + let blocks_remaining = + total_block_number - inode_block_number - inode_bitmap_block_number - 1; + // let number of data blocks be x, the remaining block number be C, + // the corresponding data bitmap length should be ceil(x / BLK_SIZE), + // thus we have BLK_SIZE * (C-1) / (BLK_SIZE+1) <= x <= BLK_SIZE * C / (BLK_SIZE+1) + // the difference of the two bounds is less than 1, + // meaning only 1 integer could be in between. + // Thus we have x = floor(BLK_SIZE * C / (BLK_SIZE + 1)) + let data_block_number = BLOCK_SIZE * blocks_remaining / (BLOCK_SIZE + 1); + let data_bitmap_block_number = blocks_remaining - data_block_number; + debug!("dbbn: {}", data_bitmap_block_number); + debug!("ibbn: {}", inode_bitmap_block_number); + debug!("ibn: {}", inode_block_number); + debug!("dbn: {}", data_block_number); + debug!("sum: {}", 1 + data_bitmap_block_number + inode_bitmap_block_number + inode_block_number + data_block_number); + + let mut data_bitmap = Bitmap::new(1, data_bitmap_block_number, device.clone()); + let mut inode_bitmap = Bitmap::new( + data_bitmap_block_number + 1, + inode_bitmap_block_number, + device.clone(), + ); + let mut fs = Self { + device, + data_bitmap, + inode_bitmap, + inode_start_block: data_bitmap_block_number + inode_bitmap_block_number + 1, + data_start_block: data_bitmap_block_number + + inode_bitmap_block_number + + inode_block_number + + 1, + }; + + let _ = fs.inode_bitmap.allocate(); // Inode starts from 1 + let root_inode_index = fs.inode_bitmap.allocate(); + assert_eq!(root_inode_index, 1); + let (root_inode_block, root_inode_offset) = fs.locate_inode(root_inode_index); + fs.put_inode(root_inode_block, root_inode_offset, Inode::directory()); + + fs + } + + // pub fn allocate_inode(&mut self) -> usize { + // self.inode_bitmap.allocate() + // } + + pub fn inode_active(&self, inode: usize) -> bool { + self.inode_bitmap.query(inode) + } + + /// 输入 inode 编号, 返回它对应的 block number 和 block 内 offset + pub fn locate_inode(&self, inode: usize) -> (usize, usize) { + let block_number = + inode / INODE_PER_BLOCK + 1 + self.inode_bitmap.length + self.data_bitmap.length; + let block_offset = inode % INODE_PER_BLOCK * INODE_SIZE; + (block_number, block_offset) + } + + // TODO: 实现一个 LRU 的 cache 机制, 不要每次都开 buffer + pub fn put_inode(&mut self, block: usize, offset: usize, inode: Inode) { + let mut buffer = vec![0u8; BLOCK_SIZE]; + self.device.read(block, buffer.as_mut_slice()); + + let inode_raw = &inode as *const Inode as *const u8; + let inode_slice = unsafe { std::slice::from_raw_parts(inode_raw, INODE_SIZE) }; + buffer[offset..offset + INODE_SIZE].copy_from_slice(inode_slice); + + self.device.write(block, buffer.as_slice()); + } + + // TODO: 实现一个 LRU 的 cache 机制, 不要每次都开 buffer + pub fn get_inode(&self, block: usize, offset: usize) -> Inode { + let mut buffer = vec![0u8; BLOCK_SIZE]; + self.device.read(block, buffer.as_mut_slice()); + + let inode = Inode::file(); + let inode_slice = unsafe { + std::slice::from_raw_parts_mut(&inode as *const Inode as *mut u8, INODE_SIZE) + }; + inode_slice.copy_from_slice(&buffer[offset..offset + INODE_SIZE]); + inode + } +} + +impl Filesystem for MyFS { + fn lookup(&mut self, _req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEntry) { + debug!( + "Filesystem::lookup called with parent {} name {}", + parent, + name.to_str().unwrap() + ); + let parent = parent as usize; + if self.inode_active(parent) { + let (block, offset) = self.locate_inode(parent); + let inode = self.get_inode(block, offset); + debug!("{:?}", inode); + } + reply.error(ENOENT); + } + + fn getattr(&mut self, _req: &Request<'_>, ino: u64, reply: ReplyAttr) { + debug!("Filesystem::getattr called with ino {}", ino); + let ino = ino as usize; + if self.inode_active(ino) { + let (block, offset) = self.locate_inode(ino); + let inode = self.get_inode(block, offset); + debug!("{:?}", inode); + } + reply.error(ENOENT); + } + + fn read( + &mut self, + _req: &Request<'_>, + ino: u64, + _fh: u64, + offset: i64, + _size: u32, + _flags: i32, + _lock_owner: Option, + reply: ReplyData, + ) { + todo!() + } + + fn readdir( + &mut self, + _req: &Request<'_>, + ino: u64, + _fh: u64, + offset: i64, + mut reply: ReplyDirectory, + ) { + todo!() + } +} + +fn main() { + env_logger::init(); + let args = Args::parse(); + let mount_point = args.mount_point.unwrap(); + // let mut options = vec![ + // MountOption::RO, + // MountOption::FSName("hello".to_string()), + // ]; + // if args.allow_root { + // options.push(MountOption::AutoUnmount); + // } + // if args.allow_root { + // options.push(MountOption::AllowRoot); + // } + let options = vec![ + MountOption::RO, + MountOption::FSName("hello".to_string()), + MountOption::AutoUnmount, + MountOption::AllowRoot, + ]; + let mem_disk = Arc::new(MemoryDisk::new()); + let filesystem = MyFS::new(mem_disk, 16384); + + fuser::mount2(filesystem, mount_point, &options).unwrap(); +} -- cgit v1.2.3-70-g09d2