mirror of
https://github.com/chenxiaolong/avbroot.git
synced 2026-07-03 14:05:11 +02:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2bac85f080 | |||
| fa99a3bb98 | |||
| 0d5bc574b2 | |||
| e34c48c92b | |||
| 8ea08ef98c | |||
| 779b1116e1 | |||
| 821c5fe088 | |||
| 6a1da333eb | |||
| 256483d248 | |||
| 2314c371a8 | |||
| 59cf37faaf | |||
| 36269acd7b | |||
| 6aacc5a76c | |||
| bd4ebde403 | |||
| ffdae0bf88 | |||
| 8282f8087c | |||
| 6b11cd8af2 | |||
| f610f3b794 | |||
| b8d022d52c | |||
| cd38217111 | |||
| c8d548d224 | |||
| 9ad430ac7f |
@@ -83,7 +83,7 @@ jobs:
|
||||
done
|
||||
|
||||
- name: Cache Rust dependencies
|
||||
uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
|
||||
uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
|
||||
with:
|
||||
key: ${{ matrix.artifact.name }}
|
||||
|
||||
|
||||
@@ -13,4 +13,4 @@ jobs:
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Run cargo-deny
|
||||
uses: EmbarkStudios/cargo-deny-action@34899fc7ba81ca6268d5947a7a16b4649013fea1 # v2.0.11
|
||||
uses: EmbarkStudios/cargo-deny-action@30f817c6f72275c6d54dc744fbca09ebc958599f # v2.0.12
|
||||
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Create release
|
||||
uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2
|
||||
uses: softprops/action-gh-release@72f2c25fcb47643c292f7107632f7a47c1df5cd8 # v2.3.2
|
||||
with:
|
||||
tag_name: v${{ steps.get_version.outputs.version }}
|
||||
name: Version ${{ steps.get_version.outputs.version }}
|
||||
|
||||
@@ -7,6 +7,30 @@
|
||||
to update the actual links at the bottom of the file.
|
||||
-->
|
||||
|
||||
### Version 3.19.0
|
||||
|
||||
* Allow verifying hashes of unsigned images with `avbroot avb verify` ([PR #477])
|
||||
* Warn when verifying image containing insecure flags field with `avbroot avb verify` ([PR #478])
|
||||
* Force re-signing of signed images in `avbroot avb pack` when fields were changed externally ([PR #479])
|
||||
|
||||
### Version 3.18.1
|
||||
|
||||
* Fix output file corruption in `avbroot sparse unpack` when unpacking a sparse file with holes larger than 2^32 ([Issue #472], [PR #476])
|
||||
|
||||
### Version 3.18.0
|
||||
|
||||
* Make OTA metadata property file field validation more lenient ([Issue #469], [PR #470])
|
||||
* Fixes `avbroot ota verify` for stock OTAs that include extra zip file entries in the metadata
|
||||
* Remove automatic promotion of insecure SHA-1 AVB hash algorithm to SHA-256 ([Issue #366], [Issue #469], [PR #473])
|
||||
* There are insecure devices that don't support SHA-256 and won't boot with it.
|
||||
* The original feature was a bandaid for OnePlus devices to make them a tiny bit more secure. They used SHA-256 for every partition except `system`. However, OnePlus no longer supports custom AVB keys anyway, so this feature is going away.
|
||||
* Add support for Magisk 30200 ([PR #474])
|
||||
* Update dependencies ([PR #475])
|
||||
|
||||
### Version 3.17.2
|
||||
|
||||
* Add support for Magisk 30100 ([PR #468])
|
||||
|
||||
### Version 3.17.1
|
||||
|
||||
* Update end-to-end tests to place streaming and seekable OTAs in separate directories for easier troubleshooting ([PR #463])
|
||||
@@ -366,6 +390,8 @@ Behind-the-scenes changes:
|
||||
[Issue #433]: https://github.com/chenxiaolong/avbroot/issues/433
|
||||
[Issue #441]: https://github.com/chenxiaolong/avbroot/issues/441
|
||||
[Issue #451]: https://github.com/chenxiaolong/avbroot/issues/451
|
||||
[Issue #469]: https://github.com/chenxiaolong/avbroot/issues/469
|
||||
[Issue #472]: https://github.com/chenxiaolong/avbroot/issues/472
|
||||
[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
|
||||
@@ -532,3 +558,12 @@ Behind-the-scenes changes:
|
||||
[PR #463]: https://github.com/chenxiaolong/avbroot/pull/463
|
||||
[PR #464]: https://github.com/chenxiaolong/avbroot/pull/464
|
||||
[PR #467]: https://github.com/chenxiaolong/avbroot/pull/467
|
||||
[PR #468]: https://github.com/chenxiaolong/avbroot/pull/468
|
||||
[PR #470]: https://github.com/chenxiaolong/avbroot/pull/470
|
||||
[PR #473]: https://github.com/chenxiaolong/avbroot/pull/473
|
||||
[PR #474]: https://github.com/chenxiaolong/avbroot/pull/474
|
||||
[PR #475]: https://github.com/chenxiaolong/avbroot/pull/475
|
||||
[PR #476]: https://github.com/chenxiaolong/avbroot/pull/476
|
||||
[PR #477]: https://github.com/chenxiaolong/avbroot/pull/477
|
||||
[PR #478]: https://github.com/chenxiaolong/avbroot/pull/478
|
||||
[PR #479]: https://github.com/chenxiaolong/avbroot/pull/479
|
||||
|
||||
Generated
+90
-73
@@ -36,9 +36,9 @@ checksum = "e9d4ee0d472d1cd2e28c97dfa124b3d8d992e10eb0a035f33f5d12e3a177ba3b"
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.19"
|
||||
version = "0.6.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933"
|
||||
checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
@@ -66,22 +66,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.3"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9"
|
||||
checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.9"
|
||||
version = "3.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882"
|
||||
checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -113,7 +113,7 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "avbroot"
|
||||
version = "3.17.1"
|
||||
version = "3.19.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"assert_matches",
|
||||
@@ -225,9 +225,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.18.1"
|
||||
version = "3.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee"
|
||||
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
@@ -304,9 +304,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.27"
|
||||
version = "1.2.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc"
|
||||
checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
@@ -337,9 +337,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.40"
|
||||
version = "4.5.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f"
|
||||
checksum = "50fd97c9dc2399518aa331917ac6f274280ec5eb34e555dd291899745c48ec6f"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -347,9 +347,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.40"
|
||||
version = "4.5.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e"
|
||||
checksum = "c35b5830294e1fa0462034af85cc95225a4cb07092c088c55bda3147cfcd8f65"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -359,23 +359,23 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_complete"
|
||||
version = "4.5.54"
|
||||
version = "4.5.55"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aad5b1b4de04fead402672b48897030eec1f3bfe1550776322f59f6d6e6a5677"
|
||||
checksum = "a5abde44486daf70c5be8b8f8f1b66c49f86236edf6fa2abadb4d961c4c6229a"
|
||||
dependencies = [
|
||||
"clap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.40"
|
||||
version = "4.5.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce"
|
||||
checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -425,9 +425,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.4.2"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
|
||||
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
@@ -533,7 +533,7 @@ checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -544,7 +544,7 @@ checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -567,7 +567,7 @@ checksum = "ecb08c4819242b1ec89b3d0c6affa229005bef46ae4f7eed8b80768187c10087"
|
||||
|
||||
[[package]]
|
||||
name = "e2e"
|
||||
version = "3.17.1"
|
||||
version = "3.19.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"avbroot",
|
||||
@@ -662,7 +662,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "fuzz"
|
||||
version = "3.17.1"
|
||||
version = "3.19.0"
|
||||
dependencies = [
|
||||
"avbroot",
|
||||
"honggfuzz",
|
||||
@@ -775,9 +775,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.9.0"
|
||||
version = "2.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
|
||||
checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
@@ -861,9 +861,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libbz2-rs-sys"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "775bf80d5878ab7c2b1080b5351a48b2f737d9f6f8b383574eebcc22be0dfccb"
|
||||
checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
@@ -940,7 +940,7 @@ dependencies = [
|
||||
"quote",
|
||||
"regex-syntax",
|
||||
"rustc_version",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -975,9 +975,9 @@ checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
|
||||
|
||||
[[package]]
|
||||
name = "memmap2"
|
||||
version = "0.9.5"
|
||||
version = "0.9.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f"
|
||||
checksum = "483758ad303d734cec05e5c12b41d7e93e6a6390c5e9dae6bdeb7c1259012d28"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
@@ -1001,7 +1001,7 @@ checksum = "db5b29714e950dbb20d5e6f74f9dcec4edbcc1067bb7f8ed198c097b8c1a818b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1176,7 +1176,7 @@ dependencies = [
|
||||
"phf_shared",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1249,12 +1249,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.34"
|
||||
version = "0.2.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6837b9e10d61f45f987d50808f83d1ee3d206c66acf650c3e4ae2e1f6ddedf55"
|
||||
checksum = "ff24dfcda44452b9816fff4cd4227e1bb73ff5a2f1bc1105aa92fb8565ce44d2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1292,7 +1292,7 @@ dependencies = [
|
||||
"prost",
|
||||
"prost-types",
|
||||
"regex",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
@@ -1306,14 +1306,14 @@ dependencies = [
|
||||
"itertools",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost-reflect"
|
||||
version = "0.16.0"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e6b545c556471033fc1099868fca468bff8cf034f7bb9153b445f24d00aa28d"
|
||||
checksum = "3c9ae1e4084d9737646934f5ac36a8d77f9a82f962f43e267a382fde4f2a903d"
|
||||
dependencies = [
|
||||
"logos",
|
||||
"miette",
|
||||
@@ -1498,15 +1498,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.0.7"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
|
||||
checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1568,14 +1568,14 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.9"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
|
||||
checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@@ -1692,9 +1692,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.103"
|
||||
version = "2.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8"
|
||||
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1731,7 +1731,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1761,37 +1761,47 @@ checksum = "2d2e76690929402faae40aebdda620a2c0e25dd6d3b9afe48867dfd95991f4bd"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.11"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
|
||||
checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.27"
|
||||
version = "0.23.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
|
||||
checksum = "17d3b47e6b7a040216ae5302712c94d1cf88c95b47efa80e2c59ce96c878267e"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_write",
|
||||
"toml_parser",
|
||||
"toml_writer",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_write"
|
||||
version = "0.1.2"
|
||||
name = "toml_parser"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
|
||||
checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10"
|
||||
dependencies = [
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_writer"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64"
|
||||
|
||||
[[package]]
|
||||
name = "topological-sort"
|
||||
@@ -1818,7 +1828,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1952,7 +1962,7 @@ dependencies = [
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -1974,7 +1984,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@@ -2010,6 +2020,12 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
@@ -2034,7 +2050,7 @@ version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||
dependencies = [
|
||||
"windows-targets 0.53.2",
|
||||
"windows-targets 0.53.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2055,10 +2071,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.2"
|
||||
version = "0.53.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef"
|
||||
checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows_aarch64_gnullvm 0.53.0",
|
||||
"windows_aarch64_msvc 0.53.0",
|
||||
"windows_i686_gnu 0.53.0",
|
||||
@@ -2167,9 +2184,9 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.11"
|
||||
version = "0.7.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd"
|
||||
checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@@ -2209,7 +2226,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "xtask"
|
||||
version = "3.17.1"
|
||||
version = "3.19.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -2234,7 +2251,7 @@ checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2254,7 +2271,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ members = ["avbroot", "e2e", "fuzz", "xtask"]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
version = "3.17.1"
|
||||
version = "3.19.0"
|
||||
license = "GPL-3.0-only"
|
||||
edition = "2024"
|
||||
repository = "https://github.com/chenxiaolong/avbroot"
|
||||
|
||||
@@ -30,8 +30,6 @@ This subcommand packs a new AVB image from the `avb.toml` file and, for appended
|
||||
* To force an image to be signed, use `--key <path> --force`.
|
||||
* To force an image to be unsigned, use `--force` without specifying `--key`.
|
||||
|
||||
Note that if the image is an appended image and its hash or hash tree descriptor uses an insecure algorithm, like `sha1`, then it will automatically be promoted to `sha256`.
|
||||
|
||||
By default, for appended vbmeta images, the output image size will match the size of the original image that was unpacked. This size is specified by the `image_size` field in `avb.toml`. If the image is resizable (eg. `system`), then passing in `--recompute-size` will cause the `image_size` field to be ignored and the smallest possible output file that fits the raw image and AVB metadata will be built. This avoids wasting space if `raw.img` shrunk or allows the packing to work at all if `raw.img` grew. **Do not use this option for non-resizable images** (eg. `boot`) or else the device won't be able to boot.
|
||||
|
||||
When packing an image, several of the fields in `avb.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 `avb.toml` if desired.
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ sha1 = "0.10.5"
|
||||
sha2 = "0.10.7"
|
||||
tempfile = "3.8.0"
|
||||
thiserror = "2.0.3"
|
||||
toml_edit = { version = "0.22.9", features = ["serde"] }
|
||||
toml_edit = { version = "0.23.3", features = ["serde"] }
|
||||
topological-sort = "0.2.2"
|
||||
tracing = "0.1.40"
|
||||
tracing-subscriber = "0.3.18"
|
||||
|
||||
+5
-16
@@ -95,18 +95,6 @@ fn write_info(path: &Path, info: &AvbInfo) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Packing with insecure algorithms is intentionally not supported, so promote
|
||||
/// to a secure algorithm if needed.
|
||||
fn promote_insecure_hash_algorithm(algorithm: &mut String) {
|
||||
const INSECURE_ALGORITHMS: &[&str] = &["sha1"];
|
||||
const NEW_ALGORITHM: &str = "sha256";
|
||||
|
||||
if INSECURE_ALGORITHMS.contains(&algorithm.as_str()) {
|
||||
warn!("Changing insecure hash algorithm {algorithm} to {NEW_ALGORITHM}");
|
||||
NEW_ALGORITHM.clone_into(algorithm);
|
||||
}
|
||||
}
|
||||
|
||||
/// Copy `size` bytes from `reader` into a new file `path` that's opened as
|
||||
/// both readable and writable.
|
||||
fn write_raw(
|
||||
@@ -194,13 +182,11 @@ fn write_raw_and_update(
|
||||
|
||||
match info.header.appended_descriptor_mut()? {
|
||||
AppendedDescriptorMut::HashTree(d) => {
|
||||
promote_insecure_hash_algorithm(&mut d.hash_algorithm);
|
||||
d.image_size = image_size;
|
||||
d.update(&raw_file, &raw_file, None, cancel_signal)
|
||||
.context("Failed to update hash tree descriptor")?;
|
||||
}
|
||||
AppendedDescriptorMut::Hash(d) => {
|
||||
promote_insecure_hash_algorithm(&mut d.hash_algorithm);
|
||||
d.image_size = image_size;
|
||||
raw_file.rewind()?;
|
||||
d.update(&mut raw_file, cancel_signal)
|
||||
@@ -345,7 +331,7 @@ fn sign_or_clear(info: &mut AvbInfo, orig_header: &Header, key_group: &KeyGroup)
|
||||
} else {
|
||||
SignAction::Clear
|
||||
}
|
||||
} else if originally_signed && &info.header != orig_header {
|
||||
} else if originally_signed && (&info.header != orig_header || info.header.verify().is_err()) {
|
||||
SignAction::Sign
|
||||
} else {
|
||||
// If the original image was signed, we can preserve the existing
|
||||
@@ -477,6 +463,10 @@ pub fn verify_headers(
|
||||
info!("{name} has an unsigned vbmeta header");
|
||||
}
|
||||
|
||||
if header.flags != 0 {
|
||||
warn!("{name} has insecure flags: {:#x}", header.flags);
|
||||
}
|
||||
|
||||
for descriptor in &header.descriptors {
|
||||
let Some(target_name) = descriptor.partition_name() else {
|
||||
continue;
|
||||
@@ -745,7 +735,6 @@ fn repack_subcommand(cli: &RepackCli, cancel_signal: &AtomicBool) -> Result<()>
|
||||
// Write new hash tree and FEC data instead of copying the original.
|
||||
// There could have been errors in the original FEC data itself.
|
||||
if let AppendedDescriptorMut::HashTree(d) = info.header.appended_descriptor_mut()? {
|
||||
promote_insecure_hash_algorithm(&mut d.hash_algorithm);
|
||||
d.update(&file, &file, None, cancel_signal)?;
|
||||
}
|
||||
|
||||
|
||||
+17
-34
@@ -5,7 +5,6 @@ use std::{
|
||||
borrow::Cow,
|
||||
collections::{BTreeSet, HashMap, HashSet},
|
||||
ffi::{OsStr, OsString},
|
||||
fmt::Display,
|
||||
fs::{self, File},
|
||||
io::{self, BufReader, BufWriter, Read, Seek, SeekFrom, Write},
|
||||
ops::Range,
|
||||
@@ -53,28 +52,6 @@ use crate::{
|
||||
util,
|
||||
};
|
||||
|
||||
fn joined(into_iter: impl IntoIterator<Item = impl Display>) -> String {
|
||||
use std::fmt::Write;
|
||||
|
||||
let mut result = String::new();
|
||||
|
||||
for (i, item) in into_iter.into_iter().enumerate() {
|
||||
if i > 0 {
|
||||
result.push_str(", ");
|
||||
}
|
||||
|
||||
write!(result, "{item}").expect("Failed to allocate");
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn sorted<T: Ord>(iter: impl Iterator<Item = T>) -> Vec<T> {
|
||||
let mut items = iter.collect::<Vec<_>>();
|
||||
items.sort();
|
||||
items
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
@@ -226,7 +203,7 @@ fn patch_boot_images(
|
||||
|
||||
info!(
|
||||
"Candidate boot images: {}",
|
||||
joined(sorted(boot_partitions.iter())),
|
||||
util::join(util::sort(boot_partitions.iter()), ", "),
|
||||
);
|
||||
|
||||
boot::patch_boot_images(
|
||||
@@ -249,7 +226,7 @@ fn patch_boot_images(
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"Failed to patch boot images: {}",
|
||||
joined(sorted(boot_partitions.iter())),
|
||||
util::join(util::sort(boot_partitions.iter()), ", "),
|
||||
)
|
||||
})?;
|
||||
|
||||
@@ -369,7 +346,7 @@ fn ensure_partitions_protected(
|
||||
if !missing.is_empty() {
|
||||
bail!(
|
||||
"Found critical partitions that are not protected by AVB: {}",
|
||||
joined(missing),
|
||||
util::join(missing, ", "),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -412,7 +389,10 @@ fn get_vbmeta_patch_order(
|
||||
}
|
||||
|
||||
if !missing.is_empty() {
|
||||
warn!("Partitions aren't protected by AVB: {}", joined(missing));
|
||||
warn!(
|
||||
"Partitions aren't protected by AVB: {}",
|
||||
util::join(missing, ", "),
|
||||
);
|
||||
}
|
||||
|
||||
// Ensure that there's only a single root of trust. Otherwise, there could
|
||||
@@ -428,7 +408,10 @@ fn get_vbmeta_patch_order(
|
||||
|
||||
// For zero roots, let TopologicalSort report the cycle.
|
||||
if roots.len() > 1 {
|
||||
bail!("Found multiple root vbmeta images: {}", joined(roots));
|
||||
bail!(
|
||||
"Found multiple root vbmeta images: {}",
|
||||
util::join(roots, ", "),
|
||||
);
|
||||
}
|
||||
|
||||
// Compute the patching order. This only includes vbmeta images. All vbmeta
|
||||
@@ -672,7 +655,7 @@ fn update_vbmeta_headers(
|
||||
) -> Result<()> {
|
||||
info!(
|
||||
"Patching vbmeta images: {}",
|
||||
joined(order.iter().map(|(n, _)| n)),
|
||||
util::join(order.iter().map(|(n, _)| n), ", "),
|
||||
);
|
||||
|
||||
for (name, deps) in order {
|
||||
@@ -1142,7 +1125,7 @@ fn patch_ota_zip(
|
||||
}
|
||||
|
||||
if !missing.is_empty() {
|
||||
bail!("Missing entries in OTA zip: {}", joined(missing));
|
||||
bail!("Missing entries in OTA zip: {}", util::join(missing, ", "));
|
||||
} else if !paths.contains(ota::PATH_METADATA) && !paths.contains(ota::PATH_METADATA_PB) {
|
||||
bail!(
|
||||
"Neither legacy nor protobuf OTA metadata files exist: {:?}, {:?}",
|
||||
@@ -1274,7 +1257,7 @@ fn patch_ota_zip(
|
||||
let size = writer.stream_position()?;
|
||||
|
||||
entries.push(ZipEntry {
|
||||
name: path.clone(),
|
||||
path: path.clone(),
|
||||
offset,
|
||||
size,
|
||||
});
|
||||
@@ -1323,7 +1306,7 @@ pub fn extract_payload(
|
||||
}
|
||||
}
|
||||
|
||||
info!("Extracting from the payload: {}", joined(images));
|
||||
info!("Extracting from the payload: {}", util::join(images, ", "));
|
||||
|
||||
// Pre-open all output files.
|
||||
let output_files = images
|
||||
@@ -1676,7 +1659,7 @@ pub fn extract_subcommand(cli: &ExtractCli, cancel_signal: &AtomicBool) -> Resul
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if !missing_images.is_empty() {
|
||||
bail!("Invalid partitions: {}", joined(missing_images));
|
||||
bail!("Invalid partitions: {}", util::join(missing_images, ", "));
|
||||
}
|
||||
|
||||
unique_images.extend(cli.extract.partition.iter().cloned());
|
||||
@@ -1936,7 +1919,7 @@ pub fn verify_subcommand(cli: &VerifyCli, cancel_signal: &AtomicBool) -> Result<
|
||||
.with_context(|| format!("Failed to parse property files: {}", ota::PF_NAME))?;
|
||||
let pf_payload = pfs
|
||||
.iter()
|
||||
.find(|pf| pf.name == ota::PATH_PAYLOAD)
|
||||
.find(|pf| pf.name() == ota::PATH_PAYLOAD)
|
||||
.ok_or_else(|| anyhow!("Missing property files entry: {}", ota::PATH_PAYLOAD))?;
|
||||
|
||||
let section_reader = SectionReader::new(&mut reader, pf_payload.offset, pf_payload.size)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: 2024 Andrew Gunnerson
|
||||
// SPDX-FileCopyrightText: 2024-2025 Andrew Gunnerson
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use std::{
|
||||
@@ -317,11 +317,11 @@ fn unpack_subcommand(
|
||||
})?;
|
||||
}
|
||||
ChunkData::Hole => {
|
||||
// This cannot overflow.
|
||||
let to_skip = chunk.bounds.len() * metadata.header.block_size;
|
||||
// Unlike ChunkData::Data, this can overflow a u32.
|
||||
let to_skip = i64::from(chunk.bounds.len()) * i64::from(metadata.header.block_size);
|
||||
|
||||
writer
|
||||
.seek(SeekFrom::Current(to_skip.into()))
|
||||
.seek(SeekFrom::Current(to_skip))
|
||||
.with_context(|| format!("Failed to seek file: {:?}", cli.output))?;
|
||||
}
|
||||
ChunkData::Crc32(_) => {}
|
||||
|
||||
+15
-12
@@ -163,9 +163,9 @@ pub enum Error {
|
||||
|
||||
type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
pub(crate) fn digest_algorithm(name: &str, for_verify: bool) -> Result<&'static Algorithm> {
|
||||
pub(crate) fn digest_algorithm(name: &str) -> Result<&'static Algorithm> {
|
||||
match name {
|
||||
"sha1" if for_verify => Ok(&ring::digest::SHA1_FOR_LEGACY_USE_ONLY),
|
||||
"sha1" => Ok(&ring::digest::SHA1_FOR_LEGACY_USE_ONLY),
|
||||
"sha256" => Ok(&ring::digest::SHA256),
|
||||
"sha512" => Ok(&ring::digest::SHA512),
|
||||
a => Err(Error::UnsupportedHashAlgorithm(a.to_owned())),
|
||||
@@ -534,7 +534,7 @@ impl HashTreeDescriptor {
|
||||
ranges: Option<&[Range<u64>]>,
|
||||
cancel_signal: &AtomicBool,
|
||||
) -> Result<()> {
|
||||
let algorithm = digest_algorithm(&self.hash_algorithm, false)?;
|
||||
let algorithm = digest_algorithm(&self.hash_algorithm)?;
|
||||
let hash_tree = HashTree::new(self.data_block_size, algorithm, &self.salt);
|
||||
let (root_digest, hash_tree_data) = match ranges {
|
||||
Some(r) => {
|
||||
@@ -642,7 +642,7 @@ impl HashTreeDescriptor {
|
||||
) -> Result<()> {
|
||||
self.check_offsets()?;
|
||||
|
||||
let algorithm = digest_algorithm(&self.hash_algorithm, true)?;
|
||||
let algorithm = digest_algorithm(&self.hash_algorithm)?;
|
||||
|
||||
util::check_bounds(self.tree_size, ..=HASH_TREE_MAX_SIZE)
|
||||
.map_err(|e| Error::IntOutOfBounds("HashTree::tree_size", e))?;
|
||||
@@ -903,10 +903,9 @@ impl HashDescriptor {
|
||||
fn calculate(
|
||||
&self,
|
||||
reader: impl Read,
|
||||
for_verify: bool,
|
||||
cancel_signal: &AtomicBool,
|
||||
) -> Result<ring::digest::Digest> {
|
||||
let algorithm = digest_algorithm(&self.hash_algorithm, for_verify)?;
|
||||
let algorithm = digest_algorithm(&self.hash_algorithm)?;
|
||||
let mut context = Context::new(algorithm);
|
||||
context.update(&self.salt);
|
||||
|
||||
@@ -924,14 +923,14 @@ impl HashDescriptor {
|
||||
|
||||
/// Update the root hash from the input reader's contents.
|
||||
pub fn update(&mut self, reader: impl Read, cancel_signal: &AtomicBool) -> Result<()> {
|
||||
let digest = self.calculate(reader, false, cancel_signal)?;
|
||||
let digest = self.calculate(reader, cancel_signal)?;
|
||||
self.root_digest = digest.as_ref().to_vec();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Verify the root hash against the input reader.
|
||||
pub fn verify(&self, reader: impl Read, cancel_signal: &AtomicBool) -> Result<()> {
|
||||
let digest = self.calculate(reader, true, cancel_signal)?;
|
||||
let digest = self.calculate(reader, cancel_signal)?;
|
||||
|
||||
if self.root_digest != digest.as_ref() {
|
||||
return Err(Error::InvalidRootDigest {
|
||||
@@ -1759,16 +1758,20 @@ impl Header {
|
||||
/// and return the public key. If the header is not signed, then `None` is
|
||||
/// returned.
|
||||
pub fn verify(&self) -> Result<Option<RsaPublicKey>> {
|
||||
// Reconstruct the public key.
|
||||
let public_key = decode_public_key(&self.public_key)?;
|
||||
|
||||
if self.public_key.len() != self.algorithm_type.public_key_len() {
|
||||
return Err(Error::IncorrectKeySize(
|
||||
public_key.size(),
|
||||
self.public_key.len(),
|
||||
self.algorithm_type,
|
||||
));
|
||||
}
|
||||
|
||||
if self.algorithm_type == AlgorithmType::None {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
// Reconstruct the public key.
|
||||
let public_key = decode_public_key(&self.public_key)?;
|
||||
|
||||
let mut without_auth_writer = Cursor::new(Vec::new());
|
||||
self.to_writer_internal(&mut without_auth_writer, true)?;
|
||||
let without_auth = without_auth_writer.into_inner();
|
||||
|
||||
@@ -493,7 +493,7 @@ impl HashTreeImage {
|
||||
const VERSION: u16 = 1;
|
||||
|
||||
fn digest_algorithm(name: &str) -> Result<&'static Algorithm> {
|
||||
avb::digest_algorithm(name, false)
|
||||
avb::digest_algorithm(name)
|
||||
.map_err(|_| Error::UnsupportedHashAlgorithm(name.to_owned().into_bytes()))
|
||||
}
|
||||
|
||||
|
||||
+173
-62
@@ -2,11 +2,13 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
collections::BTreeMap,
|
||||
fmt,
|
||||
fmt::{self, Write as _},
|
||||
io::{self, Cursor, Read, Seek, SeekFrom, Write},
|
||||
iter,
|
||||
path::Path,
|
||||
str::FromStr,
|
||||
sync::atomic::AtomicBool,
|
||||
};
|
||||
|
||||
@@ -28,6 +30,7 @@ use crate::{
|
||||
},
|
||||
protobuf::build::tools::releasetools::{OtaMetadata, ota_metadata::OtaType},
|
||||
stream::{self, FromReader, HashingReader, HashingWriter, ReadFixedSizeExt},
|
||||
util,
|
||||
};
|
||||
|
||||
pub const PATH_METADATA: &str = "META-INF/com/android/metadata";
|
||||
@@ -73,8 +76,12 @@ pub enum Error {
|
||||
InvalidLegacyMetadataLine(String),
|
||||
#[error("Unsupported legacy metadata field: {key:?} = {value:?}")]
|
||||
UnsupportedLegacyMetadataField { key: String, value: String },
|
||||
#[error("Expected entry offsets {expected:?}, but have {actual:?}")]
|
||||
MismatchedPropertyFiles { expected: String, actual: String },
|
||||
#[error("Mismatched {key:?} entry offsets: zip only: {zip_only:?}, prop only: {prop_only:?}")]
|
||||
MismatchedPropertyFiles {
|
||||
key: String,
|
||||
zip_only: String,
|
||||
prop_only: String,
|
||||
},
|
||||
#[error("Property files {value:?} exceed {reserved} byte reserved space")]
|
||||
InsufficientReservedSpace { value: String, reserved: usize },
|
||||
#[error("Invalid property file entry: {0:?}")]
|
||||
@@ -283,16 +290,48 @@ fn serialize_metadata(metadata: &OtaMetadata) -> (String, Vec<u8>) {
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ZipEntry {
|
||||
pub name: String,
|
||||
pub path: String,
|
||||
pub offset: u64,
|
||||
pub size: u64,
|
||||
}
|
||||
|
||||
/// Parse OTA property files string.
|
||||
pub fn parse_property_files(data: &str) -> Result<Vec<ZipEntry>> {
|
||||
let mut result = vec![];
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct PropEntry {
|
||||
name: String,
|
||||
pub offset: u64,
|
||||
pub size: u64,
|
||||
}
|
||||
|
||||
for entry in data.trim_end().split(',') {
|
||||
impl PropEntry {
|
||||
pub fn new(path: &str, offset: u64, size: u64) -> Self {
|
||||
Self {
|
||||
name: property_file_name(path).to_owned(),
|
||||
offset,
|
||||
size,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&ZipEntry> for PropEntry {
|
||||
fn from(entry: &ZipEntry) -> Self {
|
||||
Self::new(&entry.path, entry.offset, entry.size)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PropEntry {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}:{}:{}", self.name, self.offset, self.size)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for PropEntry {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(entry: &str) -> Result<Self> {
|
||||
let mut pieces = entry.split(':');
|
||||
|
||||
let name = pieces
|
||||
@@ -312,17 +351,31 @@ pub fn parse_property_files(data: &str) -> Result<Vec<ZipEntry>> {
|
||||
return Err(Error::InvalidPropertyFileEntry(entry.to_owned()));
|
||||
}
|
||||
|
||||
result.push(ZipEntry { name, offset, size });
|
||||
Ok(Self { name, offset, size })
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse OTA property files string.
|
||||
pub fn parse_property_files(data: &str) -> Result<Vec<PropEntry>> {
|
||||
let mut result = vec![];
|
||||
|
||||
for entry in data.trim_end().split(',') {
|
||||
result.push(entry.parse()?);
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Get the filename for use in property files entries.
|
||||
fn property_file_name(path: &str) -> &str {
|
||||
path.rsplit_once('/').map_or(path, |p| p.1)
|
||||
}
|
||||
|
||||
/// Compute the property files entries listing the offsets and sizes to every
|
||||
/// zip entry.
|
||||
fn compute_property_files(
|
||||
pf_name: &str,
|
||||
entries: &[ZipEntry],
|
||||
entries: &[PropEntry],
|
||||
max_length: Option<usize>,
|
||||
want_pb: bool,
|
||||
) -> Result<String> {
|
||||
@@ -335,24 +388,26 @@ fn compute_property_files(
|
||||
// writes, so we reserve an additional byte to allow offsets <100 GB.
|
||||
const RESERVATION_SIZE: usize = 16;
|
||||
|
||||
let compute = |path: &'static str| -> Result<String> {
|
||||
let mut buf = String::new();
|
||||
|
||||
let mut append = |path: &'static str| -> Result<()> {
|
||||
let name = property_file_name(path);
|
||||
let entry = entries
|
||||
.iter()
|
||||
.find(|e| e.name == path)
|
||||
.find(|e| e.name == name)
|
||||
.ok_or(Error::MissingZipEntry(path))?;
|
||||
let name = path.rsplit_once('/').map_or(path, |p| p.1);
|
||||
|
||||
Ok(format!("{name}:{}:{}", entry.offset, entry.size))
|
||||
let _ = write!(&mut buf, "{entry},");
|
||||
|
||||
Ok(())
|
||||
};
|
||||
|
||||
let mut tokens = vec![];
|
||||
|
||||
if pf_name == PF_NAME {
|
||||
tokens.push(compute(NAME_PAYLOAD_METADATA)?);
|
||||
append(NAME_PAYLOAD_METADATA)?;
|
||||
}
|
||||
|
||||
for path in [PATH_PAYLOAD, PATH_PROPERTIES] {
|
||||
tokens.push(compute(path)?);
|
||||
append(path)?;
|
||||
}
|
||||
|
||||
for path in [
|
||||
@@ -361,44 +416,51 @@ fn compute_property_files(
|
||||
"care_map.txt",
|
||||
"compatibility.zip",
|
||||
] {
|
||||
if let Ok(token) = compute(path) {
|
||||
tokens.push(token);
|
||||
}
|
||||
// These are optional.
|
||||
let _ = append(path);
|
||||
}
|
||||
|
||||
if max_length.is_none() {
|
||||
tokens.push(format!("metadata:{}", " ".repeat(RESERVATION_SIZE)));
|
||||
buf.push_str(property_file_name(PATH_METADATA));
|
||||
buf.push(':');
|
||||
buf.extend(iter::repeat_n(' ', RESERVATION_SIZE));
|
||||
buf.push(',');
|
||||
|
||||
if want_pb {
|
||||
tokens.push(format!("metadata.pb:{}", " ".repeat(RESERVATION_SIZE)));
|
||||
buf.push_str(property_file_name(PATH_METADATA_PB));
|
||||
buf.push(':');
|
||||
buf.extend(iter::repeat_n(' ', RESERVATION_SIZE));
|
||||
buf.push(',');
|
||||
}
|
||||
} else {
|
||||
tokens.push(compute(PATH_METADATA)?);
|
||||
append(PATH_METADATA)?;
|
||||
if want_pb {
|
||||
tokens.push(compute(PATH_METADATA_PB)?);
|
||||
append(PATH_METADATA_PB)?;
|
||||
}
|
||||
}
|
||||
|
||||
let mut joined = tokens.join(",");
|
||||
// Strip final trailing comma.
|
||||
buf.pop();
|
||||
|
||||
if let Some(l) = max_length {
|
||||
if joined.len() > l {
|
||||
if buf.len() > l {
|
||||
return Err(Error::InsufficientReservedSpace {
|
||||
value: joined,
|
||||
value: buf,
|
||||
reserved: l,
|
||||
});
|
||||
}
|
||||
|
||||
let remain = l - joined.len();
|
||||
joined.extend(iter::repeat_n(' ', remain));
|
||||
let remain = l - buf.len();
|
||||
buf.extend(iter::repeat_n(' ', remain));
|
||||
}
|
||||
|
||||
Ok(joined)
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
// Add fake payload_metadata.bin entry, covering the header + header signature
|
||||
// regions of the payload.
|
||||
fn add_payload_metadata_entry(
|
||||
entries: &mut Vec<ZipEntry>,
|
||||
entries: &mut Vec<PropEntry>,
|
||||
payload_metadata_size: u64,
|
||||
) -> Result<()> {
|
||||
let payload_offset = entries
|
||||
@@ -406,11 +468,11 @@ fn add_payload_metadata_entry(
|
||||
.find(|e| e.name == PATH_PAYLOAD)
|
||||
.ok_or(Error::MissingZipEntry(PATH_PAYLOAD))?
|
||||
.offset;
|
||||
entries.push(ZipEntry {
|
||||
name: NAME_PAYLOAD_METADATA.to_owned(),
|
||||
offset: payload_offset,
|
||||
size: payload_metadata_size,
|
||||
});
|
||||
entries.push(PropEntry::new(
|
||||
NAME_PAYLOAD_METADATA,
|
||||
payload_offset,
|
||||
payload_metadata_size,
|
||||
));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -449,8 +511,8 @@ pub fn add_metadata(
|
||||
.last_modified_time(DateTime::default())
|
||||
.compression_method(CompressionMethod::Stored);
|
||||
|
||||
let mut zip_entries = zip_entries.to_owned();
|
||||
add_payload_metadata_entry(&mut zip_entries, payload_metadata_size)?;
|
||||
let mut prop_entries = zip_entries.iter().map(PropEntry::from).collect();
|
||||
add_payload_metadata_entry(&mut prop_entries, payload_metadata_size)?;
|
||||
|
||||
// Compute initial property files with reserved space as placeholders to
|
||||
// store the self-referential metadata entries later.
|
||||
@@ -458,7 +520,7 @@ pub fn add_metadata(
|
||||
for pf in [PF_NAME, PF_STREAMING_NAME] {
|
||||
metadata.property_files.insert(
|
||||
pf.to_owned(),
|
||||
compute_property_files(pf, &zip_entries, None, true)?,
|
||||
compute_property_files(pf, &prop_entries, None, true)?,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -485,23 +547,23 @@ pub fn add_metadata(
|
||||
.write_all(&modern_raw)
|
||||
.map_err(|e| Error::ZipEntryWrite(PATH_METADATA_PB, e))?;
|
||||
|
||||
zip_entries.push(ZipEntry {
|
||||
name: PATH_METADATA.to_owned(),
|
||||
offset: next_offset + legacy_offset,
|
||||
size: legacy_raw.len() as u64,
|
||||
});
|
||||
zip_entries.push(ZipEntry {
|
||||
name: PATH_METADATA_PB.to_owned(),
|
||||
offset: next_offset + modern_offset,
|
||||
size: modern_raw.len() as u64,
|
||||
});
|
||||
prop_entries.push(PropEntry::new(
|
||||
PATH_METADATA,
|
||||
next_offset + legacy_offset,
|
||||
legacy_raw.len() as u64,
|
||||
));
|
||||
prop_entries.push(PropEntry::new(
|
||||
PATH_METADATA_PB,
|
||||
next_offset + modern_offset,
|
||||
modern_raw.len() as u64,
|
||||
));
|
||||
|
||||
(next_offset + legacy_offset, next_offset + modern_offset)
|
||||
};
|
||||
|
||||
// Compute the final property files using the offsets of the fake entries.
|
||||
for (key, value) in &mut metadata.property_files {
|
||||
*value = compute_property_files(key, &zip_entries, Some(value.len()), true)?;
|
||||
*value = compute_property_files(key, &prop_entries, Some(value.len()), true)?;
|
||||
}
|
||||
|
||||
// Add the final metadata files to the real zip.
|
||||
@@ -542,24 +604,73 @@ pub fn verify_metadata(
|
||||
let entry = zip_reader
|
||||
.by_index(i)
|
||||
.map_err(|e| Error::ZipIndexOpen(i, e))?;
|
||||
zip_entries.push(ZipEntry {
|
||||
name: entry.name().to_owned(),
|
||||
offset: entry.data_start(),
|
||||
size: entry.size(),
|
||||
});
|
||||
|
||||
if entry.compression() != CompressionMethod::Stored {
|
||||
continue;
|
||||
}
|
||||
|
||||
zip_entries.push(PropEntry::new(
|
||||
entry.name(),
|
||||
entry.data_start(),
|
||||
entry.size(),
|
||||
));
|
||||
}
|
||||
|
||||
add_payload_metadata_entry(&mut zip_entries, payload_metadata_size)?;
|
||||
|
||||
let metadata_pb = zip_entries.iter().find(|e| e.name == PATH_METADATA_PB);
|
||||
zip_entries.sort_by(|a, b| a.name.cmp(&b.name));
|
||||
|
||||
for (key, value) in &metadata.property_files {
|
||||
let new_value =
|
||||
compute_property_files(key, &zip_entries, Some(value.len()), metadata_pb.is_some())?;
|
||||
if *value != new_value {
|
||||
let mut prop_entries = parse_property_files(value)?;
|
||||
prop_entries.sort_by(|a, b| a.name.cmp(&b.name));
|
||||
|
||||
// Check that this is a subset of the actual entries.
|
||||
let mut zip_iter = zip_entries.iter().peekable();
|
||||
let mut prop_iter = prop_entries.iter().peekable();
|
||||
let mut zip_only = vec![];
|
||||
let mut prop_only = vec![];
|
||||
|
||||
loop {
|
||||
match (zip_iter.peek(), prop_iter.peek()) {
|
||||
(Some(&zip), Some(&prop)) => match zip.name.cmp(&prop.name) {
|
||||
Ordering::Less => {
|
||||
// Exists in zip, but not in property files.
|
||||
zip_iter.next();
|
||||
}
|
||||
Ordering::Equal => {
|
||||
// If the zip had multiple files with the same filename,
|
||||
// but in different directories, this will fail.
|
||||
if zip != prop {
|
||||
zip_only.push(zip);
|
||||
prop_only.push(prop);
|
||||
}
|
||||
zip_iter.next();
|
||||
prop_iter.next();
|
||||
}
|
||||
Ordering::Greater => {
|
||||
// Exists in property files, but not in zip.
|
||||
prop_only.push(prop);
|
||||
prop_iter.next();
|
||||
}
|
||||
},
|
||||
(Some(_), None) => {
|
||||
// Exists in zip, but not in property files.
|
||||
zip_iter.next();
|
||||
}
|
||||
(None, Some(prop)) => {
|
||||
// Exists in property files, but not in zip.
|
||||
prop_only.push(prop);
|
||||
prop_iter.next();
|
||||
}
|
||||
(None, None) => break,
|
||||
}
|
||||
}
|
||||
|
||||
if !zip_only.is_empty() || !prop_only.is_empty() {
|
||||
return Err(Error::MismatchedPropertyFiles {
|
||||
expected: value.clone(),
|
||||
actual: new_value,
|
||||
key: key.clone(),
|
||||
zip_only: util::join(zip_only.into_iter().map(|e| e.to_string()), ","),
|
||||
prop_only: util::join(prop_only.into_iter().map(|e| e.to_string()), ","),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: 2024 Andrew Gunnerson
|
||||
// SPDX-FileCopyrightText: 2024-2025 Andrew Gunnerson
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use std::{
|
||||
@@ -374,6 +374,9 @@ impl fmt::Debug for ChunkData {
|
||||
/// metadata they contain.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Chunk {
|
||||
/// When [`Self::data`] is [`ChunkData::Data`], this is guaranteed to not
|
||||
/// exceed the bounds of [`u32`] when multiplied by [`Header::block_size`].
|
||||
/// For other types of data, a 64-bit signed or unsigned integer is needed.
|
||||
pub bounds: ChunkBounds,
|
||||
pub data: ChunkData,
|
||||
}
|
||||
|
||||
@@ -194,7 +194,7 @@ impl MagiskRootPatcher {
|
||||
// replaced by PREINITDEVICE
|
||||
// - Versions newer than the latest supported version are assumed to support
|
||||
// the same features as the latest version
|
||||
const VERS_SUPPORTED: &'static [Range<u32>] = &[25102..25207, 25211..30100];
|
||||
const VERS_SUPPORTED: &'static [Range<u32>] = &[25102..25207, 25211..30300];
|
||||
const VER_PREINIT_DEVICE: RangeFrom<u32> = 25211..;
|
||||
const VER_RANDOM_SEED: Range<u32> = 25211..26103;
|
||||
const VER_PATCH_VBMETA: Range<u32> = Self::VERS_SUPPORTED[0].start..26202;
|
||||
|
||||
@@ -190,23 +190,9 @@ pub fn patch_system_image(
|
||||
return Err(Error::OldZipNotFound);
|
||||
}
|
||||
|
||||
let update_ranges = if descriptor.hash_algorithm == "sha1" {
|
||||
// Promote to a secure algorithm. SHA1 is allowed for verification only.
|
||||
// The entire hash tree and FEC data will need to be recomputed.
|
||||
let new_algorithm = "sha256".to_owned();
|
||||
|
||||
debug!(
|
||||
"Changing insecure hash algorithm {} to {new_algorithm}",
|
||||
descriptor.hash_algorithm,
|
||||
);
|
||||
|
||||
descriptor.hash_algorithm = new_algorithm;
|
||||
None
|
||||
} else {
|
||||
// Only need to update the hash tree and FEC data corresponding to the
|
||||
// modified regions.
|
||||
Some(modified_ranges.as_slice())
|
||||
};
|
||||
// Only need to update the hash tree and FEC data corresponding to the
|
||||
// modified regions.
|
||||
let update_ranges = Some(modified_ranges.as_slice());
|
||||
|
||||
descriptor
|
||||
.update(input, output, update_ranges, cancel_signal)
|
||||
|
||||
+25
-2
@@ -1,9 +1,10 @@
|
||||
// SPDX-FileCopyrightText: 2023-2024 Andrew Gunnerson
|
||||
// SPDX-FileCopyrightText: 2023-2025 Andrew Gunnerson
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
fmt, mem,
|
||||
fmt::{self, Display},
|
||||
mem,
|
||||
ops::{
|
||||
Bound, Range, RangeBounds, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive,
|
||||
},
|
||||
@@ -378,6 +379,28 @@ where
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
pub fn join(into_iter: impl IntoIterator<Item = impl Display>, sep: &str) -> String {
|
||||
use std::fmt::Write;
|
||||
|
||||
let mut result = String::new();
|
||||
|
||||
for (i, item) in into_iter.into_iter().enumerate() {
|
||||
if i > 0 {
|
||||
result.push_str(sep);
|
||||
}
|
||||
|
||||
write!(result, "{item}").expect("Failed to allocate");
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn sort<T: Ord>(iter: impl Iterator<Item = T>) -> Vec<T> {
|
||||
let mut items = iter.collect::<Vec<_>>();
|
||||
items.sort();
|
||||
items
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
+1
-1
@@ -18,7 +18,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.22.9", features = ["serde"] }
|
||||
toml_edit = { version = "0.23.3", features = ["serde"] }
|
||||
topological-sort = "0.2.2"
|
||||
tracing = "0.1.40"
|
||||
tracing-subscriber = "0.3.18"
|
||||
|
||||
+1
-1
@@ -797,7 +797,7 @@ fn create_ota(
|
||||
let size = writer.stream_position()?;
|
||||
|
||||
entries.push(ZipEntry {
|
||||
name: path.to_owned(),
|
||||
path: path.to_owned(),
|
||||
offset,
|
||||
size,
|
||||
});
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ publish = false
|
||||
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.22.9"
|
||||
toml_edit = "0.23.3"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
Reference in New Issue
Block a user