nogpt 0.1.0-pre1

Rust GUID Partition Table (GPT) parsing library with `#[no_std]` support.
Documentation
use crate::{GPTError, Result};

#[derive(Copy, Clone, Debug)]
#[repr(C, packed)]
pub struct MBRPartitionRecord {
    pub boot_indicator: u8,
    pub start_head: u8,
    pub start_sector: u8,
    pub start_track: u8,
    pub os_indicator: u8,
    pub end_head: u8,
    pub end_sector: u8,
    pub end_track: u8,
    pub starting_lba: [u8; 4],
    pub size_in_lba: [u8; 4],
}

impl MBRPartitionRecord {
    /// Check if the partition is to be considered empty.
    ///
    /// A partition is considered empty if the [`Self::os_indicator`] or the [`Self::size_in_lba`]
    /// is 0.
    pub fn is_empty(&self) -> bool {
        self.os_indicator == 0 || self.size_in_lba() == 0
    }

    /// Return the starting logical block as u32.
    pub fn starting_lba(&self) -> u32 {
        u32::from_le_bytes(self.starting_lba)
    }

    /// Return the size in logical blocks as u32.
    pub fn size_in_lba(&self) -> u32 {
        u32::from_le_bytes(self.size_in_lba)
    }

    /// Helper to calculate [`Self::starting_lba`] + [`Self::size_in_lba`] to get the ending lba.
    pub fn ending_lba(&self) -> u32 {
        self.starting_lba() + self.size_in_lba()
    }

    /// Defines a UEFI system partition.
    pub const UEFI_SYSTEM_OS_TYPE: u8 = 0xef;
    /// Is used by a protective MBR to define a fake partition covering the entire disk.
    pub const GPT_PROTECTIVE_OS_TYPE: u8 = 0xee;
}

#[derive(Clone, Debug)]
#[repr(C, packed)]
pub struct MasterBootRecord {
    /// x86 code used on a non-UEFI system to select an MBR partition record and load the first
    /// logical block of that partition . This code shall not be executed on UEFI systems.
    pub bootstrapcode: [u8; 440],
    /// Unique Disk Signature This may be used by the OS to identify the disk from other disks in
    /// the system. This value is always written by the OS and is never written by EFI firmware.
    pub unique_mbr_signature: [u8; 4],
    /// Unknown. This field shall not be used by UEFI firmware.
    pub unknown: [u8; 2],
    /// Array of four legacy MBR partition records [`MBRPartitionRecord`].
    pub partition: [MBRPartitionRecord; 4],
    pub signature: [u8; 2],
}

impl MasterBootRecord {
    /// # Safety
    ///
    /// Behavior is undefined if any of the following conditions are violated:
    ///
    /// * `buf` must be valid for 512 reads.
    ///
    /// * `buf` must be properly aligned.
    ///
    pub unsafe fn from_buf(buf: &[u8]) -> Result<Self> {
        if buf.len() < core::mem::size_of::<Self>() {
            return Err(GPTError::UnexpectedEOF);
        }

        let ret: MasterBootRecord = unsafe { core::ptr::read(buf.as_ptr() as _) };
        Ok(ret)
    }

    /// Return the signature as u16
    pub fn signature(&self) -> u16 {
        u16::from_le_bytes(self.signature)
    }

    pub fn verify(&self, last_lba: Option<u32>) -> Result<()> {
        if self.signature() != 0xaa55 {
            return Err(GPTError::InvalidData);
        }

        self.verify_partitions(last_lba)?;

        Ok(())
    }

    pub fn verify_partitions(&self, last_lba: Option<u32>) -> Result<()> {
        // FIXME: 1..3 does not have to be empty, just to lazy right now
        if !self.partition[1].is_empty()
            || !self.partition[2].is_empty()
            || !self.partition[3].is_empty()
        {
            return Err(GPTError::InvalidMbr);
        }
        /*(0..=2)
        .fold([0u8, 1, 2, 3], |order, left| {
            (left + 1..=3).fold(order, |mut order, right| {
                let swap = |mut this: [u8; 4], left, right| {
                    this.swap(left, right);
                    this
                };
                if self.partition[left].starting_lba() > self.partition[right].starting_lba() {
                    swap(order, left, right)
                } else {
                    order
                }
            })
        })
        .iter()
        .take(3)
        .try_for_each(|index| {
            let index = *index as usize;
            let left = self.partition[index];
            let right = self.partition[index + 1];
            if left.is_empty() || right.is_empty() || left.ending_lba() < right.starting_lba() {
                Ok(())
            } else {
                Err(GptError::OverlappingPartitions)
            }
        });*/

        let last_lba_p = self.partition[0].ending_lba();
        if let Some(last_lba) = last_lba {
            if last_lba_p > last_lba {
                return Err(GPTError::InvalidMbr);
            }
        }

        Ok(())
    }
}

#[cfg(test)]
mod test {
    use crate::mbr::{MBRPartitionRecord, MasterBootRecord};

    #[test]
    fn size() {
        assert_eq!(core::mem::size_of::<MBRPartitionRecord>(), 16);
        assert_eq!(core::mem::size_of::<MasterBootRecord>(), 512)
    }
}