brillo_update_payload revision a566cb638023f12c9a64df31d83c464c13cf4d92
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 partitions=($(cat "${ab_partitions_list}")) 401 if [[ ${#partitions[@]} -eq 0 ]]; then 402 die "The list of partitions is empty. Can't generate a payload." 403 fi 404 else 405 warn "No ab_partitions.txt found. Using default." 406 fi 407 echo "List of A/B partitions: ${partitions[@]}" 408 409 if [[ -n "${partitions_order}" ]]; then 410 eval "${partitions_order}=(${partitions[@]})" 411 fi 412 413 # All Brillo updaters support major version 2. 414 FORCE_MAJOR_VERSION="2" 415 416 if [[ "${partitions_array}" == "SRC_PARTITIONS" ]]; then 417 # Source image 418 local ue_config=$(create_tempfile "ue_config.XXXXXX") 419 CLEANUP_FILES+=("${ue_config}") 420 if ! unzip -p "${image}" "META/update_engine_config.txt" \ 421 >"${ue_config}"; then 422 warn "No update_engine_config.txt found. Assuming pre-release image, \ 423using payload minor version 2" 424 fi 425 # For delta payloads, we use the major and minor version supported by the 426 # old updater. 427 FORCE_MINOR_VERSION=$(read_option_uint "${ue_config}" \ 428 "PAYLOAD_MINOR_VERSION" 2) 429 FORCE_MAJOR_VERSION=$(read_option_uint "${ue_config}" \ 430 "PAYLOAD_MAJOR_VERSION" 2) 431 432 # Brillo support for deltas started with minor version 3. 433 if [[ "${FORCE_MINOR_VERSION}" -le 2 ]]; then 434 warn "No delta support from minor version ${FORCE_MINOR_VERSION}. \ 435Disabling deltas for this source version." 436 exit ${EX_UNSUPPORTED_DELTA} 437 fi 438 else 439 # Target image 440 local postinstall_config=$(create_tempfile "postinstall_config.XXXXXX") 441 CLEANUP_FILES+=("${postinstall_config}") 442 if unzip -p "${image}" "META/postinstall_config.txt" \ 443 >"${postinstall_config}"; then 444 POSTINSTALL_CONFIG_FILE="${postinstall_config}" 445 fi 446 fi 447 448 local part part_file temp_raw filesize 449 for part in "${partitions[@]}"; do 450 part_file=$(create_tempfile "${part}.img.XXXXXX") 451 CLEANUP_FILES+=("${part_file}") 452 unzip -p "${image}" "IMAGES/${part}.img" >"${part_file}" 453 454 # If the partition is stored as an Android sparse image file, we need to 455 # convert them to a raw image for the update. 456 local magic=$(head --bytes=4 "${part_file}" | hexdump -e '1/1 "%.2x"') 457 if [[ "${magic}" == "3aff26ed" ]]; then 458 temp_raw=$(create_tempfile "${part}.raw.XXXXXX") 459 CLEANUP_FILES+=("${temp_raw}") 460 echo "Converting Android sparse image ${part}.img to RAW." 461 simg2img "${part_file}" "${temp_raw}" 462 # At this point, we can drop the contents of the old part_file file, but 463 # we can't delete the file because it will be deleted in cleanup. 464 true >"${part_file}" 465 part_file="${temp_raw}" 466 fi 467 468 # Extract the .map file (if one is available). 469 part_map_file=$(create_tempfile "${part}.map.XXXXXX") 470 CLEANUP_FILES+=("${part_map_file}") 471 unzip -p "${image}" "IMAGES/${part}.map" >"${part_map_file}" || \ 472 part_map_file="" 473 474 # delta_generator only supports images multiple of 4 KiB. For target images 475 # we pad the data with zeros if needed, but for source images we truncate 476 # down the data since the last block of the old image could be padded on 477 # disk with unknown data. 478 filesize=$(stat -c%s "${part_file}") 479 if [[ $(( filesize % 4096 )) -ne 0 ]]; then 480 if [[ "${partitions_array}" == "SRC_PARTITIONS" ]]; then 481 echo "Rounding DOWN partition ${part}.img to a multiple of 4 KiB." 482 : $(( filesize = filesize & -4096 )) 483 if [[ ${filesize} == 0 ]]; then 484 echo "Source partition ${part}.img is empty after rounding down," \ 485 "skipping." 486 continue 487 fi 488 else 489 echo "Rounding UP partition ${part}.img to a multiple of 4 KiB." 490 : $(( filesize = (filesize + 4095) & -4096 )) 491 fi 492 truncate_file "${part_file}" "${filesize}" 493 fi 494 495 eval "${partitions_array}[\"${part}\"]=\"${part_file}\"" 496 eval "${partitions_array}_MAP[\"${part}\"]=\"${part_map_file}\"" 497 echo "Extracted ${partitions_array}[${part}]: ${filesize} bytes" 498 done 499} 500 501validate_generate() { 502 [[ -n "${FLAGS_payload}" ]] || 503 die "You must specify an output filename with --payload FILENAME" 504 505 [[ -n "${FLAGS_target_image}" ]] || 506 die "You must specify a target image with --target_image FILENAME" 507} 508 509cmd_generate() { 510 local payload_type="delta" 511 if [[ -z "${FLAGS_source_image}" ]]; then 512 payload_type="full" 513 fi 514 515 echo "Extracting images for ${payload_type} update." 516 517 extract_image "${FLAGS_target_image}" DST_PARTITIONS PARTITIONS_ORDER 518 if [[ "${payload_type}" == "delta" ]]; then 519 extract_image "${FLAGS_source_image}" SRC_PARTITIONS 520 fi 521 522 echo "Generating ${payload_type} update." 523 # Common payload args: 524 GENERATOR_ARGS=( -out_file="${FLAGS_payload}" ) 525 526 local part old_partitions="" new_partitions="" partition_names="" 527 local old_mapfiles="" new_mapfiles="" 528 for part in "${PARTITIONS_ORDER[@]}"; do 529 if [[ -n "${partition_names}" ]]; then 530 partition_names+=":" 531 new_partitions+=":" 532 old_partitions+=":" 533 new_mapfiles+=":" 534 old_mapfiles+=":" 535 fi 536 partition_names+="${part}" 537 new_partitions+="${DST_PARTITIONS[${part}]}" 538 old_partitions+="${SRC_PARTITIONS[${part}]:-}" 539 new_mapfiles+="${DST_PARTITIONS_MAP[${part}]:-}" 540 old_mapfiles+="${SRC_PARTITIONS_MAP[${part}]:-}" 541 done 542 543 # Target image args: 544 GENERATOR_ARGS+=( 545 -partition_names="${partition_names}" 546 -new_partitions="${new_partitions}" 547 -new_mapfiles="${new_mapfiles}" 548 ) 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 561 if [[ -n "${FORCE_MAJOR_VERSION}" ]]; then 562 GENERATOR_ARGS+=( --major_version="${FORCE_MAJOR_VERSION}" ) 563 fi 564 565 if [[ -n "${FLAGS_metadata_size_file}" ]]; then 566 GENERATOR_ARGS+=( --out_metadata_size_file="${FLAGS_metadata_size_file}" ) 567 fi 568 569 if [[ -n "${POSTINSTALL_CONFIG_FILE}" ]]; then 570 GENERATOR_ARGS+=( 571 --new_postinstall_config_file="${POSTINSTALL_CONFIG_FILE}" 572 ) 573 fi 574 575 echo "Running delta_generator with args: ${GENERATOR_ARGS[@]}" 576 "${GENERATOR}" "${GENERATOR_ARGS[@]}" 577 578 echo "Done generating ${payload_type} update." 579} 580 581validate_hash() { 582 [[ -n "${FLAGS_signature_size}" ]] || 583 die "You must specify signature size with --signature_size SIZES" 584 585 [[ -n "${FLAGS_unsigned_payload}" ]] || 586 die "You must specify the input unsigned payload with \ 587--unsigned_payload FILENAME" 588 589 [[ -n "${FLAGS_payload_hash_file}" ]] || 590 die "You must specify --payload_hash_file FILENAME" 591 592 [[ -n "${FLAGS_metadata_hash_file}" ]] || 593 die "You must specify --metadata_hash_file FILENAME" 594} 595 596cmd_hash() { 597 "${GENERATOR}" \ 598 -in_file="${FLAGS_unsigned_payload}" \ 599 -signature_size="${FLAGS_signature_size}" \ 600 -out_hash_file="${FLAGS_payload_hash_file}" \ 601 -out_metadata_hash_file="${FLAGS_metadata_hash_file}" 602 603 echo "Done generating hash." 604} 605 606validate_sign() { 607 [[ -n "${FLAGS_signature_size}" ]] || 608 die "You must specify signature size with --signature_size SIZES" 609 610 [[ -n "${FLAGS_unsigned_payload}" ]] || 611 die "You must specify the input unsigned payload with \ 612--unsigned_payload FILENAME" 613 614 [[ -n "${FLAGS_payload}" ]] || 615 die "You must specify the output signed payload with --payload FILENAME" 616 617 [[ -n "${FLAGS_payload_signature_file}" ]] || 618 die "You must specify the payload signature file with \ 619--payload_signature_file SIGNATURES" 620 621 [[ -n "${FLAGS_metadata_signature_file}" ]] || 622 die "You must specify the metadata signature file with \ 623--metadata_signature_file SIGNATURES" 624} 625 626cmd_sign() { 627 GENERATOR_ARGS=( 628 -in_file="${FLAGS_unsigned_payload}" 629 -signature_size="${FLAGS_signature_size}" 630 -signature_file="${FLAGS_payload_signature_file}" 631 -metadata_signature_file="${FLAGS_metadata_signature_file}" 632 -out_file="${FLAGS_payload}" 633 ) 634 635 if [[ -n "${FLAGS_metadata_size_file}" ]]; then 636 GENERATOR_ARGS+=( --out_metadata_size_file="${FLAGS_metadata_size_file}" ) 637 fi 638 639 "${GENERATOR}" "${GENERATOR_ARGS[@]}" 640 echo "Done signing payload." 641} 642 643validate_properties() { 644 [[ -n "${FLAGS_payload}" ]] || 645 die "You must specify the payload file with --payload FILENAME" 646 647 [[ -n "${FLAGS_properties_file}" ]] || 648 die "You must specify a non empty --properties_file FILENAME" 649} 650 651cmd_properties() { 652 "${GENERATOR}" \ 653 -in_file="${FLAGS_payload}" \ 654 -properties_file="${FLAGS_properties_file}" 655} 656 657validate_verify() { 658 [[ -n "${FLAGS_payload}" ]] || 659 die "Error: you must specify an input filename with --payload FILENAME" 660 661 [[ -n "${FLAGS_target_image}" ]] || 662 die "Error: you must specify a target image with --target_image FILENAME" 663} 664 665cmd_verify() { 666 local payload_type="delta" 667 if [[ -z "${FLAGS_source_image}" ]]; then 668 payload_type="full" 669 fi 670 671 echo "Extracting images for ${payload_type} update." 672 673 if [[ "${payload_type}" == "delta" ]]; then 674 extract_image "${FLAGS_source_image}" SRC_PARTITIONS 675 fi 676 extract_image "${FLAGS_target_image}" DST_PARTITIONS PARTITIONS_ORDER 677 678 declare -A TMP_PARTITIONS 679 for part in "${PARTITIONS_ORDER[@]}"; do 680 local tmp_part=$(create_tempfile "tmp_part.bin.XXXXXX") 681 echo "Creating temporary target partition ${tmp_part} for ${part}" 682 CLEANUP_FILES+=("${tmp_part}") 683 TMP_PARTITIONS[${part}]=${tmp_part} 684 local FILESIZE=$(stat -c%s "${DST_PARTITIONS[${part}]}") 685 echo "Truncating ${TMP_PARTITIONS[${part}]} to ${FILESIZE}" 686 truncate_file "${TMP_PARTITIONS[${part}]}" "${FILESIZE}" 687 done 688 689 echo "Verifying ${payload_type} update." 690 # Common payload args: 691 GENERATOR_ARGS=( -in_file="${FLAGS_payload}" ) 692 693 local part old_partitions="" new_partitions="" partition_names="" 694 for part in "${PARTITIONS_ORDER[@]}"; do 695 if [[ -n "${partition_names}" ]]; then 696 partition_names+=":" 697 new_partitions+=":" 698 old_partitions+=":" 699 fi 700 partition_names+="${part}" 701 new_partitions+="${TMP_PARTITIONS[${part}]}" 702 old_partitions+="${SRC_PARTITIONS[${part}]:-}" 703 done 704 705 # Target image args: 706 GENERATOR_ARGS+=( 707 -partition_names="${partition_names}" 708 -new_partitions="${new_partitions}" 709 ) 710 711 if [[ "${payload_type}" == "delta" ]]; then 712 # Source image args: 713 GENERATOR_ARGS+=( 714 -old_partitions="${old_partitions}" 715 ) 716 fi 717 718 if [[ -n "${FORCE_MAJOR_VERSION}" ]]; then 719 GENERATOR_ARGS+=( --major_version="${FORCE_MAJOR_VERSION}" ) 720 fi 721 722 echo "Running delta_generator to verify ${payload_type} payload with args: \ 723${GENERATOR_ARGS[@]}" 724 "${GENERATOR}" "${GENERATOR_ARGS[@]}" 725 726 if [[ $? -eq 0 ]]; then 727 echo "Done applying ${payload_type} update." 728 echo "Checking the newly generated partitions against the target partitions" 729 for part in "${PARTITIONS_ORDER[@]}"; do 730 cmp "${TMP_PARTITIONS[${part}]}" "${DST_PARTITIONS[${part}]}" 731 local not_str="" 732 if [[ $? -ne 0 ]]; then 733 not_str="in" 734 fi 735 echo "The new partition (${part}) is ${not_str}valid." 736 done 737 else 738 echo "Failed to apply ${payload_type} update." 739 fi 740} 741 742# Sanity check that the real generator exists: 743GENERATOR="$(which delta_generator || true)" 744[[ -x "${GENERATOR}" ]] || die "can't find delta_generator" 745 746case "$COMMAND" in 747 generate) validate_generate 748 cmd_generate 749 ;; 750 hash) validate_hash 751 cmd_hash 752 ;; 753 sign) validate_sign 754 cmd_sign 755 ;; 756 properties) validate_properties 757 cmd_properties 758 ;; 759 verify) validate_verify 760 cmd_verify 761 ;; 762esac 763