mirror of
https://github.com/chenxiaolong/avbroot.git
synced 2026-07-03 14:05:11 +02:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 07b6901d8e | |||
| b2b3d98005 | |||
| 21c1dc86ce | |||
| 462084ac18 | |||
| 4c3e1efd99 | |||
| 9b7f27e384 | |||
| 2120827755 | |||
| 8b33645026 | |||
| eafc6ad3b4 | |||
| 188d256d03 | |||
| c1836d01a4 | |||
| e8532870b1 | |||
| b854654cab | |||
| f68a492131 | |||
| c22a5d6eca | |||
| 2631228e8d | |||
| f2b5d9646e | |||
| 4991c5192e | |||
| 8ef9f89cdd |
@@ -89,7 +89,7 @@ jobs:
|
||||
done
|
||||
|
||||
- name: Cache Rust dependencies
|
||||
uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
|
||||
with:
|
||||
key: ${{ matrix.artifact.name }}
|
||||
|
||||
@@ -160,7 +160,7 @@ jobs:
|
||||
run: cp LICENSE README.md target/output/
|
||||
|
||||
- name: Archive executable
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: avbroot-${{ steps.get_version.outputs.version }}-${{ matrix.artifact.name }}
|
||||
path: |
|
||||
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Create release
|
||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||
uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1
|
||||
with:
|
||||
tag_name: v${{ steps.get_version.outputs.version }}
|
||||
name: Version ${{ steps.get_version.outputs.version }}
|
||||
|
||||
@@ -7,6 +7,28 @@
|
||||
to update the actual links at the bottom of the file.
|
||||
-->
|
||||
|
||||
### Version 3.29.1
|
||||
|
||||
* Increase memory limit for payload manifest size to 16 MiB ([Issue #593], [PR #594])
|
||||
|
||||
### Version 3.29.0
|
||||
|
||||
* Add support for unpacking/packing payloads at the same time as the OTA in `avbroot zip {unpack,pack}` ([Discussion #582], [PR #586])
|
||||
* Add new `--skeleton` option to `avbroot payload unpack` ([Discussion #582], [PR #587])
|
||||
|
||||
### Version 3.28.0
|
||||
|
||||
* Add support for unpacking metadata from delta payloads in `avbroot payload unpack` ([Discussion #582], [PR #584])
|
||||
|
||||
### Version 3.27.0
|
||||
|
||||
* Add support for LZMA compression for ramdisks ([Issue #578], [PR #580])
|
||||
* Update dependencies ([PR #579])
|
||||
|
||||
### Version 3.26.1
|
||||
|
||||
* Add support for Magisk 30700 ([PR #573])
|
||||
|
||||
### Version 3.26.0
|
||||
|
||||
* Add new `--re-sign` option to `avbroot ota patch` for re-signing partitions that would otherwise be unmodified ([Issue #569], [PR #570])
|
||||
@@ -432,6 +454,7 @@ Behind-the-scenes changes:
|
||||
[Discussion #294]: https://github.com/chenxiaolong/avbroot/discussions/294
|
||||
[Discussion #417]: https://github.com/chenxiaolong/avbroot/discussions/417
|
||||
[Discussion #426]: https://github.com/chenxiaolong/avbroot/discussions/426
|
||||
[Discussion #582]: https://github.com/chenxiaolong/avbroot/discussions/582
|
||||
[Issue #138]: https://github.com/chenxiaolong/avbroot/issues/138
|
||||
[Issue #144]: https://github.com/chenxiaolong/avbroot/issues/144
|
||||
[Issue #145]: https://github.com/chenxiaolong/avbroot/issues/145
|
||||
@@ -470,6 +493,8 @@ Behind-the-scenes changes:
|
||||
[Issue #537]: https://github.com/chenxiaolong/avbroot/issues/537
|
||||
[Issue #554]: https://github.com/chenxiaolong/avbroot/issues/554
|
||||
[Issue #569]: https://github.com/chenxiaolong/avbroot/issues/569
|
||||
[Issue #578]: https://github.com/chenxiaolong/avbroot/issues/578
|
||||
[Issue #593]: https://github.com/chenxiaolong/avbroot/issues/593
|
||||
[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
|
||||
@@ -694,3 +719,10 @@ Behind-the-scenes changes:
|
||||
[PR #565]: https://github.com/chenxiaolong/avbroot/pull/565
|
||||
[PR #570]: https://github.com/chenxiaolong/avbroot/pull/570
|
||||
[PR #571]: https://github.com/chenxiaolong/avbroot/pull/571
|
||||
[PR #573]: https://github.com/chenxiaolong/avbroot/pull/573
|
||||
[PR #579]: https://github.com/chenxiaolong/avbroot/pull/579
|
||||
[PR #580]: https://github.com/chenxiaolong/avbroot/pull/580
|
||||
[PR #584]: https://github.com/chenxiaolong/avbroot/pull/584
|
||||
[PR #586]: https://github.com/chenxiaolong/avbroot/pull/586
|
||||
[PR #587]: https://github.com/chenxiaolong/avbroot/pull/587
|
||||
[PR #594]: https://github.com/chenxiaolong/avbroot/pull/594
|
||||
|
||||
Generated
+102
-118
@@ -30,9 +30,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.21"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
|
||||
checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
@@ -45,15 +45,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.13"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
|
||||
checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.7"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
|
||||
checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
@@ -80,9 +80,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.101"
|
||||
version = "1.0.102"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea"
|
||||
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
@@ -104,7 +104,7 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "avbroot"
|
||||
version = "3.26.0"
|
||||
version = "3.29.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"assert_matches",
|
||||
@@ -192,7 +192,7 @@ dependencies = [
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
"syn 2.0.116",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -268,9 +268,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.56"
|
||||
version = "1.2.57"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2"
|
||||
checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"shlex",
|
||||
@@ -320,9 +320,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.58"
|
||||
version = "4.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63be97961acde393029492ce0be7a1af7e323e6bae9511ebfac33751be5e6806"
|
||||
checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -330,9 +330,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.58"
|
||||
version = "4.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f13174bda5dfd69d7e947827e5af4b0f2f94a4a3ee92912fba07a66150f21e2"
|
||||
checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -342,30 +342,30 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_complete"
|
||||
version = "4.5.66"
|
||||
version = "4.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c757a3b7e39161a4e56f9365141ada2a6c915a8622c408ab6bb4b5d047371031"
|
||||
checksum = "19c9f1dde76b736e3681f28cec9d5a61299cbaae0fce80a68e43724ad56031eb"
|
||||
dependencies = [
|
||||
"clap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.55"
|
||||
version = "4.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5"
|
||||
checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.116",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "1.0.0"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831"
|
||||
checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
|
||||
|
||||
[[package]]
|
||||
name = "cms"
|
||||
@@ -381,9 +381,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.4"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
||||
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
@@ -406,21 +406,6 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc"
|
||||
version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d"
|
||||
dependencies = [
|
||||
"crc-catalog",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc-catalog"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.5.0"
|
||||
@@ -467,9 +452,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ctrlc"
|
||||
version = "3.5.1"
|
||||
version = "3.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73736a89c4aff73035ba2ed2e565061954da00d4970fc9ac25dcc85a2a20d790"
|
||||
checksum = "e0b1fab2ae45819af2d0731d60f2afe17227ebb1a1538a236da84c93e9a60162"
|
||||
dependencies = [
|
||||
"dispatch2",
|
||||
"nix",
|
||||
@@ -532,7 +517,7 @@ checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.116",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -549,9 +534,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "dispatch2"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec"
|
||||
checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"block2",
|
||||
@@ -567,7 +552,7 @@ checksum = "ecb08c4819242b1ec89b3d0c6affa229005bef46ae4f7eed8b80768187c10087"
|
||||
|
||||
[[package]]
|
||||
name = "e2e"
|
||||
version = "3.26.0"
|
||||
version = "3.29.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"avbroot",
|
||||
@@ -663,7 +648,7 @@ checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
[[package]]
|
||||
name = "fuzz"
|
||||
version = "3.26.0"
|
||||
version = "3.29.1"
|
||||
dependencies = [
|
||||
"avbroot",
|
||||
"honggfuzz",
|
||||
@@ -692,9 +677,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec"
|
||||
checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
@@ -773,9 +758,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "honggfuzz"
|
||||
version = "0.5.58"
|
||||
version = "0.5.59"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e8319f3cc8fe416e7aa1ab95dcc04fd49f35397a47d0b2f0f225f6dba346a07"
|
||||
checksum = "9724607fd5cc535fceca960d7f684a1735c4e96c26b4e43986fe8ce798c2550e"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
"lazy_static",
|
||||
@@ -871,9 +856,9 @@ checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.182"
|
||||
version = "0.2.183"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
|
||||
checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
@@ -893,9 +878,9 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.11.0"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
|
||||
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
@@ -925,7 +910,7 @@ dependencies = [
|
||||
"quote",
|
||||
"regex-syntax",
|
||||
"rustc_version",
|
||||
"syn 2.0.116",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -939,20 +924,19 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lz4_flex"
|
||||
version = "0.12.0"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab6473172471198271ff72e9379150e9dfd70d8e533e0752a27e515b48dd375e"
|
||||
checksum = "db9a0d582c2874f68138a16ce1867e0ffde6c0bb0a0df85e1f36d04146db488a"
|
||||
dependencies = [
|
||||
"twox-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lzma-rust2"
|
||||
version = "0.16.1"
|
||||
version = "0.16.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d673a11333485e7d8b93d62a9a5b07b22daf5e8a8655a44c1bb18aa4bf3d1524"
|
||||
checksum = "47bb1e988e6fb779cf720ad431242d3f03167c1b3f2b1aae7f1a94b2495b36ae"
|
||||
dependencies = [
|
||||
"crc",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
@@ -990,7 +974,7 @@ checksum = "db5b29714e950dbb20d5e6f74f9dcec4edbcc1067bb7f8ed198c097b8c1a818b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.116",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1017,9 +1001,9 @@ checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084"
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.30.1"
|
||||
version = "0.31.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
|
||||
checksum = "5d6d0705320c1e6ba1d912b5e37cf18071b6c2e9b7fa8215a1e8a7651966f5d3"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
@@ -1095,9 +1079,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "objc2"
|
||||
version = "0.6.3"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05"
|
||||
checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f"
|
||||
dependencies = [
|
||||
"objc2-encode",
|
||||
]
|
||||
@@ -1110,9 +1094,9 @@ checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.3"
|
||||
version = "1.21.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell_polyfill"
|
||||
@@ -1191,7 +1175,7 @@ dependencies = [
|
||||
"phf_shared",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.116",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1205,9 +1189,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.16"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
||||
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
||||
|
||||
[[package]]
|
||||
name = "pkcs1"
|
||||
@@ -1263,7 +1247,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn 2.0.116",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1300,7 +1284,7 @@ dependencies = [
|
||||
"prost",
|
||||
"prost-types",
|
||||
"regex",
|
||||
"syn 2.0.116",
|
||||
"syn 2.0.117",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
@@ -1314,7 +1298,7 @@ dependencies = [
|
||||
"itertools 0.14.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.116",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1367,18 +1351,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.44"
|
||||
version = "1.0.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
|
||||
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "5.3.0"
|
||||
version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
@@ -1412,9 +1396,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rawzip"
|
||||
version = "0.4.3"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a111bd8bfbbf5c3740c1de3e09bcf5bb487b8d1dbeef5690c03acbb9e65450aa"
|
||||
checksum = "9d9575f44c8cf85bc843ad666dcdf20d05a7753772bef56eb2a5140282b32150"
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
@@ -1461,9 +1445,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.9"
|
||||
version = "0.8.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c"
|
||||
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
@@ -1518,9 +1502,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.1.3"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34"
|
||||
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
@@ -1582,7 +1566,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.116",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1719,9 +1703,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.116"
|
||||
version = "2.0.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3df424c70518695237746f84cede799c9c58fcb37450d7b23716568cc8bc69cb"
|
||||
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1750,12 +1734,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.25.0"
|
||||
version = "3.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0136791f7c95b1f6dd99f9cc786b91bb81c3800b639b3478e561ddb7be95e5f1"
|
||||
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom 0.4.1",
|
||||
"getrandom 0.4.2",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.61.2",
|
||||
@@ -1778,7 +1762,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.116",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1808,14 +1792,14 @@ checksum = "2d2e76690929402faae40aebdda620a2c0e25dd6d3b9afe48867dfd95991f4bd"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.116",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "1.0.1+spec-1.1.0"
|
||||
version = "1.0.7+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbe30f93627849fa362d4a602212d41bb237dc2bd0f8ba0b2ce785012e124220"
|
||||
checksum = "dd28d57d8a6f6e458bc0b8784f8fdcc4b99a437936056fa122cb234f18656a96"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde_core",
|
||||
@@ -1828,18 +1812,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "1.0.0+spec-1.1.0"
|
||||
version = "1.0.1+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e"
|
||||
checksum = "9b320e741db58cac564e26c607d3cc1fdc4a88fd36c879568c07856ed83ff3e9"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.25.1+spec-1.1.0"
|
||||
version = "0.25.5+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "538ed8bc4ada0b2549f2a7be9654aac2dfa358faeabb97d7845c3eb5a297e5d6"
|
||||
checksum = "8ca1a40644a28bce036923f6a431df0b34236949d111cc07cb6dca830c9ef2e1"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"toml_datetime",
|
||||
@@ -1850,18 +1834,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_parser"
|
||||
version = "1.0.8+spec-1.1.0"
|
||||
version = "1.0.10+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0742ff5ff03ea7e67c8ae6c93cac239e0d9784833362da3f9a9c1da8dfefcbdc"
|
||||
checksum = "7df25b4befd31c4816df190124375d5a20c6b6921e2cad937316de3fccd63420"
|
||||
dependencies = [
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_writer"
|
||||
version = "1.0.6+spec-1.1.0"
|
||||
version = "1.0.7+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607"
|
||||
checksum = "f17aaa1c6e3dc22b1da4b6bba97d066e354c7945cac2f7852d4e4e7ca7a6b56d"
|
||||
|
||||
[[package]]
|
||||
name = "topological-sort"
|
||||
@@ -1888,7 +1872,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.116",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1914,9 +1898,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.3.22"
|
||||
version = "0.3.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e"
|
||||
checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319"
|
||||
dependencies = [
|
||||
"nu-ansi-term",
|
||||
"sharded-slab",
|
||||
@@ -2128,9 +2112,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.14"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
|
||||
checksum = "a90e88e4667264a994d34e6d1ab2d26d398dcdca8b7f52bec8668957517fc7d8"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@@ -2165,7 +2149,7 @@ dependencies = [
|
||||
"heck",
|
||||
"indexmap",
|
||||
"prettyplease",
|
||||
"syn 2.0.116",
|
||||
"syn 2.0.117",
|
||||
"wasm-metadata",
|
||||
"wit-bindgen-core",
|
||||
"wit-component",
|
||||
@@ -2181,7 +2165,7 @@ dependencies = [
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.116",
|
||||
"syn 2.0.117",
|
||||
"wit-bindgen-core",
|
||||
"wit-bindgen-rust",
|
||||
]
|
||||
@@ -2239,7 +2223,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "xtask"
|
||||
version = "3.26.0"
|
||||
version = "3.29.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -2249,22 +2233,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.39"
|
||||
version = "0.8.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a"
|
||||
checksum = "efbb2a062be311f2ba113ce66f697a4dc589f85e78a4aea276200804cea0ed87"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.39"
|
||||
version = "0.8.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517"
|
||||
checksum = "0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.116",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2284,14 +2268,14 @@ checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.116",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zlib-rs"
|
||||
version = "0.6.0"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7948af682ccbc3342b6e9420e8c51c1fe5d7bf7756002b4a3c6cabfe96a7e3c"
|
||||
checksum = "3be3d40e40a133f9c916ee3f9f4fa2d9d63435b5fbe1bfc6d9dae0aa0ada1513"
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ members = ["avbroot", "e2e", "fuzz", "xtask"]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
version = "3.26.0"
|
||||
version = "3.29.1"
|
||||
license = "GPL-3.0-only"
|
||||
edition = "2024"
|
||||
repository = "https://github.com/chenxiaolong/avbroot"
|
||||
|
||||
+7
-3
@@ -340,7 +340,7 @@ avbroot payload unpack -i <input payload>
|
||||
|
||||
This subcommand unpacks the payload header information to `payload.toml` and the partition images to the `payload_images` directory.
|
||||
|
||||
Only full payload binaries can be unpacked. Delta payload binaries from incremental OTAs are not supported.
|
||||
Data can only be unpacked from full payload binaries. For delta payloads, either use `--no-output-images` to skip image extraction or use `--skeleton` to create properly sized sparse files with no data.
|
||||
|
||||
### Packing a payload binary
|
||||
|
||||
@@ -429,19 +429,23 @@ This set of commands is for working with raw OTA zip files. They are intentional
|
||||
### Unpacking an OTA zip
|
||||
|
||||
```bash
|
||||
avbroot zip unpack -i <input OTA>
|
||||
avbroot zip unpack -i <input OTA> [--payload]
|
||||
```
|
||||
|
||||
This subcommand unpacks the OTA metadata to `ota.toml` and the OTA files to the `ota_files` directory.
|
||||
|
||||
When `--payload` is specified, the payload and OTA zip will be unpacked at the same time, which is significantly more efficient that unpack the payload and OTA zip separately. The payload unpacking process writes to the same `payload.toml` and `payload_images` outputs as `avbroot payload unpack`.
|
||||
|
||||
### Packing an OTA zip
|
||||
|
||||
```bash
|
||||
avbroot zip pack -o <output OTA> -k <OTA private key>
|
||||
avbroot zip pack -o <output OTA> -k <OTA private key> [--payload]
|
||||
```
|
||||
|
||||
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 `--payload` is specified, the payload and OTA zip will be packed at the same time, which is significantly more efficient than packing the payload and OTA zip separately. The payload packing process reads the same `payload.toml` and `payload_images` inputs as `avbroot payload pack`.
|
||||
|
||||
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
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ dlv-list = "0.6.0"
|
||||
flate2 = { version = "1.0.29", features = ["zlib-rs"] }
|
||||
gf256 = { version = "0.3.0", features = ["rs"] }
|
||||
hex = { version = "0.4.3", features = ["serde"] }
|
||||
lz4_flex = "0.12.0"
|
||||
lz4_flex = "0.13.0"
|
||||
lzma-rust2 = "0.16.1"
|
||||
memchr = "2.6.0"
|
||||
# We can't upgrade to 0.9.0 until rsa updates its dependency.
|
||||
|
||||
@@ -1228,11 +1228,11 @@ fn patch_ota_payload(
|
||||
.with_context(|| format!("Failed to copy from original payload: {name}"))?;
|
||||
}
|
||||
|
||||
let (_, _, properties, metadata_size) = payload_writer
|
||||
let (_, header, properties) = payload_writer
|
||||
.finish()
|
||||
.context("Failed to finalize payload")?;
|
||||
|
||||
Ok((properties, metadata_size))
|
||||
Ok((properties, header.blob_offset))
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
||||
+140
-59
@@ -1,11 +1,11 @@
|
||||
// SPDX-FileCopyrightText: 2024-2025 Andrew Gunnerson
|
||||
// SPDX-FileCopyrightText: 2024-2026 Andrew Gunnerson
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
ffi::OsString,
|
||||
fs::{self, File},
|
||||
io::{BufReader, BufWriter, Seek, SeekFrom},
|
||||
io::{BufReader, BufWriter, Seek, SeekFrom, Write},
|
||||
path::{Path, PathBuf},
|
||||
sync::{Arc, atomic::AtomicBool},
|
||||
};
|
||||
@@ -18,31 +18,34 @@ use crate::{
|
||||
cli::ota,
|
||||
crypto::{self, PassphraseSource, RsaSigningKey},
|
||||
format::payload::{PayloadHeader, PayloadWriter},
|
||||
stream::{self, FromReader},
|
||||
stream::{self, FromReader, SectionReader},
|
||||
util,
|
||||
};
|
||||
|
||||
fn open_reader(path: &Path, allow_delta: bool) -> Result<(BufReader<File>, PayloadHeader)> {
|
||||
let mut reader = File::open(path)
|
||||
.map(BufReader::new)
|
||||
.with_context(|| format!("Failed to open payload for reading: {path:?}"))?;
|
||||
fn open_raw_reader(path: &Path) -> Result<File> {
|
||||
File::open(path).with_context(|| format!("Failed to open payload for reading: {path:?}"))
|
||||
}
|
||||
|
||||
fn open_reader(path: &Path) -> Result<(BufReader<File>, PayloadHeader)> {
|
||||
let mut reader = open_raw_reader(path).map(BufReader::new)?;
|
||||
let header = PayloadHeader::from_reader(&mut reader)
|
||||
.with_context(|| format!("Failed to read payload header: {path:?}"))?;
|
||||
if !allow_delta && !header.is_full_ota() {
|
||||
bail!("Payload is a delta OTA, not a full OTA");
|
||||
}
|
||||
|
||||
Ok((reader, header))
|
||||
}
|
||||
|
||||
fn open_raw_writer(path: &Path) -> Result<BufWriter<File>> {
|
||||
File::create(path)
|
||||
.map(BufWriter::new)
|
||||
.with_context(|| format!("Failed to open payload for writing: {path:?}"))
|
||||
}
|
||||
|
||||
fn open_writer(
|
||||
path: &Path,
|
||||
header: PayloadHeader,
|
||||
key: RsaSigningKey,
|
||||
) -> Result<PayloadWriter<BufWriter<File>>> {
|
||||
let writer = File::create(path)
|
||||
.map(BufWriter::new)
|
||||
.with_context(|| format!("Failed to open payload for writing: {path:?}"))?;
|
||||
let writer = open_raw_writer(path)?;
|
||||
let payload_writer = PayloadWriter::new(writer, header, key)
|
||||
.with_context(|| format!("Failed to write payload header: {path:?}"))?;
|
||||
|
||||
@@ -67,8 +70,8 @@ fn write_info(path: &Path, manifest: &PayloadHeader) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_header(cli: &PayloadCli, header: &PayloadHeader) {
|
||||
if !cli.quiet {
|
||||
fn display_header(quiet: bool, header: &PayloadHeader) {
|
||||
if !quiet {
|
||||
println!("{header:#?}");
|
||||
}
|
||||
}
|
||||
@@ -99,52 +102,105 @@ fn load_key(group: &KeyGroup) -> Result<RsaSigningKey> {
|
||||
Ok(signing_key)
|
||||
}
|
||||
|
||||
fn unpack_subcommand(
|
||||
payload_cli: &PayloadCli,
|
||||
cli: &UnpackCli,
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn unpack_payload(
|
||||
quiet: bool,
|
||||
output_info: &Path,
|
||||
output_images: &Path,
|
||||
no_output_images: bool,
|
||||
skeleton: bool,
|
||||
reader: &File,
|
||||
payload_offset: u64,
|
||||
payload_size: u64,
|
||||
cancel_signal: &AtomicBool,
|
||||
) -> Result<()> {
|
||||
let (mut reader, header) = open_reader(&cli.input, false)?;
|
||||
let payload_size = reader
|
||||
.seek(SeekFrom::End(0))
|
||||
.with_context(|| format!("Failed to get file size: {:?}", cli.input))?;
|
||||
let section_reader = SectionReader::new(reader, payload_offset, payload_size)
|
||||
.context("Failed to directly open payload section")?;
|
||||
let header =
|
||||
PayloadHeader::from_reader(section_reader).context("Failed to read payload header")?;
|
||||
|
||||
display_header(payload_cli, &header);
|
||||
display_header(quiet, &header);
|
||||
|
||||
write_info(&cli.output_info, &header)?;
|
||||
write_info(output_info, &header)?;
|
||||
|
||||
if !cli.no_output_images {
|
||||
fs::create_dir_all(&cli.output_images)
|
||||
.with_context(|| format!("Failed to create directory: {:?}", cli.output_images))?;
|
||||
if !no_output_images {
|
||||
fs::create_dir_all(output_images)
|
||||
.with_context(|| format!("Failed to create directory: {output_images:?}"))?;
|
||||
|
||||
ota::extract_payload(
|
||||
&reader.into_inner(),
|
||||
&cli.output_images,
|
||||
0,
|
||||
payload_size,
|
||||
&header,
|
||||
&header
|
||||
.manifest
|
||||
.partitions
|
||||
.iter()
|
||||
.map(|p| &p.partition_name)
|
||||
.cloned()
|
||||
.collect(),
|
||||
cancel_signal,
|
||||
)?;
|
||||
if skeleton {
|
||||
for partition in &header.manifest.partitions {
|
||||
let name = &partition.partition_name;
|
||||
let path = util::path_join_single(output_images, format!("{name}.img"))?;
|
||||
let size = partition
|
||||
.new_partition_info
|
||||
.as_ref()
|
||||
.and_then(|info| info.size)
|
||||
.ok_or_else(|| anyhow!("Size not found for partition: {name}"))?;
|
||||
|
||||
let file = File::create(&path)
|
||||
.with_context(|| format!("Failed to create file: {path:?}"))?;
|
||||
|
||||
file.set_len(size)
|
||||
.with_context(|| format!("Failed to truncate file: {path:?}"))?;
|
||||
}
|
||||
} else {
|
||||
if !header.is_full_ota() {
|
||||
bail!("Cannot extract images from a delta payload");
|
||||
}
|
||||
|
||||
ota::extract_payload(
|
||||
reader,
|
||||
output_images,
|
||||
payload_offset,
|
||||
payload_size,
|
||||
&header,
|
||||
&header
|
||||
.manifest
|
||||
.partitions
|
||||
.iter()
|
||||
.map(|p| &p.partition_name)
|
||||
.cloned()
|
||||
.collect(),
|
||||
cancel_signal,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pack_subcommand(
|
||||
fn unpack_subcommand(
|
||||
payload_cli: &PayloadCli,
|
||||
cli: &PackCli,
|
||||
cli: &UnpackCli,
|
||||
cancel_signal: &AtomicBool,
|
||||
) -> Result<()> {
|
||||
let signing_key = load_key(&cli.key)?;
|
||||
let mut reader = open_raw_reader(&cli.input)?;
|
||||
let payload_size = reader
|
||||
.seek(SeekFrom::End(0))
|
||||
.with_context(|| format!("Failed to get file size: {:?}", cli.input))?;
|
||||
|
||||
let mut header = read_info(&cli.input_info)?;
|
||||
unpack_payload(
|
||||
payload_cli.quiet,
|
||||
&cli.output_info,
|
||||
&cli.output_images,
|
||||
cli.no_output_images,
|
||||
cli.skeleton,
|
||||
&reader,
|
||||
0,
|
||||
payload_size,
|
||||
cancel_signal,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn pack_payload(
|
||||
quiet: bool,
|
||||
input_info: &Path,
|
||||
input_images: &Path,
|
||||
writer: &mut dyn Write,
|
||||
signing_key: &RsaSigningKey,
|
||||
cancel_signal: &AtomicBool,
|
||||
) -> Result<(String, u64)> {
|
||||
let mut header = read_info(input_info)?;
|
||||
|
||||
// Pre-open all of the image files.
|
||||
let input_files = header
|
||||
@@ -152,8 +208,7 @@ fn pack_subcommand(
|
||||
.partitions
|
||||
.iter()
|
||||
.map(|p| {
|
||||
let path =
|
||||
util::path_join_single(&cli.input_images, format!("{}.img", p.partition_name))?;
|
||||
let path = util::path_join_single(input_images, format!("{}.img", p.partition_name))?;
|
||||
let file = File::open(&path)
|
||||
.map(Arc::new)
|
||||
.with_context(|| format!("Failed to open file: {path:?}"))?;
|
||||
@@ -199,7 +254,8 @@ fn pack_subcommand(
|
||||
|
||||
// Now we can write the actual payload. With everything precomputed, this is
|
||||
// mostly just a simple copy.
|
||||
let mut payload_writer = open_writer(&cli.output, header.clone(), signing_key)?;
|
||||
let mut payload_writer = PayloadWriter::new(writer, header.clone(), signing_key.clone())
|
||||
.context("Failed to write payload header")?;
|
||||
|
||||
while payload_writer
|
||||
.begin_next_operation()
|
||||
@@ -235,14 +291,35 @@ fn pack_subcommand(
|
||||
.with_context(|| format!("Failed to copy from replacement image: {name}"))?;
|
||||
}
|
||||
|
||||
let (raw_writer, header, properties, _) = payload_writer
|
||||
let (_, 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);
|
||||
display_header(quiet, &header);
|
||||
|
||||
Ok((properties, header.blob_offset))
|
||||
}
|
||||
|
||||
fn pack_subcommand(
|
||||
payload_cli: &PayloadCli,
|
||||
cli: &PackCli,
|
||||
cancel_signal: &AtomicBool,
|
||||
) -> Result<()> {
|
||||
let signing_key = load_key(&cli.key)?;
|
||||
|
||||
let mut writer = open_raw_writer(&cli.output)?;
|
||||
|
||||
let (properties, _) = pack_payload(
|
||||
payload_cli.quiet,
|
||||
&cli.input_info,
|
||||
&cli.input_images,
|
||||
&mut writer,
|
||||
&signing_key,
|
||||
cancel_signal,
|
||||
)?;
|
||||
|
||||
writer.into_inner().context("Failed to flush payload")?;
|
||||
|
||||
// Optionally, write payload_properties.txt.
|
||||
if let Some(path) = &cli.output_properties {
|
||||
@@ -260,7 +337,7 @@ fn repack_subcommand(
|
||||
) -> Result<()> {
|
||||
let signing_key = load_key(&cli.key)?;
|
||||
|
||||
let (mut reader, header) = open_reader(&cli.input, true)?;
|
||||
let (mut reader, header) = open_reader(&cli.input)?;
|
||||
|
||||
info!("Generating new OTA payload");
|
||||
|
||||
@@ -299,12 +376,12 @@ fn repack_subcommand(
|
||||
.with_context(|| format!("Failed to copy from original payload: {name}"))?;
|
||||
}
|
||||
|
||||
let (_, header, properties, _) = payload_writer
|
||||
let (_, header, properties) = payload_writer
|
||||
.finish()
|
||||
.context("Failed to finalize payload")?;
|
||||
|
||||
// Display the header information now that it has been finalized.
|
||||
display_header(payload_cli, &header);
|
||||
display_header(payload_cli.quiet, &header);
|
||||
|
||||
// Optionally, write payload_properties.txt.
|
||||
if let Some(path) = &cli.output_properties {
|
||||
@@ -316,9 +393,9 @@ fn repack_subcommand(
|
||||
}
|
||||
|
||||
fn info_subcommand(payload_cli: &PayloadCli, cli: &InfoCli) -> Result<()> {
|
||||
let (_, header) = open_reader(&cli.input, true)?;
|
||||
let (_, header) = open_reader(&cli.input)?;
|
||||
|
||||
display_header(payload_cli, &header);
|
||||
display_header(payload_cli.quiet, &header);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -391,8 +468,12 @@ struct UnpackCli {
|
||||
output_images: PathBuf,
|
||||
|
||||
/// Do not output images.
|
||||
#[arg(long, conflicts_with = "output_images")]
|
||||
#[arg(long, conflicts_with_all = ["output_images", "skeleton"])]
|
||||
no_output_images: bool,
|
||||
|
||||
/// Only create empty sparse files for output images.
|
||||
#[arg(long)]
|
||||
skeleton: bool,
|
||||
}
|
||||
|
||||
/// Pack a payload binary.
|
||||
|
||||
+151
-29
@@ -5,7 +5,7 @@ use std::{
|
||||
collections::BTreeMap,
|
||||
ffi::OsString,
|
||||
fs::{self, File, OpenOptions},
|
||||
io::{BufReader, Seek},
|
||||
io::{BufReader, Seek, Write},
|
||||
path::{Path, PathBuf},
|
||||
sync::atomic::AtomicBool,
|
||||
};
|
||||
@@ -21,6 +21,7 @@ use tracing::warn;
|
||||
use x509_cert::Certificate;
|
||||
|
||||
use crate::{
|
||||
cli::payload,
|
||||
crypto::{self, PassphraseSource, RsaSigningKey},
|
||||
format::{
|
||||
ota::{self, SigningWriter, ZipEntry, ZipMode},
|
||||
@@ -137,8 +138,8 @@ fn write_info(path: &Path, info: &OtaInfo) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_info(cli: &ZipCli, info: &OtaInfo) {
|
||||
if !cli.quiet {
|
||||
fn display_info(quiet: bool, info: &OtaInfo) {
|
||||
if !quiet {
|
||||
println!("{info:#?}");
|
||||
}
|
||||
}
|
||||
@@ -285,11 +286,33 @@ fn finalize_ota(
|
||||
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}"))?;
|
||||
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}"))?;
|
||||
|
||||
if cli.payload && path == ota::PATH_PAYLOAD {
|
||||
let (payload_offset, payload_size) = {
|
||||
let range = entry.compressed_data_range();
|
||||
|
||||
(range.0, range.1 - range.0)
|
||||
};
|
||||
|
||||
payload::unpack_payload(
|
||||
zip_cli.quiet,
|
||||
&cli.output_payload_info,
|
||||
&cli.output_payload_images,
|
||||
cli.no_output_payload_images,
|
||||
cli.payload_skeleton,
|
||||
&zip_reader.get_ref().0,
|
||||
payload_offset,
|
||||
payload_size,
|
||||
cancel_signal,
|
||||
)?;
|
||||
} else if cli.payload && path == ota::PATH_PROPERTIES {
|
||||
// We intentionally ignore this since it's recomputed when
|
||||
// packing with --payload.
|
||||
} else if !cli.no_output_files {
|
||||
let mut entry_reader = zip::verifying_reader(&entry, input_entry.compression_method)
|
||||
.with_context(|| format!("Failed to open zip entry: {path}"))?;
|
||||
|
||||
@@ -312,7 +335,7 @@ fn unpack_subcommand(zip_cli: &ZipCli, cli: &UnpackCli, cancel_signal: &AtomicBo
|
||||
files: input_entries.into_keys().collect(),
|
||||
};
|
||||
|
||||
display_info(zip_cli, &info);
|
||||
display_info(zip_cli.quiet, &info);
|
||||
write_info(&cli.output_info, &info)?;
|
||||
|
||||
Ok(())
|
||||
@@ -331,10 +354,15 @@ fn pack_subcommand(zip_cli: &ZipCli, cli: &PackCli, cancel_signal: &AtomicBool)
|
||||
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:?}"))?;
|
||||
let input_size =
|
||||
if cli.payload && (path == ota::PATH_PAYLOAD || path == ota::PATH_PROPERTIES) {
|
||||
0
|
||||
} else {
|
||||
let input_path = util::path_join(&cli.input_files, path)?;
|
||||
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);
|
||||
}
|
||||
@@ -342,28 +370,56 @@ fn pack_subcommand(zip_cli: &ZipCli, cli: &PackCli, cancel_signal: &AtomicBool)
|
||||
let mut payload_metadata_size = None;
|
||||
let mut metadata_entries = vec![];
|
||||
|
||||
// Only used when packing the zip and payload at the same time.
|
||||
let mut payload_properties = None;
|
||||
|
||||
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 && cli.payload {
|
||||
// Pack the payload directly into the zip.
|
||||
let (properties, metadata_size) = payload::pack_payload(
|
||||
zip_cli.quiet,
|
||||
&cli.input_payload_info,
|
||||
&cli.input_payload_images,
|
||||
&mut data_writer,
|
||||
&signing_key,
|
||||
cancel_signal,
|
||||
)?;
|
||||
|
||||
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(metadata_size);
|
||||
payload_properties = Some(properties);
|
||||
} else if path == ota::PATH_PROPERTIES && cli.payload {
|
||||
// We process files in order, so this would only happen if the file
|
||||
// list did not contain the payload.
|
||||
let properties = payload_properties
|
||||
.as_ref()
|
||||
.ok_or_else(|| anyhow!("Payload has not been processed yet"))?;
|
||||
|
||||
payload_metadata_size = Some(header.blob_offset);
|
||||
data_writer
|
||||
.write_all(properties.as_bytes())
|
||||
.with_context(|| format!("Failed to write zip entry: {path}"))?;
|
||||
} else {
|
||||
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:?}"))?;
|
||||
|
||||
input_file
|
||||
.rewind()
|
||||
.with_context(|| format!("Failed to seek file: {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}"))?;
|
||||
}
|
||||
|
||||
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)?;
|
||||
}
|
||||
|
||||
@@ -382,7 +438,7 @@ fn pack_subcommand(zip_cli: &ZipCli, cli: &PackCli, cancel_signal: &AtomicBool)
|
||||
info.files.retain(|p| !is_excluded_path(p));
|
||||
info.files.sort();
|
||||
|
||||
display_info(zip_cli, &info);
|
||||
display_info(zip_cli.quiet, &info);
|
||||
|
||||
if let Some(path) = &cli.output_info {
|
||||
write_info(path, &info)?;
|
||||
@@ -446,7 +502,7 @@ fn repack_subcommand(zip_cli: &ZipCli, cli: &RepackCli, cancel_signal: &AtomicBo
|
||||
files: input_entries.into_keys().collect(),
|
||||
};
|
||||
|
||||
display_info(zip_cli, &info);
|
||||
display_info(zip_cli.quiet, &info);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -458,7 +514,7 @@ fn info_subcommand(zip_cli: &ZipCli, cli: &InfoCli) -> Result<()> {
|
||||
files: input_entries.into_keys().collect(),
|
||||
};
|
||||
|
||||
display_info(zip_cli, &info);
|
||||
display_info(zip_cli.quiet, &info);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -541,6 +597,45 @@ struct UnpackCli {
|
||||
/// Do not output files.
|
||||
#[arg(long, conflicts_with = "output_files")]
|
||||
no_output_files: bool,
|
||||
|
||||
/// Also unpack the payload in the OTA.
|
||||
///
|
||||
/// Unpacking the zip and payload at the same time is more efficient than
|
||||
/// unpacking them separately.
|
||||
#[arg(long)]
|
||||
payload: bool,
|
||||
|
||||
/// Path to output payload info TOML.
|
||||
#[arg(
|
||||
long,
|
||||
value_name = "FILE",
|
||||
value_parser,
|
||||
default_value = "payload.toml",
|
||||
requires = "payload"
|
||||
)]
|
||||
output_payload_info: PathBuf,
|
||||
|
||||
/// Path to output payload images directory.
|
||||
#[arg(
|
||||
long,
|
||||
value_name = "DIR",
|
||||
value_parser,
|
||||
default_value = "payload_images",
|
||||
requires = "payload"
|
||||
)]
|
||||
output_payload_images: PathBuf,
|
||||
|
||||
/// Do not output payload images.
|
||||
#[arg(
|
||||
long,
|
||||
conflicts_with_all = ["output_payload_images", "payload_skeleton"],
|
||||
requires = "payload"
|
||||
)]
|
||||
no_output_payload_images: bool,
|
||||
|
||||
/// Only create empty sparse files for output payload images.
|
||||
#[arg(long, requires = "payload")]
|
||||
payload_skeleton: bool,
|
||||
}
|
||||
|
||||
/// Pack an OTA zip.
|
||||
@@ -573,6 +668,33 @@ struct PackCli {
|
||||
#[arg(long, value_name = "DIR", value_parser, default_value = "ota_files")]
|
||||
input_files: PathBuf,
|
||||
|
||||
/// Also pack the payload in the OTA.
|
||||
///
|
||||
/// Packing the zip and payload at the same time is more efficient than
|
||||
/// packing them separately.
|
||||
#[arg(long)]
|
||||
payload: bool,
|
||||
|
||||
/// Path to input payload info TOML.
|
||||
#[arg(
|
||||
long,
|
||||
value_name = "FILE",
|
||||
value_parser,
|
||||
default_value = "payload.toml",
|
||||
requires = "payload"
|
||||
)]
|
||||
input_payload_info: PathBuf,
|
||||
|
||||
/// Path to input payload images directory.
|
||||
#[arg(
|
||||
long,
|
||||
value_name = "DIR",
|
||||
value_parser,
|
||||
default_value = "payload_images",
|
||||
requires = "payload"
|
||||
)]
|
||||
input_payload_images: PathBuf,
|
||||
|
||||
#[command(flatten)]
|
||||
key: KeyGroup,
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: 2023-2025 Andrew Gunnerson
|
||||
// SPDX-FileCopyrightText: 2023-2026 Andrew Gunnerson
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use std::io::{self, Read, Seek, Write};
|
||||
@@ -9,7 +9,7 @@ use flate2::{
|
||||
write::{DeflateEncoder, GzEncoder},
|
||||
};
|
||||
use lz4_flex::frame::FrameDecoder;
|
||||
use lzma_rust2::{CheckType, XzOptions, XzReader, XzWriter};
|
||||
use lzma_rust2::{CheckType, LzmaOptions, LzmaReader, LzmaWriter, XzOptions, XzReader, XzWriter};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
@@ -18,6 +18,14 @@ use crate::stream::ReadFixedSizeExt;
|
||||
static GZIP_MAGIC: &[u8; 2] = b"\x1f\x8b";
|
||||
static LZ4_LEGACY_MAGIC: &[u8; 4] = b"\x02\x21\x4c\x18";
|
||||
static XZ_MAGIC: &[u8; 6] = b"\xfd\x37\x7a\x58\x5a\x00";
|
||||
// This is not actually a magic value, but is good enough. The first byte
|
||||
// encodes (lc=3, lp=0, pb=2) and the next two bytes are two of the U32LE bytes
|
||||
// encoding the dictionary size. If this heuristic is good enough for libmagic,
|
||||
// then it's good enough for us too.
|
||||
// https://nigeltao.github.io/blog/2024/xz-lzma-part-5-xz.html
|
||||
static LZMA_MAGIC: &[u8; 3] = b"\x5d\x00\x00";
|
||||
|
||||
const LZMA_MAX_MEMORY_KIB: u32 = 16 * 1024;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
@@ -26,9 +34,13 @@ pub enum Error {
|
||||
#[error("I/O error when autodetecting compression format")]
|
||||
AutoDetect(#[source] io::Error),
|
||||
#[error("Failed to initialize legacy LZ4 encoder")]
|
||||
Lz4Init(#[source] io::Error),
|
||||
Lz4EncodeInit(#[source] io::Error),
|
||||
#[error("Failed to initialize XZ encoder")]
|
||||
XzInit(#[source] io::Error),
|
||||
XzEncodeInit(#[source] io::Error),
|
||||
#[error("Failed to initialize LZMA decoder")]
|
||||
LzmaDecodeInit(#[source] io::Error),
|
||||
#[error("Failed to initialize LZMA encoder")]
|
||||
LzmaEncodeInit(#[source] io::Error),
|
||||
}
|
||||
|
||||
type Result<T> = std::result::Result<T, Error>;
|
||||
@@ -113,6 +125,7 @@ pub enum CompressedFormat {
|
||||
Gzip,
|
||||
Lz4Legacy,
|
||||
Xz,
|
||||
Lzma,
|
||||
}
|
||||
|
||||
pub enum CompressedReader<R: Read> {
|
||||
@@ -123,16 +136,21 @@ pub enum CompressedReader<R: Read> {
|
||||
Lz4(FrameDecoder<R>),
|
||||
/// Boxed because the [`XzReader`] is nearly 4 KiB.
|
||||
Xz(Box<XzReader<R>>),
|
||||
Lzma(Box<LzmaReader<R>>),
|
||||
}
|
||||
|
||||
impl<R: Read> CompressedReader<R> {
|
||||
pub fn with_format(reader: R, format: CompressedFormat) -> Self {
|
||||
pub fn with_format(reader: R, format: CompressedFormat) -> Result<Self> {
|
||||
match format {
|
||||
CompressedFormat::None => Self::None(reader),
|
||||
CompressedFormat::Deflate => Self::Deflate(DeflateDecoder::new(reader)),
|
||||
CompressedFormat::Gzip => Self::Gzip(GzDecoder::new(reader)),
|
||||
CompressedFormat::Lz4Legacy => Self::Lz4(FrameDecoder::new(reader)),
|
||||
CompressedFormat::Xz => Self::Xz(Box::new(XzReader::new(reader, false))),
|
||||
CompressedFormat::None => Ok(Self::None(reader)),
|
||||
CompressedFormat::Deflate => Ok(Self::Deflate(DeflateDecoder::new(reader))),
|
||||
CompressedFormat::Gzip => Ok(Self::Gzip(GzDecoder::new(reader))),
|
||||
CompressedFormat::Lz4Legacy => Ok(Self::Lz4(FrameDecoder::new(reader))),
|
||||
CompressedFormat::Xz => Ok(Self::Xz(Box::new(XzReader::new(reader, false)))),
|
||||
CompressedFormat::Lzma => Ok(Self::Lzma(Box::new(
|
||||
LzmaReader::new_mem_limit(reader, LZMA_MAX_MEMORY_KIB, None)
|
||||
.map_err(Error::LzmaDecodeInit)?,
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,6 +161,7 @@ impl<R: Read> CompressedReader<R> {
|
||||
Self::Gzip(_) => CompressedFormat::Gzip,
|
||||
Self::Lz4(_) => CompressedFormat::Lz4Legacy,
|
||||
Self::Xz(_) => CompressedFormat::Xz,
|
||||
Self::Lzma(_) => CompressedFormat::Lzma,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,6 +172,7 @@ impl<R: Read> CompressedReader<R> {
|
||||
Self::Gzip(r) => r.into_inner(),
|
||||
Self::Lz4(r) => r.into_inner(),
|
||||
Self::Xz(r) => r.into_inner(),
|
||||
Self::Lzma(r) => r.into_inner(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -163,17 +183,21 @@ impl<R: Read + Seek> CompressedReader<R> {
|
||||
|
||||
reader.rewind().map_err(Error::AutoDetect)?;
|
||||
|
||||
if &magic[0..2] == GZIP_MAGIC {
|
||||
Ok(Self::Gzip(GzDecoder::new(reader)))
|
||||
let format = if &magic[0..2] == GZIP_MAGIC {
|
||||
CompressedFormat::Gzip
|
||||
} else if &magic[0..4] == LZ4_LEGACY_MAGIC {
|
||||
Ok(Self::Lz4(FrameDecoder::new(reader)))
|
||||
CompressedFormat::Lz4Legacy
|
||||
} else if &magic == XZ_MAGIC {
|
||||
Ok(Self::Xz(Box::new(XzReader::new(reader, false))))
|
||||
CompressedFormat::Xz
|
||||
} else if &magic[0..3] == LZMA_MAGIC {
|
||||
CompressedFormat::Lzma
|
||||
} else if raw_if_unknown {
|
||||
Ok(Self::None(reader))
|
||||
CompressedFormat::None
|
||||
} else {
|
||||
Err(Error::UnknownFormat)
|
||||
}
|
||||
return Err(Error::UnknownFormat);
|
||||
};
|
||||
|
||||
Self::with_format(reader, format)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,6 +209,7 @@ impl<R: Read> Read for CompressedReader<R> {
|
||||
Self::Gzip(r) => r.read(buf),
|
||||
Self::Lz4(r) => r.read(buf),
|
||||
Self::Xz(r) => r.read(buf),
|
||||
Self::Lzma(r) => r.read(buf),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -195,7 +220,8 @@ pub enum CompressedWriter<W: Write> {
|
||||
Deflate(DeflateEncoder<W>),
|
||||
Gzip(GzEncoder<W>),
|
||||
Lz4Legacy(Lz4LegacyEncoder<W>),
|
||||
Xz(XzWriter<W>),
|
||||
Xz(Box<XzWriter<W>>),
|
||||
Lzma(Box<LzmaWriter<W>>),
|
||||
}
|
||||
|
||||
impl<W: Write> CompressedWriter<W> {
|
||||
@@ -210,7 +236,7 @@ impl<W: Write> CompressedWriter<W> {
|
||||
Ok(Self::Gzip(GzEncoder::new(writer, Compression::default())))
|
||||
}
|
||||
CompressedFormat::Lz4Legacy => {
|
||||
let encoder = Lz4LegacyEncoder::new(writer).map_err(Error::Lz4Init)?;
|
||||
let encoder = Lz4LegacyEncoder::new(writer).map_err(Error::Lz4EncodeInit)?;
|
||||
Ok(Self::Lz4Legacy(encoder))
|
||||
}
|
||||
CompressedFormat::Xz => {
|
||||
@@ -218,8 +244,17 @@ impl<W: Write> CompressedWriter<W> {
|
||||
let mut options = XzOptions::with_preset(6);
|
||||
options.set_check_sum_type(CheckType::Crc32);
|
||||
|
||||
let xz_writer = XzWriter::new(writer, options).map_err(Error::XzInit)?;
|
||||
Ok(Self::Xz(xz_writer))
|
||||
let xz_writer = XzWriter::new(writer, options).map_err(Error::XzEncodeInit)?;
|
||||
Ok(Self::Xz(Box::new(xz_writer)))
|
||||
}
|
||||
CompressedFormat::Lzma => {
|
||||
// Defaults are already (lc=3, lp=0, pb=2) and has preset 6.
|
||||
let options = LzmaOptions::default();
|
||||
|
||||
// We only support streaming output.
|
||||
let lzma_writer = LzmaWriter::new_use_header(writer, &options, None)
|
||||
.map_err(Error::LzmaEncodeInit)?;
|
||||
Ok(Self::Lzma(Box::new(lzma_writer)))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -231,6 +266,7 @@ impl<W: Write> CompressedWriter<W> {
|
||||
Self::Gzip(_) => CompressedFormat::Gzip,
|
||||
Self::Lz4Legacy(_) => CompressedFormat::Lz4Legacy,
|
||||
Self::Xz(_) => CompressedFormat::Xz,
|
||||
Self::Lzma(_) => CompressedFormat::Lzma,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,6 +277,7 @@ impl<W: Write> CompressedWriter<W> {
|
||||
Self::Gzip(w) => w.finish(),
|
||||
Self::Lz4Legacy(w) => w.finish(),
|
||||
Self::Xz(w) => w.finish(),
|
||||
Self::Lzma(w) => w.finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -253,6 +290,7 @@ impl<W: Write> Write for CompressedWriter<W> {
|
||||
Self::Gzip(w) => w.write(buf),
|
||||
Self::Lz4Legacy(w) => w.write(buf),
|
||||
Self::Xz(w) => w.write(buf),
|
||||
Self::Lzma(w) => w.write(buf),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,6 +301,79 @@ impl<W: Write> Write for CompressedWriter<W> {
|
||||
Self::Gzip(w) => w.flush(),
|
||||
Self::Lz4Legacy(w) => w.flush(),
|
||||
Self::Xz(w) => w.flush(),
|
||||
Self::Lzma(w) => w.flush(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::io::Cursor;
|
||||
|
||||
use rand::Rng;
|
||||
|
||||
use super::*;
|
||||
|
||||
fn round_trip(format: CompressedFormat, can_autodetect: bool) {
|
||||
let mut data = vec![0u8; 1024 * 1024];
|
||||
rand::thread_rng().fill(data.as_mut_slice());
|
||||
|
||||
let mut writer = CompressedWriter::new(Cursor::new(Vec::new()), format).unwrap();
|
||||
writer.write_all(&data).unwrap();
|
||||
|
||||
let mut compressed = writer.finish().unwrap();
|
||||
compressed.rewind().unwrap();
|
||||
|
||||
let mut reader = if can_autodetect {
|
||||
CompressedReader::new(compressed, true).unwrap()
|
||||
} else {
|
||||
CompressedReader::with_format(compressed, format).unwrap()
|
||||
};
|
||||
assert_eq!(reader.format(), format);
|
||||
|
||||
let mut buf = [0u8; 8192];
|
||||
let mut total_read = 0;
|
||||
|
||||
loop {
|
||||
let n = reader.read(&mut buf).unwrap();
|
||||
if n == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
assert_eq!(&buf[..n], &data[total_read..][..n]);
|
||||
total_read += n;
|
||||
}
|
||||
|
||||
assert_eq!(total_read, data.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn round_trip_none() {
|
||||
round_trip(CompressedFormat::None, true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn round_trip_deflate() {
|
||||
round_trip(CompressedFormat::Deflate, false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn round_trip_gzip() {
|
||||
round_trip(CompressedFormat::Gzip, true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn round_trip_lz4_legacy() {
|
||||
round_trip(CompressedFormat::Lz4Legacy, true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn round_trip_xz() {
|
||||
round_trip(CompressedFormat::Xz, true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn round_trip_lzma() {
|
||||
round_trip(CompressedFormat::Lzma, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: 2022-2025 Andrew Gunnerson
|
||||
// SPDX-FileCopyrightText: 2022-2026 Andrew Gunnerson
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use std::{
|
||||
@@ -45,7 +45,7 @@ use crate::{
|
||||
const PAYLOAD_MAGIC: &[u8; 4] = b"CrAU";
|
||||
const PAYLOAD_VERSION: u64 = 2;
|
||||
|
||||
const MANIFEST_MAX_SIZE: usize = 4 * 1024 * 1024;
|
||||
const MANIFEST_MAX_SIZE: usize = 16 * 1024 * 1024;
|
||||
|
||||
/// Size of each extent. This matches what AOSP's delta_generator does. We also
|
||||
/// require this to be a multiple of the block size and a multiple of the
|
||||
@@ -436,11 +436,11 @@ impl<W: Write> PayloadWriter<W> {
|
||||
|
||||
/// Finalize the payload. If this function is not called, the payload will
|
||||
/// be left in an incomplete state. Returns the original writer, the final
|
||||
/// header, the contents that should be written for `payload_properties.txt`
|
||||
/// and the length of the header + manifest + manifest signature sections
|
||||
/// (for constructing the `payload_metadata.bin` OTA metadata property files
|
||||
/// entry).
|
||||
pub fn finish(mut self) -> Result<(W, PayloadHeader, String, u64)> {
|
||||
/// header and the data that should be written for `payload_properties.txt`.
|
||||
/// [`PayloadHeader::blob_offset`] indicates the length of the header +
|
||||
/// manifest + manifest signature sections (for constructing the
|
||||
/// `payload_metadata.bin` OTA metadata property files entry).
|
||||
pub fn finish(mut self) -> Result<(W, PayloadHeader, String)> {
|
||||
// Append payload signature.
|
||||
let payload_partial_hash = self.h_partial.clone().finish();
|
||||
let payload_sig = sign_digest(payload_partial_hash.as_ref(), &self.key)?;
|
||||
@@ -465,7 +465,9 @@ impl<W: Write> PayloadWriter<W> {
|
||||
self.metadata_size as u64,
|
||||
);
|
||||
|
||||
Ok((self.inner, self.header, properties, metadata_with_sig_size))
|
||||
self.header.blob_offset = metadata_with_sig_size;
|
||||
|
||||
Ok((self.inner, self.header, properties))
|
||||
}
|
||||
|
||||
/// Prepare for writing the next source data blob corresponding to an
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: 2025 Andrew Gunnerson
|
||||
// SPDX-FileCopyrightText: 2025-2026 Andrew Gunnerson
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use std::{
|
||||
@@ -249,7 +249,8 @@ pub fn compressed_reader<'archive, R: ReaderAt>(
|
||||
) -> Result<CompressedReader<ZipReader<&'archive R>>, rawzip::Error> {
|
||||
let format = compression_method_to_format(compression_method)?;
|
||||
|
||||
Ok(CompressedReader::with_format(entry.reader(), format))
|
||||
CompressedReader::with_format(entry.reader(), format)
|
||||
.map_err(|e| rawzip::ErrorKind::InvalidInput { msg: e.to_string() }.into())
|
||||
}
|
||||
|
||||
pub fn compressed_slice_reader<'archive>(
|
||||
@@ -259,7 +260,8 @@ pub fn compressed_slice_reader<'archive>(
|
||||
let format = compression_method_to_format(compression_method)?;
|
||||
let raw_reader = Cursor::new(entry.data());
|
||||
|
||||
Ok(CompressedReader::with_format(raw_reader, format))
|
||||
CompressedReader::with_format(raw_reader, format)
|
||||
.map_err(|e| rawzip::ErrorKind::InvalidInput { msg: e.to_string() }.into())
|
||||
}
|
||||
|
||||
pub fn verifying_reader<'archive, R: ReaderAt>(
|
||||
@@ -286,7 +288,12 @@ pub fn compressed_writer<W: Write>(
|
||||
|
||||
match CompressedWriter::new(writer, format) {
|
||||
Ok(w) => Ok(w),
|
||||
Err(Error::Lz4Init(e) | Error::XzInit(e)) => Err(e.into()),
|
||||
Err(
|
||||
Error::Lz4EncodeInit(e)
|
||||
| Error::XzEncodeInit(e)
|
||||
| Error::LzmaDecodeInit(e)
|
||||
| Error::LzmaEncodeInit(e),
|
||||
) => Err(e.into()),
|
||||
Err(Error::UnknownFormat | Error::AutoDetect(_)) => unreachable!(),
|
||||
}
|
||||
}
|
||||
@@ -306,7 +313,7 @@ pub trait ZipArchiveReadAtExt {
|
||||
|
||||
impl ZipArchiveReadAtExt for ZipArchive<()> {}
|
||||
|
||||
pub struct ReaderAtWrapper<R: ReadAt>(R);
|
||||
pub struct ReaderAtWrapper<R: ReadAt>(pub R);
|
||||
|
||||
impl<R: ReadAt> ReaderAt for ReaderAtWrapper<R> {
|
||||
fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
|
||||
|
||||
@@ -211,7 +211,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..30700];
|
||||
const VERS_SUPPORTED: &'static [Range<u32>] = &[25102..25207, 25211..30800];
|
||||
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;
|
||||
|
||||
+2
-2
@@ -704,11 +704,11 @@ fn create_payload(
|
||||
.with_context(|| format!("Failed to copy from image: {name}"))?;
|
||||
}
|
||||
|
||||
let (_, _, properties, metadata_size) = payload_writer
|
||||
let (_, header, properties) = payload_writer
|
||||
.finish()
|
||||
.context("Failed to finalize payload")?;
|
||||
|
||||
Ok((properties, metadata_size))
|
||||
Ok((properties, header.blob_offset))
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ This will update the main [`Cargo.toml`](../Cargo.toml) file.
|
||||
|
||||
## Updating changelog links
|
||||
|
||||
To add an entry to [`CHANGELOG.md`](../CHANGELOG.md), the user should manually type in a message and add repository references, like `[Issue #0]` or `[PR #0 @user]`.
|
||||
To add an entry to [`CHANGELOG.md`](../CHANGELOG.md), the user should manually type in a message and add repository references, like `[Issue #0]` or `[PR #0]`.
|
||||
|
||||
Then, run the following command to generate the appropriate link references:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user