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