use uuid::Uuid;
use crate::Result;
use crate::block::{BlockDevice, SlicedBackend};
pub mod gpt;
pub mod mbr;
pub use gpt::Gpt;
pub use mbr::Mbr;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Partition {
pub start_lba: u64,
pub size_lba: u64,
pub kind: PartitionKind,
pub uuid: Option<Uuid>,
pub name: Option<String>,
pub bootable: bool,
pub attributes: u64,
}
impl Partition {
pub fn new(start_lba: u64, size_lba: u64, kind: PartitionKind) -> Self {
Self {
start_lba,
size_lba,
kind,
uuid: None,
name: None,
bootable: false,
attributes: 0,
}
}
pub fn end_lba(&self) -> u64 {
self.start_lba + self.size_lba - 1
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PartitionKind {
Empty,
LinuxFilesystem,
LinuxSwap,
EfiSystem,
BiosBoot,
Fat32,
MicrosoftBasicData,
Mbr(u8),
Gpt(Uuid),
}
impl PartitionKind {
pub fn as_mbr_byte(&self) -> u8 {
match self {
PartitionKind::Empty => 0x00,
PartitionKind::LinuxFilesystem => 0x83,
PartitionKind::LinuxSwap => 0x82,
PartitionKind::EfiSystem => 0xEF,
PartitionKind::Fat32 => 0x0C,
PartitionKind::MicrosoftBasicData => 0x07,
PartitionKind::Mbr(b) => *b,
PartitionKind::BiosBoot => 0x00,
PartitionKind::Gpt(_) => 0x00,
}
}
pub fn from_mbr_byte(b: u8) -> Self {
match b {
0x00 => PartitionKind::Empty,
0x83 => PartitionKind::LinuxFilesystem,
0x82 => PartitionKind::LinuxSwap,
0xEF => PartitionKind::EfiSystem,
0x0C => PartitionKind::Fat32,
0x07 => PartitionKind::MicrosoftBasicData,
other => PartitionKind::Mbr(other),
}
}
pub fn as_gpt_uuid(&self) -> Uuid {
match self {
PartitionKind::Empty => Uuid::nil(),
PartitionKind::LinuxFilesystem => uuid_const("0FC63DAF-8483-4772-8E79-3D69D8477DE4"),
PartitionKind::LinuxSwap => uuid_const("0657FD6D-A4AB-43C4-84E5-0933C84B4F4F"),
PartitionKind::EfiSystem => uuid_const("C12A7328-F81F-11D2-BA4B-00A0C93EC93B"),
PartitionKind::BiosBoot => uuid_const("21686148-6449-6E6F-744E-656564454649"),
PartitionKind::Fat32 | PartitionKind::MicrosoftBasicData => {
uuid_const("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7")
}
PartitionKind::Gpt(u) => *u,
PartitionKind::Mbr(_) => Uuid::nil(),
}
}
pub fn from_gpt_uuid(u: Uuid) -> Self {
if u.is_nil() {
return PartitionKind::Empty;
}
match u.as_hyphenated().to_string().to_ascii_uppercase().as_str() {
"0FC63DAF-8483-4772-8E79-3D69D8477DE4" => PartitionKind::LinuxFilesystem,
"0657FD6D-A4AB-43C4-84E5-0933C84B4F4F" => PartitionKind::LinuxSwap,
"C12A7328-F81F-11D2-BA4B-00A0C93EC93B" => PartitionKind::EfiSystem,
"21686148-6449-6E6F-744E-656564454649" => PartitionKind::BiosBoot,
"EBD0A0A2-B9E5-4433-87C0-68B6B72699C7" => PartitionKind::MicrosoftBasicData,
_ => PartitionKind::Gpt(u),
}
}
}
pub trait PartitionTable {
fn write(&self, dev: &mut dyn BlockDevice) -> Result<()>;
fn partitions(&self) -> &[Partition];
}
pub fn slice_partition<'a, B: BlockDevice + ?Sized, P: PartitionTable + ?Sized>(
pt: &P,
dev: &'a mut B,
index: usize,
) -> Result<SlicedBackend<'a, B>> {
let parts = pt.partitions();
let p = parts.get(index).ok_or_else(|| {
crate::Error::InvalidArgument(format!(
"partition index {index} out of range (have {})",
parts.len()
))
})?;
let bs = u64::from(dev.block_size());
SlicedBackend::new(dev, p.start_lba * bs, p.size_lba * bs)
}
fn uuid_const(s: &str) -> Uuid {
Uuid::parse_str(s).expect("hard-coded UUID literal")
}
pub(crate) fn uuid_to_gpt_bytes(u: Uuid) -> [u8; 16] {
let f = u.as_fields();
let mut out = [0u8; 16];
out[0..4].copy_from_slice(&f.0.to_le_bytes());
out[4..6].copy_from_slice(&f.1.to_le_bytes());
out[6..8].copy_from_slice(&f.2.to_le_bytes());
out[8..16].copy_from_slice(f.3);
out
}
pub(crate) fn uuid_from_gpt_bytes(b: &[u8; 16]) -> Uuid {
let d1 = u32::from_le_bytes(b[0..4].try_into().unwrap());
let d2 = u16::from_le_bytes(b[4..6].try_into().unwrap());
let d3 = u16::from_le_bytes(b[6..8].try_into().unwrap());
let mut d4 = [0u8; 8];
d4.copy_from_slice(&b[8..16]);
Uuid::from_fields(d1, d2, d3, &d4)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn uuid_endian_roundtrip() {
let cases = [
"C12A7328-F81F-11D2-BA4B-00A0C93EC93B",
"0FC63DAF-8483-4772-8E79-3D69D8477DE4",
"21686148-6449-6E6F-744E-656564454649",
];
for s in cases {
let u = Uuid::parse_str(s).unwrap();
let bytes = uuid_to_gpt_bytes(u);
let back = uuid_from_gpt_bytes(&bytes);
assert_eq!(u, back, "roundtrip for {s}");
}
}
#[test]
fn efi_uuid_disk_layout_is_correct() {
let u = Uuid::parse_str("C12A7328-F81F-11D2-BA4B-00A0C93EC93B").unwrap();
let bytes = uuid_to_gpt_bytes(u);
let expected = [
0x28, 0x73, 0x2A, 0xC1, 0x1F, 0xF8, 0xD2, 0x11, 0xBA, 0x4B, 0x00, 0xA0, 0xC9, 0x3E,
0xC9, 0x3B,
];
assert_eq!(bytes, expected);
}
#[test]
fn mbr_byte_roundtrip() {
for b in 0u8..=255 {
let k = PartitionKind::from_mbr_byte(b);
assert_eq!(k.as_mbr_byte(), b, "byte {b:#04x} round-trip");
}
}
}