use crate::fs::{Record, RecordType}; use crate::strprint::u32_hex; #[repr(packed(1), C)] pub struct BPB { pub bootjmp: [u8; 3], pub oem_name: [u8; 8], pub bytes_per_sector: u16, pub sectors_per_cluster: u8, pub reserved_sectors: u16, pub num_fats: u8, pub root_entry_count: u16, pub total_sectors_16: u16, pub media_type: u8, pub sectors_per_fat16: u16, pub sectors_per_track: u16, pub head_count: u16, pub hidden_sectors: u32, pub large_sector_count: u32, pub sectors_per_fat: u32, pub flags: u16, pub version: u16, pub root_cluster: u32, pub fsinfo_sector: u16, pub backup_boot_sector: u16, pub reserved0: [u8; 12], pub drive_number: u8, pub reserved1: u8, pub signature: u8, pub serial_number: u32, pub label: [u8; 11], pub identifier: [u8; 8], } #[repr(C)] pub struct BPBUseful { pub oem_name: [u8; 8], pub reserved_sectors: u16, pub num_fats: u8, pub sectors_per_fat_16: u32, pub sectors_per_fat: u32, pub sectors_per_cluster: u8, pub root_cluster: u32, pub padding: [u8; 1], } impl BPBUseful { pub fn empty() -> Self { Self { oem_name: [0; 8], reserved_sectors: 0, num_fats: 0, sectors_per_fat_16: 0, sectors_per_fat: 0, sectors_per_cluster: 0, root_cluster: 0, padding: [0; 1], } } pub fn fat_size(&self) -> usize { self.sectors_per_fat as usize } pub fn first_data_sector(&self) -> usize { self.reserved_sectors as usize + (self.num_fats as usize * self.fat_size()) } pub fn first_fat_sector(&self) -> usize { self.reserved_sectors as usize } } #[repr(packed(1), C)] pub struct DirectoryEntry { pub name: [u8; 8], pub ext: [u8; 3], pub attributes: u8, pub reserved: u8, pub creation_time_tenth: u8, pub creation_time: u16, pub creation_date: u16, pub last_access_date: u16, pub first_cluster_high: u16, pub last_modified_time: u16, pub last_modified_date: u16, pub first_cluster_low: u16, pub file_size: u32, } /* #[repr(packed(1), C)] pub struct LongFileNameEntry { pub order: u8, pub first_five_two_byte_characters: [u16; 5], // [u8; 10] pub attributes: u8, // always 0x0F pub long_entry_type: u8, pub checksum: u8, pub next_six_two_byte_characters: [u16; 6], // [u8; 12] pub reserved: u16, pub final_two_two_byte_characters: [u16; 2], // [u8; 4] pub directory_entry: DirectoryEntry, } */ #[repr(C)] pub struct Fat32DirectoryReader { pub underlying: Fat32FileReader, } impl Fat32DirectoryReader { pub fn empty() -> Self { Self { underlying: Fat32FileReader::empty(), } } } #[repr(C)] #[derive(Copy, Clone, Debug)] pub struct Fat32FileReader { pub cluster: usize, pub sector: usize, pub cluster_idx: usize, pub sector_offset: usize, pub eof: u8, pub implementation: u32, pub padding: [u8; 32 - 24], } impl Fat32FileReader { pub fn empty() -> Self { Self { cluster: 0, sector: 0, cluster_idx: 0, sector_offset: 0, eof: 0, implementation: 0, padding: [0; 32 - 24], } } } pub enum Fat32Error { CouldNotReadBPB, } pub fn read_bpb() -> Result { let bpb_addr = crate::syscalls::alloc_blocks(1); if crate::syscalls::read_hbd(0, bpb_addr, 1) != 0 { return Err(Fat32Error::CouldNotReadBPB); } let bpb = unsafe { (bpb_addr as *const BPB).read_volatile() }; crate::syscalls::free_blocks(bpb_addr, 1); Ok(BPBUseful { oem_name: bpb.oem_name, reserved_sectors: bpb.reserved_sectors, num_fats: bpb.num_fats, sectors_per_fat_16: bpb.sectors_per_fat16 as u32, sectors_per_fat: bpb.sectors_per_fat, sectors_per_cluster: bpb.sectors_per_cluster, root_cluster: bpb.root_cluster, padding: [0; 1], }) } pub fn reader_from_cluster(bpb: &BPBUseful, cluster: usize) -> Fat32FileReader { let first_sector_of_cluster = ((cluster - 2) * bpb.sectors_per_cluster as usize) + bpb.first_data_sector(); Fat32FileReader { cluster, sector: first_sector_of_cluster, cluster_idx: 0, sector_offset: 0, eof: 0, implementation: 0, padding: [0; 32 - 24], } } pub fn root_target(bpb: &BPBUseful) -> u32 { bpb.root_cluster as u32 } pub fn root_reader(bpb: &BPBUseful) -> Fat32DirectoryReader { let cluster = bpb.root_cluster as usize; Fat32DirectoryReader { underlying: reader_from_cluster(bpb, cluster), } } /// returns true if the record was read successfully, false if the end of the directory was reached pub fn read_one_record( bpb: &BPBUseful, reader: &mut Fat32DirectoryReader, record: &mut Record, ) -> bool { let block_addr = crate::syscalls::alloc_blocks(1); let buf = unsafe { core::slice::from_raw_parts_mut(block_addr as *mut u8, 512) }; // read without seeking let old_reader = reader.underlying; if !read_file(bpb, &mut reader.underlying, buf) { crate::syscalls::free_blocks(block_addr, 1); crate::syscalls::write_terminal(b"eof on dir"); return false; } reader.underlying = old_reader; let mut done = false; while !done { let entry = unsafe { ((buf.as_ptr() as usize + reader.underlying.implementation as usize) as *const DirectoryEntry) .read_volatile() }; if entry.name[0] == 0x00 { // end of directory crate::syscalls::free_blocks(block_addr, 1); return false; } #[allow(clippy::collapsible_if)] if entry.name[0] != 0xE5 { if entry.attributes != 0x0F { // for now we do not support long filenames let mut namelen = 0; let mut last_was_space = false; for i in 0..8 { if last_was_space { if entry.name[i] != b' ' { last_was_space = false; } } else if entry.name[i] == b' ' { namelen = i; last_was_space = true; } } if namelen == 0 { namelen = 8; } record.name = [0; 12]; record.name[..namelen].copy_from_slice(&entry.name[..namelen]); if entry.attributes & 0x10 != 0 { record.record_type = RecordType::Directory as u8; } else { record.record_type = RecordType::File as u8; record.name[namelen] = b'.'; record.name[namelen + 1] = entry.ext[0]; record.name[namelen + 2] = entry.ext[1]; record.name[namelen + 3] = entry.ext[2]; } record.target = (entry.first_cluster_high as u32) << 16 | (entry.first_cluster_low as u32); record.total_size_bytes = entry.file_size; done = true; } } reader.underlying.implementation += 32; if reader.underlying.implementation >= 512 { reader.underlying.implementation = 0; seek_forward_one_sector(bpb, &mut reader.underlying); if !read_file(bpb, &mut reader.underlying, buf) { crate::syscalls::free_blocks(block_addr, 1); return false; } } } crate::syscalls::free_blocks(block_addr, 1); true } pub fn seek_forward_one_sector(bpb: &BPBUseful, reader: &mut Fat32FileReader) -> bool { if reader.eof != 0 { return false; } reader.sector += 1; reader.sector_offset = 0; reader.cluster_idx += 1; // if we reached the end of the cluster, let's consult the FAT to see where to go next if reader.cluster_idx >= bpb.sectors_per_cluster as usize { let fat_addr = crate::syscalls::alloc_blocks(1); let fat_ofs = reader.cluster * 4; let fat_sector = bpb.first_fat_sector() + (fat_ofs / 512); let ent_offset = fat_ofs % 512; if crate::syscalls::read_hbd(fat_sector, fat_addr, 1) != 0 { crate::syscalls::free_blocks(fat_addr, 1); crate::syscalls::write_terminal(b"fatfailure"); return false; } let table_value = unsafe { ((fat_addr+(ent_offset)) as *const u32).read_volatile() } & 0x0FFFFFFF; crate::syscalls::free_blocks(fat_addr, 1); if table_value >= 0x0FFFFFF8 { // no more clusters, whole file has been read reader.eof = 1; return false; } else if table_value >= 0x00000002 { reader.cluster = table_value as usize; reader.cluster_idx = 0; reader.sector = ((reader.cluster - 2) * bpb.sectors_per_cluster as usize) + bpb.first_data_sector(); reader.sector_offset = 0; if table_value == 0x00000000 || table_value == 0x00000001 { reader.eof = 1; return false; } else if table_value == 0x0FFFFFF7 { // bad cluster, stop reading crate::syscalls::write_terminal(b"badcluster"); reader.eof = 1; return false; } } else { // cluster is free, keep going reader.eof = 1; return false; } } // continue reading true } pub fn read_file(bpb: &BPBUseful, reader: &mut Fat32FileReader, buffer: &mut [u8]) -> bool { if reader.eof != 0 { return false; } let mut buffer_idx = 0; let sector_addr = crate::syscalls::alloc_blocks(1); loop { if reader.eof != 0 { crate::syscalls::free_blocks(sector_addr, 1); return false; } // read current sector if crate::syscalls::read_hbd(reader.sector, sector_addr, 1) != 0 { crate::syscalls::free_blocks(sector_addr, 1); crate::syscalls::write_terminal(b"badhdb"); return false; } let mut left_in_sector = 512 - reader.sector_offset; if left_in_sector == 0 { crate::syscalls::write_terminal(b"SECTORNULL?"); crate::syscalls::free_blocks(sector_addr, 1); return false; } while buffer_idx < buffer.len() && left_in_sector > 0 { buffer[buffer_idx] = unsafe { (sector_addr as *const u8) .add(reader.sector_offset) .read_volatile() }; buffer_idx += 1; left_in_sector -= 1; reader.sector_offset += 1; } // if sector is full, make sure next read will start at the correct position #[allow(clippy::collapsible_if)] if left_in_sector == 0 { // we don't care about the return value because if it fails, it'll be handled by the next iteration seek_forward_one_sector(bpb, reader); } // either buffer got filled, or we need to read the next sector if buffer_idx < buffer.len() { // we must've reached the end of the sector, let's read the next one continue; } else { // buffer got filled, we're done crate::syscalls::free_blocks(sector_addr, 1); return true; } } } /* pub fn read_directory(bpb: &BPBUseful, cluster: usize) { let mut first_sector_of_cluster = ((cluster - 2) * bpb.sectors_per_cluster as usize) + bpb.first_data_sector(); let mut fsoc_addr = crate::syscalls::alloc_blocks(1); if crate::syscalls::read_hbd(first_sector_of_cluster, fsoc_addr, 1) != 0 { crate::syscalls::free_blocks(fsoc_addr, 1); crate::syscalls::write_terminal(b"fsoc"); return; } let mut traveled_in_sector = 0; loop { let entry = unsafe { (fsoc_addr as *const DirectoryEntry).read_volatile() }; if entry.name[0] == 0x00 { break; } if entry.name[0] != 0xE5 { if entry.attributes == 0x0F { //crate::syscalls::write_terminal(b" unsupported utf16"); } else { crate::syscalls::write_terminal(b" - "); let namelen = entry.name.iter().position(|&x| x == 0).unwrap_or(8); crate::syscalls::write_terminal(&entry.name[..namelen]); if entry.attributes & 0x10 != 0 { crate::syscalls::write_terminal(b" (dir)"); } else { let extlen = entry.ext.iter().position(|&x| x == 0).unwrap_or(3); crate::syscalls::write_terminal(b"."); crate::syscalls::write_terminal(&entry.ext[..extlen]); } crate::syscalls::write_terminal(b"\n"); } } fsoc_addr += 32; traveled_in_sector += 32; if traveled_in_sector >= 512 { fsoc_addr -= traveled_in_sector; traveled_in_sector = 0; first_sector_of_cluster += 1; if crate::syscalls::read_hbd(first_sector_of_cluster, fsoc_addr, 1) != 0 { crate::syscalls::free_blocks(fsoc_addr, 1); crate::syscalls::write_terminal(b"fsoc"); return; } } continue; } } */