use core::fmt::{Debug, Display};
use endian_num::Le;
#[repr(transparent)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, bytemuck::Pod, bytemuck::Zeroable)]
pub struct Guid([u8; 16]);
impl Default for Guid {
fn default() -> Self {
Self::UNUSED
}
}
impl Debug for Guid {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Guid({self})")
}
}
impl Display for Guid {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let d1 = u32::from_le_bytes([self.0[0], self.0[1], self.0[2], self.0[3]]);
let d2 = u16::from_le_bytes([self.0[4], self.0[5]]);
let d3 = u16::from_le_bytes([self.0[6], self.0[7]]);
let d4 = &self.0[8..10];
let d5 = &self.0[10..16];
write!(
f,
"{:08x}-{:04x}-{:04x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
d1, d2, d3, d4[0], d4[1], d5[0], d5[1], d5[2], d5[3], d5[4], d5[5]
)
}
}
impl Guid {
pub const fn from_bytes(bytes: [u8; 16]) -> Self {
Self(bytes)
}
pub const fn to_bytes(&self) -> [u8; 16] {
self.0
}
pub const fn is_unused(&self) -> bool {
let mut i = 0;
while i < 16 {
if self.0[i] != 0 {
return false;
}
i += 1;
}
true
}
pub const fn from_str(s: &str) -> Option<Self> {
let bytes = s.as_bytes();
if bytes.len() != 36 {
return None;
}
if bytes[8] != b'-' || bytes[13] != b'-' || bytes[18] != b'-' || bytes[23] != b'-' {
return None;
}
let d1 = match parse_hex_u32(&[
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
]) {
Some(v) => v,
None => return None,
};
let d2 = match parse_hex_u16(&[bytes[9], bytes[10], bytes[11], bytes[12]]) {
Some(v) => v,
None => return None,
};
let d3 = match parse_hex_u16(&[bytes[14], bytes[15], bytes[16], bytes[17]]) {
Some(v) => v,
None => return None,
};
let d4_0 = match parse_hex_u8(&[bytes[19], bytes[20]]) {
Some(v) => v,
None => return None,
};
let d4_1 = match parse_hex_u8(&[bytes[21], bytes[22]]) {
Some(v) => v,
None => return None,
};
let d5_0 = match parse_hex_u8(&[bytes[24], bytes[25]]) {
Some(v) => v,
None => return None,
};
let d5_1 = match parse_hex_u8(&[bytes[26], bytes[27]]) {
Some(v) => v,
None => return None,
};
let d5_2 = match parse_hex_u8(&[bytes[28], bytes[29]]) {
Some(v) => v,
None => return None,
};
let d5_3 = match parse_hex_u8(&[bytes[30], bytes[31]]) {
Some(v) => v,
None => return None,
};
let d5_4 = match parse_hex_u8(&[bytes[32], bytes[33]]) {
Some(v) => v,
None => return None,
};
let d5_5 = match parse_hex_u8(&[bytes[34], bytes[35]]) {
Some(v) => v,
None => return None,
};
let d1_bytes = d1.to_le_bytes();
let d2_bytes = d2.to_le_bytes();
let d3_bytes = d3.to_le_bytes();
Some(Self([
d1_bytes[0],
d1_bytes[1],
d1_bytes[2],
d1_bytes[3],
d2_bytes[0],
d2_bytes[1],
d3_bytes[0],
d3_bytes[1],
d4_0,
d4_1,
d5_0,
d5_1,
d5_2,
d5_3,
d5_4,
d5_5,
]))
}
}
const fn hex_digit(c: u8) -> Option<u8> {
match c {
b'0'..=b'9' => Some(c - b'0'),
b'a'..=b'f' => Some(c - b'a' + 10),
b'A'..=b'F' => Some(c - b'A' + 10),
_ => None,
}
}
const fn parse_hex_u8(chars: &[u8; 2]) -> Option<u8> {
let h = match hex_digit(chars[0]) {
Some(v) => v,
None => return None,
};
let l = match hex_digit(chars[1]) {
Some(v) => v,
None => return None,
};
Some((h << 4) | l)
}
const fn parse_hex_u16(chars: &[u8; 4]) -> Option<u16> {
let b0 = match parse_hex_u8(&[chars[0], chars[1]]) {
Some(v) => v,
None => return None,
};
let b1 = match parse_hex_u8(&[chars[2], chars[3]]) {
Some(v) => v,
None => return None,
};
Some(((b0 as u16) << 8) | (b1 as u16))
}
const fn parse_hex_u32(chars: &[u8; 8]) -> Option<u32> {
let b0 = match parse_hex_u16(&[chars[0], chars[1], chars[2], chars[3]]) {
Some(v) => v,
None => return None,
};
let b1 = match parse_hex_u16(&[chars[4], chars[5], chars[6], chars[7]]) {
Some(v) => v,
None => return None,
};
Some(((b0 as u32) << 16) | (b1 as u32))
}
impl Guid {
pub const UNUSED: Self = Self([0; 16]);
pub const EFI_SYSTEM: Self = Self([
0x28, 0x73, 0x2a, 0xc1, 0x1f, 0xf8, 0xd2, 0x11, 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9,
0x3b,
]);
pub const BIOS_BOOT: Self = Self([
0x48, 0x61, 0x68, 0x21, 0x49, 0x64, 0x6f, 0x6e, 0x74, 0x4e, 0x65, 0x65, 0x64, 0x45, 0x46,
0x49,
]);
pub const MICROSOFT_RESERVED: Self = Self([
0x16, 0xe3, 0xc9, 0xe3, 0x5c, 0x0b, 0xb8, 0x4d, 0x81, 0x7d, 0xf9, 0x2d, 0xf0, 0x02, 0x15,
0xae,
]);
pub const BASIC_DATA: Self = Self([
0xa2, 0xa0, 0xd0, 0xeb, 0xe5, 0xb9, 0x33, 0x44, 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99,
0xc7,
]);
pub const WINDOWS_LDM_METADATA: Self = Self([
0xaa, 0xc8, 0x08, 0x58, 0x8f, 0x7e, 0xe0, 0x42, 0x85, 0xd2, 0xe1, 0xe9, 0x04, 0x34, 0xcf,
0xb3,
]);
pub const WINDOWS_LDM_DATA: Self = Self([
0xa0, 0x60, 0x9b, 0xaf, 0x31, 0x14, 0x62, 0x4f, 0xbc, 0x68, 0x33, 0x11, 0x71, 0x4a, 0x69,
0xad,
]);
pub const WINDOWS_RECOVERY: Self = Self([
0xa4, 0xbb, 0x94, 0xde, 0xd1, 0x06, 0x40, 0x4d, 0xa1, 0x6a, 0xbf, 0xd5, 0x01, 0x79, 0xd6,
0xac,
]);
pub const WINDOWS_STORAGE_SPACES: Self = Self([
0xe7, 0x5c, 0xaf, 0xe7, 0xa0, 0x1a, 0x4d, 0x4d, 0xbe, 0xe7, 0x47, 0x00, 0x00, 0x00, 0x00,
0x00,
]);
pub const LINUX_FILESYSTEM: Self = Self([
0xaf, 0x3d, 0xc6, 0x0f, 0x83, 0x84, 0x72, 0x47, 0x8e, 0x79, 0x3d, 0x69, 0xd8, 0x47, 0x7d,
0xe4,
]);
pub const LINUX_RAID: Self = Self([
0x0f, 0x88, 0x9d, 0xa1, 0xfc, 0x05, 0x3b, 0x4d, 0xa0, 0x06, 0x74, 0x3f, 0x0f, 0x84, 0x91,
0x1e,
]);
pub const LINUX_ROOT_X86: Self = Self([
0x10, 0xd7, 0xda, 0x44, 0x96, 0xe5, 0xe9, 0x4c, 0xb1, 0x6b, 0x00, 0xa5, 0x24, 0x00, 0x00,
0x00,
]);
pub const LINUX_ROOT_X86_64: Self = Self([
0x02, 0xb9, 0x2f, 0x4f, 0xbe, 0x39, 0x3e, 0x4e, 0xb2, 0xc8, 0x23, 0x54, 0x61, 0x6c, 0x02,
0x72,
]);
pub const LINUX_ROOT_ARM: Self = Self([
0xb1, 0x21, 0xb0, 0x69, 0xda, 0xf3, 0x4c, 0x4c, 0x8d, 0x5a, 0x52, 0x57, 0x4c, 0x02, 0x37,
0x00,
]);
pub const LINUX_ROOT_ARM64: Self = Self([
0xd5, 0x7e, 0x44, 0xb7, 0x85, 0x00, 0x4c, 0x49, 0x8a, 0x82, 0x7f, 0x05, 0x39, 0x45, 0x00,
0x00,
]);
pub const LINUX_SWAP: Self = Self([
0x6d, 0xfd, 0x57, 0x06, 0xab, 0xa4, 0xc4, 0x43, 0x84, 0xe5, 0x09, 0x33, 0xc8, 0x4b, 0x4f,
0x4f,
]);
pub const LINUX_LVM: Self = Self([
0x79, 0xd3, 0xd6, 0xe6, 0x07, 0xf5, 0xc2, 0x44, 0xa2, 0x3c, 0x23, 0x8f, 0x2a, 0x3d, 0xf9,
0x28,
]);
pub const LINUX_HOME: Self = Self([
0x33, 0xe7, 0xd1, 0x93, 0xd8, 0x0c, 0x4d, 0x4e, 0x90, 0x4a, 0xac, 0x2d, 0x04, 0x00, 0x00,
0x00,
]);
pub const LINUX_SRV: Self = Self([
0x33, 0xe7, 0xd1, 0x93, 0xd8, 0x0c, 0x4d, 0x4e, 0x90, 0x4a, 0xac, 0x2d, 0x05, 0x00, 0x00,
0x00,
]);
pub const LINUX_LUKS: Self = Self([
0x7f, 0xff, 0xff, 0xca, 0xbc, 0xcd, 0x43, 0x4d, 0xa9, 0x17, 0x87, 0xe1, 0x14, 0x00, 0x00,
0x00,
]);
pub const APPLE_HFS_PLUS: Self = Self([
0x00, 0x53, 0x46, 0x48, 0x00, 0x00, 0xaa, 0x11, 0xaa, 0x11, 0x00, 0x30, 0x65, 0x43, 0xec,
0xac,
]);
pub const APPLE_APFS: Self = Self([
0xef, 0x57, 0x34, 0x7c, 0x00, 0x00, 0xaa, 0x11, 0xaa, 0x11, 0x00, 0x30, 0x65, 0x43, 0xec,
0xac,
]);
pub const APPLE_UFS: Self = Self([
0x00, 0x53, 0x46, 0x55, 0x00, 0x00, 0xaa, 0x11, 0xaa, 0x11, 0x00, 0x30, 0x65, 0x43, 0xec,
0xac,
]);
pub const APPLE_RAID: Self = Self([
0x2d, 0x52, 0x41, 0x49, 0x00, 0x00, 0xaa, 0x11, 0xaa, 0x11, 0x00, 0x30, 0x65, 0x43, 0xec,
0xac,
]);
pub const APPLE_RAID_OFFLINE: Self = Self([
0x2d, 0x52, 0x41, 0x49, 0x4f, 0x46, 0xaa, 0x11, 0xaa, 0x11, 0x00, 0x30, 0x65, 0x43, 0xec,
0xac,
]);
pub const APPLE_BOOT: Self = Self([
0x00, 0x74, 0x6f, 0x6f, 0x42, 0x65, 0xaa, 0x11, 0xaa, 0x11, 0x00, 0x30, 0x65, 0x43, 0xec,
0xac,
]);
pub const APPLE_LABEL: Self = Self([
0x6c, 0x65, 0x62, 0x61, 0x4c, 0x00, 0xaa, 0x11, 0xaa, 0x11, 0x00, 0x30, 0x65, 0x43, 0xec,
0xac,
]);
pub const APPLE_TV_RECOVERY: Self = Self([
0x52, 0x65, 0x63, 0x76, 0x65, 0x72, 0xaa, 0x11, 0xaa, 0x11, 0x00, 0x30, 0x65, 0x43, 0xec,
0xac,
]);
pub const APPLE_CORE_STORAGE: Self = Self([
0xb6, 0x7c, 0x6e, 0x53, 0x56, 0x43, 0xaa, 0x11, 0xaa, 0x11, 0x00, 0x30, 0x65, 0x43, 0xec,
0xac,
]);
pub const FREEBSD_BOOT: Self = Self([
0x00, 0x08, 0x00, 0x83, 0x01, 0x00, 0xb0, 0x11, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x69, 0x00,
0x00,
]);
pub const FREEBSD_DATA: Self = Self([
0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00,
]);
pub const FREEBSD_SWAP: Self = Self([
0x00, 0x08, 0x00, 0x83, 0x01, 0x00, 0xb0, 0x11, 0x00, 0x01, 0xe4, 0x00, 0x00, 0x69, 0x00,
0x00,
]);
pub const FREEBSD_UFS: Self = Self([
0x00, 0x08, 0x00, 0x83, 0x01, 0x00, 0xb0, 0x11, 0x00, 0x02, 0xe4, 0x00, 0x00, 0x69, 0x00,
0x00,
]);
pub const FREEBSD_ZFS: Self = Self([
0x00, 0x08, 0x00, 0x83, 0x01, 0x00, 0xb0, 0x11, 0x00, 0x05, 0xe4, 0x00, 0x00, 0x69, 0x00,
0x00,
]);
pub const FREEBSD_VINUM: Self = Self([
0x00, 0x08, 0x00, 0x83, 0x01, 0x00, 0xb0, 0x11, 0x00, 0x03, 0xe4, 0x00, 0x00, 0x69, 0x00,
0x00,
]);
pub const SOLARIS_BOOT: Self = Self([
0x45, 0xcb, 0x82, 0x6a, 0xd2, 0x1d, 0x11, 0xd3, 0x81, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00,
]);
pub const SOLARIS_ROOT: Self = Self([
0x45, 0xcb, 0x82, 0x6a, 0xd2, 0x1d, 0x11, 0xd3, 0x81, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00,
]);
pub const SOLARIS_SWAP: Self = Self([
0x45, 0xcb, 0x82, 0x6a, 0xd2, 0x1d, 0x11, 0xd3, 0x81, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00,
]);
pub const SOLARIS_BACKUP: Self = Self([
0x45, 0xcb, 0x82, 0x6a, 0xd2, 0x1d, 0x11, 0xd3, 0x81, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00,
]);
pub const SOLARIS_VAR: Self = Self([
0x45, 0xcb, 0x82, 0x6a, 0xd2, 0x1d, 0x11, 0xd3, 0x81, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00,
]);
pub const SOLARIS_HOME: Self = Self([
0x45, 0xcb, 0x82, 0x6a, 0xd2, 0x1d, 0x11, 0xd3, 0x81, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00,
]);
pub const SOLARIS_RESERVED: Self = Self([
0x45, 0xcb, 0x82, 0x6a, 0xd2, 0x1d, 0x11, 0xd3, 0x81, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00,
]);
pub const NETBSD_SWAP: Self = Self([
0x32, 0x8d, 0xf4, 0x49, 0x19, 0xf8, 0xd2, 0x11, 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9,
0x3b,
]);
pub const NETBSD_FFS: Self = Self([
0x32, 0x8d, 0xf4, 0x49, 0x19, 0xf8, 0xd2, 0x11, 0xba, 0x4c, 0x00, 0xa0, 0xc9, 0x3e, 0xc9,
0x3b,
]);
pub const NETBSD_LFS: Self = Self([
0x32, 0x8d, 0xf4, 0x49, 0x19, 0xf8, 0xd2, 0x11, 0xba, 0x4d, 0x00, 0xa0, 0xc9, 0x3e, 0xc9,
0x3b,
]);
pub const NETBSD_RAID: Self = Self([
0x32, 0x8d, 0xf4, 0x49, 0x19, 0xf8, 0xd2, 0x11, 0xba, 0x4f, 0x00, 0xa0, 0xc9, 0x3e, 0xc9,
0x3b,
]);
pub const CHROMEOS_KERNEL: Self = Self([
0x5d, 0x2a, 0x3a, 0xfe, 0x32, 0x4f, 0xa7, 0x41, 0xb7, 0x25, 0xac, 0xcc, 0x32, 0x85, 0xa3,
0x09,
]);
pub const CHROMEOS_ROOTFS: Self = Self([
0x02, 0xe2, 0xb8, 0x3c, 0x7e, 0x3b, 0xdd, 0x47, 0x8a, 0x3c, 0x7f, 0xf2, 0xa1, 0x3c, 0xfc,
0xec,
]);
pub const CHROMEOS_RESERVED: Self = Self([
0x3d, 0x75, 0x0a, 0x2e, 0x48, 0x9e, 0xb0, 0x43, 0x83, 0x37, 0xb1, 0x51, 0x92, 0xcb, 0x1b,
0x5e,
]);
pub const VMWARE_VMFS: Self = Self([
0x10, 0x04, 0x1d, 0xaa, 0xd1, 0xf4, 0x50, 0x4a, 0x98, 0xba, 0xfa, 0x28, 0x80, 0x9d, 0x61,
0x12,
]);
pub const VMWARE_RESERVED: Self = Self([
0x8d, 0x61, 0x00, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00,
]);
}
#[cfg(feature = "rand")]
impl Guid {
pub fn generate_v4() -> Self {
use rand::Rng;
let mut bytes = [0u8; 16];
rand::rng().fill_bytes(&mut bytes);
bytes[7] = (bytes[7] & 0x0F) | 0x40;
bytes[8] = (bytes[8] & 0x3F) | 0x80;
Self(bytes)
}
}
#[repr(transparent)]
#[derive(Clone, Copy, PartialEq, Eq, Default, bytemuck::Pod, bytemuck::Zeroable)]
pub struct GptAttributes(Le<u64>);
impl GptAttributes {
pub const PLATFORM_REQUIRED: u64 = 1 << 0;
pub const EFI_IGNORE: u64 = 1 << 1;
pub const LEGACY_BIOS_BOOTABLE: u64 = 1 << 2;
pub const fn new(value: u64) -> Self {
Self(Le::<u64>::from_ne(value))
}
pub const fn get(&self) -> u64 {
self.0.to_ne()
}
pub fn set(&mut self, value: u64) {
self.0 = Le::<u64>::from_ne(value);
}
pub const fn is_platform_required(&self) -> bool {
(self.0.to_ne() & Self::PLATFORM_REQUIRED) != 0
}
pub const fn is_efi_ignore(&self) -> bool {
(self.0.to_ne() & Self::EFI_IGNORE) != 0
}
pub const fn is_legacy_bios_bootable(&self) -> bool {
(self.0.to_ne() & Self::LEGACY_BIOS_BOOTABLE) != 0
}
}
impl Debug for GptAttributes {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("GptAttributes")
.field("raw", &format_args!("0x{:016X}", self.get()))
.field("platform_required", &self.is_platform_required())
.field("efi_ignore", &self.is_efi_ignore())
.field("legacy_bios_bootable", &self.is_legacy_bios_bootable())
.finish()
}
}
#[repr(C)]
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct GptPartitionName([u16; 36]);
unsafe impl bytemuck::Pod for GptPartitionName {}
unsafe impl bytemuck::Zeroable for GptPartitionName {}
impl Default for GptPartitionName {
fn default() -> Self {
Self([0; 36])
}
}
impl Debug for GptPartitionName {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let len = self.0.iter().position(|&c| c == 0).unwrap_or(36);
let mut s = [0u8; 36];
for (i, &c) in self.0[..len].iter().enumerate() {
s[i] = if c < 128 { c as u8 } else { b'?' };
}
write!(
f,
"GptPartitionName({:?})",
core::str::from_utf8(&s[..len]).unwrap_or("?")
)
}
}
impl GptPartitionName {
pub const fn from_ascii(s: &[u8]) -> Self {
let mut name = [0u16; 36];
let len = if s.len() < 36 { s.len() } else { 36 };
let mut i = 0;
while i < len {
if s[i] < 128 {
name[i] = s[i] as u16;
}
i += 1;
}
Self(name)
}
pub const fn as_u16_slice(&self) -> &[u16; 36] {
&self.0
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct GptHeader {
pub signature: [u8; 8],
pub revision: Le<u32>,
pub header_size: Le<u32>,
pub header_crc32: Le<u32>,
pub reserved: Le<u32>,
pub my_lba: Le<u64>,
pub alternate_lba: Le<u64>,
pub first_usable_lba: Le<u64>,
pub last_usable_lba: Le<u64>,
pub disk_guid: Guid,
pub partition_entry_lba: Le<u64>,
pub num_partition_entries: Le<u32>,
pub size_of_partition_entry: Le<u32>,
pub partition_entry_array_crc32: Le<u32>,
}
impl Default for GptHeader {
fn default() -> Self {
Self {
signature: *b"EFI PART",
revision: Le::<u32>::from_ne(0x00010000),
header_size: Le::<u32>::from_ne(92),
header_crc32: Le::<u32>::from_ne(0),
reserved: Le::<u32>::from_ne(0),
my_lba: Le::<u64>::from_ne(0),
alternate_lba: Le::<u64>::from_ne(0),
first_usable_lba: Le::<u64>::from_ne(0),
last_usable_lba: Le::<u64>::from_ne(0),
disk_guid: Guid::default(),
partition_entry_lba: Le::<u64>::from_ne(0),
num_partition_entries: Le::<u32>::from_ne(0),
size_of_partition_entry: Le::<u32>::from_ne(128),
partition_entry_array_crc32: Le::<u32>::from_ne(0),
}
}
}
impl GptHeader {
pub const SIGNATURE: [u8; 8] = *b"EFI PART";
pub const REVISION_1_0: u32 = 0x00010000;
pub const STANDARD_HEADER_SIZE: u32 = 92;
pub const STANDARD_ENTRY_SIZE: u32 = 128;
pub const fn has_valid_signature(&self) -> bool {
self.signature[0] == b'E'
&& self.signature[1] == b'F'
&& self.signature[2] == b'I'
&& self.signature[3] == b' '
&& self.signature[4] == b'P'
&& self.signature[5] == b'A'
&& self.signature[6] == b'R'
&& self.signature[7] == b'T'
}
#[cfg(feature = "crc")]
pub fn calculate_crc32(&self) -> u32 {
use crc::{CRC_32_ISO_HDLC, Crc};
const HASHER: Crc<u32> = Crc::<u32>::new(&CRC_32_ISO_HDLC);
let mut header = *self;
header.header_crc32 = Le::<u32>::from_ne(0);
let raw = header.to_raw();
HASHER.checksum(bytemuck::bytes_of(&raw))
}
#[cfg(feature = "crc")]
pub fn verify_crc32(&self) -> bool {
self.header_crc32.to_ne() == self.calculate_crc32()
}
#[cfg(feature = "crc")]
pub fn update_crc32(&mut self) {
self.header_crc32 = Le::<u32>::from_ne(0);
self.header_crc32 = Le::<u32>::from_ne(self.calculate_crc32());
}
}
#[repr(C, packed)]
#[derive(Clone, Copy)]
#[cfg(any(
feature = "crc",
all(
any(feature = "read", feature = "write"),
any(feature = "sync", feature = "async")
)
))]
pub(crate) struct GptHeaderRaw {
signature: [u8; 8],
revision: Le<u32>,
header_size: Le<u32>,
header_crc32: Le<u32>,
reserved: Le<u32>,
my_lba: Le<u64>,
alternate_lba: Le<u64>,
first_usable_lba: Le<u64>,
last_usable_lba: Le<u64>,
disk_guid: [u8; 16],
partition_entry_lba: Le<u64>,
num_partition_entries: Le<u32>,
size_of_partition_entry: Le<u32>,
partition_entry_array_crc32: Le<u32>,
}
#[cfg(any(
feature = "crc",
all(
any(feature = "read", feature = "write"),
any(feature = "sync", feature = "async")
)
))]
unsafe impl bytemuck::Pod for GptHeaderRaw {}
#[cfg(any(
feature = "crc",
all(
any(feature = "read", feature = "write"),
any(feature = "sync", feature = "async")
)
))]
unsafe impl bytemuck::Zeroable for GptHeaderRaw {}
#[cfg(any(
feature = "crc",
all(
any(feature = "read", feature = "write"),
any(feature = "sync", feature = "async")
)
))]
impl GptHeaderRaw {
pub(crate) const SIZE: usize = 92;
}
impl GptHeader {
#[cfg(any(
feature = "crc",
all(feature = "write", any(feature = "sync", feature = "async"))
))]
pub(crate) fn to_raw(self) -> GptHeaderRaw {
GptHeaderRaw {
signature: self.signature,
revision: self.revision,
header_size: self.header_size,
header_crc32: self.header_crc32,
reserved: self.reserved,
my_lba: self.my_lba,
alternate_lba: self.alternate_lba,
first_usable_lba: self.first_usable_lba,
last_usable_lba: self.last_usable_lba,
disk_guid: self.disk_guid.to_bytes(),
partition_entry_lba: self.partition_entry_lba,
num_partition_entries: self.num_partition_entries,
size_of_partition_entry: self.size_of_partition_entry,
partition_entry_array_crc32: self.partition_entry_array_crc32,
}
}
#[cfg(all(feature = "read", any(feature = "sync", feature = "async")))]
pub(crate) fn from_raw(raw: &GptHeaderRaw) -> Self {
Self {
signature: raw.signature,
revision: raw.revision,
header_size: raw.header_size,
header_crc32: raw.header_crc32,
reserved: raw.reserved,
my_lba: raw.my_lba,
alternate_lba: raw.alternate_lba,
first_usable_lba: raw.first_usable_lba,
last_usable_lba: raw.last_usable_lba,
disk_guid: Guid::from_bytes(raw.disk_guid),
partition_entry_lba: raw.partition_entry_lba,
num_partition_entries: raw.num_partition_entries,
size_of_partition_entry: raw.size_of_partition_entry,
partition_entry_array_crc32: raw.partition_entry_array_crc32,
}
}
}
#[repr(C)]
#[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
pub struct GptPartitionEntry {
pub type_guid: Guid,
pub unique_guid: Guid,
pub first_lba: Le<u64>,
pub last_lba: Le<u64>,
pub attributes: GptAttributes,
pub name: GptPartitionName,
}
impl Default for GptPartitionEntry {
fn default() -> Self {
Self {
type_guid: Guid::UNUSED,
unique_guid: Guid::UNUSED,
first_lba: Le::<u64>::from_ne(0),
last_lba: Le::<u64>::from_ne(0),
attributes: GptAttributes::default(),
name: GptPartitionName::default(),
}
}
}
impl Debug for GptPartitionEntry {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("GptPartitionEntry")
.field("type_guid", &self.type_guid)
.field("unique_guid", &self.unique_guid)
.field("first_lba", &self.first_lba.to_ne())
.field("last_lba", &self.last_lba.to_ne())
.field("attributes", &self.attributes)
.field("name", &self.name)
.finish()
}
}
impl GptPartitionEntry {
pub const fn new(type_guid: Guid, unique_guid: Guid, first_lba: u64, last_lba: u64) -> Self {
Self {
type_guid,
unique_guid,
first_lba: Le::<u64>::from_ne(first_lba),
last_lba: Le::<u64>::from_ne(last_lba),
attributes: GptAttributes::new(0),
name: GptPartitionName([0; 36]),
}
}
pub const fn is_unused(&self) -> bool {
self.type_guid.is_unused()
}
pub const fn size_sectors(&self) -> u64 {
let first = self.first_lba.to_ne();
let last = self.last_lba.to_ne();
if self.is_unused() || last < first {
0
} else {
(last - first).saturating_add(1)
}
}
pub const fn size_bytes(&self) -> u64 {
self.size_sectors().saturating_mul(512)
}
pub const fn size_bytes_with_sector_size(&self, sector_size: u32) -> u64 {
self.size_sectors().saturating_mul(sector_size as u64)
}
pub fn set_name_ascii(&mut self, name: &[u8]) {
self.name = GptPartitionName::from_ascii(name);
}
}
#[cfg(feature = "crc")]
pub fn calculate_partition_array_crc32(entries: &[GptPartitionEntry]) -> u32 {
use crc::{CRC_32_ISO_HDLC, Crc};
const HASHER: Crc<u32> = Crc::<u32>::new(&CRC_32_ISO_HDLC);
HASHER.checksum(bytemuck::cast_slice(entries))
}
#[cfg(test)]
mod tests {
extern crate alloc;
use super::*;
use crate::PartitionInfoTrait;
use alloc::format;
#[test]
fn test_guid_display() {
let guid = Guid::EFI_SYSTEM;
let s = format!("{guid}");
assert_eq!(s, "c12a7328-f81f-11d2-ba4b-00a0c93ec93b");
}
#[test]
fn test_guid_from_str() {
let guid = Guid::from_str("c12a7328-f81f-11d2-ba4b-00a0c93ec93b").unwrap();
assert_eq!(guid, Guid::EFI_SYSTEM);
}
#[test]
fn test_guid_is_unused() {
assert!(Guid::UNUSED.is_unused());
assert!(!Guid::EFI_SYSTEM.is_unused());
}
#[test]
fn test_gpt_header_size() {
assert!(core::mem::size_of::<GptHeader>() >= 92);
}
#[test]
fn test_gpt_partition_entry_size() {
assert_eq!(core::mem::size_of::<GptPartitionEntry>(), 128);
}
#[test]
fn test_partition_entry_size_sectors() {
let entry = GptPartitionEntry::new(Guid::LINUX_FILESYSTEM, Guid::UNUSED, 2048, 4095);
assert_eq!(entry.size_sectors(), 2048);
assert_eq!(entry.byte_len(512).unwrap(), 2048 * 512);
}
#[test]
fn test_partition_name_ascii() {
let name = GptPartitionName::from_ascii(b"EFI System");
let slice = name.as_u16_slice();
assert_eq!(slice[0], b'E' as u16);
assert_eq!(slice[1], b'F' as u16);
assert_eq!(slice[2], b'I' as u16);
assert_eq!(slice[10], 0); }
}