Skip to content

refactor(file): replace fdisk with gptman+mbrman#154

Open
HarryWaschkeit wants to merge 1 commit intoomnect:mainfrom
HarryWaschkeit:feature/gptman-mbrman-partition-detection
Open

refactor(file): replace fdisk with gptman+mbrman#154
HarryWaschkeit wants to merge 1 commit intoomnect:mainfrom
HarryWaschkeit:feature/gptman-mbrman-partition-detection

Conversation

@HarryWaschkeit
Copy link

Summary

Replace the fragile fdisk-based partition detection in get_partition_info() with Rust-native gptman and mbrman crates.

Changes:

  • Add gptman (GPT with CRC32 validation) and mbrman (MBR including logical partitions) as dependencies
  • Add src/file/partition.rs with get_partition_data() and is_gpt()
  • Remove locale-sensitive fdisk shell invocation and regex parsing
  • Remove fdisk from package.metadata.deb depends
  • Change PartitionInfo fields from String to typed u32/u64

Reason

The previous implementation spawned fdisk -l and parsed its text output with regex — making it locale-sensitive, version-dependent, and requiring fdisk as a runtime system dependency.

Additionally, a pre-existing bug was fixed: PartitionInfo.end held the last sector (inclusive) from fdisk output and was passed directly as dd count=, but count expects the number of sectors. For a partition starting at sector 2048 and ending at 206847, this caused dd to copy 206847 sectors instead of the correct 204800.

Fixed semantics:

  • GPT: count = ending_lba - starting_lba + 1
  • MBR: count = p.sectors (already the sector count)

Verification

  • cargo check passes
  • cargo fmt -- --check passes
  • cargo clippy -- -D warnings passes
  • Manually tested against GPT and MBR image files

Replace fragile fdisk-based partition detection with Rust-native crates:

- add gptman (GPT with CRC32 validation) and mbrman (MBR including
  logical partitions) as dependencies
- add src/file/partition.rs: get_partition_data() and is_gpt()
- remove locale-sensitive fdisk shell invocation and regex parsing
- mbrman iter() correctly reaches logical partitions 5 and 6
- fix pre-existing bug: PartitionInfo.end (last sector) was used as
  dd count= instead of sector count; rename field to count with
  correct semantics (ending_lba - starting_lba + 1 for GPT,
  p.sectors for MBR)
- remove fdisk from package.metadata.deb depends
- change PartitionInfo fields from String to typed u32/u64

Signed-off-by: Harry Waschkeit <44188360+HarryWaschkeit@users.noreply.github.com>
@HarryWaschkeit HarryWaschkeit marked this pull request as draft March 16, 2026 18:39
@HarryWaschkeit HarryWaschkeit marked this pull request as ready for review March 17, 2026 15:19
@HarryWaschkeit
Copy link
Author

@JoergZeidler @mlilien fyi

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Refactors image partition detection away from shelling out to fdisk and instead uses Rust-native parsing via gptman (GPT) and mbrman (MBR), while also correcting partition sector-count semantics for the dd operations.

Changes:

  • Introduces src/file/partition.rs to read GPT/MBR partition start/count directly from the image.
  • Updates get_partition_info() and dd invocation to use typed u32/u64 fields and correct count semantics.
  • Adds gptman/mbrman dependencies and removes fdisk from Debian runtime dependencies.

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/file/partition.rs New partition table parsing utilities using gptman/mbrman.
src/file/mod.rs Wires in the new partition module.
src/file/functions.rs Replaces fdisk+regex partition discovery with Rust-native parsing; fixes dd count= usage.
Cargo.toml Adds gptman/mbrman; removes fdisk from cargo-deb depends.
Cargo.lock Lockfile updates for the newly introduced crates and their transitive deps.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +22 to +25
return Ok(PartitionData {
num: partition_num,
start: entry.starting_lba,
count: entry.ending_lba - entry.starting_lba + 1,
pub fn is_gpt<P: AsRef<Path>>(path: P) -> Result<bool> {
let mut file = File::open(path.as_ref())
.context("is_gpt: failed to open image")?;
Ok(gptman::GPT::find_from(&mut file).is_ok())
Comment on lines 382 to 403
fn get_partition_info(image_file: &str, partition: &Partition) -> Result<PartitionInfo> {
let mut fdisk = Command::new("fdisk");
fdisk
.arg("-l")
.arg("-o")
.arg("Device,Start,End")
.arg(image_file);
let fdisk_out = exec_cmd_with_output!(fdisk);
use crate::file::partition::{get_partition_data, is_gpt};

let gpt = is_gpt(image_file)
.context("get_partition_info: failed to detect partition table type")?;

let partition_num = match partition {
let partition_num: u32 = match partition {
Partition::boot => 1,
Partition::rootA => 2,
p @ (Partition::factory | Partition::cert) => {
let re = Regex::new(r"Disklabel type: (\D{3})").unwrap();

let matches = re
.captures(&fdisk_out)
.context("get_partition_info: regex no matches found")?;
anyhow::ensure!(
matches.len() == 2,
"'get_partition_info: regex contains unexpected number of matches"
);

let partition_type = &matches[1];

debug!("partition type: {partition_type}");

match (p, partition_type) {
(Partition::factory, "gpt") => 4,
(Partition::factory, "dos") => 5,
(Partition::cert, "gpt") => 5,
(Partition::cert, "dos") => 6,
_ => anyhow::bail!("get_partition_info: unhandled partition type"),
}
Partition::factory => {
if gpt { 4 } else { 5 }
}
Partition::cert => {
if gpt { 5 } else { 6 }
}
};

let re = Regex::new(format!(r"{image_file}{partition_num}\s+(\d+)\s+(\d+)").as_str())
.context("get_partition_info: failed to create regex")?;

let matches = re
.captures(&fdisk_out)
.context("get_partition_info: regex no matches found")?;
anyhow::ensure!(
matches.len() == 3,
"'get_partition_info: regex contains unexpected number of matches"
);
debug!("get_partition_info: partition={partition}, num={partition_num}, gpt={gpt}");

let partition_offset = (matches[1].to_string(), matches[2].to_string());
let data = get_partition_data(image_file, partition_num)
.with_context(|| format!("get_partition_info: failed to read partition {partition_num}"))?;

Comment on lines +18 to +20
// Try GPT first (validates CRC32 — more robust than signature check)
if let Ok(gpt) = gptman::GPT::find_from(&mut file) {
let entry = &gpt[partition_num];

// Try GPT first (validates CRC32 — more robust than signature check)
if let Ok(gpt) = gptman::GPT::find_from(&mut file) {
let entry = &gpt[partition_num];
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants