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.
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
+28 -4
View File
@@ -108,6 +108,7 @@ pub fn unpack_payload(
output_info: &Path,
output_images: &Path,
no_output_images: bool,
skeleton: bool,
reader: &File,
payload_offset: u64,
payload_size: u64,
@@ -123,13 +124,30 @@ pub fn unpack_payload(
write_info(output_info, &header)?;
if !no_output_images {
fs::create_dir_all(output_images)
.with_context(|| format!("Failed to create directory: {output_images:?}"))?;
if skeleton {
for partition in &header.manifest.partitions {
let name = &partition.partition_name;
let path = util::path_join_single(output_images, format!("{name}.img"))?;
let size = partition
.new_partition_info
.as_ref()
.and_then(|info| info.size)
.ok_or_else(|| anyhow!("Size not found for partition: {name}"))?;
let file = File::create(&path)
.with_context(|| format!("Failed to create file: {path:?}"))?;
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");
}
fs::create_dir_all(output_images)
.with_context(|| format!("Failed to create directory: {output_images:?}"))?;
ota::extract_payload(
reader,
output_images,
@@ -146,6 +164,7 @@ pub fn unpack_payload(
cancel_signal,
)?;
}
}
Ok(())
}
@@ -165,6 +184,7 @@ fn unpack_subcommand(
&cli.output_info,
&cli.output_images,
cli.no_output_images,
cli.skeleton,
&reader,
0,
payload_size,
@@ -448,8 +468,12 @@ struct UnpackCli {
output_images: PathBuf,
/// Do not output images.
#[arg(long, conflicts_with = "output_images")]
#[arg(long, conflicts_with_all = ["output_images", "skeleton"])]
no_output_images: bool,
/// Only create empty sparse files for output images.
#[arg(long)]
skeleton: bool,
}
/// 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_images,
cli.no_output_payload_images,
cli.payload_skeleton,
&zip_reader.get_ref().0,
payload_offset,
payload_size,
@@ -625,8 +626,16 @@ struct UnpackCli {
output_payload_images: PathBuf,
/// 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,
/// Only create empty sparse files for output payload images.
#[arg(long, requires = "payload")]
payload_skeleton: bool,
}
/// Pack an OTA zip.