Skip to content

Commit

Permalink
Extract FatBuffer to its own module
Browse files Browse the repository at this point in the history
  • Loading branch information
alloncm committed Oct 4, 2023
1 parent 16c4bfd commit c735ef3
Show file tree
Hide file tree
Showing 2 changed files with 158 additions and 143 deletions.
154 changes: 154 additions & 0 deletions rpi/src/drivers/fat32/fat_buffer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
use crate::drivers::disk::Disk;

use super::SECTOR_SIZE;

const FAT_ENTRY_SIZE:usize = 4;
const FAT_ENTRY_MASK:u32 = 0x0FFF_FFFF;

#[derive(Clone, Debug)]
pub struct FatIndex{
sector_number:u32,
sector_offset:usize,
}

#[derive(Clone, Copy, PartialEq, Debug)]
pub enum FatSegmentState{
Free,
Allocated,
AllocatedEof,
Reserved,
Bad,
}

impl From<u32> for FatSegmentState{
fn from(value: u32) -> Self {
match value{
0 => Self::Free,
2..=0xFFF_FFF5 => Self::Allocated,
0xFFF_FFFF => Self::AllocatedEof,
0xFFF_FFF7 => Self::Bad,
_ => Self::Reserved
}
}
}

impl FatSegmentState{
/// Checks whether a value should be part of this segment or not
pub fn should_continue_segment(&self, other: &Self)->bool{
// AllocatedEof is should never continue segment
// otherwise fallback to check raw values of the enum
if *self == Self::AllocatedEof || *other == Self::AllocatedEof{
return false;
}
return self == other;
}
}

#[derive(Clone, Debug)]
pub struct FatSegment{
pub state:FatSegmentState,
pub len:u32,
pub start_index:u32,
}

impl FatSegment{
pub fn new(value:u32, start_index:u32)->Self{
Self { state: value.into(), len: 1, start_index}
}
}

#[derive(Clone, Copy)]
pub struct FatInfo{
first_fat_start_sector:usize,
sectors_per_fat:usize,
fats_count:usize
}

impl FatInfo{
pub fn new(first_fat_start_sector:usize, sectors_per_fat:usize, fats_count: usize)->Self{
Self { first_fat_start_sector, sectors_per_fat, fats_count }
}
}

// This is the default size of a fat buffer
// the actual size is just tweaking between fewer read operation and smaller buffer
pub const FAT_BUFFER_SIZE:usize = SECTOR_SIZE as usize * 100;

pub struct FatBuffer<const FBS:usize = FAT_BUFFER_SIZE>{
buffer:[u8;FBS],
buffer_len: usize,
fat_start_index:FatIndex,
fat_internal_index:FatIndex,
fat_info:FatInfo,
}

