Compare commits

..

11 Commits

Author SHA1 Message Date
Andrew Gunnerson 3f09a506a0 Version 3.14.0
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2025-03-23 19:27:05 -04:00
Andrew Gunnerson 4664f8ea37 CHANGELOG.md: Add entry for PR #435
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2025-03-23 19:26:29 -04:00
Andrew Gunnerson 796e2a4fa2 Update dependencies
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2025-03-23 19:17:15 -04:00
Andrew Gunnerson e6b60d5d0f CHANGELOG.md: Add entry for PR #434
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2025-03-23 19:02:39 -04:00
Andrew Gunnerson e8cb4a8d53 Fix incorrect compression input for gzip CoW size estimation
Instead of compressing the 64 MiB input in 2 MiB chunks, each loop
iteration was compressing the full 64 MiB. This massively slowed down
the patching process from seconds to potentially hours and would
temporarily waste a bunch of space during OTA installation.

Fixes: #433

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2025-03-23 18:50:44 -04:00
Andrew Gunnerson b2d280eb20 CHANGELOG.md: Add entry for PR #430
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2025-03-13 20:23:48 -04:00
Andrew Gunnerson e2dc5174b4 format/ota: Decouple OTA signature parsing from verification
This way, we can fail hard for parsing errors, but not for verification
errors in `avbroot ota verify`.

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2025-03-13 20:22:16 -04:00
Andrew Gunnerson 80c47e9a02 CHANGELOG.md: Add entry for PR #429
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2025-03-13 19:49:54 -04:00
Andrew Gunnerson 65ba3ad5cc Fix clippy 1.85 warnings
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2025-03-13 19:48:32 -04:00
Andrew Gunnerson a7438876ce CHANGELOG.md: Add entry for PR #428
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2025-03-13 19:38:16 -04:00
Andrew Gunnerson c039901a85 cli/ota: Improve ota verify otacerts.zip handling
* Try to avoid fail-fast behavior to show as many errors as possible.
  Parsing errors always fail immediately, but verification errors don't.
* Move the recovery otacerts.zip check to the end to let more important
  checks run first.
* Improve error message when otacerts.zip does not contain the signing
  certificate for the OTA to make it clear the issue is not that the zip
  contains no certificates at all.
* Always run the recovery otacerts.zip check, but just log the error as
  a warning when running with --skip-recovery-ota-cert.
* Fix unformatted error context string when parsing a boot image's
  otacerts.zip file fails.

