mirror of
https://github.com/chenxiaolong/avbroot.git
synced 2026-07-03 14:05:11 +02:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8ca4eb5ad9 | |||
| e2b1ccb7a1 | |||
| 71a31ae01b | |||
| 83d7ffbc5e | |||
| e397998d9e | |||
| 8ef22508f5 | |||
| bf42a6a75c | |||
| a2fe6fc9d8 | |||
| faeb1fe988 | |||
| f1b2c6f468 | |||
| 2f964bf113 | |||
| f393d7adc4 | |||
| 72a1c3f216 | |||
| 2db8d3826e | |||
| 1448e55205 | |||
| b3862a9c4a | |||
| 088db04673 |
@@ -7,6 +7,17 @@
|
||||
to update the actual links at the bottom of the file.
|
||||
-->
|
||||
|
||||
### Version 3.17.1
|
||||
|
||||
* Update end-to-end tests to place streaming and seekable OTAs in separate directories for easier troubleshooting ([PR #463])
|
||||
* Update dependencies ([PR #464])
|
||||
* Add support for Magisk 30000 ([PR #467])
|
||||
|
||||
### Version 3.17.0
|
||||
|
||||
* Fix reserved space error when patching OTA zips larger than ~10 GB ([Issue #451], [PR #452])
|
||||
* Update dependencies ([PR #453])
|
||||
|
||||
### Version 3.16.1
|
||||
|
||||
* Add support for Magisk 29000 ([PR #448])
|
||||
@@ -354,6 +365,7 @@ Behind-the-scenes changes:
|
||||
[Issue #393]: https://github.com/chenxiaolong/avbroot/issues/393
|
||||
[Issue #433]: https://github.com/chenxiaolong/avbroot/issues/433
|
||||
[Issue #441]: https://github.com/chenxiaolong/avbroot/issues/441
|
||||
[Issue #451]: https://github.com/chenxiaolong/avbroot/issues/451
|
||||
[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
|
||||
@@ -515,3 +527,8 @@ Behind-the-scenes changes:
|
||||
[PR #446]: https://github.com/chenxiaolong/avbroot/pull/446
|
||||
[PR #448]: https://github.com/chenxiaolong/avbroot/pull/448
|
||||
[PR #449]: https://github.com/chenxiaolong/avbroot/pull/449
|
||||
[PR #452]: https://github.com/chenxiaolong/avbroot/pull/452
|
||||
[PR #453]: https://github.com/chenxiaolong/avbroot/pull/453
|
||||
[PR #463]: https://github.com/chenxiaolong/avbroot/pull/463
|
||||
[PR #464]: https://github.com/chenxiaolong/avbroot/pull/464
|
||||
[PR #467]: https://github.com/chenxiaolong/avbroot/pull/467
|
||||
|
||||
Generated
+347
-173
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -4,7 +4,7 @@ members = ["avbroot", "e2e", "fuzz", "xtask"]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
version = "3.16.1"
|
||||
version = "3.17.1"
|
||||
license = "GPL-3.0-only"
|
||||
edition = "2024"
|
||||
repository = "https://github.com/chenxiaolong/avbroot"
|
||||
|
||||
@@ -51,6 +51,8 @@ avbroot applies the following patches to the partition images:
|
||||
|
||||
3. Follow the steps to [generate signing keys](#generating-keys).
|
||||
|
||||
Skip this step if you're updating Android, Magisk, or KernelSU after you've performed an [initial setup](#initial-setup). [Updates](#updates) do not require signing keys since you have already generated them in the initial setup.
|
||||
|
||||
4. Patch the OTA zip. The base command is:
|
||||
|
||||
```bash
|
||||
@@ -223,21 +225,25 @@ If you lose your AVB or OTA signing key, you will no longer be able to sign new
|
||||
|
||||
**WARNING**: If you are flashing CalyxOS, the setup wizard will [automatically turn off the `OEM unlocking` switch](https://github.com/CalyxOS/platform_packages_apps_SetupWizard/blob/7d2df25cedcbff83ddb608e628f9d97b38259c26/src/org/lineageos/setupwizard/SetupWizardApp.java#L135-L140). Make sure to manually reenable it again from Android's developer settings. Consider using the [`OEMUnlockOnBoot` module](https://github.com/chenxiaolong/OEMUnlockOnBoot) to automatically ensure OEM unlocking is enabled on every boot.
|
||||
|
||||
10. That's it! To install future OS, Magisk, or KernelSU updates, see the [next section](#updates).
|
||||
10. That's it! To update the OS, Magisk, or KernelSU see the [next section](#updates).
|
||||
|
||||
## Updates
|
||||
|
||||
Updates to Android, Magisk, and KernelSU are all done the same way by patching (or repatching) the OTA.
|
||||
Updates to Android, Magisk, and KernelSU are all done the same way: by patching (or repatching) the OTA.
|
||||
|
||||
1. If Magisk or KernelSU is being updated, first install their new `.apk`. If you happen to open the app, make sure it **does not** flash the boot image. Cancel the boot image update prompts if needed.
|
||||
1. Generate a new patched OTA by following the steps in the [usage section](#usage).
|
||||
|
||||
2. Follow the step in the [usage section](#usage) to patch the new OTA.
|
||||
2. If Magisk or KernelSU is being updated, first install their new `.apk`. If you happen to open the app, make sure it **does not** flash the boot image. Cancel the boot image update prompts if needed.
|
||||
|
||||
3. Reboot to recovery mode. If the screen is stuck at a `No command` message, press the volume up button once while holding down the power button.
|
||||
|
||||
4. Sideload the patched OTA with `adb sideload`.
|
||||
|
||||
5. That's it!
|
||||
5. Restart your phone. Note: the phone will likely take a long time to startup after an OS update (a few minutes in some cases).
|
||||
|
||||
**Warning**: Due to how virtual A/B works, there is a snapshot merge operation that Android runs invisibly in the background after installing an OTA and rebooting. During the snapshot merge process, it's not possible to sideload another OTA from recovery mode. Avoid doing anything that could result in a boot loop (eg. installing modules) until this process is complete because there is no way to recover, aside from unlocking the bootloader (and wiping) again.
|
||||
|
||||
The status can be found by running `adb logcat -v color -s update_engine`. Alternatively, if [Custota](https://github.com/chenxiaolong/Custota) is installed (even if it's not configured to point to a custom OTA server), it will show a notification until the snapshot merge operation completes.
|
||||
|
||||
## Reverting to stock firmware
|
||||
|
||||
|
||||
+10
-8
@@ -13,7 +13,7 @@ anyhow = "1.0.75"
|
||||
base64 = "0.22.1"
|
||||
bitflags = { version = "2.4.1", features = ["serde"] }
|
||||
bstr = "1.6.2"
|
||||
bzip2 = { version = "0.5.1", default-features = false, features = ["libbz2-rs-sys"] }
|
||||
bzip2 = "0.6.0"
|
||||
cap-std = "3.0.0"
|
||||
cap-tempfile = "3.0.0"
|
||||
clap = { version = "4.4.1", features = ["derive"] }
|
||||
@@ -34,9 +34,9 @@ memchr = "2.6.0"
|
||||
num-bigint-dig = "0.8.4"
|
||||
num-traits = "0.2.16"
|
||||
passterm = "2.0.3"
|
||||
phf = { version = "0.11.2", features = ["macros"] }
|
||||
phf = { version = "0.12.1", features = ["macros"] }
|
||||
pkcs8 = { version = "0.10.2", features = ["encryption", "pem"] }
|
||||
prost = "0.13.1"
|
||||
prost = "0.14.1"
|
||||
# We can't upgrade to 0.9.0 until rsa updates its rand_core dependency.
|
||||
rand = "0.8.5"
|
||||
rayon = "1.7.0"
|
||||
@@ -60,10 +60,12 @@ x509-cert = { version = "0.2.4", features = ["builder"] }
|
||||
zerocopy = { version = "0.8.10", features = ["std"] }
|
||||
zerocopy-derive = "0.8.5"
|
||||
|
||||
# https://github.com/zip-rs/zip/pull/383
|
||||
# https://github.com/zip-rs/zip2/pull/367
|
||||
# https://github.com/zip-rs/zip2/pull/368
|
||||
# For getting the data offset when writing new zip entries.
|
||||
[dependencies.zip]
|
||||
git = "https://github.com/chenxiaolong/zip"
|
||||
rev = "989101f9384b9e94e36e6e9e0f51908fdf98bde6"
|
||||
git = "https://github.com/chenxiaolong/zip2"
|
||||
rev = "59685f4dadbfee8cb3ea74c8fbb402b60d8137e8"
|
||||
default-features = false
|
||||
features = ["deflate"]
|
||||
|
||||
@@ -73,8 +75,8 @@ rustix = { version = "1.0.3", default-features = false, features = ["process"] }
|
||||
|
||||
[build-dependencies]
|
||||
constcat = "0.6.0"
|
||||
prost-build = "0.13.1"
|
||||
protox = "0.8.0"
|
||||
prost-build = "0.14.1"
|
||||
protox = "0.9.0"
|
||||
|
||||
[dev-dependencies]
|
||||
assert_matches = "1.5.0"
|
||||
|
||||
@@ -10,7 +10,7 @@ use std::{
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::{Parser, Subcommand, ValueEnum};
|
||||
use tracing::{debug, Level};
|
||||
use tracing::{Level, debug};
|
||||
use tracing_subscriber::fmt::{format::Writer, time::FormatTime};
|
||||
|
||||
use crate::cli::{avb, boot, completion, cpio, fec, hashtree, key, lp, ota, payload, sparse};
|
||||
|
||||
@@ -10,7 +10,7 @@ use std::{
|
||||
sync::atomic::AtomicBool,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use anyhow::{Context, Result, anyhow, bail};
|
||||
use cap_std::{
|
||||
ambient_authority,
|
||||
fs::{Dir, OpenOptions},
|
||||
@@ -19,7 +19,7 @@ use clap::{Args, Parser, Subcommand};
|
||||
use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
|
||||
use rsa::RsaPublicKey;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::{debug_span, info, warn, Span};
|
||||
use tracing::{Span, debug_span, info, warn};
|
||||
|
||||
use crate::{
|
||||
crypto::{self, PassphraseSource, RsaSigningKey},
|
||||
@@ -27,7 +27,7 @@ use crate::{
|
||||
self, AlgorithmType, AppendedDescriptorMut, AppendedDescriptorRef, Descriptor, Footer,
|
||||
HashTreeDescriptor, Header, KernelCmdlineDescriptor,
|
||||
},
|
||||
stream::{self, check_cancel, PSeekFile, ReadFixedSizeExt, Reopen, ToWriter},
|
||||
stream::{self, PSeekFile, ReadFixedSizeExt, Reopen, ToWriter, check_cancel},
|
||||
util,
|
||||
};
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ use std::{
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use anyhow::{bail, Context, Result};
|
||||
use anyhow::{Context, Result, bail};
|
||||
use clap::{Parser, Subcommand};
|
||||
|
||||
use crate::{
|
||||
|
||||
@@ -9,7 +9,7 @@ use std::{
|
||||
sync::atomic::AtomicBool,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use anyhow::{Context, Result, anyhow};
|
||||
use bstr::ByteSlice;
|
||||
use cap_std::{ambient_authority, fs::Dir};
|
||||
use clap::{Parser, Subcommand};
|
||||
|
||||
@@ -9,7 +9,7 @@ use std::{
|
||||
sync::atomic::AtomicBool,
|
||||
};
|
||||
|
||||
use anyhow::{bail, Context, Result};
|
||||
use anyhow::{Context, Result, bail};
|
||||
use cap_std::{ambient_authority, fs::Dir};
|
||||
use clap::{CommandFactory, Parser, Subcommand};
|
||||
use rayon::iter::{
|
||||
|
||||
+18
-15
@@ -11,20 +11,20 @@ use std::{
|
||||
ops::Range,
|
||||
path::{Path, PathBuf},
|
||||
str::FromStr,
|
||||
sync::{atomic::AtomicBool, Mutex},
|
||||
sync::{Mutex, atomic::AtomicBool},
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use anyhow::{Context, Result, anyhow, bail};
|
||||
use bitflags::bitflags;
|
||||
use cap_std::{ambient_authority, fs::Dir};
|
||||
use cap_tempfile::TempDir;
|
||||
use clap::{value_parser, ArgAction, Args, Parser, Subcommand};
|
||||
use clap::{ArgAction, Args, Parser, Subcommand, value_parser};
|
||||
use rayon::{iter::IntoParallelRefIterator, prelude::ParallelIterator};
|
||||
use tempfile::NamedTempFile;
|
||||
use topological_sort::TopologicalSort;
|
||||
use tracing::{debug_span, error, info, warn};
|
||||
use x509_cert::Certificate;
|
||||
use zip::{write::FileOptions, CompressionMethod, ZipArchive, ZipWriter};
|
||||
use zip::{CompressionMethod, DateTime, ZipArchive, write::SimpleFileOptions};
|
||||
|
||||
use crate::{
|
||||
cli,
|
||||
@@ -34,6 +34,7 @@ use crate::{
|
||||
ota::{self, SigningWriter, ZipEntry, ZipMode},
|
||||
padding,
|
||||
payload::{self, CowVersion, PayloadHeader, PayloadWriter, VabcAlgo, VabcParams},
|
||||
zip::ZipWriterWrapper,
|
||||
},
|
||||
patch::{
|
||||
boot::{
|
||||
@@ -495,7 +496,9 @@ fn update_security_descriptors(
|
||||
*pd = cd.clone();
|
||||
}
|
||||
_ => {
|
||||
bail!("{child_name} descriptor ({child_type}) does not match entry in {parent_name} ({parent_type})");
|
||||
bail!(
|
||||
"{child_name} descriptor ({child_type}) does not match entry in {parent_name} ({parent_type})"
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -505,7 +508,9 @@ fn update_security_descriptors(
|
||||
child_header.public_key.clone_into(&mut pd.public_key);
|
||||
}
|
||||
_ => {
|
||||
bail!("{child_name} descriptor ({parent_type}) in {parent_name} must be a chain descriptor");
|
||||
bail!(
|
||||
"{child_name} descriptor ({parent_type}) in {parent_name} must be a chain descriptor"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1111,7 +1116,7 @@ fn patch_ota_payload(
|
||||
fn patch_ota_zip(
|
||||
raw_reader: &PSeekFile,
|
||||
zip_reader: &mut ZipArchive<impl Read + Seek>,
|
||||
mut zip_writer: &mut ZipWriter<impl Write>,
|
||||
mut zip_writer: &mut ZipWriterWrapper<impl Write>,
|
||||
external_images: &HashMap<String, PathBuf>,
|
||||
boot_patchers: &[Box<dyn BootImagePatch + Sync>],
|
||||
skip_system_ota_cert: bool,
|
||||
@@ -1165,7 +1170,8 @@ fn patch_ota_zip(
|
||||
// threshold. This should be sufficient since the output file is likely
|
||||
// to be larger.
|
||||
let use_zip64 = reader.size() >= 0xffffffff;
|
||||
let options = FileOptions::default()
|
||||
let options = SimpleFileOptions::default()
|
||||
.last_modified_time(DateTime::default())
|
||||
.compression_method(CompressionMethod::Stored)
|
||||
.large_file(use_zip64);
|
||||
|
||||
@@ -1202,12 +1208,9 @@ fn patch_ota_zip(
|
||||
}
|
||||
|
||||
// All remaining entries are written immediately.
|
||||
zip_writer
|
||||
.start_file_with_extra_data(path, options)
|
||||
.with_context(|| format!("Failed to begin new zip entry: {path}"))?;
|
||||
let offset = zip_writer
|
||||
.end_extra_data()
|
||||
.with_context(|| format!("Failed to end new zip entry: {path}"))?;
|
||||
.start_file(path, options)
|
||||
.with_context(|| format!("Failed to begin new zip entry: {path}"))?;
|
||||
let mut writer = CountingWriter::new(&mut zip_writer);
|
||||
|
||||
match path.as_str() {
|
||||
@@ -1540,11 +1543,11 @@ pub fn patch_subcommand(cli: &PatchCli, cancel_signal: &AtomicBool) -> Result<()
|
||||
let mut zip_writer = match cli.zip_mode {
|
||||
ZipMode::Streaming => {
|
||||
let signing_writer = SigningWriter::new_streaming(temp_writer);
|
||||
ZipWriter::new_streaming(signing_writer)
|
||||
ZipWriterWrapper::new_streaming(signing_writer)
|
||||
}
|
||||
ZipMode::Seekable => {
|
||||
let signing_writer = SigningWriter::new_seekable(temp_writer);
|
||||
ZipWriter::new(signing_writer)
|
||||
ZipWriterWrapper::new_seekable(signing_writer)
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ use std::{
|
||||
sync::atomic::AtomicBool,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use anyhow::{Context, Result, anyhow, bail};
|
||||
use cap_std::{ambient_authority, fs::Dir};
|
||||
use clap::{Args, Parser, Subcommand};
|
||||
use tracing::info;
|
||||
|
||||
@@ -10,10 +10,10 @@ use std::{
|
||||
sync::atomic::AtomicBool,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use anyhow::{Context, Result, anyhow, bail};
|
||||
use clap::{Parser, Subcommand};
|
||||
use crc32fast::Hasher;
|
||||
use zerocopy::{little_endian, IntoBytes};
|
||||
use zerocopy::{IntoBytes, little_endian};
|
||||
|
||||
use crate::{
|
||||
format::{
|
||||
|
||||
@@ -21,25 +21,25 @@ use cms::{
|
||||
};
|
||||
use passterm::PromptError;
|
||||
use pkcs8::{
|
||||
pkcs5::{pbes2, scrypt},
|
||||
DecodePrivateKey, DecodePublicKey, EncodePrivateKey, EncodePublicKey, EncryptedPrivateKeyInfo,
|
||||
LineEnding, PrivateKeyInfo,
|
||||
pkcs5::{pbes2, scrypt},
|
||||
};
|
||||
use rand::RngCore;
|
||||
use rsa::{
|
||||
pkcs1v15::SigningKey, traits::PublicKeyParts, Pkcs1v15Sign, RsaPrivateKey, RsaPublicKey,
|
||||
Pkcs1v15Sign, RsaPrivateKey, RsaPublicKey, pkcs1v15::SigningKey, traits::PublicKeyParts,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha1::Sha1;
|
||||
use sha2::{Digest, Sha256, Sha512};
|
||||
use thiserror::Error;
|
||||
use x509_cert::{
|
||||
Certificate,
|
||||
builder::{Builder, CertificateBuilder, Profile},
|
||||
der::{pem::PemLabel, referenced::OwnedToRef, Any, Decode, DecodePem, EncodePem},
|
||||
der::{Any, Decode, DecodePem, EncodePem, pem::PemLabel, referenced::OwnedToRef},
|
||||
serial_number::SerialNumber,
|
||||
spki::{AlgorithmIdentifierOwned, SubjectPublicKeyInfoOwned},
|
||||
time::Validity,
|
||||
Certificate,
|
||||
};
|
||||
|
||||
use crate::util::DebugString;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
use std::{fmt, marker::PhantomData};
|
||||
|
||||
use bstr::{ByteSlice, ByteVec};
|
||||
use serde::{de::Visitor, Deserializer, Serializer};
|
||||
use serde::{Deserializer, Serializer, de::Visitor};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Clone, Debug, Error)]
|
||||
|
||||
@@ -14,10 +14,10 @@ use bstr::ByteSlice;
|
||||
use num_bigint_dig::{ModInverse, ToBigInt};
|
||||
use num_traits::{Pow, ToPrimitive};
|
||||
use ring::digest::{Algorithm, Context};
|
||||
use rsa::{traits::PublicKeyParts, BigUint, RsaPublicKey};
|
||||
use rsa::{BigUint, RsaPublicKey, traits::PublicKeyParts};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
use zerocopy::{big_endian, FromBytes, IntoBytes};
|
||||
use zerocopy::{FromBytes, IntoBytes, big_endian};
|
||||
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
|
||||
|
||||
use crate::{
|
||||
|
||||
@@ -13,7 +13,7 @@ use num_traits::ToPrimitive;
|
||||
use ring::digest::Context;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
use zerocopy::{little_endian, FromBytes, IntoBytes};
|
||||
use zerocopy::{FromBytes, IntoBytes, little_endian};
|
||||
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
|
||||
|
||||
use crate::{
|
||||
@@ -669,11 +669,7 @@ impl fmt::Display for BootImageV3Through4 {
|
||||
|
||||
impl BootImageExt for BootImageV3Through4 {
|
||||
fn header_version(&self) -> u32 {
|
||||
if self.v4_extra.is_some() {
|
||||
4
|
||||
} else {
|
||||
3
|
||||
}
|
||||
if self.v4_extra.is_some() { 4 } else { 3 }
|
||||
}
|
||||
|
||||
fn header_size(&self) -> u32 {
|
||||
@@ -1094,11 +1090,7 @@ impl fmt::Display for VendorBootImageV3Through4 {
|
||||
|
||||
impl BootImageExt for VendorBootImageV3Through4 {
|
||||
fn header_version(&self) -> u32 {
|
||||
if self.v4_extra.is_some() {
|
||||
4
|
||||
} else {
|
||||
3
|
||||
}
|
||||
if self.v4_extra.is_some() { 4 } else { 3 }
|
||||
}
|
||||
|
||||
fn header_size(&self) -> u32 {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use std::io::{self, Read, Seek, Write};
|
||||
|
||||
use flate2::{read::GzDecoder, write::GzEncoder, Compression};
|
||||
use flate2::{Compression, read::GzDecoder, write::GzEncoder};
|
||||
use liblzma::{
|
||||
read::XzDecoder,
|
||||
stream::{Check, Stream},
|
||||
|
||||
@@ -771,11 +771,7 @@ pub fn sort(entries: &mut [CpioEntry]) {
|
||||
/// 300000.
|
||||
pub fn assign_inodes(entries: &mut [CpioEntry], missing_only: bool) -> Result<()> {
|
||||
fn next_non_zero(i: u32) -> u32 {
|
||||
if i == u32::MAX {
|
||||
1
|
||||
} else {
|
||||
i.wrapping_add(1)
|
||||
}
|
||||
if i == u32::MAX { 1 } else { i.wrapping_add(1) }
|
||||
}
|
||||
|
||||
// (dev maj, dev min) -> (inode set, last assigned inode)
|
||||
|
||||
@@ -16,7 +16,7 @@ use rayon::{
|
||||
slice::{ParallelSlice, ParallelSliceMut},
|
||||
};
|
||||
use thiserror::Error;
|
||||
use zerocopy::{little_endian, FromBytes, IntoBytes};
|
||||
use zerocopy::{FromBytes, IntoBytes, little_endian};
|
||||
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
|
||||
|
||||
use crate::{
|
||||
@@ -827,7 +827,7 @@ impl<W: Write> ToWriter<W> for FecImage {
|
||||
mod tests {
|
||||
use std::{
|
||||
io::{Cursor, Seek},
|
||||
sync::{atomic::AtomicBool, Arc},
|
||||
sync::{Arc, atomic::AtomicBool},
|
||||
};
|
||||
|
||||
use assert_matches::assert_matches;
|
||||
|
||||
@@ -16,7 +16,7 @@ use rayon::{
|
||||
};
|
||||
use ring::digest::{Algorithm, Context};
|
||||
use thiserror::Error;
|
||||
use zerocopy::{little_endian, FromBytes, IntoBytes};
|
||||
use zerocopy::{FromBytes, IntoBytes, little_endian};
|
||||
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
|
||||
|
||||
use crate::{
|
||||
|
||||
@@ -13,7 +13,7 @@ use bitflags::bitflags;
|
||||
use bstr::ByteSlice;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
use zerocopy::{byteorder::little_endian, FromBytes, FromZeros, Immutable, IntoBytes};
|
||||
use zerocopy::{FromBytes, FromZeros, Immutable, IntoBytes, byteorder::little_endian};
|
||||
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
|
||||
|
||||
use crate::{
|
||||
@@ -21,7 +21,7 @@ use crate::{
|
||||
stream::{
|
||||
CountingReader, FromReader, ReadDiscardExt, ReadFixedSizeExt, ToWriter, WriteZerosExt,
|
||||
},
|
||||
util::{self, is_zero, DebugString},
|
||||
util::{self, DebugString, is_zero},
|
||||
};
|
||||
|
||||
/// Magic value for [`RawGeometry::magic`].
|
||||
@@ -812,7 +812,7 @@ impl RawExtent {
|
||||
return Err(Error::ExtentInvalidType {
|
||||
index,
|
||||
extent_type: n,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: 2023 Andrew Gunnerson
|
||||
// SPDX-FileCopyrightText: 2023-2025 Andrew Gunnerson
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
pub mod avb;
|
||||
@@ -13,3 +13,4 @@ pub mod padding;
|
||||
pub mod payload;
|
||||
pub mod sparse;
|
||||
pub mod verityrs;
|
||||
pub mod zip;
|
||||
|
||||
+29
-27
@@ -12,18 +12,21 @@ use std::{
|
||||
|
||||
use clap::ValueEnum;
|
||||
use cms::signed_data::SignedData;
|
||||
use const_oid::{db::rfc5912, ObjectIdentifier};
|
||||
use const_oid::{ObjectIdentifier, db::rfc5912};
|
||||
use memchr::memmem;
|
||||
use prost::Message;
|
||||
use ring::digest::{Algorithm, Context};
|
||||
use thiserror::Error;
|
||||
use x509_cert::{der::Encode, Certificate};
|
||||
use zip::{result::ZipError, write::FileOptions, CompressionMethod, ZipArchive, ZipWriter};
|
||||
use x509_cert::{Certificate, der::Encode};
|
||||
use zip::{CompressionMethod, DateTime, ZipArchive, result::ZipError, write::SimpleFileOptions};
|
||||
|
||||
use crate::{
|
||||
crypto::{self, RsaPublicKeyExt, RsaSigningKey, SignatureAlgorithm},
|
||||
format::payload::{self, PayloadHeader},
|
||||
protobuf::build::tools::releasetools::{ota_metadata::OtaType, OtaMetadata},
|
||||
format::{
|
||||
payload::{self, PayloadHeader},
|
||||
zip::ZipWriterWrapper,
|
||||
},
|
||||
protobuf::build::tools::releasetools::{OtaMetadata, ota_metadata::OtaType},
|
||||
stream::{self, FromReader, HashingReader, HashingWriter, ReadFixedSizeExt},
|
||||
};
|
||||
|
||||
@@ -323,6 +326,15 @@ fn compute_property_files(
|
||||
max_length: Option<usize>,
|
||||
want_pb: bool,
|
||||
) -> Result<String> {
|
||||
// AOSP's ota_utils.py reserves 15 bytes for the `<offset>:<size>`
|
||||
// placeholder. Since the size of `metadata.pb` is almost always 4 digits,
|
||||
// this prevents the offset from exceeding 10 digits. In the wild, there are
|
||||
// OTA files larger than 10 GB. With ota_utils.py, this limit is never
|
||||
// reached because it puts the OTA metadata files at the beginning of the
|
||||
// zip. However, avbroot needs to put them at the end due to streaming
|
||||
// writes, so we reserve an additional byte to allow offsets <100 GB.
|
||||
const RESERVATION_SIZE: usize = 16;
|
||||
|
||||
let compute = |path: &'static str| -> Result<String> {
|
||||
let entry = entries
|
||||
.iter()
|
||||
@@ -355,9 +367,9 @@ fn compute_property_files(
|
||||
}
|
||||
|
||||
if max_length.is_none() {
|
||||
tokens.push(format!("metadata:{}", " ".repeat(15)));
|
||||
tokens.push(format!("metadata:{}", " ".repeat(RESERVATION_SIZE)));
|
||||
if want_pb {
|
||||
tokens.push(format!("metadata.pb:{}", " ".repeat(15)));
|
||||
tokens.push(format!("metadata.pb:{}", " ".repeat(RESERVATION_SIZE)));
|
||||
}
|
||||
} else {
|
||||
tokens.push(compute(PATH_METADATA)?);
|
||||
@@ -426,14 +438,16 @@ impl fmt::Display for ZipMode {
|
||||
/// directory would start.
|
||||
pub fn add_metadata(
|
||||
zip_entries: &[ZipEntry],
|
||||
zip_writer: &mut ZipWriter<impl Write>,
|
||||
zip_writer: &mut ZipWriterWrapper<impl Write>,
|
||||
next_offset: u64,
|
||||
metadata: &OtaMetadata,
|
||||
payload_metadata_size: u64,
|
||||
zip_mode: ZipMode,
|
||||
) -> Result<OtaMetadata> {
|
||||
let mut metadata = metadata.clone();
|
||||
let options = FileOptions::default().compression_method(CompressionMethod::Stored);
|
||||
let options = SimpleFileOptions::default()
|
||||
.last_modified_time(DateTime::default())
|
||||
.compression_method(CompressionMethod::Stored);
|
||||
|
||||
let mut zip_entries = zip_entries.to_owned();
|
||||
add_payload_metadata_entry(&mut zip_entries, payload_metadata_size)?;
|
||||
@@ -453,25 +467,19 @@ pub fn add_metadata(
|
||||
let (legacy_raw, modern_raw) = serialize_metadata(&metadata);
|
||||
let raw_writer = Cursor::new(Vec::new());
|
||||
let mut writer = match zip_mode {
|
||||
ZipMode::Streaming => ZipWriter::new_streaming(raw_writer),
|
||||
ZipMode::Seekable => ZipWriter::new(raw_writer),
|
||||
ZipMode::Streaming => ZipWriterWrapper::new_streaming(raw_writer),
|
||||
ZipMode::Seekable => ZipWriterWrapper::new_seekable(raw_writer),
|
||||
};
|
||||
|
||||
writer
|
||||
.start_file_with_extra_data(PATH_METADATA, options)
|
||||
.map_err(|e| Error::ZipEntryStart(PATH_METADATA, e))?;
|
||||
let legacy_offset = writer
|
||||
.end_extra_data()
|
||||
.start_file(PATH_METADATA, options)
|
||||
.map_err(|e| Error::ZipEntryStart(PATH_METADATA, e))?;
|
||||
writer
|
||||
.write_all(legacy_raw.as_bytes())
|
||||
.map_err(|e| Error::ZipEntryWrite(PATH_METADATA, e))?;
|
||||
|
||||
writer
|
||||
.start_file_with_extra_data(PATH_METADATA_PB, options)
|
||||
.map_err(|e| Error::ZipEntryStart(PATH_METADATA_PB, e))?;
|
||||
let modern_offset = writer
|
||||
.end_extra_data()
|
||||
.start_file(PATH_METADATA_PB, options)
|
||||
.map_err(|e| Error::ZipEntryStart(PATH_METADATA_PB, e))?;
|
||||
writer
|
||||
.write_all(&modern_raw)
|
||||
@@ -500,21 +508,15 @@ pub fn add_metadata(
|
||||
{
|
||||
let (legacy_raw, modern_raw) = serialize_metadata(&metadata);
|
||||
|
||||
zip_writer
|
||||
.start_file_with_extra_data(PATH_METADATA, options)
|
||||
.map_err(|e| Error::ZipEntryStart(PATH_METADATA, e))?;
|
||||
let legacy_offset = zip_writer
|
||||
.end_extra_data()
|
||||
.start_file(PATH_METADATA, options)
|
||||
.map_err(|e| Error::ZipEntryStart(PATH_METADATA, e))?;
|
||||
zip_writer
|
||||
.write_all(legacy_raw.as_bytes())
|
||||
.map_err(|e| Error::ZipEntryWrite(PATH_METADATA, e))?;
|
||||
|
||||
zip_writer
|
||||
.start_file_with_extra_data(PATH_METADATA_PB, options)
|
||||
.map_err(|e| Error::ZipEntryStart(PATH_METADATA_PB, e))?;
|
||||
let modern_offset = zip_writer
|
||||
.end_extra_data()
|
||||
.start_file(PATH_METADATA_PB, options)
|
||||
.map_err(|e| Error::ZipEntryStart(PATH_METADATA_PB, e))?;
|
||||
zip_writer
|
||||
.write_all(&modern_raw)
|
||||
|
||||
@@ -11,10 +11,10 @@ use std::{
|
||||
sync::atomic::AtomicBool,
|
||||
};
|
||||
|
||||
use base64::engine::general_purpose::STANDARD;
|
||||
use base64::Engine;
|
||||
use base64::engine::general_purpose::STANDARD;
|
||||
use bzip2::write::BzDecoder;
|
||||
use flate2::{write::GzEncoder, Compression};
|
||||
use flate2::{Compression, write::GzEncoder};
|
||||
use liblzma::{
|
||||
stream::{Check, Stream},
|
||||
write::XzDecoder,
|
||||
@@ -30,14 +30,14 @@ use ring::digest::{Context, Digest};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
use x509_cert::Certificate;
|
||||
use zerocopy::{big_endian, FromBytes, IntoBytes};
|
||||
use zerocopy::{FromBytes, IntoBytes, big_endian};
|
||||
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
|
||||
|
||||
use crate::{
|
||||
crypto::{self, RsaPublicKeyExt, RsaSigningKey, SignatureAlgorithm},
|
||||
protobuf::chromeos_update_engine::{
|
||||
install_operation::Type, signatures::Signature, DeltaArchiveManifest, Extent,
|
||||
InstallOperation, PartitionInfo, PartitionUpdate, Signatures,
|
||||
DeltaArchiveManifest, Extent, InstallOperation, PartitionInfo, PartitionUpdate, Signatures,
|
||||
install_operation::Type, signatures::Signature,
|
||||
},
|
||||
stream::{
|
||||
self, CountingReader, FromReader, HashingWriter, ReadDiscardExt, ReadFixedSizeExt,
|
||||
@@ -121,7 +121,9 @@ pub enum Error {
|
||||
DataWrite(&'static str, #[source] io::Error),
|
||||
#[error("Expected {expected} bytes, but only wrote {actual} bytes")]
|
||||
UnwrittenData { actual: u64, expected: u64 },
|
||||
#[error("I/O error when applying {op_type:?} operation for {num_blocks} blocks starting at {start_block}")]
|
||||
#[error(
|
||||
"I/O error when applying {op_type:?} operation for {num_blocks} blocks starting at {start_block}"
|
||||
)]
|
||||
OperationApply {
|
||||
op_type: Type,
|
||||
start_block: u64,
|
||||
|
||||
@@ -11,7 +11,7 @@ use std::{
|
||||
use crc32fast::Hasher;
|
||||
use dlv_list::{Index, VecList};
|
||||
use thiserror::Error;
|
||||
use zerocopy::{byteorder::little_endian, FromBytes, IntoBytes};
|
||||
use zerocopy::{FromBytes, IntoBytes, byteorder::little_endian};
|
||||
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
|
||||
|
||||
use crate::stream::ReadDiscardExt;
|
||||
@@ -225,7 +225,7 @@ impl RawChunk {
|
||||
return Err(Error::InvalidChunkType {
|
||||
index,
|
||||
chunk_type: t,
|
||||
})
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
// SPDX-FileCopyrightText: 2025 Andrew Gunnerson
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use std::io::{self, Seek, SeekFrom, Write};
|
||||
|
||||
use zip::{
|
||||
ZipWriter,
|
||||
result::ZipResult,
|
||||
write::{FileOptionExtension, FileOptions, StreamWriter},
|
||||
};
|
||||
|
||||
/// A wrapper around a seekable writer. `W` must implement [`Seek`], but only
|
||||
/// during the creation of a new instance. The resulting type can be stored in a
|
||||
/// parent container where the generic type does not implement [`Seek`].
|
||||
pub struct SeekWriter<W: Write> {
|
||||
inner: W,
|
||||
seek_fn: fn(&mut W, SeekFrom) -> io::Result<u64>,
|
||||
}
|
||||
|
||||
impl<W: Write> SeekWriter<W> {
|
||||
pub fn into_inner(self) -> W {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write + Seek> SeekWriter<W> {
|
||||
pub fn new(inner: W) -> Self {
|
||||
Self {
|
||||
inner,
|
||||
seek_fn: W::seek,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> Write for SeekWriter<W> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.inner.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.inner.flush()
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> Seek for SeekWriter<W> {
|
||||
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||
(self.seek_fn)(&mut self.inner, pos)
|
||||
}
|
||||
}
|
||||
|
||||
/// This is an ugly hack to have a single type represent both seekable and
|
||||
/// streaming [`ZipWriter`]s. `W` only needs to implement [`Seek`] when creating
|
||||
/// a seekable instance via [`Self::new_seekable`].
|
||||
pub enum ZipWriterWrapper<W: Write> {
|
||||
Streaming(ZipWriter<StreamWriter<W>>),
|
||||
Seekable(ZipWriter<SeekWriter<W>>),
|
||||
}
|
||||
|
||||
impl<W: Write + Seek> ZipWriterWrapper<W> {
|
||||
pub fn new_seekable(inner: W) -> Self {
|
||||
Self::Seekable(ZipWriter::new(SeekWriter::new(inner)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> ZipWriterWrapper<W> {
|
||||
pub fn new_streaming(inner: W) -> Self {
|
||||
Self::Streaming(ZipWriter::new_stream(inner))
|
||||
}
|
||||
|
||||
pub fn start_file(
|
||||
&mut self,
|
||||
name: impl ToString,
|
||||
options: FileOptions<impl FileOptionExtension>,
|
||||
) -> ZipResult<u64> {
|
||||
match self {
|
||||
Self::Streaming(z) => z.start_file(name, options),
|
||||
Self::Seekable(z) => z.start_file(name, options),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn finish(self) -> ZipResult<W> {
|
||||
match self {
|
||||
Self::Streaming(z) => Ok(z.finish()?.into_inner()),
|
||||
Self::Seekable(z) => Ok(z.finish()?.into_inner()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> Write for ZipWriterWrapper<W> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
match self {
|
||||
Self::Streaming(z) => z.write(buf),
|
||||
Self::Seekable(z) => z.write(buf),
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
match self {
|
||||
Self::Streaming(z) => z.flush(),
|
||||
Self::Seekable(z) => z.flush(),
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -4,8 +4,8 @@
|
||||
use std::{
|
||||
process::ExitCode,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
atomic::{AtomicBool, Ordering},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ use std::{
|
||||
};
|
||||
|
||||
use num_traits::{Num, PrimInt};
|
||||
use serde::{de::Visitor, Deserializer, Serializer};
|
||||
use serde::{Deserializer, Serializer, de::Visitor};
|
||||
|
||||
pub fn serialize<S, T>(data: &T, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
|
||||
@@ -24,9 +24,9 @@ use regex::bytes::Regex;
|
||||
use ring::digest::Context;
|
||||
use rsa::RsaPublicKey;
|
||||
use thiserror::Error;
|
||||
use tracing::{debug, debug_span, trace, warn, Span};
|
||||
use tracing::{Span, debug, debug_span, trace, warn};
|
||||
use x509_cert::Certificate;
|
||||
use zip::{result::ZipError, ZipArchive};
|
||||
use zip::{ZipArchive, result::ZipError};
|
||||
|
||||
use crate::{
|
||||
crypto::{self, RsaSigningKey},
|
||||
@@ -194,7 +194,7 @@ impl MagiskRootPatcher {
|
||||
// replaced by PREINITDEVICE
|
||||
// - Versions newer than the latest supported version are assumed to support
|
||||
// the same features as the latest version
|
||||
const VERS_SUPPORTED: &'static [Range<u32>] = &[25102..25207, 25211..29100];
|
||||
const VERS_SUPPORTED: &'static [Range<u32>] = &[25102..25207, 25211..30100];
|
||||
const VER_PREINIT_DEVICE: RangeFrom<u32> = 25211..;
|
||||
const VER_RANDOM_SEED: Range<u32> = 25211..26103;
|
||||
const VER_PATCH_VBMETA: Range<u32> = Self::VERS_SUPPORTED[0].start..26202;
|
||||
|
||||
@@ -6,8 +6,8 @@ use std::{borrow::Cow, cmp::Ordering, io::Cursor, path::Path};
|
||||
use bitflags::bitflags;
|
||||
use thiserror::Error;
|
||||
use tracing::trace;
|
||||
use x509_cert::{der::asn1::BitString, Certificate};
|
||||
use zip::{result::ZipError, write::FileOptions, CompressionMethod, ZipWriter};
|
||||
use x509_cert::{Certificate, der::asn1::BitString};
|
||||
use zip::{CompressionMethod, DateTime, ZipWriter, result::ZipError, write::SimpleFileOptions};
|
||||
|
||||
use crate::{crypto, format::ota};
|
||||
|
||||
@@ -79,7 +79,9 @@ pub fn create_zip(cert: &Certificate, flags: OtaCertBuildFlags) -> Result<Vec<u8
|
||||
CompressionMethod::Stored
|
||||
};
|
||||
|
||||
let options = FileOptions::default().compression_method(compression_method);
|
||||
let options = SimpleFileOptions::default()
|
||||
.last_modified_time(DateTime::default())
|
||||
.compression_method(compression_method);
|
||||
let name = "ota.x509.pem";
|
||||
writer.start_file(name, options).map_err(Error::ZipWrite)?;
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ use std::{
|
||||
use memchr::memmem;
|
||||
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
||||
use thiserror::Error;
|
||||
use tracing::{debug, debug_span, trace, Span};
|
||||
use tracing::{Span, debug, debug_span, trace};
|
||||
use x509_cert::Certificate;
|
||||
use zip::ZipArchive;
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@ use std::{
|
||||
fs::File,
|
||||
io::{self, BufReader, BufWriter, Cursor, Read, Seek, SeekFrom, Write},
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc, Mutex, RwLock,
|
||||
atomic::{AtomicBool, Ordering},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -69,5 +69,5 @@ bypass = [
|
||||
unknown-registry = "deny"
|
||||
unknown-git = "deny"
|
||||
allow-git = [
|
||||
"https://github.com/chenxiaolong/zip",
|
||||
"https://github.com/chenxiaolong/zip2",
|
||||
]
|
||||
|
||||
+5
-3
@@ -24,10 +24,12 @@ tracing = "0.1.40"
|
||||
tracing-subscriber = "0.3.18"
|
||||
x509-cert = "0.2.5"
|
||||
|
||||
# https://github.com/zip-rs/zip/pull/383
|
||||
# https://github.com/zip-rs/zip2/pull/367
|
||||
# https://github.com/zip-rs/zip2/pull/368
|
||||
# For getting the data offset when writing new zip entries.
|
||||
[dependencies.zip]
|
||||
git = "https://github.com/chenxiaolong/zip"
|
||||
rev = "989101f9384b9e94e36e6e9e0f51908fdf98bde6"
|
||||
git = "https://github.com/chenxiaolong/zip2"
|
||||
rev = "59685f4dadbfee8cb3ea74c8fbb402b60d8137e8"
|
||||
default-features = false
|
||||
|
||||
[features]
|
||||
|
||||
+16
-16
@@ -51,12 +51,12 @@ data.version = "vendor_v4"
|
||||
data.ramdisks = [["otacerts", "first_stage", "dsu_key_dir"]]
|
||||
|
||||
[profile.pixel_v4_gki.hashes_streaming]
|
||||
original = "c83bff30dbd30ed8baf5a954a158a2c3a75b25175089809e52ea9052020b0627"
|
||||
patched = "20bd1835c6ea90104eab7ec51be48ee8aae76508f8a6501621d7a1f384103c2f"
|
||||
original = "ef6261cd9ebea90f036e52a46160a400c5b8f6ef24ed2469c4a1e9689987aa06"
|
||||
patched = "37fd353a766a7b9a339fbf51fa79c703e94640dc6a2c6310d79357aaefcc7ca1"
|
||||
|
||||
[profile.pixel_v4_gki.hashes_seekable]
|
||||
original = "ffc5c7839dfa68d5ff7d888287f808259be34bd63c5df0a385e2e78e6fdcc647"
|
||||
patched = "90a51e890560486d4bd73685176c833c94f31f9d05d524186bb4c544c8eca32c"
|
||||
original = "8a2c717607c10dfa5483d6f9a9f37b3d978acaf8d2ea18e36544af267943e750"
|
||||
patched = "2c4734c9e1d028ee6aaf02bb416e5e173857faffd2ca067366790655147b3afa"
|
||||
|
||||
# Google Pixel 6a
|
||||
# What's unique: boot (boot v4, no ramdisk) + vendor_boot (vendor v4, 2 ramdisks)
|
||||
@@ -93,12 +93,12 @@ data.version = "vendor_v4"
|
||||
data.ramdisks = [["init", "otacerts", "first_stage", "dsu_key_dir"], ["dlkm"]]
|
||||
|
||||
[profile.pixel_v4_non_gki.hashes_streaming]
|
||||
original = "8d76f17b33949c8ca3d4d5872858bb725d755e315f67d61db0e0b7b58cf69685"
|
||||
patched = "7c7cb69087d4c8c54a7f4bac6525354b1e9c1550545bc954194aaf6882f7982d"
|
||||
original = "630220ef813a2b4743d1941179cc9705da86ad4805f1c52341dcb38fbce3d29e"
|
||||
patched = "b725e91751fe58aed20495aecbf9b4bdc14d2799cd88dcbd58f3a3b02b3af15b"
|
||||
|
||||
[profile.pixel_v4_non_gki.hashes_seekable]
|
||||
original = "6825bc02115e0c64f05651571138fbcc5cc04459e5c7e225dcc5e3096051c341"
|
||||
patched = "373309f28234866625462253d6f41f5a3b016b3676c7cb87a88bf55099ad6b13"
|
||||
original = "1afbe6867ded345d941098ee7c7fcf94a3df52c50ff96ab8f3a67b2ab957259a"
|
||||
patched = "4357b977249006b101002c961916f962787315a80b8608494c6a1f0cf09cecd1"
|
||||
|
||||
# Google Pixel 4a 5G
|
||||
# What's unique: boot (boot v3) + vendor_boot (vendor v3)
|
||||
@@ -136,12 +136,12 @@ data.version = "vendor_v3"
|
||||
data.ramdisks = [["otacerts", "first_stage", "dsu_key_dir"]]
|
||||
|
||||
[profile.pixel_v3.hashes_streaming]
|
||||
original = "678f5ba8cff01802ce964d31abf7a2c77b63e9c06e911857b588a16872fb6860"
|
||||
patched = "3691c2cf89728307e143b7084b40bbbfb181e15be6f65914a6c40408e0c6eab3"
|
||||
original = "9b65037343d45211e0f9706929cba34643a9c54274d1b39740c43f45974984e0"
|
||||
patched = "fb23ab9616968b38b96d1e5e6a503154f89aebc1741e89a9e9dfd2c4d9946b05"
|
||||
|
||||
[profile.pixel_v3.hashes_seekable]
|
||||
original = "202d447215f80919f464bd59c7b38f20cbe3a9625d38bc81a0b71e5a3da334eb"
|
||||
patched = "ec94739fdef7efc4930e96828761c2ebd4ece79014b735eab303c5cbdda2e529"
|
||||
original = "e581934887dd93b8a9d9c3aa5dec1d48aa7e01bf01ac507e8c5fb256b59cbe7d"
|
||||
patched = "669a826abc6d67e7e0b1def724aa7b470461087255c65663d195df1426a355f0"
|
||||
|
||||
# Google Pixel 4a
|
||||
# What's unique: boot (boot v2)
|
||||
@@ -169,9 +169,9 @@ data.type = "vbmeta"
|
||||
data.deps = ["system"]
|
||||
|
||||
[profile.pixel_v2.hashes_streaming]
|
||||
original = "79bb20bd57f51a8a7b52d0ff59b7d096a1e4ada425a6230818ca25916a81e021"
|
||||
patched = "7f199cc4a6fd244034b597cb916651e1c3e0d6f59450617a5cadbd274f02ec7a"
|
||||
original = "f10ee15c900a474cc6bbefa705f272cef42636ea096e75563d2d78f6c4327fd1"
|
||||
patched = "6929f65909037f5550a53982b71e96bdf69ab876bc5e86702c469ed601be8a9a"
|
||||
|
||||
[profile.pixel_v2.hashes_seekable]
|
||||
original = "c9b32282f247555fed92cc6d599b9005c81ffdc88d5f55d2248e8bf6fb66f1b5"
|
||||
patched = "c856f7f7245c02d32b3cec93c2d4365a2b3823fed9a9b27f94508637d79b1492"
|
||||
original = "4e863d251b9ff6eaa1511f9c03e9bdb8919650b2e0eaf23e33892a639edafcaf"
|
||||
patched = "9a103222e73df70a097281525546d25c850df2ae7a2ba715aa5dfbbba3f7972b"
|
||||
|
||||
+20
-19
@@ -14,12 +14,12 @@ use std::{
|
||||
path::{Path, PathBuf},
|
||||
slice,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
atomic::{AtomicBool, Ordering},
|
||||
},
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use anyhow::{Context, Result, anyhow, bail};
|
||||
use avbroot::{
|
||||
cli::ota::{ExtractCli, PatchCli, VerifyCli},
|
||||
crypto::{self, PassphraseSource, RsaSigningKey},
|
||||
@@ -37,10 +37,11 @@ use avbroot::{
|
||||
ota::{self, SigningWriter, ZipEntry, ZipMode},
|
||||
padding,
|
||||
payload::{self, CowVersion, PayloadHeader, PayloadWriter, VabcParams},
|
||||
zip::ZipWriterWrapper,
|
||||
},
|
||||
patch::otacert::{self, OtaCertBuildFlags},
|
||||
protobuf::{
|
||||
build::tools::releasetools::{ota_metadata::OtaType, DeviceState, OtaMetadata},
|
||||
build::tools::releasetools::{DeviceState, OtaMetadata, ota_metadata::OtaType},
|
||||
chromeos_update_engine::{
|
||||
DeltaArchiveManifest, DynamicPartitionGroup, DynamicPartitionMetadata, PartitionUpdate,
|
||||
},
|
||||
@@ -48,12 +49,12 @@ use avbroot::{
|
||||
stream::{self, CountingWriter, FromReader, HashingReader, PSeekFile, Reopen, ToWriter},
|
||||
};
|
||||
use clap::Parser;
|
||||
use rsa::{rand_core::OsRng, traits::PublicKeyParts, BigUint};
|
||||
use rsa::{BigUint, rand_core::OsRng, traits::PublicKeyParts};
|
||||
use tempfile::TempDir;
|
||||
use topological_sort::TopologicalSort;
|
||||
use tracing::{info, info_span};
|
||||
use x509_cert::Certificate;
|
||||
use zip::{write::FileOptions, CompressionMethod, ZipWriter};
|
||||
use zip::{CompressionMethod, DateTime, ZipWriter, write::SimpleFileOptions};
|
||||
|
||||
use crate::{
|
||||
cli::{Cli, Command, HelperCli, ListCli, PassSource, ProfileGroup, TestCli},
|
||||
@@ -374,7 +375,7 @@ fn create_boot_image(
|
||||
.ramdisks
|
||||
.iter()
|
||||
.map(|c_list| {
|
||||
if c_list.iter().any(|c| *c == RamdiskContent::Dlkm) {
|
||||
if c_list.contains(&RamdiskContent::Dlkm) {
|
||||
RamdiskMeta {
|
||||
ramdisk_type: bootimage::VENDOR_RAMDISK_TYPE_DLKM,
|
||||
ramdisk_name: "dlkm".to_owned(),
|
||||
@@ -741,14 +742,15 @@ fn create_ota(
|
||||
let mut zip_writer = match zip_mode {
|
||||
ZipMode::Streaming => {
|
||||
let signing_writer = SigningWriter::new_streaming(raw_writer);
|
||||
ZipWriter::new_streaming(signing_writer)
|
||||
ZipWriterWrapper::new_streaming(signing_writer)
|
||||
}
|
||||
ZipMode::Seekable => {
|
||||
let signing_writer = SigningWriter::new_seekable(raw_writer);
|
||||
ZipWriter::new(signing_writer)
|
||||
ZipWriterWrapper::new_seekable(signing_writer)
|
||||
}
|
||||
};
|
||||
let options = FileOptions::default()
|
||||
let options = SimpleFileOptions::default()
|
||||
.last_modified_time(DateTime::default())
|
||||
.compression_method(CompressionMethod::Stored)
|
||||
.large_file(false);
|
||||
|
||||
@@ -758,12 +760,9 @@ fn create_ota(
|
||||
|
||||
for path in [ota::PATH_OTACERT, ota::PATH_PAYLOAD, ota::PATH_PROPERTIES] {
|
||||
// All remaining entries are written immediately.
|
||||
zip_writer
|
||||
.start_file_with_extra_data(path, options)
|
||||
.with_context(|| format!("Failed to begin new zip entry: {path}"))?;
|
||||
let offset = zip_writer
|
||||
.end_extra_data()
|
||||
.with_context(|| format!("Failed to end new zip entry: {path}"))?;
|
||||
.start_file(path, options)
|
||||
.with_context(|| format!("Failed to begin new zip entry: {path}"))?;
|
||||
let mut writer = CountingWriter::new(&mut zip_writer);
|
||||
|
||||
match path {
|
||||
@@ -864,6 +863,7 @@ fn create_fake_magisk(output: &Path) -> Result<()> {
|
||||
let raw_writer =
|
||||
File::create(output).with_context(|| format!("Failed to open for writing: {output:?}"))?;
|
||||
let mut zip_writer = ZipWriter::new(raw_writer);
|
||||
let options = SimpleFileOptions::default().last_modified_time(DateTime::default());
|
||||
|
||||
for path in [
|
||||
"assets/stub.apk",
|
||||
@@ -880,12 +880,12 @@ fn create_fake_magisk(output: &Path) -> Result<()> {
|
||||
"lib/x86_64/libmagisk64.so",
|
||||
"lib/x86_64/libmagiskinit.so",
|
||||
] {
|
||||
zip_writer.start_file(path, FileOptions::default())?;
|
||||
zip_writer.start_file(path, options)?;
|
||||
write!(zip_writer, "dummy contents for {path}")?;
|
||||
}
|
||||
|
||||
// avbroot looks for the version number in this file.
|
||||
zip_writer.start_file("assets/util_functions.sh", FileOptions::default())?;
|
||||
zip_writer.start_file("assets/util_functions.sh", options)?;
|
||||
zip_writer.write_all(b"MAGISK_VER_CODE=27000\n")?;
|
||||
|
||||
Ok(())
|
||||
@@ -1140,7 +1140,7 @@ fn clean_boot_image_certs(path: &Path, cancel_signal: &AtomicBool) -> Result<()>
|
||||
.iter_mut()
|
||||
.find(|e| e.path == b"system/etc/security/otacerts.zip")
|
||||
{
|
||||
let mut zip_writer = ZipWriter::new(Cursor::new(Vec::new()));
|
||||
let zip_writer = ZipWriter::new(Cursor::new(Vec::new()));
|
||||
let empty_zip = zip_writer.finish()?.into_inner();
|
||||
|
||||
entry.data = CpioEntryData::Data(empty_zip);
|
||||
@@ -1234,8 +1234,9 @@ fn test_subcommand(cli: &TestCli, cancel_signal: &AtomicBool) -> Result<()> {
|
||||
] {
|
||||
let _span = info_span!("profile", name, %zip_mode).entered();
|
||||
|
||||
// Can't used NamedTempFile because avbroot does atomic replaces.
|
||||
let profile_dir = work_dir.join(name);
|
||||
// Can't use NamedTempFile because avbroot does atomic replaces.
|
||||
let mut profile_dir = work_dir.join(name);
|
||||
profile_dir.push(zip_mode.to_string());
|
||||
let out_original = profile_dir.join("ota.zip");
|
||||
let out_magisk = profile_dir.join("ota_magisk.zip");
|
||||
let out_prepatched = profile_dir.join("ota_prepatched.zip");
|
||||
|
||||
@@ -9,7 +9,7 @@ use std::{
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use anyhow::{Result, anyhow, bail};
|
||||
use regex::Regex;
|
||||
|
||||
use crate::WORKSPACE_DIR;
|
||||
|
||||
@@ -7,9 +7,9 @@ use std::{
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use anyhow::{Result, bail};
|
||||
use clap::Parser;
|
||||
use toml_edit::{value, DocumentMut};
|
||||
use toml_edit::{DocumentMut, value};
|
||||
|
||||
use crate::WORKSPACE_DIR;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user