1#![cfg_attr(not(feature = "std"), no_std)]
2
3#[cfg(all(feature = "std", feature = "no-std"))]
4compile_error!("feature \"std\" and feature \"no-std\" cannot be enabled at the same time");
5
6use arbitrary_int::{u10, u6};
7use arrayref::array_ref;
8#[cfg(feature = "std")]
9use std::io::Read;
10#[cfg(feature = "std")]
11use thiserror::Error;
12#[cfg(feature = "no-std")]
13use thiserror_no_std::Error;
14
15#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
16pub enum AddrScheme {
17 Chs,
18 Lba,
19}
20
21#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
22pub enum PartType {
23 Empty,
24 Fat12 {
25 visible: bool,
26 },
27 Oem,
28 Fat16 {
29 visible: bool,
30 leq32mib: bool,
31 scheme: AddrScheme,
32 },
33 Extended {
34 scheme: AddrScheme,
35 },
36 ExFAT {
38 visible: bool,
39 },
40 Fat32 {
41 visible: bool,
42 scheme: AddrScheme,
43 },
44 WindowsRe,
45 DynamicDisk,
46 Gpfs,
47 LinuxSwap,
48 LinuxNative,
50 IntelRapidStart,
51 LinuxLvm,
52 FreeBsd,
53 OpenBsd,
54 NetBsd,
55 MacOs,
56 Solaris,
57 BeOs,
58 ProtectiveMbr,
59 Efi,
60 LinuxRaid,
61}
62
63impl PartType {
64 pub fn addr_scheme(&self) -> AddrScheme {
66 match self {
67 PartType::Fat32 { scheme, .. }
68 | PartType::Fat16 { scheme, .. }
69 | PartType::Extended { scheme, .. } => *scheme,
70 _ => AddrScheme::Lba,
72 }
73 }
74
75 pub fn btrfs() -> Self {
76 PartType::LinuxNative
77 }
78
79 pub fn ext4() -> Self {
80 PartType::LinuxNative
81 }
82}
83
84impl TryFrom<u8> for PartType {
85 type Error = ();
86 fn try_from(value: u8) -> Result<Self, Self::Error> {
87 use AddrScheme::*;
88 use PartType::*;
89
90 match value {
91 0x00 => Ok(Empty),
92 0x01 => Ok(Fat12 { visible: true }),
93 0x02 => Ok(Fat12 { visible: false }),
94 0x12 => Ok(Oem),
95 0x04 => Ok(Fat16 {
96 visible: true,
97 leq32mib: true,
98 scheme: Chs,
99 }),
100 0x14 => Ok(Fat16 {
101 visible: false,
102 leq32mib: true,
103 scheme: Chs,
104 }),
105 0x05 => Ok(Extended { scheme: Chs }),
106 0x06 => Ok(Fat16 {
107 visible: true,
108 leq32mib: false,
109 scheme: Chs,
110 }),
111 0x07 => Ok(ExFAT { visible: true }),
112 0x17 => Ok(ExFAT { visible: false }),
113 0x0B => Ok(Fat32 {
114 visible: true,
115 scheme: Chs,
116 }),
117 0x1B => Ok(Fat32 {
118 visible: false,
119 scheme: Chs,
120 }),
121 0x0C => Ok(Fat32 {
122 visible: true,
123 scheme: Lba,
124 }),
125 0x1C => Ok(Fat32 {
126 visible: false,
127 scheme: Lba,
128 }),
129 0x0E => Ok(Fat16 {
130 visible: true,
131 leq32mib: false,
132 scheme: Lba,
133 }),
134 0x1E => Ok(Fat16 {
135 visible: false,
136 leq32mib: false,
137 scheme: Lba,
138 }),
139 0x0F => Ok(Extended { scheme: Lba }),
140 0x27 => Ok(WindowsRe),
141 0x42 => Ok(DynamicDisk),
142 0x75 => Ok(Gpfs),
143 0x82 => Ok(LinuxSwap),
144 0x83 => Ok(LinuxNative),
145 0x84 => Ok(IntelRapidStart),
146 0x8E => Ok(LinuxLvm),
147 0xA5 => Ok(FreeBsd),
148 0xA6 => Ok(OpenBsd),
149 0xA9 => Ok(NetBsd),
150 0xAF => Ok(MacOs),
151 0xBF => Ok(Solaris),
152 0xEB => Ok(BeOs),
153 0xEE => Ok(ProtectiveMbr),
154 0xEF => Ok(Efi),
155 0xFD => Ok(LinuxRaid),
156 _ => Err(()),
157 }
158 }
159}
160
161impl TryFrom<PartType> for u8 {
162 type Error = ();
163 fn try_from(part_type: PartType) -> Result<Self, Self::Error> {
164 use AddrScheme::*;
165 use PartType::*;
166
167 match part_type {
168 Empty => Ok(0x00),
169 Fat12 { visible: true } => Ok(0x01),
170 Fat12 { visible: false } => Ok(0x02),
171 Oem => Ok(0x12),
172 Fat16 {
173 visible: true,
174 leq32mib: true,
175 scheme: Chs,
176 } => Ok(0x04),
177 Fat16 {
178 visible: false,
179 leq32mib: true,
180 scheme: Chs,
181 } => Ok(0x14),
182 Extended { scheme: Chs } => Ok(0x05),
183 Fat16 {
184 visible: true,
185 leq32mib: false,
186 scheme: Chs,
187 } => Ok(0x06),
188 ExFAT { visible: true } => Ok(0x07),
189 ExFAT { visible: false } => Ok(0x17),
190 Fat32 {
191 visible: true,
192 scheme: Chs,
193 } => Ok(0x0B),
194 Fat32 {
195 visible: false,
196 scheme: Chs,
197 } => Ok(0x1B),
198 Fat32 {
199 visible: true,
200 scheme: Lba,
201 } => Ok(0x0C),
202 Fat32 {
203 visible: false,
204 scheme: Lba,
205 } => Ok(0x1C),
206 Fat16 {
207 visible: true,
208 leq32mib: false,
209 scheme: Lba,
210 } => Ok(0x0E),
211 Fat16 {
212 visible: false,
213 leq32mib: false,
214 scheme: Lba,
215 } => Ok(0x1E),
216 Extended { scheme: Lba } => Ok(0x0F),
217 WindowsRe => Ok(0x27),
218 DynamicDisk => Ok(0x42),
219 Gpfs => Ok(0x75),
220 LinuxSwap => Ok(0x82),
221 LinuxNative => Ok(0x83),
222 IntelRapidStart => Ok(0x84),
223 LinuxLvm => Ok(0x8E),
224 FreeBsd => Ok(0xA5),
225 OpenBsd => Ok(0xA6),
226 NetBsd => Ok(0xA9),
227 MacOs => Ok(0xAF),
228 Solaris => Ok(0xBF),
229 BeOs => Ok(0xEB),
230 ProtectiveMbr => Ok(0xEE),
231 Efi => Ok(0xEF),
232 LinuxRaid => Ok(0xFD),
233 _ => Err(()),
234 }
235 }
236}
237
238#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
239pub struct ChsEntry {
240 pub raw: [u8; 3],
241}
242
243const SECTOR_MASK: u8 = 0x3F;
245
246impl ChsEntry {
247 pub fn from_chs(cylinder: u10, head: u8, sector: u6) -> Self {
251 ChsEntry {
252 raw: [
253 head,
254 (u8::from(sector) & SECTOR_MASK) | (((u16::from(cylinder) & 0x300) >> 2) as u8),
255 (u16::from(cylinder) & 0xFF) as u8,
256 ],
257 }
258 }
259
260 pub fn cylinder(&self) -> u10 {
261 u10::new((((self.raw[1] & !SECTOR_MASK) as u16) << 2) | (self.raw[2] as u16))
262 }
263
264 pub fn head(&self) -> u8 {
265 self.raw[0]
266 }
267
268 pub fn sector(&self) -> u6 {
269 u6::new(self.raw[1] & SECTOR_MASK)
270 }
271
272 pub fn chs(&self) -> (u10, u8, u6) {
273 (self.cylinder(), self.head(), self.sector())
274 }
275
276 pub fn try_from_lba(lba: u32) -> Result<Self, MbrError> {
277 let heads_per_cylinder: u32 = 4;
280 let sectors_per_track: u32 = 32;
281
282 let cylinder = lba / (heads_per_cylinder * sectors_per_track);
283 let head = (lba / sectors_per_track) % heads_per_cylinder;
284 let sector = lba % sectors_per_track + 1;
285 match (
286 u16::try_from(cylinder)
287 .ok()
288 .and_then(|x| u10::try_new(x).ok()),
289 u8::try_from(head),
290 u8::try_from(sector).ok().and_then(|x| u6::try_new(x).ok()),
291 ) {
292 (Some(c), Ok(h), Some(s)) => Ok(ChsEntry::from_chs(c, h, s)),
293 _ => Err(MbrError::InvalidAddressChs {
294 cylinder,
295 head,
296 sector,
297 }),
298 }
299 }
300
301 pub fn from_lba_truncating(lba: u32) -> Self {
303 ChsEntry::try_from_lba(lba).unwrap_or_else(|e| {
304 if let MbrError::InvalidAddressChs {
305 cylinder,
306 head,
307 sector,
308 } = e
309 {
310 ChsEntry::from_chs(
313 u10::new(cylinder as u16 & 0x3FF),
314 head as u8,
315 u6::new(sector as u8 & SECTOR_MASK),
316 )
317 } else {
318 panic!("Unexpected error: {}", e)
319 }
320 })
321 }
322}
323
324#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
325pub struct PartInfo {
326 bootable: bool,
327 first_sector_chs: ChsEntry,
328 part_type: PartType,
329 last_sector_chs: ChsEntry,
330 start_sector_lba: u32,
331 sector_count_lba: u32,
332}
333
334#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
337pub struct IncompletePartInfo {
338 pub start_lba: Option<u32>,
339 pub end_lba: Option<u32>,
340 pub size_lba: Option<u32>,
341 pub bootable: bool,
342 pub part_type: PartType,
343}
344
345impl TryFrom<IncompletePartInfo> for PartInfo {
346 type Error = MbrError;
347 fn try_from(value: IncompletePartInfo) -> Result<Self, Self::Error> {
348 let (start, size) = match (value.start_lba, value.end_lba, value.size_lba) {
350 (Some(start), Some(end), Some(size)) if 1 == start + size - end && start < end => {
351 Ok((start, size))
352 }
353 (Some(start), Some(end), Some(size)) => {
354 Err(MbrError::InconsistentPartInfo { start, end, size })
355 }
356 (Some(start), Some(end), None) if start < end => Ok((start, 1 + end - start)),
357 (Some(_start), Some(_end), None) => Err(MbrError::BrokenPartitionBounds),
358 (Some(start), None, Some(size)) => Ok((start, size)),
359 (None, Some(end), Some(size)) if size <= end => Ok((1 + end - size, end)),
360 (None, Some(_end), Some(_size)) => Err(MbrError::BrokenPartitionBounds),
361 _ => Err(MbrError::IncompleteInput),
362 }?;
363 PartInfo::try_from_lba(value.bootable, start, size, value.part_type)
364 }
365}
366
367impl PartInfo {
368 pub fn try_from_lba(
369 bootable: bool,
370 start_sector_lba: u32,
371 sector_count_lba: u32,
372 part_type: PartType,
373 ) -> Result<Self, MbrError> {
374 let end_sector_lba = start_sector_lba + sector_count_lba - 1;
375 match part_type.addr_scheme() {
376 AddrScheme::Lba => Ok(Self {
377 bootable,
378 first_sector_chs: ChsEntry::from_lba_truncating(start_sector_lba),
379 last_sector_chs: ChsEntry::from_lba_truncating(end_sector_lba),
380 start_sector_lba,
381 sector_count_lba,
382 part_type,
383 }),
384 AddrScheme::Chs => {
385 let first_sector_chs = ChsEntry::try_from_lba(start_sector_lba)?;
386 let last_sector_chs = ChsEntry::try_from_lba(end_sector_lba)?;
387 Ok(Self {
388 bootable,
389 first_sector_chs,
390 last_sector_chs,
391 start_sector_lba,
392 sector_count_lba,
393 part_type,
394 })
395 }
396 }
397 }
398
399 pub fn try_from_lba_bounds(
400 bootable: bool,
401 start_sector_lba: u32,
402 end_sector_lba: u32,
403 part_type: PartType,
404 ) -> Result<Self, MbrError> {
405 let sector_count_lba = 1 + end_sector_lba - start_sector_lba;
406 match part_type.addr_scheme() {
407 AddrScheme::Lba => Ok(Self {
408 bootable,
409 first_sector_chs: ChsEntry::from_lba_truncating(start_sector_lba),
410 last_sector_chs: ChsEntry::from_lba_truncating(end_sector_lba),
411 start_sector_lba,
412 sector_count_lba,
413 part_type,
414 }),
415 AddrScheme::Chs => {
416 let first_sector_chs = ChsEntry::try_from_lba(start_sector_lba)?;
417 let last_sector_chs = ChsEntry::try_from_lba(end_sector_lba)?;
418 Ok(Self {
419 bootable,
420 first_sector_chs,
421 last_sector_chs,
422 start_sector_lba,
423 sector_count_lba,
424 part_type,
425 })
426 }
427 }
428 }
429
430 pub fn is_zeroed(&self) -> bool {
431 !self.bootable
432 && self.first_sector_chs.raw == [0, 0, 0]
433 && self.part_type == PartType::Empty
434 && self.last_sector_chs.raw == [0, 0, 0]
435 && self.start_sector_lba == 0
436 && self.sector_count_lba == 0
437 }
438
439 pub fn sector_count_lba(&self) -> u32 {
440 self.sector_count_lba
441 }
442
443 pub fn start_sector_lba(&self) -> u32 {
444 self.start_sector_lba
445 }
446
447 pub fn end_sector_lba(&self) -> u32 {
448 self.start_sector_lba + self.sector_count_lba - 1
449 }
450
451 pub fn part_type(&self) -> &PartType {
452 &self.part_type
453 }
454
455 pub fn bootable(&self) -> bool {
456 self.bootable
457 }
458
459 pub fn first_sector_chs(&self) -> ChsEntry {
460 self.first_sector_chs
461 }
462
463 pub fn last_sector_chs(&self) -> ChsEntry {
464 self.last_sector_chs
465 }
466}
467
468#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Error)]
469pub enum PartInfoErr {
470 #[error("the `bootable` value {0:#X} of the partition is invalid. Set to 0x80 for bootable and 0x00 for non-bootable.")]
471 UnclearBootable(u8),
472 #[error("the partition type is unknown.")]
473 UnknownPartType(u8),
474 #[error("the partition type is {0:?} is not valid for MBR.")]
475 NonMbrPartType(PartType),
476}
477
478impl TryFrom<&[u8; 16]> for PartInfo {
479 type Error = PartInfoErr;
480 fn try_from(buf: &[u8; 16]) -> Result<Self, Self::Error> {
481 let bootable = match buf[0] {
482 0x80 => Ok(true),
483 0x00 => Ok(false),
484 x => Err(PartInfoErr::UnclearBootable(x)),
485 }?;
486 let part_type =
487 PartType::try_from(buf[4]).map_err(|_| PartInfoErr::UnknownPartType(buf[4]))?;
488
489 Ok(PartInfo {
490 bootable,
491 part_type,
492 first_sector_chs: ChsEntry {
493 raw: *array_ref![buf, 1, 3],
494 },
495 last_sector_chs: ChsEntry {
496 raw: *array_ref![buf, 5, 3],
497 },
498 start_sector_lba: u32::from_le_bytes(*array_ref![buf, 8, 4]),
499 sector_count_lba: u32::from_le_bytes(*array_ref![buf, 12, 4]),
500 })
501 }
502}
503
504impl TryFrom<PartInfo> for [u8; 16] {
505 type Error = PartInfoErr;
506 fn try_from(part: PartInfo) -> Result<Self, Self::Error> {
507 let mut buf = [0; 16];
508 buf[0] = if part.bootable { 0x80 } else { 0x00 };
509 buf[4] = u8::try_from(part.part_type)
510 .map_err(|_| PartInfoErr::NonMbrPartType(part.part_type))?;
511 buf[1..4].copy_from_slice(&part.first_sector_chs.raw);
512 buf[5..8].copy_from_slice(&part.last_sector_chs.raw);
513 buf[8..12].copy_from_slice(&part.start_sector_lba.to_le_bytes());
514 buf[12..16].copy_from_slice(&part.sector_count_lba.to_le_bytes());
515 Ok(buf)
516 }
517}
518
519#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
520pub struct MbrPartTable {
521 pub entries: [Option<PartInfo>; 4],
522}
523
524impl From<&[u8; 64]> for MbrPartTable {
525 fn from(buf: &[u8; 64]) -> Self {
526 let from_slice = |arr| match PartInfo::try_from(arr).ok() {
527 Some(p) if p.is_zeroed() => None,
529 x => x,
530 };
531 MbrPartTable {
532 entries: [
533 from_slice(array_ref![buf, 0, 16]),
534 from_slice(array_ref![buf, 16, 16]),
535 from_slice(array_ref![buf, 32, 16]),
536 from_slice(array_ref![buf, 48, 16]),
537 ],
538 }
539 }
540}
541
542impl TryFrom<MbrPartTable> for [u8; 64] {
543 type Error = PartInfoErr;
544 fn try_from(tbl: MbrPartTable) -> Result<Self, Self::Error> {
545 let mut buf = [0; 64];
546 buf[0..16].copy_from_slice(&match tbl.entries[0] {
547 Some(e) => <[u8; 16]>::try_from(e)?,
548 _ => Default::default(),
549 });
550 buf[16..32].copy_from_slice(&match tbl.entries[1] {
551 Some(e) => <[u8; 16]>::try_from(e)?,
552 _ => Default::default(),
553 });
554 buf[32..48].copy_from_slice(&match tbl.entries[2] {
555 Some(e) => <[u8; 16]>::try_from(e)?,
556 _ => Default::default(),
557 });
558 buf[48..64].copy_from_slice(&match tbl.entries[3] {
559 Some(e) => <[u8; 16]>::try_from(e)?,
560 _ => Default::default(),
561 });
562 Ok(buf)
563 }
564}
565
566#[cfg(feature = "std")]
567impl MbrPartTable {
568 pub fn try_from_reader<B>(mut reader: B) -> Result<Self, MbrError>
569 where
570 B: Read,
571 {
572 let mut buf = [0; 64];
573 match reader.read(&mut buf) {
574 Ok(64) => Ok(Self::from(&buf)),
575 Ok(n_bytes_read) => Err(MbrError::InputTooSmall {
576 input_size: n_bytes_read,
577 error_location: "partition table",
578 }),
579 Err(e) => Err(MbrError::IoError(e.to_string())),
580 }
581 }
582}
583
584impl MbrPartTable {
585 pub fn from_bytes<'a, B>(b: B) -> Self
586 where
587 B: Into<&'a [u8; 64]>,
588 {
589 Self::from(b.into())
590 }
591}
592
593#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
594pub struct Mbr {
595 pub bootloader: [u8; 440],
596 pub drive_signature: [u8; 4],
597 pub partition_table: MbrPartTable,
598 pub bootsector_signature: [u8; 2],
599}
600
601impl Default for Mbr {
602 fn default() -> Self {
604 Mbr {
605 bootloader: [0; 440],
606 drive_signature: [0; 4],
607 partition_table: MbrPartTable { entries: [None; 4] },
608 bootsector_signature: [0x55, 0xAA],
609 }
610 }
611}
612
613impl Mbr {
614 pub fn try_from_bytes<'a, B>(b: B) -> Result<Self, MbrError>
615 where
616 B: Into<&'a [u8; 512]>,
617 {
618 let buf = b.into();
619 let bootloader = *array_ref![buf, 0, 440];
620 let drive_signature = *array_ref![buf, 440, 4];
621 let zero_buf = *array_ref![buf, 444, 2];
622
623 match &zero_buf {
624 [0, 0] => Ok(()),
625 [0x5A, 0x5A] => Ok(()),
627 _ => Err(MbrError::NullSectorIsNotNull { val: zero_buf }),
628 }?;
629
630 let partition_table = MbrPartTable::from_bytes(array_ref![buf, 446, 64]);
631 let bootsector_signature = *array_ref![buf, 510, 2];
632
633 Ok(Self {
634 bootloader,
635 drive_signature,
636 partition_table,
637 bootsector_signature,
638 })
639 }
640}
641
642#[cfg(feature = "std")]
643impl Mbr {
644 pub fn try_from_reader<B>(mut reader: B) -> Result<Self, MbrError>
645 where
646 B: Read,
647 {
648 let mut bootloader = [0; 440];
649 match reader.read(&mut bootloader) {
650 Ok(440) => Ok(()),
651 Ok(n_bytes_read) => Err(MbrError::InputTooSmall {
652 input_size: n_bytes_read,
653 error_location: "bootloader",
654 }),
655 Err(e) => Err(MbrError::IoError(e.to_string())),
656 }?;
657
658 let mut drive_signature = [0; 4];
659 match reader.read(&mut drive_signature) {
660 Ok(4) => Ok(()),
661 Ok(n_bytes_read) => Err(MbrError::InputTooSmall {
662 input_size: n_bytes_read,
663 error_location: "drive signature",
664 }),
665 Err(e) => Err(MbrError::IoError(e.to_string())),
666 }?;
667
668 let mut zero_buf = [0; 2];
669 match reader.read(&mut zero_buf) {
670 Ok(2) if zero_buf == [0, 0] => Ok(()),
671 Ok(2) if zero_buf == [0x5A, 0x5A] => Ok(()),
673 Ok(2) => Err(MbrError::NullSectorIsNotNull { val: zero_buf }),
674 Ok(n_bytes_read) => Err(MbrError::InputTooSmall {
675 input_size: n_bytes_read,
676 error_location: "null sector",
677 }),
678 Err(e) => Err(MbrError::IoError(e.to_string())),
679 }?;
680
681 let partition_table = MbrPartTable::try_from_reader(&mut reader)?;
682 let mut bootsector_signature = [0; 2];
683
684 match reader.read(&mut bootsector_signature) {
685 Ok(2) => Ok(()),
686 Ok(n_bytes_read) => Err(MbrError::InputTooSmall {
687 input_size: n_bytes_read,
688 error_location: "bootsector signature",
689 }),
690 Err(e) => Err(MbrError::IoError(e.to_string())),
691 }?;
692 Ok(Self {
693 bootloader,
694 drive_signature,
695 partition_table,
696 bootsector_signature,
697 })
698 }
699}
700
701impl TryFrom<&Mbr> for [u8; 512] {
702 type Error = PartInfoErr;
703 fn try_from(mbr: &Mbr) -> Result<Self, Self::Error> {
704 let mut buf = [0; 512];
705 buf[0..440].copy_from_slice(&mbr.bootloader);
706 buf[440..444].copy_from_slice(&mbr.drive_signature);
707 buf[446..510].copy_from_slice(&<[u8; 64]>::try_from(mbr.partition_table)?);
710 buf[510..512].copy_from_slice(&mbr.bootsector_signature);
711 Ok(buf)
712 }
713}
714
715#[derive(Debug, PartialEq, Eq, Hash, Error)]
716pub enum MbrError {
717 #[error("can't create partition type from value {part_id:#X}. If you think this value should be valid please open an issue.")]
718 UnknownPartitionType { part_id: u8 },
719 #[error("an MBR is 512 bytes long, however the input size was only {input_size} bytes long. Couldn't read {error_location}")]
720 InputTooSmall {
721 input_size: usize,
722 error_location: &'static str,
723 },
724 #[error("the null sector in the provided MBR should be identically 0 or contain 0x5A 0x5A, but it contains {:#X} {:#X}", val[0], val[1])]
725 NullSectorIsNotNull { val: [u8; 2] },
726 #[cfg(feature = "std")]
727 #[error("IO error: {0}")]
728 IoError(String),
729 #[error("a valid MBR should contain a bootsector signature of 0x55, 0xAA at addresses 0x01FE, 0x01FF, but it contains {:#X} {:#X}", sig[0], sig[1])]
730 BootloaderSignatureNotSet { sig: [u8; 2] },
731 #[error("too many missing fields in input, can't deduce remaining fields")]
732 IncompleteInput,
733 #[error("a MBR-based CHS partition can't support CHS address ({cylinder}, {head}, {sector}). Consider switching to LBA or making the partition smaller.")]
734 InvalidAddressChs {
735 cylinder: u32,
736 head: u32,
737 sector: u32,
738 },
739 #[error("the given partition information is inconsistent: there can be no partition of size {size}s starting at {start}s and ending at {end}s")]
740 InconsistentPartInfo { start: u32, end: u32, size: u32 },
741 #[error("you attempted to create a partition with a negative size or address")]
742 BrokenPartitionBounds,
743}
744
745#[cfg(feature = "std")]
746#[cfg(test)]
747mod tests {
748 use super::*;
749 use std::{fs::File, io::Write};
750
751 #[test]
752 fn read_mbr() {
753 let raspios_img = File::open("./raspios.img").unwrap();
754 let mbr = Mbr::try_from_reader(raspios_img).unwrap();
755 dbg!(mbr);
756 let partuuid = format!("{:x}", u32::from_le_bytes(mbr.drive_signature));
757 println!("PARTUUID: {}", partuuid);
758 }
759
760 #[test]
761 fn read_and_write() {
762 let raspios_img = File::open("./raspios.img").unwrap();
763 let mut mbr = Mbr::try_from_reader(raspios_img).unwrap();
764 mbr.drive_signature = 0x090b3d33_u32.to_le_bytes();
765 mbr.partition_table.entries[2] = Some(
766 PartInfo::try_from_lba(
767 true,
768 mbr.partition_table.entries[1].unwrap().end_sector_lba(),
769 1024,
770 PartType::btrfs(),
771 )
772 .unwrap(),
773 );
774 let mut out_file = File::create("./out.img").unwrap();
775 let buf = <[u8; 512]>::try_from(&mbr).unwrap();
776 out_file.write_all(&buf).unwrap();
777 }
778}
779
780#[cfg(test)]
781mod tests_no_std {
782 use super::*;
783
784 #[test]
785 fn chs_entry_inv() {
786 let e1 = ChsEntry { raw: [6, 4, 33] };
787 let (c, h, s) = e1.chs();
788 let e2 = ChsEntry::from_chs(c, h, s);
789 assert_eq!(e1, e2);
790
791 let e1 = ChsEntry { raw: [127, 42, 33] };
792 let (c, h, s) = e1.chs();
793 let e2 = ChsEntry::from_chs(c, h, s);
794 assert_eq!(e1, e2);
795
796 let e1 = ChsEntry {
797 raw: [42, 137, 0b11001010],
798 };
799 let (c, h, s) = e1.chs();
800 let e2 = ChsEntry::from_chs(c, h, s);
801 assert_eq!(e1, e2);
802 }
803
804 #[test]
805 fn lba_to_chs() {
806 assert_eq!(
807 ChsEntry::try_from_lba(8192),
808 Ok(ChsEntry::from_chs(u10::new(64), 0, u6::new(1)))
809 );
810 assert_eq!(
811 ChsEntry::try_from_lba(532479),
812 Err(MbrError::InvalidAddressChs {
813 cylinder: 4159,
814 head: 3,
815 sector: 32
816 })
817 );
818 assert_eq!(
819 ChsEntry::try_from_lba(532480),
820 Err(MbrError::InvalidAddressChs {
821 cylinder: 4160,
822 head: 0,
823 sector: 1
824 })
825 );
826 assert_eq!(
827 ChsEntry::try_from_lba(3842047),
828 Err(MbrError::InvalidAddressChs {
829 cylinder: 30015,
830 head: 3,
831 sector: 32
832 })
833 );
834 }
835
836 #[test]
837 fn chs_from_lba() {
838 assert_eq!(
839 PartInfo::try_from_lba(
840 false,
841 8192,
842 524288,
843 PartType::Fat32 {
844 visible: true,
845 scheme: AddrScheme::Lba,
846 }
847 )
848 .unwrap(),
849 PartInfo {
850 bootable: false,
851 first_sector_chs: ChsEntry { raw: [0, 1, 64,] },
852 part_type: PartType::Fat32 {
853 visible: true,
854 scheme: AddrScheme::Lba,
855 },
856 last_sector_chs: ChsEntry { raw: [3, 32, 63,] },
857 start_sector_lba: 8192,
858 sector_count_lba: 524288,
859 }
860 );
861 }
862}