io_transform! {
use core::{cell::Cell, fmt};
use spin::Mutex;
use hadris_common::types::endian::Endian;
use crate::error::{FatError, Result};
use crate::raw::{RawBpb, RawBpbExt16, RawBpbExt32, RawFsInfo};
use super::dir::{FatDir, FileEntry};
use super::fat_table::{Fat, Fat12, Fat16, Fat32, FatType};
use super::io::{Cluster, Read, ReadExt, Sector, SectorCursor, SectorLike, Seek};
use super::read::FileReader;
#[derive(Debug, Clone)]
pub struct VolumeInfo {
oem_name: [u8; 8],
volume_id: u32,
volume_label: [u8; 11],
fs_type_str: [u8; 8],
}
impl VolumeInfo {
pub fn oem_name(&self) -> &str {
core::str::from_utf8(&self.oem_name)
.unwrap_or("")
.trim_end()
}
pub fn volume_id(&self) -> u32 {
self.volume_id
}
pub fn volume_label(&self) -> &str {
core::str::from_utf8(&self.volume_label)
.unwrap_or("")
.trim_end()
}
pub fn fs_type_str(&self) -> &str {
core::str::from_utf8(&self.fs_type_str)
.unwrap_or("")
.trim_end()
}
pub fn oem_name_raw(&self) -> &[u8; 8] {
&self.oem_name
}
pub fn volume_label_raw(&self) -> &[u8; 11] {
&self.volume_label
}
pub fn fs_type_str_raw(&self) -> &[u8; 8] {
&self.fs_type_str
}
}
#[derive(Debug)]
pub(crate) struct FatInfo {
pub(crate) cluster_size: usize,
pub(crate) data_start: usize,
#[allow(dead_code)]
pub(crate) max_cluster: u32,
}
#[derive(Debug)]
pub(crate) struct Fat12_16FsExt {
root_dir_start: usize,
root_dir_size: usize,
#[allow(dead_code)]
root_entry_count: u16,
}
#[derive(Debug)]
pub(crate) enum FatFsExt {
Fat12_16(Fat12_16FsExt),
Fat32(Fat32FsExt),
}
impl FatFsExt {
fn fixed_root_dir(&self) -> Option<(usize, usize)> {
match self {
Self::Fat12_16(ext) => Some((ext.root_dir_start, ext.root_dir_size)),
Self::Fat32(_) => None,
}
}
}
pub(crate) struct Fat32FsExt {
pub(crate) fs_info_sec: Sector<u16>,
root_clus: Cluster<u32>,
pub(crate) free_count: Cell<u32>,
pub(crate) next_free: Cell<Cluster<u32>>,
}
impl fmt::Debug for Fat32FsExt {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Fat32FsExt")
.field("fs_info_sec", &self.fs_info_sec)
.field("root_clus", &self.root_clus)
.field("free_count", &self.free_count.get())
.field("next_free", &self.next_free.get())
.finish()
}
}
pub struct FatFs<DATA: Seek> {
pub(crate) data: Mutex<SectorCursor<DATA>>,
pub(crate) info: FatInfo,
pub(crate) fat: Fat,
pub(crate) ext: FatFsExt,
volume_info: VolumeInfo,
}
impl<DATA: Seek> fmt::Debug for FatFs<DATA> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("FatFs")
.field("info", &self.info)
.field("ext", &self.ext)
.finish_non_exhaustive()
}
}
pub(crate) const FSINFO_LEAD_SIG: u32 = 0x41615252; pub(crate) const FSINFO_STRUC_SIG: u32 = 0x61417272; pub(crate) const FSINFO_TRAIL_SIG: u32 = 0xAA550000;
impl<DATA> FatFs<DATA>
where
DATA: Read + Seek,
{
pub async fn open(mut data: DATA) -> Result<Self> {
let bpb = data.read_struct::<RawBpb>().await?;
let sector_size = bpb.bytes_per_sector.get() as usize;
let cluster_size = (bpb.sectors_per_cluster as usize) * sector_size;
let data = SectorCursor::new(data, sector_size, cluster_size);
let root_entry_count = u16::from_le_bytes(bpb.root_entry_count);
let sectors_per_fat_16 = u16::from_le_bytes(bpb.sectors_per_fat_16);
if root_entry_count == 0 && sectors_per_fat_16 == 0 {
Self::open_fat32(data, bpb).await
} else {
Self::open_fat12_16(data, bpb).await
}
}
async fn open_fat12_16(mut data: SectorCursor<DATA>, bpb: RawBpb) -> Result<Self> {
let bpb_ext16 = data.read_struct::<RawBpbExt16>().await?;
let signature = u16::from_le_bytes(bpb_ext16.signature_word);
if signature != 0xAA55 {
return Err(FatError::InvalidBootSignature { found: signature });
}
let sector_size = data.sector_size;
let cluster_size = data.cluster_size;
let reserved_sectors = bpb.reserved_sector_count.get() as usize;
let fat_count = bpb.fat_count as usize;
let root_entry_count = u16::from_le_bytes(bpb.root_entry_count);
let sectors_per_fat = u16::from_le_bytes(bpb.sectors_per_fat_16) as usize;
let fat_start = reserved_sectors * sector_size;
let fat_total_size = fat_count * sectors_per_fat * sector_size;
let root_dir_start = fat_start + fat_total_size;
let root_dir_size = (root_entry_count as usize) * 32;
let root_dir_sectors = root_dir_size.div_ceil(sector_size);
let data_start = root_dir_start + root_dir_sectors * sector_size;
let total_sectors = if bpb.total_sectors_16 != [0, 0] {
u16::from_le_bytes(bpb.total_sectors_16) as u32
} else {
u32::from_le_bytes(bpb.total_sectors_32)
};
let data_sectors = total_sectors as usize
- reserved_sectors
- fat_count * sectors_per_fat
- root_dir_sectors;
let count_of_clusters = data_sectors / (bpb.sectors_per_cluster as usize);
let (fat, max_cluster) = if count_of_clusters < 4085 {
let fat12 = Fat12::new(
fat_start,
sectors_per_fat * sector_size,
fat_count,
(count_of_clusters + 1) as u16, );
(Fat::Fat12(fat12), count_of_clusters as u32 + 1)
} else {
let fat16 = Fat16::new(
fat_start,
sectors_per_fat * sector_size,
fat_count,
(count_of_clusters + 1) as u16,
);
(Fat::Fat16(fat16), count_of_clusters as u32 + 1)
};
let ext = FatFsExt::Fat12_16(Fat12_16FsExt {
root_dir_start,
root_dir_size,
root_entry_count,
});
let info = FatInfo {
cluster_size,
data_start,
max_cluster,
};
let volume_info = VolumeInfo {
oem_name: bpb.oem_name,
volume_id: u32::from_le_bytes(bpb_ext16.volume_id),
volume_label: bpb_ext16.volume_label,
fs_type_str: bpb_ext16.fs_type,
};
Ok(Self {
data: Mutex::new(data),
info,
fat,
ext,
volume_info,
})
}
async fn open_fat32(mut data: SectorCursor<DATA>, bpb: RawBpb) -> Result<Self> {
let bpb_ext32 = data.read_struct::<RawBpbExt32>().await?;
let signature = bpb_ext32.signature_word.get();
if signature != 0xAA55 {
return Err(FatError::InvalidBootSignature { found: signature });
}
let fs_info_sec = Sector(bpb_ext32.fs_info_sector.get());
data.seek_sector(fs_info_sec).await?;
let fs_info = data.read_struct::<RawFsInfo>().await?;
let lead_sig = u32::from_le_bytes(fs_info.signature);
if lead_sig != FSINFO_LEAD_SIG {
return Err(FatError::InvalidFsInfoSignature {
field: "FSI_LeadSig",
expected: FSINFO_LEAD_SIG,
found: lead_sig,
});
}
let struc_sig = u32::from_le_bytes(fs_info.structure_signature);
if struc_sig != FSINFO_STRUC_SIG {
return Err(FatError::InvalidFsInfoSignature {
field: "FSI_StrucSig",
expected: FSINFO_STRUC_SIG,
found: struc_sig,
});
}
let trail_sig = fs_info.trail_signature.get();
if trail_sig != FSINFO_TRAIL_SIG {
return Err(FatError::InvalidFsInfoSignature {
field: "FSI_TrailSig",
expected: FSINFO_TRAIL_SIG,
found: trail_sig,
});
}
let ext = FatFsExt::Fat32(Fat32FsExt {
fs_info_sec,
root_clus: Cluster(bpb_ext32.root_cluster.get()),
free_count: Cell::new(fs_info.free_count.get()),
next_free: Cell::new(Cluster(fs_info.next_free.get())),
});
let cluster_size = data.cluster_size;
let fat_start = Sector(bpb.reserved_sector_count.get()).to_bytes(data.sector_size);
let fat_size_per_fat =
Sector(bpb_ext32.sectors_per_fat_32.get()).to_bytes(data.sector_size);
let fat_size = bpb.fat_count as usize * fat_size_per_fat;
let total_sectors = if bpb.total_sectors_16 != [0, 0] {
u16::from_le_bytes(bpb.total_sectors_16) as u32
} else {
u32::from_le_bytes(bpb.total_sectors_32)
};
let reserved_sectors = bpb.reserved_sector_count.get() as u32;
let fat_sectors = bpb_ext32.sectors_per_fat_32.get() * bpb.fat_count as u32;
let data_sectors = total_sectors.saturating_sub(reserved_sectors + fat_sectors);
let max_cluster = (data_sectors / bpb.sectors_per_cluster as u32) + 1;
let fat = Fat::Fat32(Fat32::new(
fat_start,
fat_size_per_fat,
bpb.fat_count as usize,
max_cluster,
));
let info = FatInfo {
cluster_size,
data_start: fat_start + fat_size,
max_cluster,
};
let volume_info = VolumeInfo {
oem_name: bpb.oem_name,
volume_id: u32::from_le_bytes(bpb_ext32.volume_id),
volume_label: bpb_ext32.volume_label,
fs_type_str: bpb_ext32.fs_type,
};
Ok(Self {
data: Mutex::new(data),
info,
fat,
ext,
volume_info,
})
}
pub fn root_dir(&self) -> FatDir<'_, DATA> {
match &self.ext {
FatFsExt::Fat12_16(ext) => FatDir {
data: self,
cluster: Cluster(0), fixed_root: Some((ext.root_dir_start, ext.root_dir_size)),
},
FatFsExt::Fat32(ext) => FatDir {
data: self,
cluster: Cluster(ext.root_clus.0 as usize),
fixed_root: None,
},
}
}
pub fn fat_type(&self) -> FatType {
self.fat.fat_type()
}
pub fn volume_info(&self) -> &VolumeInfo {
&self.volume_info
}
pub(crate) fn fixed_root_dir_info(&self) -> Option<(usize, usize)> {
self.ext.fixed_root_dir()
}
pub async fn open_path(&self, path: &str) -> Result<FileEntry> {
let path = path.trim_start_matches('/');
if path.is_empty() {
return Err(FatError::InvalidPath);
}
let mut components = path.split('/').filter(|s| !s.is_empty()).peekable();
if components.peek().is_none() {
return Err(FatError::InvalidPath);
}
let mut current_dir = self.root_dir();
let mut last_component = None;
for component in components {
if let Some(prev) = last_component.take() {
current_dir = current_dir.open_dir(prev).await?;
}
last_component = Some(component);
}
let final_name = last_component.unwrap();
current_dir.find(final_name).await?.ok_or(FatError::EntryNotFound)
}
pub async fn open_file_path(&self, path: &str) -> Result<FileReader<'_, DATA>> {
let entry = self.open_path(path).await?;
FileReader::new(self, &entry)
}
pub async fn open_dir_path(&self, path: &str) -> Result<FatDir<'_, DATA>> {
let entry = self.open_path(path).await?;
if !entry.is_directory() {
return Err(FatError::NotADirectory);
}
Ok(FatDir {
data: self,
cluster: entry.cluster(),
fixed_root: None,
})
}
pub fn open_dir_entry(&self, entry: &FileEntry) -> Result<FatDir<'_, DATA>> {
if !entry.is_directory() {
return Err(FatError::NotADirectory);
}
Ok(FatDir {
data: self,
cluster: entry.cluster(),
fixed_root: None,
})
}
}
}