Compare commits

..

5 Commits

Author SHA1 Message Date
Andrew Gunnerson 83ab475c11 Version 3.5.0
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2024-08-15 20:45:04 -04:00
Andrew Gunnerson fe54640029 CHANGELOG.md: Add entry for PR #331
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2024-08-15 20:44:32 -04:00
Andrew Gunnerson fec1840a5f Add subcommands for packing and unpacking payload binaries
Some devices have "full" OTAs where the payload is missing the recovery
partition. These subcommands make it possible to manually add back the
missing image. Given the strict requirements for how the OTA zip is laid
out and signed, users can't just replace payload.bin in a zip and call
it a day, but it's sufficient for feeding a modified input to
`avbroot ota patch`.

Issue: #328

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2024-08-15 20:38:52 -04:00
Andrew Gunnerson 395f6934ff CHANGELOG.md: Add entry for PR #329
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2024-08-14 22:07:47 -04:00
Andrew Gunnerson a83b2fbfa9 Update dependencies
prost and protox have breaking changes, but none that affect avbroot.

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2024-08-14 21:06:21 -04:00
12 changed files with 717 additions and 149 deletions
+8
View File
@@ -7,6 +7,11 @@
to update the actual links at the bottom of the file.
-->
### Version 3.5.0
* Update all dependencies ([PR #329])
* Add new unpack and pack commands for `payload.bin` files ([Issue #328], [PR #331])
### Version 3.4.1
* Update all dependencies ([PR #321])
@@ -230,6 +235,7 @@ Behind-the-scenes changes:
[Issue #301]: https://github.com/chenxiaolong/avbroot/issues/301
[Issue #306]: https://github.com/chenxiaolong/avbroot/issues/306
[Issue #310]: https://github.com/chenxiaolong/avbroot/issues/310
[Issue #328]: https://github.com/chenxiaolong/avbroot/issues/328
[PR #130]: https://github.com/chenxiaolong/avbroot/pull/130
[PR #132]: https://github.com/chenxiaolong/avbroot/pull/132
[PR #133]: https://github.com/chenxiaolong/avbroot/pull/133
@@ -325,3 +331,5 @@ Behind-the-scenes changes:
[PR #312]: https://github.com/chenxiaolong/avbroot/pull/312
[PR #321]: https://github.com/chenxiaolong/avbroot/pull/321
[PR #323]: https://github.com/chenxiaolong/avbroot/pull/323
[PR #329]: https://github.com/chenxiaolong/avbroot/pull/329
[PR #331]: https://github.com/chenxiaolong/avbroot/pull/331
Generated
+164 -116
View File
@@ -36,9 +36,9 @@ checksum = "e9d4ee0d472d1cd2e28c97dfa124b3d8d992e10eb0a035f33f5d12e3a177ba3b"
[[package]]
name = "anstream"
version = "0.6.14"
version = "0.6.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b"
checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
dependencies = [
"anstyle",
"anstyle-parse",
@@ -51,33 +51,33 @@ dependencies = [
[[package]]
name = "anstyle"
version = "1.0.7"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
[[package]]
name = "anstyle-parse"
version = "0.2.4"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.0"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391"
checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.3"
version = "3.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
dependencies = [
"anstyle",
"windows-sys 0.52.0",
@@ -109,7 +109,7 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]]
name = "avbroot"
version = "3.4.1"
version = "3.5.0"
dependencies = [
"anyhow",
"assert_matches",
@@ -124,6 +124,7 @@ dependencies = [
"clap_complete",
"cms",
"const-oid",
"constcat",
"ctrlc",
"flate2",
"gf256",
@@ -202,9 +203,9 @@ dependencies = [
[[package]]
name = "bstr"
version = "1.9.1"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706"
checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c"
dependencies = [
"memchr",
"regex-automata",
@@ -219,9 +220,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "bytes"
version = "1.6.1"
version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952"
checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
[[package]]
name = "bzip2"
@@ -294,12 +295,13 @@ dependencies = [
[[package]]
name = "cc"
version = "1.1.5"
version = "1.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "324c74f2155653c90b04f25b2a47a8a631360cb908f92a772695f430c7e31052"
checksum = "5fb8dd288a69fc53a1996d7ecfbf4a20d59065bff137ce7e56bbd620de191189"
dependencies = [
"jobserver",
"libc",
"shlex",
]
[[package]]
@@ -310,9 +312,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cfg_aliases"
version = "0.1.1"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "cipher"
@@ -326,9 +328,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.9"
version = "4.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462"
checksum = "11d8838454fda655dafd3accb2b6e2bea645b9e4078abe84a22ceb947235c5cc"
dependencies = [
"clap_builder",
"clap_derive",
@@ -336,9 +338,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.5.9"
version = "4.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942"
checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6"
dependencies = [
"anstream",
"anstyle",
@@ -348,30 +350,30 @@ dependencies = [
[[package]]
name = "clap_complete"
version = "4.5.8"
version = "4.5.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b4be9c4c4b1f30b78d8a750e0822b6a6102d97e62061c583a6c1dea2dfb33ae"
checksum = "9c677cd0126f3026d8b093fa29eae5d812fde5c05bc66dbb29d0374eea95113a"
dependencies = [
"clap",
]
[[package]]
name = "clap_derive"
version = "4.5.8"
version = "4.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085"
checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.71",
"syn 2.0.74",
]
[[package]]
name = "clap_lex"
version = "0.7.1"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70"
checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
[[package]]
name = "cms"
@@ -387,9 +389,9 @@ dependencies = [
[[package]]
name = "colorchoice"
version = "1.0.1"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
[[package]]
name = "const-oid"
@@ -398,10 +400,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
[[package]]
name = "cpufeatures"
version = "0.2.12"
name = "constcat"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
checksum = "1f2e5af989b1955b092db01462980c0a286217f86817e12b2c09aea46bd03651"
[[package]]
name = "cpufeatures"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad"
dependencies = [
"libc",
]
@@ -452,12 +460,12 @@ dependencies = [
[[package]]
name = "ctrlc"
version = "3.4.4"
version = "3.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "672465ae37dc1bc6380a6547a8883d5dd397b0f1faaad4f265726cc7042a5345"
checksum = "90eeab0aa92f3f9b4e87f258c72b139c207d251f9cbc1080a0086b86a8870dd3"
dependencies = [
"nix",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -516,7 +524,7 @@ checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.71",
"syn 2.0.74",
]
[[package]]
@@ -533,7 +541,7 @@ dependencies = [
[[package]]
name = "e2e"
version = "3.4.1"
version = "3.5.0"
dependencies = [
"anyhow",
"avbroot",
@@ -600,9 +608,9 @@ checksum = "b3ea1ec5f8307826a5b71094dd91fc04d4ae75d5709b20ad351c7fb4815c86ec"
[[package]]
name = "flate2"
version = "1.0.30"
version = "1.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae"
checksum = "7f211bbe8e69bbd0cfdea405084f128ae8b4aaa6b0b522fc8f2b009084797920"
dependencies = [
"crc32fast",
"miniz_oxide",
@@ -627,7 +635,7 @@ dependencies = [
[[package]]
name = "fuzz"
version = "3.4.1"
version = "3.5.0"
dependencies = [
"avbroot",
"honggfuzz",
@@ -727,9 +735,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "indexmap"
version = "2.2.6"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c"
dependencies = [
"equivalent",
"hashbrown",
@@ -769,15 +777,15 @@ checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
[[package]]
name = "is_terminal_polyfill"
version = "1.70.0"
version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "itertools"
version = "0.12.1"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
dependencies = [
"either",
]
@@ -790,9 +798,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "jobserver"
version = "0.1.31"
version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e"
checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
dependencies = [
"libc",
]
@@ -814,18 +822,18 @@ checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]]
name = "liblzma"
version = "0.3.2"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "243510a5543c358949902b9e76daec3a32d7b03a43abce823e7c62a1a8360172"
checksum = "a7c45fc6fcf5b527d3cf89c1dee8c327943984b0dc8bfcf6e100473b00969e63"
dependencies = [
"liblzma-sys",
]
[[package]]
name = "liblzma-sys"
version = "0.3.4"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83cf78d20a45b5c0f3c7da2dcac255b230efe7d8684282bd35873164c1491187"
checksum = "885d379719f1be90c51852451dc658009c1aab2eb867cae8d5eb7f83bfaaca96"
dependencies = [
"cc",
"libc",
@@ -852,18 +860,18 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "logos"
version = "0.14.0"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "161971eb88a0da7ae0c333e1063467c5b5727e7fb6b710b8db4814eade3a42e8"
checksum = "ff1ceb190eb9bdeecdd8f1ad6a71d6d632a50905948771718741b5461fb01e13"
dependencies = [
"logos-derive",
]
[[package]]
name = "logos-codegen"
version = "0.14.0"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e31badd9de5131fdf4921f6473d457e3dd85b11b7f091ceb50e4df7c3eeb12a"
checksum = "90be66cb7bd40cb5cc2e9cfaf2d1133b04a3d93b72344267715010a466e0915a"
dependencies = [
"beef",
"fnv",
@@ -871,14 +879,14 @@ dependencies = [
"proc-macro2",
"quote",
"regex-syntax",
"syn 2.0.71",
"syn 2.0.74",
]
[[package]]
name = "logos-derive"
version = "0.14.0"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c2a69b3eb68d5bd595107c9ee58d7e07fe2bb5e360cc85b0f084dedac80de0a"
checksum = "45154231e8e96586b39494029e58f12f8ffcb5ecf80333a603a13aa205ea8cbd"
dependencies = [
"logos-codegen",
]
@@ -933,7 +941,7 @@ checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.71",
"syn 2.0.74",
]
[[package]]
@@ -953,9 +961,9 @@ checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03"
[[package]]
name = "nix"
version = "0.28.0"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
dependencies = [
"bitflags",
"cfg-if",
@@ -1092,7 +1100,7 @@ dependencies = [
"phf_shared",
"proc-macro2",
"quote",
"syn 2.0.71",
"syn 2.0.74",
]
[[package]]
@@ -1156,9 +1164,12 @@ checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
[[package]]
name = "ppv-lite86"
version = "0.2.17"
version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
dependencies = [
"zerocopy",
]
[[package]]
name = "prettyplease"
@@ -1167,7 +1178,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e"
dependencies = [
"proc-macro2",
"syn 2.0.71",
"syn 2.0.74",
]
[[package]]
@@ -1181,9 +1192,9 @@ dependencies = [
[[package]]
name = "prost"
version = "0.12.6"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29"
checksum = "e13db3d3fde688c61e2446b4d843bc27a7e8af269a69440c0308021dc92333cc"
dependencies = [
"bytes",
"prost-derive",
@@ -1191,9 +1202,9 @@ dependencies = [
[[package]]
name = "prost-build"
version = "0.12.6"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4"
checksum = "5bb182580f71dd070f88d01ce3de9f4da5021db7115d2e1c3605a754153b77c1"
dependencies = [
"bytes",
"heck",
@@ -1206,28 +1217,28 @@ dependencies = [
"prost",
"prost-types",
"regex",
"syn 2.0.71",
"syn 2.0.74",
"tempfile",
]
[[package]]
name = "prost-derive"
version = "0.12.6"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1"
checksum = "18bec9b0adc4eba778b33684b7ba3e7137789434769ee3ce3930463ef904cfca"
dependencies = [
"anyhow",
"itertools",
"proc-macro2",
"quote",
"syn 2.0.71",
"syn 2.0.74",
]
[[package]]
name = "prost-reflect"
version = "0.13.1"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f5eec97d5d34bdd17ad2db2219aabf46b054c6c41bd5529767c9ce55be5898f"
checksum = "55a6a9143ae25c25fa7b6a48d6cc08b10785372060009c25140a4e7c340e95af"
dependencies = [
"logos",
"miette",
@@ -1238,18 +1249,18 @@ dependencies = [
[[package]]
name = "prost-types"
version = "0.12.6"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0"
checksum = "cee5168b05f49d4b0ca581206eb14a7b22fafd963efe729ac48eb03266e25cc2"
dependencies = [
"prost",
]
[[package]]
name = "protox"
version = "0.6.1"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac532509cee918d40f38c3e12f8ef9230f215f017d54de7dd975015538a42ce7"
checksum = "70cdc4ca5487c1fddc30fa2bebba28c0f250f80c944190652ccaaeb157349175"
dependencies = [
"bytes",
"miette",
@@ -1262,9 +1273,9 @@ dependencies = [
[[package]]
name = "protox-parse"
version = "0.6.1"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f6c33f43516fe397e2f930779d720ca12cd057f7da4cd6326a0ef78d69dee96"
checksum = "a3a462d115462c080ae000c29a47f0b3985737e5d3a995fcdbcaa5c782068dde"
dependencies = [
"logos",
"miette",
@@ -1333,9 +1344,9 @@ dependencies = [
[[package]]
name = "regex"
version = "1.10.5"
version = "1.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
dependencies = [
"aho-corasick",
"memchr",
@@ -1470,29 +1481,29 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
[[package]]
name = "serde"
version = "1.0.204"
version = "1.0.207"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
checksum = "5665e14a49a4ea1b91029ba7d3bca9f299e1f7cfa194388ccc20f14743e784f2"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.204"
version = "1.0.207"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
checksum = "6aea2634c86b0e8ef2cfdc0c340baede54ec27b1e46febd7f80dffb2aa44a00e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.71",
"syn 2.0.74",
]
[[package]]
name = "serde_spanned"
version = "0.6.6"
version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0"
checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d"
dependencies = [
"serde",
]
@@ -1528,6 +1539,12 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signature"
version = "2.2.0"
@@ -1603,9 +1620,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.71"
version = "2.0.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462"
checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7"
dependencies = [
"proc-macro2",
"quote",
@@ -1614,34 +1631,35 @@ dependencies = [
[[package]]
name = "tempfile"
version = "3.10.1"
version = "3.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64"
dependencies = [
"cfg-if",
"fastrand",
"once_cell",
"rustix",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
name = "thiserror"
version = "1.0.62"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2675633b1499176c2dff06b0856a27976a8f9d436737b4cf4f312d4d91d8bbb"
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.62"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d20468752b09f49e909e55a5d338caa8bedf615594e9d80bc4c565d30faf798c"
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.71",
"syn 2.0.74",
]
[[package]]
@@ -1672,23 +1690,23 @@ checksum = "8d9ef545650e79f30233c0003bcc2504d7efac6dad25fca40744de773fe2049c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.71",
"syn 2.0.74",
]
[[package]]
name = "toml_datetime"
version = "0.6.6"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf"
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.22.15"
version = "0.22.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d59a3a72298453f564e2b111fa896f8d07fabb36f51f06d7e875fc5e0b5a3ef1"
checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d"
dependencies = [
"indexmap",
"serde",
@@ -1722,7 +1740,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.71",
"syn 2.0.74",
]
[[package]]
@@ -1817,9 +1835,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "version_check"
version = "0.9.4"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "wasi"
@@ -1867,6 +1885,15 @@ dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
@@ -1990,9 +2017,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winnow"
version = "0.6.13"
version = "0.6.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1"
checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f"
dependencies = [
"memchr",
]
@@ -2023,7 +2050,7 @@ dependencies = [
[[package]]
name = "xtask"
version = "3.4.1"
version = "3.5.0"
dependencies = [
"anyhow",
"clap",
@@ -2031,6 +2058,27 @@ dependencies = [
"toml_edit",
]
[[package]]
name = "zerocopy"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [
"byteorder",
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.74",
]
[[package]]
name = "zeroize"
version = "1.8.1"
@@ -2048,7 +2096,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.71",
"syn 2.0.74",
]
[[package]]
+1 -1
View File
@@ -4,7 +4,7 @@ members = ["avbroot", "e2e", "fuzz", "xtask"]
resolver = "2"
[workspace.package]
version = "3.4.1"
version = "3.5.0"
license = "GPL-3.0-only"
edition = "2021"
repository = "https://github.com/chenxiaolong/avbroot"
+30
View File
@@ -255,6 +255,36 @@ This will check if the input file has any corrupted blocks. Currently, the comma
## `avbroot payload`
### Unpacking a payload binary
```bash
avbroot payload unpack -i <input payload>
```
This subcommand unpacks the payload header information to `payload.toml` and the partition images to the `payload_images` directory.
Only full payload binaries can be unpacked. Delta payload binaries from incremental OTAs are not supported.
### Packing a payload binary
```bash
avbroot payload pack -o <output payload> --key <OTA private key>
```
This subcommand packs a new payload binary from the `payload.toml` file and `payload_images` directory. Any `.img` files in the `payload_images` directory that don't have a corresponding entry in `payload.toml` are silently ignored.
Packing a payload binary requires compressing all of the partition images, which is very CPU intensive. If re-signing an existing payload binary without making any other modifications is all that's needed, use the `repack` subcommand instead.
### Repacking a payload binary
```bash
avbroot payload repack -i <input payload> -o <output payload> --key <OTA private key>
```
This subcommand is logically equivalent to `avbroot payload unpack` followed by `avbroot payload pack`, except significantly more efficient. Instead of decompressing and recompressing all partition images, the raw data is directly copied from the input payload binary.
This is useful for re-signing a payload binary without making any other changes.
### Showing payload header information
```bash
+4 -3
View File
@@ -31,7 +31,7 @@ num-bigint-dig = "0.8.4"
num-traits = "0.2.16"
phf = { version = "0.11.2", features = ["macros"] }
pkcs8 = { version = "0.10.2", features = ["encryption", "pem"] }
prost = "0.12.1"
prost = "0.13.1"
rand = "0.8.5"
rayon = "1.7.0"
regex = { version = "1.9.4", default-features = false, features = ["perf", "std"] }
@@ -71,8 +71,9 @@ features = ["deflate"]
rustix = { version = "0.38.9", default-features = false, features = ["process"] }
[build-dependencies]
prost-build = "0.12.1"
protox = "0.6.0"
constcat = "0.5.0"
prost-build = "0.13.1"
protox = "0.7.0"
[dev-dependencies]
assert_matches = "1.5.0"
+55 -1
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023 Andrew Gunnerson
* SPDX-FileCopyrightText: 2023-2024 Andrew Gunnerson
* SPDX-License-Identifier: GPL-3.0-only
*/
@@ -30,8 +30,62 @@ fn main() {
let file_descriptors = protox::compile(&protos, [&in_dir]).unwrap();
const CUE_AI: &str = ".chromeos_update_engine.ApexInfo";
const CUE_DAM: &str = ".chromeos_update_engine.DeltaArchiveManifest";
const CUE_DPG: &str = ".chromeos_update_engine.DynamicPartitionGroup";
const CUE_DPM: &str = ".chromeos_update_engine.DynamicPartitionMetadata";
const CUE_PU: &str = ".chromeos_update_engine.PartitionUpdate";
const CUE_VABCFS: &str = ".chromeos_update_engine.VABCFeatureSet";
const DERIVE_SERDE: &str = "#[derive(serde::Deserialize, serde::Serialize)]";
const SERDE_DEFAULT: &str = "#[serde(default)]";
const SERDE_SKIP: &str = "#[serde(skip)]";
const SERDE_SKIP_IF_VEC_EMPTY: &str = "#[serde(skip_serializing_if = \"Vec::is_empty\")]";
use constcat::concat as c;
prost_build::Config::new()
.btree_map(["."])
// Allow deserializing and serializing the types we care about.
.type_attribute(CUE_AI, DERIVE_SERDE)
.type_attribute(CUE_DAM, DERIVE_SERDE)
.type_attribute(CUE_DPG, DERIVE_SERDE)
.type_attribute(CUE_DPM, DERIVE_SERDE)
.type_attribute(CUE_PU, DERIVE_SERDE)
.type_attribute(CUE_VABCFS, DERIVE_SERDE)
// Allow default-initializing all fields.
.type_attribute(CUE_AI, SERDE_DEFAULT)
.type_attribute(CUE_DAM, SERDE_DEFAULT)
.type_attribute(CUE_DPG, SERDE_DEFAULT)
.type_attribute(CUE_DPM, SERDE_DEFAULT)
.type_attribute(CUE_PU, SERDE_DEFAULT)
.type_attribute(CUE_VABCFS, SERDE_DEFAULT)
// Don't serialize fields that define the structure of the payload
// binary and that we recompute during packing.
.field_attribute(c!(CUE_DAM, ".signatures_offset"), SERDE_SKIP)
.field_attribute(c!(CUE_DAM, ".signatures_size"), SERDE_SKIP)
.field_attribute(c!(CUE_PU, ".operations"), SERDE_SKIP)
.field_attribute(c!(CUE_PU, ".estimate_cow_size"), SERDE_SKIP)
.field_attribute(c!(CUE_PU, ".old_partition_info"), SERDE_SKIP)
.field_attribute(c!(CUE_PU, ".new_partition_info"), SERDE_SKIP)
// Don't serialize AVB 1.0 fields.
.field_attribute(c!(CUE_PU, ".hash_tree_data_extent"), SERDE_SKIP)
.field_attribute(c!(CUE_PU, ".hash_tree_extent"), SERDE_SKIP)
.field_attribute(c!(CUE_PU, ".hash_tree_algorithm"), SERDE_SKIP)
.field_attribute(c!(CUE_PU, ".hash_tree_salt"), SERDE_SKIP)
.field_attribute(c!(CUE_PU, ".fec_data_extent"), SERDE_SKIP)
.field_attribute(c!(CUE_PU, ".fec_extent"), SERDE_SKIP)
.field_attribute(c!(CUE_PU, ".fec_roots"), SERDE_SKIP)
// Don't serialize fields for incremental OTAs.
.field_attribute(c!(CUE_PU, ".merge_operations"), SERDE_SKIP)
// Don't serialize fields for vendor-signed images, which update_engine
// doesn't support anyway.
.field_attribute(c!(CUE_PU, ".new_partition_signature"), SERDE_SKIP)
// Don't serialize empty lists.
.field_attribute(c!(CUE_DAM, ".apex_info"), SERDE_SKIP_IF_VEC_EMPTY)
.field_attribute(c!(CUE_DAM, ".partitions"), SERDE_SKIP_IF_VEC_EMPTY)
.field_attribute(c!(CUE_DPG, ".partition_names"), SERDE_SKIP_IF_VEC_EMPTY)
.field_attribute(c!(CUE_DPM, ".groups"), SERDE_SKIP_IF_VEC_EMPTY)
.compile_fds(file_descriptors)
.unwrap();
}
+1 -1
View File
@@ -164,7 +164,7 @@ pub fn main(logging_initialized: &AtomicBool, cancel_signal: &AtomicBool) -> Res
Command::HashTree(c) => hashtree::hash_tree_main(&c, cancel_signal),
Command::Key(c) => key::key_main(&c),
Command::Ota(c) => ota::ota_main(&c, cancel_signal),
Command::Payload(c) => payload::payload_main(&c),
Command::Payload(c) => payload::payload_main(&c, cancel_signal),
// Deprecated aliases.
Command::Patch(c) => ota::patch_subcommand(&c, cancel_signal),
Command::Extract(c) => ota::extract_subcommand(&c, cancel_signal),
+1 -1
View File
@@ -303,7 +303,7 @@ struct UnpackCli {
#[arg(short, long, value_name = "FILE", value_parser)]
input: PathBuf,
/// Path to output cpio info TOML.
/// Path to output info TOML.
#[arg(long, value_name = "FILE", value_parser, default_value = "cpio.toml")]
output_info: PathBuf,
+6 -6
View File
@@ -623,7 +623,7 @@ fn update_vbmeta_headers(
/// If `ranges` is [`None`], then the entire file is compressed. Otherwise, only
/// the chunks containing the specified ranges are compressed. In the latter
/// scenario, unmodified chunks must be copied from the original payload.
fn compress_image(
pub fn compress_image(
name: &str,
file: &mut PSeekFile,
header: &mut PayloadHeader,
@@ -900,7 +900,7 @@ fn patch_ota_payload(
.with_context(|| format!("Failed to copy from original payload: {name}"))?;
}
let (_, properties, metadata_size) = payload_writer
let (_, _, properties, metadata_size) = payload_writer
.finish()
.context("Failed to finalize payload")?;
@@ -1091,7 +1091,7 @@ fn patch_ota_zip(
Ok((metadata, payload_metadata_size.unwrap()))
}
fn extract_ota_zip(
pub fn extract_payload(
raw_reader: &PSeekFile,
directory: &Dir,
payload_offset: u64,
@@ -1442,7 +1442,7 @@ pub fn extract_subcommand(cli: &ExtractCli, cancel_signal: &AtomicBool) -> Resul
let directory = Dir::open_ambient_dir(&cli.directory, authority)
.with_context(|| format!("Failed to open directory: {:?}", cli.directory))?;
extract_ota_zip(
extract_payload(
&raw_reader,
&directory,
payload_offset,
@@ -1654,7 +1654,7 @@ pub fn verify_subcommand(cli: &VerifyCli, cancel_signal: &AtomicBool) -> Result<
.cloned()
.collect::<BTreeSet<_>>();
extract_ota_zip(
extract_payload(
&raw_reader,
&temp_dir,
pf_payload.offset,
@@ -1864,7 +1864,7 @@ pub struct PatchCli {
value_names = ["PARTITION", "FILE"],
value_parser = value_parser!(OsString),
num_args = 2,
help_heading = HEADING_PATH,
help_heading = HEADING_PATH
)]
pub replace: Vec<OsString>,
+436 -12
View File
@@ -3,31 +3,448 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
use std::{fs::File, io::BufReader, path::PathBuf};
use std::{
collections::HashMap,
ffi::{OsStr, OsString},
fs::{self, File},
io::{BufReader, BufWriter, Seek, SeekFrom},
path::{Path, PathBuf},
sync::atomic::AtomicBool,
};
use anyhow::{Context, Result};
use clap::{Parser, Subcommand};
use anyhow::{anyhow, bail, Context, Result};
use cap_std::{ambient_authority, fs::Dir};
use clap::{Args, Parser, Subcommand};
use tracing::info;
use crate::{format::payload::PayloadHeader, stream::FromReader};
use crate::{
cli::ota,
crypto::{self, PassphraseSource, RsaSigningKey},
format::payload::{PayloadHeader, PayloadWriter},
stream::{self, FromReader, PSeekFile},
};
fn info_subcommand(cli: &InfoCli) -> Result<()> {
let mut reader = File::open(&cli.input)
fn open_reader(path: &Path) -> Result<(BufReader<File>, PayloadHeader)> {
let mut reader = File::open(path)
.map(BufReader::new)
.with_context(|| format!("Failed to open payload: {:?}", cli.input))?;
.with_context(|| format!("Failed to open payload for reading: {path:?}"))?;
let header = PayloadHeader::from_reader(&mut reader)
.with_context(|| format!("Failed to read payload: {:?}", cli.input))?;
.with_context(|| format!("Failed to read payload header: {path:?}"))?;
if !header.is_full_ota() {
bail!("Payload is a delta OTA, not a full OTA");
}
println!("{header:#?}");
Ok((reader, header))
}
fn open_writer(
path: &Path,
header: PayloadHeader,
key: RsaSigningKey,
) -> Result<PayloadWriter<BufWriter<File>>> {
let writer = File::create(path)
.map(BufWriter::new)
.with_context(|| format!("Failed to open payload for writing: {path:?}"))?;
let payload_writer = PayloadWriter::new(writer, header, key)
.with_context(|| format!("Failed to write payload header: {path:?}"))?;
Ok(payload_writer)
}
fn read_info(path: &Path) -> Result<PayloadHeader> {
let data = fs::read_to_string(path)
.with_context(|| format!("Failed to read payload info TOML: {path:?}"))?;
let info = toml_edit::de::from_str(&data)
.with_context(|| format!("Failed to parse payload info TOML: {path:?}"))?;
Ok(info)
}
fn write_info(path: &Path, manifest: &PayloadHeader) -> Result<()> {
let data = toml_edit::ser::to_string_pretty(manifest)
.with_context(|| format!("Failed to serialize payload info TOML: {path:?}"))?;
fs::write(path, data)
.with_context(|| format!("Failed to write payload info TOML: {path:?}"))?;
Ok(())
}
pub fn payload_main(cli: &PayloadCli) -> Result<()> {
match &cli.command {
PayloadCommand::Info(c) => info_subcommand(c),
fn display_header(cli: &PayloadCli, header: &PayloadHeader) {
if !cli.quiet {
println!("{header:#?}");
}
}
fn load_key(group: &KeyGroup) -> Result<RsaSigningKey> {
let source = PassphraseSource::new(
&group.key,
group.pass_file.as_deref(),
group.pass_env_var.as_deref(),
);
let signing_key = if let Some(helper) = &group.signing_helper {
let public_key = crypto::read_pem_public_key_file(&group.key)
.with_context(|| format!("Failed to load key: {:?}", group.key))?;
RsaSigningKey::External {
program: helper.clone(),
public_key_file: group.key.clone(),
public_key,
passphrase_source: source,
}
} else {
let private_key = crypto::read_pem_key_file(&group.key, &source)
.with_context(|| format!("Failed to load key: {:?}", group.key))?;
RsaSigningKey::Internal(private_key)
};
Ok(signing_key)
}
fn unpack_subcommand(
payload_cli: &PayloadCli,
cli: &UnpackCli,
cancel_signal: &AtomicBool,
) -> Result<()> {
let (mut reader, header) = open_reader(&cli.input)?;
let payload_size = reader
.seek(SeekFrom::End(0))
.with_context(|| format!("Failed to get file size: {:?}", cli.input))?;
display_header(payload_cli, &header);
write_info(&cli.output_info, &header)?;
let authority = ambient_authority();
Dir::create_ambient_dir_all(&cli.output_images, authority)
.with_context(|| format!("Failed to create directory: {:?}", cli.output_images))?;
let directory = Dir::open_ambient_dir(&cli.output_images, authority)
.with_context(|| format!("Failed to open directory: {:?}", cli.output_images))?;
ota::extract_payload(
&PSeekFile::new(reader.into_inner()),
&directory,
0,
payload_size,
&header,
&header
.manifest
.partitions
.iter()
.map(|p| &p.partition_name)
.cloned()
.collect(),
cancel_signal,
)?;
Ok(())
}
fn pack_subcommand(
payload_cli: &PayloadCli,
cli: &PackCli,
cancel_signal: &AtomicBool,
) -> Result<()> {
let signing_key = load_key(&cli.key)?;
let mut header = read_info(&cli.input_info)?;
let authority = ambient_authority();
let directory = Dir::open_ambient_dir(&cli.input_images, authority)
.with_context(|| format!("Failed to open directory: {:?}", cli.input_images))?;
for p in &header.manifest.partitions {
let name = &p.partition_name;
if Path::new(name).file_name() != Some(OsStr::new(name)) {
bail!("Unsafe partition name: {name}");
}
}
// Pre-open all of the image files.
let input_files = header
.manifest
.partitions
.iter()
.map(|p| {
let path = format!("{}.img", p.partition_name);
let file = directory
.open(&path)
.map(|f| PSeekFile::new(f.into_std()))
.with_context(|| format!("Failed to open file: {path:?}"))?;
Ok((p.partition_name.clone(), file))
})
.collect::<Result<HashMap<_, _>>>()?;
// Compress the images and compute the list of install operations for
// insertion into the payload header. The compressed data is stored in new
// temp files and the original input files are dropped.
let mut compressed_files = input_files
.into_iter()
.map(|(name, mut input_file)| {
ota::compress_image(&name, &mut input_file, &mut header, None, cancel_signal)
.with_context(|| format!("Failed to compress image: {name}"))?;
Ok((name, input_file))
})
.collect::<Result<HashMap<_, _>>>()?;
info!("Generating new OTA payload");
// Now we can write the actual payload. With everything precomputed, this is
// mostly just a simple copy.
let mut payload_writer = open_writer(&cli.output, header.clone(), signing_key)?;
while payload_writer
.begin_next_operation()
.context("Failed to begin next payload blob entry")?
{
let name = payload_writer.partition().unwrap().partition_name.clone();
let operation = payload_writer.operation().unwrap();
let Some(data_length) = operation.data_length else {
// Otherwise, this is a ZERO/DISCARD operation.
continue;
};
let pi = payload_writer.partition_index().unwrap();
let oi = payload_writer.operation_index().unwrap();
let orig_partition = &header.manifest.partitions[pi];
let orig_operation = &orig_partition.operations[oi];
let data_offset = orig_operation
.data_offset
.ok_or_else(|| anyhow!("Missing data_offset in partition #{pi} operation #{oi}"))?;
// The compressed chunks are laid out sequentially and data_offset is
// set to the offset within that file.
let Some(input_file) = compressed_files.get_mut(&name) else {
unreachable!("Compressed data not found for image: {name}");
};
input_file
.seek(SeekFrom::Start(data_offset))
.with_context(|| format!("Failed to seek image: {name}"))?;
stream::copy_n(input_file, &mut payload_writer, data_length, cancel_signal)
.with_context(|| format!("Failed to copy from replacement image: {name}"))?;
}
let (_, header, properties, _) = payload_writer
.finish()
.context("Failed to finalize payload")?;
// Display the header information now that it has been finalized.
display_header(payload_cli, &header);
// Optionally, write payload_properties.txt.
if let Some(path) = &cli.output_properties {
fs::write(path, properties)
.with_context(|| format!("Failed to write payload properties: {path:?}"))?;
}
Ok(())
}
fn repack_subcommand(
payload_cli: &PayloadCli,
cli: &RepackCli,
cancel_signal: &AtomicBool,
) -> Result<()> {
let signing_key = load_key(&cli.key)?;
let (mut reader, header) = open_reader(&cli.input)?;
info!("Generating new OTA payload");
let mut payload_writer = open_writer(&cli.output, header.clone(), signing_key)?;
while payload_writer
.begin_next_operation()
.context("Failed to begin next payload blob entry")?
{
let name = payload_writer.partition().unwrap().partition_name.clone();
let operation = payload_writer.operation().unwrap();
let Some(data_length) = operation.data_length else {
// Otherwise, this is a ZERO/DISCARD operation.
continue;
};
let pi = payload_writer.partition_index().unwrap();
let oi = payload_writer.operation_index().unwrap();
let orig_partition = &header.manifest.partitions[pi];
let orig_operation = &orig_partition.operations[oi];
let data_offset = orig_operation
.data_offset
.ok_or_else(|| anyhow!("Missing data_offset in partition #{pi} operation #{oi}"))?;
// Directly copy blobs from the original payload.
let data_offset = data_offset
.checked_add(header.blob_offset)
.ok_or_else(|| anyhow!("data_offset overflow in partition #{pi} operation #{oi}"))?;
reader
.seek(SeekFrom::Start(data_offset))
.with_context(|| format!("Failed to seek original payload to {data_offset}"))?;
stream::copy_n(&mut reader, &mut payload_writer, data_length, cancel_signal)
.with_context(|| format!("Failed to copy from original payload: {name}"))?;
}
let (_, header, properties, _) = payload_writer
.finish()
.context("Failed to finalize payload")?;
// Display the header information now that it has been finalized.
display_header(payload_cli, &header);
// Optionally, write payload_properties.txt.
if let Some(path) = &cli.output_properties {
fs::write(path, properties)
.with_context(|| format!("Failed to write payload properties: {path:?}"))?;
}
Ok(())
}
fn info_subcommand(payload_cli: &PayloadCli, cli: &InfoCli) -> Result<()> {
let (_, header) = open_reader(&cli.input)?;
display_header(payload_cli, &header);
Ok(())
}
pub fn payload_main(cli: &PayloadCli, cancel_signal: &AtomicBool) -> Result<()> {
match &cli.command {
PayloadCommand::Unpack(c) => unpack_subcommand(cli, c, cancel_signal),
PayloadCommand::Pack(c) => pack_subcommand(cli, c, cancel_signal),
PayloadCommand::Repack(c) => repack_subcommand(cli, c, cancel_signal),
PayloadCommand::Info(c) => info_subcommand(cli, c),
}
}
#[derive(Debug, Args)]
struct KeyGroup {
/// Path to signing key.
///
/// This should normally be a private key. However, if --signing-helper is
/// used, then it should be a public key instead.
#[arg(short, long, value_name = "FILE", value_parser)]
key: PathBuf,
/// Environment variable containing private key passphrase.
#[arg(long, value_name = "ENV_VAR", value_parser, group = "pass")]
pass_env_var: Option<OsString>,
/// File containing private key passphrase.
#[arg(long, value_name = "FILE", value_parser, group = "pass")]
pass_file: Option<PathBuf>,
/// External program for signing.
///
/// If this option is specified, then --key must refer to a public key. The
/// program will be invoked as:
///
/// <program> <algo> <public key> [file <pass file>|env <pass env>]
#[arg(long, value_name = "PROGRAM", value_parser)]
signing_helper: Option<PathBuf>,
}
/// Unpack a payload binary.
///
/// Each partition is extracted to `<partition name>.img` in the output images
/// directory. The payload header metadata is written to the info TOML file.
///
/// If any partition names are unsafe to use in a path, the extraction process
/// will fail and exit. Extracted files are never written outside of the tree
/// directory, even if an external process tries to interfere.
#[derive(Debug, Parser)]
struct UnpackCli {
/// Path to input payload binary.
#[arg(short, long, value_name = "FILE", value_parser)]
input: PathBuf,
/// Path to output info TOML.
#[arg(
long,
value_name = "FILE",
value_parser,
default_value = "payload.toml"
)]
output_info: PathBuf,
/// Path to output images directory.
#[arg(
long,
value_name = "DIR",
value_parser,
default_value = "payload_images"
)]
output_images: PathBuf,
}
/// Pack a payload binary.
///
/// The new payload binary will *only* contain images listed in the info TOML
/// file. Extra images in the input images directory that aren't listed will be
/// silently ignored. Images are added to the payload in the order that they are
/// listed in the info TOML file.
#[derive(Debug, Parser)]
struct PackCli {
/// Path to output payload binary.
#[arg(short, long, value_name = "FILE", value_parser)]
output: PathBuf,
/// Path to output payload properties file.
#[arg(short, long, value_name = "FILE", value_parser)]
output_properties: Option<PathBuf>,
/// Path to input info TOML.
#[arg(
long,
value_name = "FILE",
value_parser,
default_value = "payload.toml"
)]
input_info: PathBuf,
/// Path to input images directory.
#[arg(
long,
value_name = "DIR",
value_parser,
default_value = "payload_images"
)]
input_images: PathBuf,
#[command(flatten)]
key: KeyGroup,
}
/// Repack a payload binary.
///
/// This command is equivalent to running `unpack` and `pack`, except without
/// storing the unpacked data to disk nor recompressing the partition images.
#[derive(Debug, Parser)]
struct RepackCli {
/// Path to input payload binary.
#[arg(short, long, value_name = "FILE", value_parser)]
input: PathBuf,
/// Path to output payload binary.
#[arg(short, long, value_name = "FILE", value_parser)]
output: PathBuf,
/// Path to output payload properties file.
#[arg(short, long, value_name = "FILE", value_parser)]
output_properties: Option<PathBuf>,
#[command(flatten)]
key: KeyGroup,
}
/// Display payload information.
#[derive(Debug, Parser)]
struct InfoCli {
@@ -38,6 +455,9 @@ struct InfoCli {
#[derive(Debug, Subcommand)]
enum PayloadCommand {
Unpack(UnpackCli),
Pack(PackCli),
Repack(RepackCli),
Info(InfoCli),
}
@@ -46,4 +466,8 @@ enum PayloadCommand {
pub struct PayloadCli {
#[command(subcommand)]
command: PayloadCommand,
/// Don't print payload header information.
#[arg(short, long, global = true)]
quiet: bool,
}
+10 -7
View File
@@ -26,6 +26,7 @@ use rayon::{
prelude::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator},
};
use ring::digest::{Context, Digest};
use serde::{Deserialize, Serialize};
use thiserror::Error;
use x509_cert::Certificate;
@@ -104,11 +105,13 @@ pub enum Error {
type Result<T> = std::result::Result<T, Error>;
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct PayloadHeader {
pub version: u64,
pub manifest: DeltaArchiveManifest,
#[serde(skip)]
pub metadata_signature_size: u32,
#[serde(skip)]
pub blob_offset: u64,
}
@@ -384,12 +387,12 @@ impl<W: Write> PayloadWriter<W> {
}
/// Finalize the payload. If this function is not called, the payload will
/// be left in an incomplete state. Returns the original writer, the
/// contents that should be written for `payload_properties.txt` and the
/// length of the header + manifest + manifest signature sections (for
/// constructing the `payload_metadata.bin` OTA metadata property files
/// be left in an incomplete state. Returns the original writer, the final
/// header, the contents that should be written for `payload_properties.txt`
/// and the length of the header + manifest + manifest signature sections
/// (for constructing the `payload_metadata.bin` OTA metadata property files
/// entry).
pub fn finish(mut self) -> Result<(W, String, u64)> {
pub fn finish(mut self) -> Result<(W, PayloadHeader, String, u64)> {
// Append payload signature.
let payload_partial_hash = self.h_partial.clone().finish();
let payload_sig = sign_digest(payload_partial_hash.as_ref(), &self.key)?;
@@ -413,7 +416,7 @@ impl<W: Write> PayloadWriter<W> {
self.metadata_size as u64,
);
Ok((self.inner, properties, metadata_with_sig_size))
Ok((self.inner, self.header, properties, metadata_with_sig_size))
}
/// Prepare for writing the next source data blob corresponding to an
+1 -1
View File
@@ -688,7 +688,7 @@ fn create_payload(
.with_context(|| format!("Failed to copy from image: {name}"))?;
}
let (_, properties, metadata_size) = payload_writer
let (_, _, properties, metadata_size) = payload_writer
.finish()
.context("Failed to finalize payload")?;