Discussion: #426

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2025-03-13 19:34:42 -04:00
17 changed files with 330 additions and 222 deletions
+3 -4
View File
@@ -36,8 +36,7 @@ jobs:
- aarch64-apple-darwin
- x86_64-apple-darwin
combine: lipo
# ubuntu-latest is not 24.04 yet and 22.04's qemu-user-static segfaults.
- os: ubuntu-24.04
- os: ubuntu-latest
name: aarch64-linux-android31
targets:
- aarch64-linux-android
@@ -86,7 +85,7 @@ jobs:
done
- name: Cache Rust dependencies
uses: Swatinem/rust-cache@f0deed1e0edfc6a9be95417288c0e1099b1eeec3 # v2.7.7
uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
with:
key: ${{ matrix.artifact.name }}
@@ -157,7 +156,7 @@ jobs:
run: cp LICENSE README.md target/output/
- name: Archive executable
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: avbroot-${{ steps.get_version.outputs.version }}-${{ matrix.artifact.name }}
path: |
+14
View File
@@ -7,6 +7,13 @@
to update the actual links at the bottom of the file.
-->
### Version 3.14.0
* Report as many errors as possible before failing in `avbroot ota verify` and improve error messages ([Discussion #426], [PR #428], [PR #430])
* Fix new clippy warnings introduced in Rust 1.85 ([PR #429])
* Fix massive performance regression introduced in 3.13.0 for OTAs that use gzip for virtual A/B CoW compression ([Issue #433], [PR #434])
* Update dependencies ([PR #435])
### Version 3.13.0
* Fix parsing Samsung `super.img` files in `avbroot lp` due to Samsung putting their own data structures in a region that's supposed to be filled with zeros ([PR #415])
@@ -294,6 +301,7 @@ Behind-the-scenes changes:
[Discussion #286]: https://github.com/chenxiaolong/avbroot/discussions/286
[Discussion #294]: https://github.com/chenxiaolong/avbroot/discussions/294
[Discussion #417]: https://github.com/chenxiaolong/avbroot/discussions/417
[Discussion #426]: https://github.com/chenxiaolong/avbroot/discussions/426
[Issue #138]: https://github.com/chenxiaolong/avbroot/issues/138
[Issue #144]: https://github.com/chenxiaolong/avbroot/issues/144
[Issue #145]: https://github.com/chenxiaolong/avbroot/issues/145
@@ -320,6 +328,7 @@ Behind-the-scenes changes:
[Issue #356]: https://github.com/chenxiaolong/avbroot/issues/356
[Issue #366]: https://github.com/chenxiaolong/avbroot/issues/366
[Issue #393]: https://github.com/chenxiaolong/avbroot/issues/393
[Issue #433]: https://github.com/chenxiaolong/avbroot/issues/433
[PR #130]: https://github.com/chenxiaolong/avbroot/pull/130
[PR #132]: https://github.com/chenxiaolong/avbroot/pull/132
[PR #133]: https://github.com/chenxiaolong/avbroot/pull/133
@@ -466,3 +475,8 @@ Behind-the-scenes changes:
[PR #424]: https://github.com/chenxiaolong/avbroot/pull/424
[PR #425]: https://github.com/chenxiaolong/avbroot/pull/425
[PR #427]: https://github.com/chenxiaolong/avbroot/pull/427
[PR #428]: https://github.com/chenxiaolong/avbroot/pull/428
[PR #429]: https://github.com/chenxiaolong/avbroot/pull/429
[PR #430]: https://github.com/chenxiaolong/avbroot/pull/430
[PR #434]: https://github.com/chenxiaolong/avbroot/pull/434
[PR #435]: https://github.com/chenxiaolong/avbroot/pull/435
Generated
+51 -45
View File
@@ -110,7 +110,7 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "avbroot"
version = "3.13.0"
version = "3.14.0"
dependencies = [
"anyhow",
"assert_matches",
@@ -148,7 +148,7 @@ dependencies = [
"rayon",
"regex",
"rsa",
"rustix 0.38.44",
"rustix 1.0.3",
"serde",
"sha1",
"sha2",
@@ -176,9 +176,9 @@ dependencies = [
[[package]]
name = "aws-lc-sys"
version = "0.27.0"
version = "0.27.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bbe221bbf523b625a4dd8585c7f38166e31167ec2ca98051dbcb4c3b6e825d2"
checksum = "77926887776171ced7d662120a75998e444d3750c951abfe07f90da130514b1f"
dependencies = [
"bindgen",
"cc",
@@ -195,9 +195,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "base64ct"
version = "1.7.1"
version = "1.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb97d56060ee67d285efb8001fec9d2a4c710c32efd2e14b5cbb5ba71930fc2d"
checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3"
[[package]]
name = "beef"
@@ -350,9 +350,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.2.16"
version = "1.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c"
checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a"
dependencies = [
"jobserver",
"libc",
@@ -425,9 +425,9 @@ dependencies = [
[[package]]
name = "clap_complete"
version = "4.5.46"
version = "4.5.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5c5508ea23c5366f77e53f5a0070e5a84e51687ec3ef9e0464c86dc8d13ce98"
checksum = "c06f5378ea264ad4f82bbc826628b5aad714a75abf6ece087e923010eb937fb6"
dependencies = [
"clap",
]
@@ -637,7 +637,7 @@ checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
[[package]]
name = "e2e"
version = "3.13.0"
version = "3.14.0"
dependencies = [
"anyhow",
"avbroot",
@@ -726,7 +726,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94e7099f6313ecacbe1256e8ff9d617b75d1bcb16a6fddef94866d225a01a14a"
dependencies = [
"io-lifetimes",
"rustix 1.0.2",
"rustix 1.0.3",
"windows-sys",
]
@@ -738,7 +738,7 @@ checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
[[package]]
name = "fuzz"
version = "3.13.0"
version = "3.14.0"
dependencies = [
"avbroot",
"honggfuzz",
@@ -767,14 +767,14 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.3.1"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0"
dependencies = [
"cfg-if",
"libc",
"wasi 0.13.3+wasi-0.2.2",
"windows-targets",
"r-efi",
"wasi 0.14.2+wasi-0.2.4",
]
[[package]]
@@ -847,14 +847,15 @@ dependencies = [
[[package]]
name = "honggfuzz"
version = "0.5.56"
version = "0.5.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c76b6234c13c9ea73946d1379d33186151148e0da231506b964b44f3d023505"
checksum = "fc563d4f41b17364d5c48ded509f2bcf1c3f6ae9c7f203055b4a5c325072d57e"
dependencies = [
"arbitrary",
"lazy_static",
"memmap2",
"rustc_version",
"semver",
]
[[package]]
@@ -1024,9 +1025,9 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
[[package]]
name = "linux-raw-sys"
version = "0.9.2"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db9c683daf087dc577b7506e9695b3d556a9f3849903fa28186283afd6809e9"
checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413"
[[package]]
name = "log"
@@ -1223,9 +1224,9 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.21.0"
version = "1.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cde51589ab56b20a6f686b2c68f7a0bd6add753d697abf720d63f8db3ab7b1ad"
checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc"
[[package]]
name = "overload"
@@ -1374,9 +1375,9 @@ dependencies = [
[[package]]
name = "prettyplease"
version = "0.2.30"
version = "0.2.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1ccf34da56fc294e7d4ccf69a85992b7dfb826b7cf57bac6a70bba3494cc08a"
checksum = "5316f57387668042f561aae71480de936257848f9c43ce528e311d89a07cadeb"
dependencies = [
"proc-macro2",
"syn 2.0.100",
@@ -1492,6 +1493,12 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "5.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
[[package]]
name = "rand"
version = "0.8.5"
@@ -1625,14 +1632,14 @@ dependencies = [
[[package]]
name = "rustix"
version = "1.0.2"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7178faa4b75a30e269c71e61c353ce2748cf3d76f0c44c393f4e60abf49b825"
checksum = "e56a18552996ac8d29ecc3b190b4fdbb2d91ca4ec396de7bbffaf43f3d637e96"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys 0.9.2",
"linux-raw-sys 0.9.3",
"windows-sys",
]
@@ -1814,15 +1821,14 @@ dependencies = [
[[package]]
name = "tempfile"
version = "3.18.0"
version = "3.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c317e0a526ee6120d8dabad239c8dadca62b24b6f168914bbbc8e2fb1f0e567"
checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf"
dependencies = [
"cfg-if",
"fastrand",
"getrandom 0.3.1",
"getrandom 0.3.2",
"once_cell",
"rustix 1.0.2",
"rustix 1.0.3",
"windows-sys",
]
@@ -2018,11 +2024,11 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "uuid"
version = "1.15.1"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0f540e3240398cce6128b64ba83fdbdd86129c16a3aa1a3a252efd66eb3d587"
checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9"
dependencies = [
"getrandom 0.3.1",
"getrandom 0.3.2",
]
[[package]]
@@ -2045,9 +2051,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasi"
version = "0.13.3+wasi-0.2.2"
version = "0.14.2+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
dependencies = [
"wit-bindgen-rt",
]
@@ -2180,9 +2186,9 @@ dependencies = [
[[package]]
name = "wit-bindgen-rt"
version = "0.33.0"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
dependencies = [
"bitflags",
]
@@ -2203,7 +2209,7 @@ dependencies = [
[[package]]
name = "xtask"
version = "3.13.0"
version = "3.14.0"
dependencies = [
"anyhow",
"clap",
@@ -2213,18 +2219,18 @@ dependencies = [
[[package]]
name = "zerocopy"
version = "0.8.23"
version = "0.8.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd97444d05a4328b90e75e503a34bad781f14e28a823ad3557f0750df1ebcbc6"
checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.23"
version = "0.8.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6352c01d0edd5db859a63e2605f4ea3183ddbd15e2c4a9e7d32184df75e4f154"
checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be"
dependencies = [
"proc-macro2",
"quote",
+1 -1
View File
@@ -4,7 +4,7 @@ members = ["avbroot", "e2e", "fuzz", "xtask"]
resolver = "2"
[workspace.package]
version = "3.13.0"
version = "3.14.0"
license = "GPL-3.0-only"
edition = "2021"
repository = "https://github.com/chenxiaolong/avbroot"
+3 -1
View File
@@ -24,6 +24,8 @@ cap-tempfile = "3.0.0"
clap = { version = "4.4.1", features = ["derive"] }
clap_complete = "4.4.0"
cms = { version = "0.2.2", features = ["std"] }
# We can't upgrade to 0.10.0 until x509-cert updates it too, since it's part of
# the public API.
const-oid = "0.9.5"
crc32fast = "1.4.2"
ctrlc = "3.4.0"
@@ -67,7 +69,7 @@ features = ["deflate"]
[target.'cfg(unix)'.dependencies]
libc = "0.2.158"
rustix = { version = "0.38.9", default-features = false, features = ["process"] }
rustix = { version = "1.0.3", default-features = false, features = ["process"] }
[build-dependencies]
constcat = "0.6.0"
+102 -53
View File
@@ -20,7 +20,7 @@ use clap::{value_parser, ArgAction, Args, Parser, Subcommand};
use rayon::{iter::IntoParallelRefIterator, prelude::ParallelIterator};
use tempfile::NamedTempFile;
use topological_sort::TopologicalSort;
use tracing::{debug_span, info, warn};
use tracing::{debug_span, error, info, warn};
use x509_cert::Certificate;
use zip::{write::FileOptions, CompressionMethod, ZipArchive, ZipWriter};
@@ -683,8 +683,6 @@ pub fn compress_image(
// update the CoW size estimate or else the CoW block device may run out of
// space during flashing.
let vabc_algo = if partition.estimate_cow_size.is_some() {
info!("Needs updated CoW size estimate: {name}");
// Only CoW v2 seems to exist in the wild currently, so that is all we
// support.
let Some(dpm) = &header.manifest.dynamic_partition_metadata else {
@@ -705,6 +703,8 @@ pub fn compress_image(
bail!("Unsupported VABC compression: {compression}");
};
info!("Needs updated {vabc_algo} CoW size estimate: {name}");
Some(vabc_algo)
} else {
None
@@ -1505,9 +1505,8 @@ pub fn extract_subcommand(cli: &ExtractCli, cancel_signal: &AtomicBool) -> Resul
info!("Extracting embedded OTA certificate from zip signature");
let ota_sig = ota::parse_ota_sig(&mut raw_reader)?;
let embedded_cert = ota_sig.embedded_cert()?;
crypto::write_pem_cert_file(path, embedded_cert)
crypto::write_pem_cert_file(path, &ota_sig.cert)
.with_context(|| format!("Failed to write OTA certificate: {path:?}"))?;
}
@@ -1690,6 +1689,15 @@ pub fn extract_subcommand(cli: &ExtractCli, cancel_signal: &AtomicBool) -> Resul
}
pub fn verify_subcommand(cli: &VerifyCli, cancel_signal: &AtomicBool) -> Result<()> {
let mut errors = 0;
macro_rules! fail_later {
($($arg:tt)+) => {
error!($($arg)+);
errors += 1;
};
}
let raw_reader = File::open(&cli.input)
.map(PSeekFile::new)
.with_context(|| format!("Failed to open for reading: {:?}", cli.input))?;
@@ -1697,27 +1705,38 @@ pub fn verify_subcommand(cli: &VerifyCli, cancel_signal: &AtomicBool) -> Result<
info!("Verifying whole-file signature");
let embedded_cert = ota::verify_ota(&mut reader, cancel_signal)?;
let ota_sig = ota::parse_ota_sig(&mut reader).context("Failed to parse OTA signature")?;
let (metadata, ota_cert, header, properties) = ota::parse_zip_ota_info(&mut reader)?;
if embedded_cert != ota_cert {
bail!(
"CMS embedded certificate does not match {}",
if let Err(e) = ota_sig
.verify_ota(&mut reader, cancel_signal)
.context("Failed to verify OTA against embedded certificate")
{
fail_later!("{e:?}");
}
let (metadata, ota_cert, header, properties) =
ota::parse_zip_ota_info(&mut reader).context("Failed to parse OTA metadata")?;
if ota_cert != ota_sig.cert {
fail_later!(
"{} does not match CMS embedded certificate",
ota::PATH_OTACERT,
);
} else if let Some(p) = &cli.cert_ota {
let verify_cert = crypto::read_pem_cert_file(p)
.with_context(|| format!("Failed to load certificate: {p:?}"))?;
if embedded_cert != verify_cert {
bail!("OTA has a valid signature, but was not signed with: {p:?}");
if ota_sig.cert != verify_cert {
fail_later!("OTA has a valid signature, but was not signed with: {p:?}");
}
} else {
warn!("Whole-file signature is valid, but its trust is unknown");
}
ota::verify_metadata(&mut reader, &metadata, header.blob_offset)
.context("Failed to verify OTA metadata offsets")?;
if let Err(e) = ota::verify_metadata(&mut reader, &metadata, header.blob_offset)
.context("Failed to verify OTA metadata offsets")
{
fail_later!("{e:?}");
}
info!("Verifying payload");
@@ -1735,7 +1754,12 @@ pub fn verify_subcommand(cli: &VerifyCli, cancel_signal: &AtomicBool) -> Result<
let section_reader = SectionReader::new(&mut reader, pf_payload.offset, pf_payload.size)
.context("Failed to directly open payload section")?;
payload::verify_payload(section_reader, &ota_cert, &properties, cancel_signal)?;
if let Err(e) =
payload::verify_payload(section_reader, &ota_sig.cert, &properties, cancel_signal)
.context("Failed to verify payload signatures and digests")
{
fail_later!("{e:?}");
}
info!("Extracting partition images to temporary directory");
@@ -1762,40 +1786,8 @@ pub fn verify_subcommand(cli: &VerifyCli, cancel_signal: &AtomicBool) -> Result<
info!("Verifying partition hashes");
verify_partition_hashes(&temp_dir, &header, &unique_images, cancel_signal)?;
if cli.skip_recovery_ota_cert {
warn!("Not verifying recovery ramdisk's otacerts.zip");
} else {
info!("Checking recovery ramdisk's otacerts.zip");
let required_images = RequiredImages::new(&header.manifest);
let boot_images =
boot::load_boot_images(&required_images.iter_boot().collect::<Vec<_>>(), |name| {
Ok(Box::new(
temp_dir
.open(format!("{name}.img"))
.map(|f| PSeekFile::new(f.into_std()))?,
))
})
.context("Failed to load all boot images")?;
let targets = OtaCertPatcher::new(ota_cert.clone())
.find_targets(&boot_images, cancel_signal)
.context("Failed to find boot image containing otacerts.zip")?;
if targets.is_empty() {
bail!("No boot image contains otacerts.zip");
}
for target in targets {
let boot_image = &boot_images[target].boot_image;
let ramdisk_certs = OtaCertPatcher::get_certificates(boot_image, cancel_signal)
.context("Failed to read {target}'s otacerts.zip")?;
if !ramdisk_certs.contains(&ota_cert) {
bail!("{target}'s otacerts.zip does not contain OTA certificate");
}
}
if let Err(e) = verify_partition_hashes(&temp_dir, &header, &unique_images, cancel_signal) {
fail_later!("{e:?}");
}
info!("Verifying AVB signatures");
@@ -1813,16 +1805,73 @@ pub fn verify_subcommand(cli: &VerifyCli, cancel_signal: &AtomicBool) -> Result<
let mut seen = HashSet::<String>::new();
let mut descriptors = HashMap::<String, Descriptor>::new();
cli::avb::verify_headers(
if let Err(e) = cli::avb::verify_headers(
&temp_dir,
"vbmeta",
public_key.as_ref(),
&mut seen,
&mut descriptors,
)?;
cli::avb::verify_descriptors(&temp_dir, &descriptors, false, cancel_signal)?;
)
.context("Failed to verify AVB signatures")
{
fail_later!("{e:?}");
}
info!("Signatures are all valid!");
if let Err(e) = cli::avb::verify_descriptors(&temp_dir, &descriptors, false, cancel_signal)
.context("Failed to verify images against AVB descriptors")
{
fail_later!("{e:?}");
}
info!("Checking recovery ramdisk's otacerts.zip");
let required_images = RequiredImages::new(&header.manifest);
let boot_images =
boot::load_boot_images(&required_images.iter_boot().collect::<Vec<_>>(), |name| {
Ok(Box::new(
temp_dir
.open(format!("{name}.img"))
.map(|f| PSeekFile::new(f.into_std()))?,
))
})
.context("Failed to load all boot images")?;
let targets = OtaCertPatcher::new(ota_cert.clone())
.find_targets(&boot_images, cancel_signal)
.context("Failed to find boot image containing otacerts.zip")?;
if targets.is_empty() {
let msg = "No boot image contains otacerts.zip";
if cli.skip_recovery_ota_cert {
warn!("{msg}");
} else {
fail_later!("{msg}");
}
}
for target in targets {
let boot_image = &boot_images[target].boot_image;
let ramdisk_certs = OtaCertPatcher::get_certificates(boot_image, cancel_signal)
.with_context(|| format!("Failed to read {target}'s otacerts.zip"))?;
if !ramdisk_certs.contains(&ota_cert) {
let msg = format!(
"{target}'s otacerts.zip does not contain the certificate that signed the OTA"
);
if cli.skip_recovery_ota_cert {
warn!("{msg}");
} else {
fail_later!("{msg}");
}
}
}
if errors == 0 {
info!("OK!");
} else {
bail!("Encountered {errors} error(s) during verification");
}
Ok(())
}
+2 -2
View File
@@ -122,13 +122,13 @@ fn find_allocated_regions(
loop {
stream::check_cancel(cancel_signal)?;
start = match rustix::fs::seek(reader, SeekFrom::Data(end as i64)) {
start = match rustix::fs::seek(reader, SeekFrom::Data(end)) {
Ok(offset) => offset,
Err(e) if e == Errno::NXIO => break,
Err(e) => return Err(e).with_context(|| format!("Failed to seek to data: {path:?}")),
};
end = rustix::fs::seek(reader, SeekFrom::Hole(start as i64))
end = rustix::fs::seek(reader, SeekFrom::Hole(start))
.with_context(|| format!("Failed to seek to hole: {path:?}"))?;
result.push(start..end);
+9 -10
View File
@@ -292,7 +292,7 @@ trait DescriptorTag {
/// Raw on-disk layout for the AVB property descriptor after the prefix.
#[derive(Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
#[repr(packed)]
#[repr(C, packed)]
struct RawPropertyDescriptor {
key_size: big_endian::U64,
value_size: big_endian::U64,
@@ -395,7 +395,7 @@ impl<W: Write> ToWriter<W> for PropertyDescriptor {
/// Raw on-disk layout for the AVB hash tree descriptor after the prefix.
#[derive(Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
#[repr(packed)]
#[repr(C, packed)]
struct RawHashTreeDescriptor {
dm_verity_version: big_endian::U32,
image_size: big_endian::U64,
@@ -860,7 +860,7 @@ impl<W: Write> ToWriter<W> for HashTreeDescriptor {
/// Raw on-disk layout for the AVB hash descriptor after the prefix.
#[derive(Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
#[repr(packed)]
#[repr(C, packed)]
struct RawHashDescriptor {
image_size: big_endian::U64,
hash_algorithm: [u8; 32],
@@ -1065,7 +1065,7 @@ impl<W: Write> ToWriter<W> for HashDescriptor {
/// Raw on-disk layout for the AVB kernel command line descriptor after the
/// prefix.
#[derive(Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
#[repr(packed)]
#[repr(C, packed)]
struct RawKernelCmdlineDescriptor {
flags: big_endian::U32,
cmdline_len: big_endian::U32,
@@ -1139,7 +1139,7 @@ impl<W: Write> ToWriter<W> for KernelCmdlineDescriptor {
/// Raw on-disk layout for the AVB chain partition descriptor after the prefix.
#[derive(Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
#[repr(packed)]
#[repr(C, packed)]
struct RawChainPartitionDescriptor {
rollback_index_location: big_endian::U32,
partition_name_len: big_endian::U32,
@@ -1258,7 +1258,7 @@ impl<W: Write> ToWriter<W> for ChainPartitionDescriptor {
/// Raw on-disk layout for the AVB descriptor prefix.
#[derive(Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
#[repr(packed)]
#[repr(C, packed)]
struct RawDescriptor {
tag: big_endian::U64,
num_bytes_following: big_endian::U64,
@@ -1458,7 +1458,7 @@ impl<'a> TryFrom<&'a mut Descriptor> for AppendedDescriptorMut<'a> {
/// Raw on-disk layout for the AVB header.
#[derive(Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
#[repr(packed)]
#[repr(C, packed)]
struct RawHeader {
/// Magic value. This should be equal to [`HEADER_MAGIC`].
magic: [u8; 4],
@@ -1927,7 +1927,7 @@ impl<W: Write> ToWriter<W> for Header {
/// Raw on-disk layout for the AVB footer.
#[derive(Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
#[repr(packed)]
#[repr(C, packed)]
struct RawFooter {
/// Magic value. This should be equal to [`FOOTER_MAGIC`].
magic: [u8; 4],
@@ -2015,10 +2015,9 @@ impl<W: Write> ToWriter<W> for Footer {
/// Raw on-disk layout for the AVB binary public key header.
#[derive(Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
#[repr(packed)]
#[repr(C, packed)]
struct RawPublicKey {
key_num_bits: big_endian::U32,
#[expect(unused)]
n0inv: big_endian::U32,
}
+8 -8
View File
@@ -117,7 +117,7 @@ pub trait BootImageExt {
/// Raw on-disk layout for the v0 image header.
#[derive(Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
#[repr(packed)]
#[repr(C, packed)]
struct RawV0 {
/// Magic value. This should be equal to [`BOOT_MAGIC`].
magic: [u8; 8],
@@ -139,7 +139,7 @@ struct RawV0 {
/// Raw on-disk layout for the extra v1 image header fields.
#[derive(Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
#[repr(packed)]
#[repr(C, packed)]
struct RawV1Extra {
recovery_dtbo_size: little_endian::U32,
recovery_dtbo_offset: little_endian::U64,
@@ -148,7 +148,7 @@ struct RawV1Extra {
/// Raw on-disk layout for the extra v2 image header fields.
#[derive(Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
#[repr(packed)]
#[repr(C, packed)]
struct RawV2Extra {
dtb_size: little_endian::U32,
dtb_addr: little_endian::U64,
@@ -595,7 +595,7 @@ impl<W: Write> ToWriter<W> for BootImageV0Through2 {
/// Raw on-disk layout for the v3 image header.
#[derive(Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
#[repr(packed)]
#[repr(C, packed)]
struct RawV3 {
/// Magic value. This should be equal to [`BOOT_MAGIC`].
magic: [u8; 8],
@@ -610,7 +610,7 @@ struct RawV3 {
/// Raw on-disk layout for the extra v4 image header fields.
#[derive(Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
#[repr(packed)]
#[repr(C, packed)]
struct RawV4Extra {
signature_size: little_endian::U32,
}
@@ -961,7 +961,7 @@ impl<W: Write> ToWriter<W> for BootImageV3Through4 {
/// Raw on-disk layout for the vendor v3 image header.
#[derive(Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
#[repr(packed)]
#[repr(C, packed)]
struct RawVendorV3 {
/// Magic value. This should be equal to [`VENDOR_BOOT_MAGIC`].
magic: [u8; 8],
@@ -980,7 +980,7 @@ struct RawVendorV3 {
/// Raw on-disk layout for the extra vendor v4 image header fields.
#[derive(Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
#[repr(packed)]
#[repr(C, packed)]
struct RawVendorV4Extra {
vendor_ramdisk_table_size: little_endian::U32,
vendor_ramdisk_table_entry_num: little_endian::U32,
@@ -990,7 +990,7 @@ struct RawVendorV4Extra {
/// Raw on-disk layout for the vendor v4 ramdisk table entry.
#[derive(Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
#[repr(packed)]
#[repr(C, packed)]
struct RawVendorV4RamdiskTableEntry {
ramdisk_size: little_endian::U32,
ramdisk_offset: little_endian::U32,
+2 -2
View File
@@ -76,7 +76,7 @@ pub struct InvalidHexCharError(RawHexU32, char);
/// ASCII-encoded hex integer value used in cpio header fields.
#[derive(Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
#[repr(packed)]
#[repr(C, packed)]
struct RawHexU32([u8; 8]);
impl fmt::Debug for RawHexU32 {
@@ -121,7 +121,7 @@ impl TryFrom<RawHexU32> for u32 {
/// Raw on-disk layout for the cpio header.
#[derive(Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
#[repr(packed)]
#[repr(C, packed)]
struct RawHeader {
/// Magic value. This should be equal to [`MAGIC_NEW`] or [`MAGIC_NEW_CRC`].
magic: [u8; 6],
+1 -1
View File
@@ -592,7 +592,7 @@ impl Fec {
/// Raw on-disk layout for the FEC image header.
#[derive(Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
#[repr(packed)]
#[repr(C, packed)]
struct RawHeader {
/// Magic value. This should be equal to [`FEC_MAGIC`].
magic: little_endian::U32,
+1 -1
View File
@@ -431,7 +431,7 @@ impl HashTree {
/// Raw on-disk layout for our custom hash tree image header.
#[derive(Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
#[repr(packed)]
#[repr(C, packed)]
struct RawHeader {
/// Magic value. This should be equal to [`HashTreeImage::MAGIC`].
magic: [u8; 16],
+9 -9
View File
@@ -250,7 +250,7 @@ impl PartitionAttributes {
/// Raw on-disk layout for the metadata geometry.
#[derive(Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
#[repr(packed)]
#[repr(C, packed)]
struct RawGeometry {
/// Magic value. This should be equal to [`GEOMETRY_MAGIC`].
magic: little_endian::U32,
@@ -332,7 +332,7 @@ impl RawGeometry {
/// Raw on-disk layout for a table descriptor within a [`RawHeader`].
#[derive(Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
#[repr(packed)]
#[repr(C, packed)]
struct RawTableDescriptor {
/// Offset relative to the end of the [`RawHeader`].
offset: little_endian::U32,
@@ -391,7 +391,7 @@ impl RawTableDescriptor {
/// Raw on-disk layout for the metadata header.
#[derive(Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
#[repr(packed)]
#[repr(C, packed)]
struct RawHeader {
/// Magic value. This should be equal to [`HEADER_MAGIC`].
magic: little_endian::U32,
@@ -569,7 +569,7 @@ impl RawHeader {
/// A potentially invalid raw partition name string.
#[derive(Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
#[repr(packed)]
#[repr(C, packed)]
struct PartitionName([u8; 36]);
impl fmt::Debug for PartitionName {
@@ -639,7 +639,7 @@ impl FromStr for PartitionName {
/// Raw on-disk layout for an entry in the logical partitions table.
#[derive(Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
#[repr(packed)]
#[repr(C, packed)]
struct RawPartition {
/// Partition name in ASCII. This must be unique across all partitions.
name: PartitionName,
@@ -701,7 +701,7 @@ impl RawPartition {
.first_extent_index
.get()
.checked_add(self.num_extents.get())
.map_or(true, |n| n as usize > extents.len())
.is_none_or(|n| n as usize > extents.len())
{
return Err(Error::PartitionExtentIndicesTooLarge {
name: DebugString::new(self.name),
@@ -730,7 +730,7 @@ impl RawPartition {
/// Raw on-disk layout for an entry in the extent table.
#[derive(Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
#[repr(packed)]
#[repr(C, packed)]
struct RawExtent {
/// Number of [`SECTOR_SIZE`]-byte sectors in this extent.
num_sectors: little_endian::U64,
@@ -822,7 +822,7 @@ impl RawExtent {
/// Raw on-disk layout for an entry in the partition groups table.
#[derive(Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
#[repr(packed)]
#[repr(C, packed)]
struct RawPartitionGroup {
/// Partition group name in ASCII. This must be unique across all groups.
name: PartitionName,
@@ -889,7 +889,7 @@ impl RawPartitionGroup {
/// Raw on-disk layout for an entry in the block devices table.
#[derive(Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
#[repr(packed)]
#[repr(C, packed)]
struct RawBlockDevice {
/// The first [`SECTOR_SIZE`]-byte sector where actual data for the logical
/// partitions can be allocated.
+112 -75
View File
@@ -10,7 +10,7 @@ use std::{
sync::atomic::AtomicBool,
};
use aws_lc_rs::digest::Context;
use aws_lc_rs::digest::{Algorithm, Context};
use clap::ValueEnum;
use cms::signed_data::SignedData;
use const_oid::{db::rfc5912, ObjectIdentifier};
@@ -566,14 +566,14 @@ pub fn verify_metadata(
}
#[derive(Clone, Debug)]
pub struct OtaSignature {
struct RawOtaSignature {
/// Decoded CMS structure.
pub signed_data: SignedData,
signed_data: SignedData,
/// Length of the file (from the beginning) that's covered by the signature.
pub hashed_size: u64,
hashed_size: u64,
}
impl OtaSignature {
impl RawOtaSignature {
pub fn embedded_cert(&self) -> Result<&Certificate> {
let mut iter = crypto::iter_cms_certs(&self.signed_data);
@@ -589,9 +589,110 @@ impl OtaSignature {
}
}
#[derive(Clone, Debug)]
pub struct OtaSignature {
pub cert: Certificate,
pub digest_algo: &'static Algorithm,
pub sig_algo: SignatureAlgorithm,
pub sig: Vec<u8>,
pub data_size: u64,
}
impl TryFrom<RawOtaSignature> for OtaSignature {
type Error = Error;
fn try_from(raw_ota_sig: RawOtaSignature) -> Result<Self> {
let cert = raw_ota_sig.embedded_cert()?;
// Make sure this is a signature scheme we can handle. There's currently
// no Rust library to verify arbitrary CMS signatures for large files
// without fully reading them into memory.
let signers_len = raw_ota_sig.signed_data.signer_infos.0.len();
if signers_len != 1 {
return Err(Error::NotOneCmsSignerInfo(signers_len));
}
let signer = raw_ota_sig.signed_data.signer_infos.0.get(0).unwrap();
if signer.digest_alg.oid != rfc5912::ID_SHA_256
&& signer.digest_alg.oid != rfc5912::ID_SHA_1
{
return Err(Error::UnsupportedDigestAlgorithm(signer.digest_alg.oid));
} else if signer.signature_algorithm.oid != rfc5912::RSA_ENCRYPTION
&& signer.signature_algorithm.oid != rfc5912::SHA_256_WITH_RSA_ENCRYPTION
{
return Err(Error::UnsupportedSignatureAlgorithm(
signer.signature_algorithm.oid,
));
}
// We support SHA1 for verification only.
let (digest_algo, sig_algo) = if signer.digest_alg.oid == rfc5912::ID_SHA_256 {
(
&aws_lc_rs::digest::SHA256,
SignatureAlgorithm::Sha256WithRsa,
)
} else {
(
&aws_lc_rs::digest::SHA1_FOR_LEGACY_USE_ONLY,
SignatureAlgorithm::Sha1WithRsa,
)
};
Ok(Self {
cert: cert.clone(),
digest_algo,
sig_algo,
sig: signer.signature.as_bytes().to_vec(),
data_size: raw_ota_sig.hashed_size,
})
}
}
impl OtaSignature {
/// Verify an OTA zip against its embedded certificate. This function makes
/// no assertion about whether the certificate is actually trusted.
///
/// CMS signed attributes are intentionally not supported because AOSP
/// recovery does not support them either. It expects the CMS [`SignedData`]
/// structure to be used for nothing more than a raw signature transport
/// mechanism.
pub fn verify_ota(
&self,
mut reader: impl Read + Seek,
cancel_signal: &AtomicBool,
) -> Result<()> {
let public_key = crypto::get_public_key(&self.cert).map_err(Error::OtaCertExtractPubKey)?;
// Manually hash the parts of the file covered by the signature.
reader
.seek(SeekFrom::Start(0))
.map_err(|e| Error::DataRead("raw_data", e))?;
let mut hashing_reader = HashingReader::new(reader, Context::new(self.digest_algo));
stream::copy_n(
&mut hashing_reader,
io::sink(),
self.data_size,
cancel_signal,
)
.map_err(|e| Error::DataRead("raw_data", e))?;
let (_, context) = hashing_reader.finish();
let digest = context.finish();
// Verify the signature against the public key.
public_key
.verify_sig(self.sig_algo, digest.as_ref(), &self.sig)
.map_err(Error::CmsVerify)?;
Ok(())
}
}
/// Parse the CMS signature from the OTA zip comment. This does not perform any
/// parsing of zip data structures.
pub fn parse_ota_sig(mut reader: impl Read + Seek) -> Result<OtaSignature> {
fn parse_raw_ota_sig(mut reader: impl Read + Seek) -> Result<RawOtaSignature> {
let file_size = reader
.seek(SeekFrom::End(0))
.map_err(|e| Error::DataRead("file_size", e))?;
@@ -642,80 +743,16 @@ pub fn parse_ota_sig(mut reader: impl Read + Seek) -> Result<OtaSignature> {
// length field.
let hashed_size = file_size - 2 - u64::from(comment_size);
Ok(OtaSignature {
Ok(RawOtaSignature {
signed_data,
hashed_size,
})
}
/// Verify an OTA zip against its embedded certificates. This function makes no
/// assertion about whether the certificate is actually trusted. Returns the
/// embedded certificate.
///
/// CMS signed attributes are intentionally not supported because AOSP recovery
/// does not support them either. It expects the CMS [`SignedData`] structure to
/// be used for nothing more than a raw signature transport mechanism.
pub fn verify_ota(mut reader: impl Read + Seek, cancel_signal: &AtomicBool) -> Result<Certificate> {
let ota_sig = parse_ota_sig(&mut reader)?;
let cert = ota_sig.embedded_cert()?;
let public_key = crypto::get_public_key(cert).map_err(Error::OtaCertExtractPubKey)?;
// Make sure this is a signature scheme we can handle. There's currently no
// Rust library to verify arbitrary CMS signatures for large files without
// fully reading them into memory.
let signers_len = ota_sig.signed_data.signer_infos.0.len();
if signers_len != 1 {
return Err(Error::NotOneCmsSignerInfo(signers_len));
}
let signer = ota_sig.signed_data.signer_infos.0.get(0).unwrap();
if signer.digest_alg.oid != rfc5912::ID_SHA_256 && signer.digest_alg.oid != rfc5912::ID_SHA_1 {
return Err(Error::UnsupportedDigestAlgorithm(signer.digest_alg.oid));
} else if signer.signature_algorithm.oid != rfc5912::RSA_ENCRYPTION
&& signer.signature_algorithm.oid != rfc5912::SHA_256_WITH_RSA_ENCRYPTION
{
return Err(Error::UnsupportedSignatureAlgorithm(
signer.signature_algorithm.oid,
));
}
// Manually hash the parts of the file covered by the signature.
reader
.seek(SeekFrom::Start(0))
.map_err(|e| Error::DataRead("raw_data", e))?;
// We support SHA1 for verification only.
let (algorithm, algo) = if signer.digest_alg.oid == rfc5912::ID_SHA_256 {
(
&aws_lc_rs::digest::SHA256,
SignatureAlgorithm::Sha256WithRsa,
)
} else {
(
&aws_lc_rs::digest::SHA1_FOR_LEGACY_USE_ONLY,
SignatureAlgorithm::Sha1WithRsa,
)
};
let mut hashing_reader = HashingReader::new(reader, Context::new(algorithm));
stream::copy_n(
&mut hashing_reader,
io::sink(),
ota_sig.hashed_size,
cancel_signal,
)
.map_err(|e| Error::DataRead("raw_data", e))?;
let (_, context) = hashing_reader.finish();
let digest = context.finish();
// Verify the signature against the public key.
public_key
.verify_sig(algo, digest.as_ref(), signer.signature.as_bytes())
.map_err(Error::CmsVerify)?;
Ok(cert.clone())
/// Parse the signature information from the CMS signature embedded in the OTA
/// zip archive comment.
pub fn parse_ota_sig(reader: impl Read + Seek) -> Result<OtaSignature> {
parse_raw_ota_sig(reader)?.try_into()
}
/// Get and parse the protobuf-encoded OTA metadata, the PEM-encoded otacert,
+6 -4
View File
@@ -138,7 +138,7 @@ type Result<T> = std::result::Result<T, Error>;
/// Raw on-disk layout for the payload header.
#[derive(Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
#[repr(packed)]
#[repr(C, packed)]
struct RawHeader {
/// Magic value. This should be equal to [`PAYLOAD_MAGIC`].
magic: [u8; 4],
@@ -965,20 +965,22 @@ impl VabcAlgo {
while !raw_data.is_empty() {
let n = raw_data.len().min(block_size as usize);
let (chunk, remaining) = raw_data.split_at(n);
// This should match CompressWorker::GetDefaultCompressionLevel() in
// AOSP's libsnapshot.
let compressed = match self {
Self::Lz4 => lz4_flex::block::compress(&raw_data[..n]),
Self::Lz4 => lz4_flex::block::compress(chunk),
Self::Gzip => {
let mut encoder = GzEncoder::new(Vec::new(), Compression::best());
encoder.write_all(raw_data).map_err(Error::GzCompress)?;
encoder.write_all(chunk).map_err(Error::GzCompress)?;
encoder.finish().map_err(Error::GzCompress)?
}
};
total += compressed.len().min(n) as u64;
raw_data = &raw_data[n..];
raw_data = remaining;
}
Ok(total)
+2 -2
View File
@@ -106,7 +106,7 @@ type Result<T> = std::result::Result<T, Error>;
/// Raw on-disk layout for the header.
#[derive(Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
#[repr(packed)]
#[repr(C, packed)]
struct RawHeader {
/// Magic value. This should be equal to [`HEADER_MAGIC`].
magic: little_endian::U32,
@@ -183,7 +183,7 @@ impl RawHeader {
/// Raw on-disk layout for the chunk header.
#[derive(Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
#[repr(packed)]
#[repr(C, packed)]
struct RawChunk {
/// Chunk type. Must be [`CHUNK_TYPE_RAW`], [`CHUNK_TYPE_FILL`],
/// [`CHUNK_TYPE_DONT_CARE`], or [`CHUNK_TYPE_CRC32`].
+4 -4
View File
@@ -168,9 +168,9 @@ data.type = "vbmeta"
data.deps = ["system"]
[profile.pixel_v2.hashes_streaming]
original = "4b7e5675f834ac56bf3459628adfe1425c2346a1c224ee6ea0a3be9f996db254"
patched = "4a7ca99808b4e49a2dc77a620d4fb7d8219b974ab5b3d5b5709084942805dc9a"
original = "bd2f19cf3d2285e35e8b36d44f75ed910e8e0be44c3ebd29f17a812521ba754b"
patched = "cf65d5b90500af54cd1204a646379bb852825061bc7c3f973b7a042f353f75ad"
[profile.pixel_v2.hashes_seekable]
original = "66b44b148b35a8a998214e0ae36470b42ffd6d2974e18cd8fa12cd8af0c98540"
patched = "5ddec4bb56dd78a49fbc49e9ad3de7710b0b008b031ff4dfce8fdc775e753ad1"
original = "7f96ebf7366e0b60c91ac1e5f196a2189ffdb0bbc73f77804a736466fcab7315"
patched = "c2d9d60d73c038da39f82073ffadb459c96b901d66db7af11f59da58e0dd53e4"