impl<const FBS:usize> FatBuffer<FBS>{
// The buffer Im reading will be the same buffer that Im writing back
// so it must be aligned in order to not corrupt the fat table
pub fn new(fat_info:FatInfo, first_cluster_index:usize, entries_count: Option<usize>, disk: &mut Disk)->Self{
let entries_count = entries_count.unwrap_or((FBS - SECTOR_SIZE) / FAT_ENTRY_SIZE); // The max size is smaller cause I need some padding space for alignment
let mut buffer = [0; FBS];
let fat_offset = first_cluster_index * FAT_ENTRY_SIZE;
let fat_index = FatIndex{ sector_number: (fat_info.first_fat_start_sector + (fat_offset / SECTOR_SIZE)) as u32, sector_offset: fat_offset % SECTOR_SIZE };

// Align the end read to SECTOR_SIZE, since the FAT table is not aligned we need to read exactly X sectors in order to be able to write them back later
let fat_end_read = (entries_count * FAT_ENTRY_SIZE) + (SECTOR_SIZE - ((entries_count * FAT_ENTRY_SIZE) % SECTOR_SIZE));
if fat_end_read > FBS{
core::panic!("Error fat entries count is too much: expected:{}, actual: {}", FBS / FAT_ENTRY_SIZE, entries_count);
}
disk.read(fat_index.sector_number, &mut buffer[..fat_end_read]);
return Self { buffer, fat_start_index: fat_index.clone(), fat_internal_index: fat_index, buffer_len: fat_end_read, fat_info };
}

/// On success returns the FAT entry, on error returns the last valid fat index
pub fn read(&mut self)->Result<u32, FatIndex>{
let internal_sector_index = self.get_internal_sector_index()?;
let start_index = (internal_sector_index * SECTOR_SIZE) + self.fat_internal_index.sector_offset;
let end_index = start_index + FAT_ENTRY_SIZE;
let entry = Self::bytes_to_fat_entry(self.buffer[start_index .. end_index].try_into().unwrap());
self.fat_internal_index.sector_offset += FAT_ENTRY_SIZE;
if self.fat_internal_index.sector_offset >= SECTOR_SIZE{
self.fat_internal_index.sector_number += 1;
self.fat_internal_index.sector_offset = 0;
}
// Mask the entry to hide the reserved bits
return Ok(entry & FAT_ENTRY_MASK);
}

/// On error returns the last valid fat index
pub fn write(&mut self, mut value:u32)->Result<(), FatIndex>{
let internal_sector_index = self.get_internal_sector_index()?;
let start_index = (internal_sector_index * SECTOR_SIZE) + self.fat_internal_index.sector_offset;
let end_index = start_index + FAT_ENTRY_SIZE;
let entry = Self::bytes_to_fat_entry(self.buffer[start_index .. end_index].try_into().unwrap());
let reserved_bits = entry & (!FAT_ENTRY_MASK);
value = (value & FAT_ENTRY_MASK) | reserved_bits;
self.buffer[start_index .. end_index].copy_from_slice(&Self::fat_entry_to_bytes(value));
self.fat_internal_index.sector_offset += FAT_ENTRY_SIZE;
if self.fat_internal_index.sector_offset >= SECTOR_SIZE{
self.fat_internal_index.sector_number += 1;
self.fat_internal_index.sector_offset = 0;
}
return Ok(());
}

/// Sync the fat buffer to the disk
pub fn flush(&mut self, disk:&mut Disk){
// Sync all the fat sectors to disk
for i in 0..self.fat_info.fats_count{
let start_sector = self.fat_start_index.sector_number + (self.fat_info.sectors_per_fat * i) as u32;
let _ = disk.write(start_sector, &mut self.buffer[..self.buffer_len]);
}
}

fn get_internal_sector_index(&self)->Result<usize, FatIndex>{
let internal_sector_index = (self.fat_internal_index.sector_number - self.fat_start_index.sector_number) as usize;
if internal_sector_index * SECTOR_SIZE >= self.buffer_len{
return Err(self.fat_internal_index.clone());
}
return Ok(internal_sector_index);
}

fn bytes_to_fat_entry(buffer:&[u8;FAT_ENTRY_SIZE])->u32 {u32::from_ne_bytes(*buffer)}
fn fat_entry_to_bytes(entry:u32)->[u8;FAT_ENTRY_SIZE] {u32::to_ne_bytes(entry)}
}
147 changes: 4 additions & 143 deletions rpi/src/drivers/fat32.rs → rpi/src/drivers/fat32/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
mod fat_buffer;

use core::{mem::size_of, ops::ControlFlow};

use arrayvec::ArrayVec;

use crate::peripherals::compile_time_size_assert;
use super::{as_mut_buffer, disk::*};
use fat_buffer::*;

