brillo_update_payload revision c97df43fdc7c4fcfe06a76274f43211e6aa9255c
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
124c97df43fdc7c4fcfe06a76274f43211e6aa9255cAlex Deymo# read_option_int <file.txt> <option_key> [default_value]
125c97df43fdc7c4fcfe06a76274f43211e6aa9255cAlex Deymo#
126c97df43fdc7c4fcfe06a76274f43211e6aa9255cAlex Deymo# Reads the unsigned integer value associated with |option_key| in a key=value
127c97df43fdc7c4fcfe06a76274f43211e6aa9255cAlex Deymo# file |file.txt|. Prints the read value if found and valid, otherwise prints
128c97df43fdc7c4fcfe06a76274f43211e6aa9255cAlex Deymo# the |default_value|.
129c97df43fdc7c4fcfe06a76274f43211e6aa9255cAlex Deymoread_option_uint() {
130c97df43fdc7c4fcfe06a76274f43211e6aa9255cAlex Deymo  local file_txt="$1"
131c97df43fdc7c4fcfe06a76274f43211e6aa9255cAlex Deymo  local option_key="$2"
132c97df43fdc7c4fcfe06a76274f43211e6aa9255cAlex Deymo  local default_value="${3:-}"
133c97df43fdc7c4fcfe06a76274f43211e6aa9255cAlex Deymo  local value
134c97df43fdc7c4fcfe06a76274f43211e6aa9255cAlex Deymo  if value=$(look "${option_key}=" "${file_txt}" | tail -n 1); then
135c97df43fdc7c4fcfe06a76274f43211e6aa9255cAlex Deymo    if value=$(echo "${value}" | cut -f 2- -d "=" | grep -E "^[0-9]+$"); then
136c97df43fdc7c4fcfe06a76274f43211e6aa9255cAlex Deymo      echo "${value}"
137c97df43fdc7c4fcfe06a76274f43211e6aa9255cAlex Deymo      return
138c97df43fdc7c4fcfe06a76274f43211e6aa9255cAlex Deymo    fi
139c97df43fdc7c4fcfe06a76274f43211e6aa9255cAlex Deymo  fi
140c97df43fdc7c4fcfe06a76274f43211e6aa9255cAlex Deymo  echo "${default_value}"
141c97df43fdc7c4fcfe06a76274f43211e6aa9255cAlex Deymo}
142c97df43fdc7c4fcfe06a76274f43211e6aa9255cAlex Deymo
14389ff9e3221c358977f9c3124930ee6b559853740Alex Deymo# Create a temporary file in the work_dir with an optional pattern name.
14489ff9e3221c358977f9c3124930ee6b559853740Alex Deymo# Prints the name of the newly created file.
14589ff9e3221c358977f9c3124930ee6b559853740Alex Deymocreate_tempfile() {
14689ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  local pattern="${1:-tempfile.XXXXXX}"
14789ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  mktemp --tmpdir="${FLAGS_work_dir}" "${pattern}"
14889ff9e3221c358977f9c3124930ee6b559853740Alex Deymo}
149be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
150be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusumacleanup() {
151be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  local err=""
15289ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  rm -f "${CLEANUP_FILES[@]}" || err=1
153be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
154be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  # If we are cleaning up after an error, or if we got an error during
155be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  # cleanup (even if we eventually succeeded) return a non-zero exit
156be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  # code. This triggers additional logging in most environments that call
157be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  # this script.
158be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  if [[ -n "${err}" ]]; then
159be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    die "Cleanup encountered an error."
160be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  fi
161be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma}
162be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
163be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusumacleanup_on_error() {
164be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  trap - INT TERM ERR EXIT
165be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  cleanup
166be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  die "Cleanup success after an error."
167be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma}
168be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
169be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusumacleanup_on_exit() {
170be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  trap - INT TERM ERR EXIT
171be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  cleanup
172be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma}
173be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
174be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusumatrap cleanup_on_error INT TERM ERR
175be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusumatrap cleanup_on_exit EXIT
176be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
17748b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo
17848b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo# extract_image <image> <partitions_array>
17948b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo#
18048b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo# Detect the format of the |image| file and extract its updatable partitions
18148b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo# into new temporary files. Add the list of partition names and its files to the
18248b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo# associative array passed in |partitions_array|.
18348b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymoextract_image() {
18448b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  local image="$1"
18548b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo
18648b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  # Brillo images are zip files. We detect the 4-byte magic header of the zip
18748b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  # file.
18848b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  local magic=$(head --bytes=4 "${image}" | hexdump -e '1/1 "%.2x"')
18948b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  if [[ "${magic}" == "504b0304" ]]; then
19048b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    echo "Detected .zip file, extracting Brillo image."
19148b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    extract_image_brillo "$@"
19248b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    return
19348b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  fi
19448b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo
19548b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  # Chrome OS images are GPT partitioned disks. We should have the cgpt binary
19648b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  # bundled here and we will use it to extract the partitions, so the GPT
19748b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  # headers must be valid.
19848b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  if cgpt show -q -n "${image}" >/dev/null; then
19948b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    echo "Detected GPT image, extracting Chrome OS image."
20048b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    extract_image_cros "$@"
20148b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    return
20248b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  fi
20348b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo
20448b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  die "Couldn't detect the image format of ${image}"
20548b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo}
20648b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo
20789ff9e3221c358977f9c3124930ee6b559853740Alex Deymo# extract_image_cros <image.bin> <partitions_array>
20889ff9e3221c358977f9c3124930ee6b559853740Alex Deymo#
20948b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo# Extract Chromium OS recovery images into new temporary files.
21089ff9e3221c358977f9c3124930ee6b559853740Alex Deymoextract_image_cros() {
21189ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  local image="$1"
21289ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  local partitions_array="$2"
21389ff9e3221c358977f9c3124930ee6b559853740Alex Deymo
21489ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  local kernel root
21589ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  kernel=$(create_tempfile "kernel.bin.XXXXXX")
21689ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  CLEANUP_FILES+=("${kernel}")
21789ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  root=$(create_tempfile "root.bin.XXXXXX")
21889ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  CLEANUP_FILES+=("${root}")
21989ff9e3221c358977f9c3124930ee6b559853740Alex Deymo
22089ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  cros_generate_update_payload --extract \
22189ff9e3221c358977f9c3124930ee6b559853740Alex Deymo    --image "${image}" \
22289ff9e3221c358977f9c3124930ee6b559853740Alex Deymo    --kern_path "${kernel}" --root_path "${root}" \
22389ff9e3221c358977f9c3124930ee6b559853740Alex Deymo    --work_dir "${FLAGS_work_dir}" --outside_chroot
22489ff9e3221c358977f9c3124930ee6b559853740Alex Deymo
22548b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  # When generating legacy Chrome OS images, we need to use "boot" and "system"
22648b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  # for the partition names to be compatible with updating Brillo devices with
22748b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  # Chrome OS images.
22848b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  eval ${partitions_array}[boot]=\""${kernel}"\"
22948b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  eval ${partitions_array}[system]=\""${root}"\"
23089ff9e3221c358977f9c3124930ee6b559853740Alex Deymo
23189ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  local part varname
23248b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  for part in boot system; do
23389ff9e3221c358977f9c3124930ee6b559853740Alex Deymo    varname="${partitions_array}[${part}]"
23489ff9e3221c358977f9c3124930ee6b559853740Alex Deymo    printf "md5sum of %s: " "${varname}"
23589ff9e3221c358977f9c3124930ee6b559853740Alex Deymo    md5sum "${!varname}"
23689ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  done
23789ff9e3221c358977f9c3124930ee6b559853740Alex Deymo}
23889ff9e3221c358977f9c3124930ee6b559853740Alex Deymo
23948b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo# extract_image_brillo <target_files.zip> <partitions_array>
24048b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo#
24148b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo# Extract the A/B updated partitions from a Brillo target_files zip file into
24248b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo# new temporary files.
24348b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymoextract_image_brillo() {
24448b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  local image="$1"
24548b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  local partitions_array="$2"
24648b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo
24748b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  # TODO(deymo): Read the list of partitions from the metadata. We should
24848b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  # sanitize the list of partition names to be in [a-zA-Z0-9-]+.
24948b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  local partitions=( "boot" "system" )
25048b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo
25148b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  if [[ "${partitions_array}" == "SRC_PARTITIONS" ]]; then
252c97df43fdc7c4fcfe06a76274f43211e6aa9255cAlex Deymo    ue_config=$(create_tempfile "ue_config.XXXXXX")
253c97df43fdc7c4fcfe06a76274f43211e6aa9255cAlex Deymo    CLEANUP_FILES+=("${ue_config}")
254c97df43fdc7c4fcfe06a76274f43211e6aa9255cAlex Deymo    if ! unzip -p "${image}" "META/update_engine_config.txt" \
255c97df43fdc7c4fcfe06a76274f43211e6aa9255cAlex Deymo        >"${ue_config}"; then
256c97df43fdc7c4fcfe06a76274f43211e6aa9255cAlex Deymo      warn "No update_engine_config.txt found. Assuming pre-release image, \
257c97df43fdc7c4fcfe06a76274f43211e6aa9255cAlex Deymousing payload minor version 2"
258c97df43fdc7c4fcfe06a76274f43211e6aa9255cAlex Deymo    fi
259c97df43fdc7c4fcfe06a76274f43211e6aa9255cAlex Deymo    FORCE_MINOR_VERSION=$(read_option_uint "${ue_config}" \
260c97df43fdc7c4fcfe06a76274f43211e6aa9255cAlex Deymo      "PAYLOAD_MINOR_VERSION" 2)
26148b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  fi
26248b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo
26348b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  local part part_file temp_raw filesize
26448b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  for part in "${partitions[@]}"; do
26548b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    part_file=$(create_tempfile "${part}.img.XXXXXX")
26648b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    CLEANUP_FILES+=("${part_file}")
26748b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    unzip -p "${image}" "IMAGES/${part}.img" >"${part_file}"
26848b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo
26948b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    # If the partition is stored as an Android sparse image file, we need to
27048b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    # convert them to a raw image for the update.
27148b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    local magic=$(head --bytes=4 "${part_file}" | hexdump -e '1/1 "%.2x"')
27248b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    if [[ "${magic}" == "3aff26ed" ]]; then
27348b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo      temp_raw=$(create_tempfile "${part}.raw.XXXXXX")
27448b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo      CLEANUP_FILES+=("${temp_raw}")
27548b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo      echo "Converting Android sparse image ${part}.img to RAW."
27648b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo      simg2img "${part_file}" "${temp_raw}"
27748b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo      # At this point, we can drop the contents of the old part_file file, but
27848b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo      # we can't delete the file because it will be deleted in cleanup.
27948b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo      true >"${part_file}"
28048b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo      part_file="${temp_raw}"
28148b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    fi
28248b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo
28348b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    # delta_generator only supports images multiple of 4 KiB, so we pad with
28448b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    # zeros if needed.
28548b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    filesize=$(stat -c%s "${part_file}")
28648b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    if [[ $(( filesize % 4096 )) -ne 0 ]]; then
28748b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo      echo "Rounding up partition ${part}.img to multiple of 4 KiB."
28848b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo      : $(( filesize = (filesize + 4095) & -4096 ))
28948b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo      truncate --size="${filesize}" "${part_file}"
29048b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    fi
29148b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo
29248b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    eval "${partitions_array}[\"${part}\"]=\"${part_file}\""
29348b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    echo "Extracted ${partitions_array}[${part}]: ${filesize} bytes"
29448b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  done
29548b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo}
29648b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo
297be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusumavalidate_generate() {
298be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  [[ -n "${FLAGS_payload}" ]] ||
299be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    die "Error: you must specify an output filename with --payload FILENAME"
300be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
301be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  [[ -n "${FLAGS_target_image}" ]] ||
302be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    die "Error: you must specify a target image with --target_image FILENAME"
303be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma}
304be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
305be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusumacmd_generate() {
30689ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  local payload_type="delta"
307be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  if [[ -z "${FLAGS_source_image}" ]]; then
30889ff9e3221c358977f9c3124930ee6b559853740Alex Deymo    payload_type="full"
309be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  fi
310be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
31148b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  echo "Extracting images for ${payload_type} update."
312be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
31348b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  extract_image "${FLAGS_target_image}" DST_PARTITIONS
31489ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  if [[ "${payload_type}" == "delta" ]]; then
31548b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    extract_image "${FLAGS_source_image}" SRC_PARTITIONS
316be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  fi
317be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
31848b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  echo "Generating ${payload_type} update."
319be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  GENERATOR_ARGS=(
320be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    # Common payload args:
321be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    -out_file="${FLAGS_payload}"
322be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    # Target image args:
32389ff9e3221c358977f9c3124930ee6b559853740Alex Deymo    # TODO(deymo): Pass the list of partitions to the generator.
32448b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    -new_image="${DST_PARTITIONS[system]}"
32548b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    -new_kernel="${DST_PARTITIONS[boot]}"
326be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  )
327be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
32889ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  if [[ "${payload_type}" == "delta" ]]; then
329be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    GENERATOR_ARGS+=(
330be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma      # Source image args:
33148b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo      -old_image="${SRC_PARTITIONS[system]}"
33248b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo      -old_kernel="${SRC_PARTITIONS[boot]}"
333be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    )
33448b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    if [[ -n "${FORCE_MINOR_VERSION}" ]]; then
33548b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo      GENERATOR_ARGS+=( --minor_version="${FORCE_MINOR_VERSION}" )
33648b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    fi
33748b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  fi
33848b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo
33948b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo  if [[ -n "${FORCE_MAJOR_VERSION}" ]]; then
34048b502ab0cd56bf948602a45b990448f51e3e6b5Alex Deymo    GENERATOR_ARGS+=( --major_version="${FORCE_MAJOR_VERSION}" )
341be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  fi
342be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
343be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  echo "Running delta_generator with args: ${GENERATOR_ARGS[@]}"
344be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    "${GENERATOR}" "${GENERATOR_ARGS[@]}"
345be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
34689ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  echo "Done generating ${payload_type} update."
347be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma}
348be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
349be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusumavalidate_hash() {
350be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  [[ -n "${FLAGS_signature_size}" ]] ||
351be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    die "Error: you must specify signature size with --signature_size SIZES"
352be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
353be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  [[ -n "${FLAGS_unsigned_payload}" ]] ||
354be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    die "Error: you must specify the input unsigned payload with \
355be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma--unsigned_payload FILENAME"
356be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
357be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  [[ -n "${FLAGS_metadata_hash_file}" ]] ||
358be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  [[ -n "${FLAGS_payload_hash_file}" ]] ||
359be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    die "Error: you must specify --metadata_hash_file FILENAME \
360be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusumaor --payload_hash_file FILENAME"
361be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma}
362be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
363be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusumacmd_hash() {
364be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  if [[ -n "${FLAGS_metadata_hash_file}" ]]; then
365be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    "${GENERATOR}" \
366be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma        -in_file="${FLAGS_unsigned_payload}" \
367be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma        -signature_size="${FLAGS_signature_size}" \
368be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma        -out_metadata_hash_file="${FLAGS_metadata_hash_file}"
369be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  fi
370be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
371be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  if [[ -n "${FLAGS_payload_hash_file}" ]]; then
372be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    "${GENERATOR}" \
373be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma        -in_file="${FLAGS_unsigned_payload}" \
374be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma        -signature_size="${FLAGS_signature_size}" \
375be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma        -out_hash_file="${FLAGS_payload_hash_file}"
376be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  fi
377be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  echo "Done generating hash."
378be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma}
379be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
380be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusumavalidate_sign() {
381be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  [[ -n "${FLAGS_signature_size}" ]] ||
382be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    die "Error: you must specify signature size with --signature_size SIZES"
383be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
384be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  [[ -n "${FLAGS_unsigned_payload}" ]] ||
385be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    die "Error: you must specify the input unsigned payload with \
386be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma--unsigned_payload FILENAME"
387be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
388be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  [[ -n "${FLAGS_payload}" ]] ||
389be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    die "Error: you must specify the output signed payload with \
390be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma--payload FILENAME"
391be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
392be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  [[ -n "${FLAGS_payload_signature_file}" ]] ||
393be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma    die "Error: you must specify the payload signature file with \
394be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma--payload_signature_file SIGNATURES"
39589ff9e3221c358977f9c3124930ee6b559853740Alex Deymo
39689ff9e3221c358977f9c3124930ee6b559853740Alex Deymo  [[ -n "${FLAGS_metadata_signature_file}" ]] ||
39789ff9e3221c358977f9c3124930ee6b559853740Alex Deymo    die "Error: you must specify the metadata signature file with \
39889ff9e3221c358977f9c3124930ee6b559853740Alex Deymo--metadata_signature_file SIGNATURES"
399be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma}
400be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
401be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusumacmd_sign() {
402be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  "${GENERATOR}" \
403be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma      -in_file="${FLAGS_unsigned_payload}" \
404be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma      -signature_size="${FLAGS_signature_size}" \
405be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma      -signature_file="${FLAGS_payload_signature_file}" \
40689ff9e3221c358977f9c3124930ee6b559853740Alex Deymo      -metadata_signature_file="${FLAGS_metadata_signature_file}" \
407be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma      -out_file="${FLAGS_payload}"
408be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  echo "Done signing payload."
409be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma}
410be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
411be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma# TODO: Extract the input zip files once the format is finalized
412be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
413be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma# Sanity check that the real generator exists:
414be998f47ccb02c5f118f73e79729e9187c39e919Jason KusumaGENERATOR="$(which delta_generator)"
415be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma[[ -x "${GENERATOR}" ]] || die "can't find delta_generator"
416be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma
417be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusumacase "$COMMAND" in
418be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  generate) validate_generate
419be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma            cmd_generate
420be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma            ;;
421be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  hash) validate_hash
422be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma        cmd_hash
423be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma        ;;
424be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma  sign) validate_sign
425be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma        cmd_sign
426be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusuma        ;;
427be998f47ccb02c5f118f73e79729e9187c39e919Jason Kusumaesac
428