use crate::{
guid, Guid, GuidFromStrError, LbaLe, LbaRangeInclusive, U16Le, U64Le,
};
use core::fmt::{self, Display, Formatter};
use core::num::NonZeroU32;
use core::str::FromStr;
#[cfg(feature = "bytemuck")]
use bytemuck::{Pod, Zeroable};
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)]
#[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))]
#[repr(transparent)]
pub struct GptPartitionType(pub Guid);
#[allow(clippy::doc_markdown)]
impl GptPartitionType {
pub const UNUSED: Self = Self(Guid::ZERO);
pub const EFI_SYSTEM: Self =
Self(guid!("c12a7328-f81f-11d2-ba4b-00a0c93ec93b"));
pub const LEGACY_MBR: Self =
Self(guid!("024dee41-33e7-11d3-9d69-0008c781f39f"));
pub const BASIC_DATA: Self =
Self(guid!("ebd0a0a2-b9e5-4433-87c0-68b6b72699c7"));
pub const CHROME_OS_KERNEL: Self =
Self(guid!("fe3a2a5d-4f32-41a7-b725-accc3285a309"));
pub const CHROME_OS_ROOT_FS: Self =
Self(guid!("3cb8e202-3b7e-47dd-8a3c-7ff2a13cfcec"));
}
impl Display for GptPartitionType {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
if self == &Self::UNUSED {
f.write_str("UNUSED")
} else {
write!(f, "{}", self.0)
}
}
}
impl FromStr for GptPartitionType {
type Err = GuidFromStrError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(s.parse()?))
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)]
#[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))]
#[repr(transparent)]
pub struct GptPartitionAttributes(pub U64Le);
impl GptPartitionAttributes {
pub const REQUIRED_PARTITION_BIT: u8 = 0;
pub const NO_BLOCK_IO_PROTOCOL_BIT: u8 = 1;
pub const LEGACY_BIOS_BOOTABLE_BIT: u8 = 2;
fn get_bit(self, bit: u8) -> bool {
self.0 .0[0] & (1 << bit) != 0
}
fn set_bit(&mut self, bit: u8, set: bool) {
if set {
self.0 .0[0] |= 1 << bit;
} else {
self.0 .0[0] &= !(1 << bit);
}
}
#[must_use]
pub fn required_partition(self) -> bool {
self.get_bit(Self::REQUIRED_PARTITION_BIT)
}
pub fn update_required_partition(&mut self, required: bool) {
self.set_bit(Self::REQUIRED_PARTITION_BIT, required);
}
#[must_use]
pub fn no_block_io_protocol(self) -> bool {
self.get_bit(Self::NO_BLOCK_IO_PROTOCOL_BIT)
}
pub fn update_no_block_io_protocol(&mut self, no_block_io_protocol: bool) {
self.set_bit(Self::NO_BLOCK_IO_PROTOCOL_BIT, no_block_io_protocol);
}
#[must_use]
pub fn legacy_bios_bootable(self) -> bool {
self.get_bit(Self::LEGACY_BIOS_BOOTABLE_BIT)
}
pub fn update_legacy_bios_bootable(&mut self, legacy_bios_bootable: bool) {
self.set_bit(Self::LEGACY_BIOS_BOOTABLE_BIT, legacy_bios_bootable);
}
#[must_use]
pub fn type_specific_attributes(self) -> U16Le {
U16Le([self.0 .0[6], self.0 .0[7]])
}
pub fn update_type_specific_attributes(&mut self, attrs: U16Le) {
self.0 .0[6] = attrs.0[0];
self.0 .0[7] = attrs.0[1];
}
}
impl Display for GptPartitionAttributes {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let mut first = true;
let mut sep = |f: &mut Formatter<'_>| {
if first {
first = false;
} else {
f.write_str(", ")?;
}
Ok(())
};
if self.required_partition() {
sep(f)?;
f.write_str("required_partition (1)")?;
}
if self.no_block_io_protocol() {
sep(f)?;
f.write_str("no_block_io_protocol (2)")?;
}
if self.legacy_bios_bootable() {
sep(f)?;
f.write_str("legacy_bios_bootable (4)")?;
}
let type_specific = self.type_specific_attributes();
if type_specific.to_u16() != 0 {
sep(f)?;
write!(f, "type_specific({:#x})", self.type_specific_attributes())?;
}
if first {
write!(f, "(empty)")?;
}
Ok(())
}
}
struct GptPartitionNameCharIter<'a> {
name: &'a GptPartitionName,
byte_index: usize,
}
impl<'a> Iterator for GptPartitionNameCharIter<'a> {
type Item = char;
fn next(&mut self) -> Option<Self::Item> {
let bytes = &self.name.0;
if self.byte_index >= bytes.len() {
return None;
}
let c = (u16::from(bytes[self.byte_index + 1]) << 8)
| u16::from(bytes[self.byte_index]);
if c == 0 {
self.byte_index = bytes.len();
return None;
}
self.byte_index += 2;
Some(char::try_from(u32::from(c)).unwrap_or('�'))
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub enum GptPartitionNameSetCharError {
Index,
InvalidChar,
}
impl Display for GptPartitionNameSetCharError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Index => f.write_str("invalid character index"),
Self::InvalidChar => {
f.write_str("character cannot be represented in UCS-2")
}
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
#[repr(transparent)]
pub struct GptPartitionName(pub [u8; 72]);
#[cfg(feature = "bytemuck")]
#[allow(unsafe_code)]
unsafe impl Pod for GptPartitionName {}
#[cfg(feature = "bytemuck")]
#[allow(unsafe_code)]
unsafe impl Zeroable for GptPartitionName {}
impl GptPartitionName {
#[must_use]
pub fn is_empty(&self) -> bool {
self.0[0] == 0 && self.0[1] == 0
}
pub fn chars(&self) -> impl Iterator<Item = char> + '_ {
GptPartitionNameCharIter {
name: self,
byte_index: 0,
}
}
pub fn set_char(
&mut self,
index: usize,
c: char,
) -> Result<(), GptPartitionNameSetCharError> {
if index > self.0.len() / 2 {
return Err(GptPartitionNameSetCharError::Index);
}
let c = u16::try_from(u32::from(c))
.map_err(|_| GptPartitionNameSetCharError::InvalidChar)?;
let bytes = c.to_le_bytes();
self.0[index * 2] = bytes[0];
self.0[index * 2 + 1] = bytes[1];
Ok(())
}
}
impl Display for GptPartitionName {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
for c in self.chars() {
write!(f, "{}", c)?;
}
Ok(())
}
}
impl Default for GptPartitionName {
fn default() -> Self {
Self([0; 72])
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub enum GptPartitionNameFromStrError {
Length,
InvalidChar,
}
impl Display for GptPartitionNameFromStrError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Length => f.write_str("input string is too long"),
Self::InvalidChar => f.write_str("input string contains a character that cannot be represented in UCS-2"),
}
}
}
impl From<ucs2::Error> for GptPartitionNameFromStrError {
fn from(err: ucs2::Error) -> Self {
match err {
ucs2::Error::BufferOverflow => Self::Length,
ucs2::Error::MultiByte => Self::InvalidChar,
}
}
}
impl FromStr for GptPartitionName {
type Err = GptPartitionNameFromStrError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut name = Self::default();
let max_index = name.0.len() - 2 - 1;
let mut index = 0;
ucs2::encode_with(s, |c| {
if index >= max_index {
Err(ucs2::Error::BufferOverflow)
} else {
name.0[index] = u8::try_from(c & 0xff).unwrap();
name.0[index + 1] = u8::try_from((c & 0xff00) >> 8).unwrap();
index += 2;
Ok(())
}
})?;
Ok(name)
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)]
#[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))]
#[repr(C)]
pub struct GptPartitionEntry {
pub partition_type_guid: GptPartitionType,
pub unique_partition_guid: Guid,
pub starting_lba: LbaLe,
pub ending_lba: LbaLe,
pub attributes: GptPartitionAttributes,
pub name: GptPartitionName,
}
impl GptPartitionEntry {
#[must_use]
pub fn lba_range(&self) -> Option<LbaRangeInclusive> {
LbaRangeInclusive::new(self.starting_lba.into(), self.ending_lba.into())
}
#[must_use]
pub fn is_used(&self) -> bool {
self.partition_type_guid != GptPartitionType::UNUSED
}
}
impl Display for GptPartitionEntry {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str("GptPartitionEntry { ")?;
write!(f, "partition_type_guid: {}", self.partition_type_guid)?;
write!(f, ", unique_partition_guid: {}", self.unique_partition_guid)?;
write!(f, ", starting_lba: {}", self.starting_lba)?;
write!(f, ", ending_lba: {}", self.ending_lba)?;
write!(f, ", attributes: {}", self.attributes)?;
write!(f, ", name: \"{}\"", self.name)?;
f.write_str(" }")
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct GptPartitionEntrySizeError;
impl Display for GptPartitionEntrySizeError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str("partition entry size must be a power of two greater than or equal to 128")
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
#[repr(transparent)]
pub struct GptPartitionEntrySize(NonZeroU32);
impl GptPartitionEntrySize {
pub const fn new(val: u32) -> Result<Self, GptPartitionEntrySizeError> {
if let Some(nz) = NonZeroU32::new(val) {
if val >= 128 && val.is_power_of_two() {
Ok(Self(nz))
} else {
Err(GptPartitionEntrySizeError)
}
} else {
Err(GptPartitionEntrySizeError)
}
}
#[must_use]
pub const fn to_u32(self) -> u32 {
self.0.get()
}
#[allow(clippy::as_conversions)]
#[must_use]
pub const fn to_u64(self) -> u64 {
self.0.get() as u64
}
#[must_use]
pub fn to_usize(self) -> Option<usize> {
self.0.get().try_into().ok()
}
}
impl Default for GptPartitionEntrySize {
fn default() -> Self {
Self::new(128).unwrap()
}
}
impl Display for GptPartitionEntrySize {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}