#[macro_use] extern crate static_assertions; pub mod block_device; pub mod disk; pub mod filesystem; pub mod memory; pub mod tests; pub mod utils; use indexmap::IndexMap; use log::debug; use lru::LruCache; use std::collections::HashMap; use std::ffi::OsString; use std::num::NonZeroUsize; use std::sync::atomic::AtomicU64; use std::sync::Arc; use std::time::Duration; use libc::{gid_t, uid_t}; use crate::disk::block::{DirectoryEntry, InodeBlock, SuperBlock}; use crate::memory::cached_block::BlockCache; use block_device::{BlockDevice, BLOCK_SIZE}; use disk::bitmap::Bitmap; use disk::block::DataBlock; use crate::disk::inode::INODE_PER_BLOCK; const TTL: Duration = Duration::new(0, 0); /// 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)] pub struct AyaFS { device: Arc, data_bitmap: Bitmap, inode_bitmap: Bitmap, inode_start_block: usize, data_start_block: usize, next_file_handle: AtomicU64, // file descriptor -> (inode index, read, write) file_handle_map: HashMap, // inode index -> (index aware) hashmap that maps dir entry name to inode index dir_entry_map: LruCache>, cached_inodes: BlockCache, cached_blocks: BlockCache, super_block: SuperBlock, } impl AyaFS { pub fn new(device: Arc, total_block_number: usize, uid: uid_t, gid: gid_t) -> 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!("data_bitmap_block_number: {}", data_bitmap_block_number); debug!("inode_bitmap_block_number: {}", inode_bitmap_block_number); debug!("inode_block_number: {}", inode_block_number); debug!("data_block_number: {}", data_block_number); debug!( "sum: {}", 1 + data_bitmap_block_number + inode_bitmap_block_number + inode_block_number + data_block_number ); let super_block = SuperBlock::new( data_bitmap_block_number, inode_bitmap_block_number, inode_block_number, data_block_number, total_block_number, 0, 0 ); let mut data_bitmap = Bitmap::new(1, data_bitmap_block_number, device.clone(), 0); let _ = data_bitmap.allocate().unwrap(); // data block 0 is not usable let mut inode_bitmap = Bitmap::new( data_bitmap_block_number + 1, inode_bitmap_block_number, device.clone(), 0, ); let _ = inode_bitmap.allocate().unwrap(); // inode block 0 is not usable let inode_start_block = data_bitmap_block_number + inode_bitmap_block_number + 1; let data_start_block = inode_start_block + inode_block_number; let mut fs = Self { device: device.clone(), data_bitmap, inode_bitmap, inode_start_block, data_start_block, next_file_handle: AtomicU64::new(3), // 0,1,2 are stdin, stdout and stderr file_handle_map: HashMap::new(), dir_entry_map: LruCache::new(NonZeroUsize::new(1024).unwrap()), cached_inodes: BlockCache::new(device.clone(), 8192, inode_start_block), cached_blocks: BlockCache::new(device.clone(), 16384, data_start_block), super_block, }; fs.create_directory(0o755, uid, gid, 0, None); fs } pub fn load(device: Arc) -> Self { let mut buffer = [0u8; 4096]; device.read(0, &mut buffer); let super_block: SuperBlock = unsafe { std::mem::transmute_copy(&buffer) }; let data_bitmap_block_number = super_block.data_bitmap_block_number as usize; let inode_bitmap_block_number = super_block.inode_bitmap_block_number as usize; let inode_block_number = super_block.inode_block_number as usize; let data_block_number = super_block.data_block_number as usize; debug!("data_bitmap_block_number: {}", data_bitmap_block_number); debug!("inode_bitmap_block_number: {}", inode_bitmap_block_number); debug!("inode_block_number: {}", inode_block_number); debug!("data_block_number: {}", data_block_number); debug!("total_block_number: {}", super_block.total_block_number); debug!( "sum: {}", 1 + data_bitmap_block_number + inode_bitmap_block_number + inode_block_number + data_block_number ); let data_bitmap = Bitmap::load( 1, data_bitmap_block_number, device.clone(), super_block.used_block_number, ); debug!("data_bitmap starting from: {:?}", data_bitmap.peek()); let inode_bitmap = Bitmap::load( data_bitmap_block_number + 1, inode_bitmap_block_number, device.clone(), super_block.used_inode_number, ); debug!("inode_bitmap starting from: {:?}", inode_bitmap.peek()); let inode_start_block = data_bitmap_block_number + inode_bitmap_block_number + 1; let data_start_block = inode_start_block + inode_block_number; Self { device: device.clone(), data_bitmap, inode_bitmap, inode_start_block, data_start_block, next_file_handle: AtomicU64::new(3), // 0,1,2 are stdin, stdout and stderr file_handle_map: HashMap::new(), dir_entry_map: LruCache::new(NonZeroUsize::new(1024).unwrap()), cached_inodes: BlockCache::new(device.clone(), 1024, inode_start_block), cached_blocks: BlockCache::new(device.clone(), 8192, data_start_block), super_block, } } pub fn write_back(&mut self) { self.super_block.used_inode_number = self.inode_bitmap.count; self.super_block.used_block_number = self.data_bitmap.count; let super_block_buffer = unsafe { let ptr = &self.super_block as *const SuperBlock as *const u8; std::slice::from_raw_parts(ptr, std::mem::size_of::()) }; self.device.write(0, super_block_buffer); while let Some((inode_index, dir_entry_map)) = self.dir_entry_map.pop_lru() { debug!("writing back direntry map for inode {}", inode_index); let mut inode = self.get_inode(inode_index).unwrap().clone(); self.write_back_direntry( inode_index, &mut inode, dir_entry_map, ).unwrap(); self.update_inode(inode_index, inode); } // dir entry 的 write back 是写回到 block / inode cache 里, 所以要在前面 debug!("data_bitmap stopping at: {:?}", self.data_bitmap.peek()); debug!("inode_bitmap stopping at: {:?}", self.inode_bitmap.peek()); self.data_bitmap.write_back(); self.inode_bitmap.write_back(); self.cached_blocks.write_back(); self.cached_inodes.write_back(); } }