use alloc::format;
pub const BOOT_SECTOR_PARSE_SIZE: usize = 512;
pub use super::layout::MAX_CLUSTER_COUNT;
#[derive(Debug, Clone)]
pub struct BootSector {
pub partition_offset: u64,
pub volume_length: u64,
pub fat_offset: u32,
pub fat_length: u32,
pub cluster_heap_offset: u32,
pub cluster_count: u32,
pub first_cluster_of_root_directory: u32,
pub volume_serial_number: u32,
pub fs_revision_major: u8,
pub fs_revision_minor: u8,
pub volume_flags: u16,
pub bytes_per_sector_shift: u8,
pub sectors_per_cluster_shift: u8,
pub number_of_fats: u8,
pub drive_select: u8,
pub percent_in_use: u8,
}
impl BootSector {
pub fn bytes_per_sector(&self) -> u32 {
1u32 << self.bytes_per_sector_shift
}
pub fn sectors_per_cluster(&self) -> u32 {
1u32 << self.sectors_per_cluster_shift
}
pub fn bytes_per_cluster(&self) -> u32 {
self.bytes_per_sector() << self.sectors_per_cluster_shift
}
pub fn fat_byte_offset(&self) -> u64 {
self.fat_offset as u64 * self.bytes_per_sector() as u64
}
pub fn fat_byte_length(&self) -> u64 {
self.fat_length as u64 * self.bytes_per_sector() as u64
}
pub fn cluster_heap_byte_offset(&self) -> u64 {
self.cluster_heap_offset as u64 * self.bytes_per_sector() as u64
}
pub fn cluster_byte_offset(&self, cluster: u32) -> u64 {
self.cluster_heap_byte_offset() + (cluster as u64 - 2) * self.bytes_per_cluster() as u64
}
pub fn decode(b: &[u8; BOOT_SECTOR_PARSE_SIZE]) -> crate::Result<Self> {
let raw = super::layout::Boot::decode(b)
.map_err(|e| crate::Error::InvalidImage(format!("exfat: {e}")))?;
Ok(Self {
partition_offset: raw.partition_offset,
volume_length: raw.volume_length,
fat_offset: raw.fat_offset,
fat_length: raw.fat_length,
cluster_heap_offset: raw.cluster_heap_offset,
cluster_count: raw.cluster_count,
first_cluster_of_root_directory: raw.first_cluster_of_root_directory,
volume_serial_number: raw.volume_serial_number,
fs_revision_major: raw.fs_revision_major,
fs_revision_minor: raw.fs_revision_minor,
volume_flags: raw.volume_flags,
bytes_per_sector_shift: raw.bytes_per_sector_shift,
sectors_per_cluster_shift: raw.sectors_per_cluster_shift,
number_of_fats: raw.number_of_fats,
drive_select: raw.drive_select,
percent_in_use: raw.percent_in_use,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_boot_sector() -> [u8; BOOT_SECTOR_PARSE_SIZE] {
let mut b = [0u8; BOOT_SECTOR_PARSE_SIZE];
b[0..3].copy_from_slice(&[0xEB, 0x76, 0x90]);
b[3..11].copy_from_slice(b"EXFAT ");
b[64..72].copy_from_slice(&0u64.to_le_bytes()); b[72..80].copy_from_slice(&131072u64.to_le_bytes()); b[80..84].copy_from_slice(&2048u32.to_le_bytes()); b[84..88].copy_from_slice(&128u32.to_le_bytes()); b[88..92].copy_from_slice(&2304u32.to_le_bytes()); b[92..96].copy_from_slice(&16_096u32.to_le_bytes()); b[96..100].copy_from_slice(&5u32.to_le_bytes()); b[100..104].copy_from_slice(&0xCAFE_F00Du32.to_le_bytes()); b[104..106].copy_from_slice(&0x0100u16.to_le_bytes()); b[106..108].copy_from_slice(&0u16.to_le_bytes()); b[108] = 9; b[109] = 3; b[110] = 1; b[111] = 0x80; b[112] = 0; b[510] = 0x55;
b[511] = 0xAA;
b
}
#[test]
fn rejects_cluster_count_above_the_spec_maximum() {
let mut b = make_boot_sector();
b[92..96].copy_from_slice(&MAX_CLUSTER_COUNT.to_le_bytes());
assert_eq!(
BootSector::decode(&b).unwrap().cluster_count,
MAX_CLUSTER_COUNT
);
for over in [MAX_CLUSTER_COUNT + 1, u32::MAX - 1, u32::MAX] {
b[92..96].copy_from_slice(&over.to_le_bytes());
assert!(
BootSector::decode(&b).is_err(),
"ClusterCount {over} should be rejected"
);
}
}
#[test]
fn decode_handcrafted() {
let b = make_boot_sector();
let bs = BootSector::decode(&b).unwrap();
assert_eq!(bs.bytes_per_sector(), 512);
assert_eq!(bs.sectors_per_cluster(), 8);
assert_eq!(bs.bytes_per_cluster(), 4096);
assert_eq!(bs.fat_offset, 2048);
assert_eq!(bs.fat_length, 128);
assert_eq!(bs.cluster_heap_offset, 2304);
assert_eq!(bs.cluster_count, 16_096);
assert_eq!(bs.first_cluster_of_root_directory, 5);
assert_eq!(bs.volume_serial_number, 0xCAFE_F00D);
assert_eq!(bs.fs_revision_major, 1);
assert_eq!(bs.fs_revision_minor, 0);
assert_eq!(bs.number_of_fats, 1);
assert_eq!(bs.cluster_byte_offset(2), 2304 * 512);
assert_eq!(bs.cluster_byte_offset(3), 2304 * 512 + 4096);
}
#[test]
fn rejects_missing_magic() {
let mut b = make_boot_sector();
b[3] = b'X';
assert!(BootSector::decode(&b).is_err());
}
#[test]
fn rejects_bad_signature() {
let mut b = make_boot_sector();
b[510] = 0;
assert!(BootSector::decode(&b).is_err());
}
#[test]
fn rejects_nonzero_must_be_zero() {
let mut b = make_boot_sector();
b[20] = 0xFF;
assert!(BootSector::decode(&b).is_err());
}
#[test]
fn rejects_invalid_bps_shift() {
let mut b = make_boot_sector();
b[108] = 13; assert!(BootSector::decode(&b).is_err());
}
}