lifeblood_os/src/fs/fat32.rs

422 lines
No EOL
13 KiB
Rust

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<BPBUseful, Fat32Error> {
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;
}
}
*/