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(); }