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 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 disk::inode::INODE_SIZE; use users::{get_current_gid, get_current_uid}; const TTL: Duration = Duration::new(0, 0); 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)] 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) -> 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, ); let mut data_bitmap = Bitmap::new(1, data_bitmap_block_number, device.clone()); 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(), ); let _ = inode_bitmap.allocate().unwrap(); // inode block 0 is not usable let mut fs = Self { device: device.clone(), 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, 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), cached_blocks: BlockCache::new(device.clone(), 8192), super_block, }; fs.create_directory(0o755, get_current_uid(), get_current_gid(), 0, None); fs } }