1#![deny(missing_docs)]
72
73use crc::{Crc, CRC_32_ISO_HDLC};
74use std::cmp::Ordering;
75use std::collections::HashSet;
76use std::io;
77use std::io::{Read, Seek, SeekFrom, Write};
78use std::ops::{Index, IndexMut, RangeInclusive};
79use thiserror::Error;
80use wincode::{ReadError, SchemaRead, SchemaWrite, WriteError};
81
82#[cfg(all(target_os = "linux", feature = "nix"))]
84pub mod linux;
85
86const DEFAULT_ALIGN: u64 = 2048;
87const MAX_ALIGN: u64 = 16384;
88
89#[derive(Debug, Error)]
91#[non_exhaustive]
92pub enum Error {
93 #[error("deserialization failed")]
95 Deserialize(#[from] ReadError),
96 #[error("seserialization failed")]
98 Seserialize(#[from] WriteError),
99 #[error("generic I/O error")]
101 Io(#[from] io::Error),
102 #[error("invalid signature")]
105 InvalidSignature,
106 #[error("invalid revision")]
109 InvalidRevision,
110 #[error("invalid header size")]
112 InvalidHeaderSize,
113 #[error("corrupted CRC32 checksum ({0} != {1})")]
116 InvalidChecksum(u32, u32),
117 #[error("corrupted partition entry array CRC32 checksum ({0} != {1})")]
120 InvalidPartitionEntryArrayChecksum(u32, u32),
121 #[error("could not read primary header ({0}) nor backup header ({1})")]
126 ReadError(Box<Error>, Box<Error>),
127 #[error("no space left")]
129 NoSpaceLeft,
130 #[error("conflict of partition GUIDs")]
132 ConflictPartitionGUID,
133 #[error(
139 "invalid partition boundaries: partitions must have positive size, must not overlap, \
140 and must fit within the disk"
141 )]
142 InvalidPartitionBoundaries,
143 #[error("invalid partition number: {0}")]
148 InvalidPartitionNumber(u32),
149 #[error("unused partition")]
151 UnusedPartition,
152 #[error("partition not found")]
154 PartitionNotFound,
155 #[error("an arithmetic operation overflowed")]
157 Overflow,
158}
159
160pub type Result<T> = std::result::Result<T, Error>;
162
163fn encode_into_std_write<T>(src: &T, mut writer: impl Write) -> Result<()>
164where
165 T: SchemaWrite<Src = T>,
166{
167 let buf = wincode::serialize(src)?;
168 Ok(writer.write_all(&buf)?)
169}
170
171fn decode_from_std_read<T>(mut src: impl Read) -> Result<T>
172where
173 T: for<'de> SchemaRead<'de, Dst = T>,
174{
175 let mut buf = vec![0; size_of::<T>()];
176 src.read_exact(&mut buf)?;
177 Ok(wincode::deserialize(&buf)?)
178}
179
180#[derive(Debug, SchemaRead, SchemaWrite, Clone, PartialEq, Eq)]
183pub struct GPTHeader {
184 pub signature: [u8; 8],
186 pub revision: [u8; 4],
188 pub header_size: u32,
190 pub crc32_checksum: u32,
192 pub reserved: [u8; 4],
194 pub primary_lba: u64,
196 pub backup_lba: u64,
198 pub first_usable_lba: u64,
200 pub last_usable_lba: u64,
202 pub disk_guid: [u8; 16],
204 pub partition_entry_lba: u64,
208 pub number_of_partition_entries: u32,
210 pub size_of_partition_entry: u32,
212 pub partition_entry_array_crc32: u32,
214}
215
216impl GPTHeader {
217 pub fn new_from<R>(reader: &mut R, sector_size: u64, disk_guid: [u8; 16]) -> Result<GPTHeader>
219 where
220 R: Read + Seek,
221 {
222 let mut gpt = GPTHeader {
223 signature: [0x45, 0x46, 0x49, 0x20, 0x50, 0x41, 0x52, 0x54],
224 revision: [0x00, 0x00, 0x01, 0x00],
225 header_size: 92,
226 crc32_checksum: 0,
227 reserved: [0; 4],
228 primary_lba: 1,
229 backup_lba: 0,
230 first_usable_lba: 0,
231 last_usable_lba: 0,
232 disk_guid,
233 partition_entry_lba: 2,
234 number_of_partition_entries: 128,
235 size_of_partition_entry: 128,
236 partition_entry_array_crc32: 0,
237 };
238 gpt.update_from(reader, sector_size)?;
239
240 Ok(gpt)
241 }
242
243 pub fn read_from<R>(reader: &mut R) -> Result<GPTHeader>
250 where
251 R: Read + Seek + ?Sized,
252 {
253 let gpt: GPTHeader = decode_from_std_read(reader)?;
254
255 if &gpt.signature != b"EFI PART" {
256 return Err(Error::InvalidSignature);
257 }
258
259 if gpt.revision != [0x00, 0x00, 0x01, 0x00] {
260 return Err(Error::InvalidRevision);
261 }
262
263 if gpt.header_size != 92 {
264 return Err(Error::InvalidHeaderSize);
265 }
266
267 let sum = gpt.generate_crc32_checksum();
268 if gpt.crc32_checksum != sum {
269 return Err(Error::InvalidChecksum(gpt.crc32_checksum, sum));
270 }
271
272 Ok(gpt)
273 }
274
275 pub fn write_into<W>(
278 &mut self,
279 mut writer: &mut W,
280 sector_size: u64,
281 partitions: &[GPTPartitionEntry],
282 ) -> Result<()>
283 where
284 W: Write + Seek + ?Sized,
285 {
286 self.update_partition_entry_array_crc32(partitions);
287 self.update_crc32_checksum();
288
289 writer.seek(SeekFrom::Start(self.primary_lba * sector_size))?;
290 encode_into_std_write(self, &mut writer)?;
291
292 for i in 0..self.number_of_partition_entries {
293 writer.seek(SeekFrom::Start(
294 self.partition_entry_lba * sector_size
295 + u64::from(i) * u64::from(self.size_of_partition_entry),
296 ))?;
297 encode_into_std_write(&partitions[i as usize], &mut writer)?;
298 }
299
300 Ok(())
301 }
302
303 pub fn generate_crc32_checksum(&self) -> u32 {
305 let mut clone = self.clone();
306 clone.crc32_checksum = 0;
307 let data = wincode::serialize(&clone).expect("could not serialize");
308 assert_eq!(data.len() as u32, clone.header_size);
309
310 Crc::<u32>::new(&CRC_32_ISO_HDLC).checksum(&data)
311 }
312
313 pub fn update_crc32_checksum(&mut self) {
315 self.crc32_checksum = self.generate_crc32_checksum();
316 }
317
318 pub fn generate_partition_entry_array_crc32(&self, partitions: &[GPTPartitionEntry]) -> u32 {
320 let mut clone = self.clone();
321 clone.partition_entry_array_crc32 = 0;
322 let crc = Crc::<u32>::new(&CRC_32_ISO_HDLC);
323 let mut digest = crc.digest();
324 let mut wrote = 0;
325 for x in partitions {
326 let data = wincode::serialize(x).expect("could not serialize");
327 digest.update(&data);
328 wrote += data.len();
329 }
330 assert_eq!(
331 wrote as u32,
332 clone.size_of_partition_entry * clone.number_of_partition_entries
333 );
334
335 digest.finalize()
336 }
337
338 pub fn update_partition_entry_array_crc32(&mut self, partitions: &[GPTPartitionEntry]) {
340 self.partition_entry_array_crc32 = self.generate_partition_entry_array_crc32(partitions);
341 }
342
343 pub fn update_from<S>(&mut self, seeker: &mut S, sector_size: u64) -> Result<()>
347 where
348 S: Seek + ?Sized,
349 {
350 let partition_array_size = (u64::from(self.number_of_partition_entries)
351 * u64::from(self.size_of_partition_entry)
352 - 1)
353 / sector_size
354 + 1;
355 let len = seeker.seek(SeekFrom::End(0))? / sector_size;
356 if self.primary_lba == 1 {
357 self.backup_lba = len - 1;
358 } else {
359 self.primary_lba = len - 1;
360 }
361 self.last_usable_lba = len - partition_array_size - 1 - 1;
362 self.first_usable_lba = 2 + partition_array_size;
363 if self.partition_entry_lba != 2 {
368 self.partition_entry_lba = self.last_usable_lba + 1;
369 }
370
371 Ok(())
372 }
373
374 pub fn is_primary(&self) -> bool {
377 self.primary_lba == 1
378 }
379
380 pub fn is_backup(&self) -> bool {
386 !self.is_primary()
387 }
388}
389
390#[derive(Debug, Clone, PartialEq, Eq)]
392pub struct PartitionName {
393 string: String,
394 raw_buf: [u16; 36],
395}
396
397impl PartitionName {
398 pub fn as_str(&self) -> &str {
400 &self.string
401 }
402}
403
404impl std::fmt::Display for PartitionName {
405 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
406 write!(f, "{}", &self.string)
407 }
408}
409
410impl From<&str> for PartitionName {
411 fn from(value: &str) -> PartitionName {
412 let utf16_converted: Vec<_> = value
413 .encode_utf16()
414 .chain([0].into_iter().cycle())
415 .take(36)
416 .collect();
417 let mut raw_buf = [0; 36];
418 raw_buf.copy_from_slice(&utf16_converted);
419 PartitionName {
420 string: value.to_string(),
421 raw_buf,
422 }
423 }
424}
425
426impl<'de> SchemaRead<'de> for PartitionName {
427 type Dst = PartitionName;
428
429 fn read(
430 reader: &mut impl wincode::io::Reader<'de>,
431 dst: &mut std::mem::MaybeUninit<Self::Dst>,
432 ) -> wincode::ReadResult<()> {
433 let n_bytes = 36 * size_of::<u16>();
435 let buf = reader.fill_exact(n_bytes)?;
436 let mut raw_buf = [0; 36];
437 for i in 0..n_bytes / 2 {
438 raw_buf[i] = ((buf[2 * i + 1] as u16) << 8) | buf[2 * i] as u16;
439 }
440
441 reader.consume(n_bytes)?;
442 let zero_pos = raw_buf
443 .iter()
444 .position(|x| *x == 0)
445 .unwrap_or(raw_buf.len());
446
447 let name = PartitionName {
448 string: String::from_utf16_lossy(&raw_buf[..zero_pos]),
449 raw_buf,
450 };
451 dst.write(name);
452 Ok(())
453 }
454}
455
456impl SchemaWrite for PartitionName {
457 type Src = PartitionName;
458
459 fn size_of(_: &Self::Src) -> wincode::WriteResult<usize> {
460 Ok(36 * size_of::<u16>())
461 }
462
463 fn write(writer: &mut impl wincode::io::Writer, src: &Self::Src) -> wincode::WriteResult<()> {
464 for x in src.raw_buf {
466 <u16 as SchemaWrite>::write(writer, &x)?;
467 }
468 Ok(())
469 }
470}
471
472#[derive(Debug, SchemaRead, SchemaWrite, Clone, PartialEq, Eq)]
497pub struct GPTPartitionEntry {
498 pub partition_type_guid: [u8; 16],
500 pub unique_partition_guid: [u8; 16],
502 pub starting_lba: u64,
504 pub ending_lba: u64,
506 pub attribute_bits: u64,
511 pub partition_name: PartitionName,
522}
523
524impl GPTPartitionEntry {
525 pub fn empty() -> GPTPartitionEntry {
543 GPTPartitionEntry {
544 partition_type_guid: [0; 16],
545 unique_partition_guid: [0; 16],
546 starting_lba: 0,
547 ending_lba: 0,
548 attribute_bits: 0,
549 partition_name: "".into(),
550 }
551 }
552
553 pub fn read_from<R>(reader: &mut R) -> Result<GPTPartitionEntry>
555 where
556 R: Read + ?Sized,
557 {
558 decode_from_std_read(reader)
559 }
560
561 pub fn is_unused(&self) -> bool {
563 self.partition_type_guid == [0; 16]
564 }
565
566 pub fn is_used(&self) -> bool {
568 !self.is_unused()
569 }
570
571 pub fn size(&self) -> Result<u64> {
603 if self.ending_lba < self.starting_lba {
604 return Err(Error::InvalidPartitionBoundaries);
605 }
606
607 Ok(self.ending_lba - self.starting_lba + 1)
608 }
609
610 pub fn range(&self) -> Result<RangeInclusive<u64>> {
637 if self.is_unused() {
638 return Err(Error::UnusedPartition);
639 }
640 if self.ending_lba < self.starting_lba {
641 return Err(Error::InvalidPartitionBoundaries);
642 }
643
644 Ok(self.starting_lba..=self.ending_lba)
645 }
646}
647
648#[derive(Debug, Clone, PartialEq, Eq)]
673pub struct GPT {
674 pub sector_size: u64,
679 pub header: GPTHeader,
681 partitions: Vec<GPTPartitionEntry>,
682 pub align: u64,
692}
693
694impl GPT {
695 pub fn new_from<R>(reader: &mut R, sector_size: u64, disk_guid: [u8; 16]) -> Result<GPT>
708 where
709 R: Read + Seek,
710 {
711 let header = GPTHeader::new_from(reader, sector_size, disk_guid)?;
712 let mut partitions = Vec::with_capacity(header.number_of_partition_entries as usize);
713 for _ in 0..header.number_of_partition_entries {
714 partitions.push(GPTPartitionEntry::empty());
715 }
716
717 Ok(GPT {
718 sector_size,
719 header,
720 partitions,
721 align: DEFAULT_ALIGN,
722 })
723 }
724
725 pub fn read_from<R>(mut reader: &mut R, sector_size: u64) -> Result<GPT>
743 where
744 R: Read + Seek + ?Sized,
745 {
746 use self::Error::*;
747
748 reader.seek(SeekFrom::Start(sector_size))?;
749 let header = GPTHeader::read_from(&mut reader).or_else(|primary_err| {
750 let len = reader.seek(SeekFrom::End(0))?;
751 if len < sector_size {
752 return Err(InvalidSignature);
753 }
754
755 reader.seek(SeekFrom::Start((len / sector_size - 1) * sector_size))?;
756
757 GPTHeader::read_from(&mut reader).map_err(|backup_err| {
758 match (primary_err, backup_err) {
759 (InvalidSignature, InvalidSignature) => InvalidSignature,
760 (x, y) => Error::ReadError(Box::new(x), Box::new(y)),
761 }
762 })
763 })?;
764
765 let mut partitions = Vec::with_capacity(header.number_of_partition_entries as usize);
766 for i in 0..header.number_of_partition_entries {
767 reader.seek(SeekFrom::Start(
768 header.partition_entry_lba * sector_size
769 + u64::from(i) * u64::from(header.size_of_partition_entry),
770 ))?;
771 partitions.push(GPTPartitionEntry::read_from(&mut reader)?);
772 }
773
774 let sum = header.generate_partition_entry_array_crc32(&partitions);
775 if header.partition_entry_array_crc32 != sum {
776 return Err(Error::InvalidPartitionEntryArrayChecksum(
777 header.partition_entry_array_crc32,
778 sum,
779 ));
780 }
781
782 let align = GPT::find_alignment(&header, &partitions);
783
784 Ok(GPT {
785 sector_size,
786 header,
787 partitions,
788 align,
789 })
790 }
791
792 pub fn find_from<R>(mut reader: &mut R) -> Result<GPT>
811 where
812 R: Read + Seek + ?Sized,
813 {
814 use self::Error::*;
815
816 Self::read_from(&mut reader, 512).or_else(|err_at_512| match err_at_512 {
817 InvalidSignature => Self::read_from(&mut reader, 4096),
818 err => Err(err),
819 })
820 }
821
822 fn find_alignment(header: &GPTHeader, partitions: &[GPTPartitionEntry]) -> u64 {
823 let lbas = partitions
824 .iter()
825 .filter(|x| x.is_used())
826 .map(|x| x.starting_lba)
827 .collect::<Vec<_>>();
828
829 if lbas.is_empty() {
830 return DEFAULT_ALIGN;
831 }
832
833 if lbas.len() == 1 && lbas[0] == header.first_usable_lba {
834 return 1;
835 }
836
837 (1..=MAX_ALIGN.min(*lbas.iter().max().unwrap_or(&1)))
838 .filter(|div| lbas.iter().all(|x| x % div == 0))
839 .max()
840 .unwrap()
841 }
842
843 fn check_partition_guids(&self) -> Result<()> {
844 let guids: Vec<_> = self
845 .partitions
846 .iter()
847 .filter(|x| x.is_used())
848 .map(|x| x.unique_partition_guid)
849 .collect();
850 if guids.len() != guids.iter().collect::<HashSet<_>>().len() {
851 return Err(Error::ConflictPartitionGUID);
852 }
853
854 Ok(())
855 }
856
857 fn check_partition_boundaries(&self) -> Result<()> {
858 if self
859 .partitions
860 .iter()
861 .any(|x| x.ending_lba < x.starting_lba)
862 {
863 return Err(Error::InvalidPartitionBoundaries);
864 }
865
866 let mut partitions: Vec<&GPTPartitionEntry> =
867 self.partitions.iter().filter(|x| x.is_used()).collect();
868 partitions.sort_unstable_by_key(|x| x.starting_lba);
869 let first_available =
870 partitions
871 .iter()
872 .try_fold(self.header.first_usable_lba, |first_available, x| {
873 if x.starting_lba >= first_available {
874 Ok(x.ending_lba + 1)
875 } else {
876 Err(Error::InvalidPartitionBoundaries)
877 }
878 })?;
879 if first_available > self.header.last_usable_lba + 1 {
880 return Err(Error::InvalidPartitionBoundaries);
881 }
882
883 Ok(())
884 }
885
886 pub fn write_into<W>(&mut self, mut writer: &mut W) -> Result<GPTHeader>
918 where
919 W: Write + Seek + ?Sized,
920 {
921 self.check_partition_guids()?;
922 self.check_partition_boundaries()?;
923
924 let mut backup = self.header.clone();
925 backup.primary_lba = self.header.backup_lba;
926 backup.backup_lba = self.header.primary_lba;
927 backup.partition_entry_lba = if self.header.partition_entry_lba == 2 {
928 self.header.last_usable_lba + 1
929 } else {
930 2
931 };
932
933 self.header
934 .write_into(&mut writer, self.sector_size, &self.partitions)?;
935 backup.write_into(&mut writer, self.sector_size, &self.partitions)?;
936
937 Ok(backup)
938 }
939
940 pub fn find_at_sector(&self, sector: u64) -> Option<u32> {
942 fn between(partition: &GPTPartitionEntry, sector: u64) -> bool {
943 sector >= partition.starting_lba && sector <= partition.ending_lba
944 }
945
946 self.iter()
947 .find(|(_, partition)| partition.is_used() && between(partition, sector))
948 .map(|(id, _)| id)
949 }
950
951 pub fn find_free_sectors(&self) -> Vec<(u64, u64)> {
987 assert!(self.align > 0, "align must be greater than 0");
988 let mut positions = vec![self.header.first_usable_lba - 1];
989 for partition in self.partitions.iter().filter(|x| x.is_used()) {
990 positions.push(partition.starting_lba);
991 positions.push(partition.ending_lba);
992 }
993 positions.push(self.header.last_usable_lba + 1);
994 positions.sort_unstable();
995
996 positions
997 .chunks(2)
998 .map(|x| (x[0] + 1, x[1] - x[0] - 1))
999 .filter(|(_, l)| *l > 0)
1000 .map(|(i, l)| (i, l, ((i - 1) / self.align + 1) * self.align - i))
1001 .map(|(i, l, s)| (i + s, l.saturating_sub(s)))
1002 .filter(|(_, l)| *l > 0)
1003 .collect()
1004 }
1005
1006 pub fn find_first_place(&self, size: u64) -> Option<u64> {
1036 self.find_free_sectors()
1037 .iter()
1038 .find(|(_, l)| *l >= size)
1039 .map(|(i, _)| *i)
1040 }
1041
1042 pub fn find_last_place(&self, size: u64) -> Option<u64> {
1073 self.find_free_sectors()
1074 .iter()
1075 .filter(|(_, l)| *l >= size)
1076 .last()
1077 .map(|(i, l)| (i + l - size) / self.align * self.align)
1078 }
1079
1080 pub fn find_optimal_place(&self, size: u64) -> Option<u64> {
1112 let mut slots = self
1113 .find_free_sectors()
1114 .into_iter()
1115 .filter(|(_, l)| *l >= size)
1116 .collect::<Vec<_>>();
1117 slots.sort_by(|(_, l1), (_, l2)| l1.cmp(l2));
1118 slots.first().map(|&(i, _)| i)
1119 }
1120
1121 pub fn get_maximum_partition_size(&self) -> Result<u64> {
1144 self.find_free_sectors()
1145 .into_iter()
1146 .map(|(_, l)| l / self.align * self.align)
1147 .max()
1148 .ok_or(Error::NoSpaceLeft)
1149 }
1150
1151 pub fn get_partition_byte_range(&self, partition_number: u32) -> Result<RangeInclusive<u64>> {
1179 if partition_number == 0 || partition_number > self.header.number_of_partition_entries {
1180 return Err(Error::InvalidPartitionNumber(partition_number));
1181 }
1182 let partition = &self[partition_number];
1183
1184 let sector_range = partition.range()?;
1185
1186 let start_byte = sector_range
1187 .start()
1188 .checked_mul(self.sector_size)
1189 .ok_or(Error::Overflow)?;
1190 let end_byte = sector_range
1194 .end()
1195 .checked_mul(self.sector_size)
1196 .and_then(|v| v.checked_add(self.sector_size - 1))
1197 .ok_or(Error::Overflow)?;
1198 Ok(start_byte..=end_byte)
1199 }
1200
1201 pub fn sort(&mut self) {
1203 self.partitions
1204 .sort_by(|a, b| match (a.is_used(), b.is_used()) {
1205 (true, true) => a.starting_lba.cmp(&b.starting_lba),
1206 (true, false) => Ordering::Less,
1207 (false, true) => Ordering::Greater,
1208 (false, false) => Ordering::Equal,
1209 });
1210 }
1211
1212 pub fn remove(&mut self, i: u32) -> Result<()> {
1222 if i == 0 || i > self.header.number_of_partition_entries {
1223 return Err(Error::InvalidPartitionNumber(i));
1224 }
1225
1226 self.partitions[i as usize - 1] = GPTPartitionEntry::empty();
1227
1228 Ok(())
1229 }
1230
1231 pub fn remove_at_sector(&mut self, sector: u64) -> Result<()> {
1237 self.remove(
1238 self.find_at_sector(sector)
1239 .ok_or(Error::PartitionNotFound)?,
1240 )
1241 }
1242
1243 pub fn iter(&self) -> impl Iterator<Item = (u32, &GPTPartitionEntry)> {
1245 self.partitions
1246 .iter()
1247 .enumerate()
1248 .map(|(i, x)| (i as u32 + 1, x))
1249 }
1250
1251 pub fn iter_mut(&mut self) -> impl Iterator<Item = (u32, &mut GPTPartitionEntry)> {
1254 self.partitions
1255 .iter_mut()
1256 .enumerate()
1257 .map(|(i, x)| (i as u32 + 1, x))
1258 }
1259
1260 pub fn write_protective_mbr_into<W>(mut writer: &mut W, sector_size: u64) -> Result<()>
1265 where
1266 W: Write + Seek + ?Sized,
1267 {
1268 Self::write_protective_mbr_into_impl(&mut writer, sector_size, false)
1269 }
1270
1271 pub fn write_bootable_protective_mbr_into<W>(mut writer: &mut W, sector_size: u64) -> Result<()>
1282 where
1283 W: Write + Seek + ?Sized,
1284 {
1285 Self::write_protective_mbr_into_impl(&mut writer, sector_size, true)
1286 }
1287
1288 fn write_protective_mbr_into_impl<W>(
1289 mut writer: &mut W,
1290 sector_size: u64,
1291 bootable: bool,
1292 ) -> Result<()>
1293 where
1294 W: Write + Seek + ?Sized,
1295 {
1296 let size = writer.seek(SeekFrom::End(0))? / sector_size - 1;
1297 writer.seek(SeekFrom::Start(446))?;
1298 if bootable {
1300 writer.write_all(&[0x80])?;
1301 } else {
1302 writer.write_all(&[0x00])?;
1303 }
1304 writer.write_all(&[
1305 0x00, 0x02, 0x00, 0xee, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, ])?;
1310 let n_sectors = if size > u64::from(u32::MAX) {
1312 u32::MAX
1313 } else {
1314 size as u32
1315 };
1316 encode_into_std_write(&n_sectors, &mut writer)?;
1317 writer.write_all(&[0; 16])?; writer.write_all(&[0; 16])?; writer.write_all(&[0; 16])?; writer.write_all(&[0x55, 0xaa])?; Ok(())
1323 }
1324
1325 pub fn is_primary(&self) -> bool {
1328 self.header.is_primary()
1329 }
1330
1331 pub fn is_backup(&self) -> bool {
1337 self.header.is_backup()
1338 }
1339}
1340
1341impl Index<u32> for GPT {
1342 type Output = GPTPartitionEntry;
1343
1344 fn index(&self, i: u32) -> &GPTPartitionEntry {
1345 assert!(i != 0, "invalid partition index: 0");
1346 &self.partitions[i as usize - 1]
1347 }
1348}
1349
1350impl IndexMut<u32> for GPT {
1351 fn index_mut(&mut self, i: u32) -> &mut GPTPartitionEntry {
1352 assert!(i != 0, "invalid partition index: 0");
1353 &mut self.partitions[i as usize - 1]
1354 }
1355}
1356
1357#[cfg(test)]
1358mod test {
1359 #![allow(clippy::disallowed_names)]
1360
1361 use super::*;
1362 use std::fs;
1363
1364 const DISK1: &str = "tests/fixtures/disk1.img";
1365 const DISK2: &str = "tests/fixtures/disk2.img";
1366 const DISK3: &str = "tests/fixtures/disk3.img";
1367 const DISK4: &str = "tests/fixtures/disk4.img";
1368
1369 #[test]
1370 fn read_header_and_partition_entries() {
1371 fn test(path: &str, ss: u64) {
1372 let mut f = fs::File::open(path).unwrap();
1373
1374 f.seek(SeekFrom::Start(ss)).unwrap();
1375 let mut gpt = GPTHeader::read_from(&mut f).unwrap();
1376
1377 f.seek(SeekFrom::Start(gpt.backup_lba * ss)).unwrap();
1378 assert!(GPTHeader::read_from(&mut f).is_ok());
1379
1380 f.seek(SeekFrom::Start(gpt.partition_entry_lba * ss))
1381 .unwrap();
1382 let foo = GPTPartitionEntry::read_from(&mut f).unwrap();
1383 assert!(!foo.is_unused());
1384
1385 f.seek(SeekFrom::Start(
1386 gpt.partition_entry_lba * ss + u64::from(gpt.size_of_partition_entry),
1387 ))
1388 .unwrap();
1389 let bar = GPTPartitionEntry::read_from(&mut f).unwrap();
1390 assert!(!bar.is_unused());
1391
1392 let mut unused = 0;
1393 let mut used = 0;
1394 let mut partitions = Vec::new();
1395 for i in 0..gpt.number_of_partition_entries {
1396 f.seek(SeekFrom::Start(
1397 gpt.partition_entry_lba * ss
1398 + u64::from(i) * u64::from(gpt.size_of_partition_entry),
1399 ))
1400 .unwrap();
1401 let partition = GPTPartitionEntry::read_from(&mut f).unwrap();
1402
1403 if partition.is_unused() {
1404 unused += 1;
1405 } else {
1406 used += 1;
1407 }
1408
1409 let data1 = wincode::serialize(&partition).unwrap();
1411 f.seek(SeekFrom::Start(
1412 gpt.partition_entry_lba * ss
1413 + u64::from(i) * u64::from(gpt.size_of_partition_entry),
1414 ))
1415 .unwrap();
1416 let mut data2 = vec![0; gpt.size_of_partition_entry as usize];
1417 f.read_exact(&mut data2).unwrap();
1418 assert_eq!(data1, data2);
1419
1420 partitions.push(partition);
1421 }
1422 assert_eq!(unused, 126);
1423 assert_eq!(used, 2);
1424
1425 let sum = gpt.crc32_checksum;
1426 gpt.update_crc32_checksum();
1427 assert_eq!(gpt.crc32_checksum, sum);
1428 assert_eq!(gpt.generate_crc32_checksum(), sum);
1429 assert_ne!(gpt.crc32_checksum, 0);
1430
1431 let sum = gpt.partition_entry_array_crc32;
1432 gpt.update_partition_entry_array_crc32(&partitions);
1433 assert_eq!(gpt.partition_entry_array_crc32, sum);
1434 assert_eq!(gpt.generate_partition_entry_array_crc32(&partitions), sum);
1435 assert_ne!(gpt.partition_entry_array_crc32, 0);
1436 }
1437
1438 test(DISK1, 512);
1439 test(DISK2, 4096);
1440 }
1441
1442 #[test]
1443 fn read_and_find_from_primary() {
1444 assert!(GPT::read_from(&mut fs::File::open(DISK1).unwrap(), 512).is_ok());
1445 assert!(GPT::read_from(&mut fs::File::open(DISK1).unwrap(), 4096).is_err());
1446 assert!(GPT::read_from(&mut fs::File::open(DISK2).unwrap(), 512).is_err());
1447 assert!(GPT::read_from(&mut fs::File::open(DISK2).unwrap(), 4096).is_ok());
1448 assert!(GPT::find_from(&mut fs::File::open(DISK1).unwrap()).is_ok());
1449 assert!(GPT::find_from(&mut fs::File::open(DISK2).unwrap()).is_ok());
1450 }
1451
1452 #[test]
1453 fn input_too_short() {
1454 let mut empty = io::Cursor::new(vec![1; 5]);
1455 assert!(matches!(
1456 GPT::read_from(&mut empty, 512).expect_err("Should fail on short input"),
1457 Error::InvalidSignature
1458 ));
1459 }
1460
1461 #[test]
1462 fn find_backup() {
1463 fn test(path: &str, ss: u64) {
1464 let mut cur = io::Cursor::new(fs::read(path).unwrap());
1465 let mut gpt = GPT::read_from(&mut cur, ss).unwrap();
1466 assert_eq!(gpt.header.partition_entry_lba, 2);
1467 gpt.header.crc32_checksum = 1;
1468 cur.seek(SeekFrom::Start(gpt.sector_size)).unwrap();
1469 encode_into_std_write(&gpt.header, &mut cur).unwrap();
1470 let maybe_gpt = GPT::read_from(&mut cur, gpt.sector_size);
1471 assert!(maybe_gpt.is_ok());
1472 let gpt = maybe_gpt.unwrap();
1473 let end = cur.seek(SeekFrom::End(0)).unwrap() / gpt.sector_size - 1;
1474 assert_eq!(gpt.header.primary_lba, end);
1475 assert_eq!(gpt.header.backup_lba, 1);
1476 assert_eq!(
1477 gpt.header.partition_entry_lba,
1478 gpt.header.last_usable_lba + 1
1479 );
1480 assert!(GPT::find_from(&mut cur).is_ok());
1481 }
1482
1483 test(DISK1, 512);
1484 test(DISK2, 4096);
1485 }
1486
1487 #[test]
1488 fn add_partition_left() {
1489 let mut gpt = GPT::find_from(&mut fs::File::open(DISK1).unwrap()).unwrap();
1490 gpt.align = 1;
1491
1492 assert_eq!(gpt.find_first_place(10000), None);
1493 assert_eq!(gpt.find_first_place(4), Some(44));
1494 assert_eq!(gpt.find_first_place(8), Some(53));
1495 }
1496
1497 #[test]
1498 fn add_partition_left_aligned() {
1499 let mut gpt = GPT::find_from(&mut fs::File::open(DISK1).unwrap()).unwrap();
1500
1501 gpt.align = 10000;
1502 assert_eq!(gpt.find_first_place(1), None);
1503 gpt.align = 4;
1504 assert_eq!(gpt.find_first_place(4), Some(44));
1505 gpt.align = 6;
1506 assert_eq!(gpt.find_first_place(4), Some(54));
1507 }
1508
1509 #[test]
1510 fn add_partition_right() {
1511 let mut gpt = GPT::find_from(&mut fs::File::open(DISK2).unwrap()).unwrap();
1512 gpt.align = 1;
1513
1514 assert_eq!(gpt.find_last_place(10000), None);
1515 assert_eq!(gpt.find_last_place(5), Some(90));
1516 assert_eq!(gpt.find_last_place(20), Some(50));
1517 }
1518
1519 #[test]
1520 fn add_partition_right_aligned() {
1521 let mut gpt = GPT::find_from(&mut fs::File::open(DISK2).unwrap()).unwrap();
1522
1523 gpt.align = 10000;
1524 assert_eq!(gpt.find_last_place(1), None);
1525 gpt.align = 4;
1526 assert_eq!(gpt.find_last_place(5), Some(88));
1527 gpt.align = 8;
1528 assert_eq!(gpt.find_last_place(20), Some(48));
1529
1530 gpt.align = 1;
1532 assert_eq!(gpt.find_last_place(54), Some(16));
1533 assert_eq!(gpt.find_last_place(55), None);
1534 gpt.align = 10;
1535 assert_eq!(gpt.find_last_place(54), None);
1536 }
1537
1538 #[test]
1539 fn add_partition_optimal() {
1540 let mut gpt = GPT::find_from(&mut fs::File::open(DISK2).unwrap()).unwrap();
1541 gpt.align = 1;
1542
1543 assert_eq!(gpt.find_optimal_place(10000), None);
1544 assert_eq!(gpt.find_optimal_place(5), Some(80));
1545 assert_eq!(gpt.find_optimal_place(20), Some(16));
1546 }
1547
1548 #[test]
1549 fn add_partition_optimal_aligned() {
1550 let mut gpt = GPT::find_from(&mut fs::File::open(DISK2).unwrap()).unwrap();
1551
1552 gpt.align = 10000;
1553 assert_eq!(gpt.find_optimal_place(1), None);
1554 gpt.align = 6;
1555 assert_eq!(gpt.find_optimal_place(5), Some(84));
1556 gpt.align = 9;
1557 assert_eq!(gpt.find_optimal_place(20), Some(18));
1558 }
1559
1560 #[test]
1561 fn sort_partitions() {
1562 let mut gpt = GPT::find_from(&mut fs::File::open(DISK1).unwrap()).unwrap();
1563 gpt.align = 1;
1564
1565 let starting_lba = gpt.find_first_place(4).unwrap();
1566 gpt[10] = GPTPartitionEntry {
1567 starting_lba,
1568 ending_lba: starting_lba + 3,
1569 attribute_bits: 0,
1570 partition_type_guid: [1; 16],
1571 partition_name: "Baz".into(),
1572 unique_partition_guid: [1; 16],
1573 };
1574
1575 assert_eq!(
1576 gpt.iter()
1577 .filter(|(_, x)| x.is_used())
1578 .map(|(i, x)| (i, x.partition_name.as_str()))
1579 .collect::<Vec<_>>(),
1580 vec![(1, "Foo"), (2, "Bar"), (10, "Baz")]
1581 );
1582 gpt.sort();
1583 assert_eq!(
1584 gpt.iter()
1585 .filter(|(_, x)| x.is_used())
1586 .map(|(i, x)| (i, x.partition_name.as_str()))
1587 .collect::<Vec<_>>(),
1588 vec![(1, "Foo"), (2, "Baz"), (3, "Bar")]
1589 );
1590 }
1591
1592 #[test]
1593 fn add_partition_on_unsorted_table() {
1594 let mut gpt = GPT::find_from(&mut fs::File::open(DISK1).unwrap()).unwrap();
1595 gpt.align = 1;
1596
1597 let starting_lba = gpt.find_first_place(4).unwrap();
1598 gpt.partitions[10] = GPTPartitionEntry {
1599 starting_lba,
1600 ending_lba: starting_lba + 3,
1601 attribute_bits: 0,
1602 partition_type_guid: [1; 16],
1603 partition_name: "Baz".into(),
1604 unique_partition_guid: [1; 16],
1605 };
1606
1607 assert_eq!(gpt.find_first_place(8), Some(53));
1608 }
1609
1610 #[test]
1611 fn write_from_primary() {
1612 fn test(path: &str, ss: u64) {
1613 let mut f = fs::File::open(path).unwrap();
1614 let len = f.seek(SeekFrom::End(0)).unwrap();
1615 let data = vec![0; len as usize];
1616 let mut cur = io::Cursor::new(data);
1617 let mut gpt = GPT::read_from(&mut f, ss).unwrap();
1618 let backup_lba = gpt.header.backup_lba;
1619 gpt.write_into(&mut cur).unwrap();
1620 assert!(GPT::read_from(&mut cur, ss).is_ok());
1621
1622 gpt.header.crc32_checksum = 1;
1623 cur.seek(SeekFrom::Start(ss)).unwrap();
1624 encode_into_std_write(&gpt.header, &mut cur).unwrap();
1625 let maybe_gpt = GPT::read_from(&mut cur, ss);
1626 assert!(maybe_gpt.is_ok());
1627 let gpt = maybe_gpt.unwrap();
1628 assert_eq!(gpt.header.primary_lba, backup_lba);
1629 assert_eq!(gpt.header.backup_lba, 1);
1630 }
1631
1632 test(DISK1, 512);
1633 test(DISK2, 4096);
1634 }
1635
1636 #[test]
1637 fn write_from_backup() {
1638 fn test(path: &str, ss: u64) {
1639 let mut cur = io::Cursor::new(fs::read(path).unwrap());
1640 let mut gpt = GPT::read_from(&mut cur, ss).unwrap();
1641 let primary = gpt.clone();
1642 gpt.header.crc32_checksum = 1;
1643 let backup_lba = gpt.header.backup_lba;
1644 cur.seek(SeekFrom::Start(ss)).unwrap();
1645 encode_into_std_write(&gpt.header, &mut cur).unwrap();
1646 let mut gpt = GPT::read_from(&mut cur, ss).unwrap();
1647 assert!(!gpt.is_primary());
1648 assert!(gpt.is_backup());
1649 let partition_entry_lba = gpt.header.partition_entry_lba;
1650 let first_usable_lba = gpt.header.first_usable_lba;
1651 let last_usable_lba = gpt.header.last_usable_lba;
1652 let primary_header = gpt.write_into(&mut cur).unwrap();
1653 assert!(primary_header.is_primary());
1654 assert!(!primary_header.is_backup());
1655 let mut gpt = GPT::read_from(&mut cur, ss).unwrap();
1656 assert_eq!(gpt.header.primary_lba, 1);
1657 assert_eq!(gpt.header.backup_lba, backup_lba);
1658 assert_eq!(gpt.header.partition_entry_lba, 2);
1659 assert_eq!(gpt.header.first_usable_lba, first_usable_lba);
1660 assert_eq!(gpt.header.last_usable_lba, last_usable_lba);
1661 assert_eq!(primary, gpt);
1662
1663 gpt.header.crc32_checksum = 1;
1664 cur.seek(SeekFrom::Start(ss)).unwrap();
1665 encode_into_std_write(&gpt.header, &mut cur).unwrap();
1666 let maybe_gpt = GPT::read_from(&mut cur, ss);
1667 assert!(maybe_gpt.is_ok());
1668 let gpt = maybe_gpt.unwrap();
1669 assert_eq!(gpt.header.primary_lba, backup_lba);
1670 assert_eq!(gpt.header.backup_lba, 1);
1671 assert_eq!(gpt.header.partition_entry_lba, partition_entry_lba);
1672 }
1673
1674 test(DISK1, 512);
1675 test(DISK2, 4096);
1676 }
1677
1678 #[test]
1679 fn write_with_changes() {
1680 fn test(path: &str, ss: u64) {
1681 let mut f = fs::File::open(path).unwrap();
1682 let len = f.seek(SeekFrom::End(0)).unwrap();
1683 let data = vec![0; len as usize];
1684 let mut cur = io::Cursor::new(data);
1685 let mut gpt = GPT::read_from(&mut f, ss).unwrap();
1686 let backup_lba = gpt.header.backup_lba;
1687
1688 assert!(gpt.remove(1).is_ok());
1689 gpt.write_into(&mut cur).unwrap();
1690 let maybe_gpt = GPT::read_from(&mut cur, ss);
1691 assert!(maybe_gpt.is_ok(), "{:?}", maybe_gpt.err());
1692
1693 gpt.header.crc32_checksum = 1;
1694 cur.seek(SeekFrom::Start(ss)).unwrap();
1695 encode_into_std_write(&gpt.header, &mut cur).unwrap();
1696 let maybe_gpt = GPT::read_from(&mut cur, ss);
1697 assert!(maybe_gpt.is_ok());
1698 let gpt = maybe_gpt.unwrap();
1699 assert_eq!(gpt.header.primary_lba, backup_lba);
1700 assert_eq!(gpt.header.backup_lba, 1);
1701 }
1702
1703 test(DISK1, 512);
1704 test(DISK2, 4096);
1705 }
1706
1707 #[test]
1708 #[allow(clippy::manual_swap)]
1709 fn write_invalid_boundaries() {
1710 fn test(path: &str, ss: u64) {
1711 let mut cur = io::Cursor::new(fs::read(path).unwrap());
1712 let mut gpt = GPT::read_from(&mut cur, ss).unwrap();
1714 gpt[1].starting_lba = gpt.header.first_usable_lba - 1;
1715 gpt.write_into(&mut cur).unwrap_err();
1716 let mut gpt = GPT::read_from(&mut cur, ss).unwrap();
1718 let start = gpt[1].starting_lba;
1719 gpt[1].starting_lba = gpt[1].ending_lba;
1720 gpt[1].ending_lba = start;
1721 gpt.write_into(&mut cur).unwrap_err();
1722 let mut gpt = GPT::read_from(&mut cur, ss).unwrap();
1724 gpt[1].ending_lba = gpt[2].starting_lba;
1725 gpt.write_into(&mut cur).unwrap_err();
1726 let mut gpt = GPT::read_from(&mut cur, ss).unwrap();
1728 gpt[2].ending_lba = gpt.header.last_usable_lba + 1;
1729 gpt.write_into(&mut cur).unwrap_err();
1730 let mut gpt = GPT::read_from(&mut cur, ss).unwrap();
1732 gpt.write_into(&mut cur).unwrap();
1733 }
1734 test(DISK1, 512);
1735 test(DISK2, 4096);
1736 }
1737
1738 #[test]
1739 fn get_maximum_partition_size_on_empty_disk() {
1740 let mut gpt = GPT::find_from(&mut fs::File::open(DISK1).unwrap()).unwrap();
1741 gpt.align = 1;
1742
1743 for i in 1..=gpt.header.number_of_partition_entries {
1744 assert!(gpt.remove(i).is_ok());
1745 }
1746
1747 assert_eq!(gpt.get_maximum_partition_size().ok(), Some(33));
1748 }
1749
1750 #[test]
1751 fn get_maximum_partition_size_on_disk_full() {
1752 let mut gpt = GPT::find_from(&mut fs::File::open(DISK1).unwrap()).unwrap();
1753 gpt.align = 1;
1754
1755 for partition in gpt.partitions.iter_mut().skip(1) {
1756 partition.partition_type_guid = [0; 16];
1757 }
1758 gpt.partitions[0].starting_lba = gpt.header.first_usable_lba;
1759 gpt.partitions[0].ending_lba = gpt.header.last_usable_lba;
1760
1761 assert!(gpt.get_maximum_partition_size().is_err());
1762 }
1763
1764 #[test]
1765 fn get_maximum_partition_size_on_empty_disk_and_aligned() {
1766 let mut gpt = GPT::find_from(&mut fs::File::open(DISK1).unwrap()).unwrap();
1767
1768 for i in 1..=gpt.header.number_of_partition_entries {
1769 assert!(gpt.remove(i).is_ok());
1770 }
1771
1772 gpt.align = 10;
1773 assert_eq!(gpt.get_maximum_partition_size().ok(), Some(20));
1774 gpt.align = 6;
1775 assert_eq!(gpt.get_maximum_partition_size().ok(), Some(30));
1776 }
1777
1778 #[test]
1779 fn create_new_gpt() {
1780 fn test(path: &str, ss: u64) {
1781 let mut f = fs::File::open(path).unwrap();
1782 let gpt1 = GPT::read_from(&mut f, ss).unwrap();
1783 let gpt2 = GPT::new_from(&mut f, ss, [1; 16]).unwrap();
1784 assert_eq!(gpt2.header.backup_lba, gpt1.header.backup_lba);
1785 assert_eq!(gpt2.header.last_usable_lba, gpt1.header.last_usable_lba);
1786 assert_eq!(gpt2.header.first_usable_lba, gpt1.header.first_usable_lba);
1787 }
1788
1789 test(DISK1, 512);
1790 test(DISK2, 4096);
1791 }
1792
1793 #[test]
1794 fn determine_partition_alignment_no_partition() {
1795 fn test(ss: u64) {
1796 let data = vec![0; ss as usize * DEFAULT_ALIGN as usize * 10];
1797 let mut cur = io::Cursor::new(data);
1798 let mut gpt = GPT::new_from(&mut cur, ss, [1; 16]).unwrap();
1799 assert_eq!(gpt.align, DEFAULT_ALIGN);
1800 gpt.write_into(&mut cur).unwrap();
1801 let gpt = GPT::read_from(&mut cur, ss).unwrap();
1802 assert_eq!(gpt.align, DEFAULT_ALIGN);
1803 }
1804
1805 test(512);
1806 test(4096);
1807 }
1808
1809 #[test]
1810 fn determine_partition_alignment() {
1811 fn test(ss: u64, align: u64) {
1812 let data = vec![0; ss as usize * align as usize * 21];
1813 let mut cur = io::Cursor::new(data);
1814 let mut gpt = GPT::new_from(&mut cur, ss, [1; 16]).unwrap();
1815 gpt[1] = GPTPartitionEntry {
1816 attribute_bits: 0,
1817 ending_lba: 6 * align,
1818 partition_name: "".into(),
1819 partition_type_guid: [1; 16],
1820 starting_lba: 5 * align,
1822 unique_partition_guid: [1; 16],
1823 };
1824 gpt[2] = GPTPartitionEntry {
1825 attribute_bits: 0,
1826 ending_lba: 16 * align,
1827 partition_name: "".into(),
1828 partition_type_guid: [1; 16],
1829 starting_lba: 8 * align,
1830 unique_partition_guid: [2; 16],
1831 };
1832 gpt.write_into(&mut cur).unwrap();
1833 let gpt = GPT::read_from(&mut cur, ss).unwrap();
1834 assert_eq!(gpt.align, align);
1835 }
1836
1837 test(512, 8); test(512, 2048); test(512, 2048 * 4); test(4096, 8);
1841 test(4096, 2048);
1842 test(4096, 2048 * 4);
1843 }
1844
1845 #[test]
1846 fn determine_partition_alignment_full_disk() {
1847 fn test(ss: u64) {
1848 let data = vec![0; ss as usize * 100];
1849 let mut cur = io::Cursor::new(data);
1850 let mut gpt = GPT::new_from(&mut cur, ss, [1; 16]).unwrap();
1851 gpt[1] = GPTPartitionEntry {
1852 attribute_bits: 0,
1853 ending_lba: gpt.header.last_usable_lba,
1854 partition_name: "".into(),
1855 partition_type_guid: [1; 16],
1856 starting_lba: gpt.header.first_usable_lba,
1857 unique_partition_guid: [1; 16],
1858 };
1859 gpt.write_into(&mut cur).unwrap();
1860 let gpt = GPT::read_from(&mut cur, ss).unwrap();
1861 assert_eq!(gpt.align, 1);
1862
1863 let mut gpt = GPT::new_from(&mut cur, ss, [1; 16]).unwrap();
1864 gpt[1] = GPTPartitionEntry {
1865 attribute_bits: 0,
1866 ending_lba: gpt.header.last_usable_lba,
1867 partition_name: "".into(),
1868 partition_type_guid: [1; 16],
1869 starting_lba: gpt.header.first_usable_lba + 1,
1870 unique_partition_guid: [1; 16],
1871 };
1872 gpt.write_into(&mut cur).unwrap();
1873 let gpt = GPT::read_from(&mut cur, ss).unwrap();
1874 assert_eq!(gpt.align, gpt.header.first_usable_lba + 1);
1875 }
1876
1877 test(512);
1878 test(4096);
1879 }
1880
1881 #[test]
1882 fn writing_protective_mbr() {
1883 fn test(ss: u64) {
1884 let data = vec![2; ss as usize * 100];
1885 let mut cur = io::Cursor::new(data);
1886 GPT::write_protective_mbr_into(&mut cur, ss).unwrap();
1887 let data = cur.get_ref();
1888
1889 assert_eq!(data[510], 0x55);
1890 assert_eq!(data[511], 0xaa);
1891 assert_eq!(data[446 + 4], 0xee);
1892 for (i, x) in data.iter().enumerate() {
1893 if !(446..512).contains(&i) {
1894 assert_eq!(*x, 2);
1895 }
1896 }
1897
1898 cur.seek(SeekFrom::Start(446 + 8)).unwrap();
1899 let first_lba: u32 = decode_from_std_read(&mut cur).unwrap();
1900 let sectors: u32 = decode_from_std_read(&mut cur).unwrap();
1901 assert_eq!(first_lba, 1);
1902 assert_eq!(sectors, 99);
1903 }
1904
1905 test(512);
1906 test(4096);
1907 }
1908
1909 #[test]
1910 fn read_from_smaller_disk_and_write_to_bigger_disk() {
1911 fn test(path: &str, ss: u64) {
1912 let mut f = fs::File::open(path).unwrap();
1913 let len = f.seek(SeekFrom::End(0)).unwrap();
1914 let gpt1 = GPT::read_from(&mut f, ss).unwrap();
1915 let data = vec![0; len as usize * 2];
1916 let mut cur = io::Cursor::new(data);
1917 gpt1.clone().write_into(&mut cur).unwrap();
1918 let gpt2 = GPT::read_from(&mut cur, ss).unwrap();
1919 assert_eq!(gpt1, gpt2);
1920 }
1921
1922 test(DISK1, 512);
1923 test(DISK2, 4096);
1924 }
1925
1926 #[test]
1927 fn read_label_with_trailing_garbage_in_names() {
1928 fn test(path: &str, ss: u64) {
1929 let mut f = fs::File::open(path).unwrap();
1930 let gpt = GPT::read_from(&mut f, ss);
1931 assert!(gpt.is_ok());
1933 assert_eq!(
1935 gpt.unwrap()
1936 .partitions
1937 .get(1)
1938 .unwrap()
1939 .partition_name
1940 .as_str(),
1941 "Name with garbage"
1942 );
1943 }
1944 test(DISK3, 512);
1945 test(DISK4, 4096);
1946 }
1947}
1948
1949#[cfg(doctest)]
1950mod test_readme {
1951 macro_rules! check_doc {
1954 ($x:expr) => {
1955 #[doc = $x]
1956 extern "C" {}
1957 };
1958 }
1959 check_doc!(include_str!("../README.md"));
1960}