mirror of
https://github.com/chenxiaolong/avbroot.git
synced 2026-07-03 14:05:11 +02:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 10370692d0 | |||
| 73b6e9b177 | |||
| 8d25e28ea3 | |||
| 82fc1336f7 | |||
| 953c0c1c5b | |||
| c3b073cece | |||
| 565efc5ee5 | |||
| 6ef8e548a3 | |||
| 80549346ea | |||
| 275b18b2a5 | |||
| 0a68966594 | |||
| 9ef5629556 | |||
| 1db09eb896 | |||
| a134d7f889 |
@@ -1,35 +0,0 @@
|
||||
---
|
||||
name: Modules
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
jobs:
|
||||
build:
|
||||
name: Build modules
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
# For git describe
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Get version
|
||||
id: get_version
|
||||
shell: bash
|
||||
run: |
|
||||
echo -n 'version=' >> "${GITHUB_OUTPUT}"
|
||||
git describe --always \
|
||||
| sed -E "s/^v//g;s/([^-]*-g)/r\1/;s/-/./g" \
|
||||
>> "${GITHUB_OUTPUT}"
|
||||
|
||||
- name: Build modules
|
||||
run: cargo xtask modules -a
|
||||
|
||||
- name: Archive artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: avbroot-modules-${{ steps.get_version.outputs.version }}
|
||||
path: modules/dist/
|
||||
+20
-1
@@ -7,6 +7,20 @@
|
||||
to update the actual links at the bottom of the file.
|
||||
-->
|
||||
|
||||
### Version 3.1.0
|
||||
|
||||
* The `OEMUnlockOnBoot` module has been split out to a separate repo ([Discussion #235], [PR #246])
|
||||
* https://github.com/chenxiaolong/OEMUnlockOnBoot
|
||||
* The new module supports the automatic update mechanism within Magisk/KernelSU
|
||||
* Add support for Magisk v27.0 ([PR #255])
|
||||
* Switch to using a proper logging library ([PR #251])
|
||||
* Folks who want to see the juicy details during patching can use `--log-level debug` or `--log-level trace`
|
||||
|
||||
Behind-the-scenes changes:
|
||||
|
||||
* Switch from xz2 to liblzma (maintained fork of xz2) ([PR #247])
|
||||
* Update all dependencies ([PR #256])
|
||||
|
||||
### Version 3.0.0
|
||||
|
||||
Happy New Year! This release brings two major changes:
|
||||
@@ -140,6 +154,7 @@ Behind-the-scenes changes:
|
||||
|
||||
<!-- Do not manually edit the lines below. Use `cargo xtask update-changelog` to regenerate. -->
|
||||
[Discussion #195]: https://github.com/chenxiaolong/avbroot/discussions/195
|
||||
[Discussion #235]: https://github.com/chenxiaolong/avbroot/discussions/235
|
||||
[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
|
||||
@@ -207,7 +222,6 @@ Behind-the-scenes changes:
|
||||
[PR #211]: https://github.com/chenxiaolong/avbroot/pull/211
|
||||
[PR #214]: https://github.com/chenxiaolong/avbroot/pull/214
|
||||
[PR #219]: https://github.com/chenxiaolong/avbroot/pull/219
|
||||
[PR #220]: https://github.com/chenxiaolong/avbroot/pull/220
|
||||
[PR #221]: https://github.com/chenxiaolong/avbroot/pull/221
|
||||
[PR #224]: https://github.com/chenxiaolong/avbroot/pull/224
|
||||
[PR #226]: https://github.com/chenxiaolong/avbroot/pull/226
|
||||
@@ -225,3 +239,8 @@ Behind-the-scenes changes:
|
||||
[PR #243]: https://github.com/chenxiaolong/avbroot/pull/243
|
||||
[PR #244]: https://github.com/chenxiaolong/avbroot/pull/244
|
||||
[PR #245]: https://github.com/chenxiaolong/avbroot/pull/245
|
||||
[PR #246]: https://github.com/chenxiaolong/avbroot/pull/246
|
||||
[PR #247]: https://github.com/chenxiaolong/avbroot/pull/247
|
||||
[PR #251]: https://github.com/chenxiaolong/avbroot/pull/251
|
||||
[PR #255]: https://github.com/chenxiaolong/avbroot/pull/255
|
||||
[PR #256]: https://github.com/chenxiaolong/avbroot/pull/256
|
||||
|
||||
Generated
+246
-170
@@ -36,9 +36,9 @@ checksum = "e9d4ee0d472d1cd2e28c97dfa124b3d8d992e10eb0a035f33f5d12e3a177ba3b"
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.5"
|
||||
version = "0.6.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6"
|
||||
checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
@@ -50,9 +50,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.4"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
|
||||
checksum = "2faccea4cc4ab4a667ce676a30e8ec13922a692c99bb8f5b11f1502c72e04220"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
@@ -84,9 +84,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.77"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9d19de80eff169429ac1e9f48fffb163916b448a44e8e046186232046d9e1f9"
|
||||
checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
@@ -108,12 +108,12 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "avbroot"
|
||||
version = "3.0.0"
|
||||
version = "3.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"assert_matches",
|
||||
"base64",
|
||||
"bitflags 2.4.1",
|
||||
"bitflags 2.4.2",
|
||||
"bstr",
|
||||
"byteorder",
|
||||
"bzip2",
|
||||
@@ -127,6 +127,7 @@ dependencies = [
|
||||
"flate2",
|
||||
"gf256",
|
||||
"hex",
|
||||
"liblzma",
|
||||
"lz4_flex",
|
||||
"memchr",
|
||||
"num-bigint-dig",
|
||||
@@ -150,16 +151,17 @@ dependencies = [
|
||||
"thiserror",
|
||||
"toml_edit",
|
||||
"topological-sort",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"x509-cert",
|
||||
"xz2",
|
||||
"zip",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.5"
|
||||
version = "0.21.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9"
|
||||
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
@@ -181,9 +183,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.4.1"
|
||||
version = "2.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
|
||||
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
@@ -247,9 +249,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cap-primitives"
|
||||
version = "2.0.0"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2bf30c373a3bee22c292b1b6a7a26736a38376840f1af3d2d806455edf8c3899"
|
||||
checksum = "90a0b44fc796b1a84535a63753d50ba3972c4db55c7255c186f79140e63d56d0"
|
||||
dependencies = [
|
||||
"ambient-authority",
|
||||
"fs-set-times",
|
||||
@@ -258,15 +260,15 @@ dependencies = [
|
||||
"ipnet",
|
||||
"maybe-owned",
|
||||
"rustix",
|
||||
"windows-sys 0.48.0",
|
||||
"windows-sys 0.52.0",
|
||||
"winx",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cap-std"
|
||||
version = "2.0.0"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84bade423fa6403efeebeafe568fdb230e8c590a275fba2ba978dd112efcf6e9"
|
||||
checksum = "266626ce180cf9709f317d0bf9754e3a5006359d87f4bf792f06c9c5f1b63c0f"
|
||||
dependencies = [
|
||||
"cap-primitives",
|
||||
"io-extras",
|
||||
@@ -276,9 +278,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cap-tempfile"
|
||||
version = "2.0.0"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b9e3348a3510c4619b4c7a7bcdef09a71221da18f266bda3ed6b9aea2c509e2"
|
||||
checksum = "8748c8ae8186b7362da62c7cb8edf7aabae095c86175e988d76387a842574e16"
|
||||
dependencies = [
|
||||
"cap-std",
|
||||
"rand",
|
||||
@@ -301,6 +303,7 @@ version = "1.0.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
]
|
||||
|
||||
@@ -322,9 +325,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.4.12"
|
||||
version = "4.4.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcfab8ba68f3668e89f6ff60f5b205cea56aa7b769451a59f34b8682f51c056d"
|
||||
checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -332,9 +335,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.4.12"
|
||||
version = "4.4.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb7fb5e4e979aec3be7791562fcba452f94ad85e954da024396433e0e25a79e9"
|
||||
checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -344,9 +347,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_complete"
|
||||
version = "4.4.5"
|
||||
version = "4.4.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a51919c5608a32e34ea1d6be321ad070065e17613e168c5b6977024290f2630b"
|
||||
checksum = "abb745187d7f4d76267b37485a65e0149edd0e91a4cfcdd3f27524ad86cee9f3"
|
||||
dependencies = [
|
||||
"clap",
|
||||
]
|
||||
@@ -360,7 +363,7 @@ dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.43",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -371,9 +374,9 @@ checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
|
||||
|
||||
[[package]]
|
||||
name = "cms"
|
||||
version = "0.2.2"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01b1b34bce0eaafd63b374fa6b58178d72c0b6670e92db786bdd3cde9e37a1f1"
|
||||
checksum = "7b77c319abfd5219629c45c34c89ba945ed3c5e49fcde9d16b6c3885f118a730"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
"der",
|
||||
@@ -395,9 +398,9 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.11"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0"
|
||||
checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
@@ -413,34 +416,28 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.4"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751"
|
||||
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.17"
|
||||
version = "0.9.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e3681d554572a651dda4186cd47240627c3d0114d45a95f6ad27f2f22e7548d"
|
||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.18"
|
||||
version = "0.8.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
@@ -518,7 +515,7 @@ checksum = "5fe87ce4529967e0ba1dcf8450bab64d97dfd5010a6256187ffe2e43e6f0e049"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.43",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -535,7 +532,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "e2e"
|
||||
version = "3.0.0"
|
||||
version = "3.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"avbroot",
|
||||
@@ -548,6 +545,8 @@ dependencies = [
|
||||
"tempfile",
|
||||
"toml_edit",
|
||||
"topological-sort",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"x509-cert",
|
||||
"zip",
|
||||
]
|
||||
@@ -627,7 +626,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "fuzz"
|
||||
version = "3.0.0"
|
||||
version = "3.1.0"
|
||||
dependencies = [
|
||||
"avbroot",
|
||||
"honggfuzz",
|
||||
@@ -645,9 +644,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.11"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
|
||||
checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
@@ -728,12 +727,6 @@ dependencies = [
|
||||
"rustc_version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "human-sort"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "140a09c9305e6d5e557e2ed7cbc68e05765a7d4213975b87cb04920689cc6219"
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
@@ -742,9 +735,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.1.0"
|
||||
version = "2.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
|
||||
checksum = "824b2ae422412366ba479e8111fd301f7b5faece8149317bb81925979a53f520"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
@@ -797,6 +790,15 @@ version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
@@ -808,9 +810,29 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.151"
|
||||
version = "0.2.153"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
|
||||
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
||||
|
||||
[[package]]
|
||||
name = "liblzma"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b864ea21f326142b902d10658834cc1c9e758c96a579f4b0b1aa655edb0b351c"
|
||||
dependencies = [
|
||||
"liblzma-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "liblzma-sys"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be9aaba5f9c8f8f615d41570909338b6284fbb1813dc057ecc68563d98a65097"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
@@ -820,9 +842,9 @@ checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.12"
|
||||
version = "0.4.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456"
|
||||
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
@@ -850,7 +872,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex-syntax 0.6.29",
|
||||
"syn 2.0.43",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -864,23 +886,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lz4_flex"
|
||||
version = "0.11.1"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ea9b256699eda7b0387ffbc776dd625e28bde3918446381781245b7a50349d8"
|
||||
checksum = "912b45c753ff5f7f5208307e8ace7d2a2e30d024e26d3509f3dce546c044ce15"
|
||||
dependencies = [
|
||||
"twox-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lzma-sys"
|
||||
version = "0.1.20"
|
||||
source = "git+https://github.com/chenxiaolong/xz2-rs?rev=fe2050b9c3395db15d8610f1dabb505440c1a556#fe2050b9c3395db15d8610f1dabb505440c1a556"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "maybe-owned"
|
||||
version = "0.3.4"
|
||||
@@ -922,14 +934,14 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.43",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.7.1"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
|
||||
checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
|
||||
dependencies = [
|
||||
"adler",
|
||||
]
|
||||
@@ -946,11 +958,21 @@ version = "0.27.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
|
||||
dependencies = [
|
||||
"bitflags 2.4.1",
|
||||
"bitflags 2.4.2",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.46.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
|
||||
dependencies = [
|
||||
"overload",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint-dig"
|
||||
version = "0.8.4"
|
||||
@@ -1006,6 +1028,12 @@ version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "overload"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||
|
||||
[[package]]
|
||||
name = "pbkdf2"
|
||||
version = "0.12.2"
|
||||
@@ -1065,7 +1093,7 @@ dependencies = [
|
||||
"phf_shared",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.43",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1077,6 +1105,12 @@ dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
|
||||
|
||||
[[package]]
|
||||
name = "pkcs1"
|
||||
version = "0.7.5"
|
||||
@@ -1117,9 +1151,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.28"
|
||||
version = "0.3.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a"
|
||||
checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
@@ -1129,19 +1163,19 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.15"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d"
|
||||
checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn 2.0.43",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.71"
|
||||
version = "1.0.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8"
|
||||
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -1173,7 +1207,7 @@ dependencies = [
|
||||
"prost",
|
||||
"prost-types",
|
||||
"regex",
|
||||
"syn 2.0.43",
|
||||
"syn 2.0.48",
|
||||
"tempfile",
|
||||
"which",
|
||||
]
|
||||
@@ -1188,7 +1222,7 @@ dependencies = [
|
||||
"itertools",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.43",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1242,9 +1276,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.33"
|
||||
version = "1.0.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -1281,9 +1315,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.8.0"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
|
||||
checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
@@ -1291,9 +1325,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.12.0"
|
||||
version = "1.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
|
||||
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
@@ -1310,9 +1344,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.2"
|
||||
version = "1.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
|
||||
checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -1322,9 +1356,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.3"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
|
||||
checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -1411,11 +1445,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.28"
|
||||
version = "0.38.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316"
|
||||
checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949"
|
||||
dependencies = [
|
||||
"bitflags 2.4.1",
|
||||
"bitflags 2.4.2",
|
||||
"errno",
|
||||
"itoa",
|
||||
"libc",
|
||||
@@ -1433,15 +1467,6 @@ dependencies = [
|
||||
"cipher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scrypt"
|
||||
version = "0.11.0"
|
||||
@@ -1455,28 +1480,28 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.20"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
|
||||
checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.193"
|
||||
version = "1.0.196"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
|
||||
checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.193"
|
||||
version = "1.0.196"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
|
||||
checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.43",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1510,6 +1535,15 @@ dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sharded-slab"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signature"
|
||||
version = "2.2.0"
|
||||
@@ -1528,9 +1562,9 @@ checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.11.2"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
|
||||
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
@@ -1585,9 +1619,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.43"
|
||||
version = "2.0.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee659fb5f3d355364e1f3e5bc10fb82068efbf824a1e9d1c9504244a6469ad53"
|
||||
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1609,29 +1643,39 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.52"
|
||||
version = "1.0.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83a48fd946b02c0a526b2e9481c8e2a17755e47039164a86c4070446e3a4614d"
|
||||
checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.52"
|
||||
version = "1.0.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7fbe9b594d6568a6a1443250a7e67d80b74e1e96f6d1715e1e21cc1888291d3"
|
||||
checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.43",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tls_codec"
|
||||
version = "0.4.0"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d38a1d5fcfa859f0ec2b5e111dc903890bd7dac7f34713232bf9aa4fd7cad7b2"
|
||||
checksum = "b5e78c9c330f8c85b2bae7c8368f2739157db9991235123aa1b15ef9502bfb6a"
|
||||
dependencies = [
|
||||
"tls_codec_derive",
|
||||
"zeroize",
|
||||
@@ -1639,13 +1683,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tls_codec_derive"
|
||||
version = "0.4.0"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8e00e3e7a54e0f1c8834ce72ed49c8487fbd3f801d8cfe1a0ad0640382f8e15"
|
||||
checksum = "8d9ef545650e79f30233c0003bcc2504d7efac6dad25fca40744de773fe2049c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.43",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1659,9 +1703,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.21.0"
|
||||
version = "0.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03"
|
||||
checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
@@ -1676,6 +1720,63 @@ version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea68304e134ecd095ac6c3574494fc62b909f416c4fca77e440530221e549d3d"
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
|
||||
dependencies = [
|
||||
"pin-project-lite",
|
||||
"tracing-attributes",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"valuable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-log"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
|
||||
dependencies = [
|
||||
"log",
|
||||
"once_cell",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.3.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
|
||||
dependencies = [
|
||||
"nu-ansi-term",
|
||||
"sharded-slab",
|
||||
"smallvec",
|
||||
"thread_local",
|
||||
"tracing-core",
|
||||
"tracing-log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "twox-hash"
|
||||
version = "1.6.3"
|
||||
@@ -1718,29 +1819,25 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.6.1"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560"
|
||||
checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "valuable"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
@@ -1775,15 +1872,6 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
@@ -1924,9 +2012,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.5.31"
|
||||
version = "0.5.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97a4882e6b134d6c28953a387571f1acdd3496830d5e36c5e3a1075580ea641c"
|
||||
checksum = "a7cad8365489051ae9f054164e459304af2e7e9bb407c958076c8bf4aef52da5"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@@ -1937,7 +2025,7 @@ version = "0.36.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9643b83820c0cd246ecabe5fa454dd04ba4fa67996369466d0747472d337346"
|
||||
dependencies = [
|
||||
"bitflags 2.4.1",
|
||||
"bitflags 2.4.2",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
@@ -1957,24 +2045,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "xtask"
|
||||
version = "3.0.0"
|
||||
version = "3.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"human-sort",
|
||||
"regex",
|
||||
"tempfile",
|
||||
"toml_edit",
|
||||
"walkdir",
|
||||
"zip",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xz2"
|
||||
version = "0.1.7"
|
||||
source = "git+https://github.com/chenxiaolong/xz2-rs?rev=fe2050b9c3395db15d8610f1dabb505440c1a556#fe2050b9c3395db15d8610f1dabb505440c1a556"
|
||||
dependencies = [
|
||||
"lzma-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1994,7 +2070,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.43",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ members = ["avbroot", "e2e", "fuzz", "xtask"]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
version = "3.0.0"
|
||||
version = "3.1.0"
|
||||
license = "GPL-3.0-only"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/chenxiaolong/avbroot"
|
||||
|
||||
@@ -33,11 +33,7 @@ avbroot applies the following patches to the partition images:
|
||||
|
||||
Repeat: **_ALWAYS leave `OEM unlocking` enabled if rooted._**
|
||||
|
||||
* Any operation that causes an improperly-signed boot image to be flashed will result in the device being unbootable and unrecoverable without unlocking the bootloader again (and thus, triggering a data wipe). This includes:
|
||||
|
||||
* Performing a unpatched A/B OTA update while booted into Android via the OS' default updater. This can be blocked via a Magisk/KernelSU module (see: [Blocking A/B OTA Updates](#blocking-ab-ota-updates)).
|
||||
|
||||
* The `Direct install` method for updating Magisk. Magisk updates **must** be done by repatching the OTA, not via the app.
|
||||
* Any operation that causes an improperly-signed boot image to be flashed will result in the device being unbootable and unrecoverable without unlocking the bootloader again (and thus, triggering a data wipe). This includes the `Direct install` method for updating Magisk. Magisk updates **must** be done by repatching the OTA, not via the app.
|
||||
|
||||
If the boot image is ever modified, **do not reboot**. [Open an issue](https://github.com/chenxiaolong/avbroot/issues/new) for support and be very clear about what steps were done that lead to the situation. If Android is still running and root access works, it might be possible to recover without wiping and starting over.
|
||||
|
||||
@@ -148,15 +144,17 @@ If you lose your AVB or OTA signing key, you will no longer be able to sign new
|
||||
|
||||
4. Flash the partition images that were extracted.
|
||||
|
||||
This can be done by manually running `fastboot flash <partition> extracted/<partition>.img` for each image in the `extracted/` directory or by using the following script:
|
||||
For each partition inside `extracted/`, except for `system`, run:
|
||||
|
||||
```bash
|
||||
for image in extracted/*.img; do
|
||||
partition=$(basename "${image}")
|
||||
partition=${partition%.img}
|
||||
fastboot flash <partition> extracted/<partition>.img
|
||||
```
|
||||
|
||||
fastboot flash "${partition}" "${image}"
|
||||
done
|
||||
Then, reboot into recovery's fastbootd mode and flash `system`:
|
||||
|
||||
```bash
|
||||
fastboot reboot fastboot
|
||||
fastboot flash system extracted/system.img
|
||||
```
|
||||
|
||||
5. Set up the custom AVB public key in the bootloader.
|
||||
@@ -184,12 +182,10 @@ If you lose your AVB or OTA signing key, you will no longer be able to sign new
|
||||
|
||||
Remember: **Do not uncheck `OEM unlocking`!**
|
||||
|
||||
**WARNING**: If you are flashing CalyxOS, the setup wizard will [automatically turn off the `OEM unlocking` switch](https://github.com/CalyxOS/platform_packages_apps_SetupWizard/blob/7d2df25cedcbff83ddb608e628f9d97b38259c26/src/org/lineageos/setupwizard/SetupWizardApp.java#L135-L140). Make sure to manually reenable it again from Android's developer settings. Consider using [avbroot's `oemunlockonboot` Magisk module](#oemunlockonboot-enable-oem-unlocking-on-every-boot) to automatically ensure OEM unlocking is enabled on every boot.
|
||||
**WARNING**: If you are flashing CalyxOS, the setup wizard will [automatically turn off the `OEM unlocking` switch](https://github.com/CalyxOS/platform_packages_apps_SetupWizard/blob/7d2df25cedcbff83ddb608e628f9d97b38259c26/src/org/lineageos/setupwizard/SetupWizardApp.java#L135-L140). Make sure to manually reenable it again from Android's developer settings. Consider using the [`OEMUnlockOnBoot` module](https://github.com/chenxiaolong/OEMUnlockOnBoot) to automatically ensure OEM unlocking is enabled on every boot.
|
||||
|
||||
8. That's it! To install future OS, Magisk, or KernelSU updates, see the [next section](#updates).
|
||||
|
||||
For extra safety, consider flashing [avbroot's Magisk/KernelSU modules](#avbroot-modules).
|
||||
|
||||
## Updates
|
||||
|
||||
Updates to Android, Magisk, and KernelSU are all done the same way by patching (or repatching) the OTA.
|
||||
@@ -251,16 +247,6 @@ To safely use Repair Mode:
|
||||
|
||||
Because the unrooting and rooting are done by flashing OTAs, the device's data will not be wiped.
|
||||
|
||||
## avbroot modules
|
||||
|
||||
avbroot's Magisk/KernelSU modules can be downloaded from the [releases page](https://github.com/chenxiaolong/avbroot/releases).
|
||||
|
||||
### `oemunlockonboot`: Enable OEM unlocking on every boot
|
||||
|
||||
To help reduce the risk of OEM unlocking being accidentally disabled (or intentionally disabled as part of some OS' initial setup wizard), this module will attempt to enable the OEM unlocking option on every boot.
|
||||
|
||||
The logs for this module can be found at `/data/local/tmp/avbroot_oem_unlock.log`.
|
||||
|
||||
## Magisk preinit device
|
||||
|
||||
Magisk versions 25211 and newer require a writable partition for storing custom SELinux rules that need to be accessed during early boot stages. This can only be determined on a real device, so avbroot requires the partition to be explicitly specified via `--magisk-preinit-device <name>`. To find the partition name:
|
||||
@@ -437,14 +423,6 @@ Debug builds work too, but they will run significantly slower (in the sha256 com
|
||||
|
||||
By default, the executable links to the system's bzip2 and liblzma libraries, which are the only external libraries avbroot depends on. To compile and statically link these two libraries, pass in `--features static`.
|
||||
|
||||
To build avbroot's modules from source, run:
|
||||
|
||||
```bash
|
||||
cargo xtask modules -a
|
||||
```
|
||||
|
||||
This requires Java and the Android SDK to be installed. The `ANDROID_HOME` environment variable must be set to the Android SDK path.
|
||||
|
||||
## Verifying digital signatures
|
||||
|
||||
First, save the public key to a file listing the keys to be trusted. This is the same key listed in [the author's profile](https://github.com/chenxiaolong/).
|
||||
|
||||
+6
-11
@@ -14,8 +14,8 @@ base64 = "0.21.3"
|
||||
bitflags = "2.4.1"
|
||||
bstr = "1.6.2"
|
||||
byteorder = "1.4.3"
|
||||
cap-std = "2.0.0"
|
||||
cap-tempfile = "2.0.0"
|
||||
cap-std = "3.0.0"
|
||||
cap-tempfile = "3.0.0"
|
||||
clap = { version = "4.4.1", features = ["derive"] }
|
||||
clap_complete = "4.4.0"
|
||||
cms = { version = "0.2.2", features = ["std"] }
|
||||
@@ -24,6 +24,7 @@ ctrlc = "3.4.0"
|
||||
flate2 = "1.0.27"
|
||||
gf256 = { version = "0.3.0", features = ["rs"] }
|
||||
hex = { version = "0.4.3", features = ["serde"] }
|
||||
liblzma = "0.2.1"
|
||||
lz4_flex = "0.11.1"
|
||||
memchr = "2.6.0"
|
||||
num-bigint-dig = "0.8.4"
|
||||
@@ -48,6 +49,8 @@ tempfile = "3.8.0"
|
||||
thiserror = "1.0.47"
|
||||
toml_edit = { version = "0.21.0", features = ["serde"] }
|
||||
topological-sort = "0.2.2"
|
||||
tracing = "0.1.40"
|
||||
tracing-subscriber = "0.3.18"
|
||||
x509-cert = { version = "0.2.4", features = ["builder"] }
|
||||
|
||||
# There's an upstream bug that causes an infinite loop in the write::BzDecoder
|
||||
@@ -59,14 +62,6 @@ x509-cert = { version = "0.2.4", features = ["builder"] }
|
||||
git = "https://github.com/jongiddy/bzip2-rs"
|
||||
rev = "2aefcb4d3634de1df226c73d93f758d65228bb8c"
|
||||
|
||||
# The upstream xz2 crate uses an old version of liblzma when compiling with the
|
||||
# `static` feature and doesn't enable all of the encoders and decoders. This
|
||||
# causes certain payload data to fail to decompress.
|
||||
# https://github.com/chenxiaolong/avbroot/issues/138
|
||||
[dependencies.xz2]
|
||||
git = "https://github.com/chenxiaolong/xz2-rs"
|
||||
rev = "fe2050b9c3395db15d8610f1dabb505440c1a556"
|
||||
|
||||
# https://github.com/zip-rs/zip/pull/383
|
||||
[dependencies.zip]
|
||||
git = "https://github.com/chenxiaolong/zip"
|
||||
@@ -85,4 +80,4 @@ protox = "0.5.0"
|
||||
assert_matches = "1.5.0"
|
||||
|
||||
[features]
|
||||
static = ["bzip2/static", "xz2/static"]
|
||||
static = ["bzip2/static", "liblzma/static"]
|
||||
|
||||
+118
-3
@@ -3,10 +3,17 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*/
|
||||
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::{
|
||||
fmt,
|
||||
io::{self, IsTerminal},
|
||||
sync::atomic::{AtomicBool, Ordering},
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::{Parser, Subcommand};
|
||||
use clap::{Parser, Subcommand, ValueEnum};
|
||||
use tracing::{debug, Level};
|
||||
use tracing_subscriber::fmt::{format::Writer, time::FormatTime};
|
||||
|
||||
use crate::cli::{avb, boot, completion, cpio, fec, hashtree, key, ota};
|
||||
|
||||
@@ -29,16 +36,124 @@ pub enum Command {
|
||||
MagiskInfo(boot::MagiskInfoCli),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, ValueEnum)]
|
||||
pub enum LogLevel {
|
||||
Trace,
|
||||
Debug,
|
||||
Info,
|
||||
Warn,
|
||||
Error,
|
||||
}
|
||||
|
||||
impl LogLevel {
|
||||
fn as_level(self) -> Level {
|
||||
match self {
|
||||
Self::Trace => Level::TRACE,
|
||||
Self::Debug => Level::DEBUG,
|
||||
Self::Info => Level::INFO,
|
||||
Self::Warn => Level::WARN,
|
||||
Self::Error => Level::ERROR,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for LogLevel {
|
||||
fn default() -> Self {
|
||||
Self::Info
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for LogLevel {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(self.to_possible_value().ok_or(fmt::Error)?.get_name())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, ValueEnum)]
|
||||
pub enum LogFormat {
|
||||
Short,
|
||||
Medium,
|
||||
Long,
|
||||
}
|
||||
|
||||
impl Default for LogFormat {
|
||||
fn default() -> Self {
|
||||
Self::Short
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for LogFormat {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(self.to_possible_value().ok_or(fmt::Error)?.get_name())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(version)]
|
||||
pub struct Cli {
|
||||
#[command(subcommand)]
|
||||
pub command: Command,
|
||||
|
||||
/// Lowest log message severity to output.
|
||||
#[arg(long, global = true, value_name = "LEVEL", default_value_t)]
|
||||
pub log_level: LogLevel,
|
||||
|
||||
/// Output format for log messages.
|
||||
#[arg(long, global = true, value_name = "FORMAT", default_value_t)]
|
||||
pub log_format: LogFormat,
|
||||
}
|
||||
|
||||
pub fn main(cancel_signal: &AtomicBool) -> Result<()> {
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct ShortUptime {
|
||||
epoch: Instant,
|
||||
}
|
||||
|
||||
impl Default for ShortUptime {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
epoch: Instant::now(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FormatTime for ShortUptime {
|
||||
fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result {
|
||||
let e = self.epoch.elapsed();
|
||||
write!(w, "{:3}.{:03}s", e.as_secs(), e.subsec_millis())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_logging(log_level: LogLevel, log_format: LogFormat) {
|
||||
let builder = tracing_subscriber::fmt()
|
||||
.with_writer(io::stderr)
|
||||
.with_ansi(io::stderr().is_terminal())
|
||||
.with_max_level(log_level.as_level());
|
||||
|
||||
match log_format {
|
||||
LogFormat::Short => {
|
||||
let format = tracing_subscriber::fmt::format()
|
||||
.with_timer(ShortUptime::default())
|
||||
.with_target(false);
|
||||
|
||||
builder.event_format(format).init();
|
||||
}
|
||||
LogFormat::Medium => {
|
||||
builder.with_timer(ShortUptime::default()).init();
|
||||
}
|
||||
LogFormat::Long => {
|
||||
builder.pretty().init();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main(logging_initialized: &AtomicBool, cancel_signal: &AtomicBool) -> Result<()> {
|
||||
let cli = Cli::parse();
|
||||
|
||||
init_logging(cli.log_level, cli.log_format);
|
||||
logging_initialized.store(true, Ordering::SeqCst);
|
||||
|
||||
debug!(?cli);
|
||||
|
||||
match cli.command {
|
||||
Command::Avb(c) => avb::avb_main(&c, cancel_signal),
|
||||
Command::Boot(c) => boot::boot_main(&c),
|
||||
|
||||
+24
-19
@@ -22,9 +22,9 @@ use clap::{Args, Parser, Subcommand};
|
||||
use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
|
||||
use rsa::RsaPublicKey;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::{debug_span, info, warn, Span};
|
||||
|
||||
use crate::{
|
||||
cli::{status, warning},
|
||||
crypto::{self, PassphraseSource},
|
||||
format::avb::{
|
||||
self, AlgorithmType, AppendedDescriptorMut, AppendedDescriptorRef, Descriptor, Footer,
|
||||
@@ -99,7 +99,7 @@ fn promote_insecure_hash_algorithm(algorithm: &str) -> &str {
|
||||
const NEW_ALGORITHM: &str = "sha256";
|
||||
|
||||
if INSECURE_ALGORITHMS.contains(&algorithm) {
|
||||
warning!("Changing insecure hash algorithm {algorithm} to {NEW_ALGORITHM}");
|
||||
warn!("Changing insecure hash algorithm {algorithm} to {NEW_ALGORITHM}");
|
||||
NEW_ALGORITHM
|
||||
} else {
|
||||
algorithm
|
||||
@@ -166,7 +166,7 @@ fn write_raw_and_verify(
|
||||
|
||||
if let Err(e) = result {
|
||||
if ignore_invalid {
|
||||
warning!("{e:?}");
|
||||
warn!("{e:?}");
|
||||
} else {
|
||||
return Err(e);
|
||||
}
|
||||
@@ -358,16 +358,16 @@ fn sign_or_clear(info: &mut AvbInfo, orig_header: &Header, key_group: &KeyGroup)
|
||||
match sign_action {
|
||||
SignAction::None => {
|
||||
if originally_signed {
|
||||
status!("Preserving original AVB header signature");
|
||||
info!("Preserving original AVB header signature");
|
||||
} else {
|
||||
status!("Leaving AVB header unsigned");
|
||||
info!("Leaving AVB header unsigned");
|
||||
}
|
||||
}
|
||||
SignAction::Sign => {
|
||||
if originally_signed {
|
||||
status!("Replacing AVB header signature");
|
||||
info!("Replacing AVB header signature");
|
||||
} else {
|
||||
status!("Signing AVB header");
|
||||
info!("Signing AVB header");
|
||||
}
|
||||
|
||||
let Some(key_path) = &key_group.key else {
|
||||
@@ -389,9 +389,9 @@ fn sign_or_clear(info: &mut AvbInfo, orig_header: &Header, key_group: &KeyGroup)
|
||||
}
|
||||
SignAction::Clear => {
|
||||
if originally_signed {
|
||||
status!("Clearing AVB header signature");
|
||||
info!("Clearing AVB header signature");
|
||||
} else {
|
||||
status!("Leaving AVB header unsigned");
|
||||
info!("Leaving AVB header unsigned");
|
||||
}
|
||||
|
||||
info.header.algorithm_type = AlgorithmType::None;
|
||||
@@ -451,15 +451,15 @@ pub fn verify_headers(
|
||||
|
||||
if let Some(e) = expected_key {
|
||||
if k == e {
|
||||
status!("{prefix}");
|
||||
info!("{prefix}");
|
||||
} else {
|
||||
bail!("{prefix}, but is signed by an untrusted key");
|
||||
}
|
||||
} else {
|
||||
warning!("{prefix}, but parent does not list a trusted key");
|
||||
warn!("{prefix}, but parent does not list a trusted key");
|
||||
}
|
||||
} else {
|
||||
status!("{name} has an unsigned vbmeta header");
|
||||
info!("{name} has an unsigned vbmeta header");
|
||||
}
|
||||
|
||||
for descriptor in &header.descriptors {
|
||||
@@ -502,6 +502,7 @@ fn verify_and_repair(
|
||||
repair: bool,
|
||||
cancel_signal: &AtomicBool,
|
||||
) -> Result<()> {
|
||||
let _span = debug_span!("image", name = name.unwrap_or_default()).entered();
|
||||
let suffix = match name {
|
||||
Some(n) => format!(" for: {n}"),
|
||||
None => String::new(),
|
||||
@@ -509,18 +510,18 @@ fn verify_and_repair(
|
||||
|
||||
match descriptor {
|
||||
AppendedDescriptorRef::HashTree(d) => {
|
||||
status!("Verifying hash tree descriptor{suffix}");
|
||||
info!("Verifying hash tree descriptor{suffix}");
|
||||
|
||||
match d.verify(&file, cancel_signal) {
|
||||
Err(e @ avb::Error::HashTree(_)) if repair => {
|
||||
warning!("Failed to verify hash tree descriptor{suffix}: {e}");
|
||||
warning!("Attempting to repair using FEC data{suffix}");
|
||||
warn!("Failed to verify hash tree descriptor{suffix}: {e}");
|
||||
warn!("Attempting to repair using FEC data{suffix}");
|
||||
|
||||
d.repair(&file, &file, cancel_signal)
|
||||
.with_context(|| format!("Failed to repair data{suffix}"))?;
|
||||
|
||||
d.verify(&file, cancel_signal).map(|_| {
|
||||
status!("Successfully repaired data{suffix}");
|
||||
info!("Successfully repaired data{suffix}");
|
||||
})
|
||||
}
|
||||
ret => ret,
|
||||
@@ -528,7 +529,7 @@ fn verify_and_repair(
|
||||
.with_context(|| format!("Failed to verify hash tree descriptor{suffix}"))?;
|
||||
}
|
||||
AppendedDescriptorRef::Hash(d) => {
|
||||
status!("Verifying hash descriptor{suffix}");
|
||||
info!("Verifying hash descriptor{suffix}");
|
||||
|
||||
file.rewind()?;
|
||||
d.verify(file, cancel_signal)
|
||||
@@ -547,9 +548,13 @@ pub fn verify_descriptors(
|
||||
repair: bool,
|
||||
cancel_signal: &AtomicBool,
|
||||
) -> Result<()> {
|
||||
let parent_span = Span::current();
|
||||
|
||||
descriptors
|
||||
.par_iter()
|
||||
.map(|(name, descriptor)| {
|
||||
let _span = parent_span.enter();
|
||||
|
||||
let path = format!("{name}.img");
|
||||
let file = match directory
|
||||
.open_with(&path, OpenOptions::new().read(true).write(repair))
|
||||
@@ -560,7 +565,7 @@ pub fn verify_descriptors(
|
||||
// refer to partitions that exist on the device, but not in the
|
||||
// OTA.
|
||||
Err(e) if e.kind() == io::ErrorKind::NotFound => {
|
||||
warning!("Partition image does not exist: {path:?}");
|
||||
warn!("Partition image does not exist: {path:?}");
|
||||
return Ok(());
|
||||
}
|
||||
Err(e) => {
|
||||
@@ -706,7 +711,7 @@ fn verify_subcommand(cli: &VerifyCli, cancel_signal: &AtomicBool) -> Result<()>
|
||||
)?;
|
||||
verify_descriptors(&directory, &descriptors, cli.repair, cancel_signal)?;
|
||||
|
||||
status!("Successfully verified all vbmeta signatures and hashes");
|
||||
info!("Successfully verified all vbmeta signatures and hashes");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -12,18 +12,3 @@ pub mod fec;
|
||||
pub mod hashtree;
|
||||
pub mod key;
|
||||
pub mod ota;
|
||||
|
||||
macro_rules! status {
|
||||
($($arg:tt)*) => {
|
||||
eprintln!("\x1b[1m[*] {}\x1b[0m", format!($($arg)*))
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! warning {
|
||||
($($arg:tt)*) => {
|
||||
eprintln!("\x1b[1;31m[WARNING] {}\x1b[0m", format!($($arg)*))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) use status;
|
||||
pub(crate) use warning;
|
||||
|
||||
+41
-37
@@ -13,7 +13,6 @@ use std::{
|
||||
ops::Range,
|
||||
path::{Path, PathBuf},
|
||||
sync::{atomic::AtomicBool, Mutex},
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
@@ -24,11 +23,12 @@ use rayon::{iter::IntoParallelRefIterator, prelude::ParallelIterator};
|
||||
use rsa::RsaPrivateKey;
|
||||
use tempfile::NamedTempFile;
|
||||
use topological_sort::TopologicalSort;
|
||||
use tracing::{debug_span, info, warn};
|
||||
use x509_cert::Certificate;
|
||||
use zip::{write::FileOptions, CompressionMethod, ZipArchive, ZipWriter};
|
||||
|
||||
use crate::{
|
||||
cli::{self, status, warning},
|
||||
cli,
|
||||
crypto::{self, PassphraseSource},
|
||||
format::{
|
||||
avb::Header,
|
||||
@@ -149,8 +149,10 @@ fn open_input_files(
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
for name in all_images {
|
||||
let _span = debug_span!("image", name).entered();
|
||||
|
||||
if let Some(path) = external_images.get(name) {
|
||||
status!("Opening external image: {name}: {path:?}");
|
||||
info!("Opening external image: {name}: {path:?}");
|
||||
|
||||
let file = File::open(path)
|
||||
.map(PSeekFile::new)
|
||||
@@ -163,7 +165,7 @@ fn open_input_files(
|
||||
},
|
||||
);
|
||||
} else {
|
||||
status!("Extracting from original payload: {name}");
|
||||
info!("Extracting from original payload: {name}");
|
||||
|
||||
let file = tempfile::tempfile()
|
||||
.map(PSeekFile::new)
|
||||
@@ -207,7 +209,7 @@ fn patch_boot_images<'a, 'b: 'a>(
|
||||
|
||||
let boot_partitions = required_images.iter_boot().collect::<Vec<_>>();
|
||||
|
||||
status!(
|
||||
info!(
|
||||
"Patching boot images: {}",
|
||||
joined(sorted(boot_partitions.iter())),
|
||||
);
|
||||
@@ -252,7 +254,9 @@ fn patch_system_image<'a, 'b: 'a>(
|
||||
bail!("No system partition found");
|
||||
};
|
||||
|
||||
status!("Patching system image: {target}");
|
||||
let _span = debug_span!("image", name = target).entered();
|
||||
|
||||
info!("Patching system image: {target}");
|
||||
|
||||
let input_file = input_files.get_mut(target).unwrap();
|
||||
|
||||
@@ -280,7 +284,7 @@ fn patch_system_image<'a, 'b: 'a>(
|
||||
|
||||
input_file.state = InputFileState::Modified;
|
||||
|
||||
status!("Patched otacerts.zip offsets in {target}: {ranges:?}");
|
||||
info!("Patched otacerts.zip offsets in {target}: {ranges:?}");
|
||||
|
||||
ranges.extend(other_ranges);
|
||||
|
||||
@@ -384,7 +388,7 @@ fn get_vbmeta_patch_order(
|
||||
}
|
||||
|
||||
if !missing.is_empty() {
|
||||
warning!("Partitions aren't protected by AVB: {:?}", joined(missing));
|
||||
warn!("Partitions aren't protected by AVB: {:?}", joined(missing));
|
||||
}
|
||||
|
||||
// Ensure that there's only a single root of trust. Otherwise, there could
|
||||
@@ -635,6 +639,8 @@ fn compress_image(
|
||||
ranges: Option<&[Range<u64>]>,
|
||||
cancel_signal: &AtomicBool,
|
||||
) -> Result<Vec<Range<usize>>> {
|
||||
let _span = debug_span!("image", name).entered();
|
||||
|
||||
file.rewind()?;
|
||||
|
||||
let writer = tempfile::tempfile()
|
||||
@@ -650,7 +656,7 @@ fn compress_image(
|
||||
.unwrap();
|
||||
|
||||
if let Some(r) = ranges {
|
||||
status!("Compressing partial image: {name}: {r:?}");
|
||||
info!("Compressing partial image: {name}: {r:?}");
|
||||
|
||||
match payload::compress_modified_image(
|
||||
&*file,
|
||||
@@ -668,13 +674,13 @@ fn compress_image(
|
||||
// If we can't take advantage of the optimization, we can still
|
||||
// compress the whole image.
|
||||
Err(payload::Error::ExtentsNotInOrder) => {
|
||||
warning!("Cannot use optimization for {name}: extents not in order");
|
||||
warn!("Cannot use optimization for {name}: extents not in order");
|
||||
}
|
||||
Err(e) => return Err(e.into()),
|
||||
}
|
||||
}
|
||||
|
||||
status!("Compressing full image: {name}");
|
||||
info!("Compressing full image: {name}");
|
||||
|
||||
// Otherwise, compress the entire image.
|
||||
let (partition_info, operations) =
|
||||
@@ -771,7 +777,7 @@ fn patch_ota_payload(
|
||||
|
||||
let mut vbmeta_order = get_vbmeta_patch_order(&mut input_files, &vbmeta_headers)?;
|
||||
|
||||
status!(
|
||||
info!(
|
||||
"Patching vbmeta images: {}",
|
||||
joined(vbmeta_order.iter().map(|(n, _)| n)),
|
||||
);
|
||||
@@ -810,7 +816,7 @@ fn patch_ota_payload(
|
||||
})
|
||||
.collect::<Result<HashMap<_, _>>>()?;
|
||||
|
||||
status!("Generating new OTA payload");
|
||||
info!("Generating new OTA payload");
|
||||
|
||||
let mut payload_writer = PayloadWriter::new(writer, header_locked.clone(), key_ota.clone())
|
||||
.context("Failed to write payload header")?;
|
||||
@@ -926,6 +932,8 @@ fn patch_ota_zip(
|
||||
let mut last_entry_used_zip64 = false;
|
||||
|
||||
for path in &paths {
|
||||
let _span = debug_span!("zip", entry = path).entered();
|
||||
|
||||
let mut reader = zip_reader
|
||||
.by_name(path)
|
||||
.with_context(|| format!("Failed to open zip entry: {path}"))?;
|
||||
@@ -984,13 +992,13 @@ fn patch_ota_zip(
|
||||
match path.as_str() {
|
||||
ota::PATH_OTACERT => {
|
||||
// Use the user's certificate
|
||||
status!("Replacing zip entry: {path}");
|
||||
info!("Replacing zip entry: {path}");
|
||||
|
||||
crypto::write_pem_cert(&mut writer, cert_ota)
|
||||
.with_context(|| format!("Failed to write entry: {path}"))?;
|
||||
}
|
||||
ota::PATH_PAYLOAD => {
|
||||
status!("Patching zip entry: {path}");
|
||||
info!("Patching zip entry: {path}");
|
||||
|
||||
if reader.compression() != CompressionMethod::Stored {
|
||||
bail!("{path} is not stored uncompressed");
|
||||
@@ -1022,7 +1030,7 @@ fn patch_ota_zip(
|
||||
payload_metadata_size = Some(m);
|
||||
}
|
||||
ota::PATH_PROPERTIES => {
|
||||
status!("Patching zip entry: {path}");
|
||||
info!("Patching zip entry: {path}");
|
||||
|
||||
// payload.bin is guaranteed to be patched first.
|
||||
writer
|
||||
@@ -1030,7 +1038,7 @@ fn patch_ota_zip(
|
||||
.with_context(|| format!("Failed to write payload properties: {path}"))?;
|
||||
}
|
||||
_ => {
|
||||
status!("Copying zip entry: {path}");
|
||||
info!("Copying zip entry: {path}");
|
||||
|
||||
stream::copy(&mut reader, &mut writer, cancel_signal)
|
||||
.with_context(|| format!("Failed to copy zip entry: {path}"))?;
|
||||
@@ -1049,7 +1057,7 @@ fn patch_ota_zip(
|
||||
last_entry_used_zip64 = use_zip64;
|
||||
}
|
||||
|
||||
status!("Generating new OTA metadata");
|
||||
info!("Generating new OTA metadata");
|
||||
|
||||
let data_descriptor_size = if last_entry_used_zip64 { 24 } else { 16 };
|
||||
let metadata = ota::add_metadata(
|
||||
@@ -1080,7 +1088,7 @@ fn extract_ota_zip(
|
||||
}
|
||||
}
|
||||
|
||||
status!("Extracting from the payload: {}", joined(images));
|
||||
info!("Extracting from the payload: {}", joined(images));
|
||||
|
||||
// Pre-open all output files.
|
||||
let output_files = images
|
||||
@@ -1113,6 +1121,8 @@ fn extract_ota_zip(
|
||||
)
|
||||
.context("Failed to extract images from payload")?;
|
||||
|
||||
info!("Successfully extracted OTA");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1166,7 +1176,7 @@ fn verify_partition_hashes(
|
||||
|
||||
pub fn patch_subcommand(cli: &PatchCli, cancel_signal: &AtomicBool) -> Result<()> {
|
||||
if cli.boot_partition.is_some() {
|
||||
warning!("Ignoring --boot-partition: deprecated and no longer needed");
|
||||
warn!("Ignoring --boot-partition: deprecated and no longer needed");
|
||||
}
|
||||
|
||||
let output = cli.output.as_ref().map_or_else(
|
||||
@@ -1222,7 +1232,6 @@ pub fn patch_subcommand(cli: &PatchCli, cancel_signal: &AtomicBool) -> Result<()
|
||||
cli.magisk_preinit_device.as_deref(),
|
||||
cli.magisk_random_seed,
|
||||
cli.ignore_magisk_warnings,
|
||||
move |s| warning!("{s}"),
|
||||
)
|
||||
.context("Failed to create Magisk boot image patcher")?,
|
||||
);
|
||||
@@ -1232,9 +1241,6 @@ pub fn patch_subcommand(cli: &PatchCli, cancel_signal: &AtomicBool) -> Result<()
|
||||
let patcher: Box<dyn BootImagePatch + Sync> = Box::new(PrepatchedImagePatcher::new(
|
||||
prepatched,
|
||||
cli.ignore_prepatched_compat + 1,
|
||||
move |s| {
|
||||
warning!("{s}");
|
||||
},
|
||||
));
|
||||
|
||||
Some(patcher)
|
||||
@@ -1243,8 +1249,6 @@ pub fn patch_subcommand(cli: &PatchCli, cancel_signal: &AtomicBool) -> Result<()
|
||||
None
|
||||
};
|
||||
|
||||
let start = Instant::now();
|
||||
|
||||
let raw_reader = File::open(&cli.input)
|
||||
.map(PSeekFile::new)
|
||||
.with_context(|| format!("Failed to open for reading: {:?}", cli.input))?;
|
||||
@@ -1292,7 +1296,7 @@ pub fn patch_subcommand(cli: &PatchCli, cancel_signal: &AtomicBool) -> Result<()
|
||||
temp_writer.flush().context("Failed to flush output zip")?;
|
||||
|
||||
// We do a lot of low-level hackery. Reopen and verify offsets.
|
||||
status!("Verifying metadata offsets");
|
||||
info!("Verifying metadata offsets");
|
||||
temp_writer.rewind().context("Failed to seek output zip")?;
|
||||
ota::verify_metadata(
|
||||
BufReader::new(&mut temp_writer),
|
||||
@@ -1301,7 +1305,7 @@ pub fn patch_subcommand(cli: &PatchCli, cancel_signal: &AtomicBool) -> Result<()
|
||||
)
|
||||
.context("Failed to verify OTA metadata offsets")?;
|
||||
|
||||
status!("Completed after {:.1}s", start.elapsed().as_secs_f64());
|
||||
info!("Successfully patched OTA");
|
||||
|
||||
// NamedTempFile forces 600 permissions on temp files because it's the safe
|
||||
// option for a shared /tmp. Since we're writing to the output file's
|
||||
@@ -1334,7 +1338,7 @@ pub fn patch_subcommand(cli: &PatchCli, cancel_signal: &AtomicBool) -> Result<()
|
||||
|
||||
pub fn extract_subcommand(cli: &ExtractCli, cancel_signal: &AtomicBool) -> Result<()> {
|
||||
if cli.boot_partition.is_some() {
|
||||
warning!("Ignoring --boot-partition: deprecated and no longer needed");
|
||||
warn!("Ignoring --boot-partition: deprecated and no longer needed");
|
||||
}
|
||||
|
||||
let raw_reader = File::open(&cli.input)
|
||||
@@ -1408,7 +1412,7 @@ pub fn verify_subcommand(cli: &VerifyCli, cancel_signal: &AtomicBool) -> Result<
|
||||
.with_context(|| format!("Failed to open for reading: {:?}", cli.input))?;
|
||||
let mut reader = BufReader::new(raw_reader);
|
||||
|
||||
status!("Verifying whole-file signature");
|
||||
info!("Verifying whole-file signature");
|
||||
|
||||
let embedded_cert = ota::verify_ota(&mut reader, cancel_signal)?;
|
||||
|
||||
@@ -1426,13 +1430,13 @@ pub fn verify_subcommand(cli: &VerifyCli, cancel_signal: &AtomicBool) -> Result<
|
||||
bail!("OTA has a valid signature, but was not signed with: {p:?}");
|
||||
}
|
||||
} else {
|
||||
warning!("Whole-file signature is valid, but its trust is unknown");
|
||||
warn!("Whole-file signature is valid, but its trust is unknown");
|
||||
}
|
||||
|
||||
ota::verify_metadata(&mut reader, &metadata, header.blob_offset)
|
||||
.context("Failed to verify OTA metadata offsets")?;
|
||||
|
||||
status!("Verifying payload");
|
||||
info!("Verifying payload");
|
||||
|
||||
let pfs_raw = metadata
|
||||
.property_files
|
||||
@@ -1450,7 +1454,7 @@ pub fn verify_subcommand(cli: &VerifyCli, cancel_signal: &AtomicBool) -> Result<
|
||||
|
||||
payload::verify_payload(section_reader, &ota_cert, &properties, cancel_signal)?;
|
||||
|
||||
status!("Extracting partition images to temporary directory");
|
||||
info!("Extracting partition images to temporary directory");
|
||||
|
||||
let authority = ambient_authority();
|
||||
let temp_dir = TempDir::new(authority).context("Failed to create temporary directory")?;
|
||||
@@ -1473,11 +1477,11 @@ pub fn verify_subcommand(cli: &VerifyCli, cancel_signal: &AtomicBool) -> Result<
|
||||
cancel_signal,
|
||||
)?;
|
||||
|
||||
status!("Verifying partition hashes");
|
||||
info!("Verifying partition hashes");
|
||||
|
||||
verify_partition_hashes(&temp_dir, &header, &unique_images, cancel_signal)?;
|
||||
|
||||
status!("Checking ramdisk's otacerts.zip");
|
||||
info!("Checking ramdisk's otacerts.zip");
|
||||
|
||||
{
|
||||
let required_images = RequiredImages::new(&header.manifest);
|
||||
@@ -1509,7 +1513,7 @@ pub fn verify_subcommand(cli: &VerifyCli, cancel_signal: &AtomicBool) -> Result<
|
||||
}
|
||||
}
|
||||
|
||||
status!("Verifying AVB signatures");
|
||||
info!("Verifying AVB signatures");
|
||||
|
||||
let public_key = if let Some(p) = &cli.public_key_avb {
|
||||
let data = fs::read(p).with_context(|| format!("Failed to read file: {p:?}"))?;
|
||||
@@ -1533,7 +1537,7 @@ pub fn verify_subcommand(cli: &VerifyCli, cancel_signal: &AtomicBool) -> Result<
|
||||
)?;
|
||||
cli::avb::verify_descriptors(&temp_dir, &descriptors, false, cancel_signal)?;
|
||||
|
||||
status!("Signatures are all valid!");
|
||||
info!("Signatures are all valid!");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -7,14 +7,14 @@ use std::io::{self, Read, Seek, Write};
|
||||
|
||||
use byteorder::{LittleEndian, WriteBytesExt};
|
||||
use flate2::{read::GzDecoder, write::GzEncoder, Compression};
|
||||
use lz4_flex::frame::FrameDecoder;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
use xz2::{
|
||||
use liblzma::{
|
||||
read::XzDecoder,
|
||||
stream::{Check, Stream},
|
||||
write::XzEncoder,
|
||||
};
|
||||
use lz4_flex::frame::FrameDecoder;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
static GZIP_MAGIC: &[u8; 2] = b"\x1f\x8b";
|
||||
static LZ4_LEGACY_MAGIC: &[u8; 4] = b"\x02\x21\x4c\x18";
|
||||
@@ -25,7 +25,7 @@ pub enum Error {
|
||||
#[error("Unknown compression format")]
|
||||
UnknownFormat,
|
||||
#[error("XZ stream error")]
|
||||
XzStream(#[from] xz2::stream::Error),
|
||||
XzStream(#[from] liblzma::stream::Error),
|
||||
#[error("I/O error")]
|
||||
Io(#[from] io::Error),
|
||||
}
|
||||
|
||||
@@ -807,12 +807,12 @@ mod tests {
|
||||
let size = 2 * 253 * 4096;
|
||||
let fec = Fec::new(size, 4096, 2).unwrap();
|
||||
|
||||
assert_eq!(fec.rounds_for_ranges(&[0..0]).unwrap(), HashSet::new(),);
|
||||
assert_eq!(fec.rounds_for_ranges(&[0..0]).unwrap(), HashSet::new());
|
||||
assert_eq!(
|
||||
fec.rounds_for_ranges(&[0..size]).unwrap(),
|
||||
HashSet::from([0, 1]),
|
||||
);
|
||||
assert_eq!(fec.rounds_for_ranges(&[0..1]).unwrap(), HashSet::from([0]),);
|
||||
assert_eq!(fec.rounds_for_ranges(&[0..1]).unwrap(), HashSet::from([0]));
|
||||
assert_eq!(
|
||||
fec.rounds_for_ranges(&[4095..4096]).unwrap(),
|
||||
HashSet::from([0]),
|
||||
|
||||
@@ -326,7 +326,7 @@ impl<'a> HashTree<'a> {
|
||||
cancel_signal: &AtomicBool,
|
||||
) -> Result<(Vec<u8>, Vec<u8>)> {
|
||||
let offsets = self.compute_level_offsets(image_size)?;
|
||||
let hash_tree_size = offsets.get(0).map(|r| r.end).unwrap_or(0);
|
||||
let hash_tree_size = offsets.first().map(|r| r.end).unwrap_or(0);
|
||||
let mut hash_tree_data = vec![0u8; hash_tree_size];
|
||||
|
||||
let root_digest = self.calculate(
|
||||
@@ -352,7 +352,7 @@ impl<'a> HashTree<'a> {
|
||||
cancel_signal: &AtomicBool,
|
||||
) -> Result<Vec<u8>> {
|
||||
let offsets = self.compute_level_offsets(image_size)?;
|
||||
let hash_tree_size = offsets.get(0).map(|r| r.end).unwrap_or(0);
|
||||
let hash_tree_size = offsets.first().map(|r| r.end).unwrap_or(0);
|
||||
if hash_tree_data.len() != hash_tree_size {
|
||||
return Err(Error::InvalidHashTreeSize {
|
||||
input: image_size,
|
||||
@@ -381,7 +381,7 @@ impl<'a> HashTree<'a> {
|
||||
cancel_signal: &AtomicBool,
|
||||
) -> Result<()> {
|
||||
let offsets = self.compute_level_offsets(image_size)?;
|
||||
let hash_tree_size = offsets.get(0).map(|r| r.end).unwrap_or(0);
|
||||
let hash_tree_size = offsets.first().map(|r| r.end).unwrap_or(0);
|
||||
if hash_tree_data.len() != hash_tree_size {
|
||||
return Err(Error::InvalidHashTreeSize {
|
||||
input: image_size,
|
||||
@@ -637,7 +637,7 @@ mod tests {
|
||||
hash_tree.blocks_for_ranges(16384, &[0..16384]).unwrap(),
|
||||
&[0..4],
|
||||
);
|
||||
assert_eq!(hash_tree.blocks_for_ranges(16384, &[0..0]).unwrap(), &[],);
|
||||
assert_eq!(hash_tree.blocks_for_ranges(16384, &[0..0]).unwrap(), &[]);
|
||||
assert_eq!(
|
||||
hash_tree
|
||||
.blocks_for_ranges(16384, &[12287..12289, 0..1, 5000..5001])
|
||||
|
||||
@@ -14,6 +14,11 @@ use base64::engine::general_purpose::STANDARD;
|
||||
use base64::Engine;
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
use bzip2::write::BzDecoder;
|
||||
use liblzma::{
|
||||
stream::{Check, Stream},
|
||||
write::XzDecoder,
|
||||
write::XzEncoder,
|
||||
};
|
||||
use num_traits::ToPrimitive;
|
||||
use prost::Message;
|
||||
use rayon::{
|
||||
@@ -25,11 +30,6 @@ use rsa::{traits::PublicKeyParts, Pkcs1v15Sign, RsaPrivateKey};
|
||||
use sha2::Sha256;
|
||||
use thiserror::Error;
|
||||
use x509_cert::Certificate;
|
||||
use xz2::{
|
||||
stream::{Check, Stream},
|
||||
write::XzDecoder,
|
||||
write::XzEncoder,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
crypto,
|
||||
@@ -99,7 +99,7 @@ pub enum Error {
|
||||
#[error("Failed to decode protobuf message")]
|
||||
ProtobufDecode(#[from] prost::DecodeError),
|
||||
#[error("XZ stream error")]
|
||||
XzStream(#[from] xz2::stream::Error),
|
||||
XzStream(#[from] liblzma::stream::Error),
|
||||
#[error("RSA error")]
|
||||
Rsa(#[from] rsa::Error),
|
||||
#[error("I/O error")]
|
||||
|
||||
+15
-3
@@ -8,9 +8,11 @@ use std::sync::{
|
||||
Arc,
|
||||
};
|
||||
|
||||
use anyhow::Result;
|
||||
use tracing::error;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
static LOGGING_INITIALIZED: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
fn main() {
|
||||
// Set up a cancel signal so we can properly clean up any temporary files.
|
||||
let cancel_signal = Arc::new(AtomicBool::new(false));
|
||||
{
|
||||
@@ -22,5 +24,15 @@ fn main() -> Result<()> {
|
||||
.expect("Failed to set signal handler");
|
||||
}
|
||||
|
||||
avbroot::cli::args::main(&cancel_signal)
|
||||
match avbroot::cli::args::main(&LOGGING_INITIALIZED, &cancel_signal) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
if LOGGING_INITIALIZED.load(Ordering::SeqCst) {
|
||||
error!("{e:?}");
|
||||
} else {
|
||||
eprintln!("{e:?}");
|
||||
}
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+134
-40
@@ -16,16 +16,17 @@ use std::{
|
||||
};
|
||||
|
||||
use bstr::ByteSlice;
|
||||
use liblzma::{
|
||||
stream::{Check, Stream},
|
||||
write::XzEncoder,
|
||||
};
|
||||
use rayon::iter::{IntoParallelRefIterator, IntoParallelRefMutIterator, ParallelIterator};
|
||||
use regex::bytes::Regex;
|
||||
use ring::digest::Context;
|
||||
use rsa::RsaPrivateKey;
|
||||
use thiserror::Error;
|
||||
use tracing::{debug, debug_span, trace, warn, Span};
|
||||
use x509_cert::Certificate;
|
||||
use xz2::{
|
||||
stream::{Check, Stream},
|
||||
write::XzEncoder,
|
||||
};
|
||||
use zip::{result::ZipError, ZipArchive};
|
||||
|
||||
use crate::{
|
||||
@@ -67,7 +68,7 @@ pub enum Error {
|
||||
#[error("OTA certificate error")]
|
||||
OtaCert(#[from] otacert::Error),
|
||||
#[error("XZ stream error")]
|
||||
XzStream(#[from] xz2::stream::Error),
|
||||
XzStream(#[from] liblzma::stream::Error),
|
||||
#[error("Zip error")]
|
||||
Zip(#[from] ZipError),
|
||||
#[error("I/O error")]
|
||||
@@ -86,6 +87,12 @@ fn load_ramdisk(
|
||||
let mut reader = CompressedReader::new(raw_reader, false)?;
|
||||
let entries = cpio::load(&mut reader, false, cancel_signal)?;
|
||||
|
||||
trace!(
|
||||
"Loaded {:?} ramdisk with {} entries",
|
||||
reader.format(),
|
||||
entries.len(),
|
||||
);
|
||||
|
||||
Ok((entries, reader.format()))
|
||||
}
|
||||
|
||||
@@ -98,6 +105,8 @@ fn save_ramdisk(
|
||||
let mut writer = CompressedWriter::new(raw_writer, format)?;
|
||||
cpio::save(&mut writer, entries, false, cancel_signal)?;
|
||||
|
||||
trace!("Wrote {format:?} ramdisk with {} entries", entries.len());
|
||||
|
||||
let raw_writer = writer.finish()?;
|
||||
Ok(raw_writer.into_inner())
|
||||
}
|
||||
@@ -140,20 +149,24 @@ impl MagiskRootPatcher {
|
||||
// RULESDEVICE config option, which stored the writable block device as an
|
||||
// rdev major/minor pair, which was not consistent across reboots and was
|
||||
// replaced by PREINITDEVICE
|
||||
const VERS_SUPPORTED: &'static [Range<u32>] = &[25102..25207, 25211..26500];
|
||||
const VERS_SUPPORTED: &'static [Range<u32>] = &[25102..25207, 25211..27100];
|
||||
const VER_PREINIT_DEVICE: Range<u32> =
|
||||
25211..Self::VERS_SUPPORTED[Self::VERS_SUPPORTED.len() - 1].end;
|
||||
const VER_RANDOM_SEED: Range<u32> = 25211..26103;
|
||||
const VER_PATCH_VBMETA: Range<u32> = Self::VERS_SUPPORTED[0].start..26202;
|
||||
const VER_XZ_BACKUP: Range<u32> =
|
||||
26403..Self::VERS_SUPPORTED[Self::VERS_SUPPORTED.len() - 1].end;
|
||||
|
||||
pub fn new(
|
||||
path: &Path,
|
||||
preinit_device: Option<&str>,
|
||||
random_seed: Option<u64>,
|
||||
ignore_compatibility: bool,
|
||||
warning_fn: impl Fn(&str) + Send + 'static,
|
||||
) -> Result<Self> {
|
||||
let version = Self::get_version(path)?;
|
||||
|
||||
debug!("Found Magisk version: {version}");
|
||||
|
||||
if !Self::VERS_SUPPORTED.iter().any(|v| v.contains(&version)) {
|
||||
let msg = format!(
|
||||
"Unsupported Magisk version {} (supported: {:?})",
|
||||
@@ -162,7 +175,7 @@ impl MagiskRootPatcher {
|
||||
);
|
||||
|
||||
if ignore_compatibility {
|
||||
warning_fn(&msg);
|
||||
warn!("{msg}");
|
||||
} else {
|
||||
return Err(Error::Validation(msg));
|
||||
}
|
||||
@@ -176,7 +189,7 @@ impl MagiskRootPatcher {
|
||||
);
|
||||
|
||||
if ignore_compatibility {
|
||||
warning_fn(&msg);
|
||||
warn!("{msg}");
|
||||
} else {
|
||||
return Err(Error::Validation(msg));
|
||||
}
|
||||
@@ -208,6 +221,8 @@ impl MagiskRootPatcher {
|
||||
}
|
||||
|
||||
if let Some(suffix) = line.trim_end().strip_prefix("MAGISK_VER_CODE=") {
|
||||
trace!("Magisk version code line: {line:?}");
|
||||
|
||||
let version = suffix
|
||||
.parse()
|
||||
.map_err(|e| Error::ParseMagiskVersion(suffix.to_owned(), e))?;
|
||||
@@ -216,6 +231,18 @@ impl MagiskRootPatcher {
|
||||
}
|
||||
}
|
||||
|
||||
fn xz_compress(reader: impl Read, cancel_signal: &AtomicBool) -> Result<Vec<u8>> {
|
||||
let stream = Stream::new_easy_encoder(9, Check::Crc32)?;
|
||||
let raw_writer = Cursor::new(Vec::new());
|
||||
let mut writer = XzEncoder::new_stream(raw_writer, stream);
|
||||
|
||||
stream::copy(reader, &mut writer, cancel_signal)?;
|
||||
|
||||
let raw_writer = writer.finish()?;
|
||||
|
||||
Ok(raw_writer.into_inner())
|
||||
}
|
||||
|
||||
/// Compare old and new ramdisk entry lists, creating the Magisk `.backup/`
|
||||
/// directory structure. `.backup/.rmlist` will contain a sorted list of
|
||||
/// NULL-terminated strings, listing which files were newly added or
|
||||
@@ -223,7 +250,12 @@ impl MagiskRootPatcher {
|
||||
/// entries as `.backup/<path>`.
|
||||
///
|
||||
/// Both lists and entries within the lists may be mutated.
|
||||
fn apply_magisk_backup(old_entries: &mut [CpioEntry], new_entries: &mut Vec<CpioEntry>) {
|
||||
fn apply_magisk_backup(
|
||||
old_entries: &mut [CpioEntry],
|
||||
new_entries: &mut Vec<CpioEntry>,
|
||||
xz_compress: bool,
|
||||
cancel_signal: &AtomicBool,
|
||||
) -> Result<()> {
|
||||
cpio::sort(old_entries);
|
||||
cpio::sort(new_entries);
|
||||
|
||||
@@ -268,11 +300,54 @@ impl MagiskRootPatcher {
|
||||
|
||||
new_entries.push(CpioEntry::new_directory(b".backup", 0));
|
||||
|
||||
debug!(
|
||||
"Removed entries: {:?}",
|
||||
rm_list
|
||||
.split(|b| *b == 0)
|
||||
.filter(|e| !e.is_empty())
|
||||
.map(|p| p.as_bstr().to_string())
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
debug!(
|
||||
"Added/changed entries: {:?}",
|
||||
to_back_up
|
||||
.iter()
|
||||
.map(|e| e.path.as_bstr().to_string())
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
|
||||
for old_entry in to_back_up {
|
||||
let mut new_entry = old_entry.clone();
|
||||
new_entry.path = b".backup/".to_vec();
|
||||
new_entry.path.extend(&old_entry.path);
|
||||
new_entries.push(new_entry);
|
||||
let mut new_path = b".backup/".to_vec();
|
||||
new_path.extend(&old_entry.path);
|
||||
|
||||
let mut new_data = None;
|
||||
|
||||
if xz_compress {
|
||||
if let CpioEntryData::Data(data) = &old_entry.data {
|
||||
new_path.extend(b".xz");
|
||||
|
||||
let reader = Cursor::new(data);
|
||||
let buf = Self::xz_compress(reader, cancel_signal)?;
|
||||
new_data = Some(CpioEntryData::Data(buf));
|
||||
}
|
||||
}
|
||||
|
||||
new_entries.push(CpioEntry {
|
||||
path: new_path,
|
||||
data: new_data.unwrap_or_else(|| old_entry.data.clone()),
|
||||
inode: old_entry.inode,
|
||||
file_type: old_entry.file_type,
|
||||
file_mode: old_entry.file_mode,
|
||||
uid: old_entry.uid,
|
||||
gid: old_entry.gid,
|
||||
nlink: old_entry.nlink,
|
||||
mtime: old_entry.mtime,
|
||||
dev_maj: old_entry.dev_maj,
|
||||
dev_min: old_entry.dev_min,
|
||||
rdev_maj: old_entry.rdev_maj,
|
||||
rdev_min: old_entry.rdev_min,
|
||||
crc32: old_entry.crc32,
|
||||
});
|
||||
}
|
||||
|
||||
new_entries.push(CpioEntry::new_file(
|
||||
@@ -280,6 +355,8 @@ impl MagiskRootPatcher {
|
||||
0,
|
||||
CpioEntryData::Data(rm_list),
|
||||
));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,7 +424,9 @@ impl BootImagePatch for MagiskRootPatcher {
|
||||
));
|
||||
}
|
||||
|
||||
// Add xz-compressed magisk32 and magisk64.
|
||||
// Add xz-compressed magisk32 and magisk64. We currently unconditionally
|
||||
// include magisk32 because the boot image itself doesn't contain
|
||||
// sufficient information to determine if a device is 64-bit only.
|
||||
let mut xz_files = HashMap::<&str, &[u8]>::new();
|
||||
xz_files.insert(
|
||||
"lib/armeabi-v7a/libmagisk32.so",
|
||||
@@ -361,34 +440,34 @@ impl BootImagePatch for MagiskRootPatcher {
|
||||
// Add stub apk, which only exists after Magisk commit
|
||||
// ad0e6511e11ebec65aa9b5b916e1397342850319.
|
||||
if zip.file_names().any(|n| n == "assets/stub.apk") {
|
||||
debug!("Magisk stub found");
|
||||
xz_files.insert("assets/stub.apk", b"overlay.d/sbin/stub.xz");
|
||||
}
|
||||
|
||||
for (source, target) in xz_files {
|
||||
let reader = zip.by_name(source)?;
|
||||
let raw_writer = Cursor::new(vec![]);
|
||||
let stream = Stream::new_easy_encoder(9, Check::Crc32)?;
|
||||
let mut writer = XzEncoder::new_stream(raw_writer, stream);
|
||||
let buf = Self::xz_compress(reader, cancel_signal)?;
|
||||
|
||||
stream::copy(reader, &mut writer, cancel_signal)?;
|
||||
|
||||
let raw_writer = writer.finish()?;
|
||||
|
||||
entries.push(CpioEntry::new_file(
|
||||
target,
|
||||
0o644,
|
||||
CpioEntryData::Data(raw_writer.into_inner()),
|
||||
));
|
||||
entries.push(CpioEntry::new_file(target, 0o644, CpioEntryData::Data(buf)));
|
||||
}
|
||||
|
||||
// Create Magisk .backup directory structure.
|
||||
Self::apply_magisk_backup(&mut old_entries, &mut entries);
|
||||
Self::apply_magisk_backup(
|
||||
&mut old_entries,
|
||||
&mut entries,
|
||||
Self::VER_XZ_BACKUP.contains(&self.version),
|
||||
cancel_signal,
|
||||
)?;
|
||||
|
||||
// Create Magisk config.
|
||||
let mut magisk_config = String::new();
|
||||
magisk_config.push_str("KEEPVERITY=true\n");
|
||||
magisk_config.push_str("KEEPFORCEENCRYPT=true\n");
|
||||
magisk_config.push_str("PATCHVBMETAFLAG=false\n");
|
||||
|
||||
if Self::VER_PATCH_VBMETA.contains(&self.version) {
|
||||
magisk_config.push_str("PATCHVBMETAFLAG=false\n");
|
||||
}
|
||||
|
||||
magisk_config.push_str("RECOVERYMODE=false\n");
|
||||
|
||||
if Self::VER_PREINIT_DEVICE.contains(&self.version) {
|
||||
@@ -408,6 +487,8 @@ impl BootImagePatch for MagiskRootPatcher {
|
||||
magisk_config.push_str(&format!("RANDOMSEED={:#x}\n", self.random_seed));
|
||||
}
|
||||
|
||||
trace!("Magisk config: {magisk_config:?}");
|
||||
|
||||
entries.push(CpioEntry::new_file(
|
||||
b".backup/.magisk",
|
||||
0,
|
||||
@@ -488,6 +569,7 @@ impl OtaCertPatcher {
|
||||
for index in 0..zip.len() {
|
||||
let zip_entry = zip.by_index(index)?;
|
||||
if !zip_entry.name().ends_with(".x509.pem") {
|
||||
debug!("Skipping invalid entry path: {}", zip_entry.name());
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -564,6 +646,7 @@ impl BootImagePatch for OtaCertPatcher {
|
||||
};
|
||||
|
||||
let new_zip = otacert::create_zip(&self.cert, OtaCertBuildFlags::empty())?;
|
||||
trace!("Generated new {} byte otacerts.zip", new_zip.len());
|
||||
|
||||
for ramdisk in ramdisks {
|
||||
if ramdisk.is_empty() {
|
||||
@@ -593,7 +676,6 @@ impl BootImagePatch for OtaCertPatcher {
|
||||
pub struct PrepatchedImagePatcher {
|
||||
prepatched: PathBuf,
|
||||
fatal_level: u8,
|
||||
warning_fn: Box<dyn Fn(&str) + Send + Sync>,
|
||||
}
|
||||
|
||||
impl PrepatchedImagePatcher {
|
||||
@@ -604,15 +686,10 @@ impl PrepatchedImagePatcher {
|
||||
const VERSION_REGEX: &'static str =
|
||||
r"Linux version ([0-9]+\.[0-9]+).[0-9]+-(android[0-9]+)-([0-9]+)-";
|
||||
|
||||
pub fn new(
|
||||
prepatched: &Path,
|
||||
fatal_level: u8,
|
||||
warning_fn: impl Fn(&str) + Send + Sync + 'static,
|
||||
) -> Self {
|
||||
pub fn new(prepatched: &Path, fatal_level: u8) -> Self {
|
||||
Self {
|
||||
prepatched: prepatched.to_owned(),
|
||||
fatal_level,
|
||||
warning_fn: Box::new(warning_fn),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -829,7 +906,7 @@ impl BootImagePatch for PrepatchedImagePatcher {
|
||||
msg.push_str(warning);
|
||||
}
|
||||
|
||||
(self.warning_fn)(&msg);
|
||||
warn!("{msg}");
|
||||
}
|
||||
|
||||
if !errors.is_empty() {
|
||||
@@ -853,9 +930,12 @@ pub fn load_boot_images<'a>(
|
||||
names: &[&'a str],
|
||||
open_input: impl Fn(&str) -> io::Result<Box<dyn ReadSeek>> + Sync,
|
||||
) -> Result<HashMap<&'a str, BootImageInfo>> {
|
||||
let parent_span = Span::current();
|
||||
|
||||
names
|
||||
.par_iter()
|
||||
.map(|name| {
|
||||
let _span = debug_span!(parent: &parent_span, "image", name).entered();
|
||||
let mut reader = open_input(name)?;
|
||||
|
||||
let (header, footer, image_size) = avb::load_image(&mut reader)?;
|
||||
@@ -873,6 +953,8 @@ pub fn load_boot_images<'a>(
|
||||
boot_image,
|
||||
};
|
||||
|
||||
trace!("Loaded {image_size} byte boot image: {name}");
|
||||
|
||||
Ok((*name, info))
|
||||
})
|
||||
.collect()
|
||||
@@ -891,6 +973,8 @@ pub fn patch_boot_images<'a>(
|
||||
patchers: &[Box<dyn BootImagePatch + Sync>],
|
||||
cancel_signal: &AtomicBool,
|
||||
) -> Result<HashSet<&'a str>> {
|
||||
let parent_span = Span::current();
|
||||
|
||||
// Preparse all images. Some patchers need to inspect every candidate.
|
||||
let mut images = load_boot_images(names, open_input)?;
|
||||
|
||||
@@ -898,16 +982,21 @@ pub fn patch_boot_images<'a>(
|
||||
let all_targets = patchers
|
||||
.par_iter()
|
||||
.map(|p| {
|
||||
let _span =
|
||||
debug_span!(parent: &parent_span, "patcher", name = p.patcher_name()).entered();
|
||||
p.find_targets(&images, cancel_signal).and_then(|targets| {
|
||||
if targets.is_empty() {
|
||||
Err(Error::NoTargets(p.patcher_name()))
|
||||
} else {
|
||||
debug!("Found patcher targets: {targets:?}");
|
||||
Ok(targets)
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
debug!("All patcher targets: {all_targets:?}");
|
||||
|
||||
// Regroup data so we can parallelize by target.
|
||||
let mut groups = HashMap::<&str, (BootImageInfo, Vec<&Box<dyn BootImagePatch + Sync>>)>::new();
|
||||
for (patcher, targets) in patchers.iter().zip(all_targets.into_iter()) {
|
||||
@@ -927,15 +1016,19 @@ pub fn patch_boot_images<'a>(
|
||||
groups
|
||||
.par_iter_mut()
|
||||
.try_for_each(|(_, (info, patchers))| -> Result<()> {
|
||||
patchers
|
||||
.iter()
|
||||
.try_for_each(|p| p.patch(&mut info.boot_image, cancel_signal))
|
||||
patchers.iter().try_for_each(|p| {
|
||||
let _span =
|
||||
debug_span!(parent: &parent_span, "patcher", name = p.patcher_name()).entered();
|
||||
p.patch(&mut info.boot_image, cancel_signal)
|
||||
})
|
||||
})?;
|
||||
|
||||
// Resign and write new images.
|
||||
groups
|
||||
.par_iter_mut()
|
||||
.map(|(name, (info, _))| {
|
||||
let _span = debug_span!(parent: &parent_span, "image", name).entered();
|
||||
|
||||
let AppendedDescriptorMut::Hash(descriptor) = info.header.appended_descriptor_mut()?
|
||||
else {
|
||||
return Err(Error::NoHashDescriptor);
|
||||
@@ -955,6 +1048,7 @@ pub fn patch_boot_images<'a>(
|
||||
descriptor.root_digest = context.finish().as_ref().to_vec();
|
||||
|
||||
if !info.header.public_key.is_empty() {
|
||||
debug!("Signing boot image");
|
||||
info.header.set_algo_for_key(key)?;
|
||||
info.header.sign(key)?;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ use std::{borrow::Cow, cmp::Ordering, io::Cursor};
|
||||
|
||||
use bitflags::bitflags;
|
||||
use thiserror::Error;
|
||||
use tracing::trace;
|
||||
use x509_cert::{der::asn1::BitString, Certificate};
|
||||
use zip::{result::ZipError, write::FileOptions, CompressionMethod, ZipWriter};
|
||||
|
||||
@@ -134,8 +135,12 @@ pub fn create_zip_with_size(cert: &Certificate, size: usize) -> Result<Vec<u8>>
|
||||
] {
|
||||
flags |= additional_flag;
|
||||
|
||||
trace!("Attempting to create {size} byte otacerts.zip: {flags:?}");
|
||||
|
||||
let mut data = create_zip(cert, flags)?;
|
||||
if data.len() <= size {
|
||||
trace!("Padding {} byte otacerts.zip to {size}", data.len());
|
||||
|
||||
pad_zip(&mut data, size)?;
|
||||
return Ok(data);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ use memchr::memmem;
|
||||
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
||||
use rsa::RsaPrivateKey;
|
||||
use thiserror::Error;
|
||||
use tracing::{debug, debug_span, trace, Span};
|
||||
use x509_cert::Certificate;
|
||||
use zip::ZipArchive;
|
||||
|
||||
@@ -50,6 +51,7 @@ type Result<T> = std::result::Result<T, Error>;
|
||||
fn find_zip_bounds(data: &[u8], eocd_offset: usize) -> Option<Range<usize>> {
|
||||
let eocd = &data[eocd_offset..];
|
||||
if eocd.len() < 22 {
|
||||
trace!("Buffer is too small to contain EOCD");
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -60,14 +62,18 @@ fn find_zip_bounds(data: &[u8], eocd_offset: usize) -> Option<Range<usize>> {
|
||||
let start = eocd_offset.checked_sub(cd_size)?.checked_sub(cd_offset)?;
|
||||
let end = eocd_offset.checked_add(22)?.checked_add(comment_size)?;
|
||||
if end > data.len() {
|
||||
trace!("End of zip is out of bounds");
|
||||
return None;
|
||||
}
|
||||
|
||||
trace!("Found zip bounds: {:?}", start..end);
|
||||
|
||||
let reader = SectionReader::new(Cursor::new(data), start as u64, (end - start) as u64).ok()?;
|
||||
let mut zip_reader = ZipArchive::new(reader).ok()?;
|
||||
|
||||
if zip_reader.is_empty() {
|
||||
// otacerts.zip files contain at least one cert.
|
||||
trace!("Zip is empty");
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -76,10 +82,13 @@ fn find_zip_bounds(data: &[u8], eocd_offset: usize) -> Option<Range<usize>> {
|
||||
|
||||
if !entry.name().ends_with(".x509.pem") {
|
||||
// otacerts.zip files only contain files named this way.
|
||||
trace!("Excluded due to invalid name: {:?}", entry.name());
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
debug!("Found otacerts.zip candidate");
|
||||
|
||||
// There's one or more entries and every one is named *.x509.pem.
|
||||
Some(start..end)
|
||||
}
|
||||
@@ -110,6 +119,8 @@ pub fn patch_system_image(
|
||||
// chunk boundaries.
|
||||
const CHUNK_SIZE: u64 = 2 * 1024 * 1024;
|
||||
|
||||
let parent_span = Span::current();
|
||||
|
||||
let (mut header, footer, image_size) = avb::load_image(input.reopen_boxed()?)?;
|
||||
let Some(mut footer) = footer else {
|
||||
return Err(Error::NoFooter);
|
||||
@@ -119,6 +130,7 @@ pub fn patch_system_image(
|
||||
};
|
||||
|
||||
let num_chunks = util::div_ceil(footer.original_image_size, CHUNK_SIZE);
|
||||
trace!("Parallel heuristics search for otacerts.zip with {num_chunks} chunks");
|
||||
|
||||
let modified_ranges = (0..num_chunks)
|
||||
.into_par_iter()
|
||||
@@ -137,6 +149,9 @@ pub fn patch_system_image(
|
||||
let mut ranges = Vec::<Range<u64>>::new();
|
||||
|
||||
for eocd_offset_rel in memmem::find_iter(&buf, ota::ZIP_EOCD_MAGIC) {
|
||||
let _span = debug_span!(parent: &parent_span, "otacerts", offset, eocd_offset_rel)
|
||||
.entered();
|
||||
|
||||
let Some(bounds_rel) = find_zip_bounds(&buf, eocd_offset_rel) else {
|
||||
continue;
|
||||
};
|
||||
@@ -168,7 +183,14 @@ pub fn patch_system_image(
|
||||
let update_ranges = if descriptor.hash_algorithm == "sha1" {
|
||||
// Promote to a secure algorithm. SHA1 is allowed for verification only.
|
||||
// The entire hash tree and FEC data will need to be recomputed.
|
||||
descriptor.hash_algorithm = "sha256".to_owned();
|
||||
let new_algorithm = "sha256".to_owned();
|
||||
|
||||
debug!(
|
||||
"Changing insecure hash algorithm {} to {new_algorithm}",
|
||||
descriptor.hash_algorithm,
|
||||
);
|
||||
|
||||
descriptor.hash_algorithm = new_algorithm;
|
||||
None
|
||||
} else {
|
||||
// Only need to update the hash tree and FEC data corresponding to the
|
||||
@@ -179,6 +201,7 @@ pub fn patch_system_image(
|
||||
descriptor.update(input, output, update_ranges, cancel_signal)?;
|
||||
|
||||
if !header.public_key.is_empty() {
|
||||
debug!("Signing system image");
|
||||
header.set_algo_for_key(key)?;
|
||||
header.sign(key)?;
|
||||
}
|
||||
|
||||
@@ -65,13 +65,14 @@ include-workspace = true
|
||||
bypass = [
|
||||
# Copies of unmodified crashwrangler objects for old macOS versions.
|
||||
{ name = "honggfuzz", allow-globs = ["honggfuzz/third_party/mac/CrashReport_*.o"] },
|
||||
# Test files for liblzma's test suite
|
||||
{ name = "liblzma-sys", allow-globs = ["xz/tests/compress_prepared_bcj_*"] },
|
||||
]
|
||||
|
||||
[sources]
|
||||
unknown-registry = "deny"
|
||||
unknown-git = "deny"
|
||||
allow-git = [
|
||||
"https://github.com/chenxiaolong/xz2-rs",
|
||||
"https://github.com/chenxiaolong/zip",
|
||||
"https://github.com/jongiddy/bzip2-rs",
|
||||
]
|
||||
|
||||
@@ -20,6 +20,8 @@ serde = { version = "1.0.188", features = ["derive"] }
|
||||
tempfile = "3.8.0"
|
||||
toml_edit = { version = "0.21.0", features = ["serde"] }
|
||||
topological-sort = "0.2.2"
|
||||
tracing = "0.1.40"
|
||||
tracing-subscriber = "0.3.18"
|
||||
x509-cert = "0.2.5"
|
||||
|
||||
# https://github.com/zip-rs/zip/pull/383
|
||||
|
||||
+4
-4
@@ -47,7 +47,7 @@ data.ramdisks = ["otacerts"]
|
||||
|
||||
[profile.pixel_v4_gki.hashes]
|
||||
original = "f9477a35e3b60a495e49431c61e3897f11775f453a6a9897ead568357c963618"
|
||||
patched = "eb045799a514300727357a5ec471f9b04b276991daf4fd72f17f840b2a7dd1b8"
|
||||
patched = "d3436650b4e0c60688dafb1472fa5fe95e67d19a8fad2da4850ffaa739d44574"
|
||||
|
||||
# Google Pixel 6a
|
||||
# What's unique: boot (boot v4, no ramdisk) + vendor_boot (vendor v4, 2 ramdisks)
|
||||
@@ -81,7 +81,7 @@ data.ramdisks = ["init_and_otacerts", "dlkm"]
|
||||
|
||||
[profile.pixel_v4_non_gki.hashes]
|
||||
original = "021b4510bc244f5f686fbff89eb2058ec9c96a2949c2fe8caa7750a78d593225"
|
||||
patched = "5d1a36e2eb18d9d905ab397d4eaf2d7e7c94da6e7573afe3d4e4367841171fe3"
|
||||
patched = "0f90ae4c26a54a735d48e13e98bea73b6d1c026b0e95fa5b4b26179a1d6bdc86"
|
||||
|
||||
# Google Pixel 4a 5G
|
||||
# What's unique: boot (boot v3) + vendor_boot (vendor v3)
|
||||
@@ -116,7 +116,7 @@ data.ramdisks = ["otacerts"]
|
||||
|
||||
[profile.pixel_v3.hashes]
|
||||
original = "12221a69ff32e137d5f19b61f576fc6b33f0973c4a81da7722c640554ff4bc4e"
|
||||
patched = "e2049c6eba6990fc5ce30130af470c134a5ccc42947dcdd398f737fbca7ae44a"
|
||||
patched = "0a92969bbd7cb30071a0799eb20028546d1cec3e6ec1ca4d7e1fe7776f1399fc"
|
||||
|
||||
# Google Pixel 4a
|
||||
# What's unique: boot (boot v2)
|
||||
@@ -145,4 +145,4 @@ data.deps = ["system"]
|
||||
|
||||
[profile.pixel_v2.hashes]
|
||||
original = "8b38d2d999b5b6e240e894f669e9e2643b3764c108d53bb7b02447da725e7c18"
|
||||
patched = "16f56e3d02c08bb646d8d0694ce77a6edb02a613bbcff14b10ead4a448c3dc00"
|
||||
patched = "85b411947145e89cdc7f71b7109a12586f4dbddce3021f0f96172fc465c189d6"
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use avbroot::cli::args::{LogFormat, LogLevel};
|
||||
use clap::{Args, Parser, Subcommand};
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
@@ -66,4 +67,12 @@ pub enum Command {
|
||||
pub struct Cli {
|
||||
#[command(subcommand)]
|
||||
pub command: Command,
|
||||
|
||||
/// Lowest log message severity to output.
|
||||
#[arg(long, global = true, value_name = "LEVEL", default_value_t)]
|
||||
pub log_level: LogLevel,
|
||||
|
||||
/// Output format for log messages.
|
||||
#[arg(long, global = true, value_name = "FORMAT", default_value_t = LogFormat::Medium)]
|
||||
pub log_format: LogFormat,
|
||||
}
|
||||
|
||||
+12
-6
@@ -51,6 +51,7 @@ use clap::Parser;
|
||||
use rsa::RsaPrivateKey;
|
||||
use tempfile::{NamedTempFile, TempDir};
|
||||
use topological_sort::TopologicalSort;
|
||||
use tracing::{info, info_span};
|
||||
use x509_cert::Certificate;
|
||||
use zip::{write::FileOptions, CompressionMethod, ZipWriter};
|
||||
|
||||
@@ -63,7 +64,7 @@ use crate::{
|
||||
};
|
||||
|
||||
fn hash_file(path: &Path, cancel_signal: &AtomicBool) -> Result<[u8; 32]> {
|
||||
println!("Calculating hash of {path:?}");
|
||||
info!("Calculating hash: {path:?}");
|
||||
|
||||
let raw_reader =
|
||||
File::open(path).with_context(|| format!("Failed to open for reading: {path:?}"))?;
|
||||
@@ -817,11 +818,12 @@ fn create_fake_magisk(output: &Path) -> Result<()> {
|
||||
"lib/x86_64/libmagiskinit.so",
|
||||
] {
|
||||
zip_writer.start_file(path, FileOptions::default())?;
|
||||
write!(zip_writer, "dummy contents for {path}")?;
|
||||
}
|
||||
|
||||
// avbroot looks for the version number in this file.
|
||||
zip_writer.start_file("assets/util_functions.sh", FileOptions::default())?;
|
||||
zip_writer.write_all(b"MAGISK_VER_CODE=26400\n")?;
|
||||
zip_writer.write_all(b"MAGISK_VER_CODE=27000\n")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -947,7 +949,7 @@ fn patch_image(
|
||||
keys: &KeySet,
|
||||
cancel_signal: &AtomicBool,
|
||||
) -> Result<()> {
|
||||
println!("Patching {input_file:?}");
|
||||
info!("Patching OTA: {input_file:?} -> {output_file:?}");
|
||||
|
||||
// We're intentionally using the CLI interface.
|
||||
let mut args: Vec<&OsStr> = vec![
|
||||
@@ -976,7 +978,7 @@ fn patch_image(
|
||||
}
|
||||
|
||||
fn extract_image(input_file: &Path, output_dir: &Path, cancel_signal: &AtomicBool) -> Result<()> {
|
||||
println!("Extracting AVB partitions from {input_file:?}");
|
||||
info!("Extracting AVB partitions: {input_file:?} -> {output_dir:?}");
|
||||
|
||||
let cli = ExtractCli::try_parse_from([
|
||||
OsStr::new("extract"),
|
||||
@@ -991,7 +993,7 @@ fn extract_image(input_file: &Path, output_dir: &Path, cancel_signal: &AtomicBoo
|
||||
}
|
||||
|
||||
fn verify_image(input_file: &Path, keys: &KeySet, cancel_signal: &AtomicBool) -> Result<()> {
|
||||
println!("Verifying signatures in {input_file:?}");
|
||||
info!("Verifying signatures: {input_file:?}");
|
||||
|
||||
let cli = VerifyCli::try_parse_from([
|
||||
OsStr::new("verify"),
|
||||
@@ -1064,11 +1066,13 @@ fn test_subcommand(cli: &TestCli, cancel_signal: &AtomicBool) -> Result<()> {
|
||||
];
|
||||
|
||||
for name in profiles {
|
||||
let _span = info_span!("profile", name).entered();
|
||||
|
||||
if Path::new(name).file_name() != Some(OsStr::new(name)) {
|
||||
bail!("Unsafe profile name: {name}");
|
||||
}
|
||||
|
||||
println!("Generating OTA from profile: {name}");
|
||||
info!("Generating OTA");
|
||||
|
||||
let profile = &config.profile[name];
|
||||
|
||||
@@ -1169,6 +1173,8 @@ fn main() -> Result<()> {
|
||||
|
||||
let cli = Cli::parse();
|
||||
|
||||
avbroot::cli::args::init_logging(cli.log_level, cli.log_format);
|
||||
|
||||
match cli.command {
|
||||
Command::Test(c) => test_subcommand(&c, &cancel_signal),
|
||||
Command::List(c) => list_subcommand(&c),
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
#!/sbin/sh
|
||||
|
||||
#################
|
||||
# Initialization
|
||||
#################
|
||||
|
||||
umask 022
|
||||
|
||||
# echo before loading util_functions
|
||||
ui_print() { echo "$1"; }
|
||||
|
||||
require_new_magisk() {
|
||||
ui_print "*******************************"
|
||||
ui_print " Please install Magisk v20.4+! "
|
||||
ui_print "*******************************"
|
||||
exit 1
|
||||
}
|
||||
|
||||
#########################
|
||||
# Load util_functions.sh
|
||||
#########################
|
||||
|
||||
OUTFD=$2
|
||||
ZIPFILE=$3
|
||||
|
||||
mount /data 2>/dev/null
|
||||
|
||||
[ -f /data/adb/magisk/util_functions.sh ] || require_new_magisk
|
||||
. /data/adb/magisk/util_functions.sh
|
||||
[ $MAGISK_VER_CODE -lt 20400 ] && require_new_magisk
|
||||
|
||||
install_module
|
||||
exit 0
|
||||
@@ -1 +0,0 @@
|
||||
#MAGISK
|
||||
@@ -1,83 +0,0 @@
|
||||
import android.annotation.SuppressLint;
|
||||
import android.os.IBinder;
|
||||
import android.os.IInterface;
|
||||
import android.os.Process;
|
||||
import android.system.ErrnoException;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
@SuppressLint({"DiscouragedPrivateApi", "PrivateApi", "SoonBlockedPrivateApi"})
|
||||
public class Main {
|
||||
private static final int GET_SERVICE_ATTEMPTS = 30;
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
private static IInterface getService(Class<?> interfaceClass, String serviceName) throws Exception {
|
||||
Class<?> serviceManager = Class.forName("android.os.ServiceManager");
|
||||
Method getService = serviceManager.getDeclaredMethod("getService", String.class);
|
||||
|
||||
Class<?> stub = Class.forName(interfaceClass.getCanonicalName() + "$Stub");
|
||||
Method asInterface = stub.getDeclaredMethod("asInterface", IBinder.class);
|
||||
|
||||
// ServiceManager.waitForService() tries to start the service, which we want to avoid to be
|
||||
// 100% sure we're not disrupting the boot flow.
|
||||
for (int attempt = 1; attempt <= GET_SERVICE_ATTEMPTS; ++attempt) {
|
||||
IBinder iBinder = (IBinder) getService.invoke(null, serviceName);
|
||||
if (iBinder != null) {
|
||||
return (IInterface) asInterface.invoke(null, iBinder);
|
||||
}
|
||||
|
||||
if (attempt < GET_SERVICE_ATTEMPTS) {
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalStateException(
|
||||
"Service " + serviceName + " not found after " + GET_SERVICE_ATTEMPTS + " attempts");
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
private static void unlock() throws Exception {
|
||||
Class<?> iOemLockService = Class.forName("android.service.oemlock.IOemLockService");
|
||||
IInterface iFace = getService(iOemLockService, "oem_lock");
|
||||
|
||||
Method setOemUnlockAllowedByUser = iOemLockService.getDeclaredMethod("setOemUnlockAllowedByUser", boolean.class);
|
||||
Method isOemUnlockAllowedByUser = iOemLockService.getDeclaredMethod("isOemUnlockAllowedByUser");
|
||||
|
||||
Boolean unlockAllowed = (Boolean) isOemUnlockAllowedByUser.invoke(iFace);
|
||||
if (unlockAllowed) {
|
||||
System.out.println("OEM unlocking already enabled");
|
||||
return;
|
||||
}
|
||||
|
||||
System.out.println("Enabling OEM unlocking");
|
||||
setOemUnlockAllowedByUser.invoke(iFace, true);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"ConstantConditions", "JavaReflectionMemberAccess"})
|
||||
private static void switchToSystemUid() throws Exception {
|
||||
if (Process.myUid() != Process.SYSTEM_UID) {
|
||||
Method setUid = Process.class.getDeclaredMethod("setUid", int.class);
|
||||
int errno = (int) setUid.invoke(null, Process.SYSTEM_UID);
|
||||
|
||||
if (errno != 0) {
|
||||
throw new Exception("Failed to switch to SYSTEM (" + Process.SYSTEM_UID + ") user",
|
||||
new ErrnoException("setuid", errno));
|
||||
}
|
||||
if (Process.myUid() != Process.SYSTEM_UID) {
|
||||
throw new IllegalStateException("UID didn't actually change: " +
|
||||
Process.myUid() + " != " + Process.SYSTEM_UID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
switchToSystemUid();
|
||||
unlock();
|
||||
} catch (Exception e) {
|
||||
System.err.println("Failed to enable OEM unlocking");
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
id=com.chiller3.avbroot.oemunlockonboot
|
||||
name=oemunlockonboot
|
||||
version=v3.0.0
|
||||
versionCode=196608
|
||||
author=chenxiaolong
|
||||
description=Enable OEM unlocking on every boot
|
||||
@@ -1,20 +0,0 @@
|
||||
exec >/data/local/tmp/avbroot_oem_unlock.log 2>&1
|
||||
|
||||
mod_dir=${0%/*}
|
||||
|
||||
header() {
|
||||
echo "----- ${*} -----"
|
||||
}
|
||||
|
||||
header Environment
|
||||
echo "Timestamp: $(date)"
|
||||
echo "Script: ${0}"
|
||||
echo "UID/GID/Context: $(id)"
|
||||
|
||||
header Enable OEM unlocking
|
||||
CLASSPATH="${mod_dir}/classes.dex" app_process / Main &
|
||||
pid=${!}
|
||||
wait "${pid}"
|
||||
echo "Exit status: ${?}"
|
||||
echo "Logcat:"
|
||||
logcat -d --pid "${pid}"
|
||||
@@ -11,14 +11,5 @@ publish = false
|
||||
[dependencies]
|
||||
anyhow = "1.0.75"
|
||||
clap = { version = "4.4.1", features = ["derive"] }
|
||||
human-sort = "0.2.2"
|
||||
regex = { version = "1.9.4", default-features = false, features = ["perf", "std"] }
|
||||
tempfile = "3.8.0"
|
||||
toml_edit = "0.21.0"
|
||||
walkdir = "2.3.3"
|
||||
|
||||
# https://github.com/zip-rs/zip/pull/383
|
||||
[dependencies.zip]
|
||||
git = "https://github.com/chenxiaolong/zip"
|
||||
rev = "989101f9384b9e94e36e6e9e0f51908fdf98bde6"
|
||||
default-features = false
|
||||
|
||||
+1
-12
@@ -8,7 +8,7 @@ To update the project version, run:
|
||||
cargo xtask set-version -V <version>
|
||||
```
|
||||
|
||||
This will update the main [`Cargo.toml`](../Cargo.toml) and each module's `module.prop` file.
|
||||
This will update the main [`Cargo.toml`](../Cargo.toml) file.
|
||||
|
||||
## Updating changelog links
|
||||
|
||||
@@ -19,14 +19,3 @@ Then, run the following command to generate the appropriate link references:
|
||||
```bash
|
||||
cargo xtask update-changelog
|
||||
```
|
||||
|
||||
## Build modules
|
||||
|
||||
To build avbroot's companion modules, run:
|
||||
|
||||
```bash
|
||||
cargo xtask modules -a
|
||||
# or -m <module1> -m <module2> ...
|
||||
```
|
||||
|
||||
See the main [`README.md`](../README.md#avbroot-magisk-modules) for more details.
|
||||
|
||||
+1
-4
@@ -4,13 +4,12 @@
|
||||
*/
|
||||
|
||||
mod changelog;
|
||||
mod module;
|
||||
mod version;
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::{Parser, Subcommand};
|
||||
|
||||
use crate::{module::ModulesCli, version::SetVersionCli};
|
||||
use crate::version::SetVersionCli;
|
||||
|
||||
const WORKSPACE_DIR: &str = env!("CARGO_WORKSPACE_DIR");
|
||||
|
||||
@@ -19,7 +18,6 @@ fn main() -> Result<()> {
|
||||
|
||||
match cli.command {
|
||||
Command::SetVersion(c) => version::set_version_subcommand(&c),
|
||||
Command::Modules(c) => module::modules_subcommand(&c),
|
||||
Command::UpdateChangelog => changelog::update_changelog_subcommand(),
|
||||
}
|
||||
}
|
||||
@@ -27,7 +25,6 @@ fn main() -> Result<()> {
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum Command {
|
||||
SetVersion(SetVersionCli),
|
||||
Modules(ModulesCli),
|
||||
/// Update links in CHANGELOG.md.
|
||||
UpdateChangelog,
|
||||
}
|
||||
|
||||
@@ -1,231 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Andrew Gunnerson
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*/
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
env,
|
||||
ffi::OsStr,
|
||||
fmt,
|
||||
fs::{self, File},
|
||||
io::{self, BufWriter, Write},
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use clap::{Args, Parser, ValueEnum};
|
||||
use tempfile::TempDir;
|
||||
use walkdir::WalkDir;
|
||||
use zip::{write::FileOptions, ZipWriter};
|
||||
|
||||
use crate::WORKSPACE_DIR;
|
||||
|
||||
#[cfg(unix)]
|
||||
const D8: &str = "d8";
|
||||
#[cfg(windows)]
|
||||
const D8: &str = "d8.bat";
|
||||
|
||||
fn newest_child_by_name(directory: &Path) -> Result<PathBuf> {
|
||||
let mut children = vec![];
|
||||
|
||||
for entry in fs::read_dir(directory)? {
|
||||
let entry = entry?;
|
||||
let path = entry
|
||||
.path()
|
||||
.into_os_string()
|
||||
.into_string()
|
||||
.map_err(|e| anyhow!("Non-UTF-8 path: {e:?}"))?;
|
||||
|
||||
children.push(path);
|
||||
}
|
||||
|
||||
children.sort_by(|a, b| human_sort::compare(a, b));
|
||||
|
||||
children
|
||||
.pop()
|
||||
.map(PathBuf::from)
|
||||
.ok_or_else(|| anyhow!("{directory:?} has no children"))
|
||||
}
|
||||
|
||||
fn build_dex(writer: &mut dyn Write, sources: &[&Path]) -> Result<()> {
|
||||
let sdk = env::var_os("ANDROID_HOME")
|
||||
.map(PathBuf::from)
|
||||
.ok_or_else(|| anyhow!("ANDROID_HOME must be set to the Android SDK path"))?;
|
||||
let build_tools = newest_child_by_name(&sdk.join("build-tools"))?;
|
||||
let platform = newest_child_by_name(&sdk.join("platforms"))?;
|
||||
let d8 = build_tools.join(D8);
|
||||
let android_jar = platform.join("android.jar");
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
|
||||
let mut process = Command::new("javac")
|
||||
.args(["-source", "1.8"])
|
||||
.args(["-target", "1.8"])
|
||||
.arg("-cp")
|
||||
.arg(android_jar)
|
||||
.arg("-d")
|
||||
.arg(temp_dir.path())
|
||||
.args(sources)
|
||||
.spawn()?;
|
||||
let status = process.wait()?;
|
||||
if !status.success() {
|
||||
bail!("javac failed with {status}");
|
||||
}
|
||||
|
||||
let mut class_files = vec![];
|
||||
|
||||
for entry in WalkDir::new(temp_dir.path()) {
|
||||
let entry = entry?;
|
||||
|
||||
if entry.path().extension() == Some(OsStr::new("class")) {
|
||||
class_files.push(entry.into_path());
|
||||
}
|
||||
}
|
||||
|
||||
let mut process = Command::new(d8)
|
||||
.arg("--output")
|
||||
.arg(temp_dir.path())
|
||||
.args(class_files)
|
||||
.spawn()?;
|
||||
let status = process.wait()?;
|
||||
if !status.success() {
|
||||
bail!("d8 failed with {status}");
|
||||
}
|
||||
|
||||
let mut reader = File::open(temp_dir.path().join("classes.dex"))?;
|
||||
io::copy(&mut reader, writer)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_props(data: &str) -> Result<HashMap<String, String>> {
|
||||
let mut result = HashMap::new();
|
||||
|
||||
for line in data.split('\n') {
|
||||
if line.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some((k, v)) = line.split_once('=') else {
|
||||
bail!("Malformed line: {line:?}");
|
||||
};
|
||||
|
||||
result.insert(k.trim().to_owned(), v.trim().to_owned());
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn start_module(
|
||||
dist_dir: &Path,
|
||||
common_dir: &Path,
|
||||
module_dir: &Path,
|
||||
) -> Result<(PathBuf, ZipWriter<BufWriter<File>>)> {
|
||||
let module_prop_raw = fs::read_to_string(module_dir.join("module.prop"))?;
|
||||
let module_prop = parse_props(&module_prop_raw)?;
|
||||
|
||||
let name = module_prop["name"].as_str();
|
||||
let version = module_prop["version"].as_str();
|
||||
let version = version.strip_prefix('v').unwrap_or(version);
|
||||
let zip_path = dist_dir.join(format!("{name}-{version}.zip"));
|
||||
|
||||
let raw_writer = File::create(&zip_path)?;
|
||||
let mut zip_writer = ZipWriter::new(BufWriter::new(raw_writer));
|
||||
|
||||
zip_writer.start_file(
|
||||
"META-INF/com/google/android/update-binary",
|
||||
FileOptions::default(),
|
||||
)?;
|
||||
io::copy(
|
||||
&mut File::open(common_dir.join("update-binary"))?,
|
||||
&mut zip_writer,
|
||||
)?;
|
||||
|
||||
zip_writer.start_file(
|
||||
"META-INF/com/google/android/updater-script",
|
||||
FileOptions::default(),
|
||||
)?;
|
||||
io::copy(
|
||||
&mut File::open(common_dir.join("updater-script"))?,
|
||||
&mut zip_writer,
|
||||
)?;
|
||||
|
||||
zip_writer.start_file("module.prop", FileOptions::default())?;
|
||||
zip_writer.write_all(module_prop_raw.as_bytes())?;
|
||||
|
||||
Ok((zip_path, zip_writer))
|
||||
}
|
||||
|
||||
pub fn modules_subcommand(cli: &ModulesCli) -> Result<()> {
|
||||
let modules_dir = Path::new(WORKSPACE_DIR).join("modules");
|
||||
let common_dir = modules_dir.join("common");
|
||||
let dist_dir = modules_dir.join("dist");
|
||||
|
||||
fs::create_dir_all(&dist_dir)?;
|
||||
|
||||
let modules = if cli.module.all {
|
||||
Module::value_variants()
|
||||
} else {
|
||||
&cli.module.module
|
||||
};
|
||||
|
||||
for module in modules {
|
||||
let module_dir = modules_dir.join(module.to_string());
|
||||
|
||||
let (path, mut writer) = start_module(&dist_dir, &common_dir, &module_dir)?;
|
||||
|
||||
match module {
|
||||
Module::OemUnlockOnBoot => {
|
||||
writer.start_file("classes.dex", FileOptions::default())?;
|
||||
build_dex(&mut writer, &[&module_dir.join("Main.java")])?;
|
||||
|
||||
writer.start_file("service.sh", FileOptions::default())?;
|
||||
let mut reader = File::open(module_dir.join("service.sh"))?;
|
||||
io::copy(&mut reader, &mut writer)?;
|
||||
}
|
||||
}
|
||||
|
||||
writer.finish()?;
|
||||
|
||||
let path = path.canonicalize()?;
|
||||
println!("Built module: {path:?}");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, ValueEnum)]
|
||||
#[value(rename_all = "lower")]
|
||||
enum Module {
|
||||
OemUnlockOnBoot,
|
||||
}
|
||||
|
||||
impl fmt::Display for Module {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.to_possible_value()
|
||||
.expect("no values are skipped")
|
||||
.get_name()
|
||||
.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
#[group(required = true, multiple = false)]
|
||||
pub struct ModuleGroup {
|
||||
/// Name of module.
|
||||
#[arg(short, long)]
|
||||
module: Vec<Module>,
|
||||
|
||||
/// Build all modules.
|
||||
#[arg(short, long, conflicts_with = "module")]
|
||||
all: bool,
|
||||
}
|
||||
|
||||
/// Build companion modules.
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct ModulesCli {
|
||||
#[command(flatten)]
|
||||
module: ModuleGroup,
|
||||
}
|
||||
+1
-52
@@ -27,43 +27,6 @@ fn update_cargo_version(version: &str) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_module_version(path: &Path, version: &str) -> Result<()> {
|
||||
let mut version_code = 0;
|
||||
|
||||
// 8 bits per version component.
|
||||
for piece in version.split('.') {
|
||||
let piece: u32 = piece.parse()?;
|
||||
version_code <<= 8;
|
||||
version_code |= piece;
|
||||
}
|
||||
|
||||
let raw_reader = File::open(path)?;
|
||||
let mut reader = BufReader::new(raw_reader);
|
||||
let mut result = String::new();
|
||||
let mut line = String::new();
|
||||
|
||||
loop {
|
||||
line.clear();
|
||||
|
||||
let n = reader.read_line(&mut line)?;
|
||||
if n == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
if line.starts_with("version=") {
|
||||
result.push_str(&format!("version=v{version}\n"));
|
||||
} else if line.starts_with("versionCode=") {
|
||||
result.push_str(&format!("versionCode={version_code}\n"));
|
||||
} else {
|
||||
result.push_str(&line);
|
||||
}
|
||||
}
|
||||
|
||||
fs::write(path, &result)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_changelog_version(version: &str) -> Result<()> {
|
||||
let path = Path::new(WORKSPACE_DIR).join("CHANGELOG.md");
|
||||
let raw_reader = File::open(&path)?;
|
||||
@@ -109,26 +72,12 @@ fn update_changelog_version(version: &str) -> Result<()> {
|
||||
|
||||
pub fn set_version_subcommand(cli: &SetVersionCli) -> Result<()> {
|
||||
update_cargo_version(&cli.version)?;
|
||||
|
||||
let modules_dir = Path::new(WORKSPACE_DIR).join("modules");
|
||||
|
||||
for entry in fs::read_dir(modules_dir)? {
|
||||
let entry = entry?;
|
||||
|
||||
if entry.file_type()?.is_dir() {
|
||||
let module_prop = entry.path().join("module.prop");
|
||||
if module_prop.exists() {
|
||||
update_module_version(&module_prop, &cli.version)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update_changelog_version(&cli.version)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set the version number in all Cargo.toml files and in the module metadata.
|
||||
/// Set the version number in all Cargo.toml files.
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct SetVersionCli {
|
||||
/// Version number.
|
||||
|
||||
Reference in New Issue
Block a user