Skip to content

Commit 5362d64

Browse files
committed
chroe: properly perform checksums on partition table arrays
Some partitioning tools like ones based on libparted (parted and GParted) don't wipe the entire GPT partition name field before writing new partition names. Additionally, it is not guaranteed to be properly zeroed since the UEFI specification does not clearly state that. Use a dedicated raw buffer which directly read or write to the disk to perform checksums without explicit string handling. Signed-off-by: Xinhui Yang <[email protected]>
1 parent 7bd1432 commit 5362d64

File tree

1 file changed

+44
-7
lines changed

1 file changed

+44
-7
lines changed

src/lib.rs

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -305,14 +305,16 @@ impl GPTHeader {
305305
}
306306

307307
/// Generate the CRC32 checksum of the partition entry array.
308-
pub fn generate_partition_entry_array_crc32(&self, partitions: &[GPTPartitionEntry]) -> u32 {
308+
pub fn generate_partition_entry_array_crc32(
309+
&self,
310+
partitions_raw: &[RawPartitionEntry],
311+
) -> u32 {
309312
let mut clone = self.clone();
310313
clone.partition_entry_array_crc32 = 0;
311314
let crc = Crc::<u32>::new(&CRC_32_ISO_HDLC);
312315
let mut digest = crc.digest();
313316
let mut wrote = 0;
314-
for x in partitions {
315-
let data = encode_to_vec(x, legacy()).expect("could not serialize");
317+
for data in partitions_raw {
316318
digest.update(&data);
317319
wrote += data.len();
318320
}
@@ -326,7 +328,21 @@ impl GPTHeader {
326328

327329
/// Update the CRC32 checksum of the partition entry array.
328330
pub fn update_partition_entry_array_crc32(&mut self, partitions: &[GPTPartitionEntry]) {
329-
self.partition_entry_array_crc32 = self.generate_partition_entry_array_crc32(partitions);
331+
// Dump the partitions into raw bytes.
332+
let mut partitions_raw = Vec::new();
333+
for i in 0usize..self.number_of_partition_entries as usize {
334+
let mut cur_raw = vec![0; self.size_of_partition_entry as usize];
335+
if let Some(entry) = partitions.get(i) {
336+
let data = encode_to_vec(entry, legacy()).expect("could not serialize");
337+
cur_raw.copy_from_slice(&data);
338+
} else {
339+
cur_raw.copy_from_slice(&vec![0; self.size_of_partition_entry as usize]);
340+
};
341+
partitions_raw.push(cur_raw);
342+
}
343+
// Use raw bytes for checksuming.
344+
self.partition_entry_array_crc32 =
345+
self.generate_partition_entry_array_crc32(&partitions_raw);
330346
}
331347

332348
/// Updates the header to match the specifications of the seeker given in argument.
@@ -450,6 +466,12 @@ impl Serialize for PartitionName {
450466
}
451467
}
452468

469+
/// Raw bytes containing all partition entries for checksuming, to
470+
/// accommodate extreme (but common) situations like improperly zeroed
471+
/// partition names. Tools based on libparted usually exhibit this behavior.
472+
/// Note: using Vec instead of static array; entry size is variable.
473+
type RawPartitionEntry = Vec<u8>;
474+
453475
/// A GPT partition's entry in the partition array.
454476
///
455477
/// # Examples
@@ -660,6 +682,7 @@ pub struct GPT {
660682
/// GPT partition header (disk GUID, first/last usable LBA, etc...)
661683
pub header: GPTHeader,
662684
partitions: Vec<GPTPartitionEntry>,
685+
partitions_raw: Vec<RawPartitionEntry>,
663686
/// Partitions alignment (in sectors)
664687
///
665688
/// This field change the behavior of the methods `get_maximum_partition_size()`,
@@ -699,6 +722,7 @@ impl GPT {
699722
sector_size,
700723
header,
701724
partitions,
725+
partitions_raw: vec![],
702726
align: DEFAULT_ALIGN,
703727
})
704728
}
@@ -744,15 +768,22 @@ impl GPT {
744768
})?;
745769

746770
let mut partitions = Vec::with_capacity(header.number_of_partition_entries as usize);
771+
let mut partitions_raw = Vec::new();
747772
for i in 0..header.number_of_partition_entries {
748773
reader.seek(SeekFrom::Start(
749774
header.partition_entry_lba * sector_size
750775
+ u64::from(i) * u64::from(header.size_of_partition_entry),
751776
))?;
752-
partitions.push(GPTPartitionEntry::read_from(&mut reader)?);
777+
778+
let mut raw_entry = vec![0; header.size_of_partition_entry as usize];
779+
// Read to raw buffer
780+
reader.read_exact(&mut raw_entry.as_mut_slice())?;
781+
782+
partitions.push(GPTPartitionEntry::read_from(&mut raw_entry.as_slice())?);
783+
partitions_raw.push(raw_entry);
753784
}
754785

755-
let sum = header.generate_partition_entry_array_crc32(&partitions);
786+
let sum = header.generate_partition_entry_array_crc32(&partitions_raw);
756787
if header.partition_entry_array_crc32 != sum {
757788
return Err(Error::InvalidPartitionEntryArrayChecksum(
758789
header.partition_entry_array_crc32,
@@ -766,6 +797,7 @@ impl GPT {
766797
sector_size,
767798
header,
768799
partitions,
800+
partitions_raw,
769801
align,
770802
})
771803
}
@@ -1374,6 +1406,7 @@ mod test {
13741406
let mut unused = 0;
13751407
let mut used = 0;
13761408
let mut partitions = Vec::new();
1409+
let mut partitions_raw = Vec::new();
13771410
for i in 0..gpt.number_of_partition_entries {
13781411
f.seek(SeekFrom::Start(
13791412
gpt.partition_entry_lba * ss
@@ -1400,6 +1433,7 @@ mod test {
14001433
assert_eq!(data1, data2);
14011434

14021435
partitions.push(partition);
1436+
partitions_raw.push(data2);
14031437
}
14041438
assert_eq!(unused, 126);
14051439
assert_eq!(used, 2);
@@ -1413,7 +1447,10 @@ mod test {
14131447
let sum = gpt.partition_entry_array_crc32;
14141448
gpt.update_partition_entry_array_crc32(&partitions);
14151449
assert_eq!(gpt.partition_entry_array_crc32, sum);
1416-
assert_eq!(gpt.generate_partition_entry_array_crc32(&partitions), sum);
1450+
assert_eq!(
1451+
gpt.generate_partition_entry_array_crc32(&partitions_raw),
1452+
sum
1453+
);
14171454
assert_ne!(gpt.partition_entry_array_crc32, 0);
14181455
}
14191456

0 commit comments

Comments
 (0)