diff --git a/src/lib.rs b/src/lib.rs index 44192b9..4eb63f1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -378,24 +378,37 @@ impl GPTHeader { /// A wrapper type for `String` that represents a partition's name. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct PartitionName(String); +pub struct PartitionName { + string: String, + raw_buf: [u16; 36], +} impl PartitionName { /// Extracts a string slice containing the entire `PartitionName`. pub fn as_str(&self) -> &str { - self.0.as_str() + &self.string } } impl std::fmt::Display for PartitionName { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) + write!(f, "{}", &self.string) } } impl From<&str> for PartitionName { fn from(value: &str) -> PartitionName { - PartitionName(value.to_string()) + let utf16_converted: Vec<_> = value + .encode_utf16() + .chain([0].into_iter().cycle()) + .take(36) + .collect(); + let mut raw_buf = [0; 36]; + raw_buf.copy_from_slice(&utf16_converted); + PartitionName { + string: value.to_string(), + raw_buf, + } } } @@ -412,18 +425,25 @@ impl<'de> Visitor<'de> for UTF16LEVisitor { where A: SeqAccess<'de>, { - let mut v = Vec::new(); + let mut v: Vec = Vec::new(); + let mut raw_buf = [0; 36]; let mut end = false; - loop { - match seq.next_element()? { - Some(0) => end = true, - Some(x) if !end => v.push(x), - Some(_) => {} - None => break, + let mut iter = raw_buf.iter_mut(); + while let Some(x) = seq.next_element()? { + if let Some(element) = iter.next() { + if x == 0 { + end = true; + } + if !end { + v.push(x); + } + *element = x; } } - - Ok(PartitionName(String::from_utf16_lossy(&v))) + Ok(PartitionName { + string: String::from_utf16_lossy(&v), + raw_buf, + }) } } @@ -441,10 +461,10 @@ impl Serialize for PartitionName { where S: Serializer, { - let s = self.0.encode_utf16(); + // Favor using the content in the raw buffer in case there is garbage left (used for the CRC) let mut seq = serializer.serialize_tuple(36)?; - for x in s.chain([0].iter().cycle().cloned()).take(36) { - seq.serialize_element(&x)?; + for x in self.raw_buf { + seq.serialize_element(&x.to_le_bytes())?; } seq.end() } @@ -1347,6 +1367,8 @@ mod test { const DISK1: &str = "tests/fixtures/disk1.img"; const DISK2: &str = "tests/fixtures/disk2.img"; + const DISK3: &str = "tests/fixtures/disk3.img"; + const DISK4: &str = "tests/fixtures/disk4.img"; #[test] fn read_header_and_partition_entries() { @@ -1904,6 +1926,28 @@ mod test { test(DISK1, 512); test(DISK2, 4096); } + + #[test] + fn read_label_with_trailing_garbage_in_names() { + fn test(path: &str, ss: u64) { + let mut f = fs::File::open(path).unwrap(); + let gpt = GPT::read_from(&mut f, ss); + // CRC check passes with trailing garbage within partition names + assert!(gpt.is_ok()); + // Trailing garbage data are visible using hexdump + assert_eq!( + gpt.unwrap() + .partitions + .get(1) + .unwrap() + .partition_name + .as_str(), + "Name with garbage" + ); + } + test(DISK3, 512); + test(DISK4, 4096); + } } #[cfg(doctest)] diff --git a/tests/fixtures/disk3.img b/tests/fixtures/disk3.img new file mode 100644 index 0000000..764bb6d Binary files /dev/null and b/tests/fixtures/disk3.img differ diff --git a/tests/fixtures/disk4.img b/tests/fixtures/disk4.img new file mode 100644 index 0000000..3f46266 Binary files /dev/null and b/tests/fixtures/disk4.img differ