Skip to main content

hadris_part/
gpt_io.rs

1io_transform! {
2
3#[cfg(feature = "read")]
4use super::super::Read;
5#[cfg(any(feature = "read", feature = "write"))]
6use super::super::{Seek, SeekFrom};
7#[cfg(feature = "write")]
8use super::super::Write;
9#[cfg(any(feature = "read", feature = "write"))]
10use crate::gpt::{GptHeader, GptHeaderRaw};
11
12// I/O operations for GptHeader
13
14/// Extension trait for reading/writing [`GptHeader`] from/to I/O sources.
15#[cfg(feature = "read")]
16#[cfg_attr(docsrs, doc(cfg(feature = "read")))]
17pub trait GptHeaderReadExt: Sized {
18    /// Reads a GPT header from a reader.
19    ///
20    /// The reader should be positioned at the start of the header (typically LBA 1).
21    ///
22    /// # Errors
23    ///
24    /// Returns an error if reading fails or if the signature is invalid.
25    async fn read_from<R: Read>(reader: &mut R) -> crate::error::Result<Self>;
26
27    /// Reads a GPT header from a specific LBA.
28    ///
29    /// # Errors
30    ///
31    /// Returns an error if seeking/reading fails or if the signature is invalid.
32    async fn read_from_lba<R: Read + Seek>(
33        reader: &mut R,
34        lba: u64,
35        block_size: u32,
36    ) -> crate::error::Result<Self>;
37}
38
39#[cfg(feature = "read")]
40impl GptHeaderReadExt for GptHeader {
41    async fn read_from<R: Read>(reader: &mut R) -> crate::error::Result<Self> {
42        let mut buf = [0u8; GptHeaderRaw::SIZE];
43        reader
44            .read_exact(&mut buf)
45            .await
46            .map_err(crate::error::Error::from)?;
47        let raw: GptHeaderRaw = bytemuck::cast(buf);
48        let header = Self::from_raw(&raw);
49
50        if !header.has_valid_signature() {
51            return Err(crate::error::Error::InvalidGptSignature {
52                found: header.signature,
53            });
54        }
55
56        Ok(header)
57    }
58
59    async fn read_from_lba<R: Read + Seek>(
60        reader: &mut R,
61        lba: u64,
62        block_size: u32,
63    ) -> crate::error::Result<Self> {
64        // `lba` may be an untrusted on-disk value; saturate instead of
65        // overflowing and let the seek/read fail on the impossible offset.
66        reader
67            .seek(SeekFrom::Start(lba.saturating_mul(u64::from(block_size))))
68            .await
69            .map_err(crate::error::Error::from)?;
70        Self::read_from(reader).await
71    }
72}
73
74/// Extension trait for writing [`GptHeader`] to I/O sinks.
75#[cfg(feature = "write")]
76#[cfg_attr(docsrs, doc(cfg(feature = "write")))]
77pub trait GptHeaderWriteExt {
78    /// Writes this GPT header to a writer.
79    ///
80    /// Only writes the 92-byte header, not padding to sector size.
81    ///
82    /// # Errors
83    ///
84    /// Returns an error if writing fails.
85    async fn write_to<W: Write>(&self, writer: &mut W) -> crate::error::Result<()>;
86
87    /// Writes this GPT header to a specific LBA, padded to block size.
88    ///
89    /// # Errors
90    ///
91    /// Returns an error if seeking/writing fails.
92    async fn write_to_lba<W: Write + Seek>(
93        &self,
94        writer: &mut W,
95        lba: u64,
96        block_size: u32,
97    ) -> crate::error::Result<()>;
98}
99
100#[cfg(feature = "write")]
101impl GptHeaderWriteExt for GptHeader {
102    async fn write_to<W: Write>(&self, writer: &mut W) -> crate::error::Result<()> {
103        let raw = self.to_raw();
104        writer
105            .write_all(bytemuck::bytes_of(&raw))
106            .await
107            .map_err(crate::error::Error::from)
108    }
109
110    async fn write_to_lba<W: Write + Seek>(
111        &self,
112        writer: &mut W,
113        lba: u64,
114        block_size: u32,
115    ) -> crate::error::Result<()> {
116        if block_size < GptHeaderRaw::SIZE as u32 {
117            return Err(crate::error::Error::InvalidBlockSize {
118                size: block_size,
119                minimum: GptHeaderRaw::SIZE as u32,
120            });
121        }
122        let Some(offset) = lba.checked_mul(u64::from(block_size)) else {
123            return Err(crate::error::Error::lba_offset_overflow());
124        };
125        writer
126            .seek(SeekFrom::Start(offset))
127            .await
128            .map_err(crate::error::Error::from)?;
129
130        let raw = self.to_raw();
131        writer
132            .write_all(bytemuck::bytes_of(&raw))
133            .await
134            .map_err(crate::error::Error::from)?;
135
136        // Pad to block size
137        let mut padding_size = block_size as usize - GptHeaderRaw::SIZE;
138        let padding = [0u8; 512];
139        while padding_size > 0 {
140            let chunk = padding_size.min(padding.len());
141            writer
142                .write_all(&padding[..chunk])
143                .await
144                .map_err(crate::error::Error::from)?;
145            padding_size -= chunk;
146        }
147
148        Ok(())
149    }
150}
151
152} // io_transform!