use crate::crc32;
use crate::guid::Guid;
use crate::Error;
pub const SIGNATURE: &[u8; 8] = b"EFI PART";
pub const MIN_HEADER_SIZE: u32 = 92;
const HEADER_CRC_OFFSET: usize = 16;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct GptHeader {
pub revision: u32,
pub header_size: u32,
pub header_crc32: u32,
pub header_crc_valid: bool,
pub my_lba: u64,
pub alternate_lba: u64,
pub first_usable_lba: u64,
pub last_usable_lba: u64,
pub disk_guid: Guid,
pub partition_entry_lba: u64,
pub num_partition_entries: u32,
pub partition_entry_size: u32,
pub partition_array_crc32: u32,
}
fn u32_le(b: &[u8], off: usize) -> u32 {
u32::from_le_bytes([b[off], b[off + 1], b[off + 2], b[off + 3]])
}
fn u64_le(b: &[u8], off: usize) -> u64 {
let mut a = [0u8; 8];
a.copy_from_slice(&b[off..off + 8]);
u64::from_le_bytes(a)
}
impl GptHeader {
pub fn parse(sector: &[u8]) -> Result<GptHeader, Error> {
if sector.len() < MIN_HEADER_SIZE as usize {
return Err(Error::TooShort {
need: MIN_HEADER_SIZE as usize,
got: sector.len(),
});
}
if §or[0..8] != SIGNATURE {
return Err(Error::BadSignature);
}
let header_size = u32_le(sector, 12);
let header_crc32 = u32_le(sector, 16);
let header_crc_valid = compute_header_crc(sector, header_size)
.is_some_and(|computed| computed == header_crc32);
let mut disk_guid = [0u8; 16];
disk_guid.copy_from_slice(§or[56..72]);
Ok(GptHeader {
revision: u32_le(sector, 8),
header_size,
header_crc32,
header_crc_valid,
my_lba: u64_le(sector, 24),
alternate_lba: u64_le(sector, 32),
first_usable_lba: u64_le(sector, 40),
last_usable_lba: u64_le(sector, 48),
disk_guid: Guid(disk_guid),
partition_entry_lba: u64_le(sector, 72),
num_partition_entries: u32_le(sector, 80),
partition_entry_size: u32_le(sector, 84),
partition_array_crc32: u32_le(sector, 88),
})
}
}
fn compute_header_crc(sector: &[u8], header_size: u32) -> Option<u32> {
let size = header_size as usize;
if header_size < MIN_HEADER_SIZE || size > sector.len() {
return None;
}
let mut buf = sector[..size].to_vec();
buf[HEADER_CRC_OFFSET..HEADER_CRC_OFFSET + 4].fill(0);
Some(crc32::checksum(&buf))
}