use crate::models::{BiosParameterBlock, File, Partition};
pub type Result<T> = core::result::Result<T, Error>;
const LOGICAL_BLOCK_SIZE: u64 = 512;
const MAX_FILE_SIZE: u64 = 4294967296;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Error {
ReadError,
WriteError,
SliceError,
NotInitalisedError,
DiskFullError,
}
pub trait BlockIO {
fn read_block(&mut self, byte_offset: u64) -> Result<[u8; 512]>;
fn write_block(&mut self, byte_offset: u64, data: [u8; 512]) -> Result<()>;
}
pub struct Disk<T: BlockIO> {
io: T,
pub partitions: [Partition; 4],
pub partition: Option<Partition>,
pub bios_parameter_block: Option<BiosParameterBlock>,
pub reads: u32,
pub writes: u32,
}
struct FilePointer<'a, T: BlockIO> {
cluster: u64,
sector: u64,
sector_count: u64,
disk: &'a mut Disk<T>,
}
impl<'a, T: BlockIO> Iterator for FilePointer<'a, T> {
type Item = Result<([u8; 512], u64)>;
fn next(&mut self) -> Option<Self::Item> {
let partition_offset = self.disk.partition.unwrap().byte_offset;
let bios_parameter_block = self.disk.bios_parameter_block.unwrap();
let data_sector_byte_offset = bios_parameter_block.data_sector_bytes_offset;
let bytes_per_cluster = bios_parameter_block.bytes_per_cluster;
let root_dir_first_cluster = bios_parameter_block.root_cluster;
let sectors_per_cluster = bios_parameter_block.sectors_per_cluster;
let sector_to_read = self.sector % sectors_per_cluster;
if self.sector == self.sector_count || self.cluster >= 0x0FFFFFF8 {
None
} else {
let offset = data_sector_byte_offset
+ ((self.cluster - root_dir_first_cluster) * bytes_per_cluster);
let result = self
.disk
.read_file_block(partition_offset + offset + (sector_to_read * LOGICAL_BLOCK_SIZE));
if result.is_err() {
let e = result.err().unwrap();
return Some(Err(e));
}
let data = result.unwrap();
self.sector += 1;
if self.sector % sectors_per_cluster == 0 {
let cluster_result_option = self.disk.get_next_cluster(self.cluster);
match cluster_result_option {
None => return None,
Some(cluster_result) => {
if cluster_result.is_err() {
let e = result.err().unwrap();
return Some(Err(e));
}
self.cluster = cluster_result.unwrap();
}
}
}
Some(Ok((data, offset + (sector_to_read * LOGICAL_BLOCK_SIZE))))
}
}
}
fn make_file_pointer<T: BlockIO>(file: File, disk: &mut Disk<T>) -> FilePointer<T> {
let cluster = file.start_cluster as u64;
let mut sector_count = file.size / LOGICAL_BLOCK_SIZE;
if file.size % LOGICAL_BLOCK_SIZE != 0 {
sector_count += 1;
}
FilePointer {
cluster,
sector: 0,
sector_count,
disk,
}
}
pub struct FileContent<'a, T: BlockIO> {
idx: usize,
file_size: u64,
bytes: Option<[u8; 512]>,
file_pointer: FilePointer<'a, T>,
}
impl<'a, T: BlockIO> Iterator for FileContent<'a, T> {
type Item = Result<u8>;
fn next(&mut self) -> Option<Self::Item> {
if self.idx == self.file_size as usize {
return None;
} else {
if self.idx % 512 == 0 {
let option_result = self.file_pointer.next();
match option_result {
None => return None,
Some(result) => match result {
Err(e) => return Some(Err(e)),
Ok(files) => self.bytes = Some(files.0),
},
}
}
let byte = self.bytes.unwrap()[self.idx % 512];
self.idx += 1;
return Some(Ok(byte));
}
}
}
fn make_file_content<T: BlockIO>(file: File, disk: &mut Disk<T>) -> FileContent<T> {
let file_pointer = make_file_pointer(file, disk);
FileContent {
idx: 0,
file_size: file.size,
bytes: None,
file_pointer,
}
}
struct FileList<'a, T: BlockIO> {
file_pointer: FilePointer<'a, T>,
}
impl<'a, T: BlockIO> Iterator for FileList<'a, T> {
type Item = Result<[File; 16]>;
fn next(&mut self) -> Option<Self::Item> {
let next_block = self.file_pointer.next();
match next_block {
None => None,
Some(result) => match result {
Ok((data, offset)) => Some(Ok(File::from_bytes(data, offset))),
Err(e) => Some(Err(e)),
},
}
}
}
fn make_file_list<T: BlockIO>(disk: &mut Disk<T>) -> FileList<T> {
let root_dir_first_cluster = disk.bios_parameter_block.unwrap().root_cluster;
let file = File::new([0; 11], root_dir_first_cluster, MAX_FILE_SIZE);
let file_pointer = make_file_pointer(file, disk);
FileList { file_pointer }
}
pub struct Files<'a, T: BlockIO> {
idx: usize,
files: Option<[File; 16]>,
file_list: FileList<'a, T>,
}
impl<'a, T: BlockIO> Iterator for Files<'a, T> {
type Item = Result<File>;
fn next(&mut self) -> Option<Self::Item> {
let mut next_file = File::default();
while next_file.attributes != 32 && next_file.is_lfn == false {
if self.idx % 16 == 0 {
let option_result = self.file_list.next();
match option_result {
None => return None,
Some(result) => match result {
Err(e) => return Some(Err(e)),
Ok(files) => self.files = Some(files),
},
}
}
for i in (self.idx % 16)..16 {
let file = self.files.unwrap()[i];
if !file.is_lfn && file.attributes == 32 {
next_file = file;
self.idx += 1;
break;
}
self.idx += 1;
}
}
Some(Ok(next_file))
}
}
fn make_files<T: BlockIO>(disk: &mut Disk<T>) -> Files<T> {
let file_list = make_file_list(disk);
Files {
idx: 0,
files: None,
file_list,
}
}
impl<T: BlockIO> Disk<T> {
pub fn new(io: T) -> Self {
Self {
io,
partitions: Default::default(),
partition: None,
bios_parameter_block: None,
reads: 0,
writes: 0,
}
}
fn read_file_block(&mut self, byte_offset: u64) -> Result<[u8; 512]> {
self.reads += 1;
self.io.read_block(byte_offset)
}
fn write_file_block(&mut self, byte_offset: u64, data: [u8; 512]) -> Result<()> {
self.writes += 1;
self.io.write_block(byte_offset, data)
}
fn get_next_cluster(&mut self, cluster: u64) -> Option<Result<u64>> {
if cluster >= 0x0FFFFFF8 {
return None;
}
let partition_offset = self.partition.unwrap().byte_offset;
let bios_parameter_block = self.bios_parameter_block.unwrap();
let offset = bios_parameter_block.fat_table_byte_offset;
let bytes_per_sector = bios_parameter_block.bytes_per_sector;
let cluster_byte_start = cluster * 4;
let sector_num = cluster_byte_start / bytes_per_sector;
let data_result =
self.read_file_block(partition_offset + offset + (sector_num * LOGICAL_BLOCK_SIZE));
if data_result.is_err() {
let e = data_result.err().unwrap();
return Some(Err(e));
}
let data = data_result.unwrap();
let start = (cluster_byte_start % LOGICAL_BLOCK_SIZE) as usize;
let slice_result = data[start..start + 4]
.try_into()
.map_err(|_| Error::SliceError);
if slice_result.is_err() {
let e = slice_result.err().unwrap();
return Some(Err(e));
}
let slice = slice_result.unwrap();
let next_cluster = u32::from_le_bytes(slice);
Some(Ok((next_cluster & 0x0FFFFFFF) as u64))
}
fn get_files_last_cluster(&mut self, file: &File) -> Result<u64> {
let mut cluster = file.start_cluster as u64;
while let Some(result) = self.get_next_cluster(cluster) {
let next_cluster = result.unwrap();
if next_cluster >= 0x0FFFFFF8 {
break;
}
cluster = next_cluster;
}
Ok(cluster)
}
fn write_to_last_cluster(&mut self, file: &File, data: &[u8], written: &mut u64) -> Result<()> {
let partition_offset = self.partition.unwrap().byte_offset;
let bytes_per_cluster = self.bios_parameter_block.unwrap().bytes_per_cluster;
let data_sector_bytes_offset = self.bios_parameter_block.unwrap().data_sector_bytes_offset;
let root_dir_first_cluster = self.bios_parameter_block.unwrap().root_cluster;
let sectors_per_cluster = self.bios_parameter_block.unwrap().sectors_per_cluster;
let file_size = file.size;
let mut free_bytes_in_cluster = bytes_per_cluster - (file_size % bytes_per_cluster);
let last_cluster = self.get_files_last_cluster(file)?;
let cluster_used_bytes = file_size % bytes_per_cluster;
let mut used_sectors = cluster_used_bytes / LOGICAL_BLOCK_SIZE;
let mut eof_index = cluster_used_bytes % LOGICAL_BLOCK_SIZE;
let offset = data_sector_bytes_offset
+ ((last_cluster - root_dir_first_cluster) * bytes_per_cluster);
let mut block =
self.read_file_block(partition_offset + offset + (used_sectors * LOGICAL_BLOCK_SIZE))?;
while free_bytes_in_cluster > 0 {
if *written == data.len() as u64 {
break;
}
let d = data[*written as usize];
block[eof_index as usize] = d;
*written += 1;
free_bytes_in_cluster -= 1;
eof_index += 1;
if eof_index == LOGICAL_BLOCK_SIZE {
self.write_file_block(
partition_offset + offset + (used_sectors * LOGICAL_BLOCK_SIZE),
block,
)?;
used_sectors += 1;
eof_index = 0;
block = self.read_file_block(
partition_offset + offset + (used_sectors * LOGICAL_BLOCK_SIZE),
)?;
}
if used_sectors == sectors_per_cluster {
return Ok(());
}
}
self.write_file_block(
partition_offset + offset + (used_sectors * LOGICAL_BLOCK_SIZE),
block,
)?;
Ok(())
}
fn find_next_empty_fat_entry(&mut self, cluster: u64) -> Result<([u8; 512], u64, u64)> {
let partition_offset = self.partition.unwrap().byte_offset;
let fat_table_byte_offset = self.bios_parameter_block.unwrap().fat_table_byte_offset;
let fat_sectors = self.bios_parameter_block.unwrap().fat_size_32;
let mut idx: Option<u64> = None;
let start_sector = (cluster * 4) / LOGICAL_BLOCK_SIZE;
let mut sector_num = start_sector;
let mut data = [0u8; 512];
let mut start_id = (cluster * 4) % LOGICAL_BLOCK_SIZE;
'outer: while idx.is_none() {
data = self.read_file_block(
partition_offset + fat_table_byte_offset + (sector_num * LOGICAL_BLOCK_SIZE),
)?;
for i in (start_id as usize..data.len()).step_by(4) {
let start_lba_arr: [u8; 4] = data[i..i + 4].try_into().unwrap();
let content: u64 = u32::from_le_bytes(start_lba_arr) as u64 & 0x0FFFFFFF;
if content == 0x00000000 {
idx = Some(i as u64);
break 'outer;
}
}
start_id = 0;
sector_num += 1;
if sector_num == fat_sectors {
sector_num = 0;
}
if start_sector == sector_num {
return Err(Error::DiskFullError);
}
}
return Ok((data, sector_num, idx.unwrap()));
}
fn write_fat_entry(
&mut self,
mut data: [u8; 512],
sector_num: u64,
idx: u64,
entry: [u8; 4],
) -> Result<()> {
let partition_offset = self.partition.unwrap().byte_offset;
let fat_table_byte_offset = self.bios_parameter_block.unwrap().fat_table_byte_offset;
data[idx as usize] = entry[0];
data[idx as usize + 1] = entry[1];
data[idx as usize + 2] = entry[2];
data[idx as usize + 3] = entry[3];
self.write_file_block(
partition_offset + fat_table_byte_offset + (sector_num * LOGICAL_BLOCK_SIZE),
data,
)
}
fn get_fat_block_for_cluster(&mut self, cluster: u64) -> ([u8; 512], u64, u64) {
let partition_offset = self.partition.unwrap().byte_offset;
let fat_table_byte_offset = self.bios_parameter_block.unwrap().fat_table_byte_offset;
let idx = (cluster * 4) % LOGICAL_BLOCK_SIZE;
let sector_num = (cluster * 4) / LOGICAL_BLOCK_SIZE;
let data = self
.read_file_block(
partition_offset + fat_table_byte_offset + (sector_num * LOGICAL_BLOCK_SIZE),
)
.unwrap();
return (data, sector_num, idx);
}
fn allocate_next_free_cluster(&mut self, last_cluster: u64) -> Result<()> {
let (data, sector_num, idx) = self.find_next_empty_fat_entry(last_cluster)?;
let entry: [u8; 4] = ((0x0FFFFFF8 & 0x0FFFFFFF) as u32).to_le_bytes();
self.write_fat_entry(data, sector_num, idx, entry)?;
let new_cluster = ((sector_num * LOGICAL_BLOCK_SIZE) + idx) / 4;
let (data, sector_num, idx) = self.get_fat_block_for_cluster(last_cluster);
let entry: [u8; 4] = ((new_cluster & 0x0FFFFFFF) as u32).to_le_bytes();
self.write_fat_entry(data, sector_num, idx, entry)?;
Ok(())
}
fn update_file_record_size(&mut self, file: File) -> File {
let partition_offset = self.partition.unwrap().byte_offset;
let sector_offset = file.byte_offset / LOGICAL_BLOCK_SIZE;
let sector_idx = file.byte_offset % LOGICAL_BLOCK_SIZE;
let mut block = self
.read_file_block(partition_offset + (sector_offset * LOGICAL_BLOCK_SIZE))
.unwrap();
let file_bytes = file.to_bytes();
block[sector_idx as usize..(sector_idx as usize + file_bytes.len())]
.copy_from_slice(&file_bytes);
self.write_file_block(
partition_offset + (sector_offset * LOGICAL_BLOCK_SIZE),
block,
)
.unwrap();
return file;
}
fn append_to_file_with_update_file_size(
&mut self,
file: &mut File,
data: &[u8],
update_file_size: bool,
) -> Result<File> {
let original_file_size = file.size;
let mut written: u64 = 0;
while written < data.len() as u64 {
self.write_to_last_cluster(file, data, &mut written)?;
file.size = original_file_size + written;
if written < data.len() as u64 {
let last_cluster = self.get_files_last_cluster(file)?;
self.allocate_next_free_cluster(last_cluster)?;
}
}
if update_file_size {
Ok(self.update_file_record_size(*file))
} else {
Ok(*file)
}
}
fn allocate_first_free_cluster(&mut self) -> Result<u64> {
let (data, sector_num, idx) = self.find_next_empty_fat_entry(2)?;
let entry: [u8; 4] = ((0x0FFFFFF8 & 0x0FFFFFFF) as u32).to_le_bytes();
self.write_fat_entry(data, sector_num, idx, entry)?;
Ok(((sector_num * LOGICAL_BLOCK_SIZE) + idx) / 4)
}
fn get_root_file_size(&mut self, file: File) -> Result<u64> {
let mut size = 0;
'outer: for result in make_file_pointer(file, self) {
let (block, _) = result?;
for pointer in block.chunks(32) {
if *pointer.first().unwrap() == 0x00 {
break 'outer;
}
size += 32;
}
}
Ok(size)
}
fn create_file_with_name(&mut self, name: [u8; 11]) -> Result<File> {
let start_cluster = self.allocate_first_free_cluster()?;
let data_sector_bytes_offset = self.bios_parameter_block.unwrap().data_sector_bytes_offset;
let mut file = File::new(name, start_cluster, 0);
let file_bytes = file.to_bytes();
let mut to_write = [0u8; 33];
to_write[..32].copy_from_slice(&file_bytes);
let root_dir_first_cluster = self.bios_parameter_block.unwrap().root_cluster;
let mut root_file = File::new([0; 11], root_dir_first_cluster, MAX_FILE_SIZE);
let actual_root_file_size = self.get_root_file_size(root_file)?;
root_file.size = actual_root_file_size;
let root_dir_last_cluster = self.get_files_last_cluster(&root_file)?;
let root_file =
self.append_to_file_with_update_file_size(&mut root_file, &to_write, false)?;
let root_dir_first_cluster = self.bios_parameter_block.unwrap().root_cluster;
let bytes_per_cluster = self.bios_parameter_block.unwrap().bytes_per_cluster;
let offset = data_sector_bytes_offset
+ ((root_dir_last_cluster - root_dir_first_cluster) * bytes_per_cluster);
let byte_offset = offset + ((root_file.size - 33) % bytes_per_cluster);
file.byte_offset = byte_offset;
Ok(file)
}
fn is_init(&mut self) -> Result<()> {
if self.partition.is_none() || self.bios_parameter_block.is_none() {
return Err(Error::NotInitalisedError);
}
Ok(())
}
pub fn init(&mut self) -> Result<()> {
self.reads = 0;
self.writes = 0;
let partition_data = self.read_file_block(0)?;
self.partitions = Partition::from_bytes(partition_data);
self.partition = first_largest_non_zero_partition(&self.partitions);
let offset = self.partition.unwrap().byte_offset;
let bios_parameter_block_data = self.read_file_block(offset)?;
self.bios_parameter_block = Some(BiosParameterBlock::from_bytes(bios_parameter_block_data));
return self.is_init();
}
pub fn list_root_files(&mut self) -> Result<Files<T>> {
self.is_init()?;
self.reads = 0;
self.writes = 0;
Ok(make_files(self))
}
pub fn read_file(&mut self, file: File) -> Result<FileContent<T>> {
self.is_init()?;
self.reads = 0;
self.writes = 0;
Ok(make_file_content(file, self))
}
pub fn append_to_file(&mut self, file: &mut File, data: &[u8]) -> Result<File> {
self.is_init()?;
self.reads = 0;
self.writes = 0;
self.append_to_file_with_update_file_size(file, data, true)
}
pub fn create_file(&mut self, name: [u8; 11]) -> Result<File> {
self.is_init()?;
self.reads = 0;
self.writes = 0;
self.create_file_with_name(name)
}
pub fn get_root_file_by_name(&mut self, name: [u8; 11]) -> Option<Result<File>> {
let init_result = self.is_init();
if init_result.is_err() {
let e = init_result.err().unwrap();
return Some(Err(e));
}
self.reads = 0;
self.writes = 0;
let files_result = self.list_root_files();
if files_result.is_err() {
let e = files_result.err().unwrap();
return Some(Err(e));
}
for file_result in files_result.unwrap() {
if file_result.is_err() {
let e = file_result.err().unwrap();
return Some(Err(e));
}
let file = file_result.unwrap();
if file.name == name {
return Some(Ok(file));
}
}
None
}
}
fn first_largest_non_zero_partition(partitions: &[Partition; 4]) -> Option<Partition> {
partitions
.iter()
.filter(|p| p.num_sectors > 0)
.copied()
.max_by_key(|p| p.num_sectors)
}