use crate::hw::virtio_blk::VirtioBlk; const SECTOR_SIZE: usize = 512; struct BootFat16 { reserved_sectors: u16, root_dir_start: u32, root_dir_sectors: u32, data_start: u32, sectors_per_cluster: u8, } impl BootFat16 { fn parse(boot_sector: &[u8; SECTOR_SIZE]) -> Option { if boot_sector[510] != 0x55 || boot_sector[511] != 0xAA { return None; } let bytes_per_sector = u16::from_le_bytes([boot_sector[11], boot_sector[12]]); if bytes_per_sector != SECTOR_SIZE as u16 { return None; } let sectors_per_cluster = boot_sector[13]; let reserved_sectors = u16::from_le_bytes([boot_sector[14], boot_sector[15]]); let num_fats = boot_sector[16]; let root_entry_count = u16::from_le_bytes([boot_sector[17], boot_sector[18]]); let sectors_per_fat = u16::from_le_bytes([boot_sector[22], boot_sector[23]]); let fat_start = reserved_sectors as u32; let root_dir_start = fat_start + (num_fats as u32) * (sectors_per_fat as u32); let root_dir_sectors = ((root_entry_count as u32) * 32).div_ceil(512); let data_start = root_dir_start + root_dir_sectors; Some(Self { reserved_sectors, root_dir_start, root_dir_sectors, data_start, sectors_per_cluster, }) } fn cluster_to_sector(&self, cluster: u16) -> u32 { self.data_start + ((cluster as u32) - 2) * (self.sectors_per_cluster as u32) } } fn to_fat_name(name: &[u8]) -> [u8; 11] { let mut fat = [b' '; 11]; let dot_pos = name.iter().position(|&b| b == b'.').unwrap_or(name.len()); for i in 0..dot_pos.min(8) { fat[i] = name[i].to_ascii_uppercase(); } if dot_pos < name.len() { for i in 0..(name.len() - dot_pos - 1).min(3) { fat[8 + i] = name[dot_pos + 1 + i].to_ascii_uppercase(); } } fat } pub fn read_file_from_disk( blk: &mut VirtioBlk, filename: &[u8], out_buf: &mut [u8], ) -> Option { let mut sector_buf = [0u8; SECTOR_SIZE]; if !blk.read_sector(0, &mut sector_buf) { return None; } let fs = BootFat16::parse(§or_buf)?; let fat_name = to_fat_name(filename); let mut first_cluster: u16 = 0; let mut file_size: u32 = 0; let mut found = false; for s in 0..fs.root_dir_sectors { if !blk.read_sector(fs.root_dir_start + s, &mut sector_buf) { continue; } for i in 0..(SECTOR_SIZE / 32) { let off = i * 32; if sector_buf[off] == 0x00 { break; } if sector_buf[off] == 0xE5 { continue; } if sector_buf[off..off + 11] == fat_name { first_cluster = u16::from_le_bytes([sector_buf[off + 26], sector_buf[off + 27]]); file_size = u32::from_le_bytes([ sector_buf[off + 28], sector_buf[off + 29], sector_buf[off + 30], sector_buf[off + 31], ]); found = true; break; } } if found { break; } } if !found { return None; } let size = (file_size as usize).min(out_buf.len()); let mut read = 0; let mut cluster = first_cluster; let sectors_per_cluster = fs.sectors_per_cluster as u32; while read < size && (2..0xFFF8).contains(&cluster) { for s in 0..sectors_per_cluster { if read >= size { break; } let sector = fs.cluster_to_sector(cluster) + s; if !blk.read_sector(sector, &mut sector_buf) { return Some(read); } let chunk = (size - read).min(SECTOR_SIZE); out_buf[read..read + chunk].copy_from_slice(§or_buf[..chunk]); read += chunk; } let fat_offset = (cluster as u32) * 2; let fat_sector = fs.reserved_sectors as u32 + fat_offset / SECTOR_SIZE as u32; let fat_byte = (fat_offset % SECTOR_SIZE as u32) as usize; if !blk.read_sector(fat_sector, &mut sector_buf) { break; } cluster = u16::from_le_bytes([sector_buf[fat_byte], sector_buf[fat_byte + 1]]); } Some(read) }