#[derive(Default)]
#[repr(C, packed)]
Expand Down Expand Up @@ -121,9 +124,7 @@ struct FatLongDirEntry{

const DISK_PARTITION_INDEX:u8 = 0;
const SECTOR_SIZE:usize = Disk::get_block_size() as usize;
const FAT_ENTRY_SIZE:usize = size_of::<u32>(); // each fat entry in fat32 is 4 the size of u32
const FAT_ENTRY_EOF_INDEX:u32 = 0x0FFF_FFFF;
const FAT_ENTRY_MASK:u32 = 0x0FFF_FFFF;
const FAT_ENTRY_FREE_INDEX:u32 = 0;
const DELETED_DIR_ENTRY_PREFIX:u8 = 0xE5;
const DIR_EOF_PREFIX:u8 = 0;
Expand Down Expand Up @@ -153,147 +154,7 @@ impl FileEntry{
}
}

#[derive(Clone, Debug)]
struct FatIndex{
sector_number:u32,
sector_offset:usize,
}

#[derive(Clone, Copy, PartialEq, Debug)]
enum FatSegmentState{
Free,
Allocated,
AllocatedEof,
Reserved,
Bad,
}

impl From<u32> for FatSegmentState{
fn from(value: u32) -> Self {
match value{
0 => Self::Free,
2..=0xFFF_FFF5 => Self::Allocated,
0xFFF_FFFF => Self::AllocatedEof,
0xFFF_FFF7 => Self::Bad,
_ => Self::Reserved
}
}
}

impl FatSegmentState{
/// Checks whether a value should be part of this segment or not
fn should_continue_segment(&self, other: &Self)->bool{
// AllocatedEof is should never continue segment
// otherwise fallback to check raw values of the enum
if *self == Self::AllocatedEof || *other == Self::AllocatedEof{
return false;
}
return self == other;
}
}

#[derive(Clone, Debug)]
struct FatSegment{
state:FatSegmentState,
len:u32,
start_index:u32,
}

impl FatSegment{
fn new(value:u32, start_index:u32)->Self{
Self { state: value.into(), len: 1, start_index}
}
}

#[derive(Clone, Copy)]
struct FatInfo{
first_fat_start_sector:usize,
sectors_per_fat:usize,
fats_count:usize
}

// This is the default size of a fat buffer
// the actual size is just tweaking between fewer read operation and smaller buffer
const FAT_BUFFER_SIZE:usize = SECTOR_SIZE as usize * 100;

struct FatBuffer<const FBS:usize = FAT_BUFFER_SIZE>{
buffer:[u8;FBS],
buffer_len: usize,
fat_start_index:FatIndex,
fat_internal_index:FatIndex,
fat_info:FatInfo,
}

