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