mod dir;
mod entry;
mod file;
#[cfg(test)]
mod tests;
pub use dir::{Dir, DirEntry, DirIter, Metadata};
pub use file::File;
use super::layout::{self, Boot, FatEntry};
use crate::device::{SectorDriver, gpt, mbr};
pub use crate::fs::fat::Timestamp;
pub const MAX_NAME_LEN: usize = layout::MAX_NAME_UNITS;
const UP_ASCII: usize = 128;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Error<E> {
Io(E),
NotExfat,
UnsupportedVersion {
major: u8,
minor: u8,
},
SectorSizeMismatch {
volume: u32,
driver: u32,
},
ScratchTooSmall {
needed: usize,
got: usize,
},
VolumeExceedsDevice,
BadGeometry,
NoSuchPartition,
NoAllocationBitmap,
NotFound,
NotADirectory,
IsADirectory,
AlreadyExists,
DirectoryNotEmpty,
InvalidName,
InvalidPath,
DirectoryFull,
NoSpace,
CorruptChain,
CorruptEntry,
FileTooLarge,
InvalidOffset,
Unsupported(&'static str),
}
impl<E> Error<E> {
pub fn is_not_found(&self) -> bool {
matches!(self, Error::NotFound)
}
}
impl<E: core::fmt::Display> core::fmt::Display for Error<E> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Error::Io(e) => write!(f, "device error: {e}"),
Error::NotExfat => f.write_str("not an exFAT volume"),
Error::UnsupportedVersion { major, minor } => {
write!(f, "unsupported exFAT revision {major}.{minor}")
}
Error::SectorSizeMismatch { volume, driver } => write!(
f,
"volume declares {volume}-byte sectors, driver uses {driver}"
),
Error::ScratchTooSmall { needed, got } => {
write!(f, "scratch buffer is {got} bytes, need {needed}")
}
Error::VolumeExceedsDevice => f.write_str("volume runs past the end of the device"),
Error::BadGeometry => f.write_str("geometry exFAT cannot use"),
Error::NoSuchPartition => f.write_str("no such partition"),
Error::NoAllocationBitmap => f.write_str("volume has no allocation bitmap"),
Error::NotFound => f.write_str("no such file or directory"),
Error::NotADirectory => f.write_str("not a directory"),
Error::IsADirectory => f.write_str("is a directory"),
Error::AlreadyExists => f.write_str("already exists"),
Error::DirectoryNotEmpty => f.write_str("directory not empty"),
Error::InvalidName => f.write_str("invalid name"),
Error::InvalidPath => f.write_str("invalid path"),
Error::DirectoryFull => f.write_str("directory full"),
Error::NoSpace => f.write_str("no space left on volume"),
Error::CorruptChain => f.write_str("corrupt cluster chain"),
Error::CorruptEntry => f.write_str("corrupt directory entry set"),
Error::FileTooLarge => f.write_str("file too large"),
Error::InvalidOffset => f.write_str("offset out of range"),
Error::Unsupported(what) => write!(f, "unsupported: {what}"),
}
}
}
#[cfg(feature = "std")]
impl<E: core::fmt::Debug + core::fmt::Display> std::error::Error for Error<E> {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Geometry {
pub part_start: u64,
pub volume_sectors: u64,
pub bytes_per_sector: u32,
pub sectors_per_cluster: u32,
pub cluster_count: u32,
pub root_cluster: u32,
pub revision: (u8, u8),
pub serial: u32,
fat_start: u32,
fat_sectors: u32,
active_fat: u8,
heap_start: u32,
}
impl Geometry {
pub fn cluster_bytes(&self) -> u32 {
self.bytes_per_sector * self.sectors_per_cluster
}
pub fn total_bytes(&self) -> u64 {
self.cluster_count as u64 * self.cluster_bytes() as u64
}
pub fn is_data_cluster(&self, cluster: u32) -> bool {
cluster >= 2 && cluster < self.cluster_count + 2
}
fn cluster_first_sector(&self, cluster: u32) -> u64 {
self.part_start
+ self.heap_start as u64
+ (cluster as u64 - 2) * self.sectors_per_cluster as u64
}
fn fat_first_sector(&self) -> u64 {
self.part_start + self.fat_start as u64 + self.active_fat as u64 * self.fat_sectors as u64
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) struct Stream {
pub first_cluster: u32,
pub len: u64,
pub contiguous: bool,
}
impl Stream {
pub(super) fn dir(first_cluster: u32, len: u64) -> Self {
Self {
first_cluster,
len,
contiguous: false,
}
}
}
#[derive(Debug)]
pub struct Volume<D: SectorDriver, const SECTOR: usize = 512> {
dev: D,
geom: Geometry,
buf: [u8; SECTOR],
cache_lba: Option<u64>,
cache_dirty: bool,
bitmap: Option<Stream>,
upcase: Option<Stream>,
up_ascii: [u16; UP_ASCII],
walk: Option<(u32, u32, u32)>,
next_free: u32,
now: Timestamp,
#[cfg(feature = "alloc")]
up_cache: Option<::alloc::vec::Vec<u16>>,
}
impl<D: SectorDriver, const SECTOR: usize> Volume<D, SECTOR> {
pub fn mount(dev: D) -> Result<Self, Error<D::Error>> {
Self::mount_at(dev, 0)
}
pub fn mount_partition(mut dev: D, index: u8) -> Result<Self, Error<D::Error>> {
let part = Self::partition(&mut dev, index)?;
Self::mount_at(dev, part.start_lba)
}
pub fn mount_auto(mut dev: D) -> Result<Self, Error<D::Error>> {
match Self::probe(&mut dev)? {
Some(lba) => Self::mount_at(dev, lba),
None => Err(Error::NotExfat),
}
}
pub fn probe(dev: &mut D) -> Result<Option<u64>, Error<D::Error>> {
Self::check_scratch(dev)?;
let ss = dev.sector_size() as usize;
let mut first = [0u8; SECTOR];
Self::read_raw(dev, 0, &mut first[..ss])?;
if Boot::decode(&first[..ss]).is_ok() {
return Ok(Some(0));
}
if let Some(table) = gpt::Table::read(dev, &mut first[..ss]).map_err(Error::Io)? {
for pass in 0..2 {
for i in 0..table.entries() {
let Some(part) = table.entry(dev, &mut first[..ss], i).map_err(Error::Io)?
else {
continue;
};
if (pass == 0) != part.looks_like_fat_family() {
continue;
}
if Self::read_raw(dev, part.start_lba, &mut first[..ss]).is_ok()
&& Boot::decode(&first[..ss]).is_ok()
{
return Ok(Some(part.start_lba));
}
}
}
return Ok(None);
}
Self::read_raw(dev, 0, &mut first[..ss])?;
if let Some(table) = mbr::parse(&first[..ss]) {
for pass in 0..2 {
for slot in table.iter().flatten() {
if (pass == 0) != slot.looks_like_exfat() {
continue;
}
if Self::read_raw(dev, slot.start_lba, &mut first[..ss]).is_ok()
&& Boot::decode(&first[..ss]).is_ok()
{
return Ok(Some(slot.start_lba));
}
}
}
}
Ok(None)
}
pub fn mount_at(mut dev: D, start_lba: u64) -> Result<Self, Error<D::Error>> {
Self::check_scratch(&dev)?;
let ss = dev.sector_size();
let mut sector = [0u8; SECTOR];
Self::read_raw(&mut dev, start_lba, &mut sector[..ss as usize])?;
let boot = Boot::decode(§or[..ss as usize]).map_err(|_| Error::NotExfat)?;
if boot.bytes_per_sector() != ss {
return Err(Error::SectorSizeMismatch {
volume: boot.bytes_per_sector(),
driver: ss,
});
}
if boot.fs_revision_major != 1 {
return Err(Error::UnsupportedVersion {
major: boot.fs_revision_major,
minor: boot.fs_revision_minor,
});
}
let geom = Self::geometry_from(&boot, start_lba, &dev)?;
let mut vol = Self {
dev,
geom,
buf: [0u8; SECTOR],
cache_lba: None,
cache_dirty: false,
bitmap: None,
upcase: None,
up_ascii: [0u16; UP_ASCII],
walk: None,
next_free: 2,
now: Timestamp::EPOCH,
#[cfg(feature = "alloc")]
up_cache: None,
};
for (i, slot) in vol.up_ascii.iter_mut().enumerate() {
*slot = i as u16;
}
vol.scan_root_metadata()?;
vol.load_upcase_ascii()?;
Ok(vol)
}
pub fn partition(dev: &mut D, index: u8) -> Result<mbr::Partition, Error<D::Error>> {
if index == 0 || index > 4 {
return Err(Error::NoSuchPartition);
}
Self::check_scratch(dev)?;
let ss = dev.sector_size() as usize;
let mut sector = [0u8; SECTOR];
Self::read_raw(dev, 0, &mut sector[..ss])?;
mbr::parse(§or[..ss])
.and_then(|t| t[index as usize - 1])
.ok_or(Error::NoSuchPartition)
}
fn geometry_from(boot: &Boot, part_start: u64, dev: &D) -> Result<Geometry, Error<D::Error>> {
let bps = boot.bytes_per_sector();
let spc = boot.sectors_per_cluster();
if boot.cluster_count == 0 || boot.fat_length == 0 || spc == 0 {
return Err(Error::BadGeometry);
}
let end = part_start
.checked_add(boot.volume_length)
.ok_or(Error::BadGeometry)?;
if boot.volume_length == 0 || end > dev.sector_count() {
return Err(Error::VolumeExceedsDevice);
}
let fat_end = (boot.fat_offset as u64)
.checked_add(boot.fat_length as u64 * boot.number_of_fats as u64)
.ok_or(Error::BadGeometry)?;
if fat_end > boot.volume_length {
return Err(Error::BadGeometry);
}
let need_entries = boot.cluster_count as u64 + 2;
if boot.fat_length as u64 * bps as u64 / 4 < need_entries {
return Err(Error::BadGeometry);
}
let heap_sectors = boot.cluster_count as u64 * spc as u64;
let heap_end = (boot.cluster_heap_offset as u64)
.checked_add(heap_sectors)
.ok_or(Error::BadGeometry)?;
if (boot.cluster_heap_offset as u64) < fat_end || heap_end > boot.volume_length {
return Err(Error::BadGeometry);
}
let root = boot.first_cluster_of_root_directory;
if root < 2 || root >= boot.cluster_count + 2 {
return Err(Error::BadGeometry);
}
Ok(Geometry {
part_start,
volume_sectors: boot.volume_length,
bytes_per_sector: bps,
sectors_per_cluster: spc,
cluster_count: boot.cluster_count,
root_cluster: root,
revision: (boot.fs_revision_major, boot.fs_revision_minor),
serial: boot.volume_serial_number,
fat_start: boot.fat_offset,
fat_sectors: boot.fat_length,
active_fat: if boot.number_of_fats > 1 {
(boot.volume_flags & 1) as u8
} else {
0
},
heap_start: boot.cluster_heap_offset,
})
}
fn check_scratch(dev: &D) -> Result<(), Error<D::Error>> {
let ss = dev.sector_size() as usize;
if !(512..=4096).contains(&ss) || !ss.is_power_of_two() {
return Err(Error::NotExfat);
}
if SECTOR < ss {
return Err(Error::ScratchTooSmall {
needed: ss,
got: SECTOR,
});
}
Ok(())
}
fn read_raw(dev: &mut D, lba: u64, buf: &mut [u8]) -> Result<(), Error<D::Error>> {
let ss = dev.sector_size() as u64;
if lba.saturating_add(buf.len() as u64 / ss) > dev.sector_count() {
return Err(Error::VolumeExceedsDevice);
}
dev.read_sectors(lba, buf).map_err(Error::Io)
}
fn scan_root_metadata(&mut self) -> Result<(), Error<D::Error>> {
let root = Stream::dir(self.geom.root_cluster, u64::MAX);
let mut pos = 0u64;
let mut slots = 0u64;
let max_slots = self.geom.cluster_count as u64
* (self.geom.cluster_bytes() as u64 / layout::ENTRY_SIZE as u64);
while slots <= max_slots {
let Some(slot) = self.read_slot(&root, pos)? else {
break;
};
match slot[0] {
0 => break,
layout::ENTRY_ALLOCATION_BITMAP => {
let which = slot[1] & 1;
if which == self.geom.active_fat && self.bitmap.is_none() {
let first = layout::le32(&slot, 20);
let len = layout::le64(&slot, 24);
if self.geom.is_data_cluster(first) {
self.bitmap = Some(Stream {
first_cluster: first,
len,
contiguous: slot[1] & layout::SECFLAG_NO_FAT_CHAIN != 0,
});
}
}
}
layout::ENTRY_UPCASE_TABLE => {
if self.upcase.is_none() {
let first = layout::le32(&slot, 20);
let len = layout::le64(&slot, 24);
if self.geom.is_data_cluster(first) {
self.upcase = Some(Stream {
first_cluster: first,
len,
contiguous: slot[1] & layout::SECFLAG_NO_FAT_CHAIN != 0,
});
}
}
}
layout::ENTRY_FILE => {
pos += slot[1] as u64 * layout::ENTRY_SIZE as u64;
slots += slot[1] as u64;
}
_ => {}
}
pos += layout::ENTRY_SIZE as u64;
slots += 1;
}
if let Some(b) = self.bitmap
&& b.len < (self.geom.cluster_count as u64).div_ceil(8)
{
self.bitmap = None;
}
Ok(())
}
fn load_upcase_ascii(&mut self) -> Result<(), Error<D::Error>> {
let Some(table) = self.upcase else {
return Ok(());
};
let mut index = 0usize;
let mut off = 0u64;
while index < UP_ASCII && off + 2 <= table.len {
let v = self.stream_u16(&table, off)?;
off += 2;
if v == 0xFFFF {
if off + 2 > table.len {
break;
}
let count = self.stream_u16(&table, off)? as usize;
off += 2;
for _ in 0..count {
if index >= UP_ASCII {
break;
}
self.up_ascii[index] = index as u16;
index += 1;
}
} else {
self.up_ascii[index] = v;
index += 1;
}
}
Ok(())
}
pub fn geometry(&self) -> &Geometry {
&self.geom
}
pub fn cluster_bytes(&self) -> u32 {
self.geom.cluster_bytes()
}
pub fn total_bytes(&self) -> u64 {
self.geom.total_bytes()
}
pub fn is_writable(&self) -> bool {
self.bitmap.is_some()
}
pub fn driver(&self) -> &D {
&self.dev
}
pub fn driver_mut(&mut self) -> &mut D {
&mut self.dev
}
pub fn set_time(&mut self, now: Timestamp) {
self.now = now;
}
pub fn time(&self) -> Timestamp {
self.now
}
pub fn upcase_cache_bytes(&self) -> usize {
#[cfg(feature = "alloc")]
{
self.up_cache.as_ref().map_or(0, |t| t.len() * 2)
}
#[cfg(not(feature = "alloc"))]
{
0
}
}
pub fn flush(&mut self) -> Result<(), Error<D::Error>> {
self.flush_cache()?;
self.dev.flush().map_err(Error::Io)
}
pub fn unmount(mut self) -> Result<D, Error<D::Error>> {
self.flush()?;
let Self { dev, .. } = self;
Ok(dev)
}
fn bps(&self) -> usize {
self.geom.bytes_per_sector as usize
}
fn check_lba(&self, lba: u64) -> Result<(), Error<D::Error>> {
let end = self.geom.part_start + self.geom.volume_sectors;
if lba < self.geom.part_start || lba >= end {
return Err(Error::CorruptChain);
}
Ok(())
}
fn load(&mut self, lba: u64) -> Result<(), Error<D::Error>> {
self.check_lba(lba)?;
if self.cache_lba == Some(lba) {
return Ok(());
}
self.flush_cache()?;
let n = self.bps();
self.dev
.read_sectors(lba, &mut self.buf[..n])
.map_err(Error::Io)?;
self.cache_lba = Some(lba);
Ok(())
}
fn sector(&mut self, lba: u64) -> Result<&[u8], Error<D::Error>> {
self.load(lba)?;
let n = self.bps();
Ok(&self.buf[..n])
}
fn sector_mut(&mut self, lba: u64) -> Result<&mut [u8], Error<D::Error>> {
self.load(lba)?;
self.cache_dirty = true;
let n = self.bps();
Ok(&mut self.buf[..n])
}
fn flush_cache(&mut self) -> Result<(), Error<D::Error>> {
if self.cache_dirty {
if let Some(lba) = self.cache_lba {
let n = self.bps();
self.dev
.write_sectors(lba, &self.buf[..n])
.map_err(Error::Io)?;
}
self.cache_dirty = false;
}
Ok(())
}
fn invalidate(&mut self, first: u64, count: u64) -> Result<(), Error<D::Error>> {
if let Some(lba) = self.cache_lba
&& lba >= first
&& lba < first + count
{
self.flush_cache()?;
self.cache_lba = None;
}
Ok(())
}
fn read_sectors_direct(&mut self, first: u64, buf: &mut [u8]) -> Result<(), Error<D::Error>> {
let count = (buf.len() / self.bps()) as u64;
self.check_lba(first)?;
self.check_lba(first + count - 1)?;
self.invalidate(first, count)?;
self.dev.read_sectors(first, buf).map_err(Error::Io)
}
fn write_sectors_direct(&mut self, first: u64, buf: &[u8]) -> Result<(), Error<D::Error>> {
let count = (buf.len() / self.bps()) as u64;
self.check_lba(first)?;
self.check_lba(first + count - 1)?;
self.invalidate(first, count)?;
self.dev.write_sectors(first, buf).map_err(Error::Io)
}
fn fat_entry(&mut self, cluster: u32) -> Result<u32, Error<D::Error>> {
if !self.geom.is_data_cluster(cluster) {
return Err(Error::CorruptChain);
}
let bps = self.bps() as u64;
let off = cluster as u64 * 4;
if off / bps >= self.geom.fat_sectors as u64 {
return Err(Error::CorruptChain);
}
let lba = self.geom.fat_first_sector() + off / bps;
let at = (off % bps) as usize;
let s = self.sector(lba)?;
Ok(layout::le32(s, at))
}
fn set_fat_entry(&mut self, cluster: u32, value: u32) -> Result<(), Error<D::Error>> {
if !self.geom.is_data_cluster(cluster) {
return Err(Error::CorruptChain);
}
let bps = self.bps() as u64;
let off = cluster as u64 * 4;
if off / bps >= self.geom.fat_sectors as u64 {
return Err(Error::CorruptChain);
}
let lba = self.geom.fat_first_sector() + off / bps;
let at = (off % bps) as usize;
let s = self.sector_mut(lba)?;
s[at..at + 4].copy_from_slice(&value.to_le_bytes());
self.walk = None;
Ok(())
}
fn next_cluster(&mut self, cluster: u32) -> Result<Option<u32>, Error<D::Error>> {
match layout::classify(self.fat_entry(cluster)?) {
FatEntry::Eoc => Ok(None),
FatEntry::Next(next) if self.geom.is_data_cluster(next) => Ok(Some(next)),
_ => Err(Error::CorruptChain),
}
}
fn stream_cluster(&mut self, s: &Stream, index: u32) -> Result<Option<u32>, Error<D::Error>> {
if s.first_cluster < 2 {
return Ok(None);
}
if s.contiguous {
let c = s
.first_cluster
.checked_add(index)
.ok_or(Error::CorruptChain)?;
return if self.geom.is_data_cluster(c) {
Ok(Some(c))
} else {
Err(Error::CorruptChain)
};
}
let (mut cluster, mut at) = match self.walk {
Some((first, i, c)) if first == s.first_cluster && i <= index => (c, i),
_ => (s.first_cluster, 0),
};
if !self.geom.is_data_cluster(cluster) {
return Err(Error::CorruptChain);
}
while at < index {
match self.next_cluster(cluster)? {
Some(next) => {
cluster = next;
at += 1;
}
None => return Ok(None),
}
if at > self.geom.cluster_count {
return Err(Error::CorruptChain);
}
}
self.walk = Some((s.first_cluster, at, cluster));
Ok(Some(cluster))
}
fn stream_pos(&mut self, s: &Stream, off: u64) -> Result<(u64, usize), Error<D::Error>> {
let cb = self.geom.cluster_bytes() as u64;
let index = (off / cb) as u32;
let in_cluster = off % cb;
let cluster = self.stream_cluster(s, index)?.ok_or(Error::CorruptChain)?;
let bps = self.bps() as u64;
let lba = self.geom.cluster_first_sector(cluster) + in_cluster / bps;
Ok((lba, (in_cluster % bps) as usize))
}
fn stream_u16(&mut self, s: &Stream, off: u64) -> Result<u16, Error<D::Error>> {
let (lba, at) = self.stream_pos(s, off)?;
let bps = self.bps();
if at + 2 <= bps {
let sec = self.sector(lba)?;
return Ok(layout::le16(sec, at));
}
let lo = self.sector(lba)?[at];
let (lba2, at2) = self.stream_pos(s, off + 1)?;
let hi = self.sector(lba2)?[at2];
Ok(u16::from_le_bytes([lo, hi]))
}
fn read_slot(
&mut self,
dir: &Stream,
off: u64,
) -> Result<Option<[u8; layout::ENTRY_SIZE]>, Error<D::Error>> {
if dir.len != u64::MAX && off + layout::ENTRY_SIZE as u64 > dir.len {
return Ok(None);
}
let cb = self.geom.cluster_bytes() as u64;
let index = (off / cb) as u32;
if self.stream_cluster(dir, index)?.is_none() {
return Ok(None);
}
let (lba, at) = self.stream_pos(dir, off)?;
let sec = self.sector(lba)?;
let mut out = [0u8; layout::ENTRY_SIZE];
out.copy_from_slice(&sec[at..at + layout::ENTRY_SIZE]);
Ok(Some(out))
}
fn write_slot(
&mut self,
dir: &Stream,
off: u64,
slot: &[u8; layout::ENTRY_SIZE],
) -> Result<(), Error<D::Error>> {
let (lba, at) = self.stream_pos(dir, off)?;
let sec = self.sector_mut(lba)?;
sec[at..at + layout::ENTRY_SIZE].copy_from_slice(slot);
Ok(())
}
fn bitmap(&self) -> Result<Stream, Error<D::Error>> {
self.bitmap.ok_or(Error::NoAllocationBitmap)
}
pub(super) fn require_bitmap(&self) -> Result<(), Error<D::Error>> {
if self.bitmap.is_some() {
Ok(())
} else {
Err(Error::NoAllocationBitmap)
}
}
fn alloc_cluster(&mut self, prev: Option<u32>) -> Result<u32, Error<D::Error>> {
let (first, _) = self.alloc_run(prev, 1)?;
Ok(first)
}
fn alloc_run(&mut self, prev: Option<u32>, want: u64) -> Result<(u32, u64), Error<D::Error>> {
let bm = self.bitmap()?;
let last_cluster = self.geom.cluster_count + 1;
let mut cursor = self.next_free.clamp(2, last_cluster);
let mut scanned = 0u32;
while scanned <= self.geom.cluster_count {
let bit = (cursor - 2) as u64;
let (lba, at) = self.stream_pos(&bm, bit / 8)?;
let byte = self.sector(lba)?[at];
if byte == 0xff {
let step = 8 - (bit % 8) as u32;
cursor = cursor.saturating_add(step);
scanned += step;
} else if byte & (1u8 << (bit % 8)) != 0 {
cursor += 1;
scanned += 1;
} else {
let mut count = 1u64;
while count < want {
let next = cursor as u64 + count;
if next > last_cluster as u64 {
break;
}
let nbit = next - 2;
let (lba, at) = self.stream_pos(&bm, nbit / 8)?;
if self.sector(lba)?[at] & (1u8 << (nbit % 8)) != 0 {
break;
}
count += 1;
}
let mut usable = 0u64;
while usable < count {
let c = cursor + usable as u32;
if self.fat_entry(c)? != layout::FAT_FREE {
break;
}
usable += 1;
}
if usable == 0 {
cursor += 1;
scanned += 1;
continue;
}
let first = cursor;
for i in 0..usable {
let c = first + i as u32;
let value = if i + 1 == usable {
layout::FAT_EOC
} else {
c + 1
};
self.set_fat_entry(c, value)?;
}
if let Some(prev) = prev {
self.set_fat_entry(prev, first)?;
}
self.mark_run(first, usable, true)?;
self.next_free = if first as u64 + usable > last_cluster as u64 {
2
} else {
first + usable as u32
};
return Ok((first, usable));
}
if cursor > last_cluster {
cursor = 2;
}
}
Err(Error::NoSpace)
}
fn mark_run(&mut self, first: u32, count: u64, used: bool) -> Result<(), Error<D::Error>> {
let bm = self.bitmap()?;
let mut done = 0u64;
while done < count {
let cluster = first as u64 + done;
if cluster > self.geom.cluster_count as u64 + 1 {
return Err(Error::CorruptChain);
}
let bit = cluster - 2;
let (lba, at) = self.stream_pos(&bm, bit / 8)?;
let in_byte = (bit % 8) as u32;
let bits = (8 - in_byte as u64).min(count - done);
let mask = if bits == 8 {
0xffu8
} else {
(((1u16 << bits) - 1) as u8) << in_byte
};
let sec = self.sector_mut(lba)?;
if used {
sec[at] |= mask;
} else {
sec[at] &= !mask;
}
done += bits;
}
if !used {
self.next_free = self.next_free.min(first);
}
Ok(())
}
fn alloc_zeroed_cluster(&mut self, prev: Option<u32>) -> Result<u32, Error<D::Error>> {
let cluster = self.alloc_cluster(prev)?;
self.zero_cluster(cluster)?;
Ok(cluster)
}
fn zero_cluster(&mut self, cluster: u32) -> Result<(), Error<D::Error>> {
let first = self.geom.cluster_first_sector(cluster);
for i in 0..self.geom.sectors_per_cluster as u64 {
let sec = self.sector_mut(first + i)?;
sec.fill(0);
}
Ok(())
}
fn free_chain(&mut self, cluster: u32) -> Result<(), Error<D::Error>> {
let mut cur = cluster;
let mut run_start = cluster;
let mut run = 0u64;
let mut freed = 0u32;
loop {
if !self.geom.is_data_cluster(cur) {
return Err(Error::CorruptChain);
}
let entry = self.fat_entry(cur)?;
self.set_fat_entry(cur, layout::FAT_FREE)?;
if cur as u64 == run_start as u64 + run {
run += 1;
} else {
self.mark_run(run_start, run, false)?;
run_start = cur;
run = 1;
}
freed += 1;
if freed > self.geom.cluster_count {
return Err(Error::CorruptChain);
}
match layout::classify(entry) {
FatEntry::Next(next) if self.geom.is_data_cluster(next) => cur = next,
FatEntry::Eoc | FatEntry::Free => break,
_ => return Err(Error::CorruptChain),
}
}
self.mark_run(run_start, run, false)
}
fn free_run(&mut self, first: u32, count: u64) -> Result<(), Error<D::Error>> {
if count == 0 {
return Ok(());
}
let last = first as u64 + count - 1;
if last > u32::MAX as u64 || !self.geom.is_data_cluster(last as u32) {
return Err(Error::CorruptChain);
}
self.mark_run(first, count, false)
}
fn free_stream(&mut self, s: &Stream) -> Result<(), Error<D::Error>> {
if s.first_cluster < 2 {
return Ok(());
}
if s.contiguous {
let cb = self.geom.cluster_bytes() as u64;
let clusters = s.len.div_ceil(cb).max(1);
self.free_run(s.first_cluster, clusters)
} else {
self.free_chain(s.first_cluster)
}
}
pub fn used_clusters(&mut self) -> Result<u32, Error<D::Error>> {
let bm = self.bitmap()?;
let count = self.geom.cluster_count;
let bytes = (count as u64).div_ceil(8);
let mut used = 0u32;
let mut off = 0u64;
while off < bytes {
let (lba, at) = self.stream_pos(&bm, off)?;
let bps = self.bps();
let sec = self.sector(lba)?;
let n = ((bytes - off) as usize).min(bps - at);
for &b in &sec[at..at + n] {
used += b.count_ones();
}
off += n as u64;
}
Ok(used.min(count))
}
pub fn free_clusters(&mut self) -> Result<u32, Error<D::Error>> {
Ok(self.geom.cluster_count - self.used_clusters()?)
}
pub fn free_bytes(&mut self) -> Result<u64, Error<D::Error>> {
Ok(self.free_clusters()? as u64 * self.geom.cluster_bytes() as u64)
}
fn up(&mut self, ch: u16) -> Result<u16, Error<D::Error>> {
if (ch as usize) < UP_ASCII {
return Ok(self.up_ascii[ch as usize]);
}
#[cfg(feature = "alloc")]
{
if self.up_cache.is_none() {
self.build_up_cache()?;
}
if let Some(t) = &self.up_cache {
return Ok(t.get(ch as usize).copied().unwrap_or(ch));
}
}
self.up_on_disk(ch)
}
fn up_on_disk(&mut self, ch: u16) -> Result<u16, Error<D::Error>> {
let Some(table) = self.upcase else {
return Ok(ch);
};
let want = ch as usize;
let mut index = 0usize;
let mut off = 0u64;
while off + 2 <= table.len {
let v = self.stream_u16(&table, off)?;
off += 2;
if v == 0xFFFF {
if off + 2 > table.len {
break;
}
let count = self.stream_u16(&table, off)? as usize;
off += 2;
if want < index + count {
return Ok(ch);
}
index += count;
} else {
if index == want {
return Ok(v);
}
index += 1;
}
if index > want {
break;
}
}
Ok(ch)
}
#[cfg(feature = "alloc")]
fn build_up_cache(&mut self) -> Result<(), Error<D::Error>> {
let Some(table) = self.upcase else {
self.up_cache = Some(::alloc::vec::Vec::new());
return Ok(());
};
let mut out: ::alloc::vec::Vec<u16> = ::alloc::vec::Vec::new();
if out
.try_reserve_exact((table.len as usize / 2).min(0x1_0000))
.is_err()
{
return Ok(());
}
let mut off = 0u64;
while off + 2 <= table.len && out.len() < 0x1_0000 {
let v = self.stream_u16(&table, off)?;
off += 2;
if v == 0xFFFF {
if off + 2 > table.len {
break;
}
let count = self.stream_u16(&table, off)? as usize;
off += 2;
for _ in 0..count {
if out.len() >= 0x1_0000 {
break;
}
let identity = out.len() as u16;
out.push(identity);
}
} else {
out.push(v);
}
}
self.up_cache = Some(out);
Ok(())
}
fn name_hash(&mut self, name: &str) -> Result<u16, Error<D::Error>> {
let mut hash = 0u16;
for unit in name.encode_utf16() {
hash = layout::name_hash_step(hash, self.up(unit)?);
}
Ok(hash)
}
}