mod boot;
mod dir;
mod file;
pub use boot::{Geometry, MAX_SECTOR_SIZE, MIN_SECTOR_SIZE};
pub use dir::{DirEntry, DirIter};
pub use file::{File, MAX_FILE_LEN};
pub use crate::device::SectorDriver;
use crate::device::{gpt, mbr};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Error<E> {
Io(E),
NotFat,
SectorSizeMismatch {
volume: u32,
driver: u32,
},
ScratchTooSmall {
needed: usize,
got: usize,
},
VolumeExceedsDevice,
NoSuchPartition,
NotFound,
NotADirectory,
IsADirectory,
AlreadyExists,
DirectoryNotEmpty,
InvalidName,
InvalidPath,
DirectoryFull,
NoSpace,
CorruptChain,
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::NotFat => f.write_str("not a FAT volume"),
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::NoSuchPartition => f.write_str("no such partition"),
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::FileTooLarge => f.write_str("file would exceed 4 GiB"),
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 enum FatKind {
Fat12,
Fat16,
Fat32,
}
impl FatKind {
fn eoc_floor(&self) -> u32 {
self.eoc_min()
}
fn eoc_mark(&self) -> u32 {
self.eoc()
}
pub fn bits(self) -> u32 {
match self {
FatKind::Fat12 => 12,
FatKind::Fat16 => 16,
FatKind::Fat32 => 32,
}
}
pub fn entry_mask(self) -> u32 {
match self {
FatKind::Fat12 => 0x0000_0FFF,
FatKind::Fat16 => 0x0000_FFFF,
FatKind::Fat32 => 0x0FFF_FFFF,
}
}
pub fn eoc(self) -> u32 {
self.entry_mask()
}
pub fn eoc_min(self) -> u32 {
self.entry_mask() & !0x7
}
pub fn is_eoc(self, value: u32) -> bool {
value >= self.eoc_min()
}
pub fn bad_cluster(self) -> u32 {
self.eoc_min() - 1
}
pub fn min_clusters(self) -> u32 {
match self {
FatKind::Fat12 => 1,
FatKind::Fat16 => 4085,
FatKind::Fat32 => 65525,
}
}
pub fn max_clusters(self) -> u32 {
match self {
FatKind::Fat12 => 4084,
FatKind::Fat16 => 65524,
FatKind::Fat32 => 0x0FFF_FFF4,
}
}
pub fn from_cluster_count(clusters: u32) -> FatKind {
if clusters < FatKind::Fat16.min_clusters() {
FatKind::Fat12
} else if clusters < FatKind::Fat32.min_clusters() {
FatKind::Fat16
} else {
FatKind::Fat32
}
}
pub fn fat_bytes(self, entries: u64) -> u64 {
match self {
FatKind::Fat12 => (entries * 3).div_ceil(2),
FatKind::Fat16 => entries * 2,
FatKind::Fat32 => entries * 4,
}
}
pub fn entries_in(self, bytes: usize) -> usize {
match self {
FatKind::Fat12 => bytes * 2 / 3,
FatKind::Fat16 => bytes / 2,
FatKind::Fat32 => bytes / 4,
}
}
pub fn fs_type_label(self) -> &'static [u8; 8] {
match self {
FatKind::Fat12 => b"FAT12 ",
FatKind::Fat16 => b"FAT16 ",
FatKind::Fat32 => b"FAT32 ",
}
}
pub fn as_str(self) -> &'static str {
match self {
FatKind::Fat12 => "fat12",
FatKind::Fat16 => "fat16",
FatKind::Fat32 => "fat32",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Timestamp {
pub date: u16,
pub time: u16,
pub tenths: u8,
}
impl Timestamp {
pub const EPOCH: Self = Self {
date: 0x0021,
time: 0,
tenths: 0,
};
pub fn from_ymd_hms(year: u16, month: u8, day: u8, hour: u8, min: u8, sec: u8) -> Self {
let y = year.clamp(1980, 2107) - 1980;
let mo = month.clamp(1, 12) as u16;
let d = day.clamp(1, 31) as u16;
let h = hour.min(23) as u16;
let mi = min.min(59) as u16;
let s = sec.min(59) as u16;
Self {
date: (y << 9) | (mo << 5) | d,
time: (h << 11) | (mi << 5) | (s / 2),
tenths: if s % 2 == 1 { 100 } else { 0 },
}
}
pub fn year(&self) -> u16 {
1980 + (self.date >> 9)
}
pub fn month(&self) -> u8 {
((self.date >> 5) & 0x0F) as u8
}
pub fn day(&self) -> u8 {
(self.date & 0x1F) as u8
}
pub fn hour(&self) -> u8 {
(self.time >> 11) as u8
}
pub fn minute(&self) -> u8 {
((self.time >> 5) & 0x3F) as u8
}
pub fn second(&self) -> u8 {
((self.time & 0x1F) * 2) as u8 + u8::from(self.tenths >= 100)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Attributes(pub u8);
impl Attributes {
pub const READ_ONLY: u8 = 0x01;
pub const HIDDEN: u8 = 0x02;
pub const SYSTEM: u8 = 0x04;
pub const VOLUME_ID: u8 = 0x08;
pub const DIRECTORY: u8 = 0x10;
pub const ARCHIVE: u8 = 0x20;
pub const LONG_NAME: u8 = 0x0F;
pub fn is_read_only(&self) -> bool {
self.0 & Self::READ_ONLY != 0
}
pub fn is_hidden(&self) -> bool {
self.0 & Self::HIDDEN != 0
}
pub fn is_system(&self) -> bool {
self.0 & Self::SYSTEM != 0
}
pub fn is_volume_id(&self) -> bool {
self.0 & Self::VOLUME_ID != 0
}
pub fn is_dir(&self) -> bool {
self.0 & Self::DIRECTORY != 0
}
pub fn is_archive(&self) -> bool {
self.0 & Self::ARCHIVE != 0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct EntryLoc {
pub(crate) sector: u32,
pub(crate) offset: u16,
}
impl EntryLoc {
pub(crate) const NONE: Self = Self {
sector: 0,
offset: 0,
};
pub(crate) fn is_none(&self) -> bool {
self.sector == 0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Metadata {
pub attrs: Attributes,
pub len: u32,
pub created: Timestamp,
pub modified: Timestamp,
pub(crate) first_cluster: u32,
pub(crate) loc: EntryLoc,
}
impl Metadata {
pub fn is_dir(&self) -> bool {
self.attrs.is_dir()
}
pub fn is_file(&self) -> bool {
!self.attrs.is_dir() && !self.attrs.is_volume_id()
}
pub fn len(&self) -> u32 {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Dir {
pub(crate) first_cluster: u32,
pub(crate) fixed_root: bool,
pub(crate) loc: EntryLoc,
}
#[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,
free_count: Option<u32>,
next_free: u32,
#[cfg(feature = "alloc")]
fat_cache: FatCache,
fsinfo_dirty: bool,
now: Timestamp,
}
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>> {
Self::check_scratch(&dev)?;
let mut first = [0u8; SECTOR];
let ss = dev.sector_size() as usize;
Self::read_raw(&mut dev, 0, &mut first[..ss])?;
let device_bytes = Self::device_bytes(&dev);
if Geometry::parse::<D::Error>(&first[..ss], 0, device_bytes).is_ok() {
return Self::mount_at(dev, 0);
}
if let Some(table) = gpt::Table::read(&mut dev, &mut first[..ss]).map_err(Error::Io)? {
for pass in 0..2 {
for i in 0..table.entries() {
let Some(part) = table
.entry(&mut dev, &mut first[..ss], i)
.map_err(Error::Io)?
else {
continue;
};
if (pass == 0) != part.looks_like_fat_family() {
continue;
}
if Self::probe_at(&mut dev, part.start_lba, &mut first[..ss]).is_ok() {
return Self::mount_at(dev, part.start_lba);
}
}
}
return Err(Error::NotFat);
}
Self::read_raw(&mut dev, 0, &mut first[..ss])?;
if let Some(table) = boot::parse_mbr(&first[..ss]) {
for pass in 0..2 {
for slot in table.iter().flatten() {
if (pass == 0) != slot.looks_like_fat() {
continue;
}
if Self::probe_at(&mut dev, slot.start_lba, &mut first[..ss]).is_ok() {
return Self::mount_at(dev, slot.start_lba);
}
}
}
}
Err(Error::NotFat)
}
pub fn mount_at(mut dev: D, start_lba: u64) -> Result<Self, Error<D::Error>> {
Self::check_scratch(&dev)?;
let ss = dev.sector_size() as usize;
let mut sector = [0u8; SECTOR];
Self::read_raw(&mut dev, start_lba, &mut sector[..ss])?;
let device_bytes = Self::device_bytes(&dev);
let geom = Geometry::parse::<D::Error>(§or[..ss], start_lba, device_bytes)?;
if geom.bytes_per_sector != dev.sector_size() {
return Err(Error::SectorSizeMismatch {
volume: geom.bytes_per_sector,
driver: dev.sector_size(),
});
}
let mut vol = Self {
dev,
geom,
buf: [0u8; SECTOR],
cache_lba: None,
cache_dirty: false,
free_count: None,
#[cfg(feature = "alloc")]
fat_cache: FatCache::default(),
next_free: 2,
fsinfo_dirty: false,
now: Timestamp::EPOCH,
};
vol.load_fsinfo()?;
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])?;
boot::parse_mbr(§or[..ss])
.and_then(|t| t[index as usize - 1])
.ok_or(Error::NoSuchPartition)
}
fn probe_at(dev: &mut D, lba: u64, scratch: &mut [u8]) -> Result<Geometry, Error<D::Error>> {
Self::read_raw(dev, lba, scratch)?;
let device_bytes = Self::device_bytes(dev);
Geometry::parse::<D::Error>(scratch, lba, device_bytes)
}
fn check_scratch(dev: &D) -> Result<(), Error<D::Error>> {
let ss = dev.sector_size() as usize;
if !(MIN_SECTOR_SIZE..=MAX_SECTOR_SIZE).contains(&ss) || !ss.is_power_of_two() {
return Err(Error::NotFat);
}
if SECTOR < ss {
return Err(Error::ScratchTooSmall {
needed: ss,
got: SECTOR,
});
}
Ok(())
}
fn device_bytes(dev: &D) -> u64 {
dev.sector_count().saturating_mul(dev.sector_size() as u64)
}
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)
}
pub fn geometry(&self) -> &Geometry {
&self.geom
}
pub fn kind(&self) -> FatKind {
self.geom.kind
}
pub fn cluster_bytes(&self) -> u32 {
self.geom.cluster_bytes()
}
pub fn total_bytes(&self) -> u64 {
self.geom.cluster_count as u64 * self.geom.cluster_bytes() as u64
}
pub fn driver(&self) -> &D {
&self.dev
}
pub fn driver_mut(&mut self) -> &mut D {
&mut self.dev
}
pub fn unmount(mut self) -> Result<D, Error<D::Error>> {
self.flush()?;
#[cfg(feature = "alloc")]
drop(core::mem::take(&mut self.fat_cache));
let Self {
dev: _,
geom: _,
buf: _,
cache_lba: _,
cache_dirty: _,
free_count: _,
next_free: _,
#[cfg(feature = "alloc")]
fat_cache: _,
fsinfo_dirty: _,
now: _,
} = &self;
let me = core::mem::ManuallyDrop::new(self);
Ok(unsafe { core::ptr::read(&me.dev) })
}
pub fn set_time(&mut self, now: Timestamp) {
self.now = now;
}
pub fn time(&self) -> Timestamp {
self.now
}
pub fn fat_cache_bytes(&self) -> usize {
#[cfg(feature = "alloc")]
{
self.fat_cache.bytes_held()
}
#[cfg(not(feature = "alloc"))]
{
0
}
}
pub fn root(&self) -> Dir {
Dir {
first_cluster: if self.geom.kind == FatKind::Fat32 {
self.geom.root_cluster
} else {
0
},
fixed_root: self.geom.kind != FatKind::Fat32,
loc: EntryLoc::NONE,
}
}
fn bps(&self) -> usize {
self.geom.bytes_per_sector as usize
}
fn abs(&self, rel_sector: u32) -> u64 {
self.geom.part_start + rel_sector as u64
}
fn load(&mut self, sector: u32) -> Result<(), Error<D::Error>> {
if sector >= self.geom.total_sectors {
return Err(Error::CorruptChain);
}
let abs = self.abs(sector);
if self.cache_lba == Some(abs) {
return Ok(());
}
self.flush_cache()?;
let n = self.bps();
self.dev
.read_sectors(abs, &mut self.buf[..n])
.map_err(Error::Io)?;
self.cache_lba = Some(abs);
Ok(())
}
fn sector(&mut self, sector: u32) -> Result<&[u8], Error<D::Error>> {
self.load(sector)?;
let n = self.bps();
Ok(&self.buf[..n])
}
fn sector_mut(&mut self, sector: u32) -> Result<&mut [u8], Error<D::Error>> {
self.load(sector)?;
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(())
}
pub(crate) fn check_range(&self, first: u32, count: u32) -> Result<(), Error<D::Error>> {
let end = first as u64 + count as u64;
if end > self.geom.total_sectors as u64 {
return Err(Error::CorruptChain);
}
Ok(())
}
fn invalidate(&mut self, first: u32, count: u32) -> Result<(), Error<D::Error>> {
if let Some(lba) = self.cache_lba {
let first_abs = self.abs(first);
if lba >= first_abs && lba < first_abs + count as u64 {
self.flush_cache()?;
self.cache_lba = None;
}
}
Ok(())
}
pub fn flush(&mut self) -> Result<(), Error<D::Error>> {
self.flush_cache()?;
self.store_fsinfo()?;
self.flush_cache()?;
self.dev.flush().map_err(Error::Io)
}
fn fat_offset(&self, cluster: u32) -> u64 {
match self.geom.kind {
FatKind::Fat12 => cluster as u64 + (cluster as u64 / 2),
FatKind::Fat16 => cluster as u64 * 2,
FatKind::Fat32 => cluster as u64 * 4,
}
}
fn fat_start(&self, n: u32) -> u32 {
self.geom.reserved_sectors + n * self.geom.fat_sectors
}
fn fat_byte(&mut self, off: u64) -> Result<u8, Error<D::Error>> {
let bps = self.bps() as u64;
let sector = self.fat_start(self.geom.active_fat) + (off / bps) as u32;
let at = (off % bps) as usize;
#[cfg(feature = "alloc")]
{
let rel = (off / bps) as u32;
if let Some(byte) = self.cached_fat_byte(rel, at)? {
return Ok(byte);
}
}
Ok(self.sector(sector)?[at])
}
#[cfg(feature = "alloc")]
fn cached_fat_byte(&mut self, rel: u32, at: usize) -> Result<Option<u8>, Error<D::Error>> {
let bps = self.bps();
if rel >= self.geom.fat_sectors {
return Ok(None);
}
if !self.fat_cache.holds(rel) {
let sector = self.fat_start(self.geom.active_fat) + rel;
let mut tmp = [0u8; SECTOR];
tmp[..bps].copy_from_slice(self.sector(sector)?);
self.fat_cache
.store(rel, &tmp[..bps], self.geom.fat_sectors);
}
Ok(self.fat_cache.byte(rel, at, bps))
}
fn fat_entry(&mut self, cluster: u32) -> Result<u32, Error<D::Error>> {
if cluster > self.geom.cluster_count + 1 {
return Err(Error::CorruptChain);
}
let off = self.fat_offset(cluster);
match self.geom.kind {
FatKind::Fat12 => {
let lo = self.fat_byte(off)? as u32;
let hi = self.fat_byte(off + 1)? as u32;
let raw = lo | (hi << 8);
Ok(if cluster & 1 == 0 {
raw & 0x0FFF
} else {
raw >> 4
})
}
FatKind::Fat16 => {
let lo = self.fat_byte(off)? as u32;
let hi = self.fat_byte(off + 1)? as u32;
Ok(lo | (hi << 8))
}
FatKind::Fat32 => {
let mut b = [0u8; 4];
for (i, slot) in b.iter_mut().enumerate() {
*slot = self.fat_byte(off + i as u64)?;
}
Ok(u32::from_le_bytes(b) & 0x0FFF_FFFF)
}
}
}
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 off = self.fat_offset(cluster);
let mut edits = [(0u64, 0u8, 0u8); 4];
let n = match self.geom.kind {
FatKind::Fat12 => {
let v = value & 0x0FFF;
if cluster & 1 == 0 {
edits[0] = (off, 0x00, (v & 0xFF) as u8);
edits[1] = (off + 1, 0xF0, (v >> 8) as u8 & 0x0F);
} else {
edits[0] = (off, 0x0F, ((v & 0x0F) as u8) << 4);
edits[1] = (off + 1, 0x00, (v >> 4) as u8);
}
2
}
FatKind::Fat16 => {
let b = ((value & 0xFFFF) as u16).to_le_bytes();
edits[0] = (off, 0, b[0]);
edits[1] = (off + 1, 0, b[1]);
2
}
FatKind::Fat32 => {
let b = (value & 0x0FFF_FFFF).to_le_bytes();
edits[0] = (off, 0, b[0]);
edits[1] = (off + 1, 0, b[1]);
edits[2] = (off + 2, 0, b[2]);
edits[3] = (off + 3, 0xF0, b[3] & 0x0F);
4
}
};
let bps = self.bps() as u64;
let copies = if self.geom.mirrored {
0..self.geom.num_fats
} else {
self.geom.active_fat..self.geom.active_fat + 1
};
for copy in copies {
for &(at_off, keep, set) in &edits[..n] {
let sector = self.fat_start(copy) + (at_off / bps) as u32;
let at = (at_off % bps) as usize;
let buf = self.sector_mut(sector)?;
buf[at] = (buf[at] & keep) | set;
}
}
#[cfg(feature = "alloc")]
{
let bps = self.bps();
for &(at_off, keep, set) in &edits[..n] {
let rel = (at_off / bps as u64) as u32;
let at = (at_off % bps as u64) as usize;
self.fat_cache.patch(rel, at, keep, set, bps);
}
}
Ok(())
}
pub(crate) fn next_cluster(&mut self, cluster: u32) -> Result<Option<u32>, Error<D::Error>> {
let entry = self.fat_entry(cluster)?;
if entry >= self.geom.kind.eoc_floor() {
return Ok(None);
}
if !self.geom.is_data_cluster(entry) {
return Err(Error::CorruptChain);
}
Ok(Some(entry))
}
pub(crate) fn alloc_cluster(&mut self, prev: Option<u32>) -> Result<u32, Error<D::Error>> {
let last = self.geom.cluster_count + 1;
let start = self.next_free.clamp(2, last);
let mut found = None;
for cluster in start..=last {
if self.fat_entry(cluster)? == 0 {
found = Some(cluster);
break;
}
}
if found.is_none() {
for cluster in 2..start {
if self.fat_entry(cluster)? == 0 {
found = Some(cluster);
break;
}
}
}
let cluster = found.ok_or(Error::NoSpace)?;
let eoc = self.geom.kind.eoc_mark();
self.set_fat_entry(cluster, eoc)?;
if let Some(prev) = prev {
self.set_fat_entry(prev, cluster)?;
}
self.next_free = if cluster >= last { 2 } else { cluster + 1 };
if let Some(free) = self.free_count.as_mut() {
*free = free.saturating_sub(1);
}
self.fsinfo_dirty = true;
Ok(cluster)
}
pub(crate) fn alloc_zeroed_cluster(
&mut self,
prev: Option<u32>,
) -> Result<u32, Error<D::Error>> {
let cluster = self.alloc_cluster(prev)?;
let first = self.geom.cluster_first_sector(cluster);
for i in 0..self.geom.sectors_per_cluster {
let buf = self.sector_mut(first + i)?;
buf.fill(0);
}
Ok(cluster)
}
pub(crate) fn free_chain(&mut self, cluster: u32) -> Result<(), Error<D::Error>> {
let mut cur = cluster;
loop {
if !self.geom.is_data_cluster(cur) {
return Err(Error::CorruptChain);
}
let entry = self.fat_entry(cur)?;
self.set_fat_entry(cur, 0)?;
if let Some(free) = self.free_count.as_mut() {
*free = free.saturating_add(1);
}
self.next_free = self.next_free.min(cur);
self.fsinfo_dirty = true;
if entry >= self.geom.kind.eoc_floor() {
return Ok(());
}
if !self.geom.is_data_cluster(entry) {
return Err(Error::CorruptChain);
}
cur = entry;
}
}
pub(crate) fn truncate_chain(&mut self, cluster: u32) -> Result<(), Error<D::Error>> {
let rest = self.next_cluster(cluster)?;
let eoc = self.geom.kind.eoc_mark();
self.set_fat_entry(cluster, eoc)?;
if let Some(next) = rest {
self.free_chain(next)?;
}
Ok(())
}
pub fn free_clusters(&mut self) -> Result<u32, Error<D::Error>> {
if let Some(free) = self.free_count {
return Ok(free);
}
let mut free = 0;
for cluster in 2..=self.geom.cluster_count + 1 {
if self.fat_entry(cluster)? == 0 {
free += 1;
}
}
self.free_count = Some(free);
Ok(free)
}
pub fn free_bytes(&mut self) -> Result<u64, Error<D::Error>> {
Ok(self.free_clusters()? as u64 * self.geom.cluster_bytes() as u64)
}
fn load_fsinfo(&mut self) -> Result<(), Error<D::Error>> {
if self.geom.kind != FatKind::Fat32 || self.geom.fs_info_sector == 0 {
return Ok(());
}
let sector = self.geom.fs_info_sector;
let buf = self.sector(sector)?;
let lead = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
let struc = u32::from_le_bytes([buf[484], buf[485], buf[486], buf[487]]);
let trail = u32::from_le_bytes([buf[508], buf[509], buf[510], buf[511]]);
if lead != 0x4161_5252 || struc != 0x6141_7272 || trail != 0xAA55_0000 {
return Ok(());
}
let free = u32::from_le_bytes([buf[488], buf[489], buf[490], buf[491]]);
let next = u32::from_le_bytes([buf[492], buf[493], buf[494], buf[495]]);
let last = self.geom.cluster_count + 1;
if free != u32::MAX && free <= self.geom.cluster_count {
self.free_count = Some(free);
}
self.next_free = if next >= 2 && next <= last { next } else { 2 };
Ok(())
}
fn store_fsinfo(&mut self) -> Result<(), Error<D::Error>> {
if !self.fsinfo_dirty || self.geom.kind != FatKind::Fat32 || self.geom.fs_info_sector == 0 {
return Ok(());
}
let free = self.free_count.unwrap_or(u32::MAX);
let next = self.next_free;
let sector = self.geom.fs_info_sector;
let buf = self.sector_mut(sector)?;
let lead = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
let struc = u32::from_le_bytes([buf[484], buf[485], buf[486], buf[487]]);
if lead == 0x4161_5252 && struc == 0x6141_7272 {
buf[488..492].copy_from_slice(&free.to_le_bytes());
buf[492..496].copy_from_slice(&next.to_le_bytes());
}
self.fsinfo_dirty = false;
Ok(())
}
}
#[cfg(feature = "alloc")]
#[derive(Debug, Default)]
struct FatCache {
bytes: alloc::vec::Vec<u8>,
present: alloc::vec::Vec<bool>,
}
#[cfg(feature = "alloc")]
impl FatCache {
fn holds(&self, rel: u32) -> bool {
self.present.get(rel as usize).copied().unwrap_or(false)
}
fn store(&mut self, rel: u32, sector: &[u8], fat_sectors: u32) {
if self.bytes.is_empty() {
let total = fat_sectors as usize * sector.len();
if total == 0 || self.bytes.try_reserve_exact(total).is_err() {
return;
}
self.bytes.resize(total, 0);
self.present.resize(fat_sectors as usize, false);
}
let at = rel as usize * sector.len();
if at + sector.len() <= self.bytes.len() {
self.bytes[at..at + sector.len()].copy_from_slice(sector);
self.present[rel as usize] = true;
}
}
fn byte(&self, rel: u32, at: usize, bps: usize) -> Option<u8> {
if !self.holds(rel) {
return None;
}
self.bytes.get(rel as usize * bps + at).copied()
}
fn patch(&mut self, rel: u32, at: usize, keep: u8, set: u8, bps: usize) {
if !self.holds(rel) {
return;
}
if let Some(b) = self.bytes.get_mut(rel as usize * bps + at) {
*b = (*b & keep) | set;
}
}
fn bytes_held(&self) -> usize {
self.bytes.len()
}
}
impl<D: SectorDriver, const SECTOR: usize> Drop for Volume<D, SECTOR> {
fn drop(&mut self) {
let _ = self.flush();
}
}
#[cfg(test)]
mod tests;