1#![no_std]
60#![deny(missing_docs)]
61#![allow(async_fn_in_trait)]
62#![allow(clippy::duplicate_mod)]
64#![cfg_attr(docsrs, feature(doc_cfg))]
65
66#[cfg(feature = "alloc")]
67extern crate alloc;
68
69#[cfg(feature = "std")]
70extern crate std;
71
72pub mod error;
77pub mod geometry;
78pub mod gpt;
79pub mod hybrid;
80pub mod mbr;
81pub mod scheme;
82
83#[cfg(feature = "sync")]
88#[path = ""]
89pub mod sync {
90 pub use hadris_io::Result as IoResult;
95 pub use hadris_io::sync::{Parsable, Read, ReadExt, Seek, Writable, Write};
96 pub use hadris_io::{Error, ErrorKind, SeekFrom};
97
98 macro_rules! io_transform {
99 ($($item:tt)*) => { hadris_macros::strip_async!{ $($item)* } };
100 }
101
102 #[allow(unused_macros)]
103 macro_rules! sync_only {
104 ($($item:tt)*) => { $($item)* };
105 }
106
107 #[allow(unused_macros)]
108 macro_rules! async_only {
109 ($($item:tt)*) => {};
110 }
111
112 #[path = "."]
113 mod __inner {
114 pub mod gpt_io;
116 pub mod mbr_io;
118 #[cfg(all(feature = "alloc", feature = "read"))]
119 #[path = "partition_table_io.rs"]
120 pub mod partition_table;
122 pub mod scheme_io;
124 }
125 pub use __inner::*;
126}
127
128#[cfg(feature = "async")]
133#[path = ""]
134pub mod r#async {
135 pub use hadris_io::Result as IoResult;
140 pub use hadris_io::r#async::{Parsable, Read, ReadExt, Seek, Writable, Write};
141 pub use hadris_io::{Error, ErrorKind, SeekFrom};
142
143 macro_rules! io_transform {
144 ($($item:tt)*) => { $($item)* };
145 }
146
147 #[allow(unused_macros)]
148 macro_rules! sync_only {
149 ($($item:tt)*) => {};
150 }
151
152 #[allow(unused_macros)]
153 macro_rules! async_only {
154 ($($item:tt)*) => { $($item)* };
155 }
156
157 #[path = "."]
158 mod __inner {
159 pub mod gpt_io;
161 pub mod mbr_io;
163 #[cfg(all(feature = "alloc", feature = "read"))]
164 #[path = "partition_table_io.rs"]
165 pub mod partition_table;
167 pub mod scheme_io;
169 }
170 pub use __inner::*;
171}
172
173#[cfg(feature = "sync")]
178pub use sync::*;
179
180pub use endian_num::Le;
182pub use error::{Error, Result};
183pub use geometry::{DiskGeometry, validate_partition_alignment};
184pub use gpt::{GptHeader, GptPartitionEntry, Guid};
185pub use mbr::{Chs, MasterBootRecord, MbrPartition, MbrPartitionTable, MbrPartitionType};
186pub use scheme::{PartitionInfo, PartitionSchemeType, PartitionType};
187
188#[cfg(feature = "alloc")]
189#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
190pub use scheme::{GptDisk, PartitionTable};
191
192#[cfg(all(feature = "sync", feature = "read"))]
194#[cfg_attr(docsrs, doc(cfg(feature = "read")))]
195pub use sync::gpt_io::GptHeaderReadExt;
196#[cfg(all(feature = "sync", feature = "write"))]
197#[cfg_attr(docsrs, doc(cfg(feature = "write")))]
198pub use sync::gpt_io::GptHeaderWriteExt;
199#[cfg(all(feature = "sync", feature = "read"))]
200#[cfg_attr(docsrs, doc(cfg(feature = "read")))]
201pub use sync::mbr_io::MasterBootRecordReadExt;
202#[cfg(all(feature = "sync", feature = "write"))]
203#[cfg_attr(docsrs, doc(cfg(feature = "write")))]
204pub use sync::mbr_io::MasterBootRecordWriteExt;
205#[cfg(all(feature = "sync", feature = "alloc", feature = "read"))]
206#[cfg_attr(docsrs, doc(cfg(all(feature = "alloc", feature = "read"))))]
207pub use sync::scheme_io::{GptDiskReadExt, PartitionTableReadExt};
208#[cfg(all(feature = "sync", feature = "alloc", feature = "write"))]
209#[cfg_attr(docsrs, doc(cfg(all(feature = "alloc", feature = "write"))))]
210pub use sync::scheme_io::{GptDiskWriteExt, PartitionTableWriteExt};
211
212pub trait PartitionInfoTrait {
217 fn start_lba(&self) -> u64;
219
220 fn size_sectors(&self) -> u64;
222
223 fn end_lba(&self) -> u64 {
228 let size = self.size_sectors();
229 if size == 0 {
230 self.start_lba()
231 } else {
232 self.start_lba().saturating_add(size - 1)
233 }
234 }
235
236 fn checked_end_lba(&self) -> Option<u64> {
238 let size = self.size_sectors();
239 if size == 0 {
240 Some(self.start_lba())
241 } else {
242 self.start_lba().checked_add(size - 1)
243 }
244 }
245
246 fn byte_len(&self, logical_block_size: u32) -> Option<u64> {
248 self.size_sectors().checked_mul(logical_block_size as u64)
249 }
250}
251
252impl PartitionInfoTrait for MbrPartition {
253 fn start_lba(&self) -> u64 {
254 self.start_lba.to_ne() as u64
255 }
256
257 fn size_sectors(&self) -> u64 {
258 self.sector_count.to_ne() as u64
259 }
260}
261
262impl PartitionInfoTrait for GptPartitionEntry {
263 fn start_lba(&self) -> u64 {
264 self.first_lba.to_ne()
265 }
266
267 fn size_sectors(&self) -> u64 {
268 let first = self.first_lba.to_ne();
269 let last = self.last_lba.to_ne();
270 if self.is_unused() || last < first {
271 0
272 } else {
273 (last - first).saturating_add(1)
276 }
277 }
278
279 fn end_lba(&self) -> u64 {
280 self.last_lba.to_ne()
281 }
282}
283
284impl PartitionInfoTrait for PartitionInfo {
285 fn start_lba(&self) -> u64 {
286 self.start_lba
287 }
288
289 fn size_sectors(&self) -> u64 {
290 self.size_sectors
291 }
292
293 fn end_lba(&self) -> u64 {
294 self.end_lba
295 }
296}
297
298pub trait PartitionTableRead {
300 type Partition: PartitionInfoTrait;
302
303 fn partition_count(&self) -> usize;
305
306 fn partition(&self, index: usize) -> Option<&Self::Partition>;
308}
309
310impl PartitionTableRead for MbrPartitionTable {
311 type Partition = MbrPartition;
312
313 fn partition_count(&self) -> usize {
314 self.count()
315 }
316
317 fn partition(&self, index: usize) -> Option<&Self::Partition> {
318 if index < 4 && !self.partitions[index].is_empty() {
319 Some(&self.partitions[index])
320 } else {
321 None
322 }
323 }
324}
325
326#[cfg(test)]
327mod tests {
328 use super::*;
329
330 #[test]
331 fn test_mbr_partition_trait() {
332 let partition = MbrPartition::new(MbrPartitionType::Fat32, 2048, 204800);
333 assert_eq!(partition.start_lba(), 2048);
334 assert_eq!(partition.size_sectors(), 204800);
335 assert_eq!(partition.end_lba(), 2048 + 204800 - 1);
336 assert_eq!(partition.byte_len(512), Some(204800 * 512));
337 }
338
339 #[test]
340 fn test_gpt_partition_trait() {
341 let partition = GptPartitionEntry::new(Guid::EFI_SYSTEM, Guid::UNUSED, 2048, 206847);
342 assert_eq!(partition.start_lba(), 2048);
343 assert_eq!(partition.size_sectors(), 204800);
344 assert_eq!(partition.end_lba(), 206847);
345 }
346
347 #[test]
348 fn test_mbr_table_read_trait() {
349 let mut table = MbrPartitionTable::new();
350 table[0] = MbrPartition::new(MbrPartitionType::Fat32, 2048, 204800);
351 table[1] = MbrPartition::new(MbrPartitionType::LinuxNative, 206848, 1000000);
352
353 assert_eq!(table.partition_count(), 2);
354 assert!(table.partition(0).is_some());
355 assert!(table.partition(1).is_some());
356 assert!(table.partition(2).is_none());
357 assert!(table.partition(3).is_none());
358 }
359
360 #[test]
361 fn test_struct_sizes() {
362 assert_eq!(core::mem::size_of::<MbrPartition>(), 16);
364 assert_eq!(core::mem::size_of::<MbrPartitionTable>(), 64);
365 assert_eq!(core::mem::size_of::<MasterBootRecord>(), 512);
366 assert!(core::mem::size_of::<GptHeader>() >= 92);
369 assert_eq!(core::mem::size_of::<GptPartitionEntry>(), 128);
370 }
371}