Skip to content

Commit

Permalink
Towards 4.0
Browse files Browse the repository at this point in the history
- crc32 are stored in the header after it has been written
- Remove initialized state & configuration
- add the option to allow the partition count to be changed
- add the option to only open a disk if both headers are valid
- add the option to keep the backup partition readonly
- split `GptDisk::remove_partition` into two functions `remove_partition` and `remove_partition_by_guid`
- add `GptDisk::header` function which allows to get the current header (either primary or backup)
- add `GptDisk::take_partitions`
- remove `GptDisk::update_partitions_safe` since you can now use the readonly backup option
- remove `GptDisk::update_partitions_embedded` since you can now allow the partition count to be changed
- add `GptDisk::device_ref`
- add `GptDisk::device_mut`
  • Loading branch information
soerenmeier committed Aug 26, 2023
1 parent 8e505e3 commit c0335be
Show file tree
Hide file tree
Showing 7 changed files with 506 additions and 341 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# gpt
[![crates.io](https://img.shields.io/crates/v/gpt.svg)](https://crates.io/crates/gpt)
![minimum rust 1.46](https://img.shields.io/badge/rust-1.46%2B-orange.svg)
![minimum rust 1.63](https://img.shields.io/badge/rust-1.63%2B-orange.svg)
[![Documentation](https://docs.rs/gpt/badge.svg)](https://docs.rs/gpt)

A pure-Rust library to work with GPT partition tables.
Expand Down
4 changes: 2 additions & 2 deletions src/header/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ impl HeaderBuilder {
backup_lba: 0,
first_usable: 0,
last_usable: 0,
num_parts: 128,
num_parts: super::MIN_NUM_PARTS,
part_size: 128,
}
}
Expand Down Expand Up @@ -102,7 +102,7 @@ impl HeaderBuilder {
/// ## Warning
/// This might change the first usable and last usable part
pub fn num_parts(&mut self, num_parts: u32) -> &mut Self {
self.num_parts = num_parts.max(128);
self.num_parts = num_parts.max(super::MIN_NUM_PARTS);
self
}

Expand Down
69 changes: 40 additions & 29 deletions src/header/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ use crate::disk;

use simple_bytes::{BytesArray, BytesRead, BytesSeek, BytesWrite};

const MIN_NUM_PARTS: u32 = 128;

#[non_exhaustive]
#[derive(Debug)]
/// Errors returned when interacting with a header.
Expand Down Expand Up @@ -86,7 +88,8 @@ pub struct Header {
pub revision: (u16, u16), // Offset 8
/// little endian
pub header_size_le: u32, // Offset 12
/// CRC32 of the header with crc32 section zeroed
/// CRC32 of the header, will be incorrect after changing something until the
/// header get's written
pub crc32: u32, // Offset 16
/// must be 0
pub reserved: u32, // Offset 20
Expand All @@ -106,17 +109,15 @@ pub struct Header {
pub num_parts: u32, // Offset 80
/// Size of a partition entry, usually 128
pub part_size: u32, // Offset 84
/// CRC32 of the partition table
/// CRC32 of the partition table, will be incorrect after changing something until the
/// header get's written
pub crc32_parts: u32, // Offset 88
}

impl Header {
/// Write the primary header.
///
/// With a CRC32 set to zero this will set the crc32 after
/// writing the header out.
pub fn write_primary<D: Read + Write + Seek>(
&self,
&mut self,
file: &mut D,
lb_size: disk::LogicalBlockSize,
) -> Result<usize, HeaderError> {
Expand All @@ -132,11 +133,8 @@ impl Header {
}

/// Write the backup header.
///
/// With a CRC32 set to zero this will set the crc32 after
/// writing the header out.
pub fn write_backup<D: Read + Write + Seek>(
&self,
&mut self,
file: &mut D,
lb_size: disk::LogicalBlockSize,
) -> Result<usize, HeaderError> {
Expand All @@ -152,51 +150,61 @@ impl Header {
}

/// Write an header to an arbitrary LBA.
///
/// With a CRC32 set to zero this will set the crc32 after
/// writing the header out.
fn file_write_header<D: Read + Write + Seek>(
&self,
&mut self,
file: &mut D,
lba: u64,
lb_size: disk::LogicalBlockSize,
) -> Result<usize, HeaderError> {
// Build up byte array in memory
let parts_checksum = partentry_checksum(file, self, lb_size)?;
self.crc32_parts = parts_checksum;
trace!("computed partitions CRC32: {:#x}", parts_checksum);
let bytes = self.to_bytes(None, Some(parts_checksum));
trace!("bytes before checksum: {:?}", bytes);
let (checksum_pos, mut header_bytes) = self.to_bytes(parts_checksum);
trace!("bytes before checksum: {:?}", header_bytes);

// Calculate the CRC32 from the byte array
let checksum = calculate_crc32(&bytes);
let checksum = calculate_crc32(header_bytes.as_slice());
self.crc32 = checksum;
trace!("computed header CRC32: {:#x}", checksum);

// write checksum to bytes
BytesSeek::seek(&mut header_bytes, checksum_pos);
header_bytes.write_le_u32(checksum);

// Write it to disk in 1 shot
let start = lba
.checked_mul(lb_size.into())
.ok_or(HeaderError::Overflow("writing header: lba * lbs"))?;
trace!("Seeking to {}", start);
let _ = file.seek(SeekFrom::Start(start))?;
let header_bytes = self.to_bytes(Some(checksum), Some(parts_checksum));
// Per the spec, the rest of the logical block must be zeros...
let mut bytes = Vec::with_capacity(lb_size.as_usize());
bytes.extend_from_slice(&header_bytes);
bytes.extend_from_slice(header_bytes.as_slice());
bytes.resize(lb_size.as_usize(), 0);
let len = file.write(&bytes)?;
trace!("Wrote {} bytes", len);
file.write_all(&bytes)?;
trace!("Wrote {} bytes", bytes.len());

Ok(len)
Ok(bytes.len())
}

fn to_bytes(&self, header_checksum: Option<u32>, partitions_checksum: Option<u32>) -> [u8; 92] {
/// Returns true if the num parts changes
pub(crate) fn num_parts_would_change(&self, partitions_len: u32) -> bool {
let n_num_parts = partitions_len.max(MIN_NUM_PARTS).max(self.num_parts);
self.num_parts != n_num_parts
}

/// returns the position where the checksum should be written
fn to_bytes(&self, partitions_checksum: u32) -> (usize, BytesArray<92>) {
let mut bytes = BytesArray::from([0u8; 92]);
let disk_guid_fields = self.disk_guid.as_fields();

BytesWrite::write(&mut bytes, self.signature.as_bytes());
bytes.write_le_u16(self.revision.1);
bytes.write_le_u16(self.revision.0);
bytes.write_le_u32(self.header_size_le);
bytes.write_le_u32(header_checksum.unwrap_or_default());
let checksum_position = bytes.position();
bytes.write_le_u32(0);
bytes.write_le_u32(0);
bytes.write_le_u64(self.current_lba);
bytes.write_le_u64(self.backup_lba);
Expand All @@ -209,9 +217,9 @@ impl Header {
bytes.write_le_u64(self.part_start);
bytes.write_le_u32(self.num_parts);
bytes.write_le_u32(self.part_size);
bytes.write_le_u32(partitions_checksum.unwrap_or_default());
bytes.write_le_u32(partitions_checksum);

bytes.into_array()
(checksum_position, bytes)
}
}

Expand Down Expand Up @@ -340,6 +348,8 @@ pub(crate) fn file_read_header<D: Read + Seek>(
BytesSeek::seek(&mut bytes, 16);
bytes.write_u32(0);

// todo should probably also validate the partitions crc32

let c = calculate_crc32(bytes.as_slice());
trace!("header CRC32: {:#x} - computed CRC32: {:#x}", h.crc32, c);
if c == h.crc32 {
Expand All @@ -349,6 +359,7 @@ pub(crate) fn file_read_header<D: Read + Seek>(
}
}

/// get the backup position in lba
pub(crate) fn find_backup_lba<D: Read + Seek>(
f: &mut D,
sector_size: disk::LogicalBlockSize,
Expand Down Expand Up @@ -434,7 +445,7 @@ pub fn write_header(
header.disk_guid(uuid);
}

let header = header.backup_lba(bak).build(sector_size)?;
let mut header = header.backup_lba(bak).build(sector_size)?;

debug!("new header: {:#?}", header);
header.write_primary(&mut file, sector_size)?;
Expand Down Expand Up @@ -539,13 +550,13 @@ mod tests {
fn write_gpt_disk() {
let lb_size = LogicalBlockSize::Lb512;

let primary = HeaderBuilder::new()
let mut primary = HeaderBuilder::new()
.disk_guid("1B6A2BFA-E92B-184C-A8A7-ED0610D54821".parse().unwrap())
.backup_lba(71)
.build(lb_size)
.unwrap();

let backup = HeaderBuilder::new()
let mut backup = HeaderBuilder::new()
.disk_guid("1B6A2BFA-E92B-184C-A8A7-ED0610D54821".parse().unwrap())
.backup_lba(71)
.primary(false)
Expand Down
Loading

0 comments on commit c0335be

Please sign in to comment.