use super::{Error, FatKind};
pub const MIN_SECTOR_SIZE: usize = 512;
pub const MAX_SECTOR_SIZE: usize = 4096;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Geometry {
pub kind: FatKind,
pub bytes_per_sector: u32,
pub sectors_per_cluster: u32,
pub reserved_sectors: u32,
pub num_fats: u32,
pub root_entry_count: u32,
pub fat_sectors: u32,
pub total_sectors: u32,
pub root_dir_sectors: u32,
pub first_data_sector: u32,
pub cluster_count: u32,
pub root_cluster: u32,
pub fs_info_sector: u32,
pub active_fat: u32,
pub mirrored: bool,
pub part_start: u64,
}
fn u16_at(b: &[u8], at: usize) -> u32 {
u16::from_le_bytes([b[at], b[at + 1]]) as u32
}
fn u32_at(b: &[u8], at: usize) -> u32 {
u32::from_le_bytes([b[at], b[at + 1], b[at + 2], b[at + 3]])
}
impl Geometry {
pub fn parse<E>(sector: &[u8], part_start: u64, device_bytes: u64) -> Result<Self, Error<E>> {
if sector.len() < MIN_SECTOR_SIZE {
return Err(Error::NotFat);
}
if sector[510] != 0x55 || sector[511] != 0xAA {
return Err(Error::NotFat);
}
if sector[0] != 0xEB && sector[0] != 0xE9 {
return Err(Error::NotFat);
}
let bytes_per_sector = u16_at(sector, 11);
if !matches!(bytes_per_sector, 512 | 1024 | 2048 | 4096) {
return Err(Error::NotFat);
}
let sectors_per_cluster = sector[13] as u32;
if sectors_per_cluster == 0
|| !sectors_per_cluster.is_power_of_two()
|| sectors_per_cluster > 128
{
return Err(Error::NotFat);
}
if bytes_per_sector
.checked_mul(sectors_per_cluster)
.is_none_or(|cb| cb > 64 * 1024)
{
return Err(Error::NotFat);
}
let reserved_sectors = u16_at(sector, 14);
if reserved_sectors == 0 {
return Err(Error::NotFat);
}
let num_fats = sector[16] as u32;
if num_fats == 0 || num_fats > 4 {
return Err(Error::NotFat);
}
let root_entry_count = u16_at(sector, 17);
let total_16 = u16_at(sector, 19);
let fat_16 = u16_at(sector, 22);
let total_32 = u32_at(sector, 32);
let fat_32 = u32_at(sector, 36);
let total_sectors = if total_16 != 0 { total_16 } else { total_32 };
let fat_sectors = if fat_16 != 0 { fat_16 } else { fat_32 };
if total_sectors == 0 || fat_sectors == 0 {
return Err(Error::NotFat);
}
let root_dir_sectors = root_entry_count
.checked_mul(32)
.ok_or(Error::NotFat)?
.div_ceil(bytes_per_sector);
let meta_sectors = reserved_sectors
.checked_add(num_fats.checked_mul(fat_sectors).ok_or(Error::NotFat)?)
.and_then(|n| n.checked_add(root_dir_sectors))
.ok_or(Error::NotFat)?;
if meta_sectors >= total_sectors {
return Err(Error::NotFat);
}
let first_data_sector = meta_sectors;
let cluster_count = (total_sectors - meta_sectors) / sectors_per_cluster;
if cluster_count == 0 {
return Err(Error::NotFat);
}
let kind = if cluster_count < 4085 {
FatKind::Fat12
} else if cluster_count < 65525 {
FatKind::Fat16
} else {
FatKind::Fat32
};
if kind == FatKind::Fat32 && cluster_count > 0x0FFF_FFF5 {
return Err(Error::NotFat);
}
let needed_bytes = match kind {
FatKind::Fat12 => (cluster_count as u64 + 2).div_ceil(2) * 3,
FatKind::Fat16 => (cluster_count as u64 + 2) * 2,
FatKind::Fat32 => (cluster_count as u64 + 2) * 4,
};
if (fat_sectors as u64) * (bytes_per_sector as u64) < needed_bytes {
return Err(Error::NotFat);
}
let (root_cluster, fs_info_sector, ext_flags) = if kind == FatKind::Fat32 {
if root_entry_count != 0 {
return Err(Error::NotFat);
}
let root_cluster = u32_at(sector, 44);
if root_cluster < 2 || root_cluster > cluster_count + 1 {
return Err(Error::NotFat);
}
let fs_info = u16_at(sector, 48);
let fs_info = if fs_info == 0 || fs_info >= reserved_sectors {
0
} else {
fs_info
};
(root_cluster, fs_info, u16_at(sector, 40))
} else {
if root_entry_count == 0 {
return Err(Error::NotFat);
}
(0, 0, 0)
};
let mirrored = ext_flags & 0x80 == 0;
let active_fat = if mirrored { 0 } else { ext_flags & 0x0F };
if active_fat >= num_fats {
return Err(Error::NotFat);
}
let volume_bytes = (total_sectors as u64)
.checked_mul(bytes_per_sector as u64)
.ok_or(Error::NotFat)?;
let start_bytes = part_start
.checked_mul(bytes_per_sector as u64)
.ok_or(Error::VolumeExceedsDevice)?;
if start_bytes
.checked_add(volume_bytes)
.is_none_or(|end| end > device_bytes)
{
return Err(Error::VolumeExceedsDevice);
}
Ok(Self {
kind,
bytes_per_sector,
sectors_per_cluster,
reserved_sectors,
num_fats,
root_entry_count,
fat_sectors,
total_sectors,
root_dir_sectors,
first_data_sector,
cluster_count,
root_cluster,
fs_info_sector,
active_fat,
mirrored,
part_start,
})
}
pub fn cluster_bytes(&self) -> u32 {
self.bytes_per_sector * self.sectors_per_cluster
}
pub fn cluster_first_sector(&self, cluster: u32) -> u32 {
self.first_data_sector + (cluster - 2) * self.sectors_per_cluster
}
pub fn is_data_cluster(&self, cluster: u32) -> bool {
cluster >= 2 && cluster <= self.cluster_count + 1
}
pub fn root_dir_first_sector(&self) -> u32 {
self.reserved_sectors + self.num_fats * self.fat_sectors
}
}
pub use crate::device::mbr::parse as parse_mbr;