io_transform! {
use core::{cell::Cell, fmt};
use spin::Mutex;
use hadris_common::types::endian::Endian;
use hadris_path::{Component, VPath};
use crate::error::{Error, 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, ClusterLike, Read, ReadExt, Sector, SectorCursor, SectorLike, Seek, SeekFrom};
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 {
#[cfg(feature = "alloc")]
pub(crate) cluster_size: usize,
pub(crate) data_start: usize,
#[cfg(feature = "alloc")]
pub(crate) max_cluster: u32,
}
#[derive(Debug)]
pub(crate) struct Fat12_16FsExt {
root_dir_start: usize,
root_dir_size: usize,
}
#[derive(Debug)]
pub(crate) enum FatFsExt {
Fat12_16(Fat12_16FsExt),
Fat32(Fat32FsExt),
}
impl FatFsExt {
#[cfg(feature = "write")]
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 FatVolume<DATA: Seek> {
pub(crate) data: Mutex<SectorCursor<DATA>>,
pub(crate) info: FatInfo,
pub(crate) fat: Fat,
pub(crate) ext: FatFsExt,
volume_info: VolumeInfo,
time_provider: &'static dyn crate::time::TimeProvider,
oem_converter: &'static dyn crate::oem::OemCpConverter,
#[cfg(feature = "cache")]
pub(crate) fat_cache: Option<Mutex<crate::cache::FatSectorCache>>,
}
impl<DATA: Seek> FatVolume<DATA> {
pub fn into_inner(self) -> DATA {
self.data.into_inner().data
}
}
impl<DATA: Seek> fmt::Debug for FatVolume<DATA> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("FatVolume")
.field("info", &self.info)
.field("ext", &self.ext)
.field("time_provider", &self.time_provider)
.field("oem_converter", &self.oem_converter)
.finish_non_exhaustive()
}
}
pub struct FatVolumeBuilder<DATA: Read + Seek> {
data: DATA,
time_provider: &'static dyn crate::time::TimeProvider,
oem_converter: &'static dyn crate::oem::OemCpConverter,
#[cfg(feature = "cache")]
fat_cache_capacity: Option<usize>,
}
impl<DATA: Read + Seek> FatVolumeBuilder<DATA> {
pub fn new(data: DATA) -> Self {
Self {
data,
time_provider: &crate::time::DEFAULT_TIME_PROVIDER,
oem_converter: &crate::oem::DEFAULT_OEM_CONVERTER,
#[cfg(feature = "cache")]
fat_cache_capacity: None,
}
}
pub fn time_provider(
mut self,
provider: &'static dyn crate::time::TimeProvider,
) -> Self {
self.time_provider = provider;
self
}
pub fn oem_converter(
mut self,
converter: &'static dyn crate::oem::OemCpConverter,
) -> Self {
self.oem_converter = converter;
self
}
#[cfg(feature = "cache")]
pub fn fat_cache(mut self, capacity_sectors: usize) -> Self {
if capacity_sectors == 0 {
self.fat_cache_capacity = None;
} else {
self.fat_cache_capacity = Some(capacity_sectors);
}
self
}
pub async fn open(self) -> Result<FatVolume<DATA>> {
#[cfg(feature = "cache")]
let cap = self.fat_cache_capacity;
#[cfg(not(feature = "cache"))]
let fs = FatVolume::open_with_providers(self.data, self.time_provider, self.oem_converter).await?;
#[cfg(feature = "cache")]
let mut fs = FatVolume::open_with_providers(self.data, self.time_provider, self.oem_converter).await?;
#[cfg(feature = "cache")]
if let Some(capacity) = cap {
let (fat_start, fat_size, fat_count, sector_size) = {
let data = fs.data.lock();
let sector_size = data.sector_size;
let (start, size, count) = match &fs.fat {
Fat::Fat12(f) => f.cache_layout(),
Fat::Fat16(f) => f.cache_layout(),
Fat::Fat32(f) => f.cache_layout(),
};
(start, size, count, sector_size)
};
let cache = crate::cache::FatSectorCache::new(
fat_start, fat_size, fat_count, sector_size, capacity,
);
fs.fat_cache = Some(Mutex::new(cache));
}
Ok(fs)
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct FsStatusFlags {
pub dirty: bool,
pub io_errors: bool,
}
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> FatVolume<DATA>
where
DATA: Read + Seek,
{
pub async fn open(data: DATA) -> Result<Self> {
Self::open_with_providers(
data,
&crate::time::DEFAULT_TIME_PROVIDER,
&crate::oem::DEFAULT_OEM_CONVERTER,
)
.await
}
pub fn builder(data: DATA) -> FatVolumeBuilder<DATA> {
FatVolumeBuilder::new(data)
}
pub(crate) async fn open_with_providers(
mut data: DATA,
time_provider: &'static dyn crate::time::TimeProvider,
oem_converter: &'static dyn crate::oem::OemCpConverter,
) -> Result<Self> {
let bpb = data
.read_struct::<RawBpb>()
.await
.map_err(|source| Error::IoContext {
op: "boot sector",
sector: Some(0),
source: source.erase(),
})?;
let sector_size = bpb.bytes_per_sector.get() as usize;
if !matches!(sector_size, 512 | 1024 | 2048 | 4096) {
return Err(Error::CorruptFilesystem {
context: "BPB bytes_per_sector must be 512, 1024, 2048, or 4096",
});
}
if !bpb.sectors_per_cluster.is_power_of_two() || bpb.sectors_per_cluster > 128 {
return Err(Error::CorruptFilesystem {
context: "BPB sectors_per_cluster must be a power of two from 1 through 128",
});
}
let cluster_size = (bpb.sectors_per_cluster as usize) * sector_size;
if cluster_size > 32 * 1024 {
return Err(Error::CorruptFilesystem {
context: "BPB cluster size must not exceed 32 KiB",
});
}
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, time_provider, oem_converter).await
} else {
Self::open_fat12_16(data, bpb, time_provider, oem_converter).await
}
}
async fn open_fat12_16(
mut data: SectorCursor<DATA>,
bpb: RawBpb,
time_provider: &'static dyn crate::time::TimeProvider,
oem_converter: &'static dyn crate::oem::OemCpConverter,
) -> Result<Self> {
let bpb_ext16 = data
.read_struct::<RawBpbExt16>()
.await
.map_err(|source| Error::IoContext {
op: "boot sector (FAT12/16 extended fields)",
sector: Some(0),
source: source.erase(),
})?;
let signature = u16::from_le_bytes(bpb_ext16.signature_word);
if signature != 0xAA55 {
return Err(Error::InvalidBootSignature { found: signature });
}
if bpb.fat_count != 1 && bpb.fat_count != 2 {
return Err(Error::CorruptFilesystem {
context: "BPB fat_count must be 1 or 2",
});
}
let sector_size = data.sector_size;
#[cfg(feature = "alloc")]
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
.checked_mul(sector_size)
.ok_or(Error::CorruptFilesystem {
context: "reserved_sectors * sector_size",
})?;
let fat_total_size = fat_count
.checked_mul(sectors_per_fat)
.and_then(|v| v.checked_mul(sector_size))
.ok_or(Error::CorruptFilesystem {
context: "fat_count * sectors_per_fat * sector_size",
})?;
let root_dir_start = fat_start
.checked_add(fat_total_size)
.ok_or(Error::CorruptFilesystem {
context: "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
.checked_add(root_dir_sectors * sector_size)
.ok_or(Error::CorruptFilesystem {
context: "data_start arithmetic",
})?;
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 metadata_sectors = reserved_sectors
.checked_add(fat_count.checked_mul(sectors_per_fat).ok_or(
Error::CorruptFilesystem {
context: "fat_count * sectors_per_fat",
},
)?)
.and_then(|v| v.checked_add(root_dir_sectors))
.ok_or(Error::CorruptFilesystem {
context: "metadata sector total",
})?;
let data_sectors = (total_sectors as usize).saturating_sub(metadata_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)
};
#[cfg(not(feature = "alloc"))]
let _ = max_cluster;
let ext = FatFsExt::Fat12_16(Fat12_16FsExt {
root_dir_start,
root_dir_size,
});
let info = FatInfo {
#[cfg(feature = "alloc")]
cluster_size,
data_start,
#[cfg(feature = "alloc")]
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,
time_provider,
oem_converter,
#[cfg(feature = "cache")]
fat_cache: None,
})
}
async fn open_fat32(
mut data: SectorCursor<DATA>,
bpb: RawBpb,
time_provider: &'static dyn crate::time::TimeProvider,
oem_converter: &'static dyn crate::oem::OemCpConverter,
) -> Result<Self> {
let bpb_ext32 = data
.read_struct::<RawBpbExt32>()
.await
.map_err(|source| Error::IoContext {
op: "boot sector (FAT32 extended fields)",
sector: Some(0),
source: source.erase(),
})?;
let signature = bpb_ext32.signature_word.get();
if signature != 0xAA55 {
return Err(Error::InvalidBootSignature { found: signature });
}
if bpb_ext32.version != [0, 0] {
return Err(Error::CorruptFilesystem {
context: "unsupported FAT32 filesystem version",
});
}
if bpb.fat_count != 1 && bpb.fat_count != 2 {
return Err(Error::CorruptFilesystem {
context: "BPB fat_count must be 1 or 2",
});
}
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
.map_err(|source| Error::IoContext {
op: "FSInfo",
sector: Some(fs_info_sec.0 as u64),
source: source.erase(),
})?;
let lead_sig = u32::from_le_bytes(fs_info.signature);
if lead_sig != FSINFO_LEAD_SIG {
return Err(Error::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(Error::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(Error::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())),
});
#[cfg(feature = "alloc")]
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 {
#[cfg(feature = "alloc")]
cluster_size,
data_start: fat_start + fat_size,
#[cfg(feature = "alloc")]
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,
time_provider,
oem_converter,
#[cfg(feature = "cache")]
fat_cache: None,
})
}
pub fn time_provider(&self) -> &dyn crate::time::TimeProvider {
self.time_provider
}
pub fn oem_converter(&self) -> &dyn crate::oem::OemCpConverter {
self.oem_converter
}
pub fn fat(&self) -> &Fat {
&self.fat
}
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
}
#[cfg(feature = "write")]
pub(crate) fn fixed_root_dir_info(&self) -> Option<(usize, usize)> {
self.ext.fixed_root_dir()
}
#[cfg(feature = "write")]
pub(crate) fn is_fat32_root_cluster(&self, cluster: u32) -> bool {
matches!(&self.ext, FatFsExt::Fat32(ext) if ext.root_clus.0 == cluster)
}
pub async fn read_status_flags(&self) -> Result<FsStatusFlags> {
let (dirty, io_errors) = self.read_status_flags_routed().await?;
Ok(FsStatusFlags { dirty, io_errors })
}
pub async fn read_root_label(&self) -> Result<Option<[u8; 11]>> {
match self.find_root_label_entry().await? {
Some((_, raw)) => Ok(Some(unsafe { raw.file }.name)),
None => Ok(None),
}
}
pub(crate) async fn find_root_label_entry(
&self,
) -> Result<Option<(usize, crate::raw::RawDirectoryEntry)>> {
use crate::raw::{DirEntryAttrFlags, RawDirectoryEntry};
let entry_size = core::mem::size_of::<RawDirectoryEntry>();
let mut data = self.data.lock();
let is_label =
|attr: u8| DirEntryAttrFlags::from_bits_retain(attr).is_volume_label_entry();
match &self.ext {
FatFsExt::Fat12_16(ext) => {
let end = ext.root_dir_start + ext.root_dir_size;
let mut pos = ext.root_dir_start;
while pos + entry_size <= end {
data.seek(SeekFrom::Start(pos as u64)).await?;
let raw = data.read_struct::<RawDirectoryEntry>().await?;
let bytes = unsafe { raw.bytes };
if bytes[0] == 0 {
return Ok(None);
}
if bytes[0] != 0xE5 && is_label(unsafe { raw.file }.attributes) {
return Ok(Some((pos, raw)));
}
pos += entry_size;
}
Ok(None)
}
FatFsExt::Fat32(ext) => {
let cluster_size = data.cluster_size;
let mut current = ext.root_clus.0 as usize;
let chain_limit = self.fat.max_cluster();
let mut steps: u32 = 0;
loop {
steps = steps.saturating_add(1);
if steps > chain_limit {
return Err(Error::ClusterLoop { cluster: current as u32 });
}
let cluster_start = Cluster(current).to_bytes(self.info.data_start, cluster_size);
let mut offset = 0;
while offset + entry_size <= cluster_size {
let pos = cluster_start + offset;
data.seek(SeekFrom::Start(pos as u64)).await?;
let raw = data.read_struct::<RawDirectoryEntry>().await?;
let bytes = unsafe { raw.bytes };
if bytes[0] == 0 {
return Ok(None);
}
if bytes[0] != 0xE5 && is_label(unsafe { raw.file }.attributes) {
return Ok(Some((pos, raw)));
}
offset += entry_size;
}
drop(data);
let next_cluster = self.next_cluster_routed(current).await?;
data = self.data.lock();
match next_cluster {
Some(next) => current = next as usize,
None => return Ok(None),
}
}
}
}
}
pub async fn open_path(&self, path: &str) -> Result<FileEntry> {
let mut current_dir = self.root_dir();
let mut last_component = None;
for component in VPath::new(path).components() {
let component = match component {
Component::Root | Component::Current => continue,
Component::Parent => return Err(Error::InvalidPath),
Component::Normal(component) => component,
};
if let Some(prev) = last_component.take() {
current_dir = current_dir.open_dir(prev).await?;
}
last_component = Some(component);
}
let final_name = last_component.ok_or(Error::InvalidPath)?;
current_dir.find(final_name).await?.ok_or(Error::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(Error::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(Error::NotADirectory);
}
Ok(FatDir {
data: self,
cluster: entry.cluster(),
fixed_root: None,
})
}
}
}
#[cfg(feature = "cache")]
sync_only! {
impl<DATA> FatVolume<DATA>
where
DATA: Read + Seek,
{
pub fn fat_cache(&self) -> Option<&Mutex<crate::cache::FatSectorCache>> {
self.fat_cache.as_ref()
}
pub fn with_cached_fat<R>(
&self,
f: impl FnOnce(&mut crate::cache::CachedFat<'_>, &mut SectorCursor<DATA>) -> R,
) -> Option<R> {
let cache_mutex = self.fat_cache.as_ref()?;
let mut cache = cache_mutex.lock();
let mut data = self.data.lock();
let mut cached = crate::cache::CachedFat::new(&mut cache, &self.fat);
Some(f(&mut cached, &mut *data))
}
pub fn with_fat_cache_locked<R>(
&self,
f: impl FnOnce(&mut crate::cache::FatSectorCache, &mut SectorCursor<DATA>) -> R,
) -> Option<R> {
let cache_mutex = self.fat_cache.as_ref()?;
let mut cache = cache_mutex.lock();
let mut data = self.data.lock();
Some(f(&mut cache, &mut *data))
}
}
}
#[cfg(feature = "cache")]
sync_only! {
impl<DATA> FatVolume<DATA>
where
DATA: Read + Seek,
{
pub(crate) fn next_cluster_routed(&self, cluster: usize) -> Result<Option<u32>> {
use core::ops::DerefMut;
let mut cache_guard = self.fat_cache.as_ref().map(|m| m.lock());
let mut data = self.data.lock();
if let Some(cache) = cache_guard.as_mut() {
let mut cached = crate::cache::CachedFat::new(cache, &self.fat);
cached.next_cluster(data.deref_mut(), cluster)
} else {
self.fat.next_cluster(data.deref_mut(), cluster)
}
}
pub(crate) fn read_status_flags_routed(&self) -> Result<(bool, bool)> {
use core::ops::DerefMut;
if matches!(self.fat, Fat::Fat12(_)) {
return Ok((false, false));
}
let mut cache_guard = self.fat_cache.as_ref().map(|m| m.lock());
let mut data = self.data.lock();
match (&self.fat, cache_guard.as_deref_mut()) {
(Fat::Fat16(_), Some(cache)) => {
let val = cache.read_fat16_entry(data.deref_mut(), 1)?;
Ok((val & 0x8000 == 0, val & 0x4000 == 0))
}
(Fat::Fat32(_), Some(cache)) => {
let val = cache.read_fat32_entry(data.deref_mut(), 1)?;
Ok((val & 0x0800_0000 == 0, val & 0x0400_0000 == 0))
}
_ => self.fat.read_status_flags(data.deref_mut()),
}
}
}
}
#[cfg(feature = "cache")]
async_only! {
impl<DATA> FatVolume<DATA>
where
DATA: Read + Seek,
{
pub(crate) async fn next_cluster_routed(&self, cluster: usize) -> Result<Option<u32>> {
use core::ops::DerefMut;
let mut data = self.data.lock();
self.fat.next_cluster(data.deref_mut(), cluster).await
}
pub(crate) async fn read_status_flags_routed(&self) -> Result<(bool, bool)> {
use core::ops::DerefMut;
let mut data = self.data.lock();
self.fat.read_status_flags(data.deref_mut()).await
}
}
}
#[cfg(not(feature = "cache"))]
io_transform! {
impl<DATA> FatVolume<DATA>
where
DATA: Read + Seek,
{
pub(crate) async fn next_cluster_routed(&self, cluster: usize) -> Result<Option<u32>> {
use core::ops::DerefMut;
let mut data = self.data.lock();
self.fat.next_cluster(data.deref_mut(), cluster).await
}
pub(crate) async fn read_status_flags_routed(&self) -> Result<(bool, bool)> {
use core::ops::DerefMut;
let mut data = self.data.lock();
self.fat.read_status_flags(data.deref_mut()).await
}
}
}
#[cfg(all(feature = "cache", feature = "write"))]
sync_only! {
impl<DATA> FatVolume<DATA>
where
DATA: Read + super::io::Write + Seek,
{
pub(crate) fn write_clus_routed(&self, cluster: usize, value: u32) -> Result<()> {
use core::ops::DerefMut;
let mut cache_guard = self.fat_cache.as_ref().map(|m| m.lock());
let mut data = self.data.lock();
if let Some(ref mut cache) = cache_guard {
match self.fat.fat_type() {
FatType::Fat12 => cache.write_fat12_entry(data.deref_mut(), cluster, value as u16),
FatType::Fat16 => cache.write_fat16_entry(data.deref_mut(), cluster, value as u16),
FatType::Fat32 => cache.write_fat32_entry(data.deref_mut(), cluster, value),
}
} else {
match &self.fat {
Fat::Fat12(f) => f.write_clus(data.deref_mut(), cluster, value as u16),
Fat::Fat16(f) => f.write_clus(data.deref_mut(), cluster, value as u16),
Fat::Fat32(f) => f.write_clus(data.deref_mut(), cluster, value),
}
}
}
pub(crate) fn allocate_cluster_routed(&self, hint: u32) -> Result<u32> {
use core::ops::DerefMut;
let mut cache_guard = self.fat_cache.as_ref().map(|m| m.lock());
let mut data = self.data.lock();
if let Some(ref mut cache) = cache_guard {
allocate_cluster_via_cache(cache, &self.fat, data.deref_mut(), hint)
} else {
match &self.fat {
Fat::Fat12(f) => f.allocate_cluster(data.deref_mut(), hint as u16).map(|c| c as u32),
Fat::Fat16(f) => f.allocate_cluster(data.deref_mut(), hint as u16).map(|c| c as u32),
Fat::Fat32(f) => f.allocate_cluster(data.deref_mut(), hint),
}
}
}
pub(crate) fn free_chain_routed(&self, start: u32) -> Result<u32> {
use core::ops::DerefMut;
let mut cache_guard = self.fat_cache.as_ref().map(|m| m.lock());
let mut data = self.data.lock();
if let Some(ref mut cache) = cache_guard {
free_chain_via_cache(cache, &self.fat, data.deref_mut(), start)
} else {
self.fat.free_chain(data.deref_mut(), start as usize)
}
}
pub(crate) fn truncate_chain_routed(&self, cluster: u32) -> Result<u32> {
use core::ops::DerefMut;
let mut cache_guard = self.fat_cache.as_ref().map(|m| m.lock());
let mut data = self.data.lock();
if let Some(ref mut cache) = cache_guard {
truncate_chain_via_cache(cache, &self.fat, data.deref_mut(), cluster)
} else {
self.fat.truncate_chain(data.deref_mut(), cluster as usize)
}
}
}
}
#[cfg(all(feature = "cache", feature = "write"))]
async_only! {
impl<DATA> FatVolume<DATA>
where
DATA: Read + super::io::Write + Seek,
{
pub(crate) async fn write_clus_routed(&self, cluster: usize, value: u32) -> Result<()> {
use core::ops::DerefMut;
let mut data = self.data.lock();
match &self.fat {
Fat::Fat12(f) => f.write_clus(data.deref_mut(), cluster, value as u16).await,
Fat::Fat16(f) => f.write_clus(data.deref_mut(), cluster, value as u16).await,
Fat::Fat32(f) => f.write_clus(data.deref_mut(), cluster, value).await,
}
}
pub(crate) async fn allocate_cluster_routed(&self, hint: u32) -> Result<u32> {
use core::ops::DerefMut;
let mut data = self.data.lock();
match &self.fat {
Fat::Fat12(f) => f.allocate_cluster(data.deref_mut(), hint as u16).await.map(|c| c as u32),
Fat::Fat16(f) => f.allocate_cluster(data.deref_mut(), hint as u16).await.map(|c| c as u32),
Fat::Fat32(f) => f.allocate_cluster(data.deref_mut(), hint).await,
}
}
pub(crate) async fn free_chain_routed(&self, start: u32) -> Result<u32> {
use core::ops::DerefMut;
let mut data = self.data.lock();
self.fat.free_chain(data.deref_mut(), start as usize).await
}
pub(crate) async fn truncate_chain_routed(&self, cluster: u32) -> Result<u32> {
use core::ops::DerefMut;
let mut data = self.data.lock();
self.fat.truncate_chain(data.deref_mut(), cluster as usize).await
}
}
}
#[cfg(all(not(feature = "cache"), feature = "write"))]
io_transform! {
impl<DATA> FatVolume<DATA>
where
DATA: Read + super::io::Write + Seek,
{
pub(crate) async fn write_clus_routed(&self, cluster: usize, value: u32) -> Result<()> {
use core::ops::DerefMut;
let mut data = self.data.lock();
match &self.fat {
Fat::Fat12(f) => f.write_clus(data.deref_mut(), cluster, value as u16).await,
Fat::Fat16(f) => f.write_clus(data.deref_mut(), cluster, value as u16).await,
Fat::Fat32(f) => f.write_clus(data.deref_mut(), cluster, value).await,
}
}
pub(crate) async fn allocate_cluster_routed(&self, hint: u32) -> Result<u32> {
use core::ops::DerefMut;
let mut data = self.data.lock();
match &self.fat {
Fat::Fat12(f) => f.allocate_cluster(data.deref_mut(), hint as u16).await.map(|c| c as u32),
Fat::Fat16(f) => f.allocate_cluster(data.deref_mut(), hint as u16).await.map(|c| c as u32),
Fat::Fat32(f) => f.allocate_cluster(data.deref_mut(), hint).await,
}
}
pub(crate) async fn free_chain_routed(&self, start: u32) -> Result<u32> {
use core::ops::DerefMut;
let mut data = self.data.lock();
self.fat.free_chain(data.deref_mut(), start as usize).await
}
pub(crate) async fn truncate_chain_routed(&self, cluster: u32) -> Result<u32> {
use core::ops::DerefMut;
let mut data = self.data.lock();
self.fat.truncate_chain(data.deref_mut(), cluster as usize).await
}
}
}
#[cfg(all(feature = "cache", feature = "write"))]
sync_only! {
fn allocate_cluster_via_cache<T>(
cache: &mut crate::cache::FatSectorCache,
fat: &Fat,
data: &mut T,
hint: u32,
) -> Result<u32>
where
T: super::io::Read + super::io::Write + super::io::Seek,
{
const FIRST: u32 = 2;
let max_cluster = fat.max_cluster();
let start = if hint >= FIRST && hint <= max_cluster {
hint
} else {
FIRST
};
let scan = |cache: &mut crate::cache::FatSectorCache,
data: &mut T,
fat: &Fat,
lo: u32,
hi: u32|
-> Result<Option<u32>> {
for c in lo..=hi {
let free = match fat.fat_type() {
FatType::Fat12 => (cache.read_fat12_entry(data, c as usize)? & 0x0FFF) == 0,
FatType::Fat16 => cache.read_fat16_entry(data, c as usize)? == 0,
FatType::Fat32 => (cache.read_fat32_entry(data, c as usize)? & 0x0FFF_FFFF) == 0,
};
if free {
return Ok(Some(c));
}
}
Ok(None)
};
let claim =
|cache: &mut crate::cache::FatSectorCache, data: &mut T, fat: &Fat, c: u32| -> Result<()> {
match fat.fat_type() {
FatType::Fat12 => cache.write_fat12_entry(data, c as usize, 0x0FF8),
FatType::Fat16 => cache.write_fat16_entry(data, c as usize, 0xFFF8),
FatType::Fat32 => cache.write_fat32_entry(data, c as usize, 0x0FFF_FFF8),
}
};
if let Some(c) = scan(cache, data, fat, start, max_cluster)? {
claim(cache, data, fat, c)?;
return Ok(c);
}
if start > FIRST
&& let Some(c) = scan(cache, data, fat, FIRST, start - 1)?
{
claim(cache, data, fat, c)?;
return Ok(c);
}
Err(Error::NoFreeSpace)
}
#[cfg(all(feature = "cache", feature = "write"))]
fn free_chain_via_cache<T>(
cache: &mut crate::cache::FatSectorCache,
fat: &Fat,
data: &mut T,
start: u32,
) -> Result<u32>
where
T: super::io::Read + super::io::Write + super::io::Seek,
{
const FIRST: u32 = 2;
let max_cluster = fat.max_cluster();
let mut count = 0u32;
let mut current = start;
loop {
if current < FIRST || current > max_cluster {
break;
}
let next = read_fat_entry_via_cache(cache, fat, data, current as usize)?;
write_fat_entry_via_cache(cache, fat, data, current as usize, 0)?;
count += 1;
if is_eoc(fat.fat_type(), next) || is_bad(fat.fat_type(), next) || next == 0 {
break;
}
current = next;
}
Ok(count)
}
#[cfg(all(feature = "cache", feature = "write"))]
fn truncate_chain_via_cache<T>(
cache: &mut crate::cache::FatSectorCache,
fat: &Fat,
data: &mut T,
cluster: u32,
) -> Result<u32>
where
T: super::io::Read + super::io::Write + super::io::Seek,
{
const FIRST: u32 = 2;
let max_cluster = fat.max_cluster();
if cluster < FIRST || cluster > max_cluster {
return Ok(0);
}
let next = read_fat_entry_via_cache(cache, fat, data, cluster as usize)?;
let eoc = match fat.fat_type() {
FatType::Fat12 => 0x0FF8,
FatType::Fat16 => 0xFFF8,
FatType::Fat32 => 0x0FFF_FFF8,
};
write_fat_entry_via_cache(cache, fat, data, cluster as usize, eoc)?;
if !is_eoc(fat.fat_type(), next) && next >= FIRST && next <= max_cluster {
free_chain_via_cache(cache, fat, data, next)
} else {
Ok(0)
}
}
#[cfg(all(feature = "cache", feature = "write"))]
fn read_fat_entry_via_cache<T>(
cache: &mut crate::cache::FatSectorCache,
fat: &Fat,
data: &mut T,
cluster: usize,
) -> Result<u32>
where
T: super::io::Read + super::io::Seek,
{
Ok(match fat.fat_type() {
FatType::Fat12 => (cache.read_fat12_entry(data, cluster)? & 0x0FFF) as u32,
FatType::Fat16 => cache.read_fat16_entry(data, cluster)? as u32,
FatType::Fat32 => cache.read_fat32_entry(data, cluster)? & 0x0FFF_FFFF,
})
}
#[cfg(all(feature = "cache", feature = "write"))]
fn write_fat_entry_via_cache<T>(
cache: &mut crate::cache::FatSectorCache,
fat: &Fat,
data: &mut T,
cluster: usize,
value: u32,
) -> Result<()>
where
T: super::io::Read + super::io::Write + super::io::Seek,
{
match fat.fat_type() {
FatType::Fat12 => cache.write_fat12_entry(data, cluster, value as u16),
FatType::Fat16 => cache.write_fat16_entry(data, cluster, value as u16),
FatType::Fat32 => cache.write_fat32_entry(data, cluster, value),
}
}
#[cfg(all(feature = "cache", feature = "write"))]
fn is_eoc(ty: FatType, value: u32) -> bool {
match ty {
FatType::Fat12 => value >= 0x0FF8,
FatType::Fat16 => value >= 0xFFF8,
FatType::Fat32 => value >= 0x0FFF_FFF8,
}
}
#[cfg(all(feature = "cache", feature = "write"))]
fn is_bad(ty: FatType, value: u32) -> bool {
match ty {
FatType::Fat12 => value == 0x0FF7,
FatType::Fat16 => value == 0xFFF7,
FatType::Fat32 => value == 0x0FFF_FFF7,
}
}
}
#[cfg(all(feature = "cache", feature = "write"))]
sync_only! {
impl<DATA> FatVolume<DATA>
where
DATA: Read + super::io::Write + Seek,
{
pub fn flush(&self) -> Result<()> {
use core::ops::DerefMut;
if let Some(cache_mutex) = &self.fat_cache {
let mut cache = cache_mutex.lock();
let mut data = self.data.lock();
cache.flush(data.deref_mut())?;
}
Ok(())
}
}
}