brillo_update_payload revision 48b502ab0cd56bf948602a45b990448f51e3e6b5
1be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma#!/bin/bash
2be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
3be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma# Copyright 2015 The Chromium OS Authors. All rights reserved.
4be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma# Use of this source code is governed by a BSD-style license that can be
5be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma# found in the LICENSE file.
6be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
7be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma# Script to generate a Brillo update for use by the update engine.
8be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma#
9be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma# usage: brillo_update_payload COMMAND [ARGS]
10be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma# The following commands are supported:
11be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma#  generate    generate an unsigned payload
12be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma#  hash        generate a payload or metadata hash
13be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma#  sign        generate a signed payload
14be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma#
15be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma#  Generate command arguments:
16be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma#  --payload       generated unsigned payload output file
17be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma#  --source_image  if defined, generate a delta payload from the specified
18be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma#                  image to the target_image
19be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma#  --target_image  the target image that should be sent to clients
20be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma#
21be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma#  Hash command arguments:
22be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma#  --unsigned_payload    the input unsigned payload to generate the hash from
23be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma#  --signature_size      signature sizes in bytes in the following format:
2489ff9e3221c358977f9c3124930ee6b559853740Alex Deymo#                        "size1:size2[:...]"
25be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma#  --payload_hash_file   if defined, generate a payload hash and output to the
26be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma#                        specified file
27be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma#  --metadata_hash_file  if defined, generate a metadata hash and output to the
28be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma#                        specified file
29be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma#
30be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma#  Sign command arguments:
3189ff9e3221c358977f9c3124930ee6b559853740Alex Deymo#  --unsigned_payload        the input unsigned payload to insert the signatures
3289ff9e3221c358977f9c3124930ee6b559853740Alex Deymo#  --payload                 the output signed payload
3389ff9e3221c358977f9c3124930ee6b559853740Alex Deymo#  --signature_size          signature sizes in bytes in the following format:
3489ff9e3221c358977f9c3124930ee6b559853740Alex Deymo#                            "size1:size2[:...]"
3589ff9e3221c358977f9c3124930ee6b559853740Alex Deymo#  --payload_signature_file  the payload signature files in the following
3689ff9e3221c358977f9c3124930ee6b559853740Alex Deymo#                            format:
3789ff9e3221c358977f9c3124930ee6b559853740Alex Deymo#                            "payload_signature1:payload_signature2[:...]"
3889ff9e3221c358977f9c3124930ee6b559853740Alex Deymo#  --metadata_signature_file the metadata signature files in the following
3989ff9e3221c358977f9c3124930ee6b559853740Alex Deymo#                            format:
4089ff9e3221c358977f9c3124930ee6b559853740Alex Deymo#                            "metadata_signature1:metadata_signature2[:...]"
41be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma#  Note that the number of signature sizes and payload signatures have to match.
42be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
43be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma# Load common CrOS utilities.  Inside the chroot this file is installed in
44be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma# /usr/lib/crosutils.  This script may also be called from a zipfile, in which
45be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma# case common.sh will be in the current directory.
46be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusumafind_common_sh() {
47be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  local thisdir="$(dirname "$(readlink -f "$0")")"
48be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  local common_paths=(/usr/lib/crosutils "${thisdir}")
49be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  local path
50be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
51be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  SCRIPT_ROOT="${common_paths[0]}"
52be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  for path in "${common_paths[@]}"; do
53be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    if [[ -r "${path}/common.sh" ]]; then
54be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma      SCRIPT_ROOT="${path}"
55be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma      break
56be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    fi
57be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  done
58be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
59be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  # We have to fake GCLIENT_ROOT in case we're running inside
60be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  # au_zip enviroment. GCLIENT_ROOT detection became fatal.
61be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  [[ "${SCRIPT_ROOT}" == "${thisdir}" ]] && export GCLIENT_ROOT="."
62be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma}
63be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
64be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusumafind_common_sh
65be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma. "${SCRIPT_ROOT}/common.sh" || exit 1
66be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
67be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma# Check that a command is specified
68be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusumaif [[ $# -lt 1 ]]; then
69be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  echo "Please specify a command [generate|hash|sign]"
70be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  exit 1
71be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusumafi
72be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
73be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma# Parse command
74be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusumacase "$1" in
75be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  generate|hash|sign)
76be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    COMMAND=$1
77be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    ;;
78be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  *)
79be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    echo "Unrecognized command:" $1
80be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    exit 1
81be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    ;;
82be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusumaesac
83be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
84be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusumashift
85be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
86be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma# Flags
87be998f47ccb02c5f118f73e79729e9187c39e919Jason KusumaDEFINE_string payload "" "Path to output the generated payload file."
8889ff9e3221c358977f9c3124930ee6b559853740Alex DeymoDEFINE_string target_image "" \
8989ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  "Path to the target image that should be sent to clients."
9089ff9e3221c358977f9c3124930ee6b559853740Alex DeymoDEFINE_string source_image "" \
9189ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  "Optional: Path to a source image. If specified, this makes\
92be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma a delta update."
93be998f47ccb02c5f118f73e79729e9187c39e919Jason KusumaDEFINE_string unsigned_payload "" "Path to the generated unsigned payload."
9489ff9e3221c358977f9c3124930ee6b559853740Alex DeymoDEFINE_string signature_size "" \
9589ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  "Signature sizes in bytes in the following format: size1:size2[:...]"
96be998f47ccb02c5f118f73e79729e9187c39e919Jason KusumaDEFINE_string payload_hash_file "" "Optional: Path to output payload hash file."
9789ff9e3221c358977f9c3124930ee6b559853740Alex DeymoDEFINE_string metadata_hash_file "" \
9889ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  "Optional: Path to output metadata hash file."
9989ff9e3221c358977f9c3124930ee6b559853740Alex DeymoDEFINE_string payload_signature_file "" \
10089ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  "The payload signatures in the following format:\
10189ff9e3221c358977f9c3124930ee6b559853740Alex Deymo payload_signature1:payload_signature2[:...]"
10289ff9e3221c358977f9c3124930ee6b559853740Alex DeymoDEFINE_string metadata_signature_file "" \
10389ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  "The metatada signatures in the following format:\
10489ff9e3221c358977f9c3124930ee6b559853740Alex Deymo metadata_signature1:metadata_signature2[:...]"
105be998f47ccb02c5f118f73e79729e9187c39e919Jason KusumaDEFINE_string work_dir "/tmp" "Where to dump temporary files."
106be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
107be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma# Parse command line flag arguments
108be998f47ccb02c5f118f73e79729e9187c39e919Jason KusumaFLAGS "$@" || exit 1
109be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusumaeval set -- "${FLAGS_ARGV}"
11089ff9e3221c358977f9c3124930ee6b559853740Alex Deymoset -e
111be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
11289ff9e3221c358977f9c3124930ee6b559853740Alex Deymo# Associative arrays from partition name to file in the source and target
11389ff9e3221c358977f9c3124930ee6b559853740Alex Deymo# images. The size of the updated area must be the size of the file.
11489ff9e3221c358977f9c3124930ee6b559853740Alex Deymodeclare -A SRC_PARTITIONS
11589ff9e3221c358977f9c3124930ee6b559853740Alex Deymodeclare -A DST_PARTITIONS
11689ff9e3221c358977f9c3124930ee6b559853740Alex Deymo
11789ff9e3221c358977f9c3124930ee6b559853740Alex Deymo# A list of temporary files to remove during cleanup.
11889ff9e3221c358977f9c3124930ee6b559853740Alex DeymoCLEANUP_FILES=()
11989ff9e3221c358977f9c3124930ee6b559853740Alex Deymo
12048b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo# Global options to force the version of the payload.
12148b502ab0cd56bf948602a45b990448f51e3e6b5Alex DeymoFORCE_MAJOR_VERSION=""
12248b502ab0cd56bf948602a45b990448f51e3e6b5Alex DeymoFORCE_MINOR_VERSION=""
12348b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo
12489ff9e3221c358977f9c3124930ee6b559853740Alex Deymo# Create a temporary file in the work_dir with an optional pattern name.
12589ff9e3221c358977f9c3124930ee6b559853740Alex Deymo# Prints the name of the newly created file.
12689ff9e3221c358977f9c3124930ee6b559853740Alex Deymocreate_tempfile() {
12789ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  local pattern="${1:-tempfile.XXXXXX}"
12889ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  mktemp --tmpdir="${FLAGS_work_dir}" "${pattern}"
12989ff9e3221c358977f9c3124930ee6b559853740Alex Deymo}
130be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
131be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusumacleanup() {
132be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  local err=""
13389ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  rm -f "${CLEANUP_FILES[@]}" || err=1
134be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
135be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  # If we are cleaning up after an error, or if we got an error during
136be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  # cleanup (even if we eventually succeeded) return a non-zero exit
137be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  # code. This triggers additional logging in most environments that call
138be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  # this script.
139be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  if [[ -n "${err}" ]]; then
140be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    die "Cleanup encountered an error."
141be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  fi
142be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma}
143be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
144be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusumacleanup_on_error() {
145be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  trap - INT TERM ERR EXIT
146be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  cleanup
147be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  die "Cleanup success after an error."
148be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma}
149be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
150be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusumacleanup_on_exit() {
151be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  trap - INT TERM ERR EXIT
152be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  cleanup
153be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma}
154be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
155be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusumatrap cleanup_on_error INT TERM ERR
156be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusumatrap cleanup_on_exit EXIT
157be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
15848b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo
15948b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo# extract_image <image> <partitions_array>
16048b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo#
16148b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo# Detect the format of the |image| file and extract its updatable partitions
16248b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo# into new temporary files. Add the list of partition names and its files to the
16348b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo# associative array passed in |partitions_array|.
16448b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymoextract_image() {
16548b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  local image="$1"
16648b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo
16748b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  # Brillo images are zip files. We detect the 4-byte magic header of the zip
16848b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  # file.
16948b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  local magic=$(head --bytes=4 "${image}" | hexdump -e '1/1 "%.2x"')
17048b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  if [[ "${magic}" == "504b0304" ]]; then
17148b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    echo "Detected .zip file, extracting Brillo image."
17248b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    extract_image_brillo "$@"
17348b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    return
17448b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  fi
17548b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo
17648b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  # Chrome OS images are GPT partitioned disks. We should have the cgpt binary
17748b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  # bundled here and we will use it to extract the partitions, so the GPT
17848b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  # headers must be valid.
17948b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  if cgpt show -q -n "${image}" >/dev/null; then
18048b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    echo "Detected GPT image, extracting Chrome OS image."
18148b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    extract_image_cros "$@"
18248b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    return
18348b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  fi
18448b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo
18548b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  die "Couldn't detect the image format of ${image}"
18648b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo}
18748b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo
18889ff9e3221c358977f9c3124930ee6b559853740Alex Deymo# extract_image_cros <image.bin> <partitions_array>
18989ff9e3221c358977f9c3124930ee6b559853740Alex Deymo#
19048b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo# Extract Chromium OS recovery images into new temporary files.
19189ff9e3221c358977f9c3124930ee6b559853740Alex Deymoextract_image_cros() {
19289ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  local image="$1"
19389ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  local partitions_array="$2"
19489ff9e3221c358977f9c3124930ee6b559853740Alex Deymo
19589ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  local kernel root
19689ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  kernel=$(create_tempfile "kernel.bin.XXXXXX")
19789ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  CLEANUP_FILES+=("${kernel}")
19889ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  root=$(create_tempfile "root.bin.XXXXXX")
19989ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  CLEANUP_FILES+=("${root}")
20089ff9e3221c358977f9c3124930ee6b559853740Alex Deymo
20189ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  cros_generate_update_payload --extract \
20289ff9e3221c358977f9c3124930ee6b559853740Alex Deymo    --image "${image}" \
20389ff9e3221c358977f9c3124930ee6b559853740Alex Deymo    --kern_path "${kernel}" --root_path "${root}" \
20489ff9e3221c358977f9c3124930ee6b559853740Alex Deymo    --work_dir "${FLAGS_work_dir}" --outside_chroot
20589ff9e3221c358977f9c3124930ee6b559853740Alex Deymo
20648b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  # When generating legacy Chrome OS images, we need to use "boot" and "system"
20748b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  # for the partition names to be compatible with updating Brillo devices with
20848b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  # Chrome OS images.
20948b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  eval ${partitions_array}[boot]=\""${kernel}"\"
21048b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  eval ${partitions_array}[system]=\""${root}"\"
21189ff9e3221c358977f9c3124930ee6b559853740Alex Deymo
21289ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  local part varname
21348b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  for part in boot system; do
21489ff9e3221c358977f9c3124930ee6b559853740Alex Deymo    varname="${partitions_array}[${part}]"
21589ff9e3221c358977f9c3124930ee6b559853740Alex Deymo    printf "md5sum of %s: " "${varname}"
21689ff9e3221c358977f9c3124930ee6b559853740Alex Deymo    md5sum "${!varname}"
21789ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  done
21889ff9e3221c358977f9c3124930ee6b559853740Alex Deymo}
21989ff9e3221c358977f9c3124930ee6b559853740Alex Deymo
22048b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo# extract_image_brillo <target_files.zip> <partitions_array>
22148b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo#
22248b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo# Extract the A/B updated partitions from a Brillo target_files zip file into
22348b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo# new temporary files.
22448b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymoextract_image_brillo() {
22548b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  local image="$1"
22648b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  local partitions_array="$2"
22748b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo
22848b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  # TODO(deymo): Read the list of partitions from the metadata. We should
22948b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  # sanitize the list of partition names to be in [a-zA-Z0-9-]+.
23048b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  local partitions=( "boot" "system" )
23148b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo
23248b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  if [[ "${partitions_array}" == "SRC_PARTITIONS" ]]; then
23348b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    # TODO(deymo): Read the supported minor version from the .zip metadata.
23448b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    FORCE_MINOR_VERSION="2"
23548b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  fi
23648b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo
23748b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  local part part_file temp_raw filesize
23848b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  for part in "${partitions[@]}"; do
23948b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    part_file=$(create_tempfile "${part}.img.XXXXXX")
24048b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    CLEANUP_FILES+=("${part_file}")
24148b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    unzip -p "${image}" "IMAGES/${part}.img" >"${part_file}"
24248b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo
24348b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    # If the partition is stored as an Android sparse image file, we need to
24448b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    # convert them to a raw image for the update.
24548b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    local magic=$(head --bytes=4 "${part_file}" | hexdump -e '1/1 "%.2x"')
24648b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    if [[ "${magic}" == "3aff26ed" ]]; then
24748b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo      temp_raw=$(create_tempfile "${part}.raw.XXXXXX")
24848b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo      CLEANUP_FILES+=("${temp_raw}")
24948b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo      echo "Converting Android sparse image ${part}.img to RAW."
25048b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo      simg2img "${part_file}" "${temp_raw}"
25148b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo      # At this point, we can drop the contents of the old part_file file, but
25248b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo      # we can't delete the file because it will be deleted in cleanup.
25348b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo      true >"${part_file}"
25448b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo      part_file="${temp_raw}"
25548b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    fi
25648b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo
25748b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    # delta_generator only supports images multiple of 4 KiB, so we pad with
25848b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    # zeros if needed.
25948b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    filesize=$(stat -c%s "${part_file}")
26048b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    if [[ $(( filesize % 4096 )) -ne 0 ]]; then
26148b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo      echo "Rounding up partition ${part}.img to multiple of 4 KiB."
26248b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo      : $(( filesize = (filesize + 4095) & -4096 ))
26348b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo      truncate --size="${filesize}" "${part_file}"
26448b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    fi
26548b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo
26648b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    eval "${partitions_array}[\"${part}\"]=\"${part_file}\""
26748b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    echo "Extracted ${partitions_array}[${part}]: ${filesize} bytes"
26848b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  done
26948b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo}
27048b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo
271be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusumavalidate_generate() {
272be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  [[ -n "${FLAGS_payload}" ]] ||
273be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    die "Error: you must specify an output filename with --payload FILENAME"
274be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
275be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  [[ -n "${FLAGS_target_image}" ]] ||
276be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    die "Error: you must specify a target image with --target_image FILENAME"
277be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma}
278be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
279be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusumacmd_generate() {
28089ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  local payload_type="delta"
281be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  if [[ -z "${FLAGS_source_image}" ]]; then
28289ff9e3221c358977f9c3124930ee6b559853740Alex Deymo    payload_type="full"
283be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  fi
284be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
28548b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  echo "Extracting images for ${payload_type} update."
286be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
28748b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  extract_image "${FLAGS_target_image}" DST_PARTITIONS
28889ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  if [[ "${payload_type}" == "delta" ]]; then
28948b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    extract_image "${FLAGS_source_image}" SRC_PARTITIONS
290be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  fi
291be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
29248b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  echo "Generating ${payload_type} update."
293be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  GENERATOR_ARGS=(
294be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    # Common payload args:
295be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    -out_file="${FLAGS_payload}"
296be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    # Target image args:
29789ff9e3221c358977f9c3124930ee6b559853740Alex Deymo    # TODO(deymo): Pass the list of partitions to the generator.
29848b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    -new_image="${DST_PARTITIONS[system]}"
29948b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    -new_kernel="${DST_PARTITIONS[boot]}"
300be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  )
301be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
30289ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  if [[ "${payload_type}" == "delta" ]]; then
303be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    GENERATOR_ARGS+=(
304be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma      # Source image args:
30548b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo      -old_image="${SRC_PARTITIONS[system]}"
30648b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo      -old_kernel="${SRC_PARTITIONS[boot]}"
307be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    )
30848b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    if [[ -n "${FORCE_MINOR_VERSION}" ]]; then
30948b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo      GENERATOR_ARGS+=( --minor_version="${FORCE_MINOR_VERSION}" )
31048b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    fi
31148b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  fi
31248b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo
31348b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  if [[ -n "${FORCE_MAJOR_VERSION}" ]]; then
31448b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    GENERATOR_ARGS+=( --major_version="${FORCE_MAJOR_VERSION}" )
315be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  fi
316be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
317be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  echo "Running delta_generator with args: ${GENERATOR_ARGS[@]}"
318be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    "${GENERATOR}" "${GENERATOR_ARGS[@]}"
319be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
32089ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  echo "Done generating ${payload_type} update."
321be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma}
322be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
323be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusumavalidate_hash() {
324be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  [[ -n "${FLAGS_signature_size}" ]] ||
325be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    die "Error: you must specify signature size with --signature_size SIZES"
326be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
327be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  [[ -n "${FLAGS_unsigned_payload}" ]] ||
328be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    die "Error: you must specify the input unsigned payload with \
329be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma--unsigned_payload FILENAME"
330be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
331be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  [[ -n "${FLAGS_metadata_hash_file}" ]] ||
332be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  [[ -n "${FLAGS_payload_hash_file}" ]] ||
333be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    die "Error: you must specify --metadata_hash_file FILENAME \
334be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusumaor --payload_hash_file FILENAME"
335be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma}
336be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
337be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusumacmd_hash() {
338be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  if [[ -n "${FLAGS_metadata_hash_file}" ]]; then
339be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    "${GENERATOR}" \
340be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma        -in_file="${FLAGS_unsigned_payload}" \
341be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma        -signature_size="${FLAGS_signature_size}" \
342be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma        -out_metadata_hash_file="${FLAGS_metadata_hash_file}"
343be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  fi
344be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
345be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  if [[ -n "${FLAGS_payload_hash_file}" ]]; then
346be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    "${GENERATOR}" \
347be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma        -in_file="${FLAGS_unsigned_payload}" \
348be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma        -signature_size="${FLAGS_signature_size}" \
349be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma        -out_hash_file="${FLAGS_payload_hash_file}"
350be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  fi
351be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  echo "Done generating hash."
352be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma}
353be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
354be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusumavalidate_sign() {
355be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  [[ -n "${FLAGS_signature_size}" ]] ||
356be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    die "Error: you must specify signature size with --signature_size SIZES"
357be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
358be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  [[ -n "${FLAGS_unsigned_payload}" ]] ||
359be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    die "Error: you must specify the input unsigned payload with \
360be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma--unsigned_payload FILENAME"
361be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
362be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  [[ -n "${FLAGS_payload}" ]] ||
363be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    die "Error: you must specify the output signed payload with \
364be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma--payload FILENAME"
365be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
366be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  [[ -n "${FLAGS_payload_signature_file}" ]] ||
367be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    die "Error: you must specify the payload signature file with \
368be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma--payload_signature_file SIGNATURES"
36989ff9e3221c358977f9c3124930ee6b559853740Alex Deymo
37089ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  [[ -n "${FLAGS_metadata_signature_file}" ]] ||
37189ff9e3221c358977f9c3124930ee6b559853740Alex Deymo    die "Error: you must specify the metadata signature file with \
37289ff9e3221c358977f9c3124930ee6b559853740Alex Deymo--metadata_signature_file SIGNATURES"
373be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma}
374be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
375be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusumacmd_sign() {
376be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  "${GENERATOR}" \
377be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma      -in_file="${FLAGS_unsigned_payload}" \
378be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma      -signature_size="${FLAGS_signature_size}" \
379be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma      -signature_file="${FLAGS_payload_signature_file}" \
38089ff9e3221c358977f9c3124930ee6b559853740Alex Deymo      -metadata_signature_file="${FLAGS_metadata_signature_file}" \
381be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma      -out_file="${FLAGS_payload}"
382be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  echo "Done signing payload."
383be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma}
384be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
385be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma# TODO: Extract the input zip files once the format is finalized
386be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
387be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma# Sanity check that the real generator exists:
388be998f47ccb02c5f118f73e79729e9187c39e919Jason KusumaGENERATOR="$(which delta_generator)"
389be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma[[ -x "${GENERATOR}" ]] || die "can't find delta_generator"
390be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
391be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusumacase "$COMMAND" in
392be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  generate) validate_generate
393be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma            cmd_generate
394be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma            ;;
395be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  hash) validate_hash
396be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma        cmd_hash
397be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma        ;;
398be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  sign) validate_sign
399be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma        cmd_sign
400be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma        ;;
401be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusumaesac
402