Skip to main content

hadris_part/
error.rs

1//! Error types for partition operations.
2
3use core::fmt::{self, Debug, Display};
4
5/// Errors that can occur during partition table operations.
6#[derive(Debug)]
7pub enum Error {
8    /// An I/O error occurred.
9    Io(hadris_io::Error),
10
11    /// The MBR signature (0x55AA) is invalid.
12    InvalidMbrSignature {
13        /// The actual signature bytes found.
14        found: [u8; 2],
15    },
16
17    /// The GPT signature ("EFI PART") is invalid.
18    InvalidGptSignature {
19        /// The actual signature bytes found.
20        found: [u8; 8],
21    },
22
23    /// The GPT header CRC32 checksum does not match.
24    GptHeaderCrcMismatch {
25        /// The expected CRC32 value (from header).
26        expected: u32,
27        /// The actual CRC32 value (calculated).
28        actual: u32,
29    },
30
31    /// The GPT partition entry array CRC32 checksum does not match.
32    GptEntriesCrcMismatch {
33        /// The expected CRC32 value (from header).
34        expected: u32,
35        /// The actual CRC32 value (calculated).
36        actual: u32,
37    },
38
39    /// The backup GPT header could not be read from its declared LBA.
40    BackupHeaderIo {
41        /// LBA declared by the primary header for the backup header.
42        lba: u64,
43        /// Underlying I/O failure.
44        source: hadris_io::Error,
45    },
46
47    /// The backup GPT header signature is invalid.
48    InvalidBackupGptSignature {
49        /// The actual signature bytes found.
50        found: [u8; 8],
51    },
52
53    /// The backup GPT header CRC32 checksum does not match.
54    BackupGptHeaderCrcMismatch {
55        /// The expected CRC32 value stored in the backup header.
56        expected: u32,
57        /// The calculated CRC32 value.
58        actual: u32,
59    },
60
61    /// Two partitions overlap.
62    PartitionOverlap {
63        /// Index of the first overlapping partition.
64        index1: usize,
65        /// Index of the second overlapping partition.
66        index2: usize,
67        /// Starting LBA of the overlap.
68        overlap_start: u64,
69        /// Ending LBA of the overlap.
70        overlap_end: u64,
71    },
72
73    /// Too many partitions requested.
74    TooManyPartitions {
75        /// Maximum number of partitions allowed.
76        max: usize,
77        /// Number of partitions requested.
78        requested: usize,
79    },
80
81    /// A partition extends beyond the disk boundary.
82    PartitionOutOfBounds {
83        /// Index of the offending partition.
84        index: usize,
85        /// Ending LBA of the partition.
86        partition_end: u64,
87        /// Last usable LBA of the disk.
88        disk_end: u64,
89    },
90
91    /// The partition entry size is invalid.
92    InvalidPartitionEntrySize {
93        /// The invalid size.
94        size: u32,
95    },
96
97    /// A logical block is too small to contain the requested structure.
98    InvalidBlockSize {
99        /// Supplied logical block size in bytes.
100        size: u32,
101        /// Minimum required size in bytes.
102        minimum: u32,
103    },
104
105    /// The backup GPT header does not match the primary.
106    BackupHeaderMismatch,
107
108    /// No protective MBR found on a GPT disk.
109    NoProtectiveMbr,
110
111    /// Invalid hybrid MBR configuration.
112    InvalidHybridMbr {
113        /// Description of the error.
114        reason: &'static str,
115    },
116
117    /// The disk is too small for the requested partitions.
118    DiskTooSmall {
119        /// Required size in sectors.
120        required: u64,
121        /// Available size in sectors.
122        available: u64,
123    },
124
125    /// A required feature is not available.
126    FeatureNotAvailable(&'static str),
127
128    /// A partition is not properly aligned.
129    MisalignedPartition {
130        /// The misaligned LBA.
131        lba: u64,
132        /// The required alignment in sectors.
133        required_alignment: u64,
134    },
135}
136
137impl Display for Error {
138    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139        match self {
140            Self::Io(err) => write!(f, "I/O error: {err}"),
141            Self::InvalidMbrSignature { found } => {
142                write!(
143                    f,
144                    "invalid MBR signature: expected 0x55AA, found 0x{:02X}{:02X}",
145                    found[0], found[1]
146                )
147            }
148            Self::InvalidGptSignature { found } => {
149                write!(
150                    f,
151                    "invalid GPT signature: expected 'EFI PART', found {found:?}"
152                )
153            }
154            Self::GptHeaderCrcMismatch { expected, actual } => {
155                write!(
156                    f,
157                    "GPT header CRC mismatch: expected 0x{expected:08X}, got 0x{actual:08X}"
158                )
159            }
160            Self::GptEntriesCrcMismatch { expected, actual } => {
161                write!(
162                    f,
163                    "GPT entries CRC mismatch: expected 0x{expected:08X}, got 0x{actual:08X}"
164                )
165            }
166            Self::InvalidBlockSize { size, minimum } => {
167                write!(f, "invalid block size {size}; minimum is {minimum} bytes")
168            }
169            Self::BackupHeaderIo { lba, source } => {
170                write!(f, "failed to read backup GPT header at LBA {lba}: {source}")
171            }
172            Self::InvalidBackupGptSignature { found } => {
173                write!(
174                    f,
175                    "invalid backup GPT signature: expected 'EFI PART', found {found:?}"
176                )
177            }
178            Self::BackupGptHeaderCrcMismatch { expected, actual } => {
179                write!(
180                    f,
181                    "backup GPT header CRC mismatch: expected 0x{expected:08X}, got 0x{actual:08X}"
182                )
183            }
184            Self::PartitionOverlap {
185                index1,
186                index2,
187                overlap_start,
188                overlap_end,
189            } => {
190                write!(
191                    f,
192                    "partitions {index1} and {index2} overlap (LBA {overlap_start}-{overlap_end})"
193                )
194            }
195            Self::TooManyPartitions { max, requested } => {
196                write!(
197                    f,
198                    "too many partitions: maximum is {max}, requested {requested}"
199                )
200            }
201            Self::PartitionOutOfBounds {
202                index,
203                partition_end,
204                disk_end,
205            } => {
206                write!(
207                    f,
208                    "partition {index} extends beyond disk (ends at LBA {partition_end}, disk ends at {disk_end})"
209                )
210            }
211            Self::InvalidPartitionEntrySize { size } => {
212                write!(
213                    f,
214                    "invalid partition entry size: {size} (must be 128 * 2^n)"
215                )
216            }
217            Self::BackupHeaderMismatch => {
218                write!(f, "backup GPT header does not match primary")
219            }
220            Self::NoProtectiveMbr => {
221                write!(f, "no protective MBR found on GPT disk")
222            }
223            Self::InvalidHybridMbr { reason } => {
224                write!(f, "invalid hybrid MBR: {reason}")
225            }
226            Self::DiskTooSmall {
227                required,
228                available,
229            } => {
230                write!(
231                    f,
232                    "disk too small: requires {required} sectors, only {available} available"
233                )
234            }
235            Self::FeatureNotAvailable(feature) => {
236                write!(f, "feature not available: {feature}")
237            }
238            Self::MisalignedPartition {
239                lba,
240                required_alignment,
241            } => {
242                write!(
243                    f,
244                    "partition at LBA {lba} is not aligned to {required_alignment} sectors"
245                )
246            }
247        }
248    }
249}
250
251impl<E: hadris_io::IoError> From<hadris_io::Error<E>> for Error {
252    fn from(err: hadris_io::Error<E>) -> Self {
253        Self::Io(err.erase())
254    }
255}
256
257#[cfg(all(feature = "write", any(feature = "sync", feature = "async")))]
258impl Error {
259    /// An on-disk LBA field whose byte offset is not representable.
260    pub(crate) fn lba_offset_overflow() -> Self {
261        Self::Io(hadris_io::Error::new(
262            hadris_io::ErrorKind::InvalidInput,
263            "LBA value overflows byte offset",
264        ))
265    }
266}
267
268#[cfg(feature = "std")]
269impl std::error::Error for Error {
270    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
271        match self {
272            Self::Io(err) => Some(err),
273            Self::BackupHeaderIo { source, .. } => Some(source),
274            _ => None,
275        }
276    }
277}
278
279/// A specialized `Result` type for partition operations.
280pub type Result<T> = core::result::Result<T, Error>;