| #!/bin/bash |
| |
| # Copyright 2015 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| # Script to generate a Brillo update for use by the update engine. |
| # |
| # usage: brillo_update_payload COMMAND [ARGS] |
| # The following commands are supported: |
| # generate generate an unsigned payload |
| # hash generate a payload or metadata hash |
| # sign generate a signed payload |
| # |
| # Generate command arguments: |
| # --payload generated unsigned payload output file |
| # --source_image if defined, generate a delta payload from the specified |
| # image to the target_image |
| # --target_image the target image that should be sent to clients |
| # |
| # Hash command arguments: |
| # --unsigned_payload the input unsigned payload to generate the hash from |
| # --signature_size signature sizes in bytes in the following format: |
| # "size1:size2[:...]" |
| # --payload_hash_file if defined, generate a payload hash and output to the |
| # specified file |
| # --metadata_hash_file if defined, generate a metadata hash and output to the |
| # specified file |
| # |
| # Sign command arguments: |
| # --unsigned_payload the input unsigned payload to insert the signatures |
| # --payload the output signed payload |
| # --signature_size signature sizes in bytes in the following format: |
| # "size1:size2[:...]" |
| # --payload_signature_file the payload signature files in the following |
| # format: |
| # "payload_signature1:payload_signature2[:...]" |
| # --metadata_signature_file the metadata signature files in the following |
| # format: |
| # "metadata_signature1:metadata_signature2[:...]" |
| # Note that the number of signature sizes and payload signatures have to match. |
| |
| # Load common CrOS utilities. Inside the chroot this file is installed in |
| # /usr/lib/crosutils. This script may also be called from a zipfile, in which |
| # case common.sh will be in the current directory. |
| find_common_sh() { |
| local thisdir="$(dirname "$(readlink -f "$0")")" |
| local common_paths=(/usr/lib/crosutils "${thisdir}") |
| local path |
| |
| SCRIPT_ROOT="${common_paths[0]}" |
| for path in "${common_paths[@]}"; do |
| if [[ -r "${path}/common.sh" ]]; then |
| SCRIPT_ROOT="${path}" |
| break |
| fi |
| done |
| |
| # We have to fake GCLIENT_ROOT in case we're running inside |
| # au_zip enviroment. GCLIENT_ROOT detection became fatal. |
| [[ "${SCRIPT_ROOT}" == "${thisdir}" ]] && export GCLIENT_ROOT="." |
| } |
| |
| find_common_sh |
| . "${SCRIPT_ROOT}/common.sh" || exit 1 |
| |
| # Check that a command is specified |
| if [[ $# -lt 1 ]]; then |
| echo "Please specify a command [generate|hash|sign]" |
| exit 1 |
| fi |
| |
| # Parse command |
| case "$1" in |
| generate|hash|sign) |
| COMMAND=$1 |
| ;; |
| *) |
| echo "Unrecognized command:" $1 |
| exit 1 |
| ;; |
| esac |
| |
| shift |
| |
| # Flags |
| DEFINE_string payload "" "Path to output the generated payload file." |
| DEFINE_string target_image "" \ |
| "Path to the target image that should be sent to clients." |
| DEFINE_string source_image "" \ |
| "Optional: Path to a source image. If specified, this makes\ |
| a delta update." |
| DEFINE_string unsigned_payload "" "Path to the generated unsigned payload." |
| DEFINE_string signature_size "" \ |
| "Signature sizes in bytes in the following format: size1:size2[:...]" |
| DEFINE_string payload_hash_file "" "Optional: Path to output payload hash file." |
| DEFINE_string metadata_hash_file "" \ |
| "Optional: Path to output metadata hash file." |
| DEFINE_string payload_signature_file "" \ |
| "The payload signatures in the following format:\ |
| payload_signature1:payload_signature2[:...]" |
| DEFINE_string metadata_signature_file "" \ |
| "The metatada signatures in the following format:\ |
| metadata_signature1:metadata_signature2[:...]" |
| DEFINE_string work_dir "/tmp" "Where to dump temporary files." |
| |
| # Parse command line flag arguments |
| FLAGS "$@" || exit 1 |
| eval set -- "${FLAGS_ARGV}" |
| set -e |
| |
| # Associative arrays from partition name to file in the source and target |
| # images. The size of the updated area must be the size of the file. |
| declare -A SRC_PARTITIONS |
| declare -A DST_PARTITIONS |
| |
| # A list of temporary files to remove during cleanup. |
| CLEANUP_FILES=() |
| |
| # Create a temporary file in the work_dir with an optional pattern name. |
| # Prints the name of the newly created file. |
| create_tempfile() { |
| local pattern="${1:-tempfile.XXXXXX}" |
| mktemp --tmpdir="${FLAGS_work_dir}" "${pattern}" |
| } |
| |
| cleanup() { |
| local err="" |
| rm -f "${CLEANUP_FILES[@]}" || err=1 |
| |
| # If we are cleaning up after an error, or if we got an error during |
| # cleanup (even if we eventually succeeded) return a non-zero exit |
| # code. This triggers additional logging in most environments that call |
| # this script. |
| if [[ -n "${err}" ]]; then |
| die "Cleanup encountered an error." |
| fi |
| } |
| |
| cleanup_on_error() { |
| trap - INT TERM ERR EXIT |
| cleanup |
| die "Cleanup success after an error." |
| } |
| |
| cleanup_on_exit() { |
| trap - INT TERM ERR EXIT |
| cleanup |
| } |
| |
| trap cleanup_on_error INT TERM ERR |
| trap cleanup_on_exit EXIT |
| |
| # extract_image_cros <image.bin> <partitions_array> |
| # |
| # Extract Chromium OS recovery images into new temporary files. Add the list |
| # of partition names and its files to the associative array passed in |
| # partitions_array. |
| extract_image_cros() { |
| local image="$1" |
| local partitions_array="$2" |
| |
| local kernel root |
| kernel=$(create_tempfile "kernel.bin.XXXXXX") |
| CLEANUP_FILES+=("${kernel}") |
| root=$(create_tempfile "root.bin.XXXXXX") |
| CLEANUP_FILES+=("${root}") |
| |
| cros_generate_update_payload --extract \ |
| --image "${image}" \ |
| --kern_path "${kernel}" --root_path "${root}" \ |
| --work_dir "${FLAGS_work_dir}" --outside_chroot |
| |
| # When generating legacy Chrome OS images, we need to use "kernel" and "root" |
| # for the partition names. |
| eval ${partitions_array}[kernel]=\""${kernel}"\" |
| eval ${partitions_array}[root]=\""${root}"\" |
| |
| local part varname |
| for part in root kernel; do |
| varname="${partitions_array}[${part}]" |
| printf "md5sum of %s: " "${varname}" |
| md5sum "${!varname}" |
| done |
| } |
| |
| validate_generate() { |
| [[ -n "${FLAGS_payload}" ]] || |
| die "Error: you must specify an output filename with --payload FILENAME" |
| |
| [[ -n "${FLAGS_target_image}" ]] || |
| die "Error: you must specify a target image with --target_image FILENAME" |
| } |
| |
| cmd_generate() { |
| local payload_type="delta" |
| if [[ -z "${FLAGS_source_image}" ]]; then |
| payload_type="full" |
| fi |
| |
| echo "Generating ${payload_type} update" |
| |
| # TODO(deymo): Detect the format the image and call the right extract_image |
| # function. |
| extract_image_cros "${FLAGS_target_image}" DST_PARTITIONS |
| if [[ "${payload_type}" == "delta" ]]; then |
| extract_image_cros "${FLAGS_source_image}" SRC_PARTITIONS |
| fi |
| |
| GENERATOR_ARGS=( |
| # Common payload args: |
| -out_file="${FLAGS_payload}" |
| # Target image args: |
| # TODO(deymo): Pass the list of partitions to the generator. |
| -new_image="${DST_PARTITIONS[root]}" |
| -new_kernel="${DST_PARTITIONS[kernel]}" |
| ) |
| |
| if [[ "${payload_type}" == "delta" ]]; then |
| GENERATOR_ARGS+=( |
| # Source image args: |
| -old_image="${SRC_PARTITIONS[root]}" |
| -old_kernel="${SRC_PARTITIONS[kernel]}" |
| ) |
| fi |
| |
| echo "Running delta_generator with args: ${GENERATOR_ARGS[@]}" |
| "${GENERATOR}" "${GENERATOR_ARGS[@]}" |
| |
| echo "Done generating ${payload_type} update." |
| } |
| |
| validate_hash() { |
| [[ -n "${FLAGS_signature_size}" ]] || |
| die "Error: you must specify signature size with --signature_size SIZES" |
| |
| [[ -n "${FLAGS_unsigned_payload}" ]] || |
| die "Error: you must specify the input unsigned payload with \ |
| --unsigned_payload FILENAME" |
| |
| [[ -n "${FLAGS_metadata_hash_file}" ]] || |
| [[ -n "${FLAGS_payload_hash_file}" ]] || |
| die "Error: you must specify --metadata_hash_file FILENAME \ |
| or --payload_hash_file FILENAME" |
| } |
| |
| cmd_hash() { |
| if [[ -n "${FLAGS_metadata_hash_file}" ]]; then |
| "${GENERATOR}" \ |
| -in_file="${FLAGS_unsigned_payload}" \ |
| -signature_size="${FLAGS_signature_size}" \ |
| -out_metadata_hash_file="${FLAGS_metadata_hash_file}" |
| fi |
| |
| if [[ -n "${FLAGS_payload_hash_file}" ]]; then |
| "${GENERATOR}" \ |
| -in_file="${FLAGS_unsigned_payload}" \ |
| -signature_size="${FLAGS_signature_size}" \ |
| -out_hash_file="${FLAGS_payload_hash_file}" |
| fi |
| echo "Done generating hash." |
| } |
| |
| validate_sign() { |
| [[ -n "${FLAGS_signature_size}" ]] || |
| die "Error: you must specify signature size with --signature_size SIZES" |
| |
| [[ -n "${FLAGS_unsigned_payload}" ]] || |
| die "Error: you must specify the input unsigned payload with \ |
| --unsigned_payload FILENAME" |
| |
| [[ -n "${FLAGS_payload}" ]] || |
| die "Error: you must specify the output signed payload with \ |
| --payload FILENAME" |
| |
| [[ -n "${FLAGS_payload_signature_file}" ]] || |
| die "Error: you must specify the payload signature file with \ |
| --payload_signature_file SIGNATURES" |
| |
| [[ -n "${FLAGS_metadata_signature_file}" ]] || |
| die "Error: you must specify the metadata signature file with \ |
| --metadata_signature_file SIGNATURES" |
| } |
| |
| cmd_sign() { |
| "${GENERATOR}" \ |
| -in_file="${FLAGS_unsigned_payload}" \ |
| -signature_size="${FLAGS_signature_size}" \ |
| -signature_file="${FLAGS_payload_signature_file}" \ |
| -metadata_signature_file="${FLAGS_metadata_signature_file}" \ |
| -out_file="${FLAGS_payload}" |
| echo "Done signing payload." |
| } |
| |
| # TODO: Extract the input zip files once the format is finalized |
| |
| # Sanity check that the real generator exists: |
| GENERATOR="$(which delta_generator)" |
| [[ -x "${GENERATOR}" ]] || die "can't find delta_generator" |
| |
| case "$COMMAND" in |
| generate) validate_generate |
| cmd_generate |
| ;; |
| hash) validate_hash |
| cmd_hash |
| ;; |
| sign) validate_sign |
| cmd_sign |
| ;; |
| esac |