hadris-part 2.1.0

Partition table support for MBR, GPT, and Hybrid MBR
Documentation
io_transform! {

#[cfg(feature = "read")]
use super::super::Read;
#[cfg(any(feature = "read", feature = "write"))]
use super::super::{Seek, SeekFrom};
#[cfg(feature = "write")]
use super::super::Write;
#[cfg(any(feature = "read", feature = "write"))]
use crate::gpt::{GptHeader, GptHeaderRaw};

// I/O operations for GptHeader

/// Extension trait for reading/writing [`GptHeader`] from/to I/O sources.
#[cfg(feature = "read")]
#[cfg_attr(docsrs, doc(cfg(feature = "read")))]
pub trait GptHeaderReadExt: Sized {
    /// Reads a GPT header from a reader.
    ///
    /// The reader should be positioned at the start of the header (typically LBA 1).
    ///
    /// # Errors
    ///
    /// Returns an error if reading fails or if the signature is invalid.
    async fn read_from<R: Read>(reader: &mut R) -> crate::error::Result<Self>;

    /// Reads a GPT header from a specific LBA.
    ///
    /// # Errors
    ///
    /// Returns an error if seeking/reading fails or if the signature is invalid.
    async fn read_from_lba<R: Read + Seek>(
        reader: &mut R,
        lba: u64,
        block_size: u32,
    ) -> crate::error::Result<Self>;
}

#[cfg(feature = "read")]
impl GptHeaderReadExt for GptHeader {
    async fn read_from<R: Read>(reader: &mut R) -> crate::error::Result<Self> {
        let mut buf = [0u8; GptHeaderRaw::SIZE];
        reader
            .read_exact(&mut buf)
            .await
            .map_err(crate::error::Error::from)?;
        let raw: GptHeaderRaw = bytemuck::cast(buf);
        let header = Self::from_raw(&raw);

        if !header.has_valid_signature() {
            return Err(crate::error::Error::InvalidGptSignature {
                found: header.signature,
            });
        }

        Ok(header)
    }

    async fn read_from_lba<R: Read + Seek>(
        reader: &mut R,
        lba: u64,
        block_size: u32,
    ) -> crate::error::Result<Self> {
        // `lba` may be an untrusted on-disk value; saturate instead of
        // overflowing and let the seek/read fail on the impossible offset.
        reader
            .seek(SeekFrom::Start(lba.saturating_mul(u64::from(block_size))))
            .await
            .map_err(crate::error::Error::from)?;
        Self::read_from(reader).await
    }
}

/// Extension trait for writing [`GptHeader`] to I/O sinks.
#[cfg(feature = "write")]
#[cfg_attr(docsrs, doc(cfg(feature = "write")))]
pub trait GptHeaderWriteExt {
    /// Writes this GPT header to a writer.
    ///
    /// Only writes the 92-byte header, not padding to sector size.
    ///
    /// # Errors
    ///
    /// Returns an error if writing fails.
    async fn write_to<W: Write>(&self, writer: &mut W) -> crate::error::Result<()>;

    /// Writes this GPT header to a specific LBA, padded to block size.
    ///
    /// # Errors
    ///
    /// Returns an error if seeking/writing fails.
    async fn write_to_lba<W: Write + Seek>(
        &self,
        writer: &mut W,
        lba: u64,
        block_size: u32,
    ) -> crate::error::Result<()>;
}

#[cfg(feature = "write")]
impl GptHeaderWriteExt for GptHeader {
    async fn write_to<W: Write>(&self, writer: &mut W) -> crate::error::Result<()> {
        let raw = self.to_raw();
        writer
            .write_all(bytemuck::bytes_of(&raw))
            .await
            .map_err(crate::error::Error::from)
    }

    async fn write_to_lba<W: Write + Seek>(
        &self,
        writer: &mut W,
        lba: u64,
        block_size: u32,
    ) -> crate::error::Result<()> {
        if block_size < GptHeaderRaw::SIZE as u32 {
            return Err(crate::error::Error::InvalidBlockSize {
                size: block_size,
                minimum: GptHeaderRaw::SIZE as u32,
            });
        }
        let Some(offset) = lba.checked_mul(u64::from(block_size)) else {
            return Err(crate::error::Error::lba_offset_overflow());
        };
        writer
            .seek(SeekFrom::Start(offset))
            .await
            .map_err(crate::error::Error::from)?;

        let raw = self.to_raw();
        writer
            .write_all(bytemuck::bytes_of(&raw))
            .await
            .map_err(crate::error::Error::from)?;

        // Pad to block size
        let mut padding_size = block_size as usize - GptHeaderRaw::SIZE;
        let padding = [0u8; 512];
        while padding_size > 0 {
            let chunk = padding_size.min(padding.len());
            writer
                .write_all(&padding[..chunk])
                .await
                .map_err(crate::error::Error::from)?;
            padding_size -= chunk;
        }

        Ok(())
    }
}

} // io_transform!