mirror of
https://github.com/chenxiaolong/avbroot.git
synced 2026-07-03 14:05:11 +02:00
Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5df2ffa435 | |||
| 16c1c2a4d3 | |||
| 1155a14b00 | |||
| 806870664a | |||
| 4717407a43 | |||
| ff4a2a6db8 | |||
| 49bd389bf3 | |||
| 5afe76bf2b | |||
| 0c8357924c | |||
| ef9e563010 | |||
| 2e53147f87 | |||
| 69903c4382 | |||
| 9987d21cef | |||
| 9802bba5f7 | |||
| 1b30328b01 | |||
| efb25e15dd | |||
| 2e5754e16c | |||
| cd4da8d147 | |||
| 9c48d0ddc5 | |||
| ee70fdbcae | |||
| f8f1afd6b3 | |||
| f2b98623cb | |||
| 63e21c5aa9 | |||
| a78a122763 | |||
| cd7316b429 | |||
| 68d5bd6990 | |||
| 2a4c422943 | |||
| 94b12bf727 | |||
| f966714310 | |||
| 3eb08f48d1 | |||
| dbb5bc4767 | |||
| 903d6dcd13 | |||
| ddcef31b96 | |||
| cbd419f8e9 | |||
| 9214a832cd | |||
| dc9244f014 | |||
| 7017bc66dd | |||
| bff45fc53c | |||
| 0e5ba0f0e2 | |||
| 2033e48163 |
@@ -47,7 +47,7 @@ jobs:
|
||||
android_api: '31'
|
||||
steps:
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
# For git describe
|
||||
fetch-depth: 0
|
||||
@@ -160,7 +160,7 @@ jobs:
|
||||
run: cp LICENSE README.md target/output/
|
||||
|
||||
- name: Archive executable
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: avbroot-${{ steps.get_version.outputs.version }}-${{ matrix.artifact.name }}
|
||||
path: |
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Run cargo-deny
|
||||
uses: EmbarkStudios/cargo-deny-action@76cd80eb775d7bbbd2d80292136d74d39e1b4918 # v2.0.14
|
||||
uses: EmbarkStudios/cargo-deny-action@3fd3802e88374d3fe9159b834c7714ec57d6c979 # v2.0.15
|
||||
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
echo "version=${version}" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Create release
|
||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||
|
||||
@@ -7,6 +7,31 @@
|
||||
to update the actual links at the bottom of the file.
|
||||
-->
|
||||
|
||||
### Version 3.25.0
|
||||
|
||||
* Remove outdated mentions of guarantees provided by cap-std in `--help` text, which was removed in version 3.20.0 ([PR #561])
|
||||
* Add new commands for packing and unpacking raw OTA zips ([PR #562])
|
||||
* For folks who are familiar with how OTAs are built, this means it's now possible create an OTA from scratch without using `avbroot ota patch`.
|
||||
* Fix `-o` being used as the short option for both `--output` and `--output-properties` in `avbroot payload pack` and `avbroot payload repack` ([PR #563])
|
||||
* The short option for `--output-properties` is now uppercase `-O`.
|
||||
* Add missing file flush operations and remove them where they are no-ops ([PR #564])
|
||||
* Update dependencies ([PR #565])
|
||||
|
||||
### Version 3.24.1
|
||||
|
||||
* Update `avbroot avb verify-device --help` to mention that `ro.boot.vbmeta.public_key_digest` is used when `-p` is not specified ([Issue #554], [PR #555])
|
||||
* Update dependencies ([PR #559])
|
||||
|
||||
### Version 3.24.0
|
||||
|
||||
* Always sort ramdisk cpio entries and ensure they have inode numbers assigned ([PR #540])
|
||||
* Relax LP image parser's validation to allow parsing the `/metadata/gsi/dsu/dsu/lp_metadata` image used by DSU ([PR #541], [PR #545])
|
||||
* Add support for devices with separate recovery partitions when using `--dsu` ([Issue #536], [PR #543])
|
||||
* Fix broken symlinks in fuzzing corpus ([Issue #532], [PR #544], [PR #548])
|
||||
* Ignore unused vbmeta partitions that are filled with zeros ([Issue #537], [PR #539])
|
||||
* Update dependencies ([PR #547], [PR #549])
|
||||
* Fix `cargo deny` failing after running fuzzers with `cargo hfuzz` ([PR #550])
|
||||
|
||||
### Version 3.23.3
|
||||
|
||||
* Add support for Magisk 30600 ([PR #529])
|
||||
@@ -435,6 +460,10 @@ Behind-the-scenes changes:
|
||||
[Issue #472]: https://github.com/chenxiaolong/avbroot/issues/472
|
||||
[Issue #482]: https://github.com/chenxiaolong/avbroot/issues/482
|
||||
[Issue #493]: https://github.com/chenxiaolong/avbroot/issues/493
|
||||
[Issue #532]: https://github.com/chenxiaolong/avbroot/issues/532
|
||||
[Issue #536]: https://github.com/chenxiaolong/avbroot/issues/536
|
||||
[Issue #537]: https://github.com/chenxiaolong/avbroot/issues/537
|
||||
[Issue #554]: https://github.com/chenxiaolong/avbroot/issues/554
|
||||
[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
|
||||
@@ -640,3 +669,20 @@ Behind-the-scenes changes:
|
||||
[PR #524]: https://github.com/chenxiaolong/avbroot/pull/524
|
||||
[PR #529]: https://github.com/chenxiaolong/avbroot/pull/529
|
||||
[PR #530]: https://github.com/chenxiaolong/avbroot/pull/530
|
||||
[PR #539]: https://github.com/chenxiaolong/avbroot/pull/539
|
||||
[PR #540]: https://github.com/chenxiaolong/avbroot/pull/540
|
||||
[PR #541]: https://github.com/chenxiaolong/avbroot/pull/541
|
||||
[PR #543]: https://github.com/chenxiaolong/avbroot/pull/543
|
||||
[PR #544]: https://github.com/chenxiaolong/avbroot/pull/544
|
||||
[PR #545]: https://github.com/chenxiaolong/avbroot/pull/545
|
||||
[PR #547]: https://github.com/chenxiaolong/avbroot/pull/547
|
||||
[PR #548]: https://github.com/chenxiaolong/avbroot/pull/548
|
||||
[PR #549]: https://github.com/chenxiaolong/avbroot/pull/549
|
||||
[PR #550]: https://github.com/chenxiaolong/avbroot/pull/550
|
||||
[PR #555]: https://github.com/chenxiaolong/avbroot/pull/555
|
||||
[PR #559]: https://github.com/chenxiaolong/avbroot/pull/559
|
||||
[PR #561]: https://github.com/chenxiaolong/avbroot/pull/561
|
||||
[PR #562]: https://github.com/chenxiaolong/avbroot/pull/562
|
||||
[PR #563]: https://github.com/chenxiaolong/avbroot/pull/563
|
||||
[PR #564]: https://github.com/chenxiaolong/avbroot/pull/564
|
||||
[PR #565]: https://github.com/chenxiaolong/avbroot/pull/565
|
||||
|
||||
Generated
+348
-157
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.23.3"
|
||||
version = "3.25.0"
|
||||
license = "GPL-3.0-only"
|
||||
edition = "2024"
|
||||
repository = "https://github.com/chenxiaolong/avbroot"
|
||||
|
||||
+63
-5
@@ -19,7 +19,7 @@ The vbmeta descriptor digests are validated during unpacking. For dm-verity imag
|
||||
### Packing an AVB image
|
||||
|
||||
```bash
|
||||
avbroot avb pack -o <output AVB image> [--key <AVB private key>]
|
||||
avbroot avb pack -o <output AVB image> [-k <AVB private key>]
|
||||
```
|
||||
|
||||
This subcommand packs a new AVB image from the `avb.toml` file and, for appended vbmeta images, the `raw.img` file.
|
||||
@@ -52,6 +52,8 @@ avbroot avb info -i <image>
|
||||
|
||||
This subcommand shows all of the vbmeta header and footer fields. `vbmeta` partition images will only have a header, while partitions with actual data (eg. boot images) will have both a header and a footer.
|
||||
|
||||
(The `unpack`, `pack`, and `repack` subcommands also show this information. This specific subcommand just does so without performing any other operation.)
|
||||
|
||||
### Verifying AVB hashes and signatures
|
||||
|
||||
```bash
|
||||
@@ -117,7 +119,9 @@ This subcommand repacks a boot image without writing the individual components t
|
||||
avbroot boot info -i <input boot image>
|
||||
```
|
||||
|
||||
All of the `boot` subcommands show the boot image information. This specific subcommand just does it without performing any other operation. To show avbroot's internal representation of the information, pass in `-d`.
|
||||
This subcommand shows the information contained in the boot image's header. To show avbroot's internal representation of the information, pass in `-d`.
|
||||
|
||||
(All of the `boot` subcommands show this information. This specific subcommand just does so without performing any other operation.)
|
||||
|
||||
## `avbroot cpio`
|
||||
|
||||
@@ -159,7 +163,9 @@ This is almost equivalent to running `avbroot cpio unpack` followed by `avbroot
|
||||
avbroot cpio info -i <input cpio archive>
|
||||
```
|
||||
|
||||
All of the `cpio` subcommands show details about all the entries in the archive. This specific subcommand just does it without performing any other operation.
|
||||
This subcommand shows details about every entry in the archive.
|
||||
|
||||
(All of the `cpio` subcommands show this information. This specific subcommand just does so without performing any other operation.)
|
||||
|
||||
## `avbroot fec`
|
||||
|
||||
@@ -320,6 +326,8 @@ avbroot lp info -i <first LP image>
|
||||
|
||||
This subcommand shows the LP image metadata, including all metadata slots. If there are multiple images, only the first one is needed because it is the only one that stores the metadata.
|
||||
|
||||
(All of the `lp` subcommands show this information. This specific subcommand just does so without performing any other operation.)
|
||||
|
||||
## `avbroot payload`
|
||||
|
||||
This set of commands is for working with payload binary files (`payload.bin`). The `unpack` and `pack` commands can only work with full payloads because they require the complete data to be available, but the `repack` and `info` commands also work with delta payloads.
|
||||
@@ -337,17 +345,19 @@ Only full payload binaries can be unpacked. Delta payload binaries from incremen
|
||||
### Packing a payload binary
|
||||
|
||||
```bash
|
||||
avbroot payload pack -o <output payload> --key <OTA private key>
|
||||
avbroot payload pack -o <output payload> -k <OTA private key> [-O <output properties>]
|
||||
```
|
||||
|
||||
This subcommand packs a new payload binary from the `payload.toml` file and `payload_images` directory. Any `.img` files in the `payload_images` directory that don't have a corresponding entry in `payload.toml` are silently ignored.
|
||||
|
||||
When replacing the payload binary in an OTA, it is not sufficient to only update `payload.bin`. The checksums in `payload_properties.txt` need to be updated as well. Use `--output-properties` to generate a new properties file.
|
||||
|
||||
Packing a payload binary requires compressing all of the partition images, which is very CPU intensive. If re-signing an existing payload binary without making any other modifications is all that's needed, use the `repack` subcommand instead.
|
||||
|
||||
### Repacking a payload binary
|
||||
|
||||
```bash
|
||||
avbroot payload repack -i <input payload> -o <output payload> --key <OTA private key>
|
||||
avbroot payload repack -i <input payload> -o <output payload> -k <OTA private key> [-O <output properties>]
|
||||
```
|
||||
|
||||
This subcommand is logically equivalent to `avbroot payload unpack` followed by `avbroot payload pack`, except significantly more efficient. Instead of decompressing and recompressing all partition images, the raw data is directly copied from the input payload binary.
|
||||
@@ -362,6 +372,8 @@ avbroot payload info -i <payload>
|
||||
|
||||
This subcommand shows all of the payload header fields (which will likely be extremely long).
|
||||
|
||||
(All of the `payload` subcommands show this information. This specific subcommand just does so without performing any other operation.)
|
||||
|
||||
## `avbroot sparse`
|
||||
|
||||
This set of commands is for working with Android sparse images. All features of the file format are supported, including hole chunks and CRC32 checksums.
|
||||
@@ -405,3 +417,49 @@ avbroot sparse info -i <input sparse image>
|
||||
```
|
||||
|
||||
This subcommand shows the sparse image metadata, including the header and all chunks.
|
||||
|
||||
(All of the `sparse` subcommands show this information. This specific subcommand just does so without performing any other operation.)
|
||||
|
||||
## `avbroot zip`
|
||||
|
||||
This set of commands is for working with raw OTA zip files. They are intentionally placed outside of `avbroot ota` to make them less discoverable by accident. These commands are useful for creating OTA zip files without going through the normal `avbroot ota patch` mechanism.
|
||||
|
||||
**WARNING**: Make sure to run `avbroot ota verify` on OTA files before installing them. These pack commands are low level operations that only check that the file structure of the OTA itself is valid, not the contents contained within.
|
||||
|
||||
### Unpacking an OTA zip
|
||||
|
||||
```bash
|
||||
avbroot ota unpack -i <input OTA>
|
||||
```
|
||||
|
||||
This subcommand unpacks the OTA metadata to `ota.toml` and the OTA files to the `ota_files` directory.
|
||||
|
||||
### Packing an OTA zip
|
||||
|
||||
```bash
|
||||
avbroot ota pack -o <output OTA> -k <OTA private key>
|
||||
```
|
||||
|
||||
This subcommand packs a new OTA zip from the `ota.toml` file and `ota_files` directory. Any files in the `ota_files` directory that don't have a corresponding entry in `ota.toml` are silently ignored.
|
||||
|
||||
When packing an OTA zip, the `metadata.property_files` field in `ota.toml` may potentially be recomputed. To write a TOML file containing the new values, use `--output-info <output TOML>`. It is safe to overwrite the existing `ota.toml` if desired.
|
||||
|
||||
### Repacking an OTA zip
|
||||
|
||||
```bash
|
||||
avbroot ota repack -i <input OTA> -o <output OTA> -k <OTA private key>
|
||||
```
|
||||
|
||||
This subcommand is logically equivalent to `avbroot ota unpack` followed by `avbroot ota pack`, except more efficient.
|
||||
|
||||
**WARNING**: This is generally not a useful command. Resigning the OTA zip without also resigning the payload binary inside results in an invalid OTA.
|
||||
|
||||
### Showing OTA metadata
|
||||
|
||||
```bash
|
||||
avbroot ota info -i <input OTA>
|
||||
```
|
||||
|
||||
This subcommand shows all of the OTA metadata fields. If both the modern protobuf metadata and the legacy plain text metadata exist, the protobuf metadata takes precedence.
|
||||
|
||||
(All of the `ota` subcommands show this information. This specific subcommand just does so without performing any other operation.)
|
||||
|
||||
@@ -237,7 +237,7 @@ Updates to Android, Magisk, and KernelSU are all done the same way: by patching
|
||||
|
||||
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.
|
||||
3. Reboot to recovery mode: `adb reboot recovery`. 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`.
|
||||
|
||||
|
||||
+1
-1
@@ -235,7 +235,7 @@ avbroot совместим с любым стандартным 4096-битны
|
||||
|
||||
2. Если обновляется Magisk или KernelSU, сначала установите их новый `.apk`. Если вы случайно открыли приложение, убедитесь, что оно **не начало** прошивать загрузочный образ. Если в самом приложении появится предложение обновить загрузочный образ, отклоните его.
|
||||
|
||||
3. Перезагрузитесь в режим Recovery. Если устройство повисло на сплеше с сообщением "No command", удерживайте кнопку питания, а затем нажмите кнопку увеличения громкости один раз.
|
||||
3. Перезагрузитесь в режим Recovery: `adb reboot recovery`. Если устройство повисло на сплеше с сообщением "No command", удерживайте кнопку питания, а затем один раз нажмите кнопку увеличения громкости.
|
||||
|
||||
4. Обновитесь (Apply update from adb → `adb sideload <ota.zip.patched>`).
|
||||
|
||||
|
||||
+2
-2
@@ -28,7 +28,7 @@ flate2 = { version = "1.0.29", features = ["zlib-rs"] }
|
||||
gf256 = { version = "0.3.0", features = ["rs"] }
|
||||
hex = { version = "0.4.3", features = ["serde"] }
|
||||
lz4_flex = "0.12.0"
|
||||
lzma-rust2 = "0.15.2"
|
||||
lzma-rust2 = "0.16.1"
|
||||
memchr = "2.6.0"
|
||||
# We can't upgrade to 0.9.0 until rsa updates its dependency.
|
||||
num-bigint-dig = "0.8.4"
|
||||
@@ -53,7 +53,7 @@ sha1 = "0.10.5"
|
||||
sha2 = "0.10.7"
|
||||
tempfile = "3.8.0"
|
||||
thiserror = "2.0.3"
|
||||
toml_edit = { version = "0.23.3", features = ["serde"] }
|
||||
toml = { version = "1.0.1", features = ["serde"] }
|
||||
topological-sort = "0.2.2"
|
||||
tracing = "0.1.40"
|
||||
tracing-subscriber = "0.3.18"
|
||||
|
||||
+28
-13
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: 2023-2024 Andrew Gunnerson
|
||||
// SPDX-FileCopyrightText: 2023-2026 Andrew Gunnerson
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use std::{env, ffi::OsStr, fs, io, path::Path};
|
||||
@@ -35,6 +35,10 @@ fn main() {
|
||||
const CUE_PU: &str = ".chromeos_update_engine.PartitionUpdate";
|
||||
const CUE_VABCFS: &str = ".chromeos_update_engine.VABCFeatureSet";
|
||||
|
||||
const BTR_DS: &str = ".build.tools.releasetools.DeviceState";
|
||||
const BTR_OM: &str = ".build.tools.releasetools.OtaMetadata";
|
||||
const BTR_PS: &str = ".build.tools.releasetools.PartitionState";
|
||||
|
||||
const DERIVE_SERDE: &str = "#[derive(serde::Deserialize, serde::Serialize)]";
|
||||
const SERDE_DEFAULT: &str = "#[serde(default)]";
|
||||
const SERDE_SKIP: &str = "#[serde(skip)]";
|
||||
@@ -45,19 +49,25 @@ fn main() {
|
||||
prost_build::Config::new()
|
||||
.btree_map(["."])
|
||||
// Allow deserializing and serializing the types we care about.
|
||||
.type_attribute(CUE_AI, DERIVE_SERDE)
|
||||
.type_attribute(CUE_DAM, DERIVE_SERDE)
|
||||
.type_attribute(CUE_DPG, DERIVE_SERDE)
|
||||
.type_attribute(CUE_DPM, DERIVE_SERDE)
|
||||
.type_attribute(CUE_PU, DERIVE_SERDE)
|
||||
.type_attribute(CUE_VABCFS, DERIVE_SERDE)
|
||||
.message_attribute(CUE_AI, DERIVE_SERDE)
|
||||
.message_attribute(CUE_DAM, DERIVE_SERDE)
|
||||
.message_attribute(CUE_DPG, DERIVE_SERDE)
|
||||
.message_attribute(CUE_DPM, DERIVE_SERDE)
|
||||
.message_attribute(CUE_PU, DERIVE_SERDE)
|
||||
.message_attribute(CUE_VABCFS, DERIVE_SERDE)
|
||||
.message_attribute(BTR_DS, DERIVE_SERDE)
|
||||
.message_attribute(BTR_OM, DERIVE_SERDE)
|
||||
.message_attribute(BTR_PS, DERIVE_SERDE)
|
||||
// Allow default-initializing all fields.
|
||||
.type_attribute(CUE_AI, SERDE_DEFAULT)
|
||||
.type_attribute(CUE_DAM, SERDE_DEFAULT)
|
||||
.type_attribute(CUE_DPG, SERDE_DEFAULT)
|
||||
.type_attribute(CUE_DPM, SERDE_DEFAULT)
|
||||
.type_attribute(CUE_PU, SERDE_DEFAULT)
|
||||
.type_attribute(CUE_VABCFS, SERDE_DEFAULT)
|
||||
.message_attribute(CUE_AI, SERDE_DEFAULT)
|
||||
.message_attribute(CUE_DAM, SERDE_DEFAULT)
|
||||
.message_attribute(CUE_DPG, SERDE_DEFAULT)
|
||||
.message_attribute(CUE_DPM, SERDE_DEFAULT)
|
||||
.message_attribute(CUE_PU, SERDE_DEFAULT)
|
||||
.message_attribute(CUE_VABCFS, SERDE_DEFAULT)
|
||||
.message_attribute(BTR_DS, SERDE_DEFAULT)
|
||||
.message_attribute(BTR_OM, SERDE_DEFAULT)
|
||||
.message_attribute(BTR_PS, SERDE_DEFAULT)
|
||||
// Don't serialize fields that define the structure of the payload
|
||||
// binary and that we recompute during packing.
|
||||
.field_attribute(c!(CUE_DAM, ".signatures_offset"), SERDE_SKIP)
|
||||
@@ -84,6 +94,11 @@ fn main() {
|
||||
.field_attribute(c!(CUE_DAM, ".partitions"), SERDE_SKIP_IF_VEC_EMPTY)
|
||||
.field_attribute(c!(CUE_DPG, ".partition_names"), SERDE_SKIP_IF_VEC_EMPTY)
|
||||
.field_attribute(c!(CUE_DPM, ".groups"), SERDE_SKIP_IF_VEC_EMPTY)
|
||||
// Serialize enums as their enum types, not their underlying reprs.
|
||||
.field_attribute(
|
||||
c!(BTR_OM, ".type"),
|
||||
"#[serde(with = \"crate::protobuf::ota_type\")]",
|
||||
)
|
||||
.compile_fds(file_descriptors)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: 2023-2024 Andrew Gunnerson
|
||||
// SPDX-FileCopyrightText: 2023-2026 Andrew Gunnerson
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use std::{
|
||||
@@ -13,7 +13,7 @@ use clap::{Parser, Subcommand, ValueEnum};
|
||||
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};
|
||||
use crate::cli::{avb, boot, completion, cpio, fec, hashtree, key, lp, ota, payload, sparse, zip};
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug, Subcommand)]
|
||||
@@ -29,6 +29,7 @@ pub enum Command {
|
||||
Ota(ota::OtaCli),
|
||||
Payload(payload::PayloadCli),
|
||||
Sparse(sparse::SparseCli),
|
||||
Zip(zip::ZipCli),
|
||||
/// (Deprecated: Use `avbroot ota patch` instead.)
|
||||
#[command(hide = true)]
|
||||
Patch(ota::PatchCli),
|
||||
@@ -132,6 +133,7 @@ pub fn main(logging_initialized: &AtomicBool, cancel_signal: &AtomicBool) -> Res
|
||||
Command::Ota(c) => ota::ota_main(&c, cancel_signal),
|
||||
Command::Payload(c) => payload::payload_main(&c, cancel_signal),
|
||||
Command::Sparse(c) => sparse::sparse_main(&c, cancel_signal),
|
||||
Command::Zip(c) => zip::zip_main(&c, cancel_signal),
|
||||
// Deprecated aliases.
|
||||
Command::Patch(c) => ota::patch_subcommand(&c, cancel_signal),
|
||||
Command::Extract(c) => ota::extract_subcommand(&c, cancel_signal),
|
||||
|
||||
@@ -37,9 +37,9 @@ struct AvbInfo {
|
||||
}
|
||||
|
||||
fn read_avb_image(path: &Path) -> Result<(AvbInfo, BufReader<File>)> {
|
||||
let file = File::open(path)
|
||||
let mut reader = File::open(path)
|
||||
.map(BufReader::new)
|
||||
.with_context(|| format!("Failed to open AVB image for reading: {path:?}"))?;
|
||||
let mut reader = BufReader::new(file);
|
||||
let (header, footer, image_size) = avb::load_image(&mut reader)
|
||||
.with_context(|| format!("Failed to load AVB image: {path:?}"))?;
|
||||
|
||||
@@ -78,7 +78,7 @@ fn write_avb_image(file: &File, info: &mut AvbInfo, recompute_size: bool) -> Res
|
||||
fn read_info(path: &Path) -> Result<AvbInfo> {
|
||||
let data = fs::read_to_string(path)
|
||||
.with_context(|| format!("Failed to read AVB info TOML: {path:?}"))?;
|
||||
let info = toml_edit::de::from_str(&data)
|
||||
let info = toml::de::from_str(&data)
|
||||
.with_context(|| format!("Failed to parse AVB info TOML: {path:?}"))?;
|
||||
|
||||
Ok(info)
|
||||
@@ -86,7 +86,7 @@ fn read_info(path: &Path) -> Result<AvbInfo> {
|
||||
|
||||
/// Write AVB information to TOML file.
|
||||
fn write_info(path: &Path, info: &AvbInfo) -> Result<()> {
|
||||
let data = toml_edit::ser::to_string_pretty(info)
|
||||
let data = toml::ser::to_string_pretty(info)
|
||||
.with_context(|| format!("Failed to serialize AVB info TOML: {path:?}"))?;
|
||||
fs::write(path, data).with_context(|| format!("Failed to write AVB info TOML: {path:?}"))?;
|
||||
|
||||
@@ -101,14 +101,14 @@ fn write_raw(
|
||||
size: u64,
|
||||
cancel_signal: &AtomicBool,
|
||||
) -> Result<File> {
|
||||
let file = fs::OpenOptions::new()
|
||||
let mut writer = fs::OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.open(path)
|
||||
.map(BufWriter::new)
|
||||
.with_context(|| format!("Failed to open raw image for writing: {path:?}"))?;
|
||||
let mut writer = BufWriter::new(file);
|
||||
|
||||
reader
|
||||
.rewind()
|
||||
@@ -1169,8 +1169,8 @@ struct VerifyCli {
|
||||
struct VerifyDeviceCli {
|
||||
/// Path to public key in AVB binary format.
|
||||
///
|
||||
/// If this is not specified, the signatures can only be checked for
|
||||
/// validity, not whether they are trusted.
|
||||
/// If this is not specified, then the signature is verified against the
|
||||
/// ro.boot.vbmeta.public_key_digest value provided by the bootloader.
|
||||
#[arg(short, long, value_name = "FILE", value_parser)]
|
||||
public_key: Option<PathBuf>,
|
||||
|
||||
|
||||
+22
-14
@@ -16,8 +16,9 @@ use crate::{
|
||||
};
|
||||
|
||||
fn read_image(path: &Path) -> Result<BootImage> {
|
||||
let file = File::open(path).with_context(|| format!("Failed to open for reading: {path:?}"))?;
|
||||
let reader = BufReader::new(file);
|
||||
let reader = File::open(path)
|
||||
.map(BufReader::new)
|
||||
.with_context(|| format!("Failed to open for reading: {path:?}"))?;
|
||||
let image = BootImage::from_reader(reader)
|
||||
.with_context(|| format!("Failed to read boot image: {path:?}"))?;
|
||||
|
||||
@@ -25,9 +26,9 @@ fn read_image(path: &Path) -> Result<BootImage> {
|
||||
}
|
||||
|
||||
fn write_image(path: &Path, image: &BootImage) -> Result<()> {
|
||||
let file =
|
||||
File::create(path).with_context(|| format!("Failed to open for writing: {path:?}"))?;
|
||||
let mut writer = BufWriter::new(file);
|
||||
let mut writer = File::create(path)
|
||||
.map(BufWriter::new)
|
||||
.with_context(|| format!("Failed to open for writing: {path:?}"))?;
|
||||
image
|
||||
.to_writer(&mut writer)
|
||||
.with_context(|| format!("Failed to write boot image: {path:?}"))?;
|
||||
@@ -41,14 +42,14 @@ fn write_image(path: &Path, image: &BootImage) -> Result<()> {
|
||||
fn read_header(path: &Path) -> Result<BootImage> {
|
||||
let data = fs::read_to_string(path)
|
||||
.with_context(|| format!("Failed to read header TOML: {path:?}"))?;
|
||||
let image = toml_edit::de::from_str(&data)
|
||||
let image = toml::de::from_str(&data)
|
||||
.with_context(|| format!("Failed to parse header TOML: {path:?}"))?;
|
||||
|
||||
Ok(image)
|
||||
}
|
||||
|
||||
fn write_header(path: &Path, image: &BootImage) -> Result<()> {
|
||||
let data = toml_edit::ser::to_string_pretty(image)
|
||||
let data = toml::ser::to_string_pretty(image)
|
||||
.with_context(|| format!("Failed to serialize header TOML: {path:?}"))?;
|
||||
fs::write(path, data).with_context(|| format!("Failed to write header TOML: {path:?}"))?;
|
||||
|
||||
@@ -76,12 +77,12 @@ fn read_text_if_exists(path: &Path) -> Result<Option<String>> {
|
||||
}
|
||||
|
||||
fn read_avb_header_if_exists(path: &Path) -> Result<Option<Header>> {
|
||||
let file = match File::open(path) {
|
||||
Ok(f) => f,
|
||||
let reader = match File::open(path) {
|
||||
Ok(f) => BufReader::new(f),
|
||||
Err(e) if e.kind() == io::ErrorKind::NotFound => return Ok(None),
|
||||
Err(e) => Err(e).with_context(|| format!("Failed to open for reading: {path:?}"))?,
|
||||
};
|
||||
let header = Header::from_reader(BufReader::new(file))
|
||||
let header = Header::from_reader(reader)
|
||||
.with_context(|| format!("Failed to read vbmeta header: {path:?}"))?;
|
||||
|
||||
Ok(Some(header))
|
||||
@@ -105,9 +106,15 @@ 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(|| format!("Failed to open for writing: {path:?}"))?;
|
||||
header.to_writer(BufWriter::new(file))?;
|
||||
let mut writer = File::create(path)
|
||||
.map(BufWriter::new)
|
||||
.with_context(|| format!("Failed to open for writing: {path:?}"))?;
|
||||
header
|
||||
.to_writer(&mut writer)
|
||||
.with_context(|| format!("Failed to write AVB header: {path:?}"))?;
|
||||
writer
|
||||
.flush()
|
||||
.with_context(|| format!("Failed to flush AVB header: {path:?}"))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -286,8 +293,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)
|
||||
.map(BufReader::new)
|
||||
.with_context(|| format!("Failed to open for reading: {:?}", cli.image))?;
|
||||
let boot_image = BootImage::from_reader(BufReader::new(raw_reader))
|
||||
let boot_image = BootImage::from_reader(raw_reader)
|
||||
.with_context(|| format!("Failed to load boot image: {:?}", cli.image))?;
|
||||
|
||||
let mut ramdisks = vec![];
|
||||
|
||||
+11
-10
@@ -36,9 +36,10 @@ fn open_reader(
|
||||
CpioReader<CompressedReader<BufReader<File>>>,
|
||||
CompressedFormat,
|
||||
)> {
|
||||
let file =
|
||||
File::open(path).with_context(|| format!("Failed to open cpio for reading: {path:?}"))?;
|
||||
let reader = CompressedReader::new(BufReader::new(file), true)
|
||||
let raw_reader = File::open(path)
|
||||
.map(BufReader::new)
|
||||
.with_context(|| format!("Failed to open cpio for reading: {path:?}"))?;
|
||||
let reader = CompressedReader::new(raw_reader, true)
|
||||
.with_context(|| format!("Failed to open decompressor: {path:?}"))?;
|
||||
let format = reader.format();
|
||||
let cpio_reader = CpioReader::new(reader, include_trailer);
|
||||
@@ -50,9 +51,10 @@ fn open_writer(
|
||||
path: &Path,
|
||||
format: CompressedFormat,
|
||||
) -> Result<CpioWriter<CompressedWriter<BufWriter<File>>>> {
|
||||
let file =
|
||||
File::create(path).with_context(|| format!("Failed to open cpio for writing: {path:?}"))?;
|
||||
let writer = CompressedWriter::new(BufWriter::new(file), format)
|
||||
let raw_writer = File::create(path)
|
||||
.map(BufWriter::new)
|
||||
.with_context(|| format!("Failed to open cpio for writing: {path:?}"))?;
|
||||
let writer = CompressedWriter::new(raw_writer, format)
|
||||
.with_context(|| format!("Failed to open compressor: {path:?}"))?;
|
||||
let cpio_writer = CpioWriter::new(writer, false);
|
||||
|
||||
@@ -73,7 +75,7 @@ fn flush_writer(writer: CpioWriter<CompressedWriter<BufWriter<File>>>) -> Result
|
||||
fn read_info(path: &Path) -> Result<CpioInfo> {
|
||||
let data = fs::read_to_string(path)
|
||||
.with_context(|| format!("Failed to read cpio info TOML: {path:?}"))?;
|
||||
let info = toml_edit::de::from_str(&data)
|
||||
let info = toml::de::from_str(&data)
|
||||
.with_context(|| format!("Failed to parse cpio info TOML: {path:?}"))?;
|
||||
|
||||
Ok(info)
|
||||
@@ -81,7 +83,7 @@ fn read_info(path: &Path) -> Result<CpioInfo> {
|
||||
|
||||
/// Write cpio information to TOML file.
|
||||
fn write_info(path: &Path, info: &CpioInfo) -> Result<()> {
|
||||
let data = toml_edit::ser::to_string_pretty(info)
|
||||
let data = toml::ser::to_string_pretty(info)
|
||||
.with_context(|| format!("Failed to serialize cpio info TOML: {path:?}"))?;
|
||||
fs::write(path, data).with_context(|| format!("Failed to write cpio info TOML: {path:?}"))?;
|
||||
|
||||
@@ -287,8 +289,7 @@ pub fn cpio_main(cli: &CpioCli, cancel_signal: &AtomicBool) -> Result<()> {
|
||||
/// TOML file, like the UID/GID, permissions, and symlink targets.
|
||||
///
|
||||
/// If any paths inside the cpio archive are unsafe, the extraction process will
|
||||
/// fail and exit. Extracted files are never written outside of the tree
|
||||
/// directory, even if an external process tries to interfere.
|
||||
/// fail and exit.
|
||||
#[derive(Debug, Parser)]
|
||||
struct UnpackCli {
|
||||
/// Path to input cpio file.
|
||||
|
||||
@@ -50,14 +50,14 @@ fn open_lp_outputs(paths: &[impl AsRef<Path>]) -> Result<Vec<File>> {
|
||||
fn read_info(path: &Path) -> Result<Metadata> {
|
||||
let data = fs::read_to_string(path)
|
||||
.with_context(|| format!("Failed to read metadata info TOML: {path:?}"))?;
|
||||
let info = toml_edit::de::from_str(&data)
|
||||
let info = toml::de::from_str(&data)
|
||||
.with_context(|| format!("Failed to parse metadata info TOML: {path:?}"))?;
|
||||
|
||||
Ok(info)
|
||||
}
|
||||
|
||||
fn write_info(path: &Path, metadata: &Metadata) -> Result<()> {
|
||||
let data = toml_edit::ser::to_string_pretty(metadata)
|
||||
let data = toml::ser::to_string_pretty(metadata)
|
||||
.with_context(|| format!("Failed to serialize metadata info TOML: {path:?}"))?;
|
||||
fs::write(path, data)
|
||||
.with_context(|| format!("Failed to write metadata info TOML: {path:?}"))?;
|
||||
@@ -515,8 +515,7 @@ pub fn lp_main(cli: &LpCli, cancel_signal: &AtomicBool) -> Result<()> {
|
||||
/// directory. For empty images, the output images directory is unused.
|
||||
///
|
||||
/// If any partition names are unsafe to use in a path, the extraction process
|
||||
/// will fail and exit. Extracted files are never written outside of the tree
|
||||
/// directory, even if an external process tries to interfere.
|
||||
/// will fail and exit.
|
||||
#[derive(Debug, Parser)]
|
||||
struct UnpackCli {
|
||||
/// Path to input LP images.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: 2023-2024 Andrew Gunnerson
|
||||
// SPDX-FileCopyrightText: 2023-2026 Andrew Gunnerson
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
pub mod args;
|
||||
@@ -13,3 +13,4 @@ pub mod lp;
|
||||
pub mod ota;
|
||||
pub mod payload;
|
||||
pub mod sparse;
|
||||
pub mod zip;
|
||||
|
||||
+46
-18
@@ -78,6 +78,23 @@ bitflags! {
|
||||
}
|
||||
}
|
||||
|
||||
fn is_zero_sha256(mut size: u64, digest: &[u8]) -> bool {
|
||||
if digest.len() != ring::digest::SHA256_OUTPUT_LEN {
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut context = ring::digest::Context::new(&ring::digest::SHA256);
|
||||
while size > 0 {
|
||||
let n = size.min(util::ZEROS.len() as u64);
|
||||
context.update(&util::ZEROS[..n as usize]);
|
||||
size -= n;
|
||||
}
|
||||
|
||||
let zero_digest = context.finish();
|
||||
|
||||
zero_digest.as_ref() == digest
|
||||
}
|
||||
|
||||
/// Get the images required for patching. If [`RequiredFlags::SYSTEM`] is
|
||||
/// specified, then the system image is included. If [`RequiredFlags::ALL_COW`]
|
||||
/// is specified, then all images with CoW size estimates are included.
|
||||
@@ -96,6 +113,18 @@ pub fn get_required_images(
|
||||
} else if required_flags.contains(RequiredFlags::SYSTEM) && name == "system" {
|
||||
flags |= PartitionFlags::SYSTEM;
|
||||
} else if name.starts_with("vbmeta") {
|
||||
let Some(pi) = &partition.new_partition_info else {
|
||||
continue;
|
||||
};
|
||||
|
||||
// Some devices seem to ship with empty unused vbmeta partitions.
|
||||
// Use the SHA-256 checksum to skip them so we don't have to extract
|
||||
// them to check.
|
||||
let size = pi.size();
|
||||
if (4096..=65536).contains(&size) && is_zero_sha256(size, pi.hash()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
flags |= PartitionFlags::VBMETA;
|
||||
}
|
||||
|
||||
@@ -1571,7 +1600,6 @@ pub fn patch_subcommand(cli: &PatchCli, cancel_signal: &AtomicBool) -> Result<()
|
||||
let mut temp_writer = signing_writer
|
||||
.finish(&key_ota, &cert_ota, cancel_signal)
|
||||
.context("Failed to sign output zip")?;
|
||||
temp_writer.flush().context("Failed to flush output zip")?;
|
||||
|
||||
// We do a lot of low-level hackery. Reopen and verify offsets.
|
||||
info!("Verifying metadata offsets");
|
||||
@@ -1911,9 +1939,9 @@ pub fn verify_subcommand(cli: &VerifyCli, cancel_signal: &AtomicBool) -> Result<
|
||||
};
|
||||
}
|
||||
|
||||
let raw_reader = File::open(&cli.input)
|
||||
let mut reader = File::open(&cli.input)
|
||||
.map(BufReader::new)
|
||||
.with_context(|| format!("Failed to open for reading: {:?}", cli.input))?;
|
||||
let mut reader = BufReader::new(raw_reader);
|
||||
|
||||
info!("Verifying whole-file signature");
|
||||
|
||||
@@ -2066,10 +2094,10 @@ pub fn verify_subcommand(cli: &VerifyCli, cancel_signal: &AtomicBool) -> Result<
|
||||
}
|
||||
}
|
||||
|
||||
let boot_images = boot::load_boot_images(&boot_image_names, &Opener(temp_dir.path()))
|
||||
let (boot_images, layout) = boot::load_boot_images(&boot_image_names, &Opener(temp_dir.path()))
|
||||
.context("Failed to load all boot images")?;
|
||||
let targets = OtaCertPatcher::new(ota_cert.clone())
|
||||
.find_targets(&boot_images, cancel_signal)
|
||||
.find_targets(layout, &boot_images, cancel_signal)
|
||||
.context("Failed to find boot image containing otacerts.zip")?;
|
||||
|
||||
if targets.is_empty() {
|
||||
@@ -2164,7 +2192,7 @@ pub struct PatchCli {
|
||||
alias = "privkey-avb",
|
||||
value_name = "FILE",
|
||||
value_parser,
|
||||
help_heading = HEADING_KEY
|
||||
help_heading = HEADING_KEY,
|
||||
)]
|
||||
pub key_avb: PathBuf,
|
||||
|
||||
@@ -2177,7 +2205,7 @@ pub struct PatchCli {
|
||||
alias = "privkey-ota",
|
||||
value_name = "FILE",
|
||||
value_parser,
|
||||
help_heading = HEADING_KEY
|
||||
help_heading = HEADING_KEY,
|
||||
)]
|
||||
pub key_ota: PathBuf,
|
||||
|
||||
@@ -2192,7 +2220,7 @@ pub struct PatchCli {
|
||||
value_name = "ENV_VAR",
|
||||
value_parser,
|
||||
group = "pass_avb",
|
||||
help_heading = HEADING_KEY
|
||||
help_heading = HEADING_KEY,
|
||||
)]
|
||||
pub pass_avb_env_var: Option<OsString>,
|
||||
|
||||
@@ -2203,7 +2231,7 @@ pub struct PatchCli {
|
||||
value_name = "FILE",
|
||||
value_parser,
|
||||
group = "pass_avb",
|
||||
help_heading = HEADING_KEY
|
||||
help_heading = HEADING_KEY,
|
||||
)]
|
||||
pub pass_avb_file: Option<PathBuf>,
|
||||
|
||||
@@ -2214,7 +2242,7 @@ pub struct PatchCli {
|
||||
value_name = "ENV_VAR",
|
||||
value_parser,
|
||||
group = "pass_ota",
|
||||
help_heading = HEADING_KEY
|
||||
help_heading = HEADING_KEY,
|
||||
)]
|
||||
pub pass_ota_env_var: Option<OsString>,
|
||||
|
||||
@@ -2225,7 +2253,7 @@ pub struct PatchCli {
|
||||
value_name = "FILE",
|
||||
value_parser,
|
||||
group = "pass_ota",
|
||||
help_heading = HEADING_KEY
|
||||
help_heading = HEADING_KEY,
|
||||
)]
|
||||
pub pass_ota_file: Option<PathBuf>,
|
||||
|
||||
@@ -2244,7 +2272,7 @@ pub struct PatchCli {
|
||||
value_names = ["PARTITION", "FILE"],
|
||||
value_parser = value_parser!(OsString),
|
||||
num_args = 2,
|
||||
help_heading = HEADING_PATH
|
||||
help_heading = HEADING_PATH,
|
||||
)]
|
||||
pub replace: Vec<OsString>,
|
||||
|
||||
@@ -2256,7 +2284,7 @@ pub struct PatchCli {
|
||||
long,
|
||||
value_name = "PARTITION",
|
||||
conflicts_with_all = ["prepatched", "rootless"],
|
||||
help_heading = HEADING_MAGISK
|
||||
help_heading = HEADING_MAGISK,
|
||||
)]
|
||||
pub magisk_preinit_device: Option<String>,
|
||||
|
||||
@@ -2265,7 +2293,7 @@ pub struct PatchCli {
|
||||
long,
|
||||
value_name = "NUMBER",
|
||||
conflicts_with_all = ["prepatched", "rootless"],
|
||||
help_heading = HEADING_MAGISK
|
||||
help_heading = HEADING_MAGISK,
|
||||
)]
|
||||
pub magisk_random_seed: Option<u64>,
|
||||
|
||||
@@ -2273,7 +2301,7 @@ pub struct PatchCli {
|
||||
#[arg(
|
||||
long,
|
||||
conflicts_with_all = ["prepatched", "rootless"],
|
||||
help_heading = HEADING_MAGISK
|
||||
help_heading = HEADING_MAGISK,
|
||||
)]
|
||||
pub ignore_magisk_warnings: bool,
|
||||
|
||||
@@ -2282,7 +2310,7 @@ pub struct PatchCli {
|
||||
long,
|
||||
action = ArgAction::Count,
|
||||
conflicts_with_all = ["magisk", "rootless"],
|
||||
help_heading = HEADING_PREPATCHED
|
||||
help_heading = HEADING_PREPATCHED,
|
||||
)]
|
||||
pub ignore_prepatched_compat: u8,
|
||||
|
||||
@@ -2339,7 +2367,7 @@ pub struct PatchCli {
|
||||
long,
|
||||
value_name = "MODE",
|
||||
default_value_t = ZipMode::Streaming,
|
||||
help_heading = HEADING_OTHER
|
||||
help_heading = HEADING_OTHER,
|
||||
)]
|
||||
pub zip_mode: ZipMode,
|
||||
|
||||
@@ -2348,7 +2376,7 @@ pub struct PatchCli {
|
||||
long,
|
||||
value_name = "PARTITION",
|
||||
hide = true,
|
||||
help_heading = HEADING_OTHER
|
||||
help_heading = HEADING_OTHER,
|
||||
)]
|
||||
pub boot_partition: Option<String>,
|
||||
}
|
||||
|
||||
@@ -52,14 +52,14 @@ fn open_writer(
|
||||
fn read_info(path: &Path) -> Result<PayloadHeader> {
|
||||
let data = fs::read_to_string(path)
|
||||
.with_context(|| format!("Failed to read payload info TOML: {path:?}"))?;
|
||||
let info = toml_edit::de::from_str(&data)
|
||||
let info = toml::de::from_str(&data)
|
||||
.with_context(|| format!("Failed to parse payload info TOML: {path:?}"))?;
|
||||
|
||||
Ok(info)
|
||||
}
|
||||
|
||||
fn write_info(path: &Path, manifest: &PayloadHeader) -> Result<()> {
|
||||
let data = toml_edit::ser::to_string_pretty(manifest)
|
||||
let data = toml::ser::to_string_pretty(manifest)
|
||||
.with_context(|| format!("Failed to serialize payload info TOML: {path:?}"))?;
|
||||
fs::write(path, data)
|
||||
.with_context(|| format!("Failed to write payload info TOML: {path:?}"))?;
|
||||
@@ -235,10 +235,12 @@ fn pack_subcommand(
|
||||
.with_context(|| format!("Failed to copy from replacement image: {name}"))?;
|
||||
}
|
||||
|
||||
let (_, header, properties, _) = payload_writer
|
||||
let (raw_writer, header, properties, _) = payload_writer
|
||||
.finish()
|
||||
.context("Failed to finalize payload")?;
|
||||
|
||||
raw_writer.into_inner().context("Failed to flush payload")?;
|
||||
|
||||
// Display the header information now that it has been finalized.
|
||||
display_header(payload_cli, &header);
|
||||
|
||||
@@ -363,8 +365,7 @@ struct KeyGroup {
|
||||
/// directory. The payload header metadata is written to the info TOML file.
|
||||
///
|
||||
/// If any partition names are unsafe to use in a path, the extraction process
|
||||
/// will fail and exit. Extracted files are never written outside of the tree
|
||||
/// directory, even if an external process tries to interfere.
|
||||
/// will fail and exit.
|
||||
#[derive(Debug, Parser)]
|
||||
struct UnpackCli {
|
||||
/// Path to input payload binary.
|
||||
@@ -407,7 +408,7 @@ struct PackCli {
|
||||
output: PathBuf,
|
||||
|
||||
/// Path to output payload properties file.
|
||||
#[arg(short, long, value_name = "FILE", value_parser)]
|
||||
#[arg(short = 'O', long, value_name = "FILE", value_parser)]
|
||||
output_properties: Option<PathBuf>,
|
||||
|
||||
/// Path to input info TOML.
|
||||
@@ -447,7 +448,7 @@ struct RepackCli {
|
||||
output: PathBuf,
|
||||
|
||||
/// Path to output payload properties file.
|
||||
#[arg(short, long, value_name = "FILE", value_parser)]
|
||||
#[arg(short = 'O', long, value_name = "FILE", value_parser)]
|
||||
output_properties: Option<PathBuf>,
|
||||
|
||||
#[command(flatten)]
|
||||
@@ -457,7 +458,7 @@ struct RepackCli {
|
||||
/// Display payload information.
|
||||
#[derive(Debug, Parser)]
|
||||
struct InfoCli {
|
||||
/// Path to input payload file.
|
||||
/// Path to input payload binary.
|
||||
#[arg(short, long, value_name = "FILE", value_parser)]
|
||||
input: PathBuf,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,629 @@
|
||||
// SPDX-FileCopyrightText: 2026 Andrew Gunnerson
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
ffi::OsString,
|
||||
fs::{self, File, OpenOptions},
|
||||
io::{BufReader, Seek},
|
||||
path::{Path, PathBuf},
|
||||
sync::atomic::AtomicBool,
|
||||
};
|
||||
|
||||
use anyhow::{Context, Result, anyhow};
|
||||
use clap::{Args, Parser, Subcommand};
|
||||
use rawzip::{
|
||||
CompressionMethod, RECOMMENDED_BUFFER_SIZE, ZipArchive, ZipArchiveEntryWayfinder,
|
||||
ZipArchiveWriter, ZipDataWriter, ZipEntryWriter, extra_fields::ExtraFieldId,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::warn;
|
||||
use x509_cert::Certificate;
|
||||
|
||||
use crate::{
|
||||
crypto::{self, PassphraseSource, RsaSigningKey},
|
||||
format::{
|
||||
ota::{self, SigningWriter, ZipEntry, ZipMode},
|
||||
payload::PayloadHeader,
|
||||
zip::{
|
||||
self, ReaderAtWrapper, ZipArchiveReadAtExt, ZipEntriesSafeExt, ZipFileHeaderRecordExt,
|
||||
},
|
||||
},
|
||||
protobuf::build::tools::releasetools::OtaMetadata,
|
||||
stream::{self, FromReader},
|
||||
util,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
struct OtaInfo {
|
||||
metadata: OtaMetadata,
|
||||
files: Vec<String>,
|
||||
}
|
||||
|
||||
struct InputEntry {
|
||||
compression_method: CompressionMethod,
|
||||
is_zip64: bool,
|
||||
wayfinder: ZipArchiveEntryWayfinder,
|
||||
}
|
||||
|
||||
fn is_excluded_path(path: &str) -> bool {
|
||||
path == ota::PATH_METADATA || path == ota::PATH_METADATA_PB || path == ota::PATH_OTACERT
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn open_reader(
|
||||
path: &Path,
|
||||
) -> Result<(
|
||||
ZipArchive<ReaderAtWrapper<File>>,
|
||||
OtaMetadata,
|
||||
BTreeMap<String, InputEntry>,
|
||||
)> {
|
||||
let mut reader =
|
||||
File::open(path).with_context(|| format!("Failed to open OTA for reading: {path:?}"))?;
|
||||
|
||||
let (metadata, _, _, _) = ota::parse_zip_ota_info(&mut reader)
|
||||
.with_context(|| format!("Failed to parse OTA metadata: {path:?}"))?;
|
||||
|
||||
let mut buffer = vec![0u8; RECOMMENDED_BUFFER_SIZE];
|
||||
let zip_reader =
|
||||
ZipArchive::from_read_at(reader, &mut buffer).context("Failed to read OTA zip")?;
|
||||
|
||||
let mut entries = zip_reader.entries_safe(&mut buffer);
|
||||
let mut input_entries = BTreeMap::new();
|
||||
|
||||
while let Some((cd_entry, _)) = entries.next_entry().context("Failed to list zip entries")? {
|
||||
if cd_entry.is_dir() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let path = cd_entry
|
||||
.file_path_utf8()
|
||||
.context("Zip contains non-UTF-8 paths")?;
|
||||
|
||||
if is_excluded_path(path) {
|
||||
// OTA metadata is never copied from the input file.
|
||||
continue;
|
||||
}
|
||||
|
||||
input_entries.insert(
|
||||
path.to_owned(),
|
||||
InputEntry {
|
||||
compression_method: cd_entry.compression_method(),
|
||||
// We only check for the sizes here instead of the presence of
|
||||
// the ZIP64 extra field. The central header's extra fields may
|
||||
// have ZIP64 only for the local header offset.
|
||||
is_zip64: cd_entry.compressed_size_hint() >= 0xffffffff
|
||||
|| cd_entry.uncompressed_size_hint() >= 0xffffffff,
|
||||
wayfinder: cd_entry.wayfinder(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Ok((zip_reader, metadata, input_entries))
|
||||
}
|
||||
|
||||
fn open_writer(path: &Path, zip_mode: ZipMode) -> Result<ZipArchiveWriter<SigningWriter<File>>> {
|
||||
let writer = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.open(path)
|
||||
.with_context(|| format!("Failed to open OTA for writing: {path:?}"))?;
|
||||
|
||||
let signing_writer = match zip_mode {
|
||||
ZipMode::Streaming => SigningWriter::new_streaming(writer),
|
||||
ZipMode::Seekable => SigningWriter::new_seekable(writer),
|
||||
};
|
||||
let zip_writer = ZipArchiveWriter::new(signing_writer);
|
||||
|
||||
Ok(zip_writer)
|
||||
}
|
||||
|
||||
fn read_info(path: &Path) -> Result<OtaInfo> {
|
||||
let data = fs::read_to_string(path)
|
||||
.with_context(|| format!("Failed to read OTA info TOML: {path:?}"))?;
|
||||
let info = toml::de::from_str(&data)
|
||||
.with_context(|| format!("Failed to parse OTA info TOML: {path:?}"))?;
|
||||
|
||||
Ok(info)
|
||||
}
|
||||
|
||||
fn write_info(path: &Path, info: &OtaInfo) -> Result<()> {
|
||||
let data = toml::ser::to_string_pretty(info)
|
||||
.with_context(|| format!("Failed to serialize OTA info TOML: {path:?}"))?;
|
||||
fs::write(path, data).with_context(|| format!("Failed to write OTA info TOML: {path:?}"))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_info(cli: &ZipCli, info: &OtaInfo) {
|
||||
if !cli.quiet {
|
||||
println!("{info:#?}");
|
||||
}
|
||||
}
|
||||
|
||||
fn load_key(group: &KeyGroup) -> Result<(RsaSigningKey, Certificate)> {
|
||||
let source = PassphraseSource::new(
|
||||
&group.key,
|
||||
group.pass_file.as_deref(),
|
||||
group.pass_env_var.as_deref(),
|
||||
);
|
||||
let signing_key = if let Some(helper) = &group.signing_helper {
|
||||
let public_key = crypto::read_pem_public_key_file(&group.key)
|
||||
.with_context(|| format!("Failed to load key: {:?}", group.key))?;
|
||||
|
||||
RsaSigningKey::External {
|
||||
program: helper.clone(),
|
||||
public_key_file: group.key.clone(),
|
||||
public_key,
|
||||
passphrase_source: source,
|
||||
}
|
||||
} else {
|
||||
let private_key = crypto::read_pem_key_file(&group.key, &source)
|
||||
.with_context(|| format!("Failed to load key: {:?}", group.key))?;
|
||||
|
||||
RsaSigningKey::Internal(private_key)
|
||||
};
|
||||
|
||||
let cert = crypto::read_pem_cert_file(&group.cert)
|
||||
.with_context(|| format!("Failed to load certificate: {:?}", group.cert))?;
|
||||
|
||||
Ok((signing_key, cert))
|
||||
}
|
||||
|
||||
fn start_entry<'a>(
|
||||
zip_writer: &'a mut ZipArchiveWriter<SigningWriter<File>>,
|
||||
path: &str,
|
||||
zip_mode: ZipMode,
|
||||
is_zip64: bool,
|
||||
) -> Result<(u64, ZipDataWriter<ZipEntryWriter<'a, SigningWriter<File>>>)> {
|
||||
let mut builder = zip_writer
|
||||
.new_file(path)
|
||||
.compression_method(CompressionMethod::Store);
|
||||
|
||||
if zip_mode == ZipMode::Seekable && is_zip64 {
|
||||
// We need to reserve space for the ZIP64 extra field when doing the
|
||||
// post-processing to convert a streaming zip to a seekable one.
|
||||
builder = builder.extra_field(
|
||||
ExtraFieldId::ANDROID_ZIP_ALIGNMENT,
|
||||
&[0u8; 16],
|
||||
rawzip::Header::LOCAL,
|
||||
)?;
|
||||
}
|
||||
|
||||
let (entry_writer, data_config) = builder
|
||||
.start()
|
||||
.with_context(|| format!("Failed to begin new zip entry: {path}"))?;
|
||||
let offset = entry_writer.stream_offset();
|
||||
let data_writer = data_config.wrap(entry_writer);
|
||||
|
||||
Ok((offset, data_writer))
|
||||
}
|
||||
|
||||
fn finalize_entry(
|
||||
path: &str,
|
||||
offset: u64,
|
||||
data_writer: ZipDataWriter<ZipEntryWriter<SigningWriter<File>>>,
|
||||
metadata_entries: &mut Vec<ZipEntry>,
|
||||
) -> Result<()> {
|
||||
let size = data_writer
|
||||
.finish()
|
||||
.and_then(|(w, d)| w.finish(d))
|
||||
.with_context(|| format!("Failed to finalize zip entry: {path}"))?;
|
||||
|
||||
metadata_entries.push(ZipEntry {
|
||||
path: path.to_owned(),
|
||||
offset,
|
||||
size,
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn add_otacert_entry(
|
||||
zip_writer: &mut ZipArchiveWriter<SigningWriter<File>>,
|
||||
zip_mode: ZipMode,
|
||||
cert: &Certificate,
|
||||
metadata_entries: &mut Vec<ZipEntry>,
|
||||
) -> Result<()> {
|
||||
let (offset, mut data_writer) = start_entry(zip_writer, ota::PATH_OTACERT, zip_mode, false)?;
|
||||
|
||||
crypto::write_pem_cert(Path::new(ota::PATH_OTACERT), &mut data_writer, cert)
|
||||
.with_context(|| format!("Failed to write entry: {}", ota::PATH_OTACERT))?;
|
||||
|
||||
finalize_entry(ota::PATH_OTACERT, offset, data_writer, metadata_entries)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn finalize_ota(
|
||||
key: &RsaSigningKey,
|
||||
cert: &Certificate,
|
||||
mut zip_writer: ZipArchiveWriter<SigningWriter<File>>,
|
||||
zip_mode: ZipMode,
|
||||
metadata_entries: &mut Vec<ZipEntry>,
|
||||
payload_metadata_size: Option<u64>,
|
||||
metadata: &mut OtaMetadata,
|
||||
cancel_signal: &AtomicBool,
|
||||
) -> Result<File> {
|
||||
add_otacert_entry(&mut zip_writer, zip_mode, cert, metadata_entries)?;
|
||||
|
||||
let payload_metadata_size =
|
||||
payload_metadata_size.ok_or_else(|| anyhow!("Missing payload metadata size"))?;
|
||||
let metadata_offset = zip_writer.stream_offset();
|
||||
|
||||
*metadata = ota::add_metadata(
|
||||
metadata_entries,
|
||||
&mut zip_writer,
|
||||
// Offset where next entry would begin.
|
||||
metadata_offset,
|
||||
metadata,
|
||||
payload_metadata_size,
|
||||
)
|
||||
.context("Failed to write new OTA metadata")?;
|
||||
|
||||
let signing_writer = zip_writer
|
||||
.finish()
|
||||
.context("Failed to finalize output zip")?;
|
||||
let mut raw_writer = signing_writer
|
||||
.finish(key, cert, cancel_signal)
|
||||
.context("Failed to sign output zip")?;
|
||||
|
||||
raw_writer.rewind().context("Failed to seek output zip")?;
|
||||
ota::verify_metadata(
|
||||
BufReader::new(&mut raw_writer),
|
||||
metadata,
|
||||
payload_metadata_size,
|
||||
)
|
||||
.context("Failed to verify OTA metadata offsets")?;
|
||||
|
||||
Ok(raw_writer)
|
||||
}
|
||||
|
||||
fn unpack_subcommand(zip_cli: &ZipCli, cli: &UnpackCli, cancel_signal: &AtomicBool) -> Result<()> {
|
||||
let (zip_reader, metadata, input_entries) = open_reader(&cli.input)?;
|
||||
|
||||
if !cli.no_output_files {
|
||||
for (path, input_entry) in &input_entries {
|
||||
let entry = zip_reader
|
||||
.get_entry(input_entry.wayfinder)
|
||||
.with_context(|| format!("Failed to open zip entry: {path}"))?;
|
||||
let mut entry_reader = zip::verifying_reader(&entry, input_entry.compression_method)
|
||||
.with_context(|| format!("Failed to open zip entry: {path}"))?;
|
||||
|
||||
let output_path = util::path_join(&cli.output_files, path)?;
|
||||
let output_parent = output_path.parent().expect("No parent path");
|
||||
|
||||
fs::create_dir_all(output_parent)
|
||||
.with_context(|| format!("Failed to create directory: {output_parent:?}"))?;
|
||||
|
||||
let mut output_file = File::create(&output_path)
|
||||
.with_context(|| format!("Failed to open for writing: {output_path:?}"))?;
|
||||
|
||||
stream::copy(&mut entry_reader, &mut output_file, cancel_signal)
|
||||
.with_context(|| format!("Failed to extract zip entry: {path}"))?;
|
||||
}
|
||||
}
|
||||
|
||||
let info = OtaInfo {
|
||||
metadata,
|
||||
files: input_entries.into_keys().collect(),
|
||||
};
|
||||
|
||||
display_info(zip_cli, &info);
|
||||
write_info(&cli.output_info, &info)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pack_subcommand(zip_cli: &ZipCli, cli: &PackCli, cancel_signal: &AtomicBool) -> Result<()> {
|
||||
let (signing_key, cert) = load_key(&cli.key)?;
|
||||
let mut info = read_info(&cli.input_info)?;
|
||||
let mut zip_writer = open_writer(&cli.output, cli.zip_mode.zip_mode)?;
|
||||
|
||||
let mut input_entries = BTreeMap::new();
|
||||
|
||||
for path in &info.files {
|
||||
if is_excluded_path(path) {
|
||||
warn!("Ignoring input file: {path}");
|
||||
continue;
|
||||
}
|
||||
|
||||
let input_path = util::path_join(&cli.input_files, path)?;
|
||||
let input_size = fs::metadata(&input_path)
|
||||
.map(|m| m.len())
|
||||
.with_context(|| format!("Failed to get file size: {input_path:?}"))?;
|
||||
|
||||
input_entries.insert(path, input_size >= 0xffffffff);
|
||||
}
|
||||
|
||||
let mut payload_metadata_size = None;
|
||||
let mut metadata_entries = vec![];
|
||||
|
||||
for (&path, &is_zip64) in &input_entries {
|
||||
let (offset, mut data_writer) =
|
||||
start_entry(&mut zip_writer, path, cli.zip_mode.zip_mode, is_zip64)?;
|
||||
|
||||
let input_path = util::path_join(&cli.input_files, path)?;
|
||||
let mut input_file = File::open(&input_path)
|
||||
.with_context(|| format!("Failed to open for reading: {input_path:?}"))?;
|
||||
|
||||
if path == ota::PATH_PAYLOAD {
|
||||
let header = PayloadHeader::from_reader(&mut input_file)
|
||||
.with_context(|| format!("Failed to read payload header: {input_path:?}"))?;
|
||||
|
||||
payload_metadata_size = Some(header.blob_offset);
|
||||
|
||||
input_file
|
||||
.rewind()
|
||||
.with_context(|| format!("Failed to seek file: {input_path:?}"))?;
|
||||
}
|
||||
|
||||
stream::copy(&mut input_file, &mut data_writer, cancel_signal)
|
||||
.with_context(|| format!("Failed to copy zip entry: {path}"))?;
|
||||
|
||||
finalize_entry(path, offset, data_writer, &mut metadata_entries)?;
|
||||
}
|
||||
|
||||
finalize_ota(
|
||||
&signing_key,
|
||||
&cert,
|
||||
zip_writer,
|
||||
cli.zip_mode.zip_mode,
|
||||
&mut metadata_entries,
|
||||
payload_metadata_size,
|
||||
&mut info.metadata,
|
||||
cancel_signal,
|
||||
)?;
|
||||
|
||||
drop(input_entries);
|
||||
info.files.retain(|p| !is_excluded_path(p));
|
||||
info.files.sort();
|
||||
|
||||
display_info(zip_cli, &info);
|
||||
|
||||
if let Some(path) = &cli.output_info {
|
||||
write_info(path, &info)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn repack_subcommand(zip_cli: &ZipCli, cli: &RepackCli, cancel_signal: &AtomicBool) -> Result<()> {
|
||||
let (signing_key, cert) = load_key(&cli.key)?;
|
||||
let (zip_reader, mut metadata, input_entries) = open_reader(&cli.input)?;
|
||||
let mut zip_writer = open_writer(&cli.output, cli.zip_mode.zip_mode)?;
|
||||
|
||||
let mut payload_metadata_size = None;
|
||||
let mut metadata_entries = vec![];
|
||||
|
||||
for (path, input_entry) in &input_entries {
|
||||
let (offset, mut data_writer) = start_entry(
|
||||
&mut zip_writer,
|
||||
path,
|
||||
cli.zip_mode.zip_mode,
|
||||
input_entry.is_zip64,
|
||||
)?;
|
||||
|
||||
let entry = zip_reader
|
||||
.get_entry(input_entry.wayfinder)
|
||||
.with_context(|| format!("Failed to open zip entry: {path}"))?;
|
||||
|
||||
if path == ota::PATH_PAYLOAD {
|
||||
let entry_reader = zip::verifying_reader(&entry, input_entry.compression_method)
|
||||
.with_context(|| format!("Failed to open zip entry: {path}"))?;
|
||||
|
||||
let header = PayloadHeader::from_reader(entry_reader)
|
||||
.with_context(|| format!("Failed to read payload header: {path:?}"))?;
|
||||
|
||||
payload_metadata_size = Some(header.blob_offset);
|
||||
}
|
||||
|
||||
let mut entry_reader = zip::verifying_reader(&entry, input_entry.compression_method)
|
||||
.with_context(|| format!("Failed to open zip entry: {path}"))?;
|
||||
|
||||
stream::copy(&mut entry_reader, &mut data_writer, cancel_signal)
|
||||
.with_context(|| format!("Failed to copy zip entry: {path}"))?;
|
||||
|
||||
finalize_entry(path, offset, data_writer, &mut metadata_entries)?;
|
||||
}
|
||||
|
||||
finalize_ota(
|
||||
&signing_key,
|
||||
&cert,
|
||||
zip_writer,
|
||||
cli.zip_mode.zip_mode,
|
||||
&mut metadata_entries,
|
||||
payload_metadata_size,
|
||||
&mut metadata,
|
||||
cancel_signal,
|
||||
)?;
|
||||
|
||||
let info = OtaInfo {
|
||||
metadata,
|
||||
files: input_entries.into_keys().collect(),
|
||||
};
|
||||
|
||||
display_info(zip_cli, &info);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn info_subcommand(zip_cli: &ZipCli, cli: &InfoCli) -> Result<()> {
|
||||
let (_, metadata, input_entries) = open_reader(&cli.input)?;
|
||||
let info = OtaInfo {
|
||||
metadata,
|
||||
files: input_entries.into_keys().collect(),
|
||||
};
|
||||
|
||||
display_info(zip_cli, &info);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn zip_main(cli: &ZipCli, cancel_signal: &AtomicBool) -> Result<()> {
|
||||
match &cli.command {
|
||||
ZipCommand::Unpack(c) => unpack_subcommand(cli, c, cancel_signal),
|
||||
ZipCommand::Pack(c) => pack_subcommand(cli, c, cancel_signal),
|
||||
ZipCommand::Repack(c) => repack_subcommand(cli, c, cancel_signal),
|
||||
ZipCommand::Info(c) => info_subcommand(cli, c),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
struct KeyGroup {
|
||||
/// Path to signing key.
|
||||
///
|
||||
/// This should normally be a private key. However, if --signing-helper is
|
||||
/// used, then it should be a public key instead.
|
||||
#[arg(short, long, value_name = "FILE", value_parser)]
|
||||
key: PathBuf,
|
||||
|
||||
/// Certificate for signing key.
|
||||
#[arg(short, long, value_name = "FILE", value_parser)]
|
||||
cert: PathBuf,
|
||||
|
||||
/// Environment variable containing private key passphrase.
|
||||
#[arg(long, value_name = "ENV_VAR", value_parser, group = "pass")]
|
||||
pass_env_var: Option<OsString>,
|
||||
|
||||
/// File containing private key passphrase.
|
||||
#[arg(long, value_name = "FILE", value_parser, group = "pass")]
|
||||
pass_file: Option<PathBuf>,
|
||||
|
||||
/// External program for signing.
|
||||
///
|
||||
/// If this option is specified, then --key must refer to a public key. The
|
||||
/// program will be invoked as:
|
||||
///
|
||||
/// <program> <algo> <public key> [file <pass file>|env <pass env>]
|
||||
#[arg(long, value_name = "PROGRAM", value_parser)]
|
||||
signing_helper: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
struct ZipModeGroup {
|
||||
/// Zip creation mode for the output OTA zip.
|
||||
///
|
||||
/// The streaming mode produces zip files that contain data descriptors.
|
||||
/// The zip file is hashed as it is being written. This mode is the default
|
||||
/// and works with the vast majority of devices.
|
||||
///
|
||||
/// The seekable mode produces zip files that do not use data descriptors.
|
||||
/// The zip file is reread and hashed after it has been fully written. The
|
||||
/// output file is more likely to be compatible with devices that have
|
||||
/// broken zip file parsers.
|
||||
#[arg(
|
||||
long,
|
||||
value_name = "MODE",
|
||||
default_value_t = ZipMode::Streaming,
|
||||
)]
|
||||
zip_mode: ZipMode,
|
||||
}
|
||||
|
||||
/// Unpack an OTA zip.
|
||||
#[derive(Debug, Parser)]
|
||||
struct UnpackCli {
|
||||
/// Path to input OTA zip.
|
||||
#[arg(short, long, value_name = "FILE", value_parser)]
|
||||
input: PathBuf,
|
||||
|
||||
/// Path to output info TOML.
|
||||
#[arg(long, value_name = "FILE", value_parser, default_value = "ota.toml")]
|
||||
output_info: PathBuf,
|
||||
|
||||
/// Path to output files directory.
|
||||
#[arg(long, value_name = "DIR", value_parser, default_value = "ota_files")]
|
||||
output_files: PathBuf,
|
||||
|
||||
/// Do not output files.
|
||||
#[arg(long, conflicts_with = "output_files")]
|
||||
no_output_files: bool,
|
||||
}
|
||||
|
||||
/// Pack an OTA zip.
|
||||
///
|
||||
/// The new OTA zip will *only* contain files listed in the info TOML file.
|
||||
/// Extra files in the input files directory that aren't listed will be silently
|
||||
/// ignored.
|
||||
///
|
||||
/// WARNING: This subcommand is for raw file manipulation and does not perform
|
||||
/// any validation of the contents of the payload binary. For normal usage, use
|
||||
/// `avbroot ota patch` instead.
|
||||
#[derive(Debug, Parser)]
|
||||
struct PackCli {
|
||||
/// Path to output OTA zip.
|
||||
#[arg(short, long, value_name = "FILE", value_parser)]
|
||||
output: PathBuf,
|
||||
|
||||
/// Path to input info TOML.
|
||||
#[arg(long, value_name = "FILE", value_parser, default_value = "ota.toml")]
|
||||
input_info: PathBuf,
|
||||
|
||||
/// Path to output info TOML.
|
||||
///
|
||||
/// If specified, the OTA info containing all recomputed fields will be
|
||||
/// written to this file. This can point to the same file as --input-info.
|
||||
#[arg(long, value_name = "FILE", value_parser)]
|
||||
output_info: Option<PathBuf>,
|
||||
|
||||
/// Path to input files directory.
|
||||
#[arg(long, value_name = "DIR", value_parser, default_value = "ota_files")]
|
||||
input_files: PathBuf,
|
||||
|
||||
#[command(flatten)]
|
||||
key: KeyGroup,
|
||||
|
||||
#[command(flatten)]
|
||||
zip_mode: ZipModeGroup,
|
||||
}
|
||||
|
||||
/// Repack an OTA zip.
|
||||
///
|
||||
/// This command is equivalent to running `unpack` and `pack`, except without
|
||||
/// storing the unpacked data to disk first.
|
||||
#[derive(Debug, Parser)]
|
||||
struct RepackCli {
|
||||
/// Path to input OTA zip.
|
||||
#[arg(short, long, value_name = "FILE", value_parser)]
|
||||
input: PathBuf,
|
||||
|
||||
/// Path to output OTA zip.
|
||||
#[arg(short, long, value_name = "FILE", value_parser)]
|
||||
output: PathBuf,
|
||||
|
||||
#[command(flatten)]
|
||||
key: KeyGroup,
|
||||
|
||||
#[command(flatten)]
|
||||
zip_mode: ZipModeGroup,
|
||||
}
|
||||
|
||||
/// Display OTA zip information.
|
||||
#[derive(Debug, Parser)]
|
||||
struct InfoCli {
|
||||
/// Path to input OTA zip.
|
||||
#[arg(short, long, value_name = "FILE", value_parser)]
|
||||
input: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
enum ZipCommand {
|
||||
Unpack(UnpackCli),
|
||||
Pack(PackCli),
|
||||
Repack(RepackCli),
|
||||
Info(InfoCli),
|
||||
}
|
||||
|
||||
/// Pack, unpack, and inspect OTA zips.
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct ZipCli {
|
||||
#[command(subcommand)]
|
||||
command: ZipCommand,
|
||||
|
||||
/// Don't print OTA metadata information.
|
||||
#[arg(short, long, global = true)]
|
||||
quiet: bool,
|
||||
}
|
||||
@@ -151,11 +151,11 @@ mod tests {
|
||||
contents.extend("💩".as_bytes());
|
||||
|
||||
let data = TestData { contents };
|
||||
let serialized = toml_edit::ser::to_string(&data).unwrap();
|
||||
let serialized = toml::ser::to_string(&data).unwrap();
|
||||
|
||||
assert_eq!(serialized, "contents = 'foo\\xFFbar💩'\n");
|
||||
|
||||
let new_data: TestData = toml_edit::de::from_str(&serialized).unwrap();
|
||||
let new_data: TestData = toml::de::from_str(&serialized).unwrap();
|
||||
assert_eq!(data.contents, new_data.contents);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -775,8 +775,8 @@ pub fn assign_inodes(entries: &mut [CpioEntry], missing_only: bool) -> Result<()
|
||||
// (dev maj, dev min) -> (inode set, last assigned inode)
|
||||
let mut inodes: HashMap<(u32, u32), (HashSet<u32>, u32)> = HashMap::new();
|
||||
|
||||
if missing_only {
|
||||
for entry in &mut *entries {
|
||||
for entry in &mut *entries {
|
||||
if missing_only {
|
||||
if entry.inode != 0 {
|
||||
let key = (entry.dev_maj, entry.dev_min);
|
||||
let (set, last) = inodes.entry(key).or_default();
|
||||
@@ -784,6 +784,8 @@ pub fn assign_inodes(entries: &mut [CpioEntry], missing_only: bool) -> Result<()
|
||||
set.insert(entry.inode);
|
||||
*last = (*last).max(entry.inode);
|
||||
}
|
||||
} else {
|
||||
entry.inode = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -835,3 +837,56 @@ pub fn save(
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_assign_inodes_missing() {
|
||||
let mut entries = [
|
||||
CpioEntry::new_directory(b"a", 0o755),
|
||||
CpioEntry::new_directory(b"b", 0o755),
|
||||
CpioEntry::new_directory(b"c", 0o755),
|
||||
CpioEntry::new_directory(b"d", 0o755),
|
||||
CpioEntry::new_directory(b"e", 0o755),
|
||||
];
|
||||
entries[0].inode = 999;
|
||||
entries[2].inode = 500;
|
||||
entries[3].dev_maj = 1;
|
||||
entries[3].dev_min = 1;
|
||||
|
||||
assign_inodes(&mut entries, true).unwrap();
|
||||
|
||||
assert_eq!(entries[0].inode, 999);
|
||||
assert_eq!(entries[1].inode, 1000);
|
||||
assert_eq!(entries[2].inode, 500);
|
||||
assert_eq!(entries[3].inode, 300000);
|
||||
assert_eq!(entries[4].inode, 1001);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_assign_inodes_reassign() {
|
||||
let mut entries = [
|
||||
CpioEntry::new_directory(b"a", 0o755),
|
||||
CpioEntry::new_directory(b"b", 0o755),
|
||||
CpioEntry::new_directory(b"c", 0o755),
|
||||
CpioEntry::new_directory(b"d", 0o755),
|
||||
CpioEntry::new_directory(b"e", 0o755),
|
||||
];
|
||||
entries[0].dev_maj = 1;
|
||||
entries[0].dev_min = 1;
|
||||
entries[0].inode = 1000;
|
||||
entries[1].inode = 1000;
|
||||
entries[2].inode = 500;
|
||||
entries[3].dev_maj = 1;
|
||||
entries[3].dev_min = 1;
|
||||
|
||||
assign_inodes(&mut entries, false).unwrap();
|
||||
|
||||
assert_eq!(entries[0].inode, 300000);
|
||||
assert_eq!(entries[1].inode, 300000);
|
||||
assert_eq!(entries[2].inode, 300001);
|
||||
assert_eq!(entries[3].inode, 300001);
|
||||
}
|
||||
}
|
||||
|
||||
+16
-34
@@ -54,7 +54,7 @@ const PARTITION_RESERVED_BYTES: u32 = 4096;
|
||||
|
||||
/// Maximum allowed size of [`RawGeometry::metadata_max_size`] to prevent the
|
||||
/// memory usage from blowing up.
|
||||
const METADATA_MAX_SIZE: u32 = 128 * 1024;
|
||||
const METADATA_MAX_SIZE: u32 = 512 * 1024;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
@@ -118,8 +118,6 @@ pub enum Error {
|
||||
},
|
||||
#[error("Partition {name:?}: Extent indices too large")]
|
||||
PartitionExtentIndicesTooLarge { name: DebugString },
|
||||
#[error("Partition {name:?}: Extent indices set on empty image")]
|
||||
PartitionExtentIndicesEmptyImage { name: DebugString },
|
||||
#[error("Partition {name:?}: Extent index too large")]
|
||||
PartitionExtentIndexTooLarge { name: DebugString },
|
||||
#[error("Partition {name:?}: Extent count too large")]
|
||||
@@ -674,7 +672,6 @@ impl RawPartition {
|
||||
/// [`RawPartitionGroup::validate`] do not.
|
||||
fn validate(
|
||||
&self,
|
||||
image_type: ImageType,
|
||||
header: &RawHeader,
|
||||
extents: &[RawExtent],
|
||||
groups: &[RawPartitionGroup],
|
||||
@@ -695,26 +692,15 @@ impl RawPartition {
|
||||
});
|
||||
}
|
||||
|
||||
match image_type {
|
||||
ImageType::Normal => {
|
||||
if self
|
||||
.first_extent_index
|
||||
.get()
|
||||
.checked_add(self.num_extents.get())
|
||||
.is_none_or(|n| n as usize > extents.len())
|
||||
{
|
||||
return Err(Error::PartitionExtentIndicesTooLarge {
|
||||
name: DebugString::new(self.name),
|
||||
});
|
||||
}
|
||||
}
|
||||
ImageType::Empty => {
|
||||
if self.first_extent_index.get() != 0 || self.num_extents.get() != 0 {
|
||||
return Err(Error::PartitionExtentIndicesEmptyImage {
|
||||
name: DebugString::new(self.name),
|
||||
});
|
||||
}
|
||||
}
|
||||
if self
|
||||
.first_extent_index
|
||||
.get()
|
||||
.checked_add(self.num_extents.get())
|
||||
.is_none_or(|n| n as usize > extents.len())
|
||||
{
|
||||
return Err(Error::PartitionExtentIndicesTooLarge {
|
||||
name: DebugString::new(self.name),
|
||||
});
|
||||
}
|
||||
|
||||
if self.group_index.get() as usize >= groups.len() {
|
||||
@@ -971,7 +957,7 @@ struct RawMetadataSlot {
|
||||
impl RawMetadataSlot {
|
||||
/// Ensure that all fields are semantically valid and can be used without
|
||||
/// further checks.
|
||||
fn validate(&self, image_type: ImageType, geometry: &RawGeometry) -> Result<()> {
|
||||
fn validate(&self, geometry: &RawGeometry) -> Result<()> {
|
||||
self.header.validate(geometry)?;
|
||||
|
||||
for (len, descriptor, name) in [
|
||||
@@ -1032,7 +1018,7 @@ impl RawMetadataSlot {
|
||||
}
|
||||
|
||||
for partition in &self.partitions {
|
||||
partition.validate(image_type, &self.header, &self.extents, &self.groups)?;
|
||||
partition.validate(&self.header, &self.extents, &self.groups)?;
|
||||
}
|
||||
|
||||
for (i, group) in self.groups.iter().enumerate() {
|
||||
@@ -1111,11 +1097,7 @@ impl RawMetadata {
|
||||
/// Read a single [`RawMetadataSlot`], including the header and all tables.
|
||||
/// This does not read the extra padding between the end of the last table
|
||||
/// and [`RawGeometry::metadata_max_size`].
|
||||
fn read_metadata(
|
||||
mut reader: impl Read,
|
||||
image_type: ImageType,
|
||||
geometry: &RawGeometry,
|
||||
) -> Result<RawMetadataSlot> {
|
||||
fn read_metadata(mut reader: impl Read, geometry: &RawGeometry) -> Result<RawMetadataSlot> {
|
||||
let mut header = RawHeader::new_zeroed();
|
||||
|
||||
reader
|
||||
@@ -1155,7 +1137,7 @@ impl RawMetadata {
|
||||
block_devices: block_devices.to_vec(),
|
||||
};
|
||||
|
||||
slot.validate(image_type, geometry)?;
|
||||
slot.validate(geometry)?;
|
||||
|
||||
Ok(slot)
|
||||
}
|
||||
@@ -1194,7 +1176,7 @@ impl RawMetadata {
|
||||
}
|
||||
}
|
||||
|
||||
slot.validate(self.image_type, &self.geometry)?;
|
||||
slot.validate(&self.geometry)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -1229,7 +1211,7 @@ impl<R: Read> FromReader<R> for RawMetadata {
|
||||
.stream_position()
|
||||
.map_err(|e| Error::DataRead("orig_offset", e))?;
|
||||
|
||||
match Self::read_metadata(&mut reader, image_type, &geometry) {
|
||||
match Self::read_metadata(&mut reader, &geometry) {
|
||||
Ok(m) => *slot = Some(m),
|
||||
Err(e @ Error::DataRead(_, _)) => return Err(e),
|
||||
Err(e) => last_err = Some(e),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// SPDX-FileCopyrightText: 2023 Andrew Gunnerson
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
//! Hack to format an integer as an octal string because toml_edit can't output
|
||||
//! Hack to format an integer as an octal string because toml can't output
|
||||
//! octal-formatted integers and many other toml parsers can't parse it either.
|
||||
|
||||
use std::{
|
||||
|
||||
+102
-35
@@ -139,10 +139,13 @@ fn load_ramdisk(
|
||||
}
|
||||
|
||||
fn save_ramdisk(
|
||||
entries: &[CpioEntry],
|
||||
entries: &mut [CpioEntry],
|
||||
format: CompressedFormat,
|
||||
cancel_signal: &AtomicBool,
|
||||
) -> Result<Vec<u8>> {
|
||||
cpio::sort(entries);
|
||||
cpio::assign_inodes(entries, false).map_err(Error::RamdiskSaveCpio)?;
|
||||
|
||||
let raw_writer = Cursor::new(vec![]);
|
||||
let mut writer =
|
||||
CompressedWriter::new(raw_writer, format).map_err(Error::RamdiskSaveCompression)?;
|
||||
@@ -156,6 +159,12 @@ fn save_ramdisk(
|
||||
Ok(raw_writer.into_inner())
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub struct BootLayoutInfo {
|
||||
pub has_recovery: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct BootImageInfo {
|
||||
pub header: Header,
|
||||
pub footer: Footer,
|
||||
@@ -171,11 +180,17 @@ pub trait BootImagePatch {
|
||||
/// can be inspected, but during patching, only the boot image is available.
|
||||
fn find_targets<'a>(
|
||||
&self,
|
||||
layout: BootLayoutInfo,
|
||||
boot_images: &HashMap<&'a str, BootImageInfo>,
|
||||
cancel_signal: &AtomicBool,
|
||||
) -> TargetsResult<Vec<&'a str>>;
|
||||
|
||||
fn patch(&self, boot_image: &mut BootImage, cancel_signal: &AtomicBool) -> Result<()>;
|
||||
fn patch(
|
||||
&self,
|
||||
layout: BootLayoutInfo,
|
||||
boot_image: &mut BootImage,
|
||||
cancel_signal: &AtomicBool,
|
||||
) -> Result<()>;
|
||||
}
|
||||
|
||||
/// Root a boot image with Magisk.
|
||||
@@ -437,6 +452,7 @@ impl BootImagePatch for MagiskRootPatcher {
|
||||
|
||||
fn find_targets<'a>(
|
||||
&self,
|
||||
_layout: BootLayoutInfo,
|
||||
boot_images: &HashMap<&'a str, BootImageInfo>,
|
||||
_cancel_signal: &AtomicBool,
|
||||
) -> TargetsResult<Vec<&'a str>> {
|
||||
@@ -451,7 +467,12 @@ impl BootImagePatch for MagiskRootPatcher {
|
||||
Ok(targets)
|
||||
}
|
||||
|
||||
fn patch(&self, boot_image: &mut BootImage, cancel_signal: &AtomicBool) -> Result<()> {
|
||||
fn patch(
|
||||
&self,
|
||||
_layout: BootLayoutInfo,
|
||||
boot_image: &mut BootImage,
|
||||
cancel_signal: &AtomicBool,
|
||||
) -> Result<()> {
|
||||
// Load the first ramdisk. If it doesn't exist, we have to generate one
|
||||
// from scratch.
|
||||
let ramdisk = match boot_image {
|
||||
@@ -619,9 +640,7 @@ impl BootImagePatch for MagiskRootPatcher {
|
||||
));
|
||||
|
||||
// Repack ramdisk.
|
||||
cpio::sort(&mut entries);
|
||||
cpio::assign_inodes(&mut entries, false).map_err(Error::RamdiskSaveCpio)?;
|
||||
let new_ramdisk = save_ramdisk(&entries, ramdisk_format, cancel_signal)?;
|
||||
let new_ramdisk = save_ramdisk(&mut entries, ramdisk_format, cancel_signal)?;
|
||||
|
||||
match boot_image {
|
||||
BootImage::V0Through2(b) => b.ramdisk = new_ramdisk,
|
||||
@@ -732,7 +751,7 @@ impl OtaCertPatcher {
|
||||
entry.data = CpioEntryData::Data(zip.to_vec());
|
||||
|
||||
// Repack ramdisk.
|
||||
*ramdisk = save_ramdisk(&entries, ramdisk_format, cancel_signal)?;
|
||||
*ramdisk = save_ramdisk(&mut entries, ramdisk_format, cancel_signal)?;
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
@@ -745,6 +764,7 @@ impl BootImagePatch for OtaCertPatcher {
|
||||
|
||||
fn find_targets<'a>(
|
||||
&self,
|
||||
_layout: BootLayoutInfo,
|
||||
boot_images: &HashMap<&'a str, BootImageInfo>,
|
||||
cancel_signal: &AtomicBool,
|
||||
) -> TargetsResult<Vec<&'a str>> {
|
||||
@@ -777,7 +797,12 @@ impl BootImagePatch for OtaCertPatcher {
|
||||
Ok(targets)
|
||||
}
|
||||
|
||||
fn patch(&self, boot_image: &mut BootImage, cancel_signal: &AtomicBool) -> Result<()> {
|
||||
fn patch(
|
||||
&self,
|
||||
_layout: BootLayoutInfo,
|
||||
boot_image: &mut BootImage,
|
||||
cancel_signal: &AtomicBool,
|
||||
) -> Result<()> {
|
||||
let ramdisks = match boot_image {
|
||||
BootImage::V0Through2(b) => slice::from_mut(&mut b.ramdisk),
|
||||
BootImage::V3Through4(b) => slice::from_mut(&mut b.ramdisk),
|
||||
@@ -816,34 +841,49 @@ impl DsuPubKeyPatcher {
|
||||
const FIRST_STAGE_PATH: &'static [u8] = b"first_stage_ramdisk";
|
||||
const DSU_KEYS_PATH: &'static [u8] = b"first_stage_ramdisk/avb";
|
||||
const AVBROOT_KEY_PATH: &'static [u8] = b"first_stage_ramdisk/avb/avbroot.avbpubkey";
|
||||
const RECOVERY_DSU_KEYS_PATH: &'static [u8] = b"avb";
|
||||
const RECOVERY_AVBROOT_KEY_PATH: &'static [u8] = b"avb/avbroot.avbpubkey";
|
||||
|
||||
pub fn new(key: RsaPublicKey) -> Self {
|
||||
Self { key }
|
||||
}
|
||||
|
||||
fn patch_ramdisk(&self, ramdisk: &mut Vec<u8>, cancel_signal: &AtomicBool) -> Result<bool> {
|
||||
fn patch_ramdisk(
|
||||
&self,
|
||||
layout: BootLayoutInfo,
|
||||
ramdisk: &mut Vec<u8>,
|
||||
cancel_signal: &AtomicBool,
|
||||
) -> Result<bool> {
|
||||
let (mut entries, ramdisk_format) = load_ramdisk(ramdisk, cancel_signal)?;
|
||||
if !entries.iter_mut().any(|e| e.path == Self::FIRST_STAGE_PATH) {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
if !entries.iter().any(|e| e.path == Self::DSU_KEYS_PATH) {
|
||||
entries.push(CpioEntry::new_directory(Self::DSU_KEYS_PATH, 0o755));
|
||||
let (dsu_keys_path, avbroot_key_path) = if layout.has_recovery {
|
||||
(
|
||||
Self::RECOVERY_DSU_KEYS_PATH,
|
||||
Self::RECOVERY_AVBROOT_KEY_PATH,
|
||||
)
|
||||
} else {
|
||||
(Self::DSU_KEYS_PATH, Self::AVBROOT_KEY_PATH)
|
||||
};
|
||||
|
||||
debug!("DSU key path: {:?}", avbroot_key_path.as_bstr());
|
||||
|
||||
if !entries.iter().any(|e| e.path == dsu_keys_path) {
|
||||
entries.push(CpioEntry::new_directory(dsu_keys_path, 0o755));
|
||||
}
|
||||
|
||||
let binary_key = avb::encode_public_key(&self.key).map_err(Error::AvbEncodeKey)?;
|
||||
let data = CpioEntryData::Data(binary_key);
|
||||
|
||||
if let Some(e) = entries
|
||||
.iter_mut()
|
||||
.find(|e| e.path == Self::AVBROOT_KEY_PATH)
|
||||
{
|
||||
if let Some(e) = entries.iter_mut().find(|e| e.path == avbroot_key_path) {
|
||||
e.data = data;
|
||||
} else {
|
||||
entries.push(CpioEntry::new_file(Self::AVBROOT_KEY_PATH, 0o644, data));
|
||||
entries.push(CpioEntry::new_file(avbroot_key_path, 0o644, data));
|
||||
}
|
||||
|
||||
*ramdisk = save_ramdisk(&entries, ramdisk_format, cancel_signal)?;
|
||||
*ramdisk = save_ramdisk(&mut entries, ramdisk_format, cancel_signal)?;
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
@@ -856,6 +896,7 @@ impl BootImagePatch for DsuPubKeyPatcher {
|
||||
|
||||
fn find_targets<'a>(
|
||||
&self,
|
||||
layout: BootLayoutInfo,
|
||||
boot_images: &HashMap<&'a str, BootImageInfo>,
|
||||
cancel_signal: &AtomicBool,
|
||||
) -> TargetsResult<Vec<&'a str>> {
|
||||
@@ -869,6 +910,12 @@ impl BootImagePatch for DsuPubKeyPatcher {
|
||||
BootImage::VendorV3Through4(b) => &b.ramdisks,
|
||||
};
|
||||
|
||||
let dsu_keys_path = if layout.has_recovery {
|
||||
Self::RECOVERY_DSU_KEYS_PATH
|
||||
} else {
|
||||
Self::DSU_KEYS_PATH
|
||||
};
|
||||
|
||||
for ramdisk in ramdisks {
|
||||
if ramdisk.is_empty() {
|
||||
continue;
|
||||
@@ -879,10 +926,12 @@ impl BootImagePatch for DsuPubKeyPatcher {
|
||||
let mut found = false;
|
||||
|
||||
for entry in entries {
|
||||
if entry.path == Self::DSU_KEYS_PATH {
|
||||
if entry.path == dsu_keys_path {
|
||||
dsu_keys_targets.push(name);
|
||||
found = true;
|
||||
} else if entry.path == Self::FIRST_STAGE_PATH {
|
||||
// This exists even on devices with recovery partitions.
|
||||
// init just doesn't switch root to it.
|
||||
first_stage_targets.push(name);
|
||||
found = true;
|
||||
}
|
||||
@@ -916,7 +965,12 @@ impl BootImagePatch for DsuPubKeyPatcher {
|
||||
}
|
||||
}
|
||||
|
||||
fn patch(&self, boot_image: &mut BootImage, cancel_signal: &AtomicBool) -> Result<()> {
|
||||
fn patch(
|
||||
&self,
|
||||
layout: BootLayoutInfo,
|
||||
boot_image: &mut BootImage,
|
||||
cancel_signal: &AtomicBool,
|
||||
) -> Result<()> {
|
||||
let ramdisks = match boot_image {
|
||||
BootImage::V0Through2(b) => slice::from_mut(&mut b.ramdisk),
|
||||
BootImage::V3Through4(b) => slice::from_mut(&mut b.ramdisk),
|
||||
@@ -928,7 +982,7 @@ impl BootImagePatch for DsuPubKeyPatcher {
|
||||
continue;
|
||||
}
|
||||
|
||||
if self.patch_ramdisk(ramdisk, cancel_signal)? {
|
||||
if self.patch_ramdisk(layout, ramdisk, cancel_signal)? {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
@@ -1013,6 +1067,7 @@ impl BootImagePatch for PrepatchedImagePatcher {
|
||||
|
||||
fn find_targets<'a>(
|
||||
&self,
|
||||
_layout: BootLayoutInfo,
|
||||
boot_images: &HashMap<&'a str, BootImageInfo>,
|
||||
_cancel_signal: &AtomicBool,
|
||||
) -> TargetsResult<Vec<&'a str>> {
|
||||
@@ -1037,7 +1092,12 @@ impl BootImagePatch for PrepatchedImagePatcher {
|
||||
Ok(targets)
|
||||
}
|
||||
|
||||
fn patch(&self, boot_image: &mut BootImage, _cancel_signal: &AtomicBool) -> Result<()> {
|
||||
fn patch(
|
||||
&self,
|
||||
_layout: BootLayoutInfo,
|
||||
boot_image: &mut BootImage,
|
||||
_cancel_signal: &AtomicBool,
|
||||
) -> Result<()> {
|
||||
let prepatched_image = self.load_prepatched_image()?;
|
||||
|
||||
// Level 0: Warnings that don't affect booting
|
||||
@@ -1286,10 +1346,10 @@ pub trait BootImageOpener {
|
||||
pub fn load_boot_images<'a>(
|
||||
names: &[&'a str],
|
||||
opener: &(dyn BootImageOpener + Sync),
|
||||
) -> TargetsResult<HashMap<&'a str, BootImageInfo>> {
|
||||
) -> TargetsResult<(HashMap<&'a str, BootImageInfo>, BootLayoutInfo)> {
|
||||
let parent_span = Span::current();
|
||||
|
||||
names
|
||||
let images = names
|
||||
.par_iter()
|
||||
.map(|&name| {
|
||||
let _span = debug_span!(parent: &parent_span, "image", name).entered();
|
||||
@@ -1302,7 +1362,13 @@ pub fn load_boot_images<'a>(
|
||||
|
||||
Ok((name, info))
|
||||
})
|
||||
.collect()
|
||||
.collect::<TargetsResult<HashMap<_, _>>>()?;
|
||||
|
||||
let layout = BootLayoutInfo {
|
||||
has_recovery: images.contains_key("recovery"),
|
||||
};
|
||||
|
||||
Ok((images, layout))
|
||||
}
|
||||
|
||||
/// Apply applicable patches to the list of specified boot images. For each
|
||||
@@ -1325,7 +1391,7 @@ pub fn patch_boot_images<'a>(
|
||||
}
|
||||
|
||||
// Preparse all images. Some patchers need to inspect every candidate.
|
||||
let mut images = load_boot_images(names, opener)?;
|
||||
let (mut images, layout) = load_boot_images(names, opener)?;
|
||||
|
||||
// Find the targets that each patcher wants to patch.
|
||||
let all_targets = patchers
|
||||
@@ -1333,14 +1399,15 @@ pub fn patch_boot_images<'a>(
|
||||
.map(|p| {
|
||||
let _span =
|
||||
debug_span!(parent: &parent_span, "patcher", name = p.patcher_name()).entered();
|
||||
p.find_targets(&images, cancel_signal).and_then(|targets| {
|
||||
if targets.is_empty() {
|
||||
Err(TargetsError::NoTargets(p.patcher_name()))
|
||||
} else {
|
||||
debug!("Found patcher targets: {targets:?}");
|
||||
Ok(targets)
|
||||
}
|
||||
})
|
||||
p.find_targets(layout, &images, cancel_signal)
|
||||
.and_then(|targets| {
|
||||
if targets.is_empty() {
|
||||
Err(TargetsError::NoTargets(p.patcher_name()))
|
||||
} else {
|
||||
debug!("Found patcher targets: {targets:?}");
|
||||
Ok(targets)
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect::<TargetsResult<Vec<_>>>()?;
|
||||
|
||||
@@ -1367,8 +1434,8 @@ pub fn patch_boot_images<'a>(
|
||||
.try_for_each(|(&name, (info, patchers))| -> TargetsResult<()> {
|
||||
patchers.iter().try_for_each(|p| {
|
||||
let _span =
|
||||
debug_span!(parent: &parent_span, "patcher", name = p.patcher_name()).entered();
|
||||
p.patch(&mut info.boot_image, cancel_signal)
|
||||
debug_span!(parent: &parent_span, "patcher", name = p.patcher_name(), image = name).entered();
|
||||
p.patch(layout, &mut info.boot_image, cancel_signal)
|
||||
.map_err(|e| TargetsError::Patch(name.to_owned(), e))
|
||||
})
|
||||
})?;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: 2026 Andrew Gunnerson
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
#![allow(clippy::all)]
|
||||
#![allow(clippy::nursery)]
|
||||
#![allow(clippy::pedantic)]
|
||||
@@ -13,3 +16,82 @@ pub mod build {
|
||||
pub mod chromeos_update_engine {
|
||||
include!(concat!(env!("OUT_DIR"), "/chromeos_update_engine.rs"));
|
||||
}
|
||||
|
||||
/// Implement [`serde::Serialize`] and [`serde::Deserialize`] for prost enum
|
||||
/// type and create a bridge to use with `#[serde(with = "...")]` since enum
|
||||
/// fields are stored as their underlying repr. `$type` must be a fully
|
||||
/// qualified type.
|
||||
macro_rules! serde_derive_prost_enum {
|
||||
($type:ty, $module:ident $(,)?) => {
|
||||
impl serde::Serialize for $type {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
self.as_str_name().serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde::Deserialize<'de> for $type {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
struct EnumVisitor;
|
||||
|
||||
impl serde::de::Visitor<'_> for EnumVisitor {
|
||||
type Value = $type;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(formatter, "a string containing a valid enum variant")
|
||||
}
|
||||
|
||||
fn visit_str<R>(self, v: &str) -> Result<Self::Value, R>
|
||||
where
|
||||
R: serde::de::Error,
|
||||
{
|
||||
match <$type>::from_str_name(v) {
|
||||
Some(e) => Ok(e),
|
||||
None => Err(serde::de::Error::custom(format!(
|
||||
"invalid enum variant: {v}",
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_any(EnumVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
mod $module {
|
||||
pub fn serialize<S, T>(data: &T, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
T: Copy + std::fmt::Display,
|
||||
$type: TryFrom<T>,
|
||||
<$type as TryFrom<T>>::Error: std::fmt::Display,
|
||||
{
|
||||
let value = <$type>::try_from(*data).map_err(|e| {
|
||||
serde::ser::Error::custom(format!("invalid enum value: {data}: {e}"))
|
||||
})?;
|
||||
|
||||
serde::Serialize::serialize(&value, serializer)
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
T: From<$type>,
|
||||
{
|
||||
let value: $type = serde::Deserialize::deserialize(deserializer)?;
|
||||
|
||||
Ok(value.into())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
serde_derive_prost_enum!(
|
||||
crate::protobuf::build::tools::releasetools::ota_metadata::OtaType,
|
||||
ota_type,
|
||||
);
|
||||
|
||||
@@ -54,8 +54,11 @@ executables = "deny"
|
||||
include-dependencies = true
|
||||
include-workspace = true
|
||||
bypass = [
|
||||
# Copies of unmodified crashwrangler objects for old macOS versions.
|
||||
{ name = "honggfuzz", allow-globs = ["honggfuzz/third_party/mac/CrashReport_*.o"] },
|
||||
# honggfuzz/third_party/mac/CrashReport_*.o are copies of unmodified
|
||||
# crashwrangler objects for old macOS versions. Unfortunately, when running
|
||||
# cargo hfuzz, the compiled hongfuzz binary and corresponding object files
|
||||
# are written to the source directory too.
|
||||
{ name = "honggfuzz", allow-globs = ["honggfuzz/*.o", "honggfuzz/honggfuzz"] },
|
||||
# Only used in tests.
|
||||
{ name = "libloading", allow-globs = ["tests/nagisa*.dll"] },
|
||||
]
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ ring = "0.17.14"
|
||||
rsa = { version = "0.9.6", features = ["hazmat"] }
|
||||
serde = { version = "1.0.188", features = ["derive"] }
|
||||
tempfile = "3.8.0"
|
||||
toml_edit = { version = "0.23.3", features = ["serde"] }
|
||||
toml = { version = "1.0.1", features = ["serde"] }
|
||||
topological-sort = "0.2.2"
|
||||
tracing = "0.1.40"
|
||||
tracing-subscriber = "0.3.18"
|
||||
|
||||
+8
-8
@@ -53,11 +53,11 @@ data.ramdisks = [["otacerts", "first_stage", "dsu_key_dir"]]
|
||||
|
||||
[profile.pixel_v4_gki.hashes_streaming]
|
||||
original = "ea96196191e3a4133db4aff45d47aa3468514e29e0a724faac8081cbf4adf808"
|
||||
patched = "357a448d1a7505b2308ce1c2d19b063e606c60e7349784913a48cdc3f6d50aa4"
|
||||
patched = "179923431ff94148186d8b0a457636c8a60057d047d55a4fc4da3a162e73eeed"
|
||||
|
||||
[profile.pixel_v4_gki.hashes_seekable]
|
||||
original = "f6615ae355eba38689d24aa535981d09175d4832e7c65c04cdd89aa95d21f09d"
|
||||
patched = "9195ba963d9897af2f0821ff6051c1efe3fe130055b7936bedf2cd189998fd89"
|
||||
patched = "b006858b78cddef0e3db1d77454cd945111ce15708121e3a483ff711abedec8b"
|
||||
|
||||
# Google Pixel 6a
|
||||
# What's unique: boot (boot v4, no ramdisk) + vendor_boot (vendor v4, 2 ramdisks)
|
||||
@@ -97,11 +97,11 @@ data.ramdisks = [["init", "otacerts", "first_stage", "dsu_key_dir"], ["dlkm"]]
|
||||
|
||||
[profile.pixel_v4_non_gki.hashes_streaming]
|
||||
original = "cb2a2e406d2b4c68c8a38f819256a10ada0ed5a4732822bec35bdf7bcdc458eb"
|
||||
patched = "12f15d29fceeb18af14c9a805de3aedb7f270fe8ec316d9d0fe37fb0c667eacd"
|
||||
patched = "45cbe96e01b8b2e3ba5e4a2c990e7ff08bee285316a4a7a314aab3ffddf27d0d"
|
||||
|
||||
[profile.pixel_v4_non_gki.hashes_seekable]
|
||||
original = "9450c212c34fe55453b52345c7cc22f72397dc80d9ddc792eafc20b461c2afc9"
|
||||
patched = "5018c579df9608f9dfe3add2afd8b176de61ba267376cf3a3e422ecc9d3dd112"
|
||||
patched = "111fdfae91977d80cd141f6c3dc69f3af45bb7fcdf3e2e442bc3f0ad5fa77faa"
|
||||
|
||||
# Google Pixel 4a 5G
|
||||
# What's unique: boot (boot v3) + vendor_boot (vendor v3)
|
||||
@@ -141,11 +141,11 @@ data.ramdisks = [["otacerts", "first_stage", "dsu_key_dir"]]
|
||||
|
||||
[profile.pixel_v3.hashes_streaming]
|
||||
original = "4b864b906a13ec98b1d6c3440a5eca7404ec63d073080e80a3b84afabcbd2fa2"
|
||||
patched = "a9077fe32dee8eb7a0369c1334d8f3378ae8044814ae04da700f33294b12a4d8"
|
||||
patched = "da3b9a4d6a58bb5aaadfc63006cb36b7af9c1c5945e81b38587c245bd5ad1f5a"
|
||||
|
||||
[profile.pixel_v3.hashes_seekable]
|
||||
original = "e0e93ee11de56992f3a5f543858c1d11858b4deb76a91a4caf4fb0f3f34be855"
|
||||
patched = "8730b398367179a0be092a67e4714f09c2267c656fe3e42ec1f8b43bb3a28634"
|
||||
patched = "71682a4f31a5920784a0e2e38a6ee5fe3c65213436c7bf1ce347b121cd01a367"
|
||||
|
||||
# Google Pixel 4a
|
||||
# What's unique: boot (boot v2)
|
||||
@@ -174,8 +174,8 @@ data.deps = ["system"]
|
||||
|
||||
[profile.pixel_v2.hashes_streaming]
|
||||
original = "abc0ad7f80020018101437c2494d6564a877df663d208a123267fc100734c175"
|
||||
patched = "b1876455f5be9d5b6eafc598a017ad10e5e76339137f8b29777097ab19cdc3b0"
|
||||
patched = "132fb4d90e3c19a7a5ee0da2082767228255228ea5fb66811df1ccd5ff600bb5"
|
||||
|
||||
[profile.pixel_v2.hashes_seekable]
|
||||
original = "57d4ac5ab7d362a593f3c02f3d2044b5400c10db0f732741a2d18e025d4231ec"
|
||||
patched = "40d7674be14e19747e7ead118e0f4faf50880c638dfb4bfc0d0154b0e9202d3f"
|
||||
patched = "b365eb32f463ac4ba13f7bed4e6535a68483379640dc6e842c591f194db042c2"
|
||||
|
||||
+2
-7
@@ -6,7 +6,6 @@ use std::{collections::BTreeMap, fs, path::Path};
|
||||
use anyhow::{Context, Result};
|
||||
use avbroot::format::payload::{CowVersion, VabcAlgo};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use toml_edit::DocumentMut;
|
||||
|
||||
#[derive(Clone, Copy, Serialize, Deserialize)]
|
||||
pub struct Sha256Hash(
|
||||
@@ -130,12 +129,8 @@ pub struct Config {
|
||||
pub profile: BTreeMap<String, Profile>,
|
||||
}
|
||||
|
||||
pub fn load_config(path: &Path) -> Result<(Config, DocumentMut)> {
|
||||
pub fn load_config(path: &Path) -> Result<Config> {
|
||||
let contents =
|
||||
fs::read_to_string(path).with_context(|| format!("Failed to read config: {path:?}"))?;
|
||||
let config: Config = toml_edit::de::from_str(&contents)
|
||||
.with_context(|| format!("Failed to parse config: {path:?}"))?;
|
||||
let document: DocumentMut = contents.parse().unwrap();
|
||||
|
||||
Ok((config, document))
|
||||
toml::de::from_str(&contents).with_context(|| format!("Failed to parse config: {path:?}"))
|
||||
}
|
||||
|
||||
+6
-6
@@ -68,11 +68,11 @@ use crate::{
|
||||
fn hash_file(path: &Path, cancel_signal: &AtomicBool) -> Result<[u8; 32]> {
|
||||
info!("Calculating hash: {path:?}");
|
||||
|
||||
let raw_reader =
|
||||
File::open(path).with_context(|| format!("Failed to open for reading: {path:?}"))?;
|
||||
let buf_reader = BufReader::new(raw_reader);
|
||||
let raw_reader = File::open(path)
|
||||
.map(BufReader::new)
|
||||
.with_context(|| format!("Failed to open for reading: {path:?}"))?;
|
||||
let context = ring::digest::Context::new(&ring::digest::SHA256);
|
||||
let mut hashing_reader = HashingReader::new(buf_reader, context);
|
||||
let mut hashing_reader = HashingReader::new(raw_reader, context);
|
||||
|
||||
stream::copy(&mut hashing_reader, io::sink(), cancel_signal)?;
|
||||
|
||||
@@ -1203,7 +1203,7 @@ fn filter_profiles<'a>(config: &'a Config, cli: &'a ProfileGroup) -> Result<BTre
|
||||
}
|
||||
|
||||
fn test_subcommand(cli: &TestCli, cancel_signal: &AtomicBool) -> Result<()> {
|
||||
let (config, _) = config::load_config(&cli.config.config)?;
|
||||
let config = config::load_config(&cli.config.config)?;
|
||||
let profiles = filter_profiles(&config, &cli.profile)?;
|
||||
|
||||
if profiles.is_empty() {
|
||||
@@ -1336,7 +1336,7 @@ fn test_subcommand(cli: &TestCli, cancel_signal: &AtomicBool) -> Result<()> {
|
||||
}
|
||||
|
||||
fn list_subcommand(cli: &ListCli) -> Result<()> {
|
||||
let (config, _) = config::load_config(&cli.config.config)?;
|
||||
let config = config::load_config(&cli.config.config)?;
|
||||
|
||||
for profile in config.profile.keys() {
|
||||
println!("{profile}");
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/hfuzz_target/
|
||||
/hfuzz_workspace/
|
||||
HONGGFUZZ.REPORT.TXT
|
||||
*.honggfuzz.cov
|
||||
*.fuzz
|
||||
|
||||
+9
-3
@@ -10,7 +10,13 @@ While avbroot's parsers are all memory-safe, it is still possible for panics or
|
||||
cargo install honggfuzz
|
||||
```
|
||||
|
||||
2. Pick a fuzz target to run. A fuzz target is the name of the source file in [`src/bin/`](./src/bin) without the `.rs` extension.
|
||||
2. [Optional] Generate sample files to use as the initial fuzzing corpus.
|
||||
|
||||
```bash
|
||||
cargo xtask fuzz-corpus
|
||||
```
|
||||
|
||||
3. Pick a fuzz target to run. A fuzz target is the name of the source file in [`src/bin/`](./src/bin) without the `.rs` extension.
|
||||
|
||||
The list of targets can be queried programmatically with:
|
||||
|
||||
@@ -18,7 +24,7 @@ While avbroot's parsers are all memory-safe, it is still possible for panics or
|
||||
cargo read-manifest | jq -r '.targets[].name'
|
||||
```
|
||||
|
||||
3. Run the fuzzer.
|
||||
4. Run the fuzzer.
|
||||
|
||||
```bash
|
||||
cargo hfuzz run <fuzz target>
|
||||
@@ -41,7 +47,7 @@ While avbroot's parsers are all memory-safe, it is still possible for panics or
|
||||
|
||||
When a crash occurs, the `Crashes` counter will increment and the input data that triggered the crash will be written to `hfuzz_workspace/<fuzz target>/*.fuzz`. New files are only written for unique crashes.
|
||||
|
||||
4. If a crash occurs, run the following command to trigger the crash in a debugger.
|
||||
5. If a crash occurs, run the following command to trigger the crash in a debugger.
|
||||
|
||||
```bash
|
||||
cargo hfuzz run-debug <fuzz target> \
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
image_size = 12288
|
||||
|
||||
[header]
|
||||
required_libavb_version_major = 1
|
||||
required_libavb_version_minor = 0
|
||||
algorithm_type = "Sha256Rsa4096"
|
||||
hash = "3035539ec16eb6e6645a9022dd62379bf448cc53178b91fb68507eb6565b5478"
|
||||
signature = "25f59424238d43e8e247583f276d45caeed1396e5d6298c4f4f5db4bdb74453ece1783143900157b35bdd99fe29123fe98f0876663b1c985a1426bdfbdf0b31b710ebe54cbb759d4eb1aa6ecc6964c5bccd364f1a9359e53d79797ec0bdc8e453a9a6c559e95ab4d8309422ad182bdc210b3d815b277b2fc8c827ff5849997ca9cf4fcb9fae69902ba865478401c2a4976f07499f27e8f8e3e609679dd32b495c27dfe9dae4de6eb7e9a86abe30da6f0e5454f85cf170eb3ea75250d697770d0028c78c0be12280b7599019ac03a05612384f81263821b8d3cbd902e4c2b6442afed2fb7800dfc4be999f77274afa4d97512147b8ccf791e59a91e75deebd4469538d71bc9272e4c7fbbc657f3d269ec269a617314a8ceb4a4920f9654fd549c8def8a3d5a54c38871cb822af8733a575f69a187717debca30884872206f04cb98aade78c765007e2d9b024185f153ec769de1684e98b0ce41fd0dbefd9aca814887ee0917f067708c66f1e5586c578f3a4f4148f71e5ce7dd84f150b99b1547e3ca6c041fafd085bb987f16a6056a4c28875d151872bda5e316b08bb43cfb2db78759b50e4cf016f0b0b0118e03ee25508da5ce9eee77f97fdeab0831a55c43760d1ba0b4cd0c8570840549f356f32c0abf8666daf478a4cad9b84cda52430137ad12ee15425a0bf948e362c6f2da0f4ff3c57683e0b8760a014e1dbd936c11"
|
||||
public_key = "0000100080af29ebacee1fc0a795d91674aaeb4f55f4d36ba6f899ddb543be5d1cc878d45d776fe6211a330c0843322334b690dbbb9dc13fa47642abc75889dbba79c82a9dba19c64d4c099f36240775834e9d86013f433a1aaab0bed1f2212dc53cf2d967ded53209c1e5eb5b563d00d307c021b9d2d084e80cdd20a695225ce31e7fb407ccebdec5cda2eb44ed079df3edf31751e77ce43ae9681bc7f6cc4e58945366bb655550e07dd215eb516815e9457feef9301b772f92291fd5758782f20734b9ac75e7469f42ffd4055ae43a8301a37e56dfda4ede5e639fec57ab7268d8787190268952a27e6b8e3ffc503759af317da4a076fd36fe6499f15de27d81dca566f500838d13a442b01ecd1ffdb655ea2ef779bab13cd8eab649402cc1a3f81444ed24717a7c3fa65ac2b8d3625450aae15b75d3dc37e962f516799cb555063eeb262bb51f9af465fc992e07f3aa66917ce086abd9a60716f04fe155efe7901e4132a4dcdcdcf7db23e4eade6b3664bee87a7fd4c6b3247beeb0553263845d6547efda705f58cab911d9a98fb7cdd807bfdbf3dbf1f81ac3e837ba75ea5512be7246bfe768aa1edf23c469db87df980bbb939fd1f28948340882350e4c58f0450329f8088da6b908b4c4c78689eb18b3c06b683b12e518bde5cd57e7fb43792df94c38f643675e5c92b1c0a95f45256969a4bc6ea1db6682745d423cc676f97f57cf2d493d850bfba9362a858faa355e2bd0c9b1a46f6581c9c0751399788bd37fc5e597a402a3bc05bb5959677a48ce4aa9fa6ed5add36a7e11117152610b0e8f86692669825206eb5d297d7f77d114bc867736b6d6eb4309329569d78dfb910cb66132c5a3f415bd1ededfb61fd58c958db4c7a2c17578f537d24b5405e545d1618ae58be3525c9659d6baf2634255e7ca26fcfde1cbc4401e8ca062babede5f5b3f2ad5cbecd3d9332098f402666a9ef85643e3f4ab214a67a5ca91f31f810eab89df57d2f9ef35e9a5f46a99228af059cdd1f0685f1b9405beb0e44fe7f590b50feea1554e8bdaf7be7fd22e6f050a7751731cd7f8737b5acb92378da9d59068106da25b30882abad4629dff6ca9c4c0cf5337bf2d2a9e8c6182dcd6d7c681da01df6c11d0b335cf1f982156393c4e572df540cee2c53a2c146dec9644be4e81cce20df4cb136b3a08fad9a186be892d6653330d11cac1e96d308fd0d2896c8a141edfbdcac827cdf97ac581f493721c9aa1305667df312277c8ab6700c3a978e6f9d00b389faaeaa99b5e023b72c5f5ce885fa2ba816ffc735599fad1e3876d91a501efbbf1315f42a503ddd7a26a784a3f290a86f43dd580c03fc4125716d6b534e35daf1156fcaec37ac8bc9e881d5ec1715632ff82b4dc4e3aabf48c5db6f3608ce2b8bb48bceae621b07f231254dd6f1f7b4584e7102969f523252a6839dca065"
|
||||
public_key_metadata = "000102030405060708090a0b0c0d0e0f"
|
||||
rollback_index = 1677974400
|
||||
flags = 0
|
||||
rollback_index_location = 0
|
||||
release_string = "MaxLengthMaxLengthMaxLengthMaxLengthMaxLengthMa"
|
||||
reserved = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
|
||||
|
||||
[[header.descriptors]]
|
||||
type = "Property"
|
||||
key = "foobar"
|
||||
value = 'Invalid\x20UTF-8:\x20\xFF'
|
||||
|
||||
[[header.descriptors]]
|
||||
type = "Hash"
|
||||
image_size = 6
|
||||
hash_algorithm = "sha256"
|
||||
partition_name = "vbmeta_appended_hash"
|
||||
salt = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
|
||||
root_digest = "2454ef9d58f39d544eb2d92294131c0b3b1473a276418d0b1362693fdafa685b"
|
||||
flags = 0
|
||||
reserved = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef01234567"
|
||||
|
||||
[footer]
|
||||
version_major = 1
|
||||
version_minor = 0
|
||||
original_image_size = 6
|
||||
vbmeta_offset = 4096
|
||||
vbmeta_size = 2176
|
||||
reserved = "0123456789abcdef0123456789abcdef0123456789abcdef01234567"
|
||||
@@ -0,0 +1 @@
|
||||
foobar
|
||||
@@ -0,0 +1,46 @@
|
||||
image_size = 28672
|
||||
|
||||
[header]
|
||||
required_libavb_version_major = 1
|
||||
required_libavb_version_minor = 0
|
||||
algorithm_type = "Sha256Rsa4096"
|
||||
hash = "2bf01b15b31a729ca8053bc1303b0a4a638ee4819cc3106fd36ff996b703e249"
|
||||
signature = "8c4f1daf4174f4347e623b57e314d4ac7717014c7476aa2f54ab3a71f3143fb366398dd2a165e092085ce34ed1412a7bf9ce8008f2061164edc3e42ef8b7f2a7e38ebc1f05059b0b829fa82cceef91aad67ffb843c79a9c6944f0f08d7523111a8c25dbfeab9aedb0cbd682f906d33a044be072fa22657024d4d056823310f38b65cad5e293b26142798a3a49c880eb4646bfbf612294f90792b652532d5e9bb5ca79699f968ec811a3aeb038d6033ac82a571f098659294db36666da4380c8a74eb7af8dfe32e5136301dad0e49d71e726d15c0cf659850eaa878133302835fcbe624122aee63b72a960d1c1432561afa55f07a9e57c6791c813131a7c0f6a0e1036036bc613e38cff14a1353b496416f06b1a3c224d8cb51218d251c8720624c936541519167e150dc2ac92091110fae8a08bac660b6ea4ef12f0c710bc7deada678a4e8d00b4803980e40c1899e9a5208b55d06bc519004e2bbfb15c9bec614c496de1f7fe103d9173067084f0170b9f6837f179689c3a4aa11b94e54aa3a49c76838ed5d31fece91a95fc089af72833e453c64c58a8270ec868ae7a4154717f3c98a940e51a197c9ebed4188ff1969faf4d9e59618713af785b538c4f41568dd4ba9b6dff68ab18222b4195938c1e500640218d1d4f6ef014c76deab8e18782e5fff6988ef6e630ff38dcc6709de7548cc1f56d817a0dbfe5df95e6a0f53"
|
||||
public_key = "0000100080af29ebacee1fc0a795d91674aaeb4f55f4d36ba6f899ddb543be5d1cc878d45d776fe6211a330c0843322334b690dbbb9dc13fa47642abc75889dbba79c82a9dba19c64d4c099f36240775834e9d86013f433a1aaab0bed1f2212dc53cf2d967ded53209c1e5eb5b563d00d307c021b9d2d084e80cdd20a695225ce31e7fb407ccebdec5cda2eb44ed079df3edf31751e77ce43ae9681bc7f6cc4e58945366bb655550e07dd215eb516815e9457feef9301b772f92291fd5758782f20734b9ac75e7469f42ffd4055ae43a8301a37e56dfda4ede5e639fec57ab7268d8787190268952a27e6b8e3ffc503759af317da4a076fd36fe6499f15de27d81dca566f500838d13a442b01ecd1ffdb655ea2ef779bab13cd8eab649402cc1a3f81444ed24717a7c3fa65ac2b8d3625450aae15b75d3dc37e962f516799cb555063eeb262bb51f9af465fc992e07f3aa66917ce086abd9a60716f04fe155efe7901e4132a4dcdcdcf7db23e4eade6b3664bee87a7fd4c6b3247beeb0553263845d6547efda705f58cab911d9a98fb7cdd807bfdbf3dbf1f81ac3e837ba75ea5512be7246bfe768aa1edf23c469db87df980bbb939fd1f28948340882350e4c58f0450329f8088da6b908b4c4c78689eb18b3c06b683b12e518bde5cd57e7fb43792df94c38f643675e5c92b1c0a95f45256969a4bc6ea1db6682745d423cc676f97f57cf2d493d850bfba9362a858faa355e2bd0c9b1a46f6581c9c0751399788bd37fc5e597a402a3bc05bb5959677a48ce4aa9fa6ed5add36a7e11117152610b0e8f86692669825206eb5d297d7f77d114bc867736b6d6eb4309329569d78dfb910cb66132c5a3f415bd1ededfb61fd58c958db4c7a2c17578f537d24b5405e545d1618ae58be3525c9659d6baf2634255e7ca26fcfde1cbc4401e8ca062babede5f5b3f2ad5cbecd3d9332098f402666a9ef85643e3f4ab214a67a5ca91f31f810eab89df57d2f9ef35e9a5f46a99228af059cdd1f0685f1b9405beb0e44fe7f590b50feea1554e8bdaf7be7fd22e6f050a7751731cd7f8737b5acb92378da9d59068106da25b30882abad4629dff6ca9c4c0cf5337bf2d2a9e8c6182dcd6d7c681da01df6c11d0b335cf1f982156393c4e572df540cee2c53a2c146dec9644be4e81cce20df4cb136b3a08fad9a186be892d6653330d11cac1e96d308fd0d2896c8a141edfbdcac827cdf97ac581f493721c9aa1305667df312277c8ab6700c3a978e6f9d00b389faaeaa99b5e023b72c5f5ce885fa2ba816ffc735599fad1e3876d91a501efbbf1315f42a503ddd7a26a784a3f290a86f43dd580c03fc4125716d6b534e35daf1156fcaec37ac8bc9e881d5ec1715632ff82b4dc4e3aabf48c5db6f3608ce2b8bb48bceae621b07f231254dd6f1f7b4584e7102969f523252a6839dca065"
|
||||
public_key_metadata = "000102030405060708090a0b0c0d0e0f"
|
||||
rollback_index = 1677974400
|
||||
flags = 0
|
||||
rollback_index_location = 0
|
||||
release_string = "MaxLengthMaxLengthMaxLengthMaxLengthMaxLengthMa"
|
||||
reserved = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
|
||||
|
||||
[[header.descriptors]]
|
||||
type = "Property"
|
||||
key = "foobar"
|
||||
value = 'Invalid\x20UTF-8:\x20\xFF'
|
||||
|
||||
[[header.descriptors]]
|
||||
type = "HashTree"
|
||||
dm_verity_version = 1
|
||||
image_size = 8192
|
||||
tree_offset = 8192
|
||||
tree_size = 4096
|
||||
data_block_size = 4096
|
||||
hash_block_size = 4096
|
||||
fec_num_roots = 2
|
||||
fec_offset = 12288
|
||||
fec_size = 8192
|
||||
hash_algorithm = "sha256"
|
||||
partition_name = "vbmeta_appended_hash_tree"
|
||||
salt = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
|
||||
root_digest = "bfb9f01152b3315096319d03697b4013d1674063418deec55920686ad13537a8"
|
||||
flags = 0
|
||||
reserved = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef01234567"
|
||||
|
||||
[footer]
|
||||
version_major = 1
|
||||
version_minor = 0
|
||||
original_image_size = 8192
|
||||
vbmeta_offset = 20480
|
||||
vbmeta_size = 2240
|
||||
reserved = "0123456789abcdef0123456789abcdef0123456789abcdef01234567"
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,61 @@
|
||||
image_size = 4096
|
||||
|
||||
[header]
|
||||
required_libavb_version_major = 1
|
||||
required_libavb_version_minor = 0
|
||||
algorithm_type = "Sha256Rsa4096"
|
||||
hash = "892d9bd80a0b8b5e5daed1cdedfc252e3fe5573ffa812fa485c2eaec17632f3b"
|
||||
signature = "752e7688a4de06eab55d37b56c4d7158e6e0477916f5217904edb85c946e4bfa4c0fb5d8aed0d1a0cc084c528cc303209075caa1067bdba7c0cc970d024c022ffe61feabc0eaf45755938d2b9b248ae07d369388b92c23c0f0f31a4a5ac1a4e92ebd2b24fff7953448a8f6827363fd3d32055977d708f72c9224b1a6616b59e79cdb20a4c17959081904d394fdfa27573f0b428fd7179ae3607c93e99a07badd6908e3f76893a346267126798503807d4a58904262afca7544349a796b04f658511724207c5a645c2976faec055f44c44ea21d63c7b2af21a8ffb7f4007249e8f81f8e2746e20965ced023b952bc48cb9c5ae11587b771f47383cfc1f5d021b1024128fc02a5a2f458d131a08975470d9373a9b70336d647943ed9e04bdaa0bbf19bfb087e0e6454cd178152eb826244bbfad20315dc20ae9a889ea99ac0045f438726bb882bf54c87fecfb3056aac7f2d649523b8cbcc8dd5a54917740e440ad7ffdaefde7e98b557798054d94653005213f377ce560fdec934435b2049b09e613906c12da713b97d31966238e7708a47501c0c91c6a9cfb4911fef54acacfa3e83f29a542d8a7d2c0f7b3b92815502ca9307e40c009415e575aab7f3b998a48d4a0a6d59adf3c8bd253620a9cbf53ea5fdb84bce5e21aca3d8d749406770725fc22f925e9a53f1c58eddc5e4dbd68d8537e7dda5035c20c54a5045434c6505"
|
||||
public_key = "0000100080af29ebacee1fc0a795d91674aaeb4f55f4d36ba6f899ddb543be5d1cc878d45d776fe6211a330c0843322334b690dbbb9dc13fa47642abc75889dbba79c82a9dba19c64d4c099f36240775834e9d86013f433a1aaab0bed1f2212dc53cf2d967ded53209c1e5eb5b563d00d307c021b9d2d084e80cdd20a695225ce31e7fb407ccebdec5cda2eb44ed079df3edf31751e77ce43ae9681bc7f6cc4e58945366bb655550e07dd215eb516815e9457feef9301b772f92291fd5758782f20734b9ac75e7469f42ffd4055ae43a8301a37e56dfda4ede5e639fec57ab7268d8787190268952a27e6b8e3ffc503759af317da4a076fd36fe6499f15de27d81dca566f500838d13a442b01ecd1ffdb655ea2ef779bab13cd8eab649402cc1a3f81444ed24717a7c3fa65ac2b8d3625450aae15b75d3dc37e962f516799cb555063eeb262bb51f9af465fc992e07f3aa66917ce086abd9a60716f04fe155efe7901e4132a4dcdcdcf7db23e4eade6b3664bee87a7fd4c6b3247beeb0553263845d6547efda705f58cab911d9a98fb7cdd807bfdbf3dbf1f81ac3e837ba75ea5512be7246bfe768aa1edf23c469db87df980bbb939fd1f28948340882350e4c58f0450329f8088da6b908b4c4c78689eb18b3c06b683b12e518bde5cd57e7fb43792df94c38f643675e5c92b1c0a95f45256969a4bc6ea1db6682745d423cc676f97f57cf2d493d850bfba9362a858faa355e2bd0c9b1a46f6581c9c0751399788bd37fc5e597a402a3bc05bb5959677a48ce4aa9fa6ed5add36a7e11117152610b0e8f86692669825206eb5d297d7f77d114bc867736b6d6eb4309329569d78dfb910cb66132c5a3f415bd1ededfb61fd58c958db4c7a2c17578f537d24b5405e545d1618ae58be3525c9659d6baf2634255e7ca26fcfde1cbc4401e8ca062babede5f5b3f2ad5cbecd3d9332098f402666a9ef85643e3f4ab214a67a5ca91f31f810eab89df57d2f9ef35e9a5f46a99228af059cdd1f0685f1b9405beb0e44fe7f590b50feea1554e8bdaf7be7fd22e6f050a7751731cd7f8737b5acb92378da9d59068106da25b30882abad4629dff6ca9c4c0cf5337bf2d2a9e8c6182dcd6d7c681da01df6c11d0b335cf1f982156393c4e572df540cee2c53a2c146dec9644be4e81cce20df4cb136b3a08fad9a186be892d6653330d11cac1e96d308fd0d2896c8a141edfbdcac827cdf97ac581f493721c9aa1305667df312277c8ab6700c3a978e6f9d00b389faaeaa99b5e023b72c5f5ce885fa2ba816ffc735599fad1e3876d91a501efbbf1315f42a503ddd7a26a784a3f290a86f43dd580c03fc4125716d6b534e35daf1156fcaec37ac8bc9e881d5ec1715632ff82b4dc4e3aabf48c5db6f3608ce2b8bb48bceae621b07f231254dd6f1f7b4584e7102969f523252a6839dca065"
|
||||
public_key_metadata = "000102030405060708090a0b0c0d0e0f"
|
||||
rollback_index = 1677974400
|
||||
flags = 0
|
||||
rollback_index_location = 0
|
||||
release_string = "MaxLengthMaxLengthMaxLengthMaxLengthMaxLengthMa"
|
||||
reserved = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
|
||||
|
||||
[[header.descriptors]]
|
||||
type = "Property"
|
||||
key = "foobar"
|
||||
value = 'Invalid\x20UTF-8:\x20\xFF'
|
||||
|
||||
[[header.descriptors]]
|
||||
type = "HashTree"
|
||||
dm_verity_version = 1
|
||||
image_size = 4096
|
||||
tree_offset = 0
|
||||
tree_size = 2048
|
||||
data_block_size = 4096
|
||||
hash_block_size = 4096
|
||||
fec_num_roots = 1
|
||||
fec_offset = 2048
|
||||
fec_size = 2048
|
||||
hash_algorithm = "sha512"
|
||||
partition_name = "hashtreed_partition"
|
||||
salt = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
|
||||
root_digest = "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210"
|
||||
flags = 0
|
||||
reserved = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef01234567"
|
||||
|
||||
[[header.descriptors]]
|
||||
type = "Hash"
|
||||
image_size = 6
|
||||
hash_algorithm = "sha256"
|
||||
partition_name = "hashed_partition"
|
||||
salt = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
|
||||
root_digest = "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210"
|
||||
flags = 0
|
||||
reserved = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef01234567"
|
||||
|
||||
[[header.descriptors]]
|
||||
type = "KernelCmdline"
|
||||
flags = 1
|
||||
cmdline = "foobar"
|
||||
|
||||
[[header.descriptors]]
|
||||
type = "ChainPartition"
|
||||
rollback_index_location = 1
|
||||
partition_name = "chained_partition"
|
||||
public_key = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
|
||||
flags = 4275878552
|
||||
reserved = "76543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210"
|
||||
@@ -0,0 +1,20 @@
|
||||
type = "V0Through2"
|
||||
kernel_addr = 19088743
|
||||
ramdisk_addr = 2309737967
|
||||
second_addr = 38177486
|
||||
tags_addr = 324508639
|
||||
page_size = 4096
|
||||
os_version = 1985229328
|
||||
name = "NameNameNameName"
|
||||
cmdline = "CmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineC"
|
||||
id = [
|
||||
1122867,
|
||||
1146447479,
|
||||
2291772091,
|
||||
3437096703,
|
||||
4293844428,
|
||||
3148519816,
|
||||
2003195204,
|
||||
857870592,
|
||||
]
|
||||
extra_cmdline = "ExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtr"
|
||||
@@ -0,0 +1 @@
|
||||
kernel data
|
||||
@@ -0,0 +1 @@
|
||||
ramdisk data
|
||||
@@ -0,0 +1 @@
|
||||
second data
|
||||
@@ -0,0 +1,23 @@
|
||||
type = "V0Through2"
|
||||
kernel_addr = 19088743
|
||||
ramdisk_addr = 2309737967
|
||||
second_addr = 38177486
|
||||
tags_addr = 324508639
|
||||
page_size = 4096
|
||||
os_version = 1985229328
|
||||
name = "NameNameNameName"
|
||||
cmdline = "CmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineC"
|
||||
id = [
|
||||
1122867,
|
||||
1146447479,
|
||||
2291772091,
|
||||
3437096703,
|
||||
4293844428,
|
||||
3148519816,
|
||||
2003195204,
|
||||
857870592,
|
||||
]
|
||||
extra_cmdline = "ExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtr"
|
||||
|
||||
[v1_extra]
|
||||
recovery_dtbo_offset = 81985529216486895
|
||||
@@ -0,0 +1 @@
|
||||
kernel data
|
||||
@@ -0,0 +1 @@
|
||||
ramdisk data
|
||||
@@ -0,0 +1 @@
|
||||
recovery_dtbo data
|
||||
@@ -0,0 +1 @@
|
||||
second data
|
||||
@@ -0,0 +1,26 @@
|
||||
type = "V0Through2"
|
||||
kernel_addr = 19088743
|
||||
ramdisk_addr = 2309737967
|
||||
second_addr = 38177486
|
||||
tags_addr = 324508639
|
||||
page_size = 4096
|
||||
os_version = 1985229328
|
||||
name = "NameNameNameName"
|
||||
cmdline = "CmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineC"
|
||||
id = [
|
||||
1122867,
|
||||
1146447479,
|
||||
2291772091,
|
||||
3437096703,
|
||||
4293844428,
|
||||
3148519816,
|
||||
2003195204,
|
||||
857870592,
|
||||
]
|
||||
extra_cmdline = "ExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtraCmdlineExtr"
|
||||
|
||||
[v1_extra]
|
||||
recovery_dtbo_offset = 81985529216486895
|
||||
|
||||
[v2_extra]
|
||||
dtb_addr = 18364758544493064720
|
||||
@@ -0,0 +1 @@
|
||||
dtb data
|
||||
@@ -0,0 +1 @@
|
||||
kernel data
|
||||
@@ -0,0 +1 @@
|
||||
ramdisk data
|
||||
@@ -0,0 +1 @@
|
||||
recovery_dtbo data
|
||||
@@ -0,0 +1 @@
|
||||
second data
|
||||
@@ -0,0 +1,9 @@
|
||||
type = "V3Through4"
|
||||
os_version = 19088743
|
||||
reserved = [
|
||||
1122867,
|
||||
1146447479,
|
||||
2291772091,
|
||||
3437096703,
|
||||
]
|
||||
cmdline = "CmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmd"
|
||||
@@ -0,0 +1 @@
|
||||
kernel data
|
||||
@@ -0,0 +1 @@
|
||||
ramdisk data
|
||||
@@ -0,0 +1,11 @@
|
||||
type = "V3Through4"
|
||||
os_version = 19088743
|
||||
reserved = [
|
||||
1122867,
|
||||
1146447479,
|
||||
2291772091,
|
||||
3437096703,
|
||||
]
|
||||
cmdline = "CmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmd"
|
||||
|
||||
[v4_extra]
|
||||
@@ -0,0 +1 @@
|
||||
kernel data
|
||||
@@ -0,0 +1 @@
|
||||
ramdisk data
|
||||
@@ -0,0 +1,11 @@
|
||||
type = "V3Through4"
|
||||
os_version = 19088743
|
||||
reserved = [
|
||||
1122867,
|
||||
1146447479,
|
||||
2291772091,
|
||||
3437096703,
|
||||
]
|
||||
cmdline = "CmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmd"
|
||||
|
||||
[v4_extra]
|
||||
@@ -0,0 +1 @@
|
||||
kernel data
|
||||
@@ -0,0 +1 @@
|
||||
ramdisk data
|
||||
@@ -0,0 +1,25 @@
|
||||
image_size = 2048
|
||||
|
||||
[header]
|
||||
required_libavb_version_major = 1
|
||||
required_libavb_version_minor = 0
|
||||
algorithm_type = "Sha256Rsa4096"
|
||||
hash = "73b770b8199f1e709ce1cc37ee7f699faa99bf7e88c6eff429a6eda1feb6a1f9"
|
||||
signature = "58c16f72718891a11f8d1ad92b8053c20d701ac3cc997ee7889d45c999ee91b05795d59d6feac44c316fe15f92dd06e9ae3bc570d8f414191bdaab7aae4c37822cb2f6d34fdcccc65a853d2dc72ecec311db000e27fbdc7bc767b80c1a0b4e88e193ce4f878ce15cac2a4b361294eddc193d36e148768b4c6b0de4001cf2e12fe2b8be84d5217a4c3ea12c0e040900a112f6e519363635a3b3108191b5858c90b62a4d459a6d66218978e002ed07478472ff34269ca575a53eb396e3e13972f115827c741b82e5988eb6f59f953e2ee5ad9fc6ab5d86754f282011e4a511c8ce4ed9d8b077a2b5f1a3e8943c222debaab02e02b6e461f98820a8f90a470ea222b625831e280a8d9bdf96bde33bf830bc2e39a62d52103f3b8988025da86f55a5f075ea362e45c0f92279216dfdf47ead516dfd5d9da546fd5a93e325a1706c0241f71a4d4cd5abec836fd2be0812b9716ce126498a67b0d6610adc94bb9cf9b137af57b1f413155428a4bc0318aff369cdcae69008f883b48d3ba6210c8f90cb473c8101c2381ad1ab2d2b17cdf3a8e65d118beb2283b140630e3f4800159d208c34abac3d9493e321948205e1a6df529a36ec47dac50b06a71aa2350f87738022ad8175c514dfb6acca04b9bac4a002ff587639cab9f1d94e68f13739d7d72b30010db4cf0e62ea796d6207e3b9264b9ec980076c6be7dbd9973e6278225be0"
|
||||
public_key = "0000100080af29ebacee1fc0a795d91674aaeb4f55f4d36ba6f899ddb543be5d1cc878d45d776fe6211a330c0843322334b690dbbb9dc13fa47642abc75889dbba79c82a9dba19c64d4c099f36240775834e9d86013f433a1aaab0bed1f2212dc53cf2d967ded53209c1e5eb5b563d00d307c021b9d2d084e80cdd20a695225ce31e7fb407ccebdec5cda2eb44ed079df3edf31751e77ce43ae9681bc7f6cc4e58945366bb655550e07dd215eb516815e9457feef9301b772f92291fd5758782f20734b9ac75e7469f42ffd4055ae43a8301a37e56dfda4ede5e639fec57ab7268d8787190268952a27e6b8e3ffc503759af317da4a076fd36fe6499f15de27d81dca566f500838d13a442b01ecd1ffdb655ea2ef779bab13cd8eab649402cc1a3f81444ed24717a7c3fa65ac2b8d3625450aae15b75d3dc37e962f516799cb555063eeb262bb51f9af465fc992e07f3aa66917ce086abd9a60716f04fe155efe7901e4132a4dcdcdcf7db23e4eade6b3664bee87a7fd4c6b3247beeb0553263845d6547efda705f58cab911d9a98fb7cdd807bfdbf3dbf1f81ac3e837ba75ea5512be7246bfe768aa1edf23c469db87df980bbb939fd1f28948340882350e4c58f0450329f8088da6b908b4c4c78689eb18b3c06b683b12e518bde5cd57e7fb43792df94c38f643675e5c92b1c0a95f45256969a4bc6ea1db6682745d423cc676f97f57cf2d493d850bfba9362a858faa355e2bd0c9b1a46f6581c9c0751399788bd37fc5e597a402a3bc05bb5959677a48ce4aa9fa6ed5add36a7e11117152610b0e8f86692669825206eb5d297d7f77d114bc867736b6d6eb4309329569d78dfb910cb66132c5a3f415bd1ededfb61fd58c958db4c7a2c17578f537d24b5405e545d1618ae58be3525c9659d6baf2634255e7ca26fcfde1cbc4401e8ca062babede5f5b3f2ad5cbecd3d9332098f402666a9ef85643e3f4ab214a67a5ca91f31f810eab89df57d2f9ef35e9a5f46a99228af059cdd1f0685f1b9405beb0e44fe7f590b50feea1554e8bdaf7be7fd22e6f050a7751731cd7f8737b5acb92378da9d59068106da25b30882abad4629dff6ca9c4c0cf5337bf2d2a9e8c6182dcd6d7c681da01df6c11d0b335cf1f982156393c4e572df540cee2c53a2c146dec9644be4e81cce20df4cb136b3a08fad9a186be892d6653330d11cac1e96d308fd0d2896c8a141edfbdcac827cdf97ac581f493721c9aa1305667df312277c8ab6700c3a978e6f9d00b389faaeaa99b5e023b72c5f5ce885fa2ba816ffc735599fad1e3876d91a501efbbf1315f42a503ddd7a26a784a3f290a86f43dd580c03fc4125716d6b534e35daf1156fcaec37ac8bc9e881d5ec1715632ff82b4dc4e3aabf48c5db6f3608ce2b8bb48bceae621b07f231254dd6f1f7b4584e7102969f523252a6839dca065"
|
||||
public_key_metadata = ""
|
||||
rollback_index = 0
|
||||
flags = 0
|
||||
rollback_index_location = 0
|
||||
release_string = "avbtool 1.2.0"
|
||||
reserved = "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
|
||||
[[header.descriptors]]
|
||||
type = "Hash"
|
||||
image_size = 12288
|
||||
hash_algorithm = "sha256"
|
||||
partition_name = "boot"
|
||||
salt = "6430306466303064"
|
||||
root_digest = "abd5483e11e7940cb9bf387587a4a1659981a1b83962b7c1faf1b0cd6307d449"
|
||||
flags = 0
|
||||
reserved = "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
@@ -0,0 +1,8 @@
|
||||
type = "VendorV3Through4"
|
||||
page_size = 4096
|
||||
kernel_addr = 19088743
|
||||
ramdisk_addr = 2309737967
|
||||
cmdline = "CmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdl"
|
||||
tags_addr = 4275878552
|
||||
name = "NameNameNameName"
|
||||
dtb_addr = 1985229328
|
||||
@@ -0,0 +1 @@
|
||||
dtb data
|
||||
@@ -0,0 +1 @@
|
||||
ramdisk data
|
||||
@@ -0,0 +1,96 @@
|
||||
type = "VendorV3Through4"
|
||||
page_size = 2048
|
||||
kernel_addr = 19088743
|
||||
ramdisk_addr = 2309737967
|
||||
cmdline = "CmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdlineCmdl"
|
||||
tags_addr = 4275878552
|
||||
name = "NameNameNameName"
|
||||
dtb_addr = 1985229328
|
||||
|
||||
[[v4_extra.ramdisk_metas]]
|
||||
ramdisk_type = 0
|
||||
ramdisk_name = "NoneNoneNoneNoneNoneNoneNoneNone"
|
||||
board_id = [
|
||||
1122867,
|
||||
1146447479,
|
||||
2291772091,
|
||||
3437096703,
|
||||
4293844428,
|
||||
3148519816,
|
||||
2003195204,
|
||||
857870592,
|
||||
4491468,
|
||||
290822621,
|
||||
577153774,
|
||||
863484927,
|
||||
4290475827,
|
||||
4004144674,
|
||||
3717813521,
|
||||
3431482368,
|
||||
]
|
||||
|
||||
[[v4_extra.ramdisk_metas]]
|
||||
ramdisk_type = 1
|
||||
ramdisk_name = "PlatformPlatformPlatformPlatform"
|
||||
board_id = [
|
||||
1122867,
|
||||
1146447479,
|
||||
2291772091,
|
||||
3437096703,
|
||||
4293844428,
|
||||
3148519816,
|
||||
2003195204,
|
||||
857870592,
|
||||
4491468,
|
||||
290822621,
|
||||
577153774,
|
||||
863484927,
|
||||
4290475827,
|
||||
4004144674,
|
||||
3717813521,
|
||||
3431482368,
|
||||
]
|
||||
|
||||
[[v4_extra.ramdisk_metas]]
|
||||
ramdisk_type = 2
|
||||
ramdisk_name = "RecoveryRecoveryRecoveryRecovery"
|
||||
board_id = [
|
||||
1122867,
|
||||
1146447479,
|
||||
2291772091,
|
||||
3437096703,
|
||||
4293844428,
|
||||
3148519816,
|
||||
2003195204,
|
||||
857870592,
|
||||
4491468,
|
||||
290822621,
|
||||
577153774,
|
||||
863484927,
|
||||
4290475827,
|
||||
4004144674,
|
||||
3717813521,
|
||||
3431482368,
|
||||
]
|
||||
|
||||
[[v4_extra.ramdisk_metas]]
|
||||
ramdisk_type = 3
|
||||
ramdisk_name = "DlkmDlkmDlkmDlkmDlkmDlkmDlkmDlkm"
|
||||
board_id = [
|
||||
1122867,
|
||||
1146447479,
|
||||
2291772091,
|
||||
3437096703,
|
||||
4293844428,
|
||||
3148519816,
|
||||
2003195204,
|
||||
857870592,
|
||||
4491468,
|
||||
290822621,
|
||||
577153774,
|
||||
863484927,
|
||||
4290475827,
|
||||
4004144674,
|
||||
3717813521,
|
||||
3431482368,
|
||||
]
|
||||
@@ -0,0 +1 @@
|
||||
bootconfig data
|
||||
@@ -0,0 +1 @@
|
||||
dtb data
|
||||
@@ -0,0 +1 @@
|
||||
ramdisk 0 data
|
||||
@@ -0,0 +1 @@
|
||||
ramdisk 1 data
|
||||
@@ -0,0 +1 @@
|
||||
ramdisk 2 data
|
||||
@@ -0,0 +1 @@
|
||||
ramdisk 3 data
|
||||
@@ -0,0 +1,35 @@
|
||||
format = "None"
|
||||
|
||||
[[entries]]
|
||||
path = "symlink"
|
||||
data = "target"
|
||||
file_type = "Symlink"
|
||||
file_mode = "777"
|
||||
nlink = 1
|
||||
|
||||
[[entries]]
|
||||
path = "directory"
|
||||
file_type = "Directory"
|
||||
file_mode = "755"
|
||||
nlink = 1
|
||||
|
||||
[[entries]]
|
||||
path = "file"
|
||||
file_type = "Regular"
|
||||
file_mode = "644"
|
||||
nlink = 1
|
||||
|
||||
[[entries]]
|
||||
path = "reserved"
|
||||
inode = 12345
|
||||
file_type = "Reserved"
|
||||
file_mode = "4777"
|
||||
uid = 12345678
|
||||
gid = 87654321
|
||||
nlink = 2
|
||||
mtime = 1700000000
|
||||
dev_maj = 2222
|
||||
dev_min = 3333
|
||||
rdev_maj = 4444
|
||||
rdev_min = 5555
|
||||
crc32 = 4275878409
|
||||
@@ -0,0 +1 @@
|
||||
foobar
|
||||
@@ -0,0 +1 @@
|
||||
datadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadata
|
||||
@@ -0,0 +1,80 @@
|
||||
image_type = "Empty"
|
||||
metadata_max_size = 65536
|
||||
metadata_slot_count = 3
|
||||
logical_block_size = 4096
|
||||
|
||||
[[slots]]
|
||||
major_version = 10
|
||||
minor_version = 2
|
||||
flags = "VIRTUAL_AB_DEVICE"
|
||||
|
||||
[[slots.groups]]
|
||||
name = "default"
|
||||
flags = ""
|
||||
partitions = []
|
||||
|
||||
[[slots.groups]]
|
||||
name = "google_dynamic_partitions_a"
|
||||
flags = ""
|
||||
maximum_size = 8527020032
|
||||
|
||||
[[slots.groups.partitions]]
|
||||
name = "system_a"
|
||||
attributes = "READONLY"
|
||||
|
||||
[[slots.groups.partitions]]
|
||||
name = "system_dlkm_a"
|
||||
attributes = "READONLY"
|
||||
|
||||
[[slots.groups.partitions]]
|
||||
name = "system_ext_a"
|
||||
attributes = "READONLY"
|
||||
|
||||
[[slots.groups.partitions]]
|
||||
name = "product_a"
|
||||
attributes = "READONLY"
|
||||
|
||||
[[slots.groups.partitions]]
|
||||
name = "vendor_a"
|
||||
attributes = "READONLY"
|
||||
|
||||
[[slots.groups.partitions]]
|
||||
name = "vendor_dlkm_a"
|
||||
attributes = "READONLY"
|
||||
|
||||
[[slots.groups]]
|
||||
name = "google_dynamic_partitions_b"
|
||||
flags = ""
|
||||
maximum_size = 8527020032
|
||||
|
||||
[[slots.groups.partitions]]
|
||||
name = "system_b"
|
||||
attributes = "READONLY"
|
||||
|
||||
[[slots.groups.partitions]]
|
||||
name = "system_dlkm_b"
|
||||
attributes = "READONLY"
|
||||
|
||||
[[slots.groups.partitions]]
|
||||
name = "system_ext_b"
|
||||
attributes = "READONLY"
|
||||
|
||||
[[slots.groups.partitions]]
|
||||
name = "product_b"
|
||||
attributes = "READONLY"
|
||||
|
||||
[[slots.groups.partitions]]
|
||||
name = "vendor_b"
|
||||
attributes = "READONLY"
|
||||
|
||||
[[slots.groups.partitions]]
|
||||
name = "vendor_dlkm_b"
|
||||
attributes = "READONLY"
|
||||
|
||||
[[slots.block_devices]]
|
||||
first_logical_sector = 2048
|
||||
alignment = 1048576
|
||||
alignment_offset = 0
|
||||
size = 8531214336
|
||||
partition_name = "super"
|
||||
flags = ""
|
||||
@@ -0,0 +1 @@
|
||||
datadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadata
|
||||
@@ -1 +0,0 @@
|
||||
../../../../avbroot/tests/data/vbmeta_appended_hash.img
|
||||
@@ -1 +0,0 @@
|
||||
../../../../avbroot/tests/data/vbmeta_appended_hash_tree.img
|
||||
@@ -1 +0,0 @@
|
||||
../../../../avbroot/tests/data/vbmeta_root.img
|
||||
@@ -1 +0,0 @@
|
||||
../../../../avbroot/tests/data/boot_v0.img
|
||||
@@ -1 +0,0 @@
|
||||
../../../../avbroot/tests/data/boot_v1.img
|
||||
@@ -1 +0,0 @@
|
||||
../../../../avbroot/tests/data/boot_v2.img
|
||||
@@ -1 +0,0 @@
|
||||
../../../../avbroot/tests/data/boot_v3.img
|
||||
@@ -1 +0,0 @@
|
||||
../../../../avbroot/tests/data/boot_v4.img
|
||||
@@ -1 +0,0 @@
|
||||
../../../../avbroot/tests/data/boot_v4_vts.img
|
||||
@@ -1 +0,0 @@
|
||||
../../../../avbroot/tests/data/vendor_v3.img
|
||||
@@ -1 +0,0 @@
|
||||
../../../../avbroot/tests/data/vendor_v4.img
|
||||
Binary file not shown.
File diff suppressed because one or more lines are too long
+1
-1
@@ -13,7 +13,7 @@ rust-version.workspace = true
|
||||
anyhow = "1.0.75"
|
||||
clap = { version = "4.4.1", features = ["derive"] }
|
||||
regex = { version = "1.9.4", default-features = false, features = ["perf", "std"] }
|
||||
toml_edit = "0.23.3"
|
||||
toml_edit = "0.25.1"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
// SPDX-FileCopyrightText: 2026 Andrew Gunnerson
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use std::{
|
||||
env,
|
||||
fs::{self},
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
};
|
||||
|
||||
use anyhow::{Context, Result, bail};
|
||||
|
||||
use crate::WORKSPACE_DIR;
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
const EXE_SUFFIX: &str = "";
|
||||
#[cfg(target_os = "windows")]
|
||||
const EXE_SUFFIX: &str = ".exe";
|
||||
|
||||
fn run(mut command: Command) -> Result<()> {
|
||||
let status = command
|
||||
.status()
|
||||
.with_context(|| format!("Failed to spawn: {command:?}"))?;
|
||||
if !status.success() {
|
||||
bail!("Command failed with status: {status}: {command:?}");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn build_avbroot() -> Result<PathBuf> {
|
||||
let mut command = Command::new(env!("CARGO"));
|
||||
command.arg("build");
|
||||
command.current_dir(WORKSPACE_DIR);
|
||||
|
||||
run(command)?;
|
||||
|
||||
let mut executable = Path::new(WORKSPACE_DIR).to_owned();
|
||||
if let Some(target_dir) = env::var_os("CARGO_TARGET_DIR") {
|
||||
executable.push(target_dir);
|
||||
} else {
|
||||
executable.push("target");
|
||||
}
|
||||
executable.push("debug");
|
||||
executable.push(format!("avbroot{EXE_SUFFIX}"));
|
||||
|
||||
Ok(executable)
|
||||
}
|
||||
|
||||
fn generate_avb_image(avbroot: &Path, input_dir: &Path, output_file: &Path) -> Result<()> {
|
||||
let mut command = Command::new(avbroot);
|
||||
command.arg("avb");
|
||||
command.arg("pack");
|
||||
command.arg("-q");
|
||||
command.arg("-o");
|
||||
command.arg(output_file);
|
||||
command.current_dir(input_dir);
|
||||
|
||||
run(command)
|
||||
}
|
||||
|
||||
fn generate_boot_image(avbroot: &Path, input_dir: &Path, output_file: &Path) -> Result<()> {
|
||||
let vts_signature_dir = input_dir.join("vts_signature");
|
||||
if vts_signature_dir.exists() {
|
||||
let mut command = Command::new(avbroot);
|
||||
command.arg("avb");
|
||||
command.arg("pack");
|
||||
command.arg("-q");
|
||||
command.arg("-o");
|
||||
command.arg("../vts_signature.img");
|
||||
command.current_dir(vts_signature_dir);
|
||||
|
||||
run(command)?;
|
||||
}
|
||||
|
||||
let mut command = Command::new(avbroot);
|
||||
command.arg("boot");
|
||||
command.arg("pack");
|
||||
command.arg("-q");
|
||||
command.arg("-o");
|
||||
command.arg(output_file);
|
||||
command.current_dir(input_dir);
|
||||
|
||||
run(command)
|
||||
}
|
||||
|
||||
fn generate_cpio_image(avbroot: &Path, input_dir: &Path, output_file: &Path) -> Result<()> {
|
||||
let mut command = Command::new(avbroot);
|
||||
command.arg("cpio");
|
||||
command.arg("pack");
|
||||
command.arg("-q");
|
||||
command.arg("-o");
|
||||
command.arg(output_file);
|
||||
command.current_dir(input_dir);
|
||||
|
||||
run(command)
|
||||
}
|
||||
|
||||
fn generate_fec_image(avbroot: &Path, input_dir: &Path, output_file: &Path) -> Result<()> {
|
||||
let mut command = Command::new(avbroot);
|
||||
command.arg("fec");
|
||||
command.arg("generate");
|
||||
command.arg("-i");
|
||||
command.arg("input.img");
|
||||
command.arg("-f");
|
||||
command.arg(output_file);
|
||||
command.current_dir(input_dir);
|
||||
|
||||
run(command)
|
||||
}
|
||||
|
||||
fn generate_lp_image(avbroot: &Path, input_dir: &Path, output_file: &Path) -> Result<()> {
|
||||
let mut command = Command::new(avbroot);
|
||||
command.arg("lp");
|
||||
command.arg("pack");
|
||||
command.arg("-q");
|
||||
command.arg("-o");
|
||||
command.arg(output_file);
|
||||
command.current_dir(input_dir);
|
||||
|
||||
run(command)
|
||||
}
|
||||
|
||||
fn generate_sparse_image(avbroot: &Path, input_dir: &Path, output_file: &Path) -> Result<()> {
|
||||
let mut command = Command::new(avbroot);
|
||||
command.arg("sparse");
|
||||
command.arg("pack");
|
||||
command.arg("-q");
|
||||
command.arg("-i");
|
||||
command.arg("input.img");
|
||||
command.arg("-o");
|
||||
command.arg(output_file);
|
||||
command.current_dir(input_dir);
|
||||
|
||||
run(command)
|
||||
}
|
||||
|
||||
type Generator = fn(&Path, &Path, &Path) -> Result<()>;
|
||||
|
||||
fn generate_corpus(
|
||||
avbroot: &Path,
|
||||
input_dir: &Path,
|
||||
output_dir: &Path,
|
||||
generator: Generator,
|
||||
suffix: &str,
|
||||
) -> Result<()> {
|
||||
fs::create_dir_all(output_dir)
|
||||
.with_context(|| format!("Failed to create directory: {output_dir:?}"))?;
|
||||
|
||||
let parent = fs::read_dir(input_dir)
|
||||
.with_context(|| format!("Failed to open directory: {input_dir:?}"))?;
|
||||
|
||||
for entry in parent {
|
||||
let entry = entry.with_context(|| format!("Failed to list directory: {input_dir:?}"))?;
|
||||
let entry_input_dir = entry.path();
|
||||
let mut entry_output_file = output_dir.join(entry.file_name());
|
||||
entry_output_file.as_mut_os_string().push(suffix);
|
||||
|
||||
println!("{entry_input_dir:?} -> {entry_output_file:?}");
|
||||
|
||||
generator(avbroot, &entry_input_dir, &entry_output_file)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn fuzz_corpus_subcommand() -> Result<()> {
|
||||
let fuzz_dir = Path::new(WORKSPACE_DIR).join("fuzz");
|
||||
let corpus = fuzz_dir.join("corpus");
|
||||
let hfuzz_workspace = fuzz_dir.join("hfuzz_workspace");
|
||||
|
||||
let avbroot = build_avbroot()?;
|
||||
|
||||
let items: [(Generator, _, _); _] = [
|
||||
(generate_avb_image, "avb", ".img"),
|
||||
(generate_boot_image, "bootimage", ".img"),
|
||||
(generate_cpio_image, "cpio", ".cpio"),
|
||||
(generate_fec_image, "fec", ".fec"),
|
||||
(generate_lp_image, "lp", ".img"),
|
||||
(generate_sparse_image, "sparse", ".img"),
|
||||
];
|
||||
|
||||
for (generator, name, suffix) in items {
|
||||
let input_dir = corpus.join(name);
|
||||
let mut output_dir = hfuzz_workspace.join(name);
|
||||
output_dir.push("input");
|
||||
|
||||
generate_corpus(&avbroot, &input_dir, &output_dir, generator, suffix)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
+5
-1
@@ -1,7 +1,8 @@
|
||||
// SPDX-FileCopyrightText: 2023 Andrew Gunnerson
|
||||
// SPDX-FileCopyrightText: 2023-2026 Andrew Gunnerson
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
mod changelog;
|
||||
mod fuzz_corpus;
|
||||
mod version;
|
||||
|
||||
use anyhow::Result;
|
||||
@@ -16,6 +17,7 @@ fn main() -> Result<()> {
|
||||
|
||||
match cli.command {
|
||||
Command::SetVersion(c) => version::set_version_subcommand(&c),
|
||||
Command::FuzzCorpus => fuzz_corpus::fuzz_corpus_subcommand(),
|
||||
Command::UpdateChangelog => changelog::update_changelog_subcommand(),
|
||||
}
|
||||
}
|
||||
@@ -23,6 +25,8 @@ fn main() -> Result<()> {
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum Command {
|
||||
SetVersion(SetVersionCli),
|
||||
/// Generate initial fuzzing corpus.
|
||||
FuzzCorpus,
|
||||
/// Update links in CHANGELOG.md.
|
||||
UpdateChangelog,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user