mirror of
https://github.com/chenxiaolong/avbroot.git
synced 2026-07-03 14:05:11 +02:00
Switch to rust-lzma2 for XZ compression/decompression
The performance, both in CPU time and compression ratios, is very comparable to liblzma. This lets us drop the last remaining compression-related dependency written in C. Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
This commit is contained in:
@@ -92,7 +92,7 @@ jobs:
|
||||
run: |
|
||||
for target in ${TARGETS}; do
|
||||
cargo android \
|
||||
clippy --release --workspace --features static \
|
||||
clippy --release --workspace \
|
||||
--target "${target}"
|
||||
done
|
||||
|
||||
@@ -101,7 +101,7 @@ jobs:
|
||||
run: |
|
||||
for target in ${TARGETS}; do
|
||||
cargo android \
|
||||
build --release --workspace --features static \
|
||||
build --release --workspace \
|
||||
--target "${target}"
|
||||
done
|
||||
|
||||
@@ -110,7 +110,7 @@ jobs:
|
||||
run: |
|
||||
for target in ${TARGETS}; do
|
||||
cargo android \
|
||||
test --release --workspace --features static \
|
||||
test --release --workspace \
|
||||
--target "${target}"
|
||||
done
|
||||
|
||||
@@ -119,7 +119,7 @@ jobs:
|
||||
run: |
|
||||
for target in ${TARGETS}; do
|
||||
cargo android \
|
||||
run --release -p e2e --features static \
|
||||
run --release -p e2e \
|
||||
--target "${target}" \
|
||||
-- test -a -c e2e/e2e.toml
|
||||
done
|
||||
|
||||
Generated
+26
-39
@@ -135,8 +135,8 @@ dependencies = [
|
||||
"gf256",
|
||||
"hex",
|
||||
"libc",
|
||||
"liblzma",
|
||||
"lz4_flex",
|
||||
"lzma-rust2",
|
||||
"memchr",
|
||||
"num-bigint-dig",
|
||||
"num-traits",
|
||||
@@ -308,8 +308,6 @@ version = "1.2.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
@@ -423,6 +421,21 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc"
|
||||
version = "3.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675"
|
||||
dependencies = [
|
||||
"crc-catalog",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc-catalog"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.5.0"
|
||||
@@ -830,16 +843,6 @@ dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a"
|
||||
dependencies = [
|
||||
"getrandom 0.3.3",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.77"
|
||||
@@ -871,26 +874,6 @@ version = "0.2.174"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||
|
||||
[[package]]
|
||||
name = "liblzma"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0791ab7e08ccc8e0ce893f6906eb2703ed8739d8e89b57c0714e71bad09024c8"
|
||||
dependencies = [
|
||||
"liblzma-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "liblzma-sys"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01b9596486f6d60c3bbe644c0e1be1aa6ccc472ad630fe8927b456973d7cb736"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.15"
|
||||
@@ -961,6 +944,16 @@ dependencies = [
|
||||
"twox-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lzma-rust2"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a82192ab5b40bc95fff6e8a61e099e421f07055778b2f5261297c102137b6081"
|
||||
dependencies = [
|
||||
"crc",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "maybe-owned"
|
||||
version = "0.3.4"
|
||||
@@ -1232,12 +1225,6 @@ dependencies = [
|
||||
"spki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.21"
|
||||
|
||||
@@ -588,8 +588,6 @@ The output binary is written to `target/release/avbroot`.
|
||||
|
||||
Debug builds work too, but they will run significantly slower (in the sha256 computations) due to compiler optimizations being turned off.
|
||||
|
||||
By default, the executable links to the system's bzip2 and liblzma libraries, which are the only external libraries avbroot depends on. To compile and statically link these two libraries, pass in `--features static`.
|
||||
|
||||
### Android cross-compilation
|
||||
|
||||
To cross-compile for Android, install [cargo-android](https://github.com/chenxiaolong/cargo-android) and use the `cargo android` wrapper. To make a release build for aarch64, run:
|
||||
|
||||
+10
-12
@@ -391,17 +391,17 @@ avbroot можно использовать для простого перепо
|
||||
Если вы используете аргумент `--skip-recovery-ota-cert`, потому что уже добавили сертификат OTA в загрузочный образ вручную, рекомендуетcя [проверить пропатченный OTA](#проверка-ota), дабы удостовериться, что замена произведена корректно. Процесс верификации проверяет только копию сертификатов OTA в загрузочном образе, не проверяя копию в образе системы.
|
||||
|
||||
### Пропуск всех патчей
|
||||
|
||||
|
||||
Чтобы внести самый минимум изменений, укажите аргументы:
|
||||
|
||||
|
||||
* `--skip-system-ota-cert`
|
||||
* `--skip-recovery-ota-cert`
|
||||
* `--rootless`
|
||||
* не используйте аргумент `--dsu`.
|
||||
|
||||
|
||||
Так, пользовательскими ключами будут переподписаны лишь образ `vbmeta` и OTA, остальные разделы останутся нетронутыми.
|
||||
|
||||
**Это следует использовать только для устранения неполадок.** Без патчей сертификатов, поверх полученного OTA не получится установить никакие обновления.
|
||||
|
||||
**Это следует использовать только для устранения неполадок.** Без патчей сертификатов, поверх полученного OTA не получится установить никакие обновления.
|
||||
|
||||
### Подмена образов
|
||||
|
||||
@@ -422,15 +422,15 @@ Verified boot is disabled by vbmeta's header flags: 0x3
|
||||
Чтобы принудительно включить AVB (очистив флаги), укажите аргумент `--clear-vbmeta-flags`.
|
||||
|
||||
### Изменение алгоритма CoW сжатия для вирутального A/B
|
||||
|
||||
|
||||
Алгоритм CoW (copy-on-write) сжатия для виртуального A/B можно изменить, используя аргумент `--vabc-algo <алгоритм>`, указав `gz` или `lz4`. Как правило, по умолчанию OTA использует алгоритм, который совместим с изначальной версией Android, на которой поставлялось устройство.
|
||||
|
||||
|
||||
* Девайсы, поставляемые с Android 12, поддерживают `gz` и `brotli` (последний не поддерживается avbroot)
|
||||
* Девайсы, поставляемые с Android 14, поддерживают `lz4`
|
||||
* Девайсы, поставляемые с Android 15, поддерживают `zstd` (не поддерживается avbroot)
|
||||
|
||||
|
||||
Выбор быстрого алгоритма, такого как lz4, может значительно ускорить установку OTA из-под системы (при использованием стороннего приложения для OTA-обновлений). Однако, при установке OTA в режиме Recovery, разницы в скорости не будет.
|
||||
|
||||
|
||||
Обратите внимание, что текущая используемая версия Android должна поддерживать выбранный алгоритм сжатия. В противном случае установка завершится ошибкой. Например, попытка установить OTA-обновление с Android 14, использующее алгоритм lz4, приведет к ошибке, если установка производится из-под Android 13.
|
||||
|
||||
### Использование в неинтерактивном режиме
|
||||
@@ -515,7 +515,7 @@ avbroot поддерживает делегирование всех опера
|
||||
|
||||
На современных устройствах с Android 16 и выше, в настройках для разработчиков может появиться опция переключения на ядро с размером страницы 16 КБ. Однако, эта функция не будет работать в системе, пропатченной с помощью avbroot, поскольку переключение данной настройки осуществляется путём установки инкрементальной OTA:
|
||||
|
||||
* `/vendor/boot_otas/boot_ota_16k.zip` — используется для переключения на ядро с размером страницы 16 КБ (в разделе `boot` уже должно быть прошито ядро с размером страницы 4K)
|
||||
* `/vendor/boot_otas/boot_ota_16k.zip` — используется для переключения на ядро с размером страницы 16 КБ (в разделе `boot` уже должно быть прошито ядро с размером страницы 4K)
|
||||
* `/vendor/boot_otas/boot_ota_4k.zip` — используется для переключения на ядро с размером страницы 4 КБ (в разделе `boot` уже должно быть прошито ядро с размером страницы 16K)
|
||||
|
||||
Эти файлы (в `boot_otas`) невозможно прошить на системе, пропатченной avbroot, потому что `payload.bin` внутри них подписан ключом производителя. Кроме того, это неполноценные OTA-файлы: у них нет метаданных, характерных для OTA, а сам zip-файл не подписан. Это просто обычный архив, который содержит подписанный `payload.bin`.
|
||||
@@ -570,8 +570,6 @@ cargo build --release
|
||||
|
||||
Дебаг-сборки тоже работают, но они будут работать значительно медленнее (в вычислениях sha256), потому что оптимизации компилятора отключены.
|
||||
|
||||
По умолчанию исполняемый файл ссылается на системные библиотеки bzip2 и liblzma, от которых зависит avbroot. Чтобы скомпилировать и статически связать эти две библиотеки, укажите аргумент `--features static`.
|
||||
|
||||
### Кросс-компиляция на Android
|
||||
|
||||
Чтобы использовать кросс-компиляцию на Android, установите [cargo-android](https://github.com/chenxiaolong/cargo-android) и воспользуйтесь оболочкой `cargo android`. Чтобы создать релизную сборку для aarch64, выполните:
|
||||
|
||||
+1
-4
@@ -28,8 +28,8 @@ dlv-list = "0.6.0"
|
||||
flate2 = { version = "1.0.29", features = ["zlib-rs"] }
|
||||
gf256 = { version = "0.3.0", features = ["rs"] }
|
||||
hex = { version = "0.4.3", features = ["serde"] }
|
||||
liblzma = "0.4.1"
|
||||
lz4_flex = "0.11.1"
|
||||
lzma-rust2 = "0.8.0"
|
||||
memchr = "2.6.0"
|
||||
num-bigint-dig = "0.8.4"
|
||||
num-traits = "0.2.16"
|
||||
@@ -81,8 +81,5 @@ protox = "0.9.0"
|
||||
[dev-dependencies]
|
||||
assert_matches = "1.5.0"
|
||||
|
||||
[features]
|
||||
static = ["liblzma/static"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
// SPDX-FileCopyrightText: 2023-2024 Andrew Gunnerson
|
||||
// SPDX-FileCopyrightText: 2023-2025 Andrew Gunnerson
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use std::io::{self, Read, Seek, Write};
|
||||
|
||||
use flate2::{Compression, read::GzDecoder, write::GzEncoder};
|
||||
use liblzma::{
|
||||
read::XzDecoder,
|
||||
stream::{Check, Stream},
|
||||
write::XzEncoder,
|
||||
};
|
||||
use lz4_flex::frame::FrameDecoder;
|
||||
use lzma_rust2::{CheckType, XZOptions, XZReader, XZWriter};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
@@ -28,7 +24,7 @@ pub enum Error {
|
||||
#[error("Failed to initialize legacy LZ4 encoder")]
|
||||
Lz4Init(#[source] io::Error),
|
||||
#[error("Failed to initialize XZ encoder")]
|
||||
XzInit(#[source] liblzma::stream::Error),
|
||||
XzInit(#[source] io::Error),
|
||||
}
|
||||
|
||||
type Result<T> = std::result::Result<T, Error>;
|
||||
@@ -114,14 +110,14 @@ pub enum CompressedFormat {
|
||||
Xz,
|
||||
}
|
||||
|
||||
pub enum CompressedReader<R: Read> {
|
||||
pub enum CompressedReader<'reader, R: Read> {
|
||||
None(R),
|
||||
Gzip(GzDecoder<R>),
|
||||
Lz4(FrameDecoder<R>),
|
||||
Xz(XzDecoder<R>),
|
||||
Xz(XZReader<'reader, R>),
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> CompressedReader<R> {
|
||||
impl<'reader, R: Read + Seek + 'reader> CompressedReader<'reader, R> {
|
||||
pub fn new(mut reader: R, raw_if_unknown: bool) -> Result<Self> {
|
||||
let magic = reader.read_array_exact::<6>().map_err(Error::AutoDetect)?;
|
||||
|
||||
@@ -132,7 +128,7 @@ impl<R: Read + Seek> CompressedReader<R> {
|
||||
} else if &magic[0..4] == LZ4_LEGACY_MAGIC {
|
||||
Ok(Self::Lz4(FrameDecoder::new(reader)))
|
||||
} else if &magic == XZ_MAGIC {
|
||||
Ok(Self::Xz(XzDecoder::new(reader)))
|
||||
Ok(Self::Xz(XZReader::new(reader, false)))
|
||||
} else if raw_if_unknown {
|
||||
Ok(Self::None(reader))
|
||||
} else {
|
||||
@@ -159,7 +155,7 @@ impl<R: Read + Seek> CompressedReader<R> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read> Read for CompressedReader<R> {
|
||||
impl<'reader, R: Read + 'reader> Read for CompressedReader<'reader, R> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
match self {
|
||||
Self::None(r) => r.read(buf),
|
||||
@@ -170,14 +166,15 @@ impl<R: Read> Read for CompressedReader<R> {
|
||||
}
|
||||
}
|
||||
|
||||
pub enum CompressedWriter<W: Write> {
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum CompressedWriter<'writer, W: Write> {
|
||||
None(W),
|
||||
Gzip(GzEncoder<W>),
|
||||
Lz4Legacy(Lz4LegacyEncoder<W>),
|
||||
Xz(XzEncoder<W>),
|
||||
Xz(XZWriter<'writer, W>),
|
||||
}
|
||||
|
||||
impl<W: Write> CompressedWriter<W> {
|
||||
impl<'writer, W: Write + 'writer> CompressedWriter<'writer, W> {
|
||||
pub fn new(writer: W, format: CompressedFormat) -> Result<Self> {
|
||||
match format {
|
||||
CompressedFormat::None => Ok(Self::None(writer)),
|
||||
@@ -190,8 +187,11 @@ impl<W: Write> CompressedWriter<W> {
|
||||
}
|
||||
CompressedFormat::Xz => {
|
||||
// Some kernels are compiled without support for the default CRC64.
|
||||
let stream = Stream::new_easy_encoder(6, Check::Crc32).map_err(Error::XzInit)?;
|
||||
Ok(Self::Xz(XzEncoder::new_stream(writer, stream)))
|
||||
let mut options = XZOptions::with_preset(6);
|
||||
options.set_check_sum_type(CheckType::Crc32);
|
||||
|
||||
let xz_writer = XZWriter::new(writer, options).map_err(Error::XzInit)?;
|
||||
Ok(Self::Xz(xz_writer))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -215,7 +215,7 @@ impl<W: Write> CompressedWriter<W> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> Write for CompressedWriter<W> {
|
||||
impl<'writer, W: Write + 'writer> Write for CompressedWriter<'writer, W> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
match self {
|
||||
Self::None(w) => w.write(buf),
|
||||
|
||||
@@ -15,11 +15,7 @@ use base64::Engine;
|
||||
use base64::engine::general_purpose::STANDARD;
|
||||
use bzip2::write::BzDecoder;
|
||||
use flate2::{Compression, write::GzEncoder};
|
||||
use liblzma::{
|
||||
stream::{Check, Stream},
|
||||
write::XzDecoder,
|
||||
write::XzEncoder,
|
||||
};
|
||||
use lzma_rust2::{CheckType, XZOptions, XZReader, XZWriter};
|
||||
use num_traits::CheckedAdd;
|
||||
use prost::Message;
|
||||
use rayon::{
|
||||
@@ -40,8 +36,8 @@ use crate::{
|
||||
install_operation::Type, signatures::Signature,
|
||||
},
|
||||
stream::{
|
||||
self, CountingReader, FromReader, HashingWriter, ReadDiscardExt, ReadFixedSizeExt,
|
||||
ReadSeekReopen, WriteSeek, WriteSeekReopen,
|
||||
self, CountingReader, FromReader, HashingReader, HashingWriter, ReadDiscardExt,
|
||||
ReadFixedSizeExt, ReadSeekReopen, WriteSeek, WriteSeekReopen,
|
||||
},
|
||||
util::{self, OutOfBoundsError},
|
||||
};
|
||||
@@ -139,7 +135,7 @@ pub enum Error {
|
||||
#[error("Failed to GZ compress partition image chunk")]
|
||||
GzCompress(#[source] io::Error),
|
||||
#[error("Failed to initialize XZ encoder")]
|
||||
XzInit(#[source] liblzma::stream::Error),
|
||||
XzInit(#[source] io::Error),
|
||||
#[error("Failed to XZ compress partition image chunk")]
|
||||
XzCompress(#[source] io::Error),
|
||||
#[error("Failed to read uncompressed input partition image chunk")]
|
||||
@@ -818,16 +814,14 @@ pub fn apply_operation(
|
||||
.map_err(error_fn)?;
|
||||
}
|
||||
Type::ReplaceXz => {
|
||||
let mut decoder = XzDecoder::new(&mut writer);
|
||||
stream::copy_n_inspect(
|
||||
&mut reader,
|
||||
&mut decoder,
|
||||
data_length,
|
||||
|data| hasher.update(data),
|
||||
cancel_signal,
|
||||
)
|
||||
.and_then(|()| decoder.finish())
|
||||
.map_err(error_fn)?;
|
||||
// lzma_rust2 does not have a Write API, so we limit the
|
||||
// reader and read till EOF.
|
||||
let limited_reader = (&mut reader).take(data_length);
|
||||
let hashing_reader = HashingReader::new(limited_reader, hasher);
|
||||
let mut decoder = XZReader::new(hashing_reader, false);
|
||||
stream::copy(&mut decoder, &mut writer, cancel_signal).map_err(error_fn)?;
|
||||
|
||||
(_, hasher) = decoder.into_inner().finish();
|
||||
}
|
||||
_ => return Err(Error::UnsupportedOperation(op.r#type())),
|
||||
}
|
||||
@@ -947,8 +941,9 @@ fn compress_chunk(raw_data: &[u8], cancel_signal: &AtomicBool) -> Result<(Vec<u8
|
||||
// decompression. Also, we intentionally pick the lowest compression level
|
||||
// since we primarily care about squishing zeros. The non-zero portions of
|
||||
// boot images are usually already-compressed kernels and ramdisks.
|
||||
let stream = Stream::new_easy_encoder(0, Check::None).map_err(Error::XzInit)?;
|
||||
let mut xz_writer = XzEncoder::new_stream(hashing_writer, stream);
|
||||
let mut options = XZOptions::with_preset(0);
|
||||
options.set_check_sum_type(CheckType::None);
|
||||
let mut xz_writer = XZWriter::new(hashing_writer, options).map_err(Error::XzInit)?;
|
||||
|
||||
stream::copy_n(reader, &mut xz_writer, raw_data.len() as u64, cancel_signal)
|
||||
.map_err(Error::XzCompress)?;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: 2022-2024 Andrew Gunnerson
|
||||
// SPDX-FileCopyrightText: 2022-2025 Andrew Gunnerson
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use std::{
|
||||
@@ -15,10 +15,7 @@ use std::{
|
||||
};
|
||||
|
||||
use bstr::ByteSlice;
|
||||
use liblzma::{
|
||||
stream::{Check, Stream},
|
||||
write::XzEncoder,
|
||||
};
|
||||
use lzma_rust2::{CheckType, XZOptions, XZWriter};
|
||||
use rayon::iter::{IntoParallelRefIterator, IntoParallelRefMutIterator, ParallelIterator};
|
||||
use regex::bytes::Regex;
|
||||
use ring::digest::Context;
|
||||
@@ -83,7 +80,7 @@ pub enum Error {
|
||||
#[error("Failed to generate replacement otacerts zip")]
|
||||
OtaCertZip(#[source] otacert::Error),
|
||||
#[error("Failed to initialize XZ encoder")]
|
||||
XzInit(#[source] liblzma::stream::Error),
|
||||
XzInit(#[source] io::Error),
|
||||
#[error("Failed to XZ compress entry: {:?}", .0.as_bstr())]
|
||||
XzCompress(Vec<u8>, #[source] io::Error),
|
||||
#[error("Failed to open zip file: {0:?}")]
|
||||
@@ -288,9 +285,11 @@ impl MagiskRootPatcher {
|
||||
}
|
||||
|
||||
fn xz_compress(name: &[u8], reader: impl Read, cancel_signal: &AtomicBool) -> Result<Vec<u8>> {
|
||||
let stream = Stream::new_easy_encoder(9, Check::Crc32).map_err(Error::XzInit)?;
|
||||
let mut options = XZOptions::with_preset(9);
|
||||
options.set_check_sum_type(CheckType::None);
|
||||
|
||||
let raw_writer = Cursor::new(Vec::new());
|
||||
let mut writer = XzEncoder::new_stream(raw_writer, stream);
|
||||
let mut writer = XZWriter::new(raw_writer, options).map_err(Error::XzInit)?;
|
||||
|
||||
let raw_writer = stream::copy(reader, &mut writer, cancel_signal)
|
||||
.and_then(|_| writer.finish())
|
||||
|
||||
+18
-12
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: 2023 Andrew Gunnerson
|
||||
// SPDX-FileCopyrightText: 2023-2025 Andrew Gunnerson
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use std::{
|
||||
@@ -603,12 +603,14 @@ pub fn copy_n(
|
||||
copy_n_inspect(reader, writer, size, |_| {}, cancel_signal)
|
||||
}
|
||||
|
||||
/// Copy data from `reader` to `writer` until `reader` reaches EOF. If `writer`
|
||||
/// reaches EOF before `reader` does, an error is returned. The operation is
|
||||
/// cancelled on the next loop iteration if `cancel_signal` is set to `true`.
|
||||
pub fn copy(
|
||||
/// Copy data from `reader` to `writer` until `reader` reaches EOF, invoking
|
||||
/// `inspect` after every buffer read iteration. If `writer` reaches EOF before
|
||||
/// `reader` does, an error is returned. The operation is cancelled on the next
|
||||
/// loop iteration if `cancel_signal` is set to `true`.
|
||||
pub fn copy_inspect(
|
||||
mut reader: impl Read,
|
||||
mut writer: impl Write,
|
||||
mut inspect: impl FnMut(&[u8]),
|
||||
cancel_signal: &AtomicBool,
|
||||
) -> io::Result<u64> {
|
||||
let mut buf = [0u8; 16384];
|
||||
@@ -622,6 +624,8 @@ pub fn copy(
|
||||
break;
|
||||
}
|
||||
|
||||
inspect(&buf[..n]);
|
||||
|
||||
writer.write_all(&buf[..n])?;
|
||||
|
||||
copied += n as u64;
|
||||
@@ -630,6 +634,11 @@ pub fn copy(
|
||||
Ok(copied)
|
||||
}
|
||||
|
||||
/// Copy data from `reader` to `writer` until `reader` reaches EOF.
|
||||
pub fn copy(reader: impl Read, writer: impl Write, cancel_signal: &AtomicBool) -> io::Result<u64> {
|
||||
copy_inspect(reader, writer, |_| {}, cancel_signal)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{
|
||||
@@ -822,21 +831,19 @@ mod tests {
|
||||
let mut reader = Cursor::new(b"foobar");
|
||||
let mut writer = Cursor::new([0u8; 6]);
|
||||
|
||||
super::copy_n_inspect(&mut reader, &mut writer, 6, |_| {}, &cancel_signal).unwrap();
|
||||
super::copy_n(&mut reader, &mut writer, 6, &cancel_signal).unwrap();
|
||||
assert_eq!(writer.get_ref(), b"foobar");
|
||||
|
||||
// Reader early EOF.
|
||||
reader.seek(SeekFrom::Start(3)).unwrap();
|
||||
writer.rewind().unwrap();
|
||||
let err =
|
||||
super::copy_n_inspect(&mut reader, &mut writer, 6, |_| {}, &cancel_signal).unwrap_err();
|
||||
let err = super::copy_n(&mut reader, &mut writer, 6, &cancel_signal).unwrap_err();
|
||||
assert_eq!(err.kind(), io::ErrorKind::UnexpectedEof);
|
||||
|
||||
// Writer early EOF.
|
||||
reader.rewind().unwrap();
|
||||
writer.seek(SeekFrom::Start(3)).unwrap();
|
||||
let err =
|
||||
super::copy_n_inspect(&mut reader, &mut writer, 6, |_| {}, &cancel_signal).unwrap_err();
|
||||
let err = super::copy_n(&mut reader, &mut writer, 6, &cancel_signal).unwrap_err();
|
||||
assert_eq!(err.kind(), io::ErrorKind::WriteZero);
|
||||
|
||||
reader.rewind().unwrap();
|
||||
@@ -860,8 +867,7 @@ mod tests {
|
||||
reader.rewind().unwrap();
|
||||
writer.rewind().unwrap();
|
||||
cancel_signal.store(true, Ordering::SeqCst);
|
||||
let err =
|
||||
super::copy_n_inspect(&mut reader, &mut writer, 6, |_| {}, &cancel_signal).unwrap_err();
|
||||
let err = super::copy_n(&mut reader, &mut writer, 6, &cancel_signal).unwrap_err();
|
||||
assert_eq!(err.kind(), io::ErrorKind::Interrupted);
|
||||
let err = super::copy(&mut reader, &mut writer, &cancel_signal).unwrap_err();
|
||||
assert_eq!(err.kind(), io::ErrorKind::Interrupted);
|
||||
|
||||
@@ -32,8 +32,5 @@ git = "https://github.com/chenxiaolong/zip2"
|
||||
rev = "59685f4dadbfee8cb3ea74c8fbb402b60d8137e8"
|
||||
default-features = false
|
||||
|
||||
[features]
|
||||
static = ["avbroot/static"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
+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 = "ef6261cd9ebea90f036e52a46160a400c5b8f6ef24ed2469c4a1e9689987aa06"
|
||||
patched = "37fd353a766a7b9a339fbf51fa79c703e94640dc6a2c6310d79357aaefcc7ca1"
|
||||
original = "4e283ad1e3450795a32f46445bf626f6af983f52bd0a3484b49f8740c1029653"
|
||||
patched = "837861bf64e9387380e02d740b21c94e60c41bba4161d85ab44fc5ce86f19631"
|
||||
|
||||
[profile.pixel_v4_gki.hashes_seekable]
|
||||
original = "8a2c717607c10dfa5483d6f9a9f37b3d978acaf8d2ea18e36544af267943e750"
|
||||
patched = "2c4734c9e1d028ee6aaf02bb416e5e173857faffd2ca067366790655147b3afa"
|
||||
original = "0ab2403a2634f00063c44f9a477a672205922ede189442e18850dd8efceb5d6f"
|
||||
patched = "2d94841c3be6cc1739f4c7ad5d9db048301b3f93f8ccd510e2c825bf13ecff90"
|
||||
|
||||
# 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 = "630220ef813a2b4743d1941179cc9705da86ad4805f1c52341dcb38fbce3d29e"
|
||||
patched = "b725e91751fe58aed20495aecbf9b4bdc14d2799cd88dcbd58f3a3b02b3af15b"
|
||||
original = "c3a978b7225632875d3b1e87e852494b1a69019f5ba4099cf3981503623facca"
|
||||
patched = "cf0ef7429c4657875018d10d7302aaca371b21c5968af234aabd941304f0035b"
|
||||
|
||||
[profile.pixel_v4_non_gki.hashes_seekable]
|
||||
original = "1afbe6867ded345d941098ee7c7fcf94a3df52c50ff96ab8f3a67b2ab957259a"
|
||||
patched = "4357b977249006b101002c961916f962787315a80b8608494c6a1f0cf09cecd1"
|
||||
original = "cc11ee5a5be66bf34dcb6834a9a635016fa9f82dd0d9a2fb1029aa4f218d1d2e"
|
||||
patched = "e2fb7d3ce2c697372fb342ae5cd7bb606cd764f06a2aa8a2d6d7bd2c88456780"
|
||||
|
||||
# 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 = "9b65037343d45211e0f9706929cba34643a9c54274d1b39740c43f45974984e0"
|
||||
patched = "fb23ab9616968b38b96d1e5e6a503154f89aebc1741e89a9e9dfd2c4d9946b05"
|
||||
original = "d12d92c051bcc832cc9ceef1d46c415af80a09808a984db4c42a88d65d644a8b"
|
||||
patched = "408496eb2cca51c0563eceaa3a9def95a5c42acff938850a8577aaa047331284"
|
||||
|
||||
[profile.pixel_v3.hashes_seekable]
|
||||
original = "e581934887dd93b8a9d9c3aa5dec1d48aa7e01bf01ac507e8c5fb256b59cbe7d"
|
||||
patched = "669a826abc6d67e7e0b1def724aa7b470461087255c65663d195df1426a355f0"
|
||||
original = "63505cfd7c2c9d948a5ee150cf53f448f11c83cdce3919fd43231640bf002812"
|
||||
patched = "b246497de1588d41b919ba002b37dff13cb565682aa3e086e543b25ead7d7aaf"
|
||||
|
||||
# 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 = "f10ee15c900a474cc6bbefa705f272cef42636ea096e75563d2d78f6c4327fd1"
|
||||
patched = "6929f65909037f5550a53982b71e96bdf69ab876bc5e86702c469ed601be8a9a"
|
||||
original = "c303728f4ee9c42ef990bb576bd0688635b263a67127d2a986caf574c9eedd63"
|
||||
patched = "e907db408767f81223e3ae53dd8a44c6f59f4702a016093cd6983499c9391ff0"
|
||||
|
||||
[profile.pixel_v2.hashes_seekable]
|
||||
original = "4e863d251b9ff6eaa1511f9c03e9bdb8919650b2e0eaf23e33892a639edafcaf"
|
||||
patched = "9a103222e73df70a097281525546d25c850df2ae7a2ba715aa5dfbbba3f7972b"
|
||||
original = "951b1f70bef7736b9e06f3215357fcebf8a4925872cd31691198fdda9d7c04ce"
|
||||
patched = "2b1bfe46f41942be88e09b40000745c68ed133900d7970f57d15019683a1ba87"
|
||||
|
||||
Reference in New Issue
Block a user