impl<const FBS:usize> FatBuffer<FBS>{
// The buffer Im reading will be the same buffer that Im writing back
// so it must be aligned in order to not corrupt the fat table
fn new(fat_info:FatInfo, first_cluster_index:usize, entries_count: Option<usize>, disk: &mut Disk)->Self{
let entries_count = entries_count.unwrap_or((FBS - SECTOR_SIZE) / FAT_ENTRY_SIZE); // The max size is smaller cause I need some padding space for alignment
let mut buffer = [0; FBS];
let fat_offset = first_cluster_index * FAT_ENTRY_SIZE;
let fat_index = FatIndex{ sector_number: (fat_info.first_fat_start_sector + (fat_offset / SECTOR_SIZE)) as u32, sector_offset: fat_offset % SECTOR_SIZE };

// Align the end read to SECTOR_SIZE, since the FAT table is not aligned we need to read exactly X sectors in order to be able to write them back later
let fat_end_read = (entries_count * FAT_ENTRY_SIZE) + (SECTOR_SIZE - ((entries_count * FAT_ENTRY_SIZE) % SECTOR_SIZE));
if fat_end_read > FBS{
core::panic!("Error fat entries count is too much: expected:{}, actual: {}", FBS / FAT_ENTRY_SIZE, entries_count);
}
disk.read(fat_index.sector_number, &mut buffer[..fat_end_read]);
return Self { buffer, fat_start_index: fat_index.clone(), fat_internal_index: fat_index, buffer_len: fat_end_read, fat_info };
}

/// On success returns the FAT entry, on error returns the last valid fat index
fn read(&mut self)->Result<u32, FatIndex>{
let internal_sector_index = self.get_internal_sector_index()?;
let start_index = (internal_sector_index * SECTOR_SIZE) + self.fat_internal_index.sector_offset;
let end_index = start_index + FAT_ENTRY_SIZE;
let entry = Self::bytes_to_fat_entry(self.buffer[start_index .. end_index].try_into().unwrap());
self.fat_internal_index.sector_offset += FAT_ENTRY_SIZE;
if self.fat_internal_index.sector_offset >= SECTOR_SIZE{
self.fat_internal_index.sector_number += 1;
self.fat_internal_index.sector_offset = 0;
}
// Mask the entry to hide the reserved bits
return Ok(entry & FAT_ENTRY_MASK);
}

/// On error returns the last valid fat index
fn write(&mut self, mut value:u32)->Result<(), FatIndex>{
let internal_sector_index = self.get_internal_sector_index()?;
let start_index = (internal_sector_index * SECTOR_SIZE) + self.fat_internal_index.sector_offset;
let end_index = start_index + FAT_ENTRY_SIZE;
let entry = Self::bytes_to_fat_entry(self.buffer[start_index .. end_index].try_into().unwrap());
let reserved_bits = entry & (!FAT_ENTRY_MASK);
value = (value & FAT_ENTRY_MASK) | reserved_bits;
self.buffer[start_index .. end_index].copy_from_slice(&Self::fat_entry_to_bytes(value));
self.fat_internal_index.sector_offset += FAT_ENTRY_SIZE;
if self.fat_internal_index.sector_offset >= SECTOR_SIZE{
self.fat_internal_index.sector_number += 1;
self.fat_internal_index.sector_offset = 0;
}
return Ok(());
}

/// Sync the fat buffer to the disk
fn flush(&mut self, disk:&mut Disk){
// Sync all the fat sectors to disk
for i in 0..self.fat_info.fats_count{
let start_sector = self.fat_start_index.sector_number + (self.fat_info.sectors_per_fat * i) as u32;
let _ = disk.write(start_sector, &mut self.buffer[..self.buffer_len]);
}
}

fn get_internal_sector_index(&self)->Result<usize, FatIndex>{
let internal_sector_index = (self.fat_internal_index.sector_number - self.fat_start_index.sector_number) as usize;
if internal_sector_index * SECTOR_SIZE >= self.buffer_len{
return Err(self.fat_internal_index.clone());
}
return Ok(internal_sector_index);
}

fn bytes_to_fat_entry(buffer:&[u8;FAT_ENTRY_SIZE])->u32 {u32::from_ne_bytes(*buffer)}
fn fat_entry_to_bytes(entry:u32)->[u8;FAT_ENTRY_SIZE] {u32::to_ne_bytes(entry)}
}

// Currently the driver support only 0x100 files in the root directory
const MAX_FILES: usize = 0x100;
Expand Down Expand Up @@ -339,7 +200,7 @@ impl Fat32Fs{
let clusters_count = fat32_data_sectors / boot_sector.fat32_bpb.sectors_per_cluster as u32;
let fat_start_sector = (bpb_sector_index + boot_sector.fat32_bpb.reserved_sectors_count as u32) as usize;
let mut fat32 = Self {
fat_info:FatInfo { first_fat_start_sector: fat_start_sector, sectors_per_fat: boot_sector.fat32_bpb.sectors_per_fat_32 as usize, fats_count: boot_sector.fat32_bpb.fats_count as usize },
fat_info:FatInfo::new( fat_start_sector, boot_sector.fat32_bpb.sectors_per_fat_32 as usize, boot_sector.fat32_bpb.fats_count as usize ),
disk, boot_sector, partition_start_sector_index:bpb_sector_index, clusters_count,
fat_table_cache: ArrayVec::<FatSegment, MAX_FAT_SEGMENTS_COUNT>::new(),
root_dir_cache: ArrayVec::<FatShortDirEntry, MAX_FILES>::new(),
Expand Down

0 comments on commit c735ef3

Please sign in to comment.