use crate::num::format_u8_slice_lower_hex_le;
use crate::{Lba, U32Le};
use core::fmt::{self, Display, Formatter};
#[cfg(feature = "bytemuck")]
use bytemuck::{Pod, Zeroable};
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct DiskGeometry {
pub heads_per_cylinder: u32,
pub sectors_per_track: u32,
}
impl DiskGeometry {
pub const UNKNOWN: Self = Self {
heads_per_cylinder: 255,
sectors_per_track: 63,
};
}
impl Default for DiskGeometry {
fn default() -> Self {
Self::UNKNOWN
}
}
impl Display for DiskGeometry {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"HPC={}/SPT={}",
self.heads_per_cylinder, self.sectors_per_track
)
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)]
#[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))]
#[repr(C)]
pub struct Chs(pub [u8; 3]);
impl Chs {
#[must_use]
pub fn cylinder(self) -> u16 {
let h = self.0[1] & 0b1100_0000;
let l = self.0[2];
(u16::from(h) << 2) | u16::from(l)
}
#[must_use]
pub fn head(self) -> u8 {
self.0[0]
}
#[must_use]
pub fn sector(self) -> u8 {
self.0[1] & 0b0011_1111
}
#[must_use]
pub fn as_tuple(self) -> (u16, u8, u8) {
(self.cylinder(), self.head(), self.sector())
}
#[allow(clippy::missing_panics_doc)]
#[must_use]
pub fn new(cylinder: u16, head: u8, sector: u8) -> Option<Self> {
if (cylinder & 0b1111_1100_0000_0000) != 0 {
return None;
}
if (sector & 0b1100_0000) != 0 {
return None;
}
Some(Chs([
head,
u8::try_from((cylinder & 0b11_0000_0000) >> 2).unwrap()
| (sector & 0b0011_1111),
u8::try_from(cylinder & 0xff).unwrap(),
]))
}
#[must_use]
pub fn from_lba(lba: Lba, geom: DiskGeometry) -> Option<Self> {
let lba = u32::try_from(lba.0).ok()?;
let cylinder = lba / (geom.heads_per_cylinder * geom.sectors_per_track);
let head = (lba / geom.sectors_per_track) % geom.heads_per_cylinder;
let sector = (lba % geom.sectors_per_track) + 1;
Self::new(
cylinder.try_into().ok()?,
head.try_into().ok()?,
sector.try_into().ok()?,
)
}
}
impl Display for Chs {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"CHS={}/{}/{}",
self.cylinder(),
self.head(),
self.sector()
)
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)]
#[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))]
#[repr(C)]
pub struct MbrPartitionRecord {
pub boot_indicator: u8,
pub start_chs: Chs,
pub os_indicator: u8,
pub end_chs: Chs,
pub starting_lba: U32Le,
pub size_in_lba: U32Le,
}
impl Display for MbrPartitionRecord {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str("MbrPartitionRecord { ")?;
write!(f, "boot_indicator: {:#x}", self.boot_indicator)?;
write!(f, ", start_chs: {}", self.start_chs)?;
write!(f, ", os_indicator: {:#x}", self.os_indicator)?;
write!(f, ", end_chs: {}", self.end_chs)?;
write!(f, ", starting_lba: {}", self.starting_lba)?;
write!(f, ", size_in_lba: {}", self.size_in_lba)?;
f.write_str(" }")
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
#[repr(C)]
pub struct MasterBootRecord {
pub boot_strap_code: [u8; 440],
pub unique_mbr_disk_signature: [u8; 4],
pub unknown: [u8; 2],
pub partitions: [MbrPartitionRecord; 4],
pub signature: [u8; 2],
}
impl Default for MasterBootRecord {
fn default() -> Self {
Self {
boot_strap_code: [0; 440],
unique_mbr_disk_signature: [0; 4],
unknown: [0, 2],
partitions: [MbrPartitionRecord::default(); 4],
signature: [0; 2],
}
}
}
#[cfg(feature = "bytemuck")]
#[allow(unsafe_code)]
unsafe impl Pod for MasterBootRecord {}
#[cfg(feature = "bytemuck")]
#[allow(unsafe_code)]
unsafe impl Zeroable for MasterBootRecord {}
impl MasterBootRecord {
#[must_use]
pub fn is_boot_strap_code_zero(&self) -> bool {
self.boot_strap_code.iter().all(|b| *b == 0)
}
#[must_use]
pub fn protective_mbr(num_blocks: u64) -> Self {
let size_in_lba = u32::try_from(num_blocks).unwrap_or(0xffff_ffff);
Self {
boot_strap_code: [0; 440],
unique_mbr_disk_signature: [0; 4],
unknown: [0; 2],
partitions: [
MbrPartitionRecord {
boot_indicator: 0,
start_chs: Chs([0, 2, 0]),
os_indicator: 0xee,
end_chs: Chs::from_lba(
Lba(num_blocks - 1),
DiskGeometry::UNKNOWN,
)
.unwrap_or(Chs([0xff, 0xff, 0xff])),
starting_lba: U32Le::from_u32(1),
size_in_lba: U32Le::from_u32(size_in_lba - 1),
},
MbrPartitionRecord::default(),
MbrPartitionRecord::default(),
MbrPartitionRecord::default(),
],
signature: [0x55, 0xaa],
}
}
}
impl Display for MasterBootRecord {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str("MasterBootRecord { boot_strap_code: ")?;
if self.is_boot_strap_code_zero() {
write!(f, "[0; {}]", self.boot_strap_code.len())?;
} else {
f.write_str("<non-zero>")?;
}
f.write_str(", unique_mbr_disk_signature: 0x")?;
format_u8_slice_lower_hex_le(f, &self.unique_mbr_disk_signature)?;
f.write_str(", unknown: ")?;
format_u8_slice_lower_hex_le(f, &self.unknown)?;
f.write_str(", partitions: [")?;
for (i, partition) in self.partitions.iter().enumerate() {
if i != 0 {
f.write_str(", ")?;
}
partition.fmt(f)?;
}
f.write_str("], signature: 0x")?;
format_u8_slice_lower_hex_le(f, &self.signature)?;
f.write_str(" }")
}
}