Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 100 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ filemagic = { version = "0.13", default-features = false, features = [
"pkg-config",
] }
flate2 = { version = "1.1", default-features = false }
gptman = { version = "1.1", default-features = false }
mbrman = { version = "0.5", default-features = false }
omnect-crypto = { git = "https://github.com/omnect/omnect-crypto.git", tag = "0.4.0" }
keyring = { version = "3.6", default-features = false }
libfs = { version = "0.9", default-features = false }
Expand Down Expand Up @@ -85,5 +87,5 @@ tar = "0.4"

# metadata for building with cargo-deb (https://crates.io/crates/cargo-deb)
[package.metadata.deb]
depends = "bmap-tools, e2tools, fdisk, keychain, libc6 (>= 2.34), libmagic1, libssl3 (>= 3.0.0), mtools"
depends = "bmap-tools, e2tools, keychain, libc6 (>= 2.34), libmagic1, libssl3 (>= 3.0.0), mtools"
revision = ""
71 changes: 21 additions & 50 deletions src/file/functions.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use anyhow::{Context, Result};
use log::{debug, warn};
use regex::Regex;
use std::collections::HashMap;
use std::fmt::{self, Display};
use std::fs;
Expand All @@ -22,9 +21,9 @@ pub enum Partition {

#[derive(Debug)]
struct PartitionInfo {
num: String,
start: String,
end: String,
num: u32,
start: u64,
count: u64,
}

impl Display for Partition {
Expand Down Expand Up @@ -381,59 +380,31 @@ pub fn read_file_from_image(
}

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 thread
HarryWaschkeit marked this conversation as resolved.
Outdated
Comment thread
HarryWaschkeit marked this conversation as resolved.

Comment thread
HarryWaschkeit marked this conversation as resolved.
let info = PartitionInfo {
num: partition_num.to_string(),
start: partition_offset.0,
end: partition_offset.1,
num: data.num,
start: data.start,
count: data.count,
};

debug!("get_partition_info: {:?}", info);
Expand All @@ -455,7 +426,7 @@ fn read_partition(
.arg(format!("of={partition_file}"))
.arg("bs=512")
.arg(format!("skip={}", partition_info.start))
.arg(format!("count={}", partition_info.end))
.arg(format!("count={}", partition_info.count))
.arg("conv=sparse")
.arg("status=none");
exec_cmd!(dd);
Expand All @@ -476,7 +447,7 @@ fn write_partition(
.arg(format!("of={image_file}"))
.arg("bs=512")
.arg(format!("seek={}", partition_info.start))
.arg(format!("count={}", partition_info.end))
.arg(format!("count={}", partition_info.count))
.arg("conv=notrunc,sparse")
.arg("status=none");
exec_cmd!(dd);
Expand Down
1 change: 1 addition & 0 deletions src/file/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod compression;
pub mod functions;
mod partition;
use super::validators::{
device_update,
identity::{IdentityConfig, IdentityType, validate_identity},
Expand Down
59 changes: 59 additions & 0 deletions src/file/partition.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use anyhow::{Context, Result};
use std::fs::File;
use std::io::SeekFrom;
use std::io::Seek;
use std::path::Path;

pub struct PartitionData {
pub num: u32,
pub start: u64,
pub count: u64,
}

pub fn get_partition_data<P: AsRef<Path>>(path: P, partition_num: u32) -> Result<PartitionData> {
let path = path.as_ref();
let mut file = File::open(path)
.with_context(|| format!("failed to open image: {}", path.display()))?;

// 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];
Comment thread
HarryWaschkeit marked this conversation as resolved.
Outdated
Comment thread
HarryWaschkeit marked this conversation as resolved.
Outdated
anyhow::ensure!(entry.is_used(), "GPT partition {partition_num} is not used");
anyhow::ensure!(
entry.ending_lba >= entry.starting_lba,
"GPT partition {partition_num} has invalid LBA range (ending_lba < starting_lba)"
);
let count = entry
.ending_lba
.checked_sub(entry.starting_lba)
.and_then(|v| v.checked_add(1))
.context("GPT partition {partition_num} has an LBA range that overflows u64")?;
Comment thread
HarryWaschkeit marked this conversation as resolved.
Outdated
return Ok(PartitionData {
num: partition_num,
start: entry.starting_lba,
count,
});
}

// Try MBR — iter() includes logical partitions (5, 6, ...)
file.seek(SeekFrom::Start(0))
.context("failed to seek to start of image")?;
let mbr = mbrman::MBR::read_from(&mut file, 512)
.context("image is neither valid GPT nor MBR")?;
for (i, p) in mbr.iter() {
if i == partition_num as usize && p.is_used() {
return Ok(PartitionData {
num: partition_num,
start: p.starting_lba as u64,
Comment thread
JanZachmann marked this conversation as resolved.
count: p.sectors as u64,
});
Comment thread
HarryWaschkeit marked this conversation as resolved.
}
}
anyhow::bail!("partition {partition_num} not found in image")
}
Comment thread
HarryWaschkeit marked this conversation as resolved.
Comment thread
JanZachmann marked this conversation as resolved.

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 thread
HarryWaschkeit marked this conversation as resolved.
Comment thread
JanZachmann marked this conversation as resolved.
}
Loading