brillo_update_payload: Extract Android target_files.zip.
When generating payloads for Brillo, we need to extract the partitions
from the target_files.zip image. This patch detects the format of the
old/new image and extract them accordingly.
CQ-DEPEND=CL:300654
BUG=b:23599483
TEST=brillo_update_payload generate --target_image dragonboard-target_files-2238075.zip --payload full-2238075.bin
Change-Id: I6e38bad35ef9d904e12232a10947f379fcd2acd7
Reviewed-on: https://chromium-review.googlesource.com/300626
Commit-Ready: Alex Deymo <deymo@chromium.org>
Tested-by: Alex Deymo <deymo@chromium.org>
Reviewed-by: Jason Kusuma <jkusuma@chromium.org>
diff --git a/scripts/brillo_update_payload b/scripts/brillo_update_payload
index 192632c..156f659 100755
--- a/scripts/brillo_update_payload
+++ b/scripts/brillo_update_payload
@@ -117,6 +117,10 @@
# A list of temporary files to remove during cleanup.
CLEANUP_FILES=()
+# Global options to force the version of the payload.
+FORCE_MAJOR_VERSION=""
+FORCE_MINOR_VERSION=""
+
# Create a temporary file in the work_dir with an optional pattern name.
# Prints the name of the newly created file.
create_tempfile() {
@@ -151,11 +155,39 @@
trap cleanup_on_error INT TERM ERR
trap cleanup_on_exit EXIT
+
+# extract_image <image> <partitions_array>
+#
+# Detect the format of the |image| file and extract its updatable partitions
+# into new temporary files. Add the list of partition names and its files to the
+# associative array passed in |partitions_array|.
+extract_image() {
+ local image="$1"
+
+ # Brillo images are zip files. We detect the 4-byte magic header of the zip
+ # file.
+ local magic=$(head --bytes=4 "${image}" | hexdump -e '1/1 "%.2x"')
+ if [[ "${magic}" == "504b0304" ]]; then
+ echo "Detected .zip file, extracting Brillo image."
+ extract_image_brillo "$@"
+ return
+ fi
+
+ # Chrome OS images are GPT partitioned disks. We should have the cgpt binary
+ # bundled here and we will use it to extract the partitions, so the GPT
+ # headers must be valid.
+ if cgpt show -q -n "${image}" >/dev/null; then
+ echo "Detected GPT image, extracting Chrome OS image."
+ extract_image_cros "$@"
+ return
+ fi
+
+ die "Couldn't detect the image format of ${image}"
+}
+
# extract_image_cros <image.bin> <partitions_array>
#
-# Extract Chromium OS recovery images into new temporary files. Add the list
-# of partition names and its files to the associative array passed in
-# partitions_array.
+# Extract Chromium OS recovery images into new temporary files.
extract_image_cros() {
local image="$1"
local partitions_array="$2"
@@ -171,19 +203,71 @@
--kern_path "${kernel}" --root_path "${root}" \
--work_dir "${FLAGS_work_dir}" --outside_chroot
- # When generating legacy Chrome OS images, we need to use "kernel" and "root"
- # for the partition names.
- eval ${partitions_array}[kernel]=\""${kernel}"\"
- eval ${partitions_array}[root]=\""${root}"\"
+ # When generating legacy Chrome OS images, we need to use "boot" and "system"
+ # for the partition names to be compatible with updating Brillo devices with
+ # Chrome OS images.
+ eval ${partitions_array}[boot]=\""${kernel}"\"
+ eval ${partitions_array}[system]=\""${root}"\"
local part varname
- for part in root kernel; do
+ for part in boot system; do
varname="${partitions_array}[${part}]"
printf "md5sum of %s: " "${varname}"
md5sum "${!varname}"
done
}
+# extract_image_brillo <target_files.zip> <partitions_array>
+#
+# Extract the A/B updated partitions from a Brillo target_files zip file into
+# new temporary files.
+extract_image_brillo() {
+ local image="$1"
+ local partitions_array="$2"
+
+ # TODO(deymo): Read the list of partitions from the metadata. We should
+ # sanitize the list of partition names to be in [a-zA-Z0-9-]+.
+ local partitions=( "boot" "system" )
+
+ if [[ "${partitions_array}" == "SRC_PARTITIONS" ]]; then
+ # TODO(deymo): Read the supported minor version from the .zip metadata.
+ FORCE_MINOR_VERSION="2"
+ fi
+
+ local part part_file temp_raw filesize
+ for part in "${partitions[@]}"; do
+ part_file=$(create_tempfile "${part}.img.XXXXXX")
+ CLEANUP_FILES+=("${part_file}")
+ unzip -p "${image}" "IMAGES/${part}.img" >"${part_file}"
+
+ # If the partition is stored as an Android sparse image file, we need to
+ # convert them to a raw image for the update.
+ local magic=$(head --bytes=4 "${part_file}" | hexdump -e '1/1 "%.2x"')
+ if [[ "${magic}" == "3aff26ed" ]]; then
+ temp_raw=$(create_tempfile "${part}.raw.XXXXXX")
+ CLEANUP_FILES+=("${temp_raw}")
+ echo "Converting Android sparse image ${part}.img to RAW."
+ simg2img "${part_file}" "${temp_raw}"
+ # At this point, we can drop the contents of the old part_file file, but
+ # we can't delete the file because it will be deleted in cleanup.
+ true >"${part_file}"
+ part_file="${temp_raw}"
+ fi
+
+ # delta_generator only supports images multiple of 4 KiB, so we pad with
+ # zeros if needed.
+ filesize=$(stat -c%s "${part_file}")
+ if [[ $(( filesize % 4096 )) -ne 0 ]]; then
+ echo "Rounding up partition ${part}.img to multiple of 4 KiB."
+ : $(( filesize = (filesize + 4095) & -4096 ))
+ truncate --size="${filesize}" "${part_file}"
+ fi
+
+ eval "${partitions_array}[\"${part}\"]=\"${part_file}\""
+ echo "Extracted ${partitions_array}[${part}]: ${filesize} bytes"
+ done
+}
+
validate_generate() {
[[ -n "${FLAGS_payload}" ]] ||
die "Error: you must specify an output filename with --payload FILENAME"
@@ -198,30 +282,36 @@
payload_type="full"
fi
- echo "Generating ${payload_type} update"
+ echo "Extracting images for ${payload_type} update."
- # TODO(deymo): Detect the format the image and call the right extract_image
- # function.
- extract_image_cros "${FLAGS_target_image}" DST_PARTITIONS
+ extract_image "${FLAGS_target_image}" DST_PARTITIONS
if [[ "${payload_type}" == "delta" ]]; then
- extract_image_cros "${FLAGS_source_image}" SRC_PARTITIONS
+ extract_image "${FLAGS_source_image}" SRC_PARTITIONS
fi
+ echo "Generating ${payload_type} update."
GENERATOR_ARGS=(
# Common payload args:
-out_file="${FLAGS_payload}"
# Target image args:
# TODO(deymo): Pass the list of partitions to the generator.
- -new_image="${DST_PARTITIONS[root]}"
- -new_kernel="${DST_PARTITIONS[kernel]}"
+ -new_image="${DST_PARTITIONS[system]}"
+ -new_kernel="${DST_PARTITIONS[boot]}"
)
if [[ "${payload_type}" == "delta" ]]; then
GENERATOR_ARGS+=(
# Source image args:
- -old_image="${SRC_PARTITIONS[root]}"
- -old_kernel="${SRC_PARTITIONS[kernel]}"
+ -old_image="${SRC_PARTITIONS[system]}"
+ -old_kernel="${SRC_PARTITIONS[boot]}"
)
+ if [[ -n "${FORCE_MINOR_VERSION}" ]]; then
+ GENERATOR_ARGS+=( --minor_version="${FORCE_MINOR_VERSION}" )
+ fi
+ fi
+
+ if [[ -n "${FORCE_MAJOR_VERSION}" ]]; then
+ GENERATOR_ARGS+=( --major_version="${FORCE_MAJOR_VERSION}" )
fi
echo "Running delta_generator with args: ${GENERATOR_ARGS[@]}"