mirror of
https://github.com/flatcar/scripts.git
synced 2025-09-24 15:11:19 +02:00
crosutils: refine memento image / factory package creation
This CL improves creation of memento / factory image payload by: - verbose progress report - allowing to compress by pigz, the parallel version of gzip - prevents unpacking entire image if partition tools (cgpt/parted) is available. BUG=chromium-os:6536,chromium-os:5208 TEST=Verified executing "time ./make_factory_package.sh ..." for ToT factory bundle: - before this CL (memento gzip param is not -9): 3m53.126s - after this CL, without pigz, with cgpt: 2m34.897s - after this CL, with pigz+cgpt, memento_gz=-9: 0m45.603s - after this CL, with pigz, without cgpt/parted, memento_gz=-9: 1m49.748s Also verified such bundle can be installed on a real netbook device. Change-Id: Ie182844ea5482d6d321b9549fa584377edf7dfe3 Review URL: http://codereview.chromium.org/4824003
This commit is contained in:
parent
4ca4b147e5
commit
51484a96e9
106
image_common.sh
Normal file
106
image_common.sh
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Copyright (c) 2009 The Chromium OS Authors. All rights reserved.
|
||||||
|
# Use of this source code is governed by a BSD-style license that can be
|
||||||
|
# found in the LICENSE file.
|
||||||
|
|
||||||
|
# This script contains common utility function to deal with disk images,
|
||||||
|
# especially for being redistributed into platforms without complete Chromium OS
|
||||||
|
# developing environment.
|
||||||
|
|
||||||
|
# Check if given command is available in current system
|
||||||
|
has_command() {
|
||||||
|
type "$1" >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
err_die() {
|
||||||
|
echo "ERROR: $@" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Finds the best gzip compressor and invoke it.
|
||||||
|
gzip_compress() {
|
||||||
|
if has_command pigz; then
|
||||||
|
# echo " ** Using parallel gzip **" >&2
|
||||||
|
# Tested with -b 32, 64, 128(default), 256, 1024, 16384, and -b 32 (max
|
||||||
|
# window size of Deflate) seems to be the best in output size.
|
||||||
|
pigz -b 32 "$@"
|
||||||
|
else
|
||||||
|
gzip "$@"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Finds if current system has tools for part_* commands
|
||||||
|
has_part_tools() {
|
||||||
|
has_command cgpt || has_command parted
|
||||||
|
}
|
||||||
|
|
||||||
|
# Finds the best partition tool and print partition offset
|
||||||
|
part_offset() {
|
||||||
|
local file="$1"
|
||||||
|
local partno="$2"
|
||||||
|
|
||||||
|
if has_command cgpt; then
|
||||||
|
cgpt show -b -i "$partno" "$file"
|
||||||
|
elif has_command parted; then
|
||||||
|
parted -m "$file" unit s print |
|
||||||
|
grep "^$partno:" | cut -d ':' -f 2 | sed 's/s$//'
|
||||||
|
else
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Finds the best partition tool and print partition size
|
||||||
|
part_size() {
|
||||||
|
local file="$1"
|
||||||
|
local partno="$2"
|
||||||
|
|
||||||
|
if has_command cgpt; then
|
||||||
|
cgpt show -s -i "$partno" "$file"
|
||||||
|
elif has_command parted; then
|
||||||
|
parted -m "$file" unit s print |
|
||||||
|
grep "^$partno:" | cut -d ':' -f 4 | sed 's/s$//'
|
||||||
|
else
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Dumps a file by given offset and size (in sectors)
|
||||||
|
dump_partial_file() {
|
||||||
|
local file="$1"
|
||||||
|
local offset="$2"
|
||||||
|
local sectors="$3"
|
||||||
|
local bs=512
|
||||||
|
|
||||||
|
# Try to use larger buffer if offset/size can be re-aligned.
|
||||||
|
# 2M / 512 = 4096
|
||||||
|
local buffer_ratio=4096
|
||||||
|
if [ $((offset % buffer_ratio)) -eq 0 -a \
|
||||||
|
$((sectors % buffer_ratio)) -eq 0 ]; then
|
||||||
|
offset=$((offset / buffer_ratio))
|
||||||
|
sectors=$((sectors / buffer_ratio))
|
||||||
|
bs=$((bs * buffer_ratio))
|
||||||
|
fi
|
||||||
|
|
||||||
|
if has_command pv; then
|
||||||
|
dd if="$file" bs=$bs skip="$offset" count="$sectors" \
|
||||||
|
oflag=sync status=noxfer 2>/dev/null |
|
||||||
|
pv -ptreb -B 4m -s $((sectors * $bs))
|
||||||
|
else
|
||||||
|
dd if="$file" bs=$bs skip="$offset" count="$sectors" \
|
||||||
|
oflag=sync status=noxfer 2>/dev/null
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Dumps a specific partition from given image file
|
||||||
|
dump_partition() {
|
||||||
|
local file="$1"
|
||||||
|
local part_num="$2"
|
||||||
|
local offset="$(part_offset "$file" "$part_num")" ||
|
||||||
|
err_die "failed to dump partition #$part_num from: $file"
|
||||||
|
local size="$(part_size "$file" "$part_num")" ||
|
||||||
|
err_die "failed to dump partition #$part_num from: $file"
|
||||||
|
|
||||||
|
dump_partial_file "$file" "$offset" "$size"
|
||||||
|
}
|
||||||
|
|
@ -18,6 +18,9 @@
|
|||||||
# Load functions and constants for chromeos-install
|
# Load functions and constants for chromeos-install
|
||||||
. "$(dirname "$0")/chromeos-common.sh"
|
. "$(dirname "$0")/chromeos-common.sh"
|
||||||
|
|
||||||
|
# Load functions designed for image processing
|
||||||
|
. "$(dirname "$0")/image_common.sh"
|
||||||
|
|
||||||
get_default_board
|
get_default_board
|
||||||
|
|
||||||
# Flags
|
# Flags
|
||||||
@ -95,6 +98,50 @@ prepare_dir() {
|
|||||||
rm -rf state.gz
|
rm -rf state.gz
|
||||||
}
|
}
|
||||||
|
|
||||||
|
compress_and_hash_memento_image() {
|
||||||
|
local input_file="$1"
|
||||||
|
|
||||||
|
if has_part_tools; then
|
||||||
|
sudo "${SCRIPTS_DIR}/mk_memento_images.sh" "$input_file" 2 3 |
|
||||||
|
grep hash |
|
||||||
|
awk '{print $4}'
|
||||||
|
else
|
||||||
|
sudo "${SCRIPTS_DIR}/mk_memento_images.sh" part_2 part_3 |
|
||||||
|
grep hash |
|
||||||
|
awk '{print $4}'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
compress_and_hash_file() {
|
||||||
|
local input_file="$1"
|
||||||
|
local output_file="$2"
|
||||||
|
|
||||||
|
if [ -z "$input_file" ]; then
|
||||||
|
# Runs as a pipe processor
|
||||||
|
gzip_compress -c -9 |
|
||||||
|
tee "$output_file" |
|
||||||
|
openssl sha1 -binary |
|
||||||
|
openssl base64
|
||||||
|
else
|
||||||
|
gzip_compress -c -9 "$input_file" |
|
||||||
|
tee "$output_file" |
|
||||||
|
openssl sha1 -binary |
|
||||||
|
openssl base64
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
compress_and_hash_partition() {
|
||||||
|
local input_file="$1"
|
||||||
|
local part_num="$2"
|
||||||
|
local output_file="$3"
|
||||||
|
|
||||||
|
if has_part_tools; then
|
||||||
|
dump_partition "$input_file" "$part_num" |
|
||||||
|
compress_and_hash_file "" "$output_file"
|
||||||
|
else
|
||||||
|
compress_and_hash_file "part_$part_num" "$output_file"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# Clean up stale config and data files.
|
# Clean up stale config and data files.
|
||||||
prepare_omaha
|
prepare_omaha
|
||||||
@ -108,21 +155,25 @@ echo "Output omaha config to ${OMAHA_DIR}/miniomaha.conf"
|
|||||||
|
|
||||||
prepare_dir
|
prepare_dir
|
||||||
|
|
||||||
sudo ./unpack_partitions.sh ${RELEASE_IMAGE} &> /dev/null
|
if ! has_part_tools; then
|
||||||
release_hash=`sudo ${SCRIPTS_DIR}/mk_memento_images.sh part_2 part_3 \
|
#TODO(hungte) we can still avoid running unpack_partitions.sh
|
||||||
| grep hash | awk '{print $4}'`
|
# by $(cat unpack_partitions.sh | grep Label | sed "s/#//" | grep ${name}" |
|
||||||
|
# awk '{ print $1}') to fetch offset/size.
|
||||||
|
echo "Unpacking image ${RELEASE_IMAGE} ..." >&2
|
||||||
|
sudo ./unpack_partitions.sh "${RELEASE_IMAGE}" 2>/dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
release_hash="$(compress_and_hash_memento_image "${RELEASE_IMAGE}")"
|
||||||
sudo chmod a+rw update.gz
|
sudo chmod a+rw update.gz
|
||||||
mv update.gz rootfs-release.gz
|
mv update.gz rootfs-release.gz
|
||||||
mv rootfs-release.gz ${OMAHA_DATA_DIR}
|
mv rootfs-release.gz ${OMAHA_DATA_DIR}
|
||||||
echo "release: ${release_hash}"
|
echo "release: ${release_hash}"
|
||||||
|
|
||||||
cat part_8 | gzip -9 > oem.gz
|
oem_hash="$(compress_and_hash_partition "${RELEASE_IMAGE}" 8 "oem.gz")"
|
||||||
oem_hash=`cat oem.gz | openssl sha1 -binary | openssl base64`
|
|
||||||
mv oem.gz ${OMAHA_DATA_DIR}
|
mv oem.gz ${OMAHA_DATA_DIR}
|
||||||
echo "oem: ${oem_hash}"
|
echo "oem: ${oem_hash}"
|
||||||
|
|
||||||
cat part_12 | gzip -9 > efi.gz
|
efi_hash="$(compress_and_hash_partition "${RELEASE_IMAGE}" 12 "efi.gz")"
|
||||||
efi_hash=`cat efi.gz | openssl sha1 -binary | openssl base64`
|
|
||||||
mv efi.gz ${OMAHA_DATA_DIR}
|
mv efi.gz ${OMAHA_DATA_DIR}
|
||||||
echo "efi: ${efi_hash}"
|
echo "efi: ${efi_hash}"
|
||||||
|
|
||||||
@ -132,17 +183,18 @@ popd > /dev/null
|
|||||||
pushd ${FACTORY_DIR} > /dev/null
|
pushd ${FACTORY_DIR} > /dev/null
|
||||||
prepare_dir
|
prepare_dir
|
||||||
|
|
||||||
|
if ! has_part_tools; then
|
||||||
|
echo "Unpacking image ${FACTORY_IMAGE} ..." >&2
|
||||||
|
sudo ./unpack_partitions.sh "${FACTORY_IMAGE}" 2>/dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
sudo ./unpack_partitions.sh ${FACTORY_IMAGE} &> /dev/null
|
test_hash="$(compress_and_hash_memento_image "${FACTORY_IMAGE}")"
|
||||||
test_hash=`sudo ${SCRIPTS_DIR}//mk_memento_images.sh part_2 part_3 \
|
|
||||||
| grep hash | awk '{print $4}'`
|
|
||||||
sudo chmod a+rw update.gz
|
sudo chmod a+rw update.gz
|
||||||
mv update.gz rootfs-test.gz
|
mv update.gz rootfs-test.gz
|
||||||
mv rootfs-test.gz ${OMAHA_DATA_DIR}
|
mv rootfs-test.gz ${OMAHA_DATA_DIR}
|
||||||
echo "test: ${test_hash}"
|
echo "test: ${test_hash}"
|
||||||
|
|
||||||
cat part_1 | gzip -9 > state.gz
|
state_hash="$(compress_and_hash_partition "${FACTORY_IMAGE}" 1 "state.gz")"
|
||||||
state_hash=`cat state.gz | openssl sha1 -binary | openssl base64`
|
|
||||||
mv state.gz ${OMAHA_DATA_DIR}
|
mv state.gz ${OMAHA_DATA_DIR}
|
||||||
echo "state: ${state_hash}"
|
echo "state: ${state_hash}"
|
||||||
|
|
||||||
@ -155,8 +207,7 @@ if [ ! -z ${FLAGS_firmware_updater} ] ; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cat $SHELLBALL | gzip -9 > firmware.gz
|
firmware_hash="$(compress_and_hash_file "$SHELLBALL" "firmware.gz")"
|
||||||
firmware_hash=`cat firmware.gz | openssl sha1 -binary | openssl base64`
|
|
||||||
mv firmware.gz ${OMAHA_DATA_DIR}
|
mv firmware.gz ${OMAHA_DATA_DIR}
|
||||||
echo "firmware: ${firmware_hash}"
|
echo "firmware: ${firmware_hash}"
|
||||||
fi
|
fi
|
||||||
|
@ -10,25 +10,64 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
LIB_IMAGE_COMMON="$(dirname "$0")/image_common.sh"
|
||||||
|
if ! . "$LIB_IMAGE_COMMON"; then
|
||||||
|
echo "Missing required library: $LIB_IMAGE_COMMON. Cannot continue."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -z "$2" -o -z "$1" ]; then
|
if [ -z "$2" -o -z "$1" ]; then
|
||||||
echo "usage: $0 path/to/kernel_partition_img path/to/rootfs_partition_img"
|
echo "usage: $0 path/to/kernel_partition_img path/to/rootfs_partition_img"
|
||||||
|
echo " or $0 path/to/chromiumos_img kern_part_no rootfs_part_no"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$CROS_GENERATE_UPDATE_PAYLOAD_CALLED" != "1" ]; then
|
if [ "$CROS_GENERATE_UPDATE_PAYLOAD_CALLED" != "1" ]; then
|
||||||
echo "WARNING:"
|
echo "WARNING:"
|
||||||
echo "This script should only be called from cros_generate_update_payload"
|
echo " This script should only be called from cros_generate_update_payload"
|
||||||
echo "Please run that script with --help to see how to use it."
|
echo " Please run that script with --help to see how to use it."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! has_command pigz; then
|
||||||
|
(echo "WARNING:"
|
||||||
|
echo " Your system does not have pigz (parallel gzip) installed."
|
||||||
|
echo " COMPRESSING WILL BE VERY SLOW. It is recommended to install pigz"
|
||||||
|
if has_command apt-get; then
|
||||||
|
echo " by 'sudo apt-get install pigz'."
|
||||||
|
elif has_command emerge; then
|
||||||
|
echo " by 'sudo emerge pigz'."
|
||||||
|
fi) >&2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $(whoami) = "root" ]; then
|
if [ $(whoami) = "root" ]; then
|
||||||
echo "running $0 as root which is unneccessary"
|
echo "running $0 as root which is unneccessary"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
KPART="$1"
|
# Determine the offset size, and file name of parameters
|
||||||
ROOT_PART="$2"
|
if [ -z "$3" ]; then
|
||||||
|
# kernnel_img rootfs_img
|
||||||
KPART_SIZE=$(stat -c%s "$KPART")
|
KPART="$1"
|
||||||
|
ROOT_PART="$2"
|
||||||
|
KPART_SIZE=$(stat -c%s "$KPART")
|
||||||
|
ROOT_PART_SIZE=$(stat -c%s "$ROOT_PART")
|
||||||
|
KPART_OFFSET=0
|
||||||
|
KPART_SECTORS=$((KPART_SIZE / 512))
|
||||||
|
ROOT_OFFSET=0
|
||||||
|
ROOT_SECTORS=$((ROOT_PART_SIZE / 512))
|
||||||
|
else
|
||||||
|
# chromiumos_img kern_part_no rootfs_part_no
|
||||||
|
KPART="$1"
|
||||||
|
ROOT_PART="$1"
|
||||||
|
KPART_OFFSET="$(part_offset "$KPART" "$2")" ||
|
||||||
|
err_die "cannot retieve kernel partition offset"
|
||||||
|
KPART_SECTORS="$(part_size "$KPART" "$2")" ||
|
||||||
|
err_die "cannot retieve kernel partition size"
|
||||||
|
ROOT_OFFSET="$(part_offset "$ROOT_PART" "$3")" ||
|
||||||
|
err_die "cannot retieve root partition offset"
|
||||||
|
ROOT_SECTORS="$(part_size "$ROOT_PART" "$3")" ||
|
||||||
|
err_die "cannot retieve root partition size"
|
||||||
|
KPART_SIZE=$((KPART_SECTORS * 512))
|
||||||
|
fi
|
||||||
|
|
||||||
# Sanity check size.
|
# Sanity check size.
|
||||||
if [ "$KPART_SIZE" -gt $((16 * 1024 * 1024)) ]; then
|
if [ "$KPART_SIZE" -gt $((16 * 1024 * 1024)) ]; then
|
||||||
@ -38,34 +77,31 @@ if [ "$KPART_SIZE" -gt $((16 * 1024 * 1024)) ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
FINAL_OUT_FILE=$(dirname "$1")/update.gz
|
FINAL_OUT_FILE=$(dirname "$1")/update.gz
|
||||||
UNCOMPRESSED_OUT_FILE="$FINAL_OUT_FILE.uncompressed"
|
|
||||||
|
|
||||||
# First, write size of kernel partition in big endian as uint64 to out file
|
# Update payload format:
|
||||||
# printf converts it to a number like 00000000003d0900. sed converts it to:
|
# [kernel_size: big-endian uint64][kernel_blob][rootfs_blob]
|
||||||
# \\x00\\x00\\x00\\x00\\x00\\x3d\\x09\\x00, then xargs converts it to binary
|
|
||||||
# with echo.
|
|
||||||
printf %016x "$KPART_SIZE" | \
|
|
||||||
sed 's/\([0-9a-f][0-9a-f]\)/\\\\x\1/g' | \
|
|
||||||
xargs echo -ne > "$UNCOMPRESSED_OUT_FILE"
|
|
||||||
|
|
||||||
# Next, write kernel partition to the out file
|
# Prepare kernel_size by using printf as a number like 00000000003d0900, then
|
||||||
cat "$KPART" >> "$UNCOMPRESSED_OUT_FILE"
|
# sed to convert as: \x00\x00\x00\x00\x00\x3d\x09\x00, finally echo -e to
|
||||||
|
# convert into binary.
|
||||||
|
KPART_SIZE_SIGNATURE="$(printf "%016x" "$KPART_SIZE" |
|
||||||
|
sed 's/\([0-9a-f][0-9a-f]\)/\\x\1/g')"
|
||||||
|
|
||||||
# Sanity check size of output file now
|
# Build the blob!
|
||||||
if [ $(stat -c%s "$UNCOMPRESSED_OUT_FILE") -ne $((8 + $KPART_SIZE)) ]; then
|
CS_AND_RET_CODES="$(
|
||||||
echo "Kernel partition changed size during image generation. Aborting."
|
(echo -en "$KPART_SIZE_SIGNATURE"
|
||||||
exit 1
|
echo "Compressing kernel..." >&2
|
||||||
fi
|
dump_partial_file "$KPART" "$KPART_OFFSET" "$KPART_SECTORS"
|
||||||
|
echo "Compressing rootfs..." >&2
|
||||||
|
dump_partial_file "$ROOT_PART" "$ROOT_OFFSET" "$ROOT_SECTORS") |
|
||||||
|
gzip_compress -9 -c |
|
||||||
|
tee "$FINAL_OUT_FILE" |
|
||||||
|
openssl sha1 -binary |
|
||||||
|
openssl base64 |
|
||||||
|
tr '\n' ' '
|
||||||
|
echo ${PIPESTATUS[*]})"
|
||||||
|
|
||||||
# Put rootfs into the out file
|
EXPECTED_RET_CODES="0 0 0 0 0 0"
|
||||||
cat "$ROOT_PART" >> "$UNCOMPRESSED_OUT_FILE"
|
|
||||||
|
|
||||||
# compress and hash
|
|
||||||
CS_AND_RET_CODES=$(gzip -c "$UNCOMPRESSED_OUT_FILE" | \
|
|
||||||
tee "$FINAL_OUT_FILE" | openssl sha1 -binary | \
|
|
||||||
openssl base64 | tr '\n' ' '; \
|
|
||||||
echo ${PIPESTATUS[*]})
|
|
||||||
EXPECTED_RET_CODES="0 0 0 0 0"
|
|
||||||
set -- $CS_AND_RET_CODES
|
set -- $CS_AND_RET_CODES
|
||||||
CALC_CS="$1"
|
CALC_CS="$1"
|
||||||
shift
|
shift
|
||||||
@ -75,6 +111,4 @@ if [ "$RET_CODES" != "$EXPECTED_RET_CODES" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
rm "$UNCOMPRESSED_OUT_FILE"
|
|
||||||
|
|
||||||
echo Success. hash is "$CALC_CS"
|
echo Success. hash is "$CALC_CS"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user