Compare commits

...

21 Commits

Author SHA1 Message Date
Andrew Gunnerson f31c4134e4 Version 2.0.3
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-09-07 14:01:00 -04:00
Andrew Gunnerson dd0d7f334b CHANGELOG.md: Add entry for PR #140
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-09-07 13:59:43 -04:00
Andrew Gunnerson e4a2a4c24c Merge pull request #140 from chenxiaolong/version
Add `--version` option
2023-09-07 13:58:50 -04:00
Andrew Gunnerson cc9aab197c CHANGELOG.md: Add entry for PR #139
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-09-07 13:58:23 -04:00
Andrew Gunnerson 92da30f53c Merge pull request #139 from chenxiaolong/xz
Upgrade xz to 5.4.4 and enable all encoders and decoders
2023-09-07 13:52:14 -04:00
Andrew Gunnerson 26142b0271 Upgrade xz to 5.4.4 and enable all encoders and decoders
When using the xz2/static feature, the xz2 crate uses a bundled version
of xz 5.2 and doesn't enable all of the available encoders and decoders.
This prevents certain payload data from being decompressed.

Fixes: #138

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-09-07 13:40:34 -04:00
Andrew Gunnerson c8df3286df Add --version option
Issue: #138

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-09-07 13:37:24 -04:00
Andrew Gunnerson 65979beff4 Version 2.0.2
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-09-06 23:16:01 -04:00
Andrew Gunnerson 47cf015f15 CHANGELOG.md: Add entry for PR #137
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-09-06 23:15:19 -04:00
Andrew Gunnerson e40e036159 Merge pull request #137 from chenxiaolong/anyhow
Remove unnecessary use of anyhow macro
2023-09-06 23:13:04 -04:00
Andrew Gunnerson 0d2a158ead Remove unnecessary use of anyhow macro
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-09-06 23:04:18 -04:00
Andrew Gunnerson ddabb19b4a CHANGELOG.md: Add entry for PR #136
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-09-06 22:51:58 -04:00
Andrew Gunnerson 3c05eb300c Merge pull request #136 from chenxiaolong/offset
PayloadWriter: Only set data_offset for operations that reference blobs
2023-09-06 22:50:31 -04:00
Andrew Gunnerson 2ffb1dfdbd PayloadWriter: Only set data_offset for operations that reference blobs
This fixes `data_offset` being set for `ZERO` and `DISCARD` operations,
which prevents some images (eg. `ossi`) from being flashed due to
update_engine's strict field validation.

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-09-06 22:40:15 -04:00
Andrew Gunnerson f93dc55f70 CHANGELOG.md: Add entry for PR #135
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-09-06 20:05:41 -04:00
Andrew Gunnerson 393e65f6b4 Merge pull request #135 from chenxiaolong/full
Move full OTA check to patch/extract subcommand function
2023-09-06 20:03:47 -04:00
Andrew Gunnerson 3f86a67038 Move full OTA check to patch/extract subcommand function
This way, the payload-related functions can be used to parse incremental
OTA files.

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-09-06 19:51:48 -04:00
Andrew Gunnerson 4337170f57 Version 2.0.1
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-09-04 17:11:28 -04:00
Andrew Gunnerson a73365592f CHANGELOG.md: Add entry for PR #132
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-09-04 16:41:28 -04:00
Andrew Gunnerson 0018e8a5ba Merge pull request #132 from chenxiaolong/magisk
boot.rs: Allow Magisk 263xx
2023-09-04 16:39:14 -04:00
Andrew Gunnerson adf0014da2 boot.rs: Allow Magisk 263xx
There are no changes that break avbroot.

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-09-04 16:30:39 -04:00
18 changed files with 184 additions and 145 deletions
+27
View File
@@ -7,9 +7,36 @@
to update the actual links at the bottom of the file.
-->
### Version 2.0.3
* Upgrade xz version in precompiled binaries ([Issue #138], [PR #139 @chenxiaolong])
* This fixes the `ota extract` and `ota verify` commands in some multithreaded situations.
* Add `--version` option to print out avbroot's version ([Issue #138], [PR #140 @chenxiaolong])
### Version 2.0.2
* Fix `data_offset` being set for payload operations that don't need it ([PR #136 @chenxiaolong])
* This fixes patched stock OnePlus images from being rejected when flashing.
Behind-the-scenes changes:
* Move full OTA check to CLI functions to allow library functions to parse delta OTAs ([PR #135 @chenxiaolong])
* Remove unnecessary use of `anyhow` macro ([PR #137 @chenxiaolong])
### Version 2.0.1
* Add support for Magisk 263xx ([PR #132 @chenxiaolong])
### Version 2.0.0
* Initial Rust release. The old Python implementation can be found in the `python` branch. ([PR #130 @chenxiaolong])
<!-- Do not manually edit the lines below. Use `cargo xtask update-changelog` to regenerate. -->
[Issue #138]: https://github.com/chenxiaolong/avbroot/issues/138
[PR #130 @chenxiaolong]: https://github.com/chenxiaolong/avbroot/pull/130
[PR #132 @chenxiaolong]: https://github.com/chenxiaolong/avbroot/pull/132
[PR #135 @chenxiaolong]: https://github.com/chenxiaolong/avbroot/pull/135
[PR #136 @chenxiaolong]: https://github.com/chenxiaolong/avbroot/pull/136
[PR #137 @chenxiaolong]: https://github.com/chenxiaolong/avbroot/pull/137
[PR #139 @chenxiaolong]: https://github.com/chenxiaolong/avbroot/pull/139
[PR #140 @chenxiaolong]: https://github.com/chenxiaolong/avbroot/pull/140
Generated
+5 -7
View File
@@ -105,7 +105,7 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "avbroot"
version = "0.1.0"
version = "2.0.3"
dependencies = [
"anyhow",
"assert_matches",
@@ -483,7 +483,7 @@ dependencies = [
[[package]]
name = "e2e"
version = "0.1.0"
version = "2.0.3"
dependencies = [
"anyhow",
"avbroot",
@@ -926,8 +926,7 @@ dependencies = [
[[package]]
name = "lzma-sys"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27"
source = "git+https://github.com/chenxiaolong/xz2-rs?rev=fe2050b9c3395db15d8610f1dabb505440c1a556#fe2050b9c3395db15d8610f1dabb505440c1a556"
dependencies = [
"cc",
"libc",
@@ -2253,7 +2252,7 @@ dependencies = [
[[package]]
name = "xtask"
version = "0.1.0"
version = "2.0.3"
dependencies = [
"anyhow",
"clap",
@@ -2268,8 +2267,7 @@ dependencies = [
[[package]]
name = "xz2"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2"
source = "git+https://github.com/chenxiaolong/xz2-rs?rev=fe2050b9c3395db15d8610f1dabb505440c1a556#fe2050b9c3395db15d8610f1dabb505440c1a556"
dependencies = [
"lzma-sys",
]
+1 -1
View File
@@ -4,7 +4,7 @@ members = ["avbroot", "e2e", "xtask"]
resolver = "2"
[workspace.package]
version = "2.0.0"
version = "2.0.3"
license = "GPL-3.0-only"
edition = "2021"
repository = "https://github.com/chenxiaolong/avbroot"
+8 -1
View File
@@ -44,7 +44,6 @@ thiserror = "1.0.47"
toml_edit = { version = "0.19.14", features = ["serde"] }
topological-sort = "0.2.2"
x509-cert = { version = "0.2.4", features = ["builder"] }
xz2 = "0.1.7"
# There's an upstream bug that causes an infinite loop in the write::BzDecoder
# destructor if the decoder is fed invalid data. While this never happens during
@@ -55,6 +54,14 @@ xz2 = "0.1.7"
git = "https://github.com/jongiddy/bzip2-rs"
rev = "2aefcb4d3634de1df226c73d93f758d65228bb8c"
# The upstream xz2 crate uses an old version of liblzma when compiling with the
# `static` feature and doesn't enable all of the encoders and decoders. This
# causes certain payload data to fail to decompress.
# https://github.com/chenxiaolong/avbroot/issues/138
[dependencies.xz2]
git = "https://github.com/chenxiaolong/xz2-rs"
rev = "fe2050b9c3395db15d8610f1dabb505440c1a556"
# https://github.com/zip-rs/zip/pull/383
[dependencies.zip]
git = "https://github.com/chenxiaolong/zip"
+1 -1
View File
@@ -108,7 +108,7 @@ impl MagiskRootPatcher {
// RULESDEVICE config option, which stored the writable block device as an
// rdev major/minor pair, which was not consistent across reboots and was
// replaced by PREINITDEVICE
const VERS_SUPPORTED: &[Range<u32>] = &[25102..25207, 25211..26300];
const VERS_SUPPORTED: &[Range<u32>] = &[25102..25207, 25211..26400];
const VER_PREINIT_DEVICE: Range<u32> =
25211..Self::VERS_SUPPORTED[Self::VERS_SUPPORTED.len() - 1].end;
const VER_RANDOM_SEED: Range<u32> = 25211..26103;
+1
View File
@@ -28,6 +28,7 @@ pub enum Command {
}
#[derive(Debug, Parser)]
#[command(version)]
pub struct Cli {
#[command(subcommand)]
pub command: Command,
+11 -11
View File
@@ -50,14 +50,14 @@ pub fn verify_headers(
let path = directory.join(format!("{name}.img"));
let raw_reader =
File::open(&path).with_context(|| anyhow!("Failed to open for reading: {path:?}"))?;
File::open(&path).with_context(|| format!("Failed to open for reading: {path:?}"))?;
let (header, _, _) = avb::load_image(BufReader::new(raw_reader))
.with_context(|| anyhow!("Failed to load vbmeta structures: {path:?}"))?;
.with_context(|| format!("Failed to load vbmeta structures: {path:?}"))?;
// Verify the header's signature.
let public_key = header
.verify()
.with_context(|| anyhow!("Failed to verify header signature: {path:?}"))?;
.with_context(|| format!("Failed to verify header signature: {path:?}"))?;
if let Some(k) = &public_key {
let prefix = format!("{name} has a signed vbmeta header");
@@ -92,7 +92,7 @@ pub fn verify_headers(
}
avb::Descriptor::ChainPartition(d) => {
let target_key = avb::decode_public_key(&d.public_key).with_context(|| {
anyhow!("Failed to decode chained public key for: {target_name}")
format!("Failed to decode chained public key for: {target_name}")
})?;
verify_headers(directory, target_name, Some(&target_key), seen, descriptors)?;
@@ -134,12 +134,12 @@ pub fn verify_descriptors(
|| Ok(Box::new(BufReader::new(reader.clone()))),
cancel_signal,
)
.with_context(|| anyhow!("Failed to verify hashtree descriptor for: {name}"))?;
.with_context(|| format!("Failed to verify hashtree descriptor for: {name}"))?;
}
Descriptor::Hash(d) => {
status!("Verifying hash descriptor for: {name}");
d.verify(BufReader::new(reader), cancel_signal)
.with_context(|| anyhow!("Failed to verify hash descriptor for: {name}"))?;
.with_context(|| format!("Failed to verify hash descriptor for: {name}"))?;
}
_ => unreachable!("Non-verifiable descriptor: {descriptor:?}"),
}
@@ -153,10 +153,10 @@ pub fn avb_main(cli: &AvbCli, cancel_signal: &Arc<AtomicBool>) -> Result<()> {
match &cli.command {
AvbCommand::Dump(c) => {
let raw_reader = File::open(&c.input)
.with_context(|| anyhow!("Failed to open for reading: {:?}", c.input))?;
.with_context(|| format!("Failed to open for reading: {:?}", c.input))?;
let reader = BufReader::new(raw_reader);
let (header, footer, image_size) = avb::load_image(reader)
.with_context(|| anyhow!("Failed to load vbmeta structures: {:?}", c.input))?;
.with_context(|| format!("Failed to load vbmeta structures: {:?}", c.input))?;
println!("Image size: {image_size}");
println!("Header: {header:#?}");
@@ -164,9 +164,9 @@ pub fn avb_main(cli: &AvbCli, cancel_signal: &Arc<AtomicBool>) -> Result<()> {
}
AvbCommand::Verify(c) => {
let public_key = if let Some(p) = &c.public_key {
let data = fs::read(p).with_context(|| anyhow!("Failed to read file: {p:?}"))?;
let data = fs::read(p).with_context(|| format!("Failed to read file: {p:?}"))?;
let key = avb::decode_public_key(&data)
.with_context(|| anyhow!("Failed to decode public key: {p:?}"))?;
.with_context(|| format!("Failed to decode public key: {p:?}"))?;
Some(key)
} else {
@@ -177,7 +177,7 @@ pub fn avb_main(cli: &AvbCli, cancel_signal: &Arc<AtomicBool>) -> Result<()> {
let name = c
.input
.file_stem()
.with_context(|| anyhow!("Path is not a file: {:?}", c.input))?
.with_context(|| format!("Path is not a file: {:?}", c.input))?
.to_str()
.ok_or_else(|| anyhow!("Invalid UTF-8: {:?}", c.input))?;
+7 -7
View File
@@ -9,7 +9,7 @@ use std::{
path::{Path, PathBuf},
};
use anyhow::{anyhow, bail, Context, Result};
use anyhow::{bail, Context, Result};
use clap::{Parser, Subcommand};
use crate::{
@@ -82,7 +82,7 @@ fn read_avb_header_if_exists(path: &Path) -> Result<Option<Header>> {
Err(e) => Err(e).with_context(|| format!("Failed to open for reading: {path:?}"))?,
};
let header = Header::from_reader(BufReader::new(file))
.with_context(|| anyhow!("Failed to read vbmeta header: {path:?}"))?;
.with_context(|| format!("Failed to read vbmeta header: {path:?}"))?;
Ok(Some(header))
}
@@ -106,7 +106,7 @@ fn write_text_if_not_empty(path: &Path, text: &str) -> Result<()> {
fn write_avb_header(path: &Path, header: &Header) -> Result<()> {
let file =
File::create(path).with_context(|| anyhow!("Failed to open for writing: {path:?}"))?;
File::create(path).with_context(|| format!("Failed to open for writing: {path:?}"))?;
header.to_writer(BufWriter::new(file))?;
Ok(())
@@ -272,9 +272,9 @@ fn info_subcommand(boot_cli: &BootCli, cli: &InfoCli) -> Result<()> {
pub fn magisk_info_subcommand(cli: &MagiskInfoCli) -> Result<()> {
let raw_reader = File::open(&cli.image)
.with_context(|| anyhow!("Failed to open for reading: {:?}", cli.image))?;
.with_context(|| format!("Failed to open for reading: {:?}", cli.image))?;
let boot_image = BootImage::from_reader(BufReader::new(raw_reader))
.with_context(|| anyhow!("Failed to load boot image: {:?}", cli.image))?;
.with_context(|| format!("Failed to load boot image: {:?}", cli.image))?;
let mut ramdisks = vec![];
@@ -297,9 +297,9 @@ pub fn magisk_info_subcommand(cli: &MagiskInfoCli) -> Result<()> {
for (i, ramdisk) in ramdisks.iter().enumerate() {
let reader = Cursor::new(ramdisk);
let reader = CompressedReader::new(reader, true)
.with_context(|| anyhow!("Failed to load ramdisk #{i}"))?;
.with_context(|| format!("Failed to load ramdisk #{i}"))?;
let entries = cpio::load(reader, false)
.with_context(|| anyhow!("Failed to load ramdisk #{i} cpio"))?;
.with_context(|| format!("Failed to load ramdisk #{i} cpio"))?;
if let Some(e) = entries.iter().find(|e| e.name == b".backup/.magisk") {
io::stdout().write_all(&e.content)?;
+8 -8
View File
@@ -10,7 +10,7 @@ use std::{
time::Duration,
};
use anyhow::{anyhow, Context, Result};
use anyhow::{Context, Result};
use clap::{Args, Parser, Subcommand};
use crate::{
@@ -36,30 +36,30 @@ pub fn key_main(cli: &KeyCli) -> Result<()> {
crypto::generate_rsa_key_pair().context("Failed to generate RSA keypair")?;
crypto::write_pem_key_file(&c.output, &private_key, &passphrase)
.with_context(|| anyhow!("Failed to write private key: {:?}", c.output))?;
.with_context(|| format!("Failed to write private key: {:?}", c.output))?;
}
KeyCommand::GenerateCert(c) => {
let passphrase = get_passphrase(&c.passphrase, &c.key);
let private_key = crypto::read_pem_key_file(&c.key, &passphrase)
.with_context(|| anyhow!("Failed to load key: {:?}", c.key))?;
.with_context(|| format!("Failed to load key: {:?}", c.key))?;
let validity = Duration::from_secs(c.validity * 24 * 60 * 60);
let cert = crypto::generate_cert(&private_key, rand::random(), validity, &c.subject)
.context("Failed to generate certificate")?;
crypto::write_pem_cert_file(&c.output, &cert)
.with_context(|| anyhow!("Failed to write certificate: {:?}", c.output))?;
.with_context(|| format!("Failed to write certificate: {:?}", c.output))?;
}
KeyCommand::ExtractAvb(c) => {
let public_key = if let Some(p) = &c.input.key {
let passphrase = get_passphrase(&c.passphrase, p);
let private_key = crypto::read_pem_key_file(p, &passphrase)
.with_context(|| anyhow!("Failed to load key: {p:?}"))?;
.with_context(|| format!("Failed to load key: {p:?}"))?;
private_key.to_public_key()
} else if let Some(p) = &c.input.cert {
let certificate = crypto::read_pem_cert_file(p)
.with_context(|| anyhow!("Failed to load certificate: {p:?}"))?;
.with_context(|| format!("Failed to load certificate: {p:?}"))?;
crypto::get_public_key(&certificate)?
} else {
@@ -67,10 +67,10 @@ pub fn key_main(cli: &KeyCli) -> Result<()> {
};
let encoded = avb::encode_public_key(&public_key)
.with_context(|| anyhow!("Failed to encode public key in AVB format"))?;
.context("Failed to encode public key in AVB format")?;
fs::write(&c.output, encoded)
.with_context(|| anyhow!("Failed to write public key: {:?}", c.output))?;
.with_context(|| format!("Failed to write public key: {:?}", c.output))?;
}
}
+68 -63
View File
@@ -159,14 +159,14 @@ fn open_input_streams(
status!("Opening external image: {name}: {path:?}");
let file = File::open(path)
.with_context(|| anyhow!("Failed to open external image: {path:?}"))?;
.with_context(|| format!("Failed to open external image: {path:?}"))?;
input_streams.insert(name.clone(), Box::new(file));
} else {
status!("Extracting from original payload: {name}");
let stream =
payload::extract_image_to_memory(&open_payload, header, name, cancel_signal)
.with_context(|| anyhow!("Failed to extract from original payload: {name}"))?;
.with_context(|| format!("Failed to extract from original payload: {name}"))?;
input_streams.insert(name.clone(), Box::new(stream));
}
}
@@ -219,7 +219,7 @@ fn patch_boot_images(
let mut writer = Cursor::new(Vec::new());
boot::patch_boot(s, &mut writer, key_avb, &p, cancel_signal)
.with_context(|| anyhow!("Failed to patch boot image: {n}"))?;
.with_context(|| format!("Failed to patch boot image: {n}"))?;
Ok((n, writer))
})
@@ -247,7 +247,7 @@ fn get_vbmeta_patch_order(
for name in vbmeta_images {
let reader = images.get_mut(name).unwrap();
let (header, footer, _) = avb::load_image(reader)
.with_context(|| anyhow!("Failed to load vbmeta image: {name}"))?;
.with_context(|| format!("Failed to load vbmeta image: {name}"))?;
if let Some(f) = footer {
warning!("{name} is a vbmeta partition, but has a footer: {f:?}");
@@ -366,7 +366,7 @@ fn update_vbmeta_descriptors(
let reader = images.get_mut(dep).unwrap();
let (header, _, _) = avb::load_image(reader)
.with_context(|| anyhow!("Failed to load vbmeta footer from image: {dep}"))?;
.with_context(|| format!("Failed to load vbmeta footer from image: {dep}"))?;
if header.public_key.is_empty() {
// vbmeta is unsigned. Use the existing descriptor.
@@ -404,15 +404,15 @@ fn update_vbmeta_descriptors(
parent_header
.sign(key)
.with_context(|| anyhow!("Failed to sign vbmeta header for image: {name}"))?;
.with_context(|| format!("Failed to sign vbmeta header for image: {name}"))?;
let mut writer = Cursor::new(Vec::new());
parent_header
.to_writer(&mut writer)
.with_context(|| anyhow!("Failed to write vbmeta image: {name}"))?;
.with_context(|| format!("Failed to write vbmeta image: {name}"))?;
padding::write_zeros(&mut writer, block_size)
.with_context(|| anyhow!("Failed to write vbmeta padding: {name}"))?;
.with_context(|| format!("Failed to write vbmeta padding: {name}"))?;
*images.get_mut(name).unwrap() = Box::new(writer);
}
@@ -462,8 +462,12 @@ fn patch_ota_payload(
cert_ota: &Certificate,
cancel_signal: &Arc<AtomicBool>,
) -> Result<(String, u64)> {
let header = PayloadHeader::from_reader(open_payload()?)
.with_context(|| anyhow!("Failed to load OTA payload header"))?;
let header =
PayloadHeader::from_reader(open_payload()?).context("Failed to load OTA payload header")?;
if !header.is_full_ota() {
bail!("Payload is a delta OTA, not a full OTA");
}
let header = Mutex::new(header);
let header_locked = header.lock().unwrap();
let all_partitions = header_locked
@@ -552,7 +556,7 @@ fn patch_ota_payload(
.par_iter_mut()
.map(|(name, stream)| -> Result<()> {
compress_image(name, stream, &header, block_size, cancel_signal)
.with_context(|| anyhow!("Failed to compress image: {name}"))
.with_context(|| format!("Failed to compress image: {name}"))
})
.collect::<Result<()>>()?;
@@ -560,12 +564,12 @@ fn patch_ota_payload(
let header_locked = header.lock().unwrap();
let mut payload_writer = PayloadWriter::new(writer, header_locked.clone(), key_ota.clone())
.with_context(|| anyhow!("Failed to write payload header"))?;
.context("Failed to write payload header")?;
let mut orig_payload_reader = open_payload()?;
while payload_writer
.begin_next_operation()
.with_context(|| anyhow!("Failed to begin next payload blob entry"))?
.context("Failed to begin next payload blob entry")?
{
let name = payload_writer.partition().unwrap().partition_name.clone();
let operation = payload_writer.operation().unwrap();
@@ -580,7 +584,7 @@ fn patch_ota_payload(
reader.rewind()?;
stream::copy_n(&mut reader, &mut payload_writer, data_length, cancel_signal)
.with_context(|| anyhow!("Failed to copy from replacement image: {name}"))?;
.with_context(|| format!("Failed to copy from replacement image: {name}"))?;
} else {
// Copy from the original payload.
let pi = payload_writer.partition_index().unwrap();
@@ -595,7 +599,7 @@ fn patch_ota_payload(
orig_payload_reader
.seek(SeekFrom::Start(data_offset))
.with_context(|| anyhow!("Failed to seek original payload to {data_offset}"))?;
.with_context(|| format!("Failed to seek original payload to {data_offset}"))?;
stream::copy_n(
&mut orig_payload_reader,
@@ -603,13 +607,13 @@ fn patch_ota_payload(
data_length,
cancel_signal,
)
.with_context(|| anyhow!("Failed to copy from original payload: {name}"))?;
.with_context(|| format!("Failed to copy from original payload: {name}"))?;
}
}
let (_, properties, metadata_size) = payload_writer
.finish()
.with_context(|| anyhow!("Failed to finalize payload"))?;
.context("Failed to finalize payload")?;
Ok((properties, metadata_size))
}
@@ -659,7 +663,7 @@ fn patch_ota_zip(
for path in &paths {
let mut reader = zip_reader
.by_name(path)
.with_context(|| anyhow!("Failed to open zip entry: {path}"))?;
.with_context(|| format!("Failed to open zip entry: {path}"))?;
// Android's libarchive parser is broken and only reads data descriptor
// size fields as 64-bit integers if the central directory says the file
@@ -682,7 +686,7 @@ fn patch_ota_zip(
let mut buf = vec![];
reader
.read_to_end(&mut buf)
.with_context(|| anyhow!("Failed to read OTA metadata: {path}"))?;
.with_context(|| format!("Failed to read OTA metadata: {path}"))?;
metadata_pb_raw = Some(buf);
continue;
}
@@ -692,10 +696,10 @@ fn patch_ota_zip(
// All remaining entries are written immediately.
zip_writer
.start_file_with_extra_data(path, options)
.with_context(|| anyhow!("Failed to begin new zip entry: {path}"))?;
.with_context(|| format!("Failed to begin new zip entry: {path}"))?;
let offset = zip_writer
.end_extra_data()
.with_context(|| anyhow!("Failed to end new zip entry: {path}"))?;
.with_context(|| format!("Failed to end new zip entry: {path}"))?;
let mut writer = CountingWriter::new(&mut zip_writer);
match path.as_str() {
@@ -704,7 +708,7 @@ fn patch_ota_zip(
status!("Replacing zip entry: {path}");
crypto::write_pem_cert(&mut writer, cert_ota)
.with_context(|| anyhow!("Failed to write entry: {path}"))?;
.with_context(|| format!("Failed to write entry: {path}"))?;
}
ota::PATH_PAYLOAD => {
status!("Patching zip entry: {path}");
@@ -737,7 +741,7 @@ fn patch_ota_zip(
cert_ota,
cancel_signal,
)
.with_context(|| anyhow!("Failed to patch payload: {path}"))?;
.with_context(|| format!("Failed to patch payload: {path}"))?;
properties = Some(p);
payload_metadata_size = Some(m);
@@ -748,13 +752,13 @@ fn patch_ota_zip(
// payload.bin is guaranteed to be patched first.
writer
.write_all(properties.as_ref().unwrap().as_bytes())
.with_context(|| anyhow!("Failed to write payload properties: {path}"))?;
.with_context(|| format!("Failed to write payload properties: {path}"))?;
}
_ => {
status!("Copying zip entry: {path}");
stream::copy(&mut reader, &mut writer, cancel_signal)
.with_context(|| anyhow!("Failed to copy zip entry: {path}"))?;
.with_context(|| format!("Failed to copy zip entry: {path}"))?;
}
}
@@ -781,7 +785,7 @@ fn patch_ota_zip(
&metadata_pb_raw.unwrap(),
payload_metadata_size.unwrap(),
)
.with_context(|| anyhow!("Failed to write new OTA metadata"))?;
.context("Failed to write new OTA metadata")?;
Ok((metadata, payload_metadata_size.unwrap()))
}
@@ -802,7 +806,7 @@ fn extract_ota_zip(
}
fs::create_dir_all(directory)
.with_context(|| anyhow!("Failed to create directory: {directory:?}"))?;
.with_context(|| format!("Failed to create directory: {directory:?}"))?;
status!("Extracting from the payload: {}", joined(images));
@@ -813,7 +817,7 @@ fn extract_ota_zip(
let path = directory.join(format!("{name}.img"));
let file = File::create(&path)
.map(PSeekFile::new)
.with_context(|| anyhow!("Failed to open for writing: {path:?}"))?;
.with_context(|| format!("Failed to open for writing: {path:?}"))?;
Ok((name.as_str(), file))
})
.collect::<Result<HashMap<_, _>>>()?;
@@ -834,7 +838,7 @@ fn extract_ota_zip(
images.iter().map(|n| n.as_str()),
cancel_signal,
)
.with_context(|| anyhow!("Failed to extract images from payload"))?;
.context("Failed to extract images from payload")?;
Ok(())
}
@@ -865,11 +869,11 @@ pub fn patch_subcommand(cli: &PatchCli, cancel_signal: &Arc<AtomicBool>) -> Resu
};
let key_avb = crypto::read_pem_key_file(&cli.key_avb, &passphrase_avb)
.with_context(|| anyhow!("Failed to load key: {:?}", cli.key_avb))?;
.with_context(|| format!("Failed to load key: {:?}", cli.key_avb))?;
let key_ota = crypto::read_pem_key_file(&cli.key_ota, &passphrase_ota)
.with_context(|| anyhow!("Failed to load key: {:?}", cli.key_ota))?;
.with_context(|| format!("Failed to load key: {:?}", cli.key_ota))?;
let cert_ota = crypto::read_pem_cert_file(&cli.cert_ota)
.with_context(|| anyhow!("Failed to load certificate: {:?}", cli.cert_ota))?;
.with_context(|| format!("Failed to load certificate: {:?}", cli.cert_ota))?;
if !crypto::cert_matches_key(&cert_ota, &key_ota)? {
bail!(
@@ -900,7 +904,7 @@ pub fn patch_subcommand(cli: &PatchCli, cancel_signal: &Arc<AtomicBool>) -> Resu
cli.ignore_magisk_warnings,
move |s| warning!("{s}"),
)
.with_context(|| anyhow!("Failed to create Magisk boot image patcher"))?;
.context("Failed to create Magisk boot image patcher")?;
Some(Box::new(patcher))
} else if let Some(prepatched) = &cli.root.prepatched {
@@ -918,9 +922,9 @@ pub fn patch_subcommand(cli: &PatchCli, cancel_signal: &Arc<AtomicBool>) -> Resu
let raw_reader = File::open(&cli.input)
.map(PSeekFile::new)
.with_context(|| anyhow!("Failed to open for reading: {:?}", cli.input))?;
.with_context(|| format!("Failed to open for reading: {:?}", cli.input))?;
let mut zip_reader = ZipArchive::new(BufReader::new(raw_reader.clone()))
.with_context(|| anyhow!("Failed to read zip: {:?}", cli.input))?;
.with_context(|| format!("Failed to read zip: {:?}", cli.input))?;
// Open the output file for reading too, so we can verify offsets later.
let temp_writer = NamedTempFile::with_prefix_in(
@@ -929,7 +933,7 @@ pub fn patch_subcommand(cli: &PatchCli, cancel_signal: &Arc<AtomicBool>) -> Resu
.unwrap_or_else(|| OsStr::new("avbroot.tmp")),
output.parent().unwrap_or_else(|| Path::new(".")),
)
.with_context(|| anyhow!("Failed to open temporary output file"))?;
.context("Failed to open temporary output file")?;
let temp_path = temp_writer.path().to_owned();
let hole_punching_writer = HolePunchingWriter::new(temp_writer);
let buffered_writer = BufWriter::new(hole_punching_writer);
@@ -949,21 +953,19 @@ pub fn patch_subcommand(cli: &PatchCli, cancel_signal: &Arc<AtomicBool>) -> Resu
&cert_ota,
cancel_signal,
)
.with_context(|| anyhow!("Failed to patch OTA zip"))?;
.context("Failed to patch OTA zip")?;
let sign_writer = zip_writer
let signing_writer = zip_writer
.finish()
.with_context(|| anyhow!("Failed to finalize output zip"))?;
let buffered_writer = sign_writer
.context("Failed to finalize output zip")?;
let buffered_writer = signing_writer
.finish(&key_ota, &cert_ota)
.with_context(|| anyhow!("Failed to sign output zip"))?;
.context("Failed to sign output zip")?;
let hole_punching_writer = buffered_writer
.into_inner()
.with_context(|| anyhow!("Failed to flush output zip"))?;
.context("Failed to flush output zip")?;
let mut temp_writer = hole_punching_writer.into_inner();
temp_writer
.flush()
.with_context(|| anyhow!("Failed to flush output zip"))?;
temp_writer.flush().context("Failed to flush output zip")?;
// We do a lot of low-level hackery. Reopen and verify offsets.
status!("Verifying metadata offsets");
@@ -973,7 +975,7 @@ pub fn patch_subcommand(cli: &PatchCli, cancel_signal: &Arc<AtomicBool>) -> Resu
&metadata,
payload_metadata_size,
)
.with_context(|| anyhow!("Failed to verify OTA metadata offsets"))?;
.context("Failed to verify OTA metadata offsets")?;
status!("Completed after {:.1}s", start.elapsed().as_secs_f64());
@@ -996,11 +998,11 @@ pub fn patch_subcommand(cli: &PatchCli, cancel_signal: &Arc<AtomicBool>) -> Resu
temp_writer
.as_file()
.set_permissions(Permissions::from_mode(mode))
.with_context(|| anyhow!("Failed to set permissions to {mode:o}: {temp_path:?}"))?;
.with_context(|| format!("Failed to set permissions to {mode:o}: {temp_path:?}"))?;
}
temp_writer.persist(output.as_ref()).with_context(|| {
anyhow!("Failed to move temporary file to output path: {temp_path:?} -> {output:?}")
format!("Failed to move temporary file to output path: {temp_path:?} -> {output:?}")
})?;
Ok(())
@@ -1009,12 +1011,12 @@ pub fn patch_subcommand(cli: &PatchCli, cancel_signal: &Arc<AtomicBool>) -> Resu
pub fn extract_subcommand(cli: &ExtractCli, cancel_signal: &Arc<AtomicBool>) -> Result<()> {
let raw_reader = File::open(&cli.input)
.map(PSeekFile::new)
.with_context(|| anyhow!("Failed to open for reading: {:?}", cli.input))?;
.with_context(|| format!("Failed to open for reading: {:?}", cli.input))?;
let mut zip = ZipArchive::new(BufReader::new(raw_reader.clone()))
.with_context(|| anyhow!("Failed to read zip: {:?}", cli.input))?;
.with_context(|| format!("Failed to read zip: {:?}", cli.input))?;
let payload_entry = zip
.by_name(ota::PATH_PAYLOAD)
.with_context(|| anyhow!("Failed to open zip entry: {:?}", ota::PATH_PAYLOAD))?;
.with_context(|| format!("Failed to open zip entry: {:?}", ota::PATH_PAYLOAD))?;
let payload_offset = payload_entry.data_start();
let payload_size = payload_entry.size();
@@ -1026,7 +1028,11 @@ pub fn extract_subcommand(cli: &ExtractCli, cancel_signal: &Arc<AtomicBool>) ->
)?;
let header = PayloadHeader::from_reader(&mut payload_reader)
.with_context(|| anyhow!("Failed to load OTA payload header"))?;
.context("Failed to load OTA payload header")?;
if !header.is_full_ota() {
bail!("Payload is a delta OTA, not a full OTA");
}
let mut unique_images = BTreeSet::new();
if cli.all {
@@ -1064,7 +1070,7 @@ pub fn extract_subcommand(cli: &ExtractCli, cancel_signal: &Arc<AtomicBool>) ->
pub fn verify_subcommand(cli: &VerifyCli, cancel_signal: &Arc<AtomicBool>) -> Result<()> {
let raw_reader = File::open(&cli.input)
.map(PSeekFile::new)
.with_context(|| anyhow!("Failed to open for reading: {:?}", cli.input))?;
.with_context(|| format!("Failed to open for reading: {:?}", cli.input))?;
let mut reader = BufReader::new(raw_reader);
status!("Verifying whole-file signature");
@@ -1079,7 +1085,7 @@ pub fn verify_subcommand(cli: &VerifyCli, cancel_signal: &Arc<AtomicBool>) -> Re
);
} else if let Some(p) = &cli.cert_ota {
let verify_cert = crypto::read_pem_cert_file(p)
.with_context(|| anyhow!("Failed to load certificate: {:?}", 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:?}");
@@ -1089,7 +1095,7 @@ pub fn verify_subcommand(cli: &VerifyCli, cancel_signal: &Arc<AtomicBool>) -> Re
}
ota::verify_metadata(&mut reader, &metadata, header.blob_offset)
.with_context(|| anyhow!("Failed to verify OTA metadata offsets"))?;
.context("Failed to verify OTA metadata offsets")?;
status!("Verifying payload");
@@ -1098,7 +1104,7 @@ pub fn verify_subcommand(cli: &VerifyCli, cancel_signal: &Arc<AtomicBool>) -> Re
.get(ota::PF_NAME)
.ok_or_else(|| anyhow!("Missing property files: {}", ota::PF_NAME))?;
let pfs = ota::parse_property_files(pfs_raw)
.with_context(|| anyhow!("Failed to parse property files: {}", ota::PF_NAME))?;
.with_context(|| format!("Failed to parse property files: {}", ota::PF_NAME))?;
let pf_payload = pfs
.iter()
.find(|pf| pf.name == ota::PATH_PAYLOAD)
@@ -1110,8 +1116,7 @@ pub fn verify_subcommand(cli: &VerifyCli, cancel_signal: &Arc<AtomicBool>) -> Re
status!("Extracting partition images to temporary directory");
let temp_dir =
TempDir::new().with_context(|| anyhow!("Failed to create temporary directory"))?;
let temp_dir = TempDir::new().context("Failed to create temporary directory")?;
let raw_reader = reader.into_inner();
let unique_images = header
.manifest
@@ -1139,13 +1144,13 @@ pub fn verify_subcommand(cli: &VerifyCli, cancel_signal: &Arc<AtomicBool>) -> Re
.path()
.join(format!("{}.img", partitions_by_type["@otacerts"]));
let file =
File::open(&path).with_context(|| anyhow!("Failed to open for reading: {path:?}"))?;
File::open(&path).with_context(|| format!("Failed to open for reading: {path:?}"))?;
BootImage::from_reader(BufReader::new(file))
.with_context(|| anyhow!("Failed to read boot image: {path:?}"))?
.with_context(|| format!("Failed to read boot image: {path:?}"))?
};
let ramdisk_certs = OtaCertPatcher::get_certificates(&boot_image)
.with_context(|| anyhow!("Failed to read ramdisk's otacerts.zip"))?;
.context("Failed to read ramdisk's otacerts.zip")?;
if !ramdisk_certs.contains(&ota_cert) {
bail!("Ramdisk's otacerts.zip does not contain OTA certificate");
}
@@ -1153,9 +1158,9 @@ pub fn verify_subcommand(cli: &VerifyCli, cancel_signal: &Arc<AtomicBool>) -> Re
status!("Verifying AVB signatures");
let public_key = if let Some(p) = &cli.public_key_avb {
let data = fs::read(p).with_context(|| anyhow!("Failed to read file: {p:?}"))?;
let data = fs::read(p).with_context(|| format!("Failed to read file: {p:?}"))?;
let key = avb::decode_public_key(&data)
.with_context(|| anyhow!("Failed to decode public key: {p:?}"))?;
.with_context(|| format!("Failed to decode public key: {p:?}"))?;
Some(key)
} else {
+14 -14
View File
@@ -49,8 +49,6 @@ pub enum Error {
UnknownMagic([u8; 4]),
#[error("Unsupported payload version: {0}")]
UnsupportedVersion(u64),
#[error("File is a delta OTA, not a full OTA")]
NotFullOta,
#[error("Payload contains no signatures")]
NoSignatures,
#[error("Blob offset should be {0}, but is {1}")]
@@ -99,6 +97,15 @@ pub struct PayloadHeader {
pub blob_offset: u64,
}
impl PayloadHeader {
pub fn is_full_ota(&self) -> bool {
self.manifest
.partitions
.iter()
.all(|p| p.old_partition_info.is_none())
}
}
impl<R: Read> FromReader<R> for PayloadHeader {
type Error = Error;
@@ -128,15 +135,6 @@ impl<R: Read> FromReader<R> for PayloadHeader {
reader.read_exact(&mut manifest_raw)?;
let manifest: DeltaArchiveManifest = util::read_protobuf(&manifest_raw)?;
// Fail as soon as possible since it's impossible to support delta OTAs.
if manifest
.partitions
.iter()
.any(|p| p.old_partition_info.is_some())
{
return Err(Error::NotFullOta);
}
// Skip manifest signatures.
reader.read_discard_exact(metadata_signature_size.into())?;
@@ -296,9 +294,10 @@ impl<W: Write> PayloadWriter<W> {
// The blob must contain all data in sequential order with no gaps.
for p in &mut header.manifest.partitions {
for op in &mut p.operations {
op.data_offset = Some(blob_size);
if let Some(length) = op.data_length {
// The field must be left unset when the blob contains no
// data for the operation.
op.data_offset = Some(blob_size);
blob_size += length;
}
}
@@ -404,7 +403,8 @@ impl<W: Write> PayloadWriter<W> {
/// [`InstallOperation::data_length`].
pub fn begin_next_operation(&mut self) -> Result<bool> {
if let Some(operation) = self.operation() {
// ZERO/DISCARD operations will not have a length.
// Only operations that reference data in the blob will have a
// length set.
if self.written < operation.data_length.unwrap_or(0) {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
+1
View File
@@ -35,6 +35,7 @@ deny = [
unknown-registry = "deny"
unknown-git = "deny"
allow-git = [
"https://github.com/chenxiaolong/xz2-rs",
"https://github.com/chenxiaolong/zip",
"https://github.com/jongiddy/bzip2-rs",
]
+2 -2
View File
@@ -107,8 +107,8 @@ sections = [
]
hash.original.full = "929f892fbd70699cf7f118a119aac1ae1b86351e1ada17715666fa4401e63472"
hash.original.stripped = "4eabaf79b6c2b5df305e3ecdc2b9570c0dd27350b4e8d6434584000c4989ff3d"
hash.patched.full = "8e9cf3159e57d706047325a7b774dc2dc84f0d56baae100ad54c0c723b621469"
hash.patched.stripped = "0b5dcdfdfea742bf6662b286ba260f8572a7b0081bb1129b7274c29214cdfb49"
hash.patched.full = "3453b2eda97ca1ba7367fa9d0dd37b21a5065e2e57cc3bbe9a6f135349d42d0b"
hash.patched.stripped = "1a41e435bdbf4302761e719e218e8682145644c0fe276739c3265f392adbd74d"
hash.avb_images."boot.img" = "f5dc3b147c54589be8db00ca15257a3688424a9eb88bd9c2cec82ebf4f6bf859"
hash.avb_images."recovery.img" = "a42c0bf4f023cd24394184a33ee113783a9c89a7cc4c0c582a5f72cc23b72309"
hash.avb_images."vbmeta.img" = "c022cf79da301a8430af5c49704944c490707fa0306031fe3ea22c39ce4734f6"
+3 -3
View File
@@ -5,7 +5,7 @@
use std::{collections::BTreeMap, fs, ops::Range, path::Path};
use anyhow::{anyhow, Context, Result};
use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};
use toml_edit::{
ser::ValueSerializer,
@@ -131,9 +131,9 @@ pub fn add_device(document: &mut Document, name: &str, device: &Device) -> Resul
pub fn load_config(path: &Path) -> Result<(Config, Document)> {
let contents =
fs::read_to_string(path).with_context(|| anyhow!("Failed to read config: {path:?}"))?;
fs::read_to_string(path).with_context(|| format!("Failed to read config: {path:?}"))?;
let config: Config = toml_edit::de::from_str(&contents)
.with_context(|| anyhow!("Failed to parse config: {path:?}"))?;
.with_context(|| format!("Failed to parse config: {path:?}"))?;
let document: Document = contents.parse().unwrap();
Ok((config, document))
+6 -6
View File
@@ -145,7 +145,7 @@ async fn download_range(
.send()
.await
.and_then(|r| r.error_for_status())
.with_context(|| anyhow!("Failed to start download for range: {initial_range:?}"))?;
.with_context(|| format!("Failed to start download for range: {initial_range:?}"))?;
let mut stream = response.bytes_stream();
let mut range = initial_range.clone();
@@ -246,11 +246,11 @@ async fn download_ranges(
.create(true)
.open(output)
.map(PSeekFile::new)
.with_context(|| anyhow!("Failed to open for writing: {output:?}"))
.with_context(|| format!("Failed to open for writing: {output:?}"))
})?;
task::block_in_place(|| file.set_len(file_size))
.with_context(|| anyhow!("Failed to set file size: {output:?}"))?;
.with_context(|| format!("Failed to set file size: {output:?}"))?;
// Queue of ranges that need to be downloaded.
let mut remaining = VecDeque::from(match initial_ranges {
@@ -381,11 +381,11 @@ fn read_state(path: &Path) -> Result<Option<State>> {
let data = match fs::read_to_string(path) {
Ok(f) => f,
Err(e) if e.kind() == io::ErrorKind::NotFound => return Ok(None),
Err(e) => Err(e).with_context(|| anyhow!("Failed to read download state: {path:?}"))?,
Err(e) => Err(e).with_context(|| format!("Failed to read download state: {path:?}"))?,
};
let state = toml_edit::de::from_str(&data)
.with_context(|| anyhow!("Failed to parse download state: {path:?}"))?;
.with_context(|| format!("Failed to parse download state: {path:?}"))?;
Ok(Some(state))
}
@@ -393,7 +393,7 @@ fn read_state(path: &Path) -> Result<Option<State>> {
fn write_state(path: &Path, state: &State) -> Result<()> {
let data = toml_edit::ser::to_string(state).unwrap();
fs::write(path, data).with_context(|| anyhow!("Failed to write download state: {path:?}"))?;
fs::write(path, data).with_context(|| format!("Failed to write download state: {path:?}"))?;
Ok(())
}
+17 -17
View File
@@ -96,12 +96,12 @@ fn strip_image(
let mut raw_reader = File::open(input)
.map(PSeekFile::new)
.with_context(|| anyhow!("Failed to open for reading: {input:?}"))?;
.with_context(|| format!("Failed to open for reading: {input:?}"))?;
let mut zip_reader = ZipArchive::new(BufReader::new(raw_reader.clone()))
.with_context(|| anyhow!("Failed to read zip: {input:?}"))?;
.with_context(|| format!("Failed to read zip: {input:?}"))?;
let payload_entry = zip_reader
.by_name(ota::PATH_PAYLOAD)
.with_context(|| anyhow!("Failed to open zip entry: {:?}", ota::PATH_PAYLOAD))?;
.with_context(|| format!("Failed to open zip entry: {:?}", ota::PATH_PAYLOAD))?;
let payload_offset = payload_entry.data_start();
let payload_size = payload_entry.size();
@@ -113,7 +113,7 @@ fn strip_image(
)?;
let header = PayloadHeader::from_reader(&mut payload_reader)
.with_context(|| anyhow!("Failed to load OTA payload header"))?;
.context("Failed to load OTA payload header")?;
let required_images =
avbroot::cli::ota::get_required_images(&header.manifest, "@gki_ramdisk", true)?
@@ -147,10 +147,10 @@ fn strip_image(
let mut context = ring::digest::Context::new(&ring::digest::SHA256);
let raw_writer =
File::create(output).with_context(|| anyhow!("Failed to open for writing: {output:?}"))?;
File::create(output).with_context(|| format!("Failed to open for writing: {output:?}"))?;
raw_writer
.set_len(file_size)
.with_context(|| anyhow!("Failed to set file size: {output:?}"))?;
.with_context(|| format!("Failed to set file size: {output:?}"))?;
let mut buf_writer = BufWriter::new(raw_writer);
let mut buf_reader = BufReader::new(raw_reader);
@@ -198,7 +198,7 @@ fn hash_file(path: &Path, cancel_signal: &Arc<AtomicBool>) -> Result<[u8; 32]> {
println!("Calculating hash of {path:?}");
let raw_reader =
File::open(path).with_context(|| anyhow!("Failed to open for reading: {path:?}"))?;
File::open(path).with_context(|| format!("Failed to open for reading: {path:?}"))?;
let buf_reader = BufReader::new(raw_reader);
let context = ring::digest::Context::new(&ring::digest::SHA256);
let mut hashing_reader = HashingReader::new(buf_reader, context);
@@ -249,7 +249,7 @@ fn download_file(
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)
.with_context(|| anyhow!("Failed to create directory: {parent:?}"))?;
.with_context(|| format!("Failed to create directory: {parent:?}"))?;
}
let mut do_validate = validate != Validate::Never;
@@ -376,7 +376,7 @@ fn test_keys() -> Result<(TempDir, Vec<OsString>, Vec<OsString>)> {
("ota.crt", &ota_cert[..], Some("--cert-ota"), Some("--cert-ota")),
] {
let path = temp_dir.path().join(name);
fs::write(&path, data).with_context(|| anyhow!("Failed to write test key: {path:?}"))?;
fs::write(&path, data).with_context(|| format!("Failed to write test key: {path:?}"))?;
if let Some(arg) = patch_arg {
patch_args.push(arg.into());
@@ -464,12 +464,12 @@ fn verify_image(input_file: &Path, cancel_signal: &Arc<AtomicBool>) -> Result<()
fn get_magisk_partition(path: &Path) -> Result<String> {
let raw_reader =
File::open(path).with_context(|| anyhow!("Failed to open for reading: {path:?}"))?;
File::open(path).with_context(|| format!("Failed to open for reading: {path:?}"))?;
let mut zip = ZipArchive::new(BufReader::new(raw_reader))
.with_context(|| anyhow!("Failed to read zip: {path:?}"))?;
.with_context(|| format!("Failed to read zip: {path:?}"))?;
let payload_entry = zip
.by_name(ota::PATH_PAYLOAD)
.with_context(|| anyhow!("Failed to open zip entry: {:?}", ota::PATH_PAYLOAD))?;
.with_context(|| format!("Failed to open zip entry: {:?}", ota::PATH_PAYLOAD))?;
let payload_offset = payload_entry.data_start();
let payload_size = payload_entry.size();
@@ -480,7 +480,7 @@ fn get_magisk_partition(path: &Path) -> Result<String> {
let mut payload_reader = SectionReader::new(buf_reader, payload_offset, payload_size)?;
let header = PayloadHeader::from_reader(&mut payload_reader)
.with_context(|| anyhow!("Failed to load OTA payload header"))?;
.context("Failed to load OTA payload header")?;
let images = avbroot::cli::ota::get_partitions_by_type(&header.manifest)?;
Ok(images["@gki_ramdisk"].clone())
@@ -625,11 +625,11 @@ fn add_subcommand(cli: &AddCli, cancel_signal: &Arc<AtomicBool>) -> Result<()> {
let config_serialized = document.to_string();
fs::write(&cli.config.config, config_serialized)
.with_context(|| anyhow!("Failed to write config: {:?}", cli.config.config))?;
.with_context(|| format!("Failed to write config: {:?}", cli.config.config))?;
if cli.patch.delete_on_success {
for path in [full_ota_patched, stripped_ota_patched] {
fs::remove_file(&path).with_context(|| anyhow!("Failed to delete file: {path:?}"))?;
fs::remove_file(&path).with_context(|| format!("Failed to delete file: {path:?}"))?;
}
}
@@ -745,7 +745,7 @@ fn test_subcommand(cli: &TestCli, cancel_signal: &Arc<AtomicBool>) -> Result<()>
];
fs::remove_file(&patched_file)
.with_context(|| anyhow!("Failed to delete file: {patched_file:?}"))?;
.with_context(|| format!("Failed to delete file: {patched_file:?}"))?;
patch_image(&image_file, &patched_file, &prepatched_args, cancel_signal)?;
@@ -753,7 +753,7 @@ fn test_subcommand(cli: &TestCli, cancel_signal: &Arc<AtomicBool>) -> Result<()>
if cli.patch.delete_on_success {
fs::remove_file(&patched_file)
.with_context(|| anyhow!("Failed to delete file: {patched_file:?}"))?;
.with_context(|| format!("Failed to delete file: {patched_file:?}"))?;
}
}
+2 -2
View File
@@ -1,6 +1,6 @@
id=com.chiller3.avbroot.clearotacerts
name=clearotacerts
version=v2.0.0
versionCode=131072
version=v2.0.3
versionCode=131075
author=chenxiaolong
description=Block A/B OTAs by clearing verification certificates
+2 -2
View File
@@ -1,6 +1,6 @@
id=com.chiller3.avbroot.oemunlockonboot
name=oemunlockonboot
version=v2.0.0
versionCode=131072
version=v2.0.3
versionCode=131075
author=chenxiaolong
description=Enable OEM unlocking on every boot