use core::mem::size_of;
use crate::error::{Error, Result};
#[cfg(feature = "write")]
use crate::io::Write;
use crate::io::{Read, Seek, SeekFrom};
use super::ExFatInfo;
pub struct ExFatTable {
fat_offset: u64,
#[cfg(feature = "write")]
fat_length: u64,
#[cfg(feature = "write")]
fat_count: u8,
max_cluster: u32,
}
impl ExFatTable {
pub const FREE_CLUSTER: u32 = 0x00000000;
pub const END_OF_CHAIN: u32 = 0xFFFFFFFF;
pub const BAD_CLUSTER: u32 = 0xFFFFFFF7;
pub const MEDIA_DESCRIPTOR: u32 = 0xFFFFFFF8;
pub const FIRST_DATA_CLUSTER: u32 = 2;
pub fn new(info: &ExFatInfo) -> Self {
Self {
fat_offset: info.fat_offset,
#[cfg(feature = "write")]
fat_length: info.fat_length,
#[cfg(feature = "write")]
fat_count: info.fat_count,
max_cluster: info.cluster_count + Self::FIRST_DATA_CLUSTER - 1,
}
}
pub fn read_entry<DATA: Read + Seek>(&self, data: &mut DATA, cluster: u32) -> Result<u32> {
self.validate_cluster(cluster)?;
let offset = self.fat_offset + (cluster as u64) * (size_of::<u32>() as u64);
data.seek(SeekFrom::Start(offset))?;
let mut buf = [0u8; 4];
data.read_exact(&mut buf)?;
Ok(u32::from_le_bytes(buf))
}
#[cfg(feature = "write")]
pub fn write_entry<DATA: Read + Write + Seek>(
&self,
data: &mut DATA,
cluster: u32,
value: u32,
) -> Result<()> {
self.validate_cluster(cluster)?;
for fat_idx in 0..self.fat_count {
let offset = self.fat_offset
+ (fat_idx as u64) * self.fat_length
+ (cluster as u64) * (size_of::<u32>() as u64);
data.seek(SeekFrom::Start(offset))?;
data.write_all(&value.to_le_bytes())?;
}
Ok(())
}
pub fn next_cluster<DATA: Read + Seek>(
&self,
data: &mut DATA,
cluster: u32,
) -> Result<Option<u32>> {
let entry = self.read_entry(data, cluster)?;
if entry == Self::END_OF_CHAIN || entry >= Self::MEDIA_DESCRIPTOR {
return Ok(None);
}
if entry == Self::BAD_CLUSTER {
return Err(Error::BadCluster { cluster });
}
if entry == Self::FREE_CLUSTER {
return Err(Error::UnexpectedEndOfChain { cluster });
}
self.validate_cluster(entry)?;
Ok(Some(entry))
}
#[cfg(feature = "write")]
pub fn allocate_cluster<DATA: Read + Write + Seek>(
&self,
data: &mut DATA,
hint: u32,
) -> Result<u32> {
let start = if hint >= Self::FIRST_DATA_CLUSTER && hint <= self.max_cluster {
hint
} else {
Self::FIRST_DATA_CLUSTER
};
for cluster in start..=self.max_cluster {
let entry = self.read_entry(data, cluster)?;
if entry == Self::FREE_CLUSTER {
self.write_entry(data, cluster, Self::END_OF_CHAIN)?;
return Ok(cluster);
}
}
for cluster in Self::FIRST_DATA_CLUSTER..start {
let entry = self.read_entry(data, cluster)?;
if entry == Self::FREE_CLUSTER {
self.write_entry(data, cluster, Self::END_OF_CHAIN)?;
return Ok(cluster);
}
}
Err(Error::NoFreeSpace)
}
#[cfg(feature = "write")]
pub fn allocate_chain<DATA: Read + Write + Seek>(
&self,
data: &mut DATA,
count: u32,
hint: u32,
) -> Result<u32> {
if count == 0 {
return Err(Error::NoFreeSpace);
}
let first = self.allocate_cluster(data, hint)?;
let mut prev = first;
for _ in 1..count {
let next = self.allocate_cluster(data, prev + 1)?;
self.write_entry(data, prev, next)?;
prev = next;
}
Ok(first)
}
#[cfg(feature = "write")]
pub fn free_chain<DATA: Read + Write + Seek>(
&self,
data: &mut DATA,
start: u32,
) -> Result<u32> {
let mut count = 0u32;
let mut current = start;
loop {
if current < Self::FIRST_DATA_CLUSTER || current > self.max_cluster {
break;
}
let next = self.read_entry(data, current)?;
self.write_entry(data, current, Self::FREE_CLUSTER)?;
count += 1;
if next == Self::END_OF_CHAIN
|| next >= Self::MEDIA_DESCRIPTOR
|| next == Self::BAD_CLUSTER
|| next == Self::FREE_CLUSTER
{
break;
}
current = next;
}
Ok(count)
}
#[cfg(feature = "write")]
pub fn extend_chain<DATA: Read + Write + Seek>(
&self,
data: &mut DATA,
last: u32,
count: u32,
hint: u32,
) -> Result<u32> {
if count == 0 {
return Ok(last);
}
let first_new = self.allocate_chain(data, count, hint)?;
self.write_entry(data, last, first_new)?;
Ok(first_new)
}
#[cfg(feature = "write")]
pub fn truncate_chain<DATA: Read + Write + Seek>(
&self,
data: &mut DATA,
cluster: u32,
) -> Result<u32> {
if cluster < Self::FIRST_DATA_CLUSTER || cluster > self.max_cluster {
return Ok(0);
}
let next = self.read_entry(data, cluster)?;
self.write_entry(data, cluster, Self::END_OF_CHAIN)?;
if next != Self::END_OF_CHAIN
&& (Self::FIRST_DATA_CLUSTER..Self::MEDIA_DESCRIPTOR).contains(&next)
&& next <= self.max_cluster
{
self.free_chain(data, next)
} else {
Ok(0)
}
}
fn validate_cluster(&self, cluster: u32) -> Result<()> {
if cluster < Self::FIRST_DATA_CLUSTER {
return Err(Error::ClusterOutOfBounds {
cluster,
max: self.max_cluster,
});
}
if cluster > self.max_cluster {
return Err(Error::ClusterOutOfBounds {
cluster,
max: self.max_cluster,
});
}
Ok(())
}
pub fn max_cluster(&self) -> u32 {
self.max_cluster
}
}