brillo_update_payload revision 1352093b7e5f1134ebfa99ad1991753ff4f3f33a
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#
29#  Generate command arguments:
30#  --payload             generated unsigned payload output file
31#  --source_image        if defined, generate a delta payload from the specified
32#                        image to the target_image
33#  --target_image        the target image that should be sent to clients
34#  --metadata_size_file  if defined, generate a file containing the size of the
35#                        payload metadata in bytes to the specified file
36#
37#  Hash command arguments:
38#  --unsigned_payload    the input unsigned payload to generate the hash from
39#  --signature_size      signature sizes in bytes in the following format:
40#                        "size1:size2[:...]"
41#  --payload_hash_file   if defined, generate a payload hash and output to the
42#                        specified file
43#  --metadata_hash_file  if defined, generate a metadata hash and output to the
44#                        specified file
45#
46#  Sign command arguments:
47#  --unsigned_payload        the input unsigned payload to insert the signatures
48#  --payload                 the output signed payload
49#  --signature_size          signature sizes in bytes in the following format:
50#                            "size1:size2[:...]"
51#  --payload_signature_file  the payload signature files in the following
52#                            format:
53#                            "payload_signature1:payload_signature2[:...]"
54#  --metadata_signature_file the metadata signature files in the following
55#                            format:
56#                            "metadata_signature1:metadata_signature2[:...]"
57#  --metadata_size_file      if defined, generate a file containing the size of
58#                            the signed payload metadata in bytes to the
59#                            specified file
60#  Note that the number of signature sizes and payload signatures have to match.
61#
62#  Properties command arguments:
63#  --payload                 the input signed or unsigned payload
64#  --properties_file         the output path where to write the properties, or
65#                            '-' for stdout.
66#  Verify command arguments:
67#  --payload             payload input file
68#  --source_image        verify payload to the specified source image.
69#  --target_image        the target image to verify upon.
70
71
72# Exit codes:
73EX_UNSUPPORTED_DELTA=100
74
75warn() {
76  echo "brillo_update_payload: warning: $*" >&2
77}
78
79die() {
80  echo "brillo_update_payload: error: $*" >&2
81  exit 1
82}
83
84# Loads shflags. We first look at the default install location; then look for
85# crosutils (chroot); finally check our own directory (au-generator zipfile).
86load_shflags() {
87  local my_dir="$(dirname "$(readlink -f "$0")")"
88  local path
89  for path in /usr/share/misc {/usr/lib/crosutils,"${my_dir}"}/lib/shflags; do
90    if [[ -r "${path}/shflags" ]]; then
91      . "${path}/shflags" || die "Could not load ${path}/shflags."
92      return
93    fi
94  done
95  die "Could not find shflags."
96}
97
98load_shflags
99
100HELP_GENERATE="generate: Generate an unsigned update payload."
101HELP_HASH="hash: Generate the hashes of the unsigned payload and metadata used \
102for signing."
103HELP_SIGN="sign: Insert the signatures into the unsigned payload."
104HELP_PROPERTIES="properties: Extract payload properties to a file."
105HELP_VERIFY="verify: Verify a (signed) update payload."
106
107usage() {
108  echo "Supported commands:"
109  echo
110  echo "${HELP_GENERATE}"
111  echo "${HELP_HASH}"
112  echo "${HELP_SIGN}"
113  echo "${HELP_PROPERTIES}"
114  echo "${HELP_VERIFY}"
115  echo
116  echo "Use: \"$0 <command> --help\" for more options."
117}
118
119# Check that a command is specified.
120if [[ $# -lt 1 ]]; then
121  echo "Please specify a command [generate|hash|sign|properties]"
122  exit 1
123fi
124
125# Parse command.
126COMMAND="${1:-}"
127shift
128
129case "${COMMAND}" in
130  generate)
131    FLAGS_HELP="${HELP_GENERATE}"
132    ;;
133
134  hash)
135    FLAGS_HELP="${HELP_HASH}"
136    ;;
137
138  sign)
139    FLAGS_HELP="${HELP_SIGN}"
140    ;;
141
142  properties)
143    FLAGS_HELP="${HELP_PROPERTIES}"
144    ;;
145
146  verify)
147    FLAGS_HELP="${HELP_VERIFY}"
148    ;;
149
150  *)
151    echo "Unrecognized command: \"${COMMAND}\"" >&2
152    usage >&2
153    exit 1
154    ;;
155esac
156
157# Flags
158FLAGS_HELP="Usage: $0 ${COMMAND} [flags]
159${FLAGS_HELP}"
160
161if [[ "${COMMAND}" == "generate" ]]; then
162  DEFINE_string payload "" \
163    "Path to output the generated unsigned payload file."
164  DEFINE_string target_image "" \
165    "Path to the target image that should be sent to clients."
166  DEFINE_string source_image "" \
167    "Optional: Path to a source image. If specified, this makes a delta update."
168  DEFINE_string metadata_size_file "" \
169    "Optional: Path to output metadata size."
170fi
171if [[ "${COMMAND}" == "hash" || "${COMMAND}" == "sign" ]]; then
172  DEFINE_string unsigned_payload "" "Path to the input unsigned payload."
173  DEFINE_string signature_size "" \
174    "Signature sizes in bytes in the following format: size1:size2[:...]"
175fi
176if [[ "${COMMAND}" == "hash" ]]; then
177  DEFINE_string metadata_hash_file "" \
178    "Optional: Path to output metadata hash file."
179  DEFINE_string payload_hash_file "" \
180    "Optional: Path to output payload hash file."
181fi
182if [[ "${COMMAND}" == "sign" ]]; then
183  DEFINE_string payload "" \
184    "Path to output the generated unsigned payload file."
185  DEFINE_string metadata_signature_file "" \
186    "The metatada signatures in the following format: \
187metadata_signature1:metadata_signature2[:...]"
188  DEFINE_string payload_signature_file "" \
189    "The payload signatures in the following format: \
190payload_signature1:payload_signature2[:...]"
191  DEFINE_string metadata_size_file "" \
192    "Optional: Path to output metadata size."
193fi
194if [[ "${COMMAND}" == "properties" ]]; then
195  DEFINE_string payload "" \
196    "Path to the input signed or unsigned payload file."
197  DEFINE_string properties_file "-" \
198    "Path to output the extracted property files. If '-' is passed stdout will \
199be used."
200fi
201if [[ "${COMMAND}" == "verify" ]]; then
202  DEFINE_string payload "" \
203    "Path to the input payload file."
204  DEFINE_string target_image "" \
205    "Path to the target image to verify upon."
206  DEFINE_string source_image "" \
207    "Optional: Path to a source image. If specified, the delta update is \
208applied to this."
209fi
210
211DEFINE_string work_dir "${TMPDIR:-/tmp}" "Where to dump temporary files."
212
213# Parse command line flag arguments
214FLAGS "$@" || exit 1
215eval set -- "${FLAGS_ARGV}"
216set -e
217
218# Override the TMPDIR with the passed work_dir flags, which anyway defaults to
219# ${TMPDIR}.
220TMPDIR="${FLAGS_work_dir}"
221export TMPDIR
222
223# Associative arrays from partition name to file in the source and target
224# images. The size of the updated area must be the size of the file.
225declare -A SRC_PARTITIONS
226declare -A DST_PARTITIONS
227
228# Associative arrays for the .map files associated with each src/dst partition
229# file in SRC_PARTITIONS and DST_PARTITIONS.
230declare -A SRC_PARTITIONS_MAP
231declare -A DST_PARTITIONS_MAP
232
233# List of partition names in order.
234declare -a PARTITIONS_ORDER
235
236# A list of temporary files to remove during cleanup.
237CLEANUP_FILES=()
238
239# Global options to force the version of the payload.
240FORCE_MAJOR_VERSION=""
241FORCE_MINOR_VERSION=""
242
243# Path to the postinstall config file in target image if exists.
244POSTINSTALL_CONFIG_FILE=""
245
246# read_option_int <file.txt> <option_key> [default_value]
247#
248# Reads the unsigned integer value associated with |option_key| in a key=value
249# file |file.txt|. Prints the read value if found and valid, otherwise prints
250# the |default_value|.
251read_option_uint() {
252  local file_txt="$1"
253  local option_key="$2"
254  local default_value="${3:-}"
255  local value
256  if value=$(look "${option_key}=" "${file_txt}" | tail -n 1); then
257    if value=$(echo "${value}" | cut -f 2- -d "=" | grep -E "^[0-9]+$"); then
258      echo "${value}"
259      return
260    fi
261  fi
262  echo "${default_value}"
263}
264
265# truncate_file <file_path> <file_size>
266#
267# Truncate the given |file_path| to |file_size| using perl.
268# The truncate binary might not be available.
269truncate_file() {
270  local file_path="$1"
271  local file_size="$2"
272  perl -e "open(FILE, \"+<\", \$ARGV[0]); \
273           truncate(FILE, ${file_size}); \
274           close(FILE);" "${file_path}"
275}
276
277# Create a temporary file in the work_dir with an optional pattern name.
278# Prints the name of the newly created file.
279create_tempfile() {
280  local pattern="${1:-tempfile.XXXXXX}"
281  mktemp --tmpdir="${FLAGS_work_dir}" "${pattern}"
282}
283
284cleanup() {
285  local err=""
286  rm -f "${CLEANUP_FILES[@]}" || err=1
287
288  # If we are cleaning up after an error, or if we got an error during
289  # cleanup (even if we eventually succeeded) return a non-zero exit
290  # code. This triggers additional logging in most environments that call
291  # this script.
292  if [[ -n "${err}" ]]; then
293    die "Cleanup encountered an error."
294  fi
295}
296
297cleanup_on_error() {
298  trap - INT TERM ERR EXIT
299  cleanup
300  die "Cleanup success after an error."
301}
302
303cleanup_on_exit() {
304  trap - INT TERM ERR EXIT
305  cleanup
306}
307
308trap cleanup_on_error INT TERM ERR
309trap cleanup_on_exit EXIT
310
311
312# extract_image <image> <partitions_array> [partitions_order]
313#
314# Detect the format of the |image| file and extract its updatable partitions
315# into new temporary files. Add the list of partition names and its files to the
316# associative array passed in |partitions_array|. If |partitions_order| is
317# passed, set it to list of partition names in order.
318extract_image() {
319  local image="$1"
320
321  # Brillo images are zip files. We detect the 4-byte magic header of the zip
322  # file.
323  local magic=$(head --bytes=4 "${image}" | hexdump -e '1/1 "%.2x"')
324  if [[ "${magic}" == "504b0304" ]]; then
325    echo "Detected .zip file, extracting Brillo image."
326    extract_image_brillo "$@"
327    return
328  fi
329
330  # Chrome OS images are GPT partitioned disks. We should have the cgpt binary
331  # bundled here and we will use it to extract the partitions, so the GPT
332  # headers must be valid.
333  if cgpt show -q -n "${image}" >/dev/null; then
334    echo "Detected GPT image, extracting Chrome OS image."
335    extract_image_cros "$@"
336    return
337  fi
338
339  die "Couldn't detect the image format of ${image}"
340}
341
342# extract_image_cros <image.bin> <partitions_array> [partitions_order]
343#
344# Extract Chromium OS recovery images into new temporary files.
345extract_image_cros() {
346  local image="$1"
347  local partitions_array="$2"
348  local partitions_order="${3:-}"
349
350  local kernel root
351  kernel=$(create_tempfile "kernel.bin.XXXXXX")
352  CLEANUP_FILES+=("${kernel}")
353  root=$(create_tempfile "root.bin.XXXXXX")
354  CLEANUP_FILES+=("${root}")
355
356  cros_generate_update_payload --extract \
357    --image "${image}" \
358    --kern_path "${kernel}" --root_path "${root}" \
359    --work_dir "${FLAGS_work_dir}" --outside_chroot
360
361  # Chrome OS uses major_version 1 payloads for all versions, even if the
362  # updater supports a newer major version.
363  FORCE_MAJOR_VERSION="1"
364
365  # When generating legacy Chrome OS images, we need to use "boot" and "system"
366  # for the partition names to be compatible with updating Brillo devices with
367  # Chrome OS images.
368  eval ${partitions_array}[boot]=\""${kernel}"\"
369  eval ${partitions_array}[system]=\""${root}"\"
370
371  if [[ -n "${partitions_order}" ]]; then
372    eval "${partitions_order}=( \"system\" \"boot\" )"
373  fi
374
375  local part varname
376  for part in boot system; do
377    varname="${partitions_array}[${part}]"
378    printf "md5sum of %s: " "${varname}"
379    md5sum "${!varname}"
380  done
381}
382
383# extract_image_brillo <target_files.zip> <partitions_array> [partitions_order]
384#
385# Extract the A/B updated partitions from a Brillo target_files zip file into
386# new temporary files.
387extract_image_brillo() {
388  local image="$1"
389  local partitions_array="$2"
390  local partitions_order="${3:-}"
391
392  local partitions=( "boot" "system" )
393  local ab_partitions_list
394  ab_partitions_list=$(create_tempfile "ab_partitions_list.XXXXXX")
395  CLEANUP_FILES+=("${ab_partitions_list}")
396  if unzip -p "${image}" "META/ab_partitions.txt" >"${ab_partitions_list}"; then
397    if grep -v -E '^[a-zA-Z0-9_-]*$' "${ab_partitions_list}" >&2; then
398      die "Invalid partition names found in the partition list."
399    fi
400    partitions=($(cat "${ab_partitions_list}"))
401    if [[ ${#partitions[@]} -eq 0 ]]; then
402      die "The list of partitions is empty. Can't generate a payload."
403    fi
404  else
405    warn "No ab_partitions.txt found. Using default."
406  fi
407  echo "List of A/B partitions: ${partitions[@]}"
408
409  if [[ -n "${partitions_order}" ]]; then
410    eval "${partitions_order}=(${partitions[@]})"
411  fi
412
413  # All Brillo updaters support major version 2.
414  FORCE_MAJOR_VERSION="2"
415
416  if [[ "${partitions_array}" == "SRC_PARTITIONS" ]]; then
417    # Source image
418    local ue_config=$(create_tempfile "ue_config.XXXXXX")
419    CLEANUP_FILES+=("${ue_config}")
420    if ! unzip -p "${image}" "META/update_engine_config.txt" \
421        >"${ue_config}"; then
422      warn "No update_engine_config.txt found. Assuming pre-release image, \
423using payload minor version 2"
424    fi
425    # For delta payloads, we use the major and minor version supported by the
426    # old updater.
427    FORCE_MINOR_VERSION=$(read_option_uint "${ue_config}" \
428      "PAYLOAD_MINOR_VERSION" 2)
429    FORCE_MAJOR_VERSION=$(read_option_uint "${ue_config}" \
430      "PAYLOAD_MAJOR_VERSION" 2)
431
432    # Brillo support for deltas started with minor version 3.
433    if [[ "${FORCE_MINOR_VERSION}" -le 2 ]]; then
434      warn "No delta support from minor version ${FORCE_MINOR_VERSION}. \
435Disabling deltas for this source version."
436      exit ${EX_UNSUPPORTED_DELTA}
437    fi
438  else
439    # Target image
440    local postinstall_config=$(create_tempfile "postinstall_config.XXXXXX")
441    CLEANUP_FILES+=("${postinstall_config}")
442    if unzip -p "${image}" "META/postinstall_config.txt" \
443        >"${postinstall_config}"; then
444      POSTINSTALL_CONFIG_FILE="${postinstall_config}"
445    fi
446  fi
447
448  local part part_file temp_raw filesize
449  for part in "${partitions[@]}"; do
450    part_file=$(create_tempfile "${part}.img.XXXXXX")
451    CLEANUP_FILES+=("${part_file}")
452    unzip -p "${image}" "IMAGES/${part}.img" >"${part_file}"
453
454    # If the partition is stored as an Android sparse image file, we need to
455    # convert them to a raw image for the update.
456    local magic=$(head --bytes=4 "${part_file}" | hexdump -e '1/1 "%.2x"')
457    if [[ "${magic}" == "3aff26ed" ]]; then
458      temp_raw=$(create_tempfile "${part}.raw.XXXXXX")
459      CLEANUP_FILES+=("${temp_raw}")
460      echo "Converting Android sparse image ${part}.img to RAW."
461      simg2img "${part_file}" "${temp_raw}"
462      # At this point, we can drop the contents of the old part_file file, but
463      # we can't delete the file because it will be deleted in cleanup.
464      true >"${part_file}"
465      part_file="${temp_raw}"
466    fi
467
468    # Extract the .map file (if one is available).
469    part_map_file=$(create_tempfile "${part}.map.XXXXXX")
470    CLEANUP_FILES+=("${part_map_file}")
471    unzip -p "${image}" "IMAGES/${part}.map" >"${part_map_file}" || \
472      part_map_file=""
473
474    # delta_generator only supports images multiple of 4 KiB. For target images
475    # we pad the data with zeros if needed, but for source images we truncate
476    # down the data since the last block of the old image could be padded on
477    # disk with unknown data.
478    filesize=$(stat -c%s "${part_file}")
479    if [[ $(( filesize % 4096 )) -ne 0 ]]; then
480      if [[ "${partitions_array}" == "SRC_PARTITIONS" ]]; then
481        echo "Rounding DOWN partition ${part}.img to a multiple of 4 KiB."
482        : $(( filesize = filesize & -4096 ))
483        if [[ ${filesize} == 0 ]]; then
484          echo "Source partition ${part}.img is empty after rounding down," \
485            "skipping."
486          continue
487        fi
488      else
489        echo "Rounding UP partition ${part}.img to a multiple of 4 KiB."
490        : $(( filesize = (filesize + 4095) & -4096 ))
491      fi
492      truncate_file "${part_file}" "${filesize}"
493    fi
494
495    eval "${partitions_array}[\"${part}\"]=\"${part_file}\""
496    eval "${partitions_array}_MAP[\"${part}\"]=\"${part_map_file}\""
497    echo "Extracted ${partitions_array}[${part}]: ${filesize} bytes"
498  done
499}
500
501validate_generate() {
502  [[ -n "${FLAGS_payload}" ]] ||
503    die "You must specify an output filename with --payload FILENAME"
504
505  [[ -n "${FLAGS_target_image}" ]] ||
506    die "You must specify a target image with --target_image FILENAME"
507}
508
509cmd_generate() {
510  local payload_type="delta"
511  if [[ -z "${FLAGS_source_image}" ]]; then
512    payload_type="full"
513  fi
514
515  echo "Extracting images for ${payload_type} update."
516
517  extract_image "${FLAGS_target_image}" DST_PARTITIONS PARTITIONS_ORDER
518  if [[ "${payload_type}" == "delta" ]]; then
519    extract_image "${FLAGS_source_image}" SRC_PARTITIONS
520  fi
521
522  echo "Generating ${payload_type} update."
523  # Common payload args:
524  GENERATOR_ARGS=( -out_file="${FLAGS_payload}" )
525
526  local part old_partitions="" new_partitions="" partition_names=""
527  local old_mapfiles="" new_mapfiles=""
528  for part in "${PARTITIONS_ORDER[@]}"; do
529    if [[ -n "${partition_names}" ]]; then
530      partition_names+=":"
531      new_partitions+=":"
532      old_partitions+=":"
533      new_mapfiles+=":"
534      old_mapfiles+=":"
535    fi
536    partition_names+="${part}"
537    new_partitions+="${DST_PARTITIONS[${part}]}"
538    old_partitions+="${SRC_PARTITIONS[${part}]:-}"
539    new_mapfiles+="${DST_PARTITIONS_MAP[${part}]:-}"
540    old_mapfiles+="${SRC_PARTITIONS_MAP[${part}]:-}"
541  done
542
543  # Target image args:
544  GENERATOR_ARGS+=(
545    -partition_names="${partition_names}"
546    -new_partitions="${new_partitions}"
547    -new_mapfiles="${new_mapfiles}"
548  )
549
550  if [[ "${payload_type}" == "delta" ]]; then
551    # Source image args:
552    GENERATOR_ARGS+=(
553      -old_partitions="${old_partitions}"
554      -old_mapfiles="${old_mapfiles}"
555    )
556    if [[ -n "${FORCE_MINOR_VERSION}" ]]; then
557      GENERATOR_ARGS+=( --minor_version="${FORCE_MINOR_VERSION}" )
558    fi
559  fi
560
561  if [[ -n "${FORCE_MAJOR_VERSION}" ]]; then
562    GENERATOR_ARGS+=( --major_version="${FORCE_MAJOR_VERSION}" )
563  fi
564
565  if [[ -n "${FLAGS_metadata_size_file}" ]]; then
566    GENERATOR_ARGS+=( --out_metadata_size_file="${FLAGS_metadata_size_file}" )
567  fi
568
569  if [[ -n "${POSTINSTALL_CONFIG_FILE}" ]]; then
570    GENERATOR_ARGS+=(
571      --new_postinstall_config_file="${POSTINSTALL_CONFIG_FILE}"
572    )
573  fi
574
575  echo "Running delta_generator with args: ${GENERATOR_ARGS[@]}"
576  "${GENERATOR}" "${GENERATOR_ARGS[@]}"
577
578  echo "Done generating ${payload_type} update."
579}
580
581validate_hash() {
582  [[ -n "${FLAGS_signature_size}" ]] ||
583    die "You must specify signature size with --signature_size SIZES"
584
585  [[ -n "${FLAGS_unsigned_payload}" ]] ||
586    die "You must specify the input unsigned payload with \
587--unsigned_payload FILENAME"
588
589  [[ -n "${FLAGS_payload_hash_file}" ]] ||
590    die "You must specify --payload_hash_file FILENAME"
591
592  [[ -n "${FLAGS_metadata_hash_file}" ]] ||
593    die "You must specify --metadata_hash_file FILENAME"
594}
595
596cmd_hash() {
597  "${GENERATOR}" \
598      -in_file="${FLAGS_unsigned_payload}" \
599      -signature_size="${FLAGS_signature_size}" \
600      -out_hash_file="${FLAGS_payload_hash_file}" \
601      -out_metadata_hash_file="${FLAGS_metadata_hash_file}"
602
603  echo "Done generating hash."
604}
605
606validate_sign() {
607  [[ -n "${FLAGS_signature_size}" ]] ||
608    die "You must specify signature size with --signature_size SIZES"
609
610  [[ -n "${FLAGS_unsigned_payload}" ]] ||
611    die "You must specify the input unsigned payload with \
612--unsigned_payload FILENAME"
613
614  [[ -n "${FLAGS_payload}" ]] ||
615    die "You must specify the output signed payload with --payload FILENAME"
616
617  [[ -n "${FLAGS_payload_signature_file}" ]] ||
618    die "You must specify the payload signature file with \
619--payload_signature_file SIGNATURES"
620
621  [[ -n "${FLAGS_metadata_signature_file}" ]] ||
622    die "You must specify the metadata signature file with \
623--metadata_signature_file SIGNATURES"
624}
625
626cmd_sign() {
627  GENERATOR_ARGS=(
628    -in_file="${FLAGS_unsigned_payload}"
629    -signature_size="${FLAGS_signature_size}"
630    -signature_file="${FLAGS_payload_signature_file}"
631    -metadata_signature_file="${FLAGS_metadata_signature_file}"
632    -out_file="${FLAGS_payload}"
633  )
634
635  if [[ -n "${FLAGS_metadata_size_file}" ]]; then
636    GENERATOR_ARGS+=( --out_metadata_size_file="${FLAGS_metadata_size_file}" )
637  fi
638
639  "${GENERATOR}" "${GENERATOR_ARGS[@]}"
640  echo "Done signing payload."
641}
642
643validate_properties() {
644  [[ -n "${FLAGS_payload}" ]] ||
645    die "You must specify the payload file with --payload FILENAME"
646
647  [[ -n "${FLAGS_properties_file}" ]] ||
648    die "You must specify a non empty --properties_file FILENAME"
649}
650
651cmd_properties() {
652  "${GENERATOR}" \
653      -in_file="${FLAGS_payload}" \
654      -properties_file="${FLAGS_properties_file}"
655}
656
657validate_verify() {
658  [[ -n "${FLAGS_payload}" ]] ||
659    die "Error: you must specify an input filename with --payload FILENAME"
660
661  [[ -n "${FLAGS_target_image}" ]] ||
662    die "Error: you must specify a target image with --target_image FILENAME"
663}
664
665cmd_verify() {
666  local payload_type="delta"
667  if [[ -z "${FLAGS_source_image}" ]]; then
668    payload_type="full"
669  fi
670
671  echo "Extracting images for ${payload_type} update."
672
673  if [[ "${payload_type}" == "delta" ]]; then
674    extract_image "${FLAGS_source_image}" SRC_PARTITIONS
675  fi
676  extract_image "${FLAGS_target_image}" DST_PARTITIONS PARTITIONS_ORDER
677
678  declare -A TMP_PARTITIONS
679  for part in "${PARTITIONS_ORDER[@]}"; do
680    local tmp_part=$(create_tempfile "tmp_part.bin.XXXXXX")
681    echo "Creating temporary target partition ${tmp_part} for ${part}"
682    CLEANUP_FILES+=("${tmp_part}")
683    TMP_PARTITIONS[${part}]=${tmp_part}
684    local FILESIZE=$(stat -c%s "${DST_PARTITIONS[${part}]}")
685    echo "Truncating ${TMP_PARTITIONS[${part}]} to ${FILESIZE}"
686    truncate_file "${TMP_PARTITIONS[${part}]}" "${FILESIZE}"
687  done
688
689  echo "Verifying ${payload_type} update."
690  # Common payload args:
691  GENERATOR_ARGS=( -in_file="${FLAGS_payload}" )
692
693  local part old_partitions="" new_partitions="" partition_names=""
694  for part in "${PARTITIONS_ORDER[@]}"; do
695    if [[ -n "${partition_names}" ]]; then
696      partition_names+=":"
697      new_partitions+=":"
698      old_partitions+=":"
699    fi
700    partition_names+="${part}"
701    new_partitions+="${TMP_PARTITIONS[${part}]}"
702    old_partitions+="${SRC_PARTITIONS[${part}]:-}"
703  done
704
705  # Target image args:
706  GENERATOR_ARGS+=(
707    -partition_names="${partition_names}"
708    -new_partitions="${new_partitions}"
709  )
710
711  if [[ "${payload_type}" == "delta" ]]; then
712    # Source image args:
713    GENERATOR_ARGS+=(
714      -old_partitions="${old_partitions}"
715    )
716  fi
717
718  echo "Running delta_generator to verify ${payload_type} payload with args: \
719${GENERATOR_ARGS[@]}"
720  "${GENERATOR}" "${GENERATOR_ARGS[@]}"
721
722  if [[ $? -eq 0 ]]; then
723    echo "Done applying ${payload_type} update."
724    echo "Checking the newly generated partitions against the target partitions"
725    for part in "${PARTITIONS_ORDER[@]}"; do
726      cmp "${TMP_PARTITIONS[${part}]}" "${DST_PARTITIONS[${part}]}"
727      local not_str=""
728      if [[ $? -ne 0 ]]; then
729        not_str="in"
730      fi
731      echo "The new partition (${part}) is ${not_str}valid."
732    done
733  else
734    echo "Failed to apply ${payload_type} update."
735  fi
736}
737
738# Sanity check that the real generator exists:
739GENERATOR="$(which delta_generator || true)"
740[[ -x "${GENERATOR}" ]] || die "can't find delta_generator"
741
742case "$COMMAND" in
743  generate) validate_generate
744            cmd_generate
745            ;;
746  hash) validate_hash
747        cmd_hash
748        ;;
749  sign) validate_sign
750        cmd_sign
751        ;;
752  properties) validate_properties
753              cmd_properties
754              ;;
755  verify) validate_verify
756          cmd_verify
757          ;;
758esac
759