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