mirror of
https://github.com/chenxiaolong/avbroot.git
synced 2026-07-03 14:05:11 +02:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5df2ffa435 | |||
| 16c1c2a4d3 | |||
| 1155a14b00 | |||
| 806870664a | |||
| 4717407a43 | |||
| ff4a2a6db8 | |||
| 49bd389bf3 | |||
| 5afe76bf2b | |||
| 0c8357924c | |||
| ef9e563010 | |||
| 2e53147f87 | |||
| 69903c4382 | |||
| 9987d21cef | |||
| 9802bba5f7 | |||
| 1b30328b01 | |||
| efb25e15dd | |||
| 2e5754e16c |
@@ -47,7 +47,7 @@ jobs:
|
||||
android_api: '31'
|
||||
steps:
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
# For git describe
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Run cargo-deny
|
||||
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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Create release
|
||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||
|
||||
@@ -7,6 +7,21 @@
|
||||
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])
|
||||
@@ -448,6 +463,7 @@ Behind-the-scenes changes:
|
||||
[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
|
||||
@@ -663,3 +679,10 @@ Behind-the-scenes changes:
|
||||
[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
+304
-126
@@ -80,9 +80,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.100"
|
||||
version = "1.0.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
|
||||
checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea"
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
@@ -104,7 +104,7 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "avbroot"
|
||||
version = "3.24.0"
|
||||
version = "3.25.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"assert_matches",
|
||||
@@ -165,9 +165,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
version = "1.8.2"
|
||||
version = "1.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d809780667f4410e7c41b07f52439b94d2bdf8528eeedc287fa38d3b7f95d82"
|
||||
checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
|
||||
|
||||
[[package]]
|
||||
name = "beef"
|
||||
@@ -192,14 +192,14 @@ dependencies = [
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
"syn 2.0.114",
|
||||
"syn 2.0.116",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.10.0"
|
||||
version = "2.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
|
||||
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
@@ -244,9 +244,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.11.0"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3"
|
||||
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
|
||||
|
||||
[[package]]
|
||||
name = "bzip2"
|
||||
@@ -268,9 +268,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.52"
|
||||
version = "1.2.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3"
|
||||
checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"shlex",
|
||||
@@ -320,9 +320,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.54"
|
||||
version = "4.5.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394"
|
||||
checksum = "63be97961acde393029492ce0be7a1af7e323e6bae9511ebfac33751be5e6806"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -330,9 +330,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.54"
|
||||
version = "4.5.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00"
|
||||
checksum = "7f13174bda5dfd69d7e947827e5af4b0f2f94a4a3ee92912fba07a66150f21e2"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -342,30 +342,30 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_complete"
|
||||
version = "4.5.65"
|
||||
version = "4.5.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "430b4dc2b5e3861848de79627b2bedc9f3342c7da5173a14eaa5d0f8dc18ae5d"
|
||||
checksum = "c757a3b7e39161a4e56f9365141ada2a6c915a8622c408ab6bb4b5d047371031"
|
||||
dependencies = [
|
||||
"clap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.49"
|
||||
version = "4.5.55"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671"
|
||||
checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.114",
|
||||
"syn 2.0.116",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.6"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d"
|
||||
checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831"
|
||||
|
||||
[[package]]
|
||||
name = "cms"
|
||||
@@ -408,9 +408,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crc"
|
||||
version = "3.3.0"
|
||||
version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675"
|
||||
checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d"
|
||||
dependencies = [
|
||||
"crc-catalog",
|
||||
]
|
||||
@@ -532,7 +532,7 @@ checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.114",
|
||||
"syn 2.0.116",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -567,7 +567,7 @@ checksum = "ecb08c4819242b1ec89b3d0c6affa229005bef46ae4f7eed8b80768187c10087"
|
||||
|
||||
[[package]]
|
||||
name = "e2e"
|
||||
version = "3.24.0"
|
||||
version = "3.25.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"avbroot",
|
||||
@@ -622,9 +622,9 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.7"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41"
|
||||
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
@@ -640,13 +640,13 @@ checksum = "b7ac824320a75a52197e8f2d787f6a38b6718bb6897a35142d749af3c0e8f4fe"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.1.5"
|
||||
version = "1.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb"
|
||||
checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"libz-rs-sys",
|
||||
"miniz_oxide",
|
||||
"zlib-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -655,9 +655,15 @@ version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
[[package]]
|
||||
name = "fuzz"
|
||||
version = "3.24.0"
|
||||
version = "3.25.0"
|
||||
dependencies = [
|
||||
"avbroot",
|
||||
"honggfuzz",
|
||||
@@ -675,9 +681,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.16"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
|
||||
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
@@ -686,14 +692,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.4"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
||||
checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"wasip2",
|
||||
"wasip3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -725,6 +732,15 @@ version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||
dependencies = [
|
||||
"foldhash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.1"
|
||||
@@ -768,6 +784,12 @@ dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "id-arena"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
@@ -781,7 +803,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
"hashbrown 0.16.1",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -818,6 +842,12 @@ dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
@@ -827,6 +857,12 @@ dependencies = [
|
||||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "leb128fmt"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
||||
|
||||
[[package]]
|
||||
name = "libbz2-rs-sys"
|
||||
version = "0.2.2"
|
||||
@@ -835,9 +871,9 @@ checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.180"
|
||||
version = "0.2.182"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
|
||||
checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
@@ -851,18 +887,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.15"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
|
||||
|
||||
[[package]]
|
||||
name = "libz-rs-sys"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c10501e7805cee23da17c7790e59df2870c0d4043ec6d03f67d31e2b53e77415"
|
||||
dependencies = [
|
||||
"zlib-rs",
|
||||
]
|
||||
checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
@@ -898,7 +925,7 @@ dependencies = [
|
||||
"quote",
|
||||
"regex-syntax",
|
||||
"rustc_version",
|
||||
"syn 2.0.114",
|
||||
"syn 2.0.116",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -921,9 +948,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lzma-rust2"
|
||||
version = "0.15.6"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17f7337d278fec032975dc884152491580dd23750ee957047856735fe0e61ede"
|
||||
checksum = "d673a11333485e7d8b93d62a9a5b07b22daf5e8a8655a44c1bb18aa4bf3d1524"
|
||||
dependencies = [
|
||||
"crc",
|
||||
"sha2",
|
||||
@@ -931,15 +958,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.6"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
||||
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||
|
||||
[[package]]
|
||||
name = "memmap2"
|
||||
version = "0.9.9"
|
||||
version = "0.9.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "744133e4a0e0a658e1374cf3bf8e415c4052a15a111acd372764c55b4177d490"
|
||||
checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
@@ -963,7 +990,7 @@ checksum = "db5b29714e950dbb20d5e6f74f9dcec4edbcc1067bb7f8ed198c097b8c1a818b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.114",
|
||||
"syn 2.0.116",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1095,11 +1122,12 @@ checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
||||
|
||||
[[package]]
|
||||
name = "passterm"
|
||||
version = "2.0.5"
|
||||
version = "2.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "150ca2316c7813c688677784f20bb0a9efab639415ae1961869863ee99a81e51"
|
||||
checksum = "9510c76e37ee20be1edfce1819a3169a7c1c67fab98bd35a33684f8886f5cf4d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1123,11 +1151,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "petgraph"
|
||||
version = "0.7.1"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772"
|
||||
checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455"
|
||||
dependencies = [
|
||||
"fixedbitset",
|
||||
"hashbrown 0.15.5",
|
||||
"indexmap",
|
||||
]
|
||||
|
||||
@@ -1162,7 +1191,7 @@ dependencies = [
|
||||
"phf_shared",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.114",
|
||||
"syn 2.0.116",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1234,23 +1263,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn 2.0.114",
|
||||
"syn 2.0.116",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.105"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost"
|
||||
version = "0.14.1"
|
||||
version = "0.14.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7231bd9b3d3d33c86b58adbac74b5ec0ad9f496b19d22801d773636feaa95f3d"
|
||||
checksum = "d2ea70524a2f82d518bce41317d0fae74151505651af45faf1ffbd6fd33f0568"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"prost-derive",
|
||||
@@ -1258,35 +1287,34 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "prost-build"
|
||||
version = "0.14.1"
|
||||
version = "0.14.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac6c3320f9abac597dcbc668774ef006702672474aad53c6d596b62e487b40b1"
|
||||
checksum = "343d3bd7056eda839b03204e68deff7d1b13aba7af2b2fd16890697274262ee7"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"itertools 0.14.0",
|
||||
"log",
|
||||
"multimap",
|
||||
"once_cell",
|
||||
"petgraph",
|
||||
"prettyplease",
|
||||
"prost",
|
||||
"prost-types",
|
||||
"regex",
|
||||
"syn 2.0.114",
|
||||
"syn 2.0.116",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost-derive"
|
||||
version = "0.14.1"
|
||||
version = "0.14.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9120690fafc389a67ba3803df527d0ec9cbbc9cc45e4cc20b332996dfb672425"
|
||||
checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.14.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.114",
|
||||
"syn 2.0.116",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1303,9 +1331,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "prost-types"
|
||||
version = "0.14.1"
|
||||
version = "0.14.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9b4db3d6da204ed77bb26ba83b6122a73aeb2e87e25fbf7ad2e84c4ccbf8f72"
|
||||
checksum = "8991c4cbdb8bc5b11f0b074ffe286c30e523de90fee5ba8132f1399f23cb3dd7"
|
||||
dependencies = [
|
||||
"prost",
|
||||
]
|
||||
@@ -1339,9 +1367,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.43"
|
||||
version = "1.0.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a"
|
||||
checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -1379,7 +1407,7 @@ version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom 0.2.16",
|
||||
"getrandom 0.2.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1410,9 +1438,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.12.2"
|
||||
version = "1.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
|
||||
checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -1422,9 +1450,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.13"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
|
||||
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -1433,9 +1461,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.8"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
|
||||
checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c"
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
@@ -1445,7 +1473,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"getrandom 0.2.16",
|
||||
"getrandom 0.2.17",
|
||||
"libc",
|
||||
"untrusted",
|
||||
"windows-sys 0.52.0",
|
||||
@@ -1554,7 +1582,20 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.114",
|
||||
"syn 2.0.116",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.149"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1621,9 +1662,9 @@ checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2"
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
|
||||
checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
@@ -1678,9 +1719,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.114"
|
||||
version = "2.0.116"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a"
|
||||
checksum = "3df424c70518695237746f84cede799c9c58fcb37450d7b23716568cc8bc69cb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1709,12 +1750,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.24.0"
|
||||
version = "3.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c"
|
||||
checksum = "0136791f7c95b1f6dd99f9cc786b91bb81c3800b639b3478e561ddb7be95e5f1"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom 0.3.4",
|
||||
"getrandom 0.4.1",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.61.2",
|
||||
@@ -1722,22 +1763,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.17"
|
||||
version = "2.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
|
||||
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.17"
|
||||
version = "2.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
|
||||
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.114",
|
||||
"syn 2.0.116",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1767,14 +1808,14 @@ checksum = "2d2e76690929402faae40aebdda620a2c0e25dd6d3b9afe48867dfd95991f4bd"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.114",
|
||||
"syn 2.0.116",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.9.11+spec-1.1.0"
|
||||
version = "1.0.1+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46"
|
||||
checksum = "bbe30f93627849fa362d4a602212d41bb237dc2bd0f8ba0b2ce785012e124220"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde_core",
|
||||
@@ -1787,18 +1828,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.7.5+spec-1.1.0"
|
||||
version = "1.0.0+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347"
|
||||
checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.24.0+spec-1.1.0"
|
||||
version = "0.25.1+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c740b185920170a6d9191122cafef7010bd6270a3824594bff6784c04d7f09e"
|
||||
checksum = "538ed8bc4ada0b2549f2a7be9654aac2dfa358faeabb97d7845c3eb5a297e5d6"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"toml_datetime",
|
||||
@@ -1809,9 +1850,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_parser"
|
||||
version = "1.0.6+spec-1.1.0"
|
||||
version = "1.0.8+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44"
|
||||
checksum = "0742ff5ff03ea7e67c8ae6c93cac239e0d9784833362da3f9a9c1da8dfefcbdc"
|
||||
dependencies = [
|
||||
"winnow",
|
||||
]
|
||||
@@ -1847,7 +1888,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.114",
|
||||
"syn 2.0.116",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1899,9 +1940,9 @@ checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.22"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
@@ -1909,6 +1950,12 @@ version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.9.0"
|
||||
@@ -1941,13 +1988,56 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
name = "wasip2"
|
||||
version = "1.0.1+wasi-0.2.4"
|
||||
version = "1.0.2+wasi-0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
|
||||
checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
|
||||
dependencies = [
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasip3"
|
||||
version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
|
||||
dependencies = [
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-encoder"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
|
||||
dependencies = [
|
||||
"leb128fmt",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-metadata"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indexmap",
|
||||
"wasm-encoder",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"hashbrown 0.15.5",
|
||||
"indexmap",
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
@@ -2047,9 +2137,91 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen"
|
||||
version = "0.46.0"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
|
||||
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
|
||||
dependencies = [
|
||||
"wit-bindgen-rust-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-core"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
"wit-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
"indexmap",
|
||||
"prettyplease",
|
||||
"syn 2.0.116",
|
||||
"wasm-metadata",
|
||||
"wit-bindgen-core",
|
||||
"wit-component",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust-macro"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.116",
|
||||
"wit-bindgen-core",
|
||||
"wit-bindgen-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-component"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags",
|
||||
"indexmap",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"wasm-encoder",
|
||||
"wasm-metadata",
|
||||
"wasmparser",
|
||||
"wit-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-parser"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"id-arena",
|
||||
"indexmap",
|
||||
"log",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"unicode-xid",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "x509-cert"
|
||||
@@ -2067,7 +2239,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "xtask"
|
||||
version = "3.24.0"
|
||||
version = "3.25.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -2077,22 +2249,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.33"
|
||||
version = "0.8.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd"
|
||||
checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.33"
|
||||
version = "0.8.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1"
|
||||
checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.114",
|
||||
"syn 2.0.116",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2112,11 +2284,17 @@ checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.114",
|
||||
"syn 2.0.116",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zlib-rs"
|
||||
version = "0.5.5"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40990edd51aae2c2b6907af74ffb635029d5788228222c4bb811e9351c0caad3"
|
||||
checksum = "a7948af682ccbc3342b6e9420e8c51c1fe5d7bf7756002b4a3c6cabfe96a7e3c"
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ members = ["avbroot", "e2e", "fuzz", "xtask"]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
version = "3.24.0"
|
||||
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.)
|
||||
|
||||
+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 = { version = "0.9.11", 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:?}"))?;
|
||||
|
||||
@@ -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>,
|
||||
|
||||
|
||||
+20
-12
@@ -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:?}"))?;
|
||||
@@ -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![];
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
+15
-16
@@ -1600,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");
|
||||
@@ -1940,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");
|
||||
|
||||
@@ -2193,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,
|
||||
|
||||
@@ -2206,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,
|
||||
|
||||
@@ -2221,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>,
|
||||
|
||||
@@ -2232,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>,
|
||||
|
||||
@@ -2243,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>,
|
||||
|
||||
@@ -2254,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>,
|
||||
|
||||
@@ -2273,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>,
|
||||
|
||||
@@ -2285,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>,
|
||||
|
||||
@@ -2294,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>,
|
||||
|
||||
@@ -2302,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,
|
||||
|
||||
@@ -2311,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,
|
||||
|
||||
@@ -2368,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,
|
||||
|
||||
@@ -2377,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>,
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
);
|
||||
|
||||
+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 = { version = "0.9.11", features = ["serde"] }
|
||||
toml = { version = "1.0.1", features = ["serde"] }
|
||||
topological-sort = "0.2.2"
|
||||
tracing = "0.1.40"
|
||||
tracing-subscriber = "0.3.18"
|
||||
|
||||
+4
-4
@@ -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)?;
|
||||
|
||||
|
||||
+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.24.0"
|
||||
toml_edit = "0.25.1"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
Reference in New Issue
Block a user