#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct Chs {
pub cylinder: u16,
pub head: u8,
pub sector: u8,
}
impl Chs {
#[must_use]
pub fn from_bytes(b: [u8; 3]) -> Self {
let head = b[0];
let sector = b[1] & 0x3F;
let cylinder = ((b[1] as u16 & 0xC0) << 2) | b[2] as u16;
Chs {
cylinder,
head,
sector,
}
}
#[must_use]
pub fn to_lba(self, heads_per_cylinder: u8, sectors_per_track: u8) -> Option<u32> {
if self.sector == 0 {
return None;
}
let hpc = heads_per_cylinder as u32;
let spt = sectors_per_track as u32;
if hpc == 0 || spt == 0 {
return None;
}
Some(
(self.cylinder as u32) * hpc * spt
+ (self.head as u32) * spt
+ (self.sector as u32 - 1),
)
}
#[must_use]
pub fn is_unused(self) -> bool {
self.cylinder == 0 && self.head == 0 && self.sector == 0
}
#[must_use]
pub fn is_overflow_marker(self) -> bool {
self.cylinder == CHS_MAX_CYLINDER
&& self.sector == STD_SECTORS_PER_TRACK
&& (self.head == 254 || self.head == 255)
}
}
pub const STD_HEADS_PER_CYL: u8 = 255;
pub const STD_SECTORS_PER_TRACK: u8 = 63;
const CHS_MAX_CYLINDER: u16 = 1023;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub enum ChsConsistency {
Consistent,
Unused,
Inconsistent,
}
#[must_use]
fn max_chs_addressable_lba(hpc: u8, spt: u8) -> u64 {
1024u64 * hpc as u64 * spt as u64 - 1
}
#[must_use]
pub fn chs_consistency(chs: Chs, lba: u32, hpc: u8, spt: u8) -> ChsConsistency {
if chs.is_unused() {
return ChsConsistency::Unused;
}
if chs.is_overflow_marker() {
return ChsConsistency::Consistent;
}
if u64::from(lba) > max_chs_addressable_lba(hpc, spt) {
return ChsConsistency::Inconsistent;
}
match chs.to_lba(hpc, spt) {
Some(translated) if translated == lba => ChsConsistency::Consistent,
_ => ChsConsistency::Inconsistent,
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct PartitionEntry {
pub status: u8,
pub chs_first: Chs,
pub type_code: TypeCode,
pub chs_last: Chs,
pub lba_start: u32,
pub lba_count: u32,
}
impl PartitionEntry {
#[must_use]
pub fn from_bytes(b: &[u8; 16]) -> Self {
PartitionEntry {
status: b[0],
chs_first: Chs::from_bytes([b[1], b[2], b[3]]),
type_code: TypeCode(b[4]),
chs_last: Chs::from_bytes([b[5], b[6], b[7]]),
lba_start: u32::from_le_bytes([b[8], b[9], b[10], b[11]]),
lba_count: u32::from_le_bytes([b[12], b[13], b[14], b[15]]),
}
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.type_code.is_empty() && self.lba_start == 0 && self.lba_count == 0
}
#[must_use]
pub fn is_bootable(&self) -> bool {
self.status == 0x80
}
#[must_use]
pub fn lba_end(&self) -> u32 {
self.lba_start
.saturating_add(self.lba_count)
.saturating_sub(1)
}
#[must_use]
pub fn is_extended(&self) -> bool {
self.type_code.is_extended()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct TypeCode(pub u8);
impl TypeCode {
#[must_use]
pub fn name(self) -> &'static str {
forensicnomicon::partition_types::type_name(self.0).unwrap_or("Unknown")
}
#[must_use]
pub fn family(self) -> PartitionFamily {
match self.0 {
0x00 => PartitionFamily::Empty,
0x01 | 0x11 => PartitionFamily::Fat12,
0x04 | 0x06 | 0x0E | 0x14 | 0x16 | 0x1E => PartitionFamily::Fat16,
0x0B | 0x0C | 0x1B | 0x1C => PartitionFamily::Fat32,
0x07 | 0x17 | 0x87 => PartitionFamily::Ntfs,
0x05 | 0x0F | 0x85 => PartitionFamily::ExtendedMbr,
0x82 => PartitionFamily::LinuxSwap,
0x83 => PartitionFamily::Linux,
0x8E => PartitionFamily::LinuxLvm,
0xFD => PartitionFamily::LinuxRaid,
0x27 => PartitionFamily::WindowsRecovery,
0x42 => PartitionFamily::WindowsDynamic,
0xA5 => PartitionFamily::FreeBsd,
0xA6 => PartitionFamily::OpenBsd,
0xA9 => PartitionFamily::NetBsd,
0xAF | 0xAB => PartitionFamily::Hfs,
0xEE => PartitionFamily::GptProtective,
0xEF => PartitionFamily::EfiSystem,
0xFB | 0xFC => PartitionFamily::Vmware,
_ => PartitionFamily::Unknown(self.0),
}
}
#[must_use]
pub fn is_empty(self) -> bool {
self.0 == 0x00
}
#[must_use]
pub fn is_extended(self) -> bool {
matches!(self.0, 0x05 | 0x0F | 0x85)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub enum PartitionFamily {
Empty,
Fat12,
Fat16,
Fat32,
Ntfs,
ExtendedMbr,
LinuxSwap,
Linux,
LinuxLvm,
LinuxRaid,
WindowsRecovery,
WindowsDynamic,
FreeBsd,
OpenBsd,
NetBsd,
Hfs,
GptProtective,
EfiSystem,
Vmware,
Unknown(u8),
}