use super::{get_block_cache, BlockDevice, BLOCK_SZ}; use alloc::sync::Arc; use alloc::vec::Vec; use core::fmt::{Debug, Formatter, Result}; const EFS_MAGIC: u32 = 0x3b800001; const INODE_DIRECT_COUNT: usize = 28; const NAME_LENGTH_LIMIT: usize = 27; const INODE_INDIRECT1_COUNT: usize = BLOCK_SZ / 4; const INODE_INDIRECT2_COUNT: usize = INODE_INDIRECT1_COUNT * INODE_INDIRECT1_COUNT; const DIRECT_BOUND: usize = INODE_DIRECT_COUNT; const INDIRECT1_BOUND: usize = DIRECT_BOUND + INODE_INDIRECT1_COUNT; #[allow(unused)] const INDIRECT2_BOUND: usize = INDIRECT1_BOUND + INODE_INDIRECT2_COUNT; #[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 } } #[derive(PartialEq)] pub enum DiskInodeType { File, Directory, } type IndirectBlock = [u32; BLOCK_SZ / 4]; type DataBlock = [u8; BLOCK_SZ]; #[repr(C)] pub struct DiskInode { pub size: u32, pub direct: [u32; INODE_DIRECT_COUNT], pub indirect1: u32, pub indirect2: u32, type_: DiskInodeType, } impl DiskInode { /// indirect1 and indirect2 block are allocated only when they are needed. 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_; } pub fn is_dir(&self) -> bool { self.type_ == DiskInodeType::Directory } #[allow(unused)] pub fn is_file(&self) -> bool { self.type_ == DiskInodeType::File } /// Return block number correspond to size. pub fn data_blocks(&self) -> u32 { Self::_data_blocks(self.size) } fn _data_blocks(size: u32) -> u32 { (size + BLOCK_SZ as u32 - 1) / BLOCK_SZ as u32 } /// Return number of blocks needed include indirect1/2. pub fn total_blocks(size: u32) -> u32 { let data_blocks = Self::_data_blocks(size) as usize; let mut total = data_blocks as usize; // indirect1 if data_blocks > INODE_DIRECT_COUNT { total += 1; } // indirect2 if data_blocks > INDIRECT1_BOUND { total += 1; // sub indirect1 total += (data_blocks - INDIRECT1_BOUND + INODE_INDIRECT1_COUNT - 1) / INODE_INDIRECT1_COUNT; } total as u32 } pub fn blocks_num_needed(&self, new_size: u32) -> u32 { assert!(new_size >= self.size); Self::total_blocks(new_size) - Self::total_blocks(self.size) } pub fn get_block_id(&self, inner_id: u32, block_device: &Arc) -> u32 { let inner_id = inner_id as usize; if inner_id < INODE_DIRECT_COUNT { self.direct[inner_id] } else if inner_id < INDIRECT1_BOUND { get_block_cache(self.indirect1 as usize, Arc::clone(block_device)) .lock() .read(0, |indirect_block: &IndirectBlock| { indirect_block[inner_id - INODE_DIRECT_COUNT] }) } else { let last = inner_id - INDIRECT1_BOUND; let indirect1 = get_block_cache(self.indirect2 as usize, Arc::clone(block_device)) .lock() .read(0, |indirect2: &IndirectBlock| { indirect2[last / INODE_INDIRECT1_COUNT] }); get_block_cache(indirect1 as usize, Arc::clone(block_device)) .lock() .read(0, |indirect1: &IndirectBlock| { indirect1[last % INODE_INDIRECT1_COUNT] }) } } pub fn increase_size( &mut self, new_size: u32, new_blocks: Vec, block_device: &Arc, ) { let mut current_blocks = self.data_blocks(); self.size = new_size; let mut total_blocks = self.data_blocks(); let mut new_blocks = new_blocks.into_iter(); // fill direct while current_blocks < total_blocks.min(INODE_DIRECT_COUNT as u32) { self.direct[current_blocks as usize] = new_blocks.next().unwrap(); current_blocks += 1; } // alloc indirect1 if total_blocks > INODE_DIRECT_COUNT as u32 { if current_blocks == INODE_DIRECT_COUNT as u32 { self.indirect1 = new_blocks.next().unwrap(); } current_blocks -= INODE_DIRECT_COUNT as u32; total_blocks -= INODE_DIRECT_COUNT as u32; } else { return; } // fill indirect1 get_block_cache(self.indirect1 as usize, Arc::clone(block_device)) .lock() .modify(0, |indirect1: &mut IndirectBlock| { while current_blocks < total_blocks.min(INODE_INDIRECT1_COUNT as u32) { indirect1[current_blocks as usize] = new_blocks.next().unwrap(); current_blocks += 1; } }); // alloc indirect2 if total_blocks > INODE_INDIRECT1_COUNT as u32 { if current_blocks == INODE_INDIRECT1_COUNT as u32 { self.indirect2 = new_blocks.next().unwrap(); } current_blocks -= INODE_INDIRECT1_COUNT as u32; total_blocks -= INODE_INDIRECT1_COUNT as u32; } else { return; } // fill indirect2 from (a0, b0) -> (a1, b1) let mut a0 = current_blocks as usize / INODE_INDIRECT1_COUNT; let mut b0 = current_blocks as usize % INODE_INDIRECT1_COUNT; let a1 = total_blocks as usize / INODE_INDIRECT1_COUNT; let b1 = total_blocks as usize % INODE_INDIRECT1_COUNT; // alloc low-level indirect1 get_block_cache(self.indirect2 as usize, Arc::clone(block_device)) .lock() .modify(0, |indirect2: &mut IndirectBlock| { while (a0 < a1) || (a0 == a1 && b0 < b1) { if b0 == 0 { indirect2[a0] = new_blocks.next().unwrap(); } // fill current get_block_cache(indirect2[a0] as usize, Arc::clone(block_device)) .lock() .modify(0, |indirect1: &mut IndirectBlock| { indirect1[b0] = new_blocks.next().unwrap(); }); // move to next b0 += 1; if b0 == INODE_INDIRECT1_COUNT { b0 = 0; a0 += 1; } } }); } /// Clear size to zero and return blocks that should be deallocated. /// /// We will clear the block contents to zero later. pub fn clear_size(&mut self, block_device: &Arc) -> Vec { let mut v: Vec = Vec::new(); let mut data_blocks = self.data_blocks() as usize; self.size = 0; let mut current_blocks = 0usize; // direct while current_blocks < data_blocks.min(INODE_DIRECT_COUNT) { v.push(self.direct[current_blocks]); self.direct[current_blocks] = 0; current_blocks += 1; } // indirect1 block if data_blocks > INODE_DIRECT_COUNT { v.push(self.indirect1); data_blocks -= INODE_DIRECT_COUNT; current_blocks = 0; } else { return v; } // indirect1 get_block_cache(self.indirect1 as usize, Arc::clone(block_device)) .lock() .modify(0, |indirect1: &mut IndirectBlock| { while current_blocks < data_blocks.min(INODE_INDIRECT1_COUNT) { v.push(indirect1[current_blocks]); //indirect1[current_blocks] = 0; current_blocks += 1; } }); self.indirect1 = 0; // indirect2 block if data_blocks > INODE_INDIRECT1_COUNT { v.push(self.indirect2); data_blocks -= INODE_INDIRECT1_COUNT; } else { return v; } // indirect2 assert!(data_blocks <= INODE_INDIRECT2_COUNT); let a1 = data_blocks / INODE_INDIRECT1_COUNT; let b1 = data_blocks % INODE_INDIRECT1_COUNT; get_block_cache(self.indirect2 as usize, Arc::clone(block_device)) .lock() .modify(0, |indirect2: &mut IndirectBlock| { // full indirect1 blocks for entry in indirect2.iter_mut().take(a1) { v.push(*entry); get_block_cache(*entry as usize, Arc::clone(block_device)) .lock() .modify(0, |indirect1: &mut IndirectBlock| { for entry in indirect1.iter() { v.push(*entry); } }); } // last indirect1 block if b1 > 0 { v.push(indirect2[a1]); get_block_cache(indirect2[a1] as usize, Arc::clone(block_device)) .lock() .modify(0, |indirect1: &mut IndirectBlock| { for entry in indirect1.iter().take(b1) { v.push(*entry); } }); //indirect2[a1] = 0; } }); self.indirect2 = 0; v } pub fn read_at( &self, offset: usize, buf: &mut [u8], block_device: &Arc, ) -> usize { let mut start = offset; let end = (offset + buf.len()).min(self.size as usize); if start >= end { return 0; } let mut start_block = start / BLOCK_SZ; let mut read_size = 0usize; loop { // calculate end of current block let mut end_current_block = (start / BLOCK_SZ + 1) * BLOCK_SZ; end_current_block = end_current_block.min(end); // read and update read size let block_read_size = end_current_block - start; let dst = &mut buf[read_size..read_size + block_read_size]; get_block_cache( self.get_block_id(start_block as u32, block_device) as usize, Arc::clone(block_device), ) .lock() .read(0, |data_block: &DataBlock| { let src = &data_block[start % BLOCK_SZ..start % BLOCK_SZ + block_read_size]; dst.copy_from_slice(src); }); read_size += block_read_size; // move to next block if end_current_block == end { break; } start_block += 1; start = end_current_block; } read_size } /// File size must be adjusted before. pub fn write_at( &mut self, offset: usize, buf: &[u8], block_device: &Arc, ) -> usize { let mut start = offset; let end = (offset + buf.len()).min(self.size as usize); assert!(start <= end); let mut start_block = start / BLOCK_SZ; let mut write_size = 0usize; loop { // calculate end of current block let mut end_current_block = (start / BLOCK_SZ + 1) * BLOCK_SZ; end_current_block = end_current_block.min(end); // write and update write size let block_write_size = end_current_block - start; get_block_cache( self.get_block_id(start_block as u32, block_device) as usize, Arc::clone(block_device), ) .lock() .modify(0, |data_block: &mut DataBlock| { let src = &buf[write_size..write_size + block_write_size]; let dst = &mut data_block[start % BLOCK_SZ..start % BLOCK_SZ + block_write_size]; dst.copy_from_slice(src); }); write_size += block_write_size; // move to next block if end_current_block == end { break; } start_block += 1; start = end_current_block; } write_size } } #[repr(C)] pub struct DirEntry { name: [u8; NAME_LENGTH_LIMIT + 1], inode_number: u32, } pub const DIRENT_SZ: usize = 32; impl DirEntry { pub fn empty() -> Self { Self { name: [0u8; NAME_LENGTH_LIMIT + 1], inode_number: 0, } } pub fn new(name: &str, inode_number: u32) -> Self { let mut bytes = [0u8; NAME_LENGTH_LIMIT + 1]; bytes[..name.len()].copy_from_slice(name.as_bytes()); Self { name: bytes, inode_number, } } pub fn as_bytes(&self) -> &[u8] { unsafe { core::slice::from_raw_parts(self as *const _ as usize as *const u8, DIRENT_SZ) } } pub fn as_bytes_mut(&mut self) -> &mut [u8] { unsafe { core::slice::from_raw_parts_mut(self as *mut _ as usize as *mut u8, DIRENT_SZ) } } pub fn name(&self) -> &str { let len = (0usize..).find(|i| self.name[*i] == 0).unwrap(); core::str::from_utf8(&self.name[..len]).unwrap() } pub fn inode_number(&self) -> u32 { self.inode_number } }