pub mod io; pub mod superblock; pub mod bgd; pub mod bitmap; pub mod dir; mod extent; mod inode; mod path; use port_block_device::BlockDevice; use crate::filesystem::{DirEntry, FileHandle, FileName, FileSystem}; use io::read_block; use superblock::Superblock; const ROOT_INODE: u32 = 2; const MAX_OPEN_FILES: usize = 8; const EXTENT_MAGIC: u16 = 0xF30A; pub use inode::{Extent, Inode}; #[derive(Clone, Copy)] struct OpenFile { inode_num: u32, size: usize, offset: usize, name: FileName, block_data: [u8; 60], } pub struct Ext4 { device: B, sb: Superblock, gdt_block: u32, open_files: [Option; MAX_OPEN_FILES], } impl Ext4 { pub fn mount(mut device: B) -> Option { let mut buf = [0u8; 1024]; if !device.read_sector(port_block_device::SectorId::new(2), &mut buf[..512]) { return None; } if !device.read_sector(port_block_device::SectorId::new(3), &mut buf[512..]) { return None; } let sb = Superblock::parse(&buf)?; let gdt_block = sb.first_data_block + 1; Some(Self { device, sb, gdt_block, open_files: [const { None }; MAX_OPEN_FILES], }) } } impl FileSystem for Ext4 { fn open(&mut self, path: &[u8]) -> Option { let inode_num = self.resolve_path(path)?; let inode = self.read_inode(inode_num)?; if !inode.is_file() { return None; } let handle = self.alloc_handle()?; self.open_files[handle.raw() as usize] = Some(OpenFile { inode_num, size: inode.size as usize, offset: 0, name: FileName::from_bytes(path), block_data: inode.block_data, }); Some(handle) } fn read(&mut self, handle: FileHandle, buf: &mut [u8]) -> Option { if handle.raw() as usize >= MAX_OPEN_FILES { return None; } let file = *self.open_files[handle.raw() as usize].as_ref()?; if file.offset >= file.size { return Some(0); } let remaining = file.size - file.offset; let to_read = buf.len().min(remaining); let bs = self.sb.block_size; let logical_block = (file.offset / bs) as u32; let byte_in_block = file.offset % bs; let inode = Inode { mode: 0, size: file.size as u64, flags: 0x80000, links_count: 0, blocks_count: 0, block_data: file.block_data, }; let phys_block = self.map_logical_block(&inode, logical_block)?; let mut block_buf = [0u8; 4096]; read_block(&mut self.device, phys_block, self.sb.sectors_per_block(), &mut block_buf); let available = bs - byte_in_block; let chunk = to_read.min(available); buf[..chunk].copy_from_slice(&block_buf[byte_in_block..byte_in_block + chunk]); self.open_files[handle.raw() as usize].as_mut().map(|f| f.offset += chunk); Some(chunk) } fn write(&mut self, handle: FileHandle, buf: &[u8]) -> Option { if handle.raw() as usize >= MAX_OPEN_FILES { return None; } let file = *self.open_files[handle.raw() as usize].as_ref()?; let bs = self.sb.block_size; let mut written = 0; let mut offset = file.offset; let mut block_data = file.block_data; let mut size = file.size; let inode_num = file.inode_num; while written < buf.len() { let logical_block = (offset / bs) as u32; let byte_in_block = offset % bs; let tmp_inode = Inode { mode: 0, size: size as u64, flags: 0x80000, links_count: 0, blocks_count: 0, block_data, }; let phys_block = match self.map_logical_block(&tmp_inode, logical_block) { Some(p) => p, None => { let new_block = match self.alloc_block() { Some(b) => b, None => break, }; if !Self::append_extent(&mut block_data, logical_block, new_block as u64, 1) { self.free_block(new_block); break; } new_block as u64 } }; let mut block_buf = [0u8; 4096]; if byte_in_block > 0 || (buf.len() - written) < bs { read_block(&mut self.device, phys_block, self.sb.sectors_per_block(), &mut block_buf); } let available = bs - byte_in_block; let chunk = available.min(buf.len() - written); block_buf[byte_in_block..byte_in_block + chunk].copy_from_slice(&buf[written..written + chunk]); io::write_block(&mut self.device, phys_block, self.sb.sectors_per_block(), &block_buf); written += chunk; offset += chunk; if offset > size { size = offset; } } if written > 0 { if let Some(f) = self.open_files[handle.raw() as usize].as_mut() { f.offset = offset; f.size = size; f.block_data = block_data; } let mut inode = match self.read_inode(inode_num) { Some(i) => i, None => return Some(written), }; inode.size = size as u64; inode.block_data = block_data; inode.blocks_count = Self::count_extent_blocks(&block_data) * (bs as u32 / 512); self.write_inode(inode_num, &inode); } Some(written) } fn close(&mut self, handle: FileHandle) { if handle.raw() as usize >= MAX_OPEN_FILES { return; } if let Some(file) = self.open_files[handle.raw() as usize].take() { if let Some(mut inode) = self.read_inode(file.inode_num) { inode.size = file.size as u64; inode.block_data = file.block_data; self.write_inode(file.inode_num, &inode); } } } fn list_dir(&mut self, path: &[u8], entries: &mut [DirEntry]) -> Option { let dir_inode_num = if path.is_empty() || path == b"/" { ROOT_INODE } else { self.resolve_path(path)? }; let inode = self.read_inode(dir_inode_num)?; let count = dir::list_entries(&mut self.device, &self.sb, &inode, entries)?; for i in 0..count { if !entries[i].is_dir { let name = entries[i].name; if let Some(child_ino) = dir::find_entry(&mut self.device, &self.sb, self.gdt_block, &inode, name.as_bytes()) { if let Some(child) = self.read_inode(child_ino) { entries[i].size = child.size as usize; } } } } Some(count) } fn create(&mut self, path: &[u8]) -> Option { let (parent_inode_num, filename) = self.resolve_parent(path)?; let parent_inode = self.read_inode(parent_inode_num)?; if !parent_inode.is_dir() { return None; } if dir::find_entry(&mut self.device, &self.sb, self.gdt_block, &parent_inode, filename).is_some() { return None; } let new_inode_num = self.alloc_inode()?; let new_inode = Self::init_empty_inode(0x8000 | 0o644); self.write_inode(new_inode_num, &new_inode); let parent_inode = self.read_inode(parent_inode_num)?; if !dir::add_entry(&mut self.device, &self.sb, self.gdt_block, &parent_inode, filename, new_inode_num, 1) { self.free_inode(new_inode_num); return None; } let handle = self.alloc_handle()?; self.open_files[handle.raw() as usize] = Some(OpenFile { inode_num: new_inode_num, size: 0, offset: 0, name: FileName::from_bytes(path), block_data: new_inode.block_data, }); Some(handle) } fn delete(&mut self, path: &[u8]) -> bool { let (parent_inode_num, filename) = match self.resolve_parent(path) { Some(v) => v, None => return false, }; let parent_inode = match self.read_inode(parent_inode_num) { Some(i) => i, None => return false, }; let child_inode_num = match dir::find_entry(&mut self.device, &self.sb, self.gdt_block, &parent_inode, filename) { Some(n) => n, None => return false, }; if let Some(child_inode) = self.read_inode(child_inode_num) { if child_inode.is_dir() { return false; } } let parent_inode = match self.read_inode(parent_inode_num) { Some(i) => i, None => return false, }; if dir::remove_entry(&mut self.device, &self.sb, self.gdt_block, &parent_inode, filename).is_none() { return false; } if let Some(child_inode) = self.read_inode(child_inode_num) { self.free_all_blocks(&child_inode); } self.free_inode(child_inode_num); for slot in self.open_files.iter_mut() { if let Some(f) = slot { if f.inode_num == child_inode_num { *slot = None; } } } true } fn file_size(&mut self, handle: FileHandle) -> Option { if handle.raw() as usize >= MAX_OPEN_FILES { return None; } self.open_files[handle.raw() as usize].as_ref().map(|f| f.size) } fn mkdir(&mut self, path: &[u8]) -> bool { let (parent_inode_num, dirname) = match self.resolve_parent(path) { Some(v) => v, None => return false, }; let parent_inode = match self.read_inode(parent_inode_num) { Some(i) => i, None => return false, }; if !parent_inode.is_dir() { return false; } if dir::find_entry(&mut self.device, &self.sb, self.gdt_block, &parent_inode, dirname).is_some() { return false; } let new_inode_num = match self.alloc_inode() { Some(n) => n, None => return false, }; let new_block = match self.alloc_block() { Some(b) => b, None => { self.free_inode(new_inode_num); return false; } }; let mut new_inode = Self::init_empty_inode(0x4000 | 0o755); new_inode.links_count = 2; new_inode.size = self.sb.block_size as u64; new_inode.blocks_count = (self.sb.block_size / 512) as u32; if !Self::append_extent(&mut new_inode.block_data, 0, new_block as u64, 1) { self.free_block(new_block); self.free_inode(new_inode_num); return false; } self.write_inode(new_inode_num, &new_inode); let mut dir_block = [0u8; 4096]; dir::init_dir_block(&mut dir_block, new_inode_num, parent_inode_num, self.sb.block_size); io::write_block(&mut self.device, new_block as u64, self.sb.sectors_per_block(), &dir_block); let parent_inode = match self.read_inode(parent_inode_num) { Some(i) => i, None => return false, }; if !dir::add_entry(&mut self.device, &self.sb, self.gdt_block, &parent_inode, dirname, new_inode_num, 2) { self.free_block(new_block); self.free_inode(new_inode_num); return false; } if let Some(mut pi) = self.read_inode(parent_inode_num) { pi.links_count += 1; self.write_inode(parent_inode_num, &pi); } true } }