pub const FREE: u32 = 0;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FatKind {
Fat12,
Fat16,
Fat32,
}
impl FatKind {
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)]
pub struct Fat {
kind: FatKind,
byte_len: usize,
entries: Vec<u32>,
}
impl Fat {
pub fn new(kind: FatKind, byte_len: usize, media: u8) -> Self {
let capacity = kind.entries_in(byte_len).max(2);
let mut entries = vec![FREE; capacity];
entries[0] = (0xFFFF_FF00 | media as u32) & kind.entry_mask();
entries[1] = kind.eoc();
Self {
kind,
byte_len,
entries,
}
}
pub fn kind(&self) -> FatKind {
self.kind
}
pub fn capacity(&self) -> usize {
self.entries.len()
}
pub fn eoc(&self) -> u32 {
self.kind.eoc()
}
pub fn is_eoc(&self, value: u32) -> bool {
self.kind.is_eoc(value)
}
pub fn get(&self, cluster: u32) -> u32 {
self.entries[cluster as usize] & self.kind.entry_mask()
}
pub fn set(&mut self, cluster: u32, value: u32) {
self.entries[cluster as usize] = value & self.kind.entry_mask();
}
pub fn encode(&self) -> Vec<u8> {
let mut out = vec![0u8; self.byte_len];
match self.kind {
FatKind::Fat12 => {
for i in (0..self.entries.len()).step_by(2) {
let e0 = self.entries[i] & 0xFFF;
let e1 = self.entries.get(i + 1).copied().unwrap_or(0) & 0xFFF;
let base = i / 2 * 3;
if base < out.len() {
out[base] = (e0 & 0xFF) as u8;
}
if base + 1 < out.len() {
out[base + 1] = (((e0 >> 8) & 0x0F) | ((e1 & 0x0F) << 4)) as u8;
}
if base + 2 < out.len() {
out[base + 2] = ((e1 >> 4) & 0xFF) as u8;
}
}
}
FatKind::Fat16 => {
for (i, &e) in self.entries.iter().enumerate() {
let at = i * 2;
if at + 2 <= out.len() {
out[at..at + 2].copy_from_slice(&(e as u16).to_le_bytes());
}
}
}
FatKind::Fat32 => {
for (i, &e) in self.entries.iter().enumerate() {
let at = i * 4;
if at + 4 <= out.len() {
out[at..at + 4].copy_from_slice(&e.to_le_bytes());
}
}
}
}
out
}
pub fn decode(kind: FatKind, bytes: &[u8]) -> Self {
let capacity = kind.entries_in(bytes.len());
let mut entries = vec![FREE; capacity];
match kind {
FatKind::Fat12 => {
for (i, slot) in entries.iter_mut().enumerate() {
let base = i / 2 * 3;
*slot = if i.is_multiple_of(2) {
u32::from(bytes[base]) | (u32::from(bytes[base + 1] & 0x0F) << 8)
} else {
u32::from(bytes[base + 1] >> 4) | (u32::from(bytes[base + 2]) << 4)
};
}
}
FatKind::Fat16 => {
for (i, slot) in entries.iter_mut().enumerate() {
*slot = u32::from(u16::from_le_bytes(
bytes[i * 2..i * 2 + 2].try_into().unwrap(),
));
}
}
FatKind::Fat32 => {
for (i, slot) in entries.iter_mut().enumerate() {
*slot = u32::from_le_bytes(bytes[i * 4..i * 4 + 4].try_into().unwrap());
}
}
}
Self {
kind,
byte_len: bytes.len(),
entries,
}
}
pub fn chain(&self, start: u32, cluster_count: u32) -> crate::Result<Vec<u32>> {
let max_cluster = (cluster_count as usize) + 2;
let bound = max_cluster.min(self.entries.len());
let mut out = Vec::new();
let mut cur = start;
while !self.is_eoc(cur) {
if cur < 2 || cur as usize >= bound {
return Err(crate::Error::InvalidImage(format!(
"fat: cluster {cur} out of range while walking a chain"
)));
}
if out.len() > cluster_count as usize {
return Err(crate::Error::InvalidImage(
"fat: cluster chain loops".into(),
));
}
out.push(cur);
cur = self.get(cur);
if cur == FREE {
return Err(crate::Error::InvalidImage(
"fat: cluster chain hits a free cluster".into(),
));
}
}
Ok(out)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn reserved_entries() {
let fat = Fat::new(FatKind::Fat32, 1024, 0xF8);
assert_eq!(fat.get(0), 0x0FFF_FFF8);
assert_eq!(fat.get(1), 0x0FFF_FFFF);
assert_eq!(fat.get(2), FREE);
let fat = Fat::new(FatKind::Fat16, 512, 0xF8);
assert_eq!(fat.get(0), 0xFFF8);
assert_eq!(fat.get(1), 0xFFFF);
let fat = Fat::new(FatKind::Fat12, 512, 0xF0);
assert_eq!(fat.get(0), 0xFF0);
assert_eq!(fat.get(1), 0xFFF);
}
#[test]
fn set_get_roundtrip_via_bytes() {
for kind in [FatKind::Fat12, FatKind::Fat16, FatKind::Fat32] {
let mut fat = Fat::new(kind, 512, 0xF8);
fat.set(2, 3);
fat.set(3, 4);
fat.set(4, kind.eoc());
let decoded = Fat::decode(kind, &fat.encode());
assert_eq!(decoded.chain(2, 60).unwrap(), vec![2, 3, 4], "{kind:?}");
}
}
#[test]
fn fat12_packing_covers_every_value_at_both_parities() {
let mut fat = Fat::new(FatKind::Fat12, 4096 * 3 / 2, 0xF8);
assert!(fat.capacity() >= 4096);
for c in 0..4096u32 {
fat.set(c, c);
}
let decoded = Fat::decode(FatKind::Fat12, &fat.encode());
for c in 0..4096u32 {
assert_eq!(decoded.get(c), c, "entry {c}");
}
}
#[test]
fn fat12_odd_byte_length_roundtrips() {
let mut fat = Fat::new(FatKind::Fat12, 512, 0xF8);
assert_eq!(fat.capacity(), 341);
fat.set(340, 0xABC);
let bytes = fat.encode();
assert_eq!(bytes.len(), 512);
assert_eq!(Fat::decode(FatKind::Fat12, &bytes).get(340), 0xABC);
}
#[test]
fn eoc_classification_per_width() {
assert!(FatKind::Fat12.is_eoc(0xFFF));
assert!(FatKind::Fat12.is_eoc(0xFF8));
assert!(!FatKind::Fat12.is_eoc(0xFF7)); assert!(!FatKind::Fat12.is_eoc(5));
assert!(FatKind::Fat16.is_eoc(0xFFF8));
assert!(!FatKind::Fat16.is_eoc(0xFFF7));
assert!(FatKind::Fat32.is_eoc(0x0FFF_FFF8));
assert!(!FatKind::Fat32.is_eoc(0x0FFF_FFF7));
}
#[test]
fn kind_from_cluster_count_follows_the_spec_thresholds() {
assert_eq!(FatKind::from_cluster_count(1), FatKind::Fat12);
assert_eq!(FatKind::from_cluster_count(4084), FatKind::Fat12);
assert_eq!(FatKind::from_cluster_count(4085), FatKind::Fat16);
assert_eq!(FatKind::from_cluster_count(65524), FatKind::Fat16);
assert_eq!(FatKind::from_cluster_count(65525), FatKind::Fat32);
}
#[test]
fn chain_detects_free_break() {
let mut fat = Fat::new(FatKind::Fat32, 512, 0xF8);
fat.set(2, 3); assert!(fat.chain(2, 62).is_err());
}
}