cli/payload: Add support for unpacking skeleton images

This is useful for getting properly sized (but empty) image files when
unpacking delta payloads.

Discussion: #582

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
This commit is contained in:
Andrew Gunnerson
2026-03-28 00:57:35 -04:00
parent 2120827755
commit 9b7f27e384
3 changed files with 55 additions and 22 deletions
+1 -1
View File
@@ -340,7 +340,7 @@ avbroot payload unpack -i <input payload>
This subcommand unpacks the payload header information to `payload.toml` and the partition images to the `payload_images` directory. This subcommand unpacks the payload header information to `payload.toml` and the partition images to the `payload_images` directory.
Only full payload binaries can be unpacked. Delta payload binaries from incremental OTAs are not supported. Data can only be unpacked from full payload binaries. For delta payloads, either use `--no-output-images` to skip image extraction or use `--skeleton` to create properly sized sparse files with no data.
### Packing a payload binary ### Packing a payload binary
+44 -20
View File
@@ -108,6 +108,7 @@ pub fn unpack_payload(
output_info: &Path, output_info: &Path,
output_images: &Path, output_images: &Path,
no_output_images: bool, no_output_images: bool,
skeleton: bool,
reader: &File, reader: &File,
payload_offset: u64, payload_offset: u64,
payload_size: u64, payload_size: u64,
@@ -123,28 +124,46 @@ pub fn unpack_payload(
write_info(output_info, &header)?; write_info(output_info, &header)?;
if !no_output_images { if !no_output_images {
if !header.is_full_ota() {
bail!("Cannot extract images from a delta payload");
}
fs::create_dir_all(output_images) fs::create_dir_all(output_images)
.with_context(|| format!("Failed to create directory: {output_images:?}"))?; .with_context(|| format!("Failed to create directory: {output_images:?}"))?;
ota::extract_payload( if skeleton {
reader, for partition in &header.manifest.partitions {
output_images, let name = &partition.partition_name;
payload_offset, let path = util::path_join_single(output_images, format!("{name}.img"))?;
payload_size, let size = partition
&header, .new_partition_info
&header .as_ref()
.manifest .and_then(|info| info.size)
.partitions .ok_or_else(|| anyhow!("Size not found for partition: {name}"))?;
.iter()
.map(|p| &p.partition_name) let file = File::create(&path)
.cloned() .with_context(|| format!("Failed to create file: {path:?}"))?;
.collect(),
cancel_signal, file.set_len(size)
)?; .with_context(|| format!("Failed to truncate file: {path:?}"))?;
}
} else {
if !header.is_full_ota() {
bail!("Cannot extract images from a delta payload");
}
ota::extract_payload(
reader,
output_images,
payload_offset,
payload_size,
&header,
&header
.manifest
.partitions
.iter()
.map(|p| &p.partition_name)
.cloned()
.collect(),
cancel_signal,
)?;
}
} }
Ok(()) Ok(())
@@ -165,6 +184,7 @@ fn unpack_subcommand(
&cli.output_info, &cli.output_info,
&cli.output_images, &cli.output_images,
cli.no_output_images, cli.no_output_images,
cli.skeleton,
&reader, &reader,
0, 0,
payload_size, payload_size,
@@ -448,8 +468,12 @@ struct UnpackCli {
output_images: PathBuf, output_images: PathBuf,
/// Do not output images. /// Do not output images.
#[arg(long, conflicts_with = "output_images")] #[arg(long, conflicts_with_all = ["output_images", "skeleton"])]
no_output_images: bool, no_output_images: bool,
/// Only create empty sparse files for output images.
#[arg(long)]
skeleton: bool,
} }
/// Pack a payload binary. /// Pack a payload binary.
+10 -1
View File
@@ -303,6 +303,7 @@ fn unpack_subcommand(zip_cli: &ZipCli, cli: &UnpackCli, cancel_signal: &AtomicBo
&cli.output_payload_info, &cli.output_payload_info,
&cli.output_payload_images, &cli.output_payload_images,
cli.no_output_payload_images, cli.no_output_payload_images,
cli.payload_skeleton,
&zip_reader.get_ref().0, &zip_reader.get_ref().0,
payload_offset, payload_offset,
payload_size, payload_size,
@@ -625,8 +626,16 @@ struct UnpackCli {
output_payload_images: PathBuf, output_payload_images: PathBuf,
/// Do not output payload images. /// Do not output payload images.
#[arg(long, conflicts_with = "output_payload_images", requires = "payload")] #[arg(
long,
conflicts_with_all = ["output_payload_images", "payload_skeleton"],
requires = "payload"
)]
no_output_payload_images: bool, no_output_payload_images: bool,
/// Only create empty sparse files for output payload images.
#[arg(long, requires = "payload")]
payload_skeleton: bool,
} }
/// Pack an OTA zip. /// Pack an OTA zip.