(factory-241) Sync factory builder scripts with ToT
We've seen Alex factory bundle corrupted several times, and usually
caused tools modified SSD rootfs partition unexpectedly.
ToT make_factory_package has more safe and solid image type detection
ability so we should port that to R11 factory.
BUG=chrome-os-partner:6299
TEST=./make_factory_package --release PATH_TO_RECOVERY \
--factory PATH_TO_FACTORY_TEST \
--firmware PATH_TO_FIRMWARE \
--hwid none
Change-Id: I9a2aa34b32e22ea21dd8e814b1e7ef53d80bcd04
Reviewed-on: https://gerrit.chromium.org/gerrit/10482
Reviewed-by: Jay Kim <yongjaek@chromium.org>
Tested-by: Hung-Te Lin <hungte@chromium.org>
diff --git a/lib/cros_image_common.sh b/lib/cros_image_common.sh
index b9e2802..d550240 100644
--- a/lib/cros_image_common.sh
+++ b/lib/cros_image_common.sh
@@ -45,6 +45,16 @@
image_has_command cgpt || image_has_command parted
}
+# Finds if specified tool can be found by current path; updates system path if
+# the tool is available in given folder.
+image_find_tool() {
+ local tool="$1"
+ local alternative_folder="$(readlink -f "$2")"
+ if ! image_has_command "$tool" && [ -x "$alternative_folder/$tool" ]; then
+ PATH="$alternative_folder:$PATH"; export PATH
+ fi
+}
+
# Finds the best partition tool and print partition offset
image_part_offset() {
local file="$1"
@@ -55,6 +65,8 @@
if image_has_command cgpt; then
cgpt show -b -i "$partno" "$file"
elif image_has_command parted; then
+ # First trial-run to make sure image is valid (because awk always return 0)
+ parted -m "$file" unit s print | grep -qs "^$partno:" || exit 1
parted -m "$file" unit s print | awk -F ':' "/^$partno:/ { print int(\$2) }"
elif [ -f "$unpack_file" ]; then
awk "/ $partno *Label:/ { print \$2 }" "$unpack_file"
@@ -73,6 +85,8 @@
if image_has_command cgpt; then
cgpt show -s -i "$partno" "$file"
elif image_has_command parted; then
+ # First trial-run to make sure image is valid (because awk always return 0)
+ parted -m "$file" unit s print | grep -qs "^$partno:" || exit 1
parted -m "$file" unit s print | awk -F ':' "/^$partno:/ { print int(\$4) }"
elif [ -s "$unpack_file" ]; then
awk "/ $partno *Label:/ { print \$3 }" "$unpack_file"
@@ -88,15 +102,13 @@
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
+ # Increase buffer size as much as possible until 8M
+ while [ $((bs < (8 * 1024 * 1024) && sectors > 0 &&
+ offset % 2 == 0 && sectors % 2 == 0)) = "1" ]; do
+ bs=$((bs * 2))
+ offset=$((offset / 2))
+ sectors=$((sectors / 2))
+ done
if image_has_command pv; then
dd if="$file" bs=$bs skip="$offset" count="$sectors" \
@@ -120,6 +132,47 @@
image_dump_partial_file "$file" "$offset" "$size"
}
+# Updates a file (from stdin) by given offset and size (in sectors)
+image_update_partial_file() {
+ local file="$1"
+ local offset="$2"
+ local sectors="$3"
+ local bs=512
+ local oflag="oflag=dsync"
+
+ # Improve performance if we're not updating block (Ex, USB) devices
+ [ -b "$file" ] || oflag=""
+
+ # Increase buffer size as much as possible until 8M
+ while [ $((bs < (8 * 1024 * 1024) && sectors > 0 &&
+ offset % 2 == 0 && sectors % 2 == 0)) = "1" ]; do
+ bs=$((bs * 2))
+ offset=$((offset / 2))
+ sectors=$((sectors / 2))
+ done
+
+ if image_has_command pv; then
+ pv -ptreb -B $bs -s $((sectors * bs)) |
+ dd of="$file" bs=$bs seek="$offset" count="$sectors" \
+ iflag=fullblock $oflag conv=notrunc status=noxfer 2>/dev/null
+ else
+ dd of="$file" bs=$bs seek="$offset" count="$sectors" \
+ iflag=fullblock $oflag conv=notrunc status=noxfer 2>/dev/null
+ fi
+}
+
+# Updates a specific partition in given image file (from stdin)
+image_update_partition() {
+ local file="$1"
+ local part_num="$2"
+ local offset="$(image_part_offset "$file" "$part_num")" ||
+ image_die "failed to find partition #$part_num from: $file"
+ local size="$(image_part_size "$file" "$part_num")" ||
+ image_die "failed to find partition #$part_num from: $file"
+
+ image_update_partial_file "$file" "$offset" "$size"
+}
+
# Maps a specific partition from given image file to a loop device
image_map_partition() {
local file="$1"
@@ -129,7 +182,7 @@
local size="$(image_part_size "$file" "$part_num")" ||
image_die "failed to find partition #$part_num from: $file"
- losetup --offset $((offset * 512)) --sizelimit=$((size * 512)) \
+ sudo losetup --offset $((offset * 512)) --sizelimit=$((size * 512)) \
-f --show "$file"
}
@@ -137,7 +190,7 @@
image_unmap_partition() {
local map_point="$1"
- losetup -d "$map_point"
+ sudo losetup -d "$map_point"
}
# Mounts a specific partition inside a given image file
@@ -156,7 +209,7 @@
mount_opt=",ro"
fi
- mount \
+ sudo mount \
-o "loop,offset=$((offset * 512)),sizelimit=$((size * 512)),$mount_opt" \
"$file" \
"$mount_point"
@@ -166,35 +219,116 @@
image_umount_partition() {
local mount_point="$1"
- umount -d "$mount_point"
+ sudo umount -d "$mount_point"
}
-# Copy a partition from one image to another.
+# Copy a partition from one image to another (size must be equal)
image_partition_copy() {
- local src="$1"
- local srcpart="$2"
- local dst="$3"
- local dstpart="$4"
-
- local srcoffset=$(image_part_offset "${src}" "${srcpart}")
- local dstoffset=$(image_part_offset "${dst}" "${dstpart}")
- local length=$(image_part_size "${src}" "${srcpart}")
- local dstlength=$(image_part_size "${dst}" "${dstpart}")
-
- if [ "${length}" -gt "${dstlength}" ]; then
- exit 1
+ local src="$1" src_part="$2" dst="$3" dst_part="$4"
+ local size1="$(image_part_size "$src" "$src_part")"
+ local size2="$(image_part_size "$dst" "$dst_part")"
+ if [ "$size1" != "$size2" ]; then
+ die "Partition size different: ($size1 != $size2)"
fi
+ image_dump_partition "$src" "$src_part" 2>/dev/null |
+ image_update_partition "$dst" "$dst_part"
+}
- # Try to use larger buffer if offset/size can be re-aligned.
- # 2M / 512 = 4096
- local buffer_ratio=4096
- local bs=512
- if [ $((dstoffset % buffer_ratio)) -eq 0 -a \
- $((length % buffer_ratio)) -eq 0 ]; then
- dstoffset=$((dstoffset / buffer_ratio))
- bs=$((bs * buffer_ratio))
+# Copy a partition from one image to another (source <= dest)
+image_partition_overwrite() {
+ local src="$1" src_part="$2" dst="$3" dst_part="$4"
+ local size1="$(image_part_size "$src" "$src_part")"
+ local size2="$(image_part_size "$dst" "$dst_part")"
+ if [ "$size1" -gt "$size2" ]; then
+ die "Destination is too small: ($size1 > $size2)"
fi
+ image_dump_partition "$src" "$src_part" 2>/dev/null |
+ image_update_partition "$dst" "$dst_part"
+}
- image_dump_partition "${src}" "${srcpart}" |
- dd of="${dst}" bs="${bs}" seek="${dstoffset}" conv=notrunc oflag=dsync
+# Copy a partition image from file to a full disk image.
+image_partition_copy_from_file() {
+ local src="$1" dst="$2" dst_part="$3"
+ local size1="$(($(stat -c"%s" "$src") / 512))"
+ local size2="$(image_part_size "$dst" "$dst_part")"
+ if [ "$size1" != "$size2" ]; then
+ die "Partition size different: ($size1 != $size2)"
+ fi
+ image_update_partition "$dst" "$dst_part" <"$src"
+}
+
+# Temporary object management
+_IMAGE_TEMP_OBJECTS=""
+
+# Add a temporary object (by mktemp) into list for image_clean_temp to clean
+image_add_temp() {
+ _IMAGE_TEMP_OBJECTS="$_IMAGE_TEMP_OBJECTS $*"
+}
+
+# Cleans objects tracked by image_add_temp.
+image_clean_temp() {
+ local temp_list="$_IMAGE_TEMP_OBJECTS"
+ local object
+ _IMAGE_TEMP_OBJECTS=""
+
+ for object in $temp_list; do
+ if [ -d "$object" ]; then
+ sudo umount -d "$object" >/dev/null 2>&1 || true
+ sudo rmdir "$object" >/dev/null 2>&1 || true
+ else
+ rm -f "$object" >/dev/null 2>&1 || true
+ fi
+ done
+}
+
+# Determines the boot type of a ChromeOS kernel partition.
+# Prints "recovery", "ssd", "usb", "factory_install", "invalid", or "unknown".
+image_cros_kernel_boot_type() {
+ local keyblock="$1"
+ local magic flag skip kernel_config
+
+ # TODO(hungte) use vbutil_keyblock if available
+
+ # Reference: firmware/include/vboot_struct.h
+ local KEY_BLOCK_FLAG_DEVELOPER_0=1 # Developer switch off
+ local KEY_BLOCK_FLAG_DEVELOPER_1=2 # Developer switch on
+ local KEY_BLOCK_FLAG_RECOVERY_0=4 # Not recovery mode
+ local KEY_BLOCK_FLAG_RECOVERY_1=8 # Recovery mode
+ local KEY_BLOCK_MAGIC="CHROMEOS"
+ local KEY_BLOCK_MAGIC_SIZE=8
+ local KEY_BLOCK_FLAG_OFFSET=72 # magic:8 major:4 minor:4 size:8 2*(sig:8*3)
+
+ magic="$(dd if="$keyblock" bs=$KEY_BLOCK_MAGIC_SIZE count=1 2>/dev/null)"
+ if [ "$magic" != "$KEY_BLOCK_MAGIC" ]; then
+ echo "invalid"
+ return
+ fi
+ skip="$KEY_BLOCK_FLAG_OFFSET"
+ flag="$(dd if="$keyblock" bs=1 count=1 skip="$skip" 2>/dev/null |
+ od -t u1 -A n)"
+ if [ "$((flag & KEY_BLOCK_FLAG_RECOVERY_0))" != 0 ]; then
+ echo "ssd"
+ elif [ "$((flag & KEY_BLOCK_FLAG_RECOVERY_1))" != 0 ]; then
+ if [ "$((flag & KEY_BLOCK_FLAG_DEVELOPER_0))" = 0 ]; then
+ echo "factory_install"
+ else
+ # Recovery or USB. Check "cros_recovery" in kernel config.
+ if image_has_command dump_kernel_config; then
+ kernel_config="$(dump_kernel_config "$keyblock")"
+ else
+ # strings is less secure than dump_kernel_config, so let's try more
+ # keywords
+ kernel_config="$(strings "$keyblock" |
+ grep -w "root=" | grep -w "cros_recovery")"
+ fi
+ if (echo "$kernel_config" | grep -qw "cros_recovery") &&
+ (echo "$kernel_config" | grep -qw "kern_b_hash"); then
+ echo "recovery"
+ else
+ echo "usb"
+ fi
+ fi
+ else
+ echo "unknown"
+ fi
}
diff --git a/make_factory_package.sh b/make_factory_package.sh
index 04366d7..0fbfa03 100755
--- a/make_factory_package.sh
+++ b/make_factory_package.sh
@@ -1,6 +1,6 @@
#!/bin/bash
-# Copyright (c) 2009 The Chromium OS Authors. All rights reserved.
+# Copyright (c) 2011 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.
@@ -10,100 +10,254 @@
#
# miniomaha lives in src/platform/dev/ and miniomaha partition sets live
# in src/platform/dev/static.
+#
+# All internal environment variables used by this script are prefixed with
+# "MFP_". Please avoid using them for other purposes.
+# "MFP_CONFIG_"* are shell variables that can be used in config file (--config)
-# --- BEGIN COMMON.SH BOILERPLATE ---
-# Load common CrOS utilities. Inside the chroot this file is installed in
-# /usr/lib/crosutils. Outside the chroot we find it relative to the script's
-# location.
-find_common_sh() {
- local common_paths=(/usr/lib/crosutils $(dirname "$(readlink -f "$0")"))
- local path
+# --- BEGIN FACTORY SCRIPTS BOILERPLATE ---
+# This script may be executed in a full CrOS source tree or an extracted factory
+# bundle with limited tools, so we must always load scripts from $SCRIPT_ROOT
+# and search for binary programs in $SCRIPT_ROOT/../bin
- SCRIPT_ROOT=
- for path in "${common_paths[@]}"; do
- if [ -r "${path}/common.sh" ]; then
- SCRIPT_ROOT=${path}
- break
- fi
- done
-}
+SCRIPT="$(readlink -f "$0")"
+SCRIPT_ROOT="$(dirname "$SCRIPT")"
+. "$SCRIPT_ROOT/lib/cros_image_common.sh" || exit 1
+image_find_tool "cgpt" "$SCRIPT_ROOT/../bin"
+# --- END FACTORY SCRIPTS BOILERPLATE ---
-find_common_sh
-. "${SCRIPT_ROOT}/common.sh" || (echo "Unable to load common.sh" && exit 1)
-# --- END COMMON.SH BOILERPLATE ---
-
-# Load functions and constants for chromeos-install
-# NOTE: This script needs to be called from outside the chroot.
-. "/usr/lib/installer/chromeos-common.sh" &> /dev/null || \
-. "${SRC_ROOT}/platform/installer/chromeos-common.sh" || \
- die "Unable to load /usr/lib/installer/chromeos-common.sh"
-
-# Load functions designed for image processing
-. "${SCRIPT_ROOT}/lib/cros_image_common.sh" ||
- die "Cannot load required library: lib/cros_image_common.sh; Abort."
+if [ -f "$SCRIPT_ROOT/../dev/devserver.py" ]; then
+ # Running within an extracted factory bundle
+ GCLIENT_ROOT="$(readlink -f "$SCRIPT_ROOT/..")"
+fi
+. "$SCRIPT_ROOT/common.sh" || exit 1
+. "$SCRIPT_ROOT/chromeos-common.sh" || exit 1
get_default_board
+FLAGS_NONE='none'
# Flags
DEFINE_string board "${DEFAULT_BOARD}" "Board for which the image was built"
DEFINE_string factory "" \
"Directory and file containing factory image: /path/chromiumos_test_image.bin"
DEFINE_string firmware_updater "" \
- "If set, include the firmware shellball into the server configuration"
+ "Firmware updater (shellball) into the server configuration,"\
+" or '$FLAGS_NONE' to prevent running firmware updater."
+DEFINE_string hwid_updater "" \
+ "The component list updater for HWID validation,"\
+" or '$FLAGS_NONE' to prevent updating the component list files."
+DEFINE_string complete_script "" \
+ "If set, include the script for the last-step execution of factory install"
DEFINE_string release "" \
"Directory and file containing release image: /path/chromiumos_image.bin"
DEFINE_string subfolder "" \
"If set, the name of the subfolder to put the payload items inside"
+DEFINE_string usbimg "" \
+ "If set, the name of the USB installation disk image file to output"
+DEFINE_string install_shim "" \
+ "Directory and file containing factory install shim for --usbimg"
DEFINE_string diskimg "" \
"If set, the name of the diskimage file to output"
DEFINE_boolean preserve ${FLAGS_FALSE} \
"If set, reuse the diskimage file, if available"
DEFINE_integer sectors 31277232 "Size of image in sectors"
+DEFINE_boolean detect_release_image ${FLAGS_TRUE} \
+ "If set, try to auto-detect the type of release image and convert if required"
+DEFINE_string config "" \
+ 'Configuration file where parameters are read from. You can use '\
+'\$MFP_CONFIG_PATH and \$MFP_CONFIG_DIR (path and directory to the '\
+'config file itself) in config file to use relative path'
+
+# Usage Help
+FLAGS_HELP="Prepares factory resources (mini-omaha server, RMA/usb/disk images)
+
+USAGE: $0 [flags] args
+Note environment variables with prefix MFP_ are for reserved for internal use.
+"
# Parse command line
FLAGS "$@" || exit 1
+ORIGINAL_PARAMS="$@"
eval set -- "${FLAGS_ARGV}"
-if [ ! -f "${FLAGS_release}" ]; then
- echo "Cannot find image file ${FLAGS_release}"
- exit 1
-fi
+on_exit() {
+ image_clean_temp
+}
-if [ ! -f "${FLAGS_factory}" ]; then
- echo "Cannot find image file ${FLAGS_factory}"
- exit 1
-fi
+# Param checking and validation
+check_file_param() {
+ local param="$1"
+ local msg="$2"
+ local param_name="${param#FLAGS_}"
+ local param_value="$(eval echo \$$1)"
-if [ -n "${FLAGS_firmware_updater}" ] &&
- [ ! -f "${FLAGS_firmware_updater}" ]; then
- echo "Cannot find firmware file ${FLAGS_firmware_updater}"
- exit 1
-fi
+ [ -n "$param_value" ] ||
+ die "You must assign a file for --$param_name $msg"
+ [ -f "$param_value" ] ||
+ die "Cannot find file: $param_value"
+}
-# Convert args to paths. Need eval to un-quote the string so that shell
-# chars like ~ are processed; just doing FOO=`readlink -f ${FOO}` won't work.
-OMAHA_DIR="${SRC_ROOT}/platform/dev"
-OMAHA_CONF="${OMAHA_DIR}/miniomaha.conf"
-OMAHA_DATA_DIR="${OMAHA_DIR}/static/"
+check_file_param_or_none() {
+ local param="$1"
+ local msg="$2"
+ local param_name="${param#FLAGS_}"
+ local param_value="$(eval echo \$$1)"
-# Note: The subfolder flag can only append configs. That means you will need
-# to have unique board IDs for every time you run. If you delete miniomaha.conf
-# you can still use this flag and it will start fresh.
-if [ -n "${FLAGS_subfolder}" ]; then
- OMAHA_DATA_DIR="${OMAHA_DIR}/static/${FLAGS_subfolder}/"
-fi
+ if [ "$param_value" = "$FLAGS_NONE" ]; then
+ eval "$param=''"
+ return
+ fi
+ [ -n "$param_value" ] ||
+ die "You must assign either a file or 'none' for --$param_name $msg"
+ [ -f "$param_value" ] ||
+ die "Cannot find file: $param_value"
+}
-if [ ${INSIDE_CHROOT} -eq 0 ]; then
- echo "Caching sudo authentication"
- sudo -v
- echo "Done"
-fi
+check_optional_file_param() {
+ local param="$1"
+ local msg="$2"
+ local param_name="${param#FLAGS_}"
+ local param_value="$(eval echo \$$1)"
-# Use this image as the source image to copy
-RELEASE_DIR="$(dirname "${FLAGS_release}")"
-FACTORY_DIR="$(dirname "${FLAGS_factory}")"
-RELEASE_IMAGE="$(basename "${FLAGS_release}")"
-FACTORY_IMAGE="$(basename "${FLAGS_factory}")"
+ if [ -n "$param_value" ] && [ ! -f "$param_value" ]; then
+ die "Cannot find file: $param_value"
+ fi
+}
+
+check_empty_param() {
+ local param="$1"
+ local msg="$2"
+ local param_name="${param#FLAGS_}"
+ local param_value="$(eval echo \$$1)"
+
+ [ -z "$param_value" ] || die "Parameter --$param_name is not supported $msg"
+}
+
+check_parameters() {
+ check_file_param FLAGS_release ""
+ check_file_param FLAGS_factory ""
+
+ # All remaining parameters must be checked:
+ # install_shim, firmware, hwid_updater, complete_script.
+
+ if [ -n "${FLAGS_usbimg}" ]; then
+ [ -z "${FLAGS_diskimg}" ] ||
+ die "--usbimg and --diskimg cannot be used at the same time."
+ check_file_param_or_none FLAGS_firmware_updater "in --usbimg mode"
+ check_file_param_or_none FLAGS_hwid_updater "in --usbimg mode"
+ check_empty_param FLAGS_complete_script "in --usbimg mode"
+ check_file_param FLAGS_install_shim "in --usbimg mode"
+ elif [ -n "${FLAGS_diskimg}" ]; then
+ check_empty_param FLAGS_firmware_updater "in --diskimg mode"
+ check_file_param_or_none FLAGS_hwid_updater "in --diskimg mode"
+ check_empty_param FLAGS_complete_script "in --diskimg mode"
+ check_empty_param FLAGS_install_shim "in --diskimg mode"
+ if [ -b "${FLAGS_diskimg}" -a ! -w "${FLAGS_diskimg}" ] &&
+ [ -z "$MFP_SUDO" -a "$(id -u)" != "0" ]; then
+ # Restart the command with original parameters with sudo for writing to
+ # block device that needs root permission.
+ # MFP_SUDO is a internal flag to prevent unexpected recursion.
+ MFP_SUDO=TRUE exec sudo "$0" $ORIGINAL_PARAMS
+ fi
+ else
+ check_file_param_or_none FLAGS_firmware_updater "in mini-omaha mode"
+ check_file_param_or_none FLAGS_hwid_updater "in mini-omaha mode"
+ check_optional_file_param FLAGS_complete_script "in mini-omaha mode"
+ check_empty_param FLAGS_install_shim "in mini-omaha mode"
+ fi
+}
+
+setup_environment() {
+ # Convert args to paths. Need eval to un-quote the string so that shell
+ # chars like ~ are processed; just doing FOO=`readlink -f ${FOO}` won't work.
+ OMAHA_DIR="${SRC_ROOT}/platform/dev"
+ OMAHA_CONF="${OMAHA_DIR}/miniomaha.conf"
+ OMAHA_DATA_DIR="${OMAHA_DIR}/static/"
+
+ # Note: The subfolder flag can only append configs. That means you will need
+ # to have unique board IDs for every time you run. If you delete
+ # miniomaha.conf you can still use this flag and it will start fresh.
+ if [ -n "${FLAGS_subfolder}" ]; then
+ OMAHA_DATA_DIR="${OMAHA_DIR}/static/${FLAGS_subfolder}/"
+ fi
+
+ # When "sudo -v" is executed inside chroot, it prompts for password; however
+ # the user account inside chroot may be using a different password (ex,
+ # "chronos") from the same account outside chroot. The /etc/sudoers file
+ # inside chroot has explicitly specified "userid ALL=NOPASSWD: ALL" for the
+ # account, so we should do nothing inside chroot.
+ if [ ${INSIDE_CHROOT} -eq 0 ]; then
+ echo "Caching sudo authentication"
+ sudo -v
+ echo "Done"
+ fi
+
+ # Use this image as the source image to copy
+ RELEASE_DIR="$(dirname "${FLAGS_release}")"
+ FACTORY_DIR="$(dirname "${FLAGS_factory}")"
+ RELEASE_IMAGE="$(basename "${FLAGS_release}")"
+ FACTORY_IMAGE="$(basename "${FLAGS_factory}")"
+
+ # Override this with path to modified kernel (for non-SSD images)
+ RELEASE_KERNEL=""
+
+ # Check required tools.
+ if ! image_has_part_tools; then
+ die "Missing partition tools. Please install cgpt/parted, or run in chroot."
+ fi
+}
+
+# Prepares release image source by checking image type, and creates modified
+# partition blob in RELEASE_KERNEL if required.
+prepare_release_image() {
+ local image="$(readlink -f "$1")"
+ local kernel="$(mktemp --tmpdir)"
+ image_add_temp "$kernel"
+
+ # Image Types:
+ # - recovery: kernel in #4 and vmlinuz_hd.vblock in #1
+ # - usb: kernel in #2 and vmlinuz_hd.vblock in #1
+ # - ssd: kernel in #2, no need to change
+ image_dump_partition "$image" "2" >"$kernel" 2>/dev/null ||
+ die "Cannot extract kernel partition from image: $image"
+
+ local image_type="$(image_cros_kernel_boot_type "$kernel")"
+ local need_vmlinuz_hd=""
+ info "Image type is [$image_type]: $image"
+
+ case "$image_type" in
+ "ssd" )
+ true
+ ;;
+ "usb" )
+ RELEASE_KERNEL="$kernel"
+ need_vmlinuz_hd="TRUE"
+ ;;
+ "recovery" )
+ RELEASE_KERNEL="$kernel"
+ image_dump_partition "$image" "4" >"$kernel" 2>/dev/null ||
+ die "Cannot extract real kernel for recovery image: $image"
+ need_vmlinuz_hd="TRUE"
+ ;;
+ * )
+ die "Unexpected release image type: $image_type."
+ ;;
+ esac
+
+ if [ -n "$need_vmlinuz_hd" ]; then
+ local temp_mount="$(mktemp -d --tmpdir)"
+ local vmlinuz_hd_file="vmlinuz_hd.vblock"
+ image_add_temp "$temp_mount"
+ image_mount_partition "$image" "1" "$temp_mount" "ro" ||
+ die "No stateful partition in $image."
+ [ -s "$temp_mount/$vmlinuz_hd_file" ] ||
+ die "Missing $vmlinuz_hd_file in stateful partition: $image"
+ sudo dd if="$temp_mount/$vmlinuz_hd_file" of="$kernel" \
+ bs=512 conv=notrunc >/dev/null 2>&1 ||
+ die "Cannot update kernel with $vmlinuz_hd_file"
+ image_umount_partition "$temp_mount"
+ fi
+}
prepare_img() {
local outdev="$(readlink -f "$FLAGS_diskimg")"
@@ -112,7 +266,8 @@
# We'll need some code to put in the PMBR, for booting on legacy BIOS.
echo "Fetch PMBR"
- local pmbrcode="$(mktemp -d)/gptmbr.bin"
+ local pmbrcode="$(mktemp --tmpdir)"
+ image_add_temp "$pmbrcode"
sudo dd bs=512 count=1 if="${FLAGS_release}" of="${pmbrcode}" status=noxfer
echo "Prepare base disk image"
@@ -123,8 +278,8 @@
"$(stat -c %s ${outdev})" != "$(( ${sectors} * 512 ))" -o \
"$FLAGS_preserve" = "$FLAGS_FALSE" ]; then
echo "Generating empty image file"
- image_dump_partial_file /dev/zero 0 "${sectors}" |
- dd of="${outdev}" bs=8M
+ truncate -s "0" "$outdev"
+ truncate -s "$((sectors * 512))" "$outdev"
else
echo "Reusing $outdev"
fi
@@ -136,37 +291,33 @@
sudo "${GPT}" add -i 2 -S 1 -P 1 "${outdev}"
}
-prepare_omaha() {
- sudo rm -rf "${OMAHA_DATA_DIR}/rootfs-test.gz"
- sudo rm -rf "${OMAHA_DATA_DIR}/rootfs-release.gz"
- rm -rf "${OMAHA_DATA_DIR}/efi.gz"
- rm -rf "${OMAHA_DATA_DIR}/oem.gz"
- rm -rf "${OMAHA_DATA_DIR}/state.gz"
- if [ ! -d "${OMAHA_DATA_DIR}" ]; then
- mkdir -p "${OMAHA_DATA_DIR}"
- fi
-}
-
prepare_dir() {
- sudo rm -rf rootfs-test.gz
- sudo rm -rf rootfs-release.gz
- rm -rf efi.gz
- rm -rf oem.gz
- rm -rf state.gz
+ local dir="$1"
+
+ # TODO(hungte) the three files were created as root by old mk_memento_images;
+ # we can prevent the sudo in future.
+ sudo rm -f "${dir}/rootfs-test.gz"
+ sudo rm -f "${dir}/rootfs-release.gz"
+ sudo rm -f "${dir}/update.gz"
+ for filename in efi oem state hwid firmware; do
+ rm -f "${dir}/${filename}.gz"
+ done
+ if [ ! -d "${dir}" ]; then
+ mkdir -p "${dir}"
+ fi
}
+# Compresses kernel and rootfs of an imge file, and output its hash.
+# Usage:compress_and_hash_memento_image kernel rootfs
+# Please see "mk_memento_images --help" for detail of parameter syntax
compress_and_hash_memento_image() {
- local input_file="$1"
+ local kern="$1"
+ local rootfs="$2"
+ [ "$#" = "2" ] || die "Internal error: compress_and_hash_memento_image $*"
- if [ -n "${IMAGE_IS_UNPACKED}" ]; then
- sudo "${SCRIPTS_DIR}/mk_memento_images.sh" part_2 part_3 |
- grep hash |
- awk '{print $4}'
- else
- sudo "${SCRIPTS_DIR}/mk_memento_images.sh" "$input_file" 2 3 |
- grep hash |
- awk '{print $4}'
- fi
+ "${SCRIPTS_DIR}/mk_memento_images.sh" "$kern" "$rootfs" "." |
+ grep hash |
+ awk '{print $4}'
}
compress_and_hash_file() {
@@ -192,78 +343,131 @@
local part_num="$2"
local output_file="$3"
- if [ -n "${IMAGE_IS_UNPACKED}" ]; then
- compress_and_hash_file "part_$part_num" "$output_file"
- else
- image_dump_partition "$input_file" "$part_num" |
+ image_dump_partition "$input_file" "$part_num" |
compress_and_hash_file "" "$output_file"
+}
+
+# Applies HWID component list files updater into stateful partition
+apply_hwid_updater() {
+ local hwid_updater="$1"
+ local outdev="$2"
+ local hwid_result="0"
+
+ if [ -n "$hwid_updater" ]; then
+ local state_dev="$(image_map_partition "${outdev}" 1)"
+ sudo sh "$hwid_updater" "$state_dev" || hwid_result="$?"
+ image_unmap_partition "$state_dev" || true
+ [ $hwid_result = "0" ] || die "Failed to update HWID ($hwid_result). abort."
fi
}
-# Decide if we should unpack partition
-if image_has_part_tools; then
- IMAGE_IS_UNPACKED=
-else
- #TODO(hungte) Currently we run unpack_partitions.sh if part_tools are not
- # found. If the format of unpack_partitions.sh is reliable, we can prevent
- # creating temporary files. See image_part_offset for more information.
- echo "WARNING: cannot find partition tools. Using unpack_partitions.sh." >&2
- IMAGE_IS_UNPACKED=1
-fi
-
-mount_esp() {
- local image="$1"
- local esp_mountpoint="$2"
- offset=$(partoffset "${image}" 12)
- sudo mount -o loop,offset=$(( offset * 512 )) \
- "${image}" "${esp_mountpoint}"
- ESP_MOUNT="${esp_mountpoint}"
-}
-
-umount_esp() {
- if [ -n "${ESP_MOUNT}" ]; then
- sudo umount "${ESP_MOUNT}"
+generate_usbimg() {
+ if ! type cgpt >/dev/null 2>&1; then
+ die "Missing 'cgpt'. Please install cgpt, or run inside chroot."
fi
+ local builder="$(dirname "$SCRIPT")/make_universal_factory_shim.sh"
+ local release_file="$FLAGS_release"
+
+ if [ -n "$RELEASE_KERNEL" ]; then
+ # TODO(hungte) Improve make_universal_factory_shim to support assigning
+ # a modified kernel to prevent creating temporary image here
+ info "Creating temporary SSD-type release image, please wait..."
+ release_file="$(mktemp --tmpdir)"
+ image_add_temp "${release_file}"
+ if image_has_part_tools pv; then
+ pv -B 16M "${FLAGS_release}" >"${release_file}"
+ else
+ cp -f "${FLAGS_release}" "${release_file}"
+ fi
+ image_partition_copy_from_file "${RELEASE_KERNEL}" "${release_file}" 2
+ fi
+
+ "$builder" -m "${FLAGS_factory}" -f "${FLAGS_usbimg}" \
+ "${FLAGS_install_shim}" "${FLAGS_factory}" "${release_file}"
+ apply_hwid_updater "${FLAGS_hwid_updater}" "${FLAGS_usbimg}"
+
+ # Extract and modify lsb-factory from original install shim
+ local lsb_path="/dev_image/etc/lsb-factory"
+ local src_dir="$(mktemp -d --tmpdir)"
+ local src_lsb="${src_dir}${lsb_path}"
+ local new_dir="$(mktemp -d --tmpdir)"
+ local new_lsb="${new_dir}${lsb_path}"
+ image_add_temp "$src_dir" "$new_dir"
+ image_mount_partition "${FLAGS_install_shim}" 1 "${src_dir}" ""
+ image_mount_partition "${FLAGS_usbimg}" 1 "${new_dir}" "rw"
+ # Copy firmware updater, if available
+ local updater_settings=""
+ if [ -n "${FLAGS_firmware_updater}" ]; then
+ local updater_new_path="${new_dir}/chromeos-firmwareupdate"
+ sudo cp -f "${FLAGS_firmware_updater}" "${updater_new_path}"
+ sudo chmod a+rx "${updater_new_path}"
+ updater_settings="FACTORY_INSTALL_FIRMWARE=/mnt/stateful_partition"
+ updater_settings="$updater_settings/$(basename $updater_new_path)"
+ fi
+ # We put the install shim kernel and rootfs into partition #2 and #3, so
+ # the factory and release image partitions must be moved to +2 location.
+ # USB_OFFSET=2 tells factory_installer/factory_install.sh this information.
+ (cat "$src_lsb" &&
+ echo "FACTORY_INSTALL_FROM_USB=1" &&
+ echo "FACTORY_INSTALL_USB_OFFSET=2" &&
+ echo "$updater_settings") |
+ sudo dd of="${new_lsb}"
+ image_umount_partition "$new_dir"
+ image_umount_partition "$src_dir"
+
+ # Deactivate all kernel partitions except installer slot
+ local i=""
+ for i in 4 5 6 7; do
+ cgpt add -P 0 -T 0 -S 0 -t data -i "$i" "${FLAGS_usbimg}"
+ done
+
+ info "Generated Image at ${FLAGS_usbimg}."
+ info "Done"
}
generate_img() {
local outdev="$(readlink -f "$FLAGS_diskimg")"
local sectors="$FLAGS_sectors"
+ local hwid_updater="${FLAGS_hwid_updater}"
+
+ if [ -n "${FLAGS_hwid_updater}" ]; then
+ hwid_updater="$(readlink -f "$FLAGS_hwid_updater")"
+ fi
prepare_img
# Get the release image.
- pushd "${RELEASE_DIR}" >/dev/null
-
+ local release_image="${RELEASE_DIR}/${RELEASE_IMAGE}"
echo "Release Kernel"
- image_partition_copy "${RELEASE_IMAGE}" 2 "${outdev}" 4
+ if [ -n "$RELEASE_KERNEL" ]; then
+ image_partition_copy_from_file "${RELEASE_KERNEL}" "${outdev}" 4
+ else
+ image_partition_copy "${release_image}" 2 "${outdev}" 4
+ fi
echo "Release Rootfs"
- image_partition_copy "${RELEASE_IMAGE}" 3 "${outdev}" 5
+ image_partition_overwrite "${release_image}" 3 "${outdev}" 5
echo "OEM parition"
- image_partition_copy "${RELEASE_IMAGE}" 8 "${outdev}" 8
-
- popd >/dev/null
+ image_partition_overwrite "${release_image}" 8 "${outdev}" 8
# Go to retrieve the factory test image.
- pushd "${FACTORY_DIR}" >/dev/null
-
+ local factory_image="${FACTORY_DIR}/${FACTORY_IMAGE}"
echo "Factory Kernel"
- image_partition_copy "${FACTORY_IMAGE}" 2 "${outdev}" 2
+ image_partition_copy "${factory_image}" 2 "${outdev}" 2
echo "Factory Rootfs"
- image_partition_copy "${FACTORY_IMAGE}" 3 "${outdev}" 3
+ image_partition_overwrite "${factory_image}" 3 "${outdev}" 3
echo "Factory Stateful"
- image_partition_copy "${FACTORY_IMAGE}" 1 "${outdev}" 1
+ image_partition_overwrite "${factory_image}" 1 "${outdev}" 1
echo "EFI Partition"
- image_partition_copy "${FACTORY_IMAGE}" 12 "${outdev}" 12
+ image_partition_copy "${factory_image}" 12 "${outdev}" 12
+ apply_hwid_updater "${hwid_updater}" "${outdev}"
# TODO(nsanders, wad): consolidate this code into some common code
# when cleaning up kernel commandlines. There is code that touches
# this in postint/chromeos-setimage and build_image. However none
# of the preexisting code actually does what we want here.
- local tmpesp="$(mktemp -d)"
- mount_esp "${outdev}" "${tmpesp}"
-
- trap "umount_esp" EXIT
+ local tmpesp="$(mktemp -d --tmpdir)"
+ image_add_temp "$tmpesp"
+ image_mount_partition "${outdev}" 12 "$tmpesp" "rw"
# Edit boot device default for legacy.
# Support both vboot and regular boot.
@@ -276,36 +480,31 @@
# Somewhat safe as ARM does not support syslinux, I believe.
sudo sed -i "s'HDROOTA'/dev/sda3'g" "${tmpesp}"/syslinux/root.A.cfg
- trap - EXIT
-
- umount_esp
-
+ image_umount_partition "$tmpesp"
echo "Generated Image at $outdev."
echo "Done"
}
generate_omaha() {
+ local kernel rootfs
+ [ -n "$FLAGS_board" ] || die "Need --board parameter for mini-omaha server."
# Clean up stale config and data files.
- prepare_omaha
+ prepare_dir "${OMAHA_DATA_DIR}"
- # Get the release image.
- pushd "${RELEASE_DIR}" >/dev/null
echo "Generating omaha release image from ${FLAGS_release}"
echo "Generating omaha factory image from ${FLAGS_factory}"
echo "Output omaha image to ${OMAHA_DATA_DIR}"
echo "Output omaha config to ${OMAHA_CONF}"
- prepare_dir
+ # Get the release image.
+ # TODO(hungte) deprecate pushd and use temporary folders
+ pushd "${RELEASE_DIR}" >/dev/null
+ prepare_dir "."
- if [ -n "${IMAGE_IS_UNPACKED}" ]; then
- 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
- mv update.gz rootfs-release.gz
- mv rootfs-release.gz "${OMAHA_DATA_DIR}"
+ kernel="${RELEASE_KERNEL:-${RELEASE_IMAGE}:2}"
+ rootfs="${RELEASE_IMAGE}:3"
+ release_hash="$(compress_and_hash_memento_image "$kernel" "$rootfs")"
+ mv ./update.gz "${OMAHA_DATA_DIR}/rootfs-release.gz"
echo "release: ${release_hash}"
oem_hash="$(compress_and_hash_partition "${RELEASE_IMAGE}" 8 "oem.gz")"
@@ -316,17 +515,12 @@
# Go to retrieve the factory test image.
pushd "${FACTORY_DIR}" >/dev/null
- prepare_dir
+ prepare_dir "."
- if [ -n "${IMAGE_IS_UNPACKED}" ]; then
- echo "Unpacking image ${FACTORY_IMAGE} ..." >&2
- sudo ./unpack_partitions.sh "${FACTORY_IMAGE}" 2>/dev/null
- fi
-
- test_hash="$(compress_and_hash_memento_image "${FACTORY_IMAGE}")"
- sudo chmod a+rw update.gz
- mv update.gz rootfs-test.gz
- mv rootfs-test.gz "${OMAHA_DATA_DIR}"
+ kernel="${FACTORY_IMAGE}:2"
+ rootfs="${FACTORY_IMAGE}:3"
+ test_hash="$(compress_and_hash_memento_image "$kernel" "$rootfs")"
+ mv ./update.gz "${OMAHA_DATA_DIR}/rootfs-test.gz"
echo "test: ${test_hash}"
state_hash="$(compress_and_hash_partition "${FACTORY_IMAGE}" 1 "state.gz")"
@@ -340,17 +534,25 @@
popd >/dev/null
if [ -n "${FLAGS_firmware_updater}" ]; then
- SHELLBALL="${FLAGS_firmware_updater}"
- if [ ! -f "$SHELLBALL" ]; then
- echo "Failed to find firmware updater: $SHELLBALL."
- exit 1
- fi
-
- firmware_hash="$(compress_and_hash_file "$SHELLBALL" "firmware.gz")"
+ firmware_hash="$(compress_and_hash_file "${FLAGS_firmware_updater}" \
+ "firmware.gz")"
mv firmware.gz "${OMAHA_DATA_DIR}"
echo "firmware: ${firmware_hash}"
fi
+ if [ -n "${FLAGS_hwid_updater}" ]; then
+ hwid_hash="$(compress_and_hash_file "${FLAGS_hwid_updater}" "hwid.gz")"
+ mv hwid.gz "${OMAHA_DATA_DIR}"
+ echo "hwid: ${hwid_hash}"
+ fi
+
+ if [ -n "${FLAGS_complete_script}" ]; then
+ complete_hash="$(compress_and_hash_file "${FLAGS_complete_script}" \
+ "complete.gz")"
+ mv complete.gz "${OMAHA_DATA_DIR}"
+ echo "complete: ${complete_hash}"
+ fi
+
# If the file does exist and we are using the subfolder flag we are going to
# append another config.
if [ -n "${FLAGS_subfolder}" ] &&
@@ -404,12 +606,24 @@
'firmware_checksum': '${firmware_hash}'," >>"${OMAHA_CONF}"
fi
+ if [ -n "${FLAGS_hwid_updater}" ] ; then
+ echo -n "
+ 'hwid_image': '${subfolder}hwid.gz',
+ 'hwid_checksum': '${hwid_hash}'," >>"${OMAHA_CONF}"
+ fi
+
+ if [ -n "${FLAGS_complete_script}" ] ; then
+ echo -n "
+ 'complete_image': '${subfolder}complete.gz',
+ 'complete_checksum': '${complete_hash}'," >>"${OMAHA_CONF}"
+ fi
+
echo -n "
},
]
" >>"${OMAHA_CONF}"
- echo "The miniomaha server lives in src/platform/dev.
+ info "The miniomaha server lives in src/platform/dev.
To validate the configutarion, run:
python2.6 devserver.py --factory_config miniomaha.conf \
--validate_factory_config
@@ -417,9 +631,94 @@
python2.6 devserver.py --factory_config miniomaha.conf"
}
-# Main
-if [ -n "$FLAGS_diskimg" ]; then
- generate_img
-else
- generate_omaha
-fi
+parse_and_run_config() {
+ # This function parses parameters from config file. Parameters can be put
+ # in sections and sections of parameters will be run in turn.
+ #
+ # Config file format:
+ # [section1]
+ # --param value
+ # --another_param # comment
+ #
+ # # some more comment
+ # [section2]
+ # --yet_another_param
+ #
+ # Note that a section header must start at the beginning of a line.
+ # And it's not allowed to read from config file recursively.
+
+ local config_file="$1"
+ local -a cmds
+ local cmd=""
+ local line
+
+ echo "Read parameters from: $config_file"
+ while read line; do
+ if [[ "$line" =~ ^\[.*] ]]; then
+ if [ -n "$cmd" ]; then
+ cmds+=("$cmd")
+ cmd=""
+ fi
+ continue
+ fi
+ line="${line%%#*}"
+ cmd="$cmd $line"
+ done < "$config_file"
+ if [ -n "$cmd" ]; then
+ cmds+=("$cmd")
+ fi
+
+ for cmd in "${cmds[@]}"
+ do
+ info "Executing: $0 $cmd"
+ # Sets internal environment variable MFP_SUBPROCESS to prevent unexpected
+ # recursion.
+ eval "MFP_SUBPROCESS=1 $0 $cmd"
+ done
+}
+
+main() {
+ set -e
+ trap on_exit EXIT
+
+ if [ -n "$FLAGS_config" ]; then
+ [ -z "$MFP_SUBPROCESS" ] ||
+ die "Recursively reading from config file is not allowed"
+
+ check_file_param FLAGS_config ""
+ check_empty_param FLAGS_release "when using config file"
+ check_empty_param FLAGS_factory "when using config file"
+ check_empty_param FLAGS_firmware_updater "when using config file"
+ check_empty_param FLAGS_hwid_updater "when using config file"
+ check_empty_param FLAGS_install_shim "when using config file"
+ check_empty_param FLAGS_complete_script "when using config file"
+ check_empty_param FLAGS_usbimg "when using config file"
+ check_empty_param FLAGS_diskimg "when using config file"
+ check_empty_param FLAGS_subfolder "when using config file"
+
+ # Make the path and folder of config file available when parsing config.
+ # These MFP_CONFIG_* are special shell variables (not environment variables)
+ # that a config file (by --config) can use them.
+ MFP_CONFIG_PATH="$(readlink -f "$FLAGS_config")"
+ MFP_CONFIG_DIR="$(dirname "$MFP_CONFIG_PATH")"
+
+ parse_and_run_config "$FLAGS_config"
+ exit
+ fi
+
+ check_parameters
+ setup_environment
+ if [ "$FLAGS_detect_release_image" = "$FLAGS_TRUE" ]; then
+ prepare_release_image "$FLAGS_release"
+ fi
+
+ if [ -n "$FLAGS_usbimg" ]; then
+ generate_usbimg
+ elif [ -n "$FLAGS_diskimg" ]; then
+ generate_img
+ else
+ generate_omaha
+ fi
+}
+
+main "$@"
diff --git a/make_universal_factory_shim.sh b/make_universal_factory_shim.sh
new file mode 100755
index 0000000..9b03218
--- /dev/null
+++ b/make_universal_factory_shim.sh
@@ -0,0 +1,298 @@
+#!/bin/sh
+
+# Copyright (c) 2011 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.
+
+# Script to generate an universal factory install shim image, by merging
+# multiple images signed by different keys.
+# CAUTION: Recovery shim images are not supported yet because they require the
+# kernel partitions to be laid out in a special way
+
+# --- BEGIN FACTORY SCRIPTS BOILERPLATE ---
+# This script may be executed in a full CrOS source tree or an extracted factory
+# bundle with limited tools, so we must always load scripts from $SCRIPT_ROOT
+# and search for binary programs in $SCRIPT_ROOT/../bin
+
+SCRIPT="$(readlink -f "$0")"
+SCRIPT_ROOT="$(dirname "$SCRIPT")"
+. "$SCRIPT_ROOT/lib/cros_image_common.sh" || exit 1
+image_find_tool "cgpt" "$SCRIPT_ROOT/../bin"
+# --- END FACTORY SCRIPTS BOILERPLATE ---
+
+# CGPT Header: PMBR, header, table; sec_table, sec_header
+CGPT_START_SIZE=$((1 + 1 + 32))
+CGPT_END_SIZE=$((32 + 1))
+CGPT_BS="512"
+
+# Alignment of partition sectors
+PARTITION_SECTOR_ALIGNMENT=256
+
+LAYOUT_FILE="$(mktemp --tmpdir)"
+
+RESERVED_PARTITION="10"
+LEGACY_PARTITIONS="10 11 12" # RESERVED, RWFW, EFI
+MAX_INPUT_SOURCES=4 # (2~9) / 2
+
+alert() {
+ echo "$*" >&2
+}
+
+die() {
+ alert "ERROR: $*"
+ exit 1
+}
+
+on_exit() {
+ rm -f "$LAYOUT_FILE"
+}
+
+# Returns offset aligned to alignment.
+# If size is given, only align if size >= alignment.
+image_alignment() {
+ local offset="$1"
+ local alignment="$2"
+ local size="$3"
+
+ # If size is assigned, align only if the new size is larger then alignment.
+ if [ "$((offset % alignment))" != "0" ]; then
+ if [ -z "$size" -o "$size" -ge "$alignment" ]; then
+ offset=$((offset + alignment - (offset % alignment)))
+ fi
+ fi
+ echo "$((offset))"
+}
+
+# Processes a logical disk image layout description file.
+# Each entry in layout is a "file:partnum" entry (:partnum is optional),
+# referring to the #partnum partition in file.
+# The index starts at one, referring to the first partition in layout.
+image_process_layout() {
+ local layout_file="$1"
+ local callback="$2"
+ shift
+ shift
+ local param="$@"
+ local index=0
+
+ while read layout; do
+ local image_file="${layout%:*}"
+ local part_num="${layout#*:}"
+ index="$((index + 1))"
+ [ "$image_file" != "$layout" ] || part_num=""
+
+ "$callback" "$image_file" "$part_num" "$index" "$param"
+ done <"$layout_file"
+}
+
+# Processes a list of disk geometry sectors into aligned (offset, sectors) form.
+# The index starts at zero, referring to the partition table object itself.
+image_process_geometry() {
+ local sectors_list="$1"
+ local callback="$2"
+ shift
+ shift
+ local param="$@"
+ local offset=0 sectors
+ local index=0
+
+ for sectors in $sectors_list; do
+ offset="$(image_alignment $offset $PARTITION_SECTOR_ALIGNMENT $sectors)"
+ "$callback" "$offset" "$sectors" "$index" "$param"
+ offset="$((offset + sectors))"
+ index="$((index + 1))"
+ done
+}
+
+# Callback of image_process_layout. Returns the size (in sectors) of given
+# object (partition in image or file).
+layout_get_sectors() {
+ local image_file="$1"
+ local part_num="$2"
+
+ if [ -n "$part_num" ]; then
+ image_part_size "$image_file" "$part_num"
+ else
+ image_alignment "$(stat -c"%s" "$image_file")" $CGPT_BS ""
+ fi
+}
+
+# Callback of image_process_layout. Copies an input source object (file or
+# partition) into specified partition on output file.
+layout_copy_partition() {
+ local input_file="$1"
+ local input_part="$2"
+ local output_part="$3"
+ local output_file="$4"
+ alert "$(basename "$input_file"):$input_part =>" \
+ "$(basename "$output_file"):$output_part"
+
+ if [ -n "$part_num" ]; then
+ # TODO(hungte) update partition type if available
+ image_partition_copy "$input_file" "$input_part" \
+ "$output_file" "$output_part"
+ # Update partition type information
+ local partition_type="$(cgpt show -q -n -t -i "$input_part" "$input_file")"
+ local partition_attr="$(cgpt show -q -n -A -i "$input_part" "$input_file")"
+ local partition_label="$(cgpt show -q -n -l -i "$input_part" "$input_file")"
+ cgpt add -t "$partition_type" -l "$partition_label" -A "$partition_attr" \
+ -i "$output_part" "$output_file"
+ else
+ image_update_partition "$output_file" "$output_part" <"$input_file"
+ fi
+}
+
+
+# Callback of image_process_geometry. Creates a partition by give offset,
+# size(sectors), and index.
+geometry_create_partition() {
+ local offset="$1"
+ local sectors="$2"
+ local index="$3"
+ local output_file="$4"
+
+ if [ "$offset" = "0" ]; then
+ # first entry is CGPT; ignore.
+ return
+ fi
+ cgpt add -b $offset -s $sectors -i $index -t reserved "$output_file"
+}
+
+# Callback of image_process_geometry. Prints the proper offset of current
+# partition by give offset and size.
+geometry_get_partition_offset() {
+ local offset="$1"
+ local sectors="$2"
+ local index="$3"
+
+ image_alignment "$offset" "$PARTITION_SECTOR_ALIGNMENT" "$sectors"
+}
+
+build_image_file() {
+ local layout_file="$1"
+ local output_file="$2"
+ local output_file_size=0
+ local sectors_list partition_offsets
+
+ # Check and obtain size information from input sources
+ sectors_list="$(image_process_layout "$layout_file" layout_get_sectors)"
+
+ # Calculate output image file size
+ partition_offsets="$(image_process_geometry \
+ "$CGPT_START_SIZE $sectors_list $CGPT_END_SIZE 1" \
+ geometry_get_partition_offset)"
+ output_file_size="$(echo "$partition_offsets" | tail -n 1)"
+
+ # Create empty image file
+ truncate -s "0" "$output_file" # starting with a new file is much faster.
+ truncate -s "$((output_file_size * CGPT_BS))" "$output_file"
+
+ # Initialize partition table (GPT)
+ cgpt create "$output_file"
+ cgpt boot -p "$output_file" >/dev/null
+
+ # Create partition tables
+ image_process_geometry "$CGPT_START_SIZE $sectors_list" \
+ geometry_create_partition \
+ "$output_file"
+ # Copy partitions content
+ image_process_layout "$layout_file" layout_copy_partition "$output_file"
+}
+
+# Creates standard multiple image layout
+create_standard_layout() {
+ local main_source="$1"
+ local layout_file="$2"
+ local image index
+ shift
+ shift
+
+ for image in "$main_source" "$@"; do
+ if [ ! -f "$image" ]; then
+ die "Cannot find input file $image."
+ fi
+ done
+
+ echo "$main_source:1" >>"$layout_file" # stateful partition
+ for index in $(seq 1 $MAX_INPUT_SOURCES); do
+ local kernel_source="$main_source:$RESERVED_PARTITION"
+ local rootfs_source="$main_source:$RESERVED_PARTITION"
+ if [ "$#" -gt 0 ]; then
+ # TODO(hungte) detect if input source is a recovery/USB image
+ kernel_source="$1:2"
+ rootfs_source="$1:3"
+ shift
+ fi
+ echo "$kernel_source" >>"$layout_file"
+ echo "$rootfs_source" >>"$layout_file"
+ done
+ for index in $LEGACY_PARTITIONS; do
+ echo "$main_source:$index" >>"$LAYOUT_FILE"
+ done
+}
+
+usage_die() {
+ alert "Usage: $SCRIPT [-m master] [-f] output shim1 [shim2 ... shim4]"
+ alert " or $SCRIPT -l layout [-f] output"
+ exit 1
+}
+
+main() {
+ local force=""
+ local image=""
+ local output=""
+ local main_source=""
+ local index=""
+ local slots="0"
+ local layout_mode=""
+
+ while [ "$#" -gt 1 ]; do
+ case "$1" in
+ "-f" )
+ force="True"
+ shift
+ ;;
+ "-m" )
+ main_source="$2"
+ shift
+ shift
+ ;;
+ "-l" )
+ cat "$2" >"$LAYOUT_FILE"
+ layout_mode="TRUE"
+ shift
+ shift
+ ;;
+ * )
+ break
+ esac
+ done
+
+ if [ -n "$layout_mode" ]; then
+ [ "$#" = 1 ] || usage_die
+ elif [ "$#" -lt 2 -o "$#" -gt "$((MAX_INPUT_SOURCES + 1))" ]; then
+ alert "ERROR: invalid number of parameters ($#)."
+ usage_die
+ fi
+
+ if [ -z "$main_source" ]; then
+ main_source="$2"
+ fi
+ output="$1"
+ shift
+
+ if [ -f "$output" -a -z "$force" ]; then
+ die "Output file $output already exists. To overwrite the file, add -f."
+ fi
+
+ if [ -z "$layout_mode" ]; then
+ create_standard_layout "$main_source" "$LAYOUT_FILE" "$@"
+ fi
+ build_image_file "$LAYOUT_FILE" "$output"
+ echo ""
+ echo "Image created: $output"
+}
+
+set -e
+trap on_exit EXIT
+main "$@"
diff --git a/mk_memento_images.sh b/mk_memento_images.sh
index 9e8b609..4c76715 100755
--- a/mk_memento_images.sh
+++ b/mk_memento_images.sh
@@ -1,6 +1,6 @@
#!/bin/bash
-# Copyright (c) 2009 The Chromium OS Authors. All rights reserved.
+# Copyright (c) 2011 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.
@@ -8,49 +8,37 @@
# build_image.sh and generates an image that can be used for auto
# update.
+# --- BEGIN FACTORY SCRIPTS BOILERPLATE ---
+# This script may be executed in a full CrOS source tree or an extracted factory
+# bundle with limited tools, so we must always load scripts from $SCRIPT_ROOT
+# and search for binary programs in $SCRIPT_ROOT/../bin
+
+SCRIPT="$(readlink -f "$0")"
+SCRIPT_ROOT="$(dirname "$SCRIPT")"
+. "$SCRIPT_ROOT/lib/cros_image_common.sh" || exit 1
+image_find_tool "cgpt" "$SCRIPT_ROOT/../bin"
+# --- END FACTORY SCRIPTS BOILERPLATE ---
+
set -e
+# We need 2-3 non-zero parameters.
+if [ "$#" -lt 2 ] || [ "$#" -gt 3 ] || [ -z "$1" ] || [ -z "$2" ]; then
+ echo "
+Usage: $0 kernel_partition_img[:index] rootfs_partition_img[:index] [output_dir]
-# --- BEGIN COMMON.SH BOILERPLATE ---
-# Load common CrOS utilities. Inside the chroot this file is installed in
-# /usr/lib/crosutils. Outside the chroot we find it relative to the script's
-# location.
-find_common_sh() {
- local common_paths=(/usr/lib/crosutils $(dirname "$(readlink -f "$0")"))
- local path
+ Input parameters may be either a simple partition image file, or a
+ disk image file name followed by ':' and target partition index number
- SCRIPT_ROOT=
- for path in "${common_paths[@]}"; do
- if [ -r "${path}/common.sh" ]; then
- SCRIPT_ROOT=${path}
- break
- fi
- done
-}
+ If output_dir is omitted, the folder of kernel_partition_img will be
+ use.
-find_common_sh
-. "${SCRIPT_ROOT}/common.sh" || (echo "Unable to load common.sh" && exit 1)
-# --- END COMMON.SH BOILERPLATE ---
-
-. "${SCRIPT_ROOT}/common.sh" || (echo "Unable to load common.sh" && exit 1)
-
-# Load functions designed for image processing
-if ! . "${SCRIPT_ROOT}/lib/cros_image_common.sh"; then
- echo "ERROR: Cannot load required library: lib/cros_image_common.sh; Abort."
+Examples:
+ $0 part_2 part_3
+ $0 chromiumos_image.bin:2 part3
+ $0 chromiumos_image.bin:2 otherimage.bin:3 /tmp/myoutput
+ "
exit 1
fi
-if [ -z "$2" -o -z "$1" ] || [ "${#@}" -ne 2 -a "${#@}" -ne 3 ]; then
- 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
-fi
-
-if [ "$CROS_GENERATE_UPDATE_PAYLOAD_CALLED" != "1" ]; then
- echo "WARNING:"
- 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."
-fi
-
if ! image_has_command pigz; then
(echo "WARNING:"
echo " Your system does not have pigz (parallel gzip) installed."
@@ -66,31 +54,46 @@
echo "running $0 as root which is unneccessary"
fi
-# Determine the offset size, and file name of parameters
-if [ -z "$3" ]; then
- # kernnel_img rootfs_img
- 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="$(image_part_offset "$KPART" "$2")" ||
- image_die "cannot retieve kernel partition offset"
- KPART_SECTORS="$(image_part_size "$KPART" "$2")" ||
- image_die "cannot retieve kernel partition size"
- ROOT_OFFSET="$(image_part_offset "$ROOT_PART" "$3")" ||
- image_die "cannot retieve root partition offset"
- ROOT_SECTORS="$(image_part_size "$ROOT_PART" "$3")" ||
- image_die "cannot retieve root partition size"
- KPART_SIZE=$((KPART_SECTORS * 512))
-fi
+# Usage: load_partition_file VARIABLE_NAME_PREFIX partition_file
+# Writes VARIABLE_NAME_PREFIX[, _OFFSE, _SIZE, _SECTORS] by parsing
+# partition_file, which can be a simple file or image:partno.
+load_partition_file() {
+ local var_prefix="$1"
+ local var="$2"
+ local var_offset=""
+ local var_size=""
+ local var_sectors=""
+ local part_no="${var##*:}"
+
+ # test if var is in image:partno format.
+ if [ "$part_no" != "$var" ]; then
+ var="${var%:*}"
+ else
+ part_no=""
+ fi
+
+ if [ -z "$part_no" ]; then
+ var_offset=0
+ var_size="$(stat -c"%s" "$var")" ||
+ image_die "Invalid file: $var"
+ var_sectors="$((var_size / 512))"
+ else
+ var_offset="$(image_part_offset "$var" "$part_no")" ||
+ image_die "Cannot retieve offset for partition $var:$part_no"
+ var_sectors="$(image_part_size "$var" "$part_no")" ||
+ image_die "Cannot retieve size for partition $var:$part_no"
+ var_size=$((var_sectors * 512))
+ fi
+
+ # publish the values
+ eval "${var_prefix}"="$var"
+ eval "${var_prefix}_OFFSET"="$var_offset"
+ eval "${var_prefix}_SIZE"="$var_size"
+ eval "${var_prefix}_SECTORS"="$var_sectors"
+}
+
+load_partition_file KPART "$1"
+load_partition_file ROOTFS "$2"
# Sanity check size.
if [ "$KPART_SIZE" -gt $((16 * 1024 * 1024)) ]; then
@@ -99,7 +102,13 @@
exit 1
fi
-FINAL_OUT_FILE=$(dirname "$1")/update.gz
+if [ "$#" = "3" ]; then
+ FINAL_OUT_DIR="$(readlink -f $3)"
+else
+ FINAL_OUT_DIR="$(dirname "$(readlink -f $1)")"
+fi
+FINAL_OUT_FILE="$FINAL_OUT_DIR/update.gz"
+echo "Output: $FINAL_OUT_FILE"
# Update payload format:
# [kernel_size: big-endian uint64][kernel_blob][rootfs_blob]
@@ -116,7 +125,7 @@
echo "Compressing kernel..." >&2
image_dump_partial_file "$KPART" "$KPART_OFFSET" "$KPART_SECTORS"
echo "Compressing rootfs..." >&2
- image_dump_partial_file "$ROOT_PART" "$ROOT_OFFSET" "$ROOT_SECTORS") |
+ image_dump_partial_file "$ROOTFS" "$ROOTFS_OFFSET" "$ROOTFS_SECTORS") |
image_gzip_compress -c -9 |
tee "$FINAL_OUT_FILE" |
openssl sha1 -binary |