brillo_update_payload 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872
  1. #!/bin/bash
  2. #
  3. # Copyright (C) 2015 The Android Open Source Project
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License");
  6. # you may not use this file except in compliance with the License.
  7. # You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. #
  17. # Script to generate a Brillo update for use by the update engine.
  18. #
  19. # usage: brillo_update_payload COMMAND [ARGS]
  20. # The following commands are supported:
  21. # generate generate an unsigned payload
  22. # hash generate a payload or metadata hash
  23. # sign generate a signed payload
  24. # properties generate a properties file from a payload
  25. # verify verify a payload by recreating a target image.
  26. # check verify a payload using paycheck (static testing)
  27. #
  28. # Generate command arguments:
  29. # --payload generated unsigned payload output file
  30. # --source_image if defined, generate a delta payload from the specified
  31. # image to the target_image
  32. # --target_image the target image that should be sent to clients
  33. # --metadata_size_file if defined, generate a file containing the size of the
  34. # payload metadata in bytes to the specified file
  35. #
  36. # Hash command arguments:
  37. # --unsigned_payload the input unsigned payload to generate the hash from
  38. # --signature_size signature sizes in bytes in the following format:
  39. # "size1:size2[:...]"
  40. # --payload_hash_file if defined, generate a payload hash and output to the
  41. # specified file
  42. # --metadata_hash_file if defined, generate a metadata hash and output to the
  43. # specified file
  44. #
  45. # Sign command arguments:
  46. # --unsigned_payload the input unsigned payload to insert the signatures
  47. # --payload the output signed payload
  48. # --signature_size signature sizes in bytes in the following format:
  49. # "size1:size2[:...]"
  50. # --payload_signature_file the payload signature files in the following
  51. # format:
  52. # "payload_signature1:payload_signature2[:...]"
  53. # --metadata_signature_file the metadata signature files in the following
  54. # format:
  55. # "metadata_signature1:metadata_signature2[:...]"
  56. # --metadata_size_file if defined, generate a file containing the size of
  57. # the signed payload metadata in bytes to the
  58. # specified file
  59. # Note that the number of signature sizes and payload signatures have to match.
  60. #
  61. # Properties command arguments:
  62. # --payload the input signed or unsigned payload
  63. # --properties_file the output path where to write the properties, or
  64. # '-' for stdout.
  65. # Verify command arguments:
  66. # --payload payload input file
  67. # --source_image verify payload to the specified source image.
  68. # --target_image the target image to verify upon.
  69. #
  70. # Check command arguments:
  71. # Symmetrical with the verify command.
  72. # Exit codes:
  73. EX_UNSUPPORTED_DELTA=100
  74. warn() {
  75. echo "brillo_update_payload: warning: $*" >&2
  76. }
  77. die() {
  78. echo "brillo_update_payload: error: $*" >&2
  79. exit 1
  80. }
  81. # Loads shflags. We first look at the default install location; then look for
  82. # crosutils (chroot); finally check our own directory.
  83. load_shflags() {
  84. local my_dir="$(dirname "$(readlink -f "$0")")"
  85. local path
  86. for path in /usr/share/misc "${my_dir}"/lib/shflags; do
  87. if [[ -r "${path}/shflags" ]]; then
  88. . "${path}/shflags" || die "Could not load ${path}/shflags."
  89. return
  90. fi
  91. done
  92. die "Could not find shflags."
  93. }
  94. load_shflags
  95. HELP_GENERATE="generate: Generate an unsigned update payload."
  96. HELP_HASH="hash: Generate the hashes of the unsigned payload and metadata used \
  97. for signing."
  98. HELP_SIGN="sign: Insert the signatures into the unsigned payload."
  99. HELP_PROPERTIES="properties: Extract payload properties to a file."
  100. HELP_VERIFY="verify: Verify a (signed) update payload using delta_generator."
  101. HELP_CHECK="check: Check a (signed) update payload using paycheck (static \
  102. testing)."
  103. usage() {
  104. echo "Supported commands:"
  105. echo
  106. echo "${HELP_GENERATE}"
  107. echo "${HELP_HASH}"
  108. echo "${HELP_SIGN}"
  109. echo "${HELP_PROPERTIES}"
  110. echo "${HELP_VERIFY}"
  111. echo "${HELP_CHECK}"
  112. echo
  113. echo "Use: \"$0 <command> --help\" for more options."
  114. }
  115. # Check that a command is specified.
  116. if [[ $# -lt 1 ]]; then
  117. echo "Please specify a command [generate|hash|sign|properties|verify|check]"
  118. exit 1
  119. fi
  120. # Parse command.
  121. COMMAND="${1:-}"
  122. shift
  123. case "${COMMAND}" in
  124. generate)
  125. FLAGS_HELP="${HELP_GENERATE}"
  126. ;;
  127. hash)
  128. FLAGS_HELP="${HELP_HASH}"
  129. ;;
  130. sign)
  131. FLAGS_HELP="${HELP_SIGN}"
  132. ;;
  133. properties)
  134. FLAGS_HELP="${HELP_PROPERTIES}"
  135. ;;
  136. verify)
  137. FLAGS_HELP="${HELP_VERIFY}"
  138. ;;
  139. check)
  140. FLAGS_HELP="${HELP_CHECK}"
  141. ;;
  142. *)
  143. echo "Unrecognized command: \"${COMMAND}\"" >&2
  144. usage >&2
  145. exit 1
  146. ;;
  147. esac
  148. # Flags
  149. FLAGS_HELP="Usage: $0 ${COMMAND} [flags]
  150. ${FLAGS_HELP}"
  151. if [[ "${COMMAND}" == "generate" ]]; then
  152. DEFINE_string payload "" \
  153. "Path to output the generated unsigned payload file."
  154. DEFINE_string target_image "" \
  155. "Path to the target image that should be sent to clients."
  156. DEFINE_string source_image "" \
  157. "Optional: Path to a source image. If specified, this makes a delta update."
  158. DEFINE_string metadata_size_file "" \
  159. "Optional: Path to output metadata size."
  160. DEFINE_string max_timestamp "" \
  161. "Optional: The maximum unix timestamp of the OS allowed to apply this \
  162. payload, should be set to a number higher than the build timestamp of the \
  163. system running on the device, 0 if not specified."
  164. fi
  165. if [[ "${COMMAND}" == "hash" || "${COMMAND}" == "sign" ]]; then
  166. DEFINE_string unsigned_payload "" "Path to the input unsigned payload."
  167. DEFINE_string signature_size "" \
  168. "Signature sizes in bytes in the following format: size1:size2[:...]"
  169. fi
  170. if [[ "${COMMAND}" == "hash" ]]; then
  171. DEFINE_string metadata_hash_file "" \
  172. "Optional: Path to output metadata hash file."
  173. DEFINE_string payload_hash_file "" \
  174. "Optional: Path to output payload hash file."
  175. fi
  176. if [[ "${COMMAND}" == "sign" ]]; then
  177. DEFINE_string payload "" \
  178. "Path to output the generated unsigned payload file."
  179. DEFINE_string metadata_signature_file "" \
  180. "The metatada signatures in the following format: \
  181. metadata_signature1:metadata_signature2[:...]"
  182. DEFINE_string payload_signature_file "" \
  183. "The payload signatures in the following format: \
  184. payload_signature1:payload_signature2[:...]"
  185. DEFINE_string metadata_size_file "" \
  186. "Optional: Path to output metadata size."
  187. fi
  188. if [[ "${COMMAND}" == "properties" ]]; then
  189. DEFINE_string payload "" \
  190. "Path to the input signed or unsigned payload file."
  191. DEFINE_string properties_file "-" \
  192. "Path to output the extracted property files. If '-' is passed stdout will \
  193. be used."
  194. fi
  195. if [[ "${COMMAND}" == "verify" || "${COMMAND}" == "check" ]]; then
  196. DEFINE_string payload "" \
  197. "Path to the input payload file."
  198. DEFINE_string target_image "" \
  199. "Path to the target image to verify upon."
  200. DEFINE_string source_image "" \
  201. "Optional: Path to a source image. If specified, the delta update is \
  202. applied to this."
  203. fi
  204. DEFINE_string work_dir "${TMPDIR:-/tmp}" "Where to dump temporary files."
  205. # Parse command line flag arguments
  206. FLAGS "$@" || exit 1
  207. eval set -- "${FLAGS_ARGV}"
  208. set -e
  209. # Override the TMPDIR with the passed work_dir flags, which anyway defaults to
  210. # ${TMPDIR}.
  211. TMPDIR="${FLAGS_work_dir}"
  212. export TMPDIR
  213. # Associative arrays from partition name to file in the source and target
  214. # images. The size of the updated area must be the size of the file.
  215. declare -A SRC_PARTITIONS
  216. declare -A DST_PARTITIONS
  217. # Associative arrays for the .map files associated with each src/dst partition
  218. # file in SRC_PARTITIONS and DST_PARTITIONS.
  219. declare -A SRC_PARTITIONS_MAP
  220. declare -A DST_PARTITIONS_MAP
  221. # List of partition names in order.
  222. declare -a PARTITIONS_ORDER
  223. # A list of PIDs of the extract_image workers.
  224. EXTRACT_IMAGE_PIDS=()
  225. # A list of temporary files to remove during cleanup.
  226. CLEANUP_FILES=()
  227. # Global options to force the version of the payload.
  228. FORCE_MAJOR_VERSION=""
  229. FORCE_MINOR_VERSION=""
  230. # Path to the postinstall config file in target image if exists.
  231. POSTINSTALL_CONFIG_FILE=""
  232. # Path to the dynamic partition info file in target image if exists.
  233. DYNAMIC_PARTITION_INFO_FILE=""
  234. # read_option_int <file.txt> <option_key> [default_value]
  235. #
  236. # Reads the unsigned integer value associated with |option_key| in a key=value
  237. # file |file.txt|. Prints the read value if found and valid, otherwise prints
  238. # the |default_value|.
  239. read_option_uint() {
  240. local file_txt="$1"
  241. local option_key="$2"
  242. local default_value="${3:-}"
  243. local value
  244. if value=$(look "${option_key}=" "${file_txt}" | tail -n 1); then
  245. if value=$(echo "${value}" | cut -f 2- -d "=" | grep -E "^[0-9]+$"); then
  246. echo "${value}"
  247. return
  248. fi
  249. fi
  250. echo "${default_value}"
  251. }
  252. # truncate_file <file_path> <file_size>
  253. #
  254. # Truncate the given |file_path| to |file_size| using python.
  255. # The truncate binary might not be available.
  256. truncate_file() {
  257. local file_path="$1"
  258. local file_size="$2"
  259. python -c "open(\"${file_path}\", 'a').truncate(${file_size})"
  260. }
  261. # Create a temporary file in the work_dir with an optional pattern name.
  262. # Prints the name of the newly created file.
  263. create_tempfile() {
  264. local pattern="${1:-tempfile.XXXXXX}"
  265. mktemp --tmpdir="${FLAGS_work_dir}" "${pattern}"
  266. }
  267. cleanup() {
  268. local err=""
  269. rm -f "${CLEANUP_FILES[@]}" || err=1
  270. # If we are cleaning up after an error, or if we got an error during
  271. # cleanup (even if we eventually succeeded) return a non-zero exit
  272. # code. This triggers additional logging in most environments that call
  273. # this script.
  274. if [[ -n "${err}" ]]; then
  275. die "Cleanup encountered an error."
  276. fi
  277. }
  278. cleanup_on_error() {
  279. trap - INT TERM ERR EXIT
  280. cleanup
  281. die "Cleanup success after an error."
  282. }
  283. cleanup_on_exit() {
  284. trap - INT TERM ERR EXIT
  285. cleanup
  286. }
  287. trap cleanup_on_error INT TERM ERR
  288. trap cleanup_on_exit EXIT
  289. # extract_image <image> <partitions_array> [partitions_order]
  290. #
  291. # Detect the format of the |image| file and extract its updatable partitions
  292. # into new temporary files. Add the list of partition names and its files to the
  293. # associative array passed in |partitions_array|. If |partitions_order| is
  294. # passed, set it to list of partition names in order.
  295. extract_image() {
  296. local image="$1"
  297. # Brillo images are zip files. We detect the 4-byte magic header of the zip
  298. # file.
  299. local magic=$(xxd -p -l4 "${image}")
  300. if [[ "${magic}" == "504b0304" ]]; then
  301. echo "Detected .zip file, extracting Brillo image."
  302. extract_image_brillo "$@"
  303. return
  304. fi
  305. # Chrome OS images are GPT partitioned disks. We should have the cgpt binary
  306. # bundled here and we will use it to extract the partitions, so the GPT
  307. # headers must be valid.
  308. if cgpt show -q -n "${image}" >/dev/null; then
  309. echo "Detected GPT image, extracting Chrome OS image."
  310. extract_image_cros "$@"
  311. return
  312. fi
  313. die "Couldn't detect the image format of ${image}"
  314. }
  315. # extract_image_cros <image.bin> <partitions_array> [partitions_order]
  316. #
  317. # Extract Chromium OS recovery images into new temporary files.
  318. extract_image_cros() {
  319. local image="$1"
  320. local partitions_array="$2"
  321. local partitions_order="${3:-}"
  322. local kernel root
  323. kernel=$(create_tempfile "kernel.bin.XXXXXX")
  324. CLEANUP_FILES+=("${kernel}")
  325. root=$(create_tempfile "root.bin.XXXXXX")
  326. CLEANUP_FILES+=("${root}")
  327. cros_generate_update_payload --extract \
  328. --image "${image}" \
  329. --kern_path "${kernel}" --root_path "${root}"
  330. # Chrome OS now uses major_version 2 payloads for all boards.
  331. # See crbug.com/794404 for more information.
  332. FORCE_MAJOR_VERSION="2"
  333. eval ${partitions_array}[kernel]=\""${kernel}"\"
  334. eval ${partitions_array}[root]=\""${root}"\"
  335. if [[ -n "${partitions_order}" ]]; then
  336. eval "${partitions_order}=( \"root\" \"kernel\" )"
  337. fi
  338. local part varname
  339. for part in kernel root; do
  340. varname="${partitions_array}[${part}]"
  341. printf "md5sum of %s: " "${varname}"
  342. md5sum "${!varname}"
  343. done
  344. }
  345. # extract_partition_brillo <target_files.zip> <partitions_array> <partition>
  346. # <part_file> <part_map_file>
  347. #
  348. # Extract the <partition> from target_files zip file into <part_file> and its
  349. # map file into <part_map_file>.
  350. extract_partition_brillo() {
  351. local image="$1"
  352. local partitions_array="$2"
  353. local part="$3"
  354. local part_file="$4"
  355. local part_map_file="$5"
  356. # For each partition, we in turn look for its image file under IMAGES/ and
  357. # RADIO/ in the given target_files zip file.
  358. local path path_in_zip
  359. for path in IMAGES RADIO; do
  360. if unzip -l "${image}" "${path}/${part}.img" >/dev/null; then
  361. path_in_zip="${path}"
  362. break
  363. fi
  364. done
  365. [[ -n "${path_in_zip}" ]] || die "Failed to find ${part}.img"
  366. unzip -p "${image}" "${path_in_zip}/${part}.img" >"${part_file}"
  367. # If the partition is stored as an Android sparse image file, we need to
  368. # convert them to a raw image for the update.
  369. local magic=$(xxd -p -l4 "${part_file}")
  370. if [[ "${magic}" == "3aff26ed" ]]; then
  371. local temp_sparse=$(create_tempfile "${part}.sparse.XXXXXX")
  372. echo "Converting Android sparse image ${part}.img to RAW."
  373. mv "${part_file}" "${temp_sparse}"
  374. simg2img "${temp_sparse}" "${part_file}"
  375. rm -f "${temp_sparse}"
  376. fi
  377. # Extract the .map file (if one is available).
  378. unzip -p "${image}" "${path_in_zip}/${part}.map" >"${part_map_file}" \
  379. 2>/dev/null || true
  380. # delta_generator only supports images multiple of 4 KiB. For target images
  381. # we pad the data with zeros if needed, but for source images we truncate
  382. # down the data since the last block of the old image could be padded on
  383. # disk with unknown data.
  384. local filesize=$(stat -c%s "${part_file}")
  385. if [[ $(( filesize % 4096 )) -ne 0 ]]; then
  386. if [[ "${partitions_array}" == "SRC_PARTITIONS" ]]; then
  387. echo "Rounding DOWN partition ${part}.img to a multiple of 4 KiB."
  388. : $(( filesize = filesize & -4096 ))
  389. else
  390. echo "Rounding UP partition ${part}.img to a multiple of 4 KiB."
  391. : $(( filesize = (filesize + 4095) & -4096 ))
  392. fi
  393. truncate_file "${part_file}" "${filesize}"
  394. fi
  395. echo "Extracted ${partitions_array}[${part}]: ${filesize} bytes"
  396. }
  397. # extract_image_brillo <target_files.zip> <partitions_array> [partitions_order]
  398. #
  399. # Extract the A/B updated partitions from a Brillo target_files zip file into
  400. # new temporary files.
  401. extract_image_brillo() {
  402. local image="$1"
  403. local partitions_array="$2"
  404. local partitions_order="${3:-}"
  405. local partitions=( "boot" "system" )
  406. local ab_partitions_list
  407. ab_partitions_list=$(create_tempfile "ab_partitions_list.XXXXXX")
  408. CLEANUP_FILES+=("${ab_partitions_list}")
  409. if unzip -p "${image}" "META/ab_partitions.txt" >"${ab_partitions_list}"; then
  410. if grep -v -E '^[a-zA-Z0-9_-]*$' "${ab_partitions_list}" >&2; then
  411. die "Invalid partition names found in the partition list."
  412. fi
  413. # Get partition list without duplicates.
  414. partitions=($(awk '!seen[$0]++' "${ab_partitions_list}"))
  415. if [[ ${#partitions[@]} -eq 0 ]]; then
  416. die "The list of partitions is empty. Can't generate a payload."
  417. fi
  418. else
  419. warn "No ab_partitions.txt found. Using default."
  420. fi
  421. echo "List of A/B partitions for ${partitions_array}: ${partitions[@]}"
  422. if [[ -n "${partitions_order}" ]]; then
  423. eval "${partitions_order}=(${partitions[@]})"
  424. fi
  425. # All Brillo updaters support major version 2.
  426. FORCE_MAJOR_VERSION="2"
  427. if [[ "${partitions_array}" == "SRC_PARTITIONS" ]]; then
  428. # Source image
  429. local ue_config=$(create_tempfile "ue_config.XXXXXX")
  430. CLEANUP_FILES+=("${ue_config}")
  431. if ! unzip -p "${image}" "META/update_engine_config.txt" \
  432. >"${ue_config}"; then
  433. warn "No update_engine_config.txt found. Assuming pre-release image, \
  434. using payload minor version 2"
  435. fi
  436. # For delta payloads, we use the major and minor version supported by the
  437. # old updater.
  438. FORCE_MINOR_VERSION=$(read_option_uint "${ue_config}" \
  439. "PAYLOAD_MINOR_VERSION" 2)
  440. FORCE_MAJOR_VERSION=$(read_option_uint "${ue_config}" \
  441. "PAYLOAD_MAJOR_VERSION" 2)
  442. # Brillo support for deltas started with minor version 3.
  443. if [[ "${FORCE_MINOR_VERSION}" -le 2 ]]; then
  444. warn "No delta support from minor version ${FORCE_MINOR_VERSION}. \
  445. Disabling deltas for this source version."
  446. exit ${EX_UNSUPPORTED_DELTA}
  447. fi
  448. else
  449. # Target image
  450. local postinstall_config=$(create_tempfile "postinstall_config.XXXXXX")
  451. CLEANUP_FILES+=("${postinstall_config}")
  452. if unzip -p "${image}" "META/postinstall_config.txt" \
  453. >"${postinstall_config}"; then
  454. POSTINSTALL_CONFIG_FILE="${postinstall_config}"
  455. fi
  456. local dynamic_partitions_info=$(create_tempfile "dynamic_partitions_info.XXXXXX")
  457. CLEANUP_FILES+=("${dynamic_partitions_info}")
  458. if unzip -p "${image}" "META/dynamic_partitions_info.txt" \
  459. >"${dynamic_partitions_info}"; then
  460. DYNAMIC_PARTITION_INFO_FILE="${dynamic_partitions_info}"
  461. fi
  462. fi
  463. local part
  464. for part in "${partitions[@]}"; do
  465. local part_file=$(create_tempfile "${part}.img.XXXXXX")
  466. local part_map_file=$(create_tempfile "${part}.map.XXXXXX")
  467. CLEANUP_FILES+=("${part_file}" "${part_map_file}")
  468. # Extract partitions in background.
  469. extract_partition_brillo "${image}" "${partitions_array}" "${part}" \
  470. "${part_file}" "${part_map_file}" &
  471. EXTRACT_IMAGE_PIDS+=("$!")
  472. eval "${partitions_array}[\"${part}\"]=\"${part_file}\""
  473. eval "${partitions_array}_MAP[\"${part}\"]=\"${part_map_file}\""
  474. done
  475. }
  476. # cleanup_partition_array <partitions_array>
  477. #
  478. # Remove all empty files in <partitions_array>.
  479. cleanup_partition_array() {
  480. local partitions_array="$1"
  481. # Have to use eval to iterate over associative array keys with variable array
  482. # names, we should change it to use nameref once bash 4.3 is available
  483. # everywhere.
  484. for part in $(eval "echo \${!${partitions_array}[@]}"); do
  485. local path="${partitions_array}[$part]"
  486. if [[ ! -s "${!path}" ]]; then
  487. eval "unset ${partitions_array}[${part}]"
  488. fi
  489. done
  490. }
  491. extract_payload_images() {
  492. local payload_type=$1
  493. echo "Extracting images for ${payload_type} update."
  494. if [[ "${payload_type}" == "delta" ]]; then
  495. extract_image "${FLAGS_source_image}" SRC_PARTITIONS
  496. fi
  497. extract_image "${FLAGS_target_image}" DST_PARTITIONS PARTITIONS_ORDER
  498. # Wait for all subprocesses to finish. Not using `wait` since it doesn't die
  499. # on non-zero subprocess exit code. Not using `wait ${EXTRACT_IMAGE_PIDS[@]}`
  500. # as it gives the status of the last process it has waited for.
  501. for pid in ${EXTRACT_IMAGE_PIDS[@]}; do
  502. wait ${pid}
  503. done
  504. cleanup_partition_array SRC_PARTITIONS
  505. cleanup_partition_array SRC_PARTITIONS_MAP
  506. cleanup_partition_array DST_PARTITIONS
  507. cleanup_partition_array DST_PARTITIONS_MAP
  508. }
  509. get_payload_type() {
  510. if [[ -z "${FLAGS_source_image}" ]]; then
  511. echo "full"
  512. else
  513. echo "delta"
  514. fi
  515. }
  516. validate_generate() {
  517. [[ -n "${FLAGS_payload}" ]] ||
  518. die "You must specify an output filename with --payload FILENAME"
  519. [[ -n "${FLAGS_target_image}" ]] ||
  520. die "You must specify a target image with --target_image FILENAME"
  521. }
  522. cmd_generate() {
  523. local payload_type=$(get_payload_type)
  524. extract_payload_images ${payload_type}
  525. echo "Generating ${payload_type} update."
  526. # Common payload args:
  527. GENERATOR_ARGS=( --out_file="${FLAGS_payload}" )
  528. local part old_partitions="" new_partitions="" partition_names=""
  529. local old_mapfiles="" new_mapfiles=""
  530. for part in "${PARTITIONS_ORDER[@]}"; do
  531. if [[ -n "${partition_names}" ]]; then
  532. partition_names+=":"
  533. new_partitions+=":"
  534. old_partitions+=":"
  535. new_mapfiles+=":"
  536. old_mapfiles+=":"
  537. fi
  538. partition_names+="${part}"
  539. new_partitions+="${DST_PARTITIONS[${part}]}"
  540. old_partitions+="${SRC_PARTITIONS[${part}]:-}"
  541. new_mapfiles+="${DST_PARTITIONS_MAP[${part}]:-}"
  542. old_mapfiles+="${SRC_PARTITIONS_MAP[${part}]:-}"
  543. done
  544. # Target image args:
  545. GENERATOR_ARGS+=(
  546. --partition_names="${partition_names}"
  547. --new_partitions="${new_partitions}"
  548. --new_mapfiles="${new_mapfiles}"
  549. )
  550. if [[ "${payload_type}" == "delta" ]]; then
  551. # Source image args:
  552. GENERATOR_ARGS+=(
  553. --old_partitions="${old_partitions}"
  554. --old_mapfiles="${old_mapfiles}"
  555. )
  556. if [[ -n "${FORCE_MINOR_VERSION}" ]]; then
  557. GENERATOR_ARGS+=( --minor_version="${FORCE_MINOR_VERSION}" )
  558. fi
  559. fi
  560. if [[ -n "${FORCE_MAJOR_VERSION}" ]]; then
  561. GENERATOR_ARGS+=( --major_version="${FORCE_MAJOR_VERSION}" )
  562. fi
  563. if [[ -n "${FLAGS_metadata_size_file}" ]]; then
  564. GENERATOR_ARGS+=( --out_metadata_size_file="${FLAGS_metadata_size_file}" )
  565. fi
  566. if [[ -n "${FLAGS_max_timestamp}" ]]; then
  567. GENERATOR_ARGS+=( --max_timestamp="${FLAGS_max_timestamp}" )
  568. fi
  569. if [[ -n "${POSTINSTALL_CONFIG_FILE}" ]]; then
  570. GENERATOR_ARGS+=(
  571. --new_postinstall_config_file="${POSTINSTALL_CONFIG_FILE}"
  572. )
  573. fi
  574. if [[ -n "{DYNAMIC_PARTITION_INFO_FILE}" ]]; then
  575. GENERATOR_ARGS+=(
  576. --dynamic_partition_info_file="${DYNAMIC_PARTITION_INFO_FILE}"
  577. )
  578. fi
  579. echo "Running delta_generator with args: ${GENERATOR_ARGS[@]}"
  580. "${GENERATOR}" "${GENERATOR_ARGS[@]}"
  581. echo "Done generating ${payload_type} update."
  582. }
  583. validate_hash() {
  584. [[ -n "${FLAGS_signature_size}" ]] ||
  585. die "You must specify signature size with --signature_size SIZES"
  586. [[ -n "${FLAGS_unsigned_payload}" ]] ||
  587. die "You must specify the input unsigned payload with \
  588. --unsigned_payload FILENAME"
  589. [[ -n "${FLAGS_payload_hash_file}" ]] ||
  590. die "You must specify --payload_hash_file FILENAME"
  591. [[ -n "${FLAGS_metadata_hash_file}" ]] ||
  592. die "You must specify --metadata_hash_file FILENAME"
  593. }
  594. cmd_hash() {
  595. "${GENERATOR}" \
  596. --in_file="${FLAGS_unsigned_payload}" \
  597. --signature_size="${FLAGS_signature_size}" \
  598. --out_hash_file="${FLAGS_payload_hash_file}" \
  599. --out_metadata_hash_file="${FLAGS_metadata_hash_file}"
  600. echo "Done generating hash."
  601. }
  602. validate_sign() {
  603. [[ -n "${FLAGS_signature_size}" ]] ||
  604. die "You must specify signature size with --signature_size SIZES"
  605. [[ -n "${FLAGS_unsigned_payload}" ]] ||
  606. die "You must specify the input unsigned payload with \
  607. --unsigned_payload FILENAME"
  608. [[ -n "${FLAGS_payload}" ]] ||
  609. die "You must specify the output signed payload with --payload FILENAME"
  610. [[ -n "${FLAGS_payload_signature_file}" ]] ||
  611. die "You must specify the payload signature file with \
  612. --payload_signature_file SIGNATURES"
  613. [[ -n "${FLAGS_metadata_signature_file}" ]] ||
  614. die "You must specify the metadata signature file with \
  615. --metadata_signature_file SIGNATURES"
  616. }
  617. cmd_sign() {
  618. GENERATOR_ARGS=(
  619. --in_file="${FLAGS_unsigned_payload}"
  620. --signature_size="${FLAGS_signature_size}"
  621. --payload_signature_file="${FLAGS_payload_signature_file}"
  622. --metadata_signature_file="${FLAGS_metadata_signature_file}"
  623. --out_file="${FLAGS_payload}"
  624. )
  625. if [[ -n "${FLAGS_metadata_size_file}" ]]; then
  626. GENERATOR_ARGS+=( --out_metadata_size_file="${FLAGS_metadata_size_file}" )
  627. fi
  628. "${GENERATOR}" "${GENERATOR_ARGS[@]}"
  629. echo "Done signing payload."
  630. }
  631. validate_properties() {
  632. [[ -n "${FLAGS_payload}" ]] ||
  633. die "You must specify the payload file with --payload FILENAME"
  634. [[ -n "${FLAGS_properties_file}" ]] ||
  635. die "You must specify a non empty --properties_file FILENAME"
  636. }
  637. cmd_properties() {
  638. "${GENERATOR}" \
  639. --in_file="${FLAGS_payload}" \
  640. --properties_file="${FLAGS_properties_file}"
  641. }
  642. validate_verify_and_check() {
  643. [[ -n "${FLAGS_payload}" ]] ||
  644. die "Error: you must specify an input filename with --payload FILENAME"
  645. [[ -n "${FLAGS_target_image}" ]] ||
  646. die "Error: you must specify a target image with --target_image FILENAME"
  647. }
  648. cmd_verify() {
  649. local payload_type=$(get_payload_type)
  650. extract_payload_images ${payload_type}
  651. declare -A TMP_PARTITIONS
  652. for part in "${PARTITIONS_ORDER[@]}"; do
  653. local tmp_part=$(create_tempfile "tmp_part.bin.XXXXXX")
  654. echo "Creating temporary target partition ${tmp_part} for ${part}"
  655. CLEANUP_FILES+=("${tmp_part}")
  656. TMP_PARTITIONS[${part}]=${tmp_part}
  657. local FILESIZE=$(stat -c%s "${DST_PARTITIONS[${part}]}")
  658. echo "Truncating ${TMP_PARTITIONS[${part}]} to ${FILESIZE}"
  659. truncate_file "${TMP_PARTITIONS[${part}]}" "${FILESIZE}"
  660. done
  661. echo "Verifying ${payload_type} update."
  662. # Common payload args:
  663. GENERATOR_ARGS=( --in_file="${FLAGS_payload}" )
  664. local part old_partitions="" new_partitions="" partition_names=""
  665. for part in "${PARTITIONS_ORDER[@]}"; do
  666. if [[ -n "${partition_names}" ]]; then
  667. partition_names+=":"
  668. new_partitions+=":"
  669. old_partitions+=":"
  670. fi
  671. partition_names+="${part}"
  672. new_partitions+="${TMP_PARTITIONS[${part}]}"
  673. old_partitions+="${SRC_PARTITIONS[${part}]:-}"
  674. done
  675. # Target image args:
  676. GENERATOR_ARGS+=(
  677. --partition_names="${partition_names}"
  678. --new_partitions="${new_partitions}"
  679. )
  680. if [[ "${payload_type}" == "delta" ]]; then
  681. # Source image args:
  682. GENERATOR_ARGS+=(
  683. --old_partitions="${old_partitions}"
  684. )
  685. fi
  686. if [[ -n "${FORCE_MAJOR_VERSION}" ]]; then
  687. GENERATOR_ARGS+=( --major_version="${FORCE_MAJOR_VERSION}" )
  688. fi
  689. echo "Running delta_generator to verify ${payload_type} payload with args: \
  690. ${GENERATOR_ARGS[@]}"
  691. "${GENERATOR}" "${GENERATOR_ARGS[@]}" || true
  692. echo "Done applying ${payload_type} update."
  693. echo "Checking the newly generated partitions against the target partitions"
  694. local need_pause=false
  695. for part in "${PARTITIONS_ORDER[@]}"; do
  696. local not_str=""
  697. if ! cmp "${TMP_PARTITIONS[${part}]}" "${DST_PARTITIONS[${part}]}"; then
  698. not_str="in"
  699. need_pause=true
  700. fi
  701. echo "The new partition (${part}) is ${not_str}valid."
  702. done
  703. # All images will be cleaned up when script exits, pause here to give a chance
  704. # to inspect the images.
  705. if [[ "$need_pause" == true ]]; then
  706. read -n1 -r -s -p "Paused to investigate invalid partitions, \
  707. press any key to exit."
  708. fi
  709. }
  710. cmd_check() {
  711. local payload_type=$(get_payload_type)
  712. extract_payload_images ${payload_type}
  713. local part dst_partitions="" src_partitions=""
  714. for part in "${PARTITIONS_ORDER[@]}"; do
  715. if [[ -n "${dst_partitions}" ]]; then
  716. dst_partitions+=" "
  717. src_partitions+=" "
  718. fi
  719. dst_partitions+="${DST_PARTITIONS[${part}]}"
  720. src_partitions+="${SRC_PARTITIONS[${part}]:-}"
  721. done
  722. # Common payload args:
  723. PAYCHECK_ARGS=( "${FLAGS_payload}" --type ${payload_type} \
  724. --part_names ${PARTITIONS_ORDER[@]} \
  725. --dst_part_paths ${dst_partitions} )
  726. if [[ ! -z "${SRC_PARTITIONS[@]}" ]]; then
  727. PAYCHECK_ARGS+=( --src_part_paths ${src_partitions} )
  728. fi
  729. echo "Checking ${payload_type} update."
  730. check_update_payload ${PAYCHECK_ARGS[@]} --check
  731. }
  732. # Sanity check that the real generator exists:
  733. GENERATOR="$(which delta_generator || true)"
  734. [[ -x "${GENERATOR}" ]] || die "can't find delta_generator"
  735. case "$COMMAND" in
  736. generate) validate_generate
  737. cmd_generate
  738. ;;
  739. hash) validate_hash
  740. cmd_hash
  741. ;;
  742. sign) validate_sign
  743. cmd_sign
  744. ;;
  745. properties) validate_properties
  746. cmd_properties
  747. ;;
  748. verify) validate_verify_and_check
  749. cmd_verify
  750. ;;
  751. check) validate_verify_and_check
  752. cmd_check
  753. ;;
  754. esac