mirror of
https://github.com/flatcar/scripts.git
synced 2025-08-09 05:56:58 +02:00
The last changes to the installer broke image_to_vm. Since image_to_vm doesn't need to run postinst, but just change the active image, it now just calls chromeos-setimage and passes in its needed flags (already supported by the last installer change). It will also detect whether the image was built with verification of the rootfs enabled or not by looking at the default.cfg file. Also updates the location of where you should run the command in build_image. TEST=built a new image with --enable_rootfs_verification and started with qemu -curses -hda [output]; did the same without verification BUG=chromium-os:2963 Review URL: http://codereview.chromium.org/3034030 Change-Id: I265d6ad8971f9427b78cc07d784fced9cceb5974
775 lines
27 KiB
Bash
Executable File
775 lines
27 KiB
Bash
Executable File
#!/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.
|
|
|
|
# Script to build a bootable keyfob-based chromeos system image from within
|
|
# a chromiumos setup. This assumes that all needed packages have been built into
|
|
# the given target's root with binary packages turned on. This script will
|
|
# build the Chrome OS image using only pre-built binary packages.
|
|
|
|
# Load common constants. This should be the first executable line.
|
|
# The path to common.sh should be relative to your script's location.
|
|
. "$(dirname "$0")/common.sh"
|
|
|
|
. "$(dirname "$0")/chromeos-common.sh" # for partoffset
|
|
locate_gpt
|
|
|
|
# Script must be run inside the chroot.
|
|
restart_in_chroot_if_needed $*
|
|
|
|
get_default_board
|
|
|
|
# Flags.
|
|
DEFINE_string board "${DEFAULT_BOARD}" \
|
|
"The board to build an image for."
|
|
DEFINE_string build_root "/build" \
|
|
"The root location for board sysroots."
|
|
DEFINE_integer build_attempt 1 \
|
|
"The build attempt for this image build."
|
|
DEFINE_string output_root "${DEFAULT_BUILD_ROOT}/images" \
|
|
"Directory in which to place image result directories (named by version)"
|
|
DEFINE_boolean replace ${FLAGS_FALSE} \
|
|
"Overwrite existing output, if any."
|
|
DEFINE_boolean withdev ${FLAGS_TRUE} \
|
|
"Include useful developer friendly utilities in the image."
|
|
DEFINE_boolean installmask ${FLAGS_TRUE} \
|
|
"Use INSTALL_MASK to shrink the resulting image."
|
|
DEFINE_integer jobs -1 \
|
|
"How many packages to build in parallel at maximum."
|
|
DEFINE_boolean statefuldev ${FLAGS_TRUE} \
|
|
"Install development packages on stateful partition rather than the rootfs"
|
|
DEFINE_string to "" \
|
|
"The target image file or device"
|
|
DEFINE_boolean factory_install ${FLAGS_FALSE} \
|
|
"Build a smaller image to overlay the factory install shim on; this argument \
|
|
is also required in image_to_usb."
|
|
DEFINE_string arm_extra_bootargs "" \
|
|
"Additional command line options to pass to the ARM kernel."
|
|
DEFINE_integer rootfs_partition_size 1024 \
|
|
"rootfs parition size in MBs."
|
|
DEFINE_integer rootfs_size 720 \
|
|
"rootfs filesystem size in MBs."
|
|
# ceil(0.1 * rootfs_size) is a good minimum.
|
|
DEFINE_integer rootfs_hash_pad 8 \
|
|
"MBs reserved at the end of the rootfs image."
|
|
DEFINE_integer statefulfs_size 1024 \
|
|
"stateful filesystem size in MBs."
|
|
DEFINE_boolean preserve ${FLAGS_FALSE} \
|
|
"Attempt to preserve the previous build image if one can be found (unstable, \
|
|
kernel/firmware not updated)"
|
|
DEFINE_boolean fast ${FLAGS_FALSE} \
|
|
"Call many emerges in parallel (unstable)"
|
|
|
|
DEFINE_string usb_disk /dev/sdb3 \
|
|
"Path syslinux should use to do a usb boot. Default: /dev/sdb3"
|
|
|
|
DEFINE_boolean enable_rootfs_verification ${FLAGS_FALSE} \
|
|
"Default all bootloaders to use kernel-based root fs integrity checking."
|
|
DEFINE_integer verity_error_behavior 2 \
|
|
"Kernel verified boot error behavior (0: I/O errors, 1: reboot, 2: nothing) \
|
|
Default: 2"
|
|
DEFINE_integer verity_depth 1 \
|
|
"Kernel verified boot hash tree depth. Default: 1"
|
|
DEFINE_integer verity_max_ios 1024 \
|
|
"Number of outstanding I/O operations dm-verity caps at. Default: 1024"
|
|
DEFINE_string verity_algorithm "sha1" \
|
|
"Cryptographic hash algorithm used for kernel vboot. Default : sha1"
|
|
|
|
DEFINE_string oem_customization \
|
|
"$GCLIENT_ROOT/src/platform/assets/oem_customization" \
|
|
"Path to directory containing OEM partner partition contents"
|
|
|
|
# Parse command line.
|
|
FLAGS "$@" || exit 1
|
|
eval set -- "${FLAGS_ARGV}"
|
|
|
|
# Only now can we die on error. shflags functions leak non-zero error codes,
|
|
# so will die prematurely if 'set -e' is specified before now.
|
|
set -e
|
|
|
|
if [ -z "${FLAGS_board}" ] ; then
|
|
error "--board is required."
|
|
exit 1
|
|
fi
|
|
|
|
if [ "$((FLAGS_rootfs_size + FLAGS_rootfs_hash_pad))" -gt \
|
|
"${FLAGS_rootfs_partition_size}" ] ; then
|
|
error "rootfs ($((FLAGS_rootfs_size + FLAGS_rootfs_hash_pad)) MB) is \
|
|
bigger than partition (${FLAGS_rootfs_partition_size} MB)."
|
|
exit 1
|
|
fi
|
|
|
|
EMERGE_BOARD_CMD="emerge-${FLAGS_board}"
|
|
if [ "${FLAGS_fast}" -eq "${FLAGS_TRUE}" ]; then
|
|
echo "Using alternate emerge"
|
|
EMERGE_BOARD_CMD="${SCRIPTS_DIR}/parallel_emerge --board=${FLAGS_board}"
|
|
fi
|
|
|
|
# Determine build version.
|
|
. "${SCRIPTS_DIR}/chromeos_version.sh"
|
|
|
|
# Use canonical path since some tools (e.g. mount) do not like symlinks.
|
|
# Append build attempt to output directory.
|
|
IMAGE_SUBDIR="${CHROMEOS_VERSION_STRING}-a${FLAGS_build_attempt}"
|
|
OUTPUT_DIR="${FLAGS_output_root}/${FLAGS_board}/${IMAGE_SUBDIR}"
|
|
|
|
OUTSIDE_OUTPUT_DIR="../build/images/${FLAGS_board}/${IMAGE_SUBDIR}"
|
|
|
|
# If we are creating a developer image, also create a pristine image with a
|
|
# different name.
|
|
DEVELOPER_IMAGE_NAME=
|
|
PRISTINE_IMAGE_NAME=chromiumos_image.bin
|
|
if [ "${FLAGS_withdev}" -eq "${FLAGS_TRUE}" ]; then
|
|
PRISTINE_IMAGE_NAME=chromiumos_base_image.bin
|
|
DEVELOPER_IMAGE_NAME=chromiumos_image.bin
|
|
fi
|
|
|
|
PRISTINE_IMG="${OUTPUT_DIR}/${PRISTINE_IMAGE_NAME}"
|
|
DEVELOPER_IMG="${OUTPUT_DIR}/${DEVELOPER_IMAGE_NAME}"
|
|
|
|
BOARD="${FLAGS_board}"
|
|
BOARD_ROOT="${FLAGS_build_root}/${BOARD}"
|
|
|
|
ROOT_FS_IMG="${OUTPUT_DIR}/rootfs.image"
|
|
ROOT_FS_DIR="${OUTPUT_DIR}/rootfs"
|
|
ROOT_FS_HASH="${OUTPUT_DIR}/rootfs.hash"
|
|
|
|
STATEFUL_FS_IMG="${OUTPUT_DIR}/stateful_partition.image"
|
|
STATEFUL_FS_DIR="${OUTPUT_DIR}/stateful_partition"
|
|
|
|
OEM_FS_IMG="${OUTPUT_DIR}/partner_partition.image"
|
|
OEM_FS_DIR="${OUTPUT_DIR}/partner_partition"
|
|
|
|
ESP_FS_IMG=${OUTPUT_DIR}/esp.image
|
|
ESP_FS_DIR=${OUTPUT_DIR}/esp
|
|
|
|
LOOP_DEV=
|
|
STATEFUL_LOOP_DEV=
|
|
OEM_LOOP_DEV=
|
|
ESP_LOOP_DEV=
|
|
|
|
# ${DEV_IMAGE_ROOT} specifies the location of where developer packages will
|
|
# be installed on the stateful dir. On a Chromium OS system, this will
|
|
# translate to /usr/local.
|
|
DEV_IMAGE_ROOT="${STATEFUL_FS_DIR}/dev_image"
|
|
|
|
# What cross-build are we targeting?
|
|
. "${BOARD_ROOT}/etc/make.conf.board_setup"
|
|
LIBC_VERSION=${LIBC_VERSION:-"2.10.1-r1"}
|
|
|
|
INSTALL_MASK=""
|
|
if [[ ${FLAGS_installmask} -eq ${FLAGS_TRUE} ]] ; then
|
|
INSTALL_MASK="${DEFAULT_INSTALL_MASK}"
|
|
fi
|
|
|
|
# Reduce the size of factory install shim.
|
|
# TODO: Build a separated ebuild for the factory install shim to reduce size.
|
|
if [[ ${FLAGS_factory_install} -eq ${FLAGS_TRUE} ]] ; then
|
|
INSTALL_MASK="${INSTALL_MASK} ${FACTORY_INSTALL_MASK}"
|
|
fi
|
|
|
|
if [[ ${FLAGS_jobs} -ne -1 ]]; then
|
|
EMERGE_JOBS="--jobs=${FLAGS_jobs}"
|
|
fi
|
|
|
|
# Figure out ARCH from the given toolchain.
|
|
# TODO: Move to common.sh as a function after scripts are switched over.
|
|
TC_ARCH=$(echo "${CHOST}" | awk -F'-' '{ print $1 }')
|
|
case "${TC_ARCH}" in
|
|
arm*)
|
|
ARCH="arm"
|
|
;;
|
|
*86)
|
|
ARCH="x86"
|
|
;;
|
|
*)
|
|
error "Unable to determine ARCH from toolchain: ${CHOST}"
|
|
exit 1
|
|
esac
|
|
|
|
# Hack to fix bug where x86_64 CHOST line gets incorrectly added.
|
|
# ToDo(msb): remove this hack.
|
|
PACKAGES_FILE="${BOARD_ROOT}/packages/Packages"
|
|
sudo sed -e "s/CHOST: x86_64-pc-linux-gnu//" -i "${PACKAGES_FILE}"
|
|
|
|
# Handle existing directory.
|
|
if [[ -e "${OUTPUT_DIR}" ]]; then
|
|
if [[ ${FLAGS_replace} -eq ${FLAGS_TRUE} ]]; then
|
|
sudo rm -rf "${OUTPUT_DIR}"
|
|
else
|
|
echo "Directory ${OUTPUT_DIR} already exists."
|
|
echo "Use --build_attempt option to specify an unused attempt."
|
|
echo "Or use --replace if you want to overwrite this directory."
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# Find previous build, if any...
|
|
PREVIOUS_DIR=$($SCRIPTS_DIR/get_latest_image.sh --board="$BOARD")
|
|
|
|
cleanup_rootfs_loop() {
|
|
sudo umount -d "${ROOT_FS_DIR}"
|
|
}
|
|
|
|
cleanup_stateful_fs_loop() {
|
|
sudo umount "${ROOT_FS_DIR}/usr/local"
|
|
sudo umount "${ROOT_FS_DIR}/var"
|
|
sudo umount -d "${STATEFUL_FS_DIR}"
|
|
}
|
|
|
|
cleanup_oem_fs_loop() {
|
|
sudo umount -d "${OEM_FS_DIR}"
|
|
}
|
|
|
|
|
|
cleanup_esp_loop() {
|
|
sudo umount -d "${ESP_FS_DIR}"
|
|
}
|
|
|
|
cleanup() {
|
|
# Disable die on error.
|
|
set +e
|
|
|
|
if [[ -n "${STATEFUL_LOOP_DEV}" ]]; then
|
|
cleanup_stateful_fs_loop
|
|
STATEFUL_LOOP_DEV=
|
|
fi
|
|
|
|
if [[ -n "${OEM_LOOP_DEV}" ]]; then
|
|
cleanup_oem_fs_loop
|
|
fi
|
|
|
|
if [[ -n "${LOOP_DEV}" ]]; then
|
|
cleanup_rootfs_loop
|
|
LOOP_DEV=
|
|
fi
|
|
|
|
if [[ -n "${ESP_LOOP_DEV}" ]]; then
|
|
cleanup_esp_loop
|
|
ESP_LOOP_DEV=
|
|
fi
|
|
|
|
# Turn die on error back on.
|
|
set -e
|
|
}
|
|
|
|
delete_prompt() {
|
|
echo "An error occurred in your build so your latest output directory" \
|
|
"is invalid."
|
|
read -p "Would you like to delete the output directory (y/N)? " SURE
|
|
SURE="${SURE:0:1}" # Get just the first character.
|
|
if [ "${SURE}" == "y" ] ; then
|
|
sudo rm -rf "${OUTPUT_DIR}"
|
|
echo "Deleted ${OUTPUT_DIR}"
|
|
else
|
|
echo "Not deleting ${OUTPUT_DIR}. Note dev server updates will not work" \
|
|
"until you successfully build another image or delete this directory"
|
|
fi
|
|
}
|
|
|
|
# $1 - Directory where developer rootfs is mounted.
|
|
# $2 - Directory where developer stateful_partition is mounted.
|
|
# $3 - Directory where the ESP partition is mounted.
|
|
mount_gpt_cleanup() {
|
|
local rootfs="${1-$ROOT_FS_DIR}"
|
|
local statefs="${2-$STATEFUL_FS_DIR}"
|
|
local espfs="${3-$ESP_FS_DIR}"
|
|
"${SCRIPTS_DIR}/mount_gpt_image.sh" \
|
|
-u -r "${rootfs}" -s "${statefs}" -e "${espfs}"
|
|
delete_prompt
|
|
}
|
|
|
|
make_image_bootable() {
|
|
local image_name="$1"
|
|
cros_root=/dev/sd%D%P
|
|
if [[ "${ARCH}" = "arm" ]]; then
|
|
# TODO(wad) assumed like in build_gpt for now.
|
|
cros_root=/dev/mmcblk1p3
|
|
fi
|
|
if [[ ${FLAGS_enable_rootfs_verification} -eq ${FLAGS_TRUE} ]]; then
|
|
cros_root=/dev/dm-0
|
|
fi
|
|
|
|
# TODO(wad) mount the root fs to LOOP_DEV from the image
|
|
trap "mount_gpt_cleanup" EXIT
|
|
${SCRIPTS_DIR}/mount_gpt_image.sh --from "${OUTPUT_DIR}" \
|
|
--image "${image_name}" -r "${ROOT_FS_DIR}" \
|
|
-s "${STATEFUL_FS_DIR}"
|
|
|
|
# The rootfs should never be mounted rw again after this point without
|
|
# re-calling make_image_bootable.
|
|
sudo mount -o remount,ro "${ROOT_FS_DIR}"
|
|
root_dev=$(mount | grep -- "on ${ROOT_FS_DIR} type" | cut -f1 -d' ' | tail -1)
|
|
|
|
DEVKEYSDIR="/usr/share/vboot/devkeys"
|
|
|
|
# Builds the kernel partition image. The temporary files are kept around
|
|
# so that we can perform a load_kernel_test later on the final image.
|
|
${SCRIPTS_DIR}/build_kernel_image.sh \
|
|
--arch="${ARCH}" \
|
|
--to="${OUTPUT_DIR}/vmlinuz.image" \
|
|
--hd_vblock="${OUTPUT_DIR}/vmlinuz_hd.vblock" \
|
|
--vmlinuz="${OUTPUT_DIR}/boot/vmlinuz" \
|
|
--working_dir="${OUTPUT_DIR}" \
|
|
--keep_work \
|
|
--rootfs_image=${root_dev} \
|
|
--rootfs_hash=${ROOT_FS_HASH} \
|
|
--verity_hash_alg=${FLAGS_verity_algorithm} \
|
|
--verity_tree_depth=${FLAGS_verity_depth} \
|
|
--verity_max_ios=${FLAGS_verity_max_ios} \
|
|
--verity_error_behavior=${FLAGS_verity_error_behavior} \
|
|
--root=${cros_root} \
|
|
--keys_dir="${DEVKEYSDIR}"
|
|
|
|
local rootfs_hash_size=$(stat -c '%s' ${ROOT_FS_HASH})
|
|
info "Appending rootfs.hash (${rootfs_hash_size} bytes) to the root fs"
|
|
if [[ ${rootfs_hash_size} -gt $((FLAGS_rootfs_hash_pad * 1024 * 1024)) ]]
|
|
then
|
|
die "--rootfs_hash_pad reserves less than the needed ${rootfs_hash_size}"
|
|
fi
|
|
# Unfortunately, mount_gpt_image uses mount and not losetup to create the
|
|
# loop devices. This means that they are not the correct size. We have to
|
|
# write directly to the image to append the hash tree data.
|
|
local hash_offset="$(partoffset ${OUTPUT_DIR}/${image_name} 3)"
|
|
hash_offset=$((hash_offset + ((1024 * 1024 * ${FLAGS_rootfs_size}) / 512)))
|
|
sudo dd bs=512 \
|
|
seek=${hash_offset} \
|
|
if="${ROOT_FS_HASH}" \
|
|
of="${OUTPUT_DIR}/${image_name}" \
|
|
conv=notrunc
|
|
# We don't need to keep the file around anymore.
|
|
sudo rm "${ROOT_FS_HASH}"
|
|
|
|
# Move the verification block needed for the hard disk install to the
|
|
# stateful partition. Mount stateful fs, copy file, and umount fs.
|
|
# In original CL: http://codereview.chromium.org/2868044, this was done in
|
|
# create_base_image(). However, it could break the build if it is a clean
|
|
# build because vmlinuz_hd.vblock hasn't been created by build_kernel_image.sh
|
|
if [[ "${ARCH}" = "x86" ]]; then
|
|
sudo cp "${OUTPUT_DIR}/vmlinuz_hd.vblock" "${STATEFUL_FS_DIR}"
|
|
fi
|
|
|
|
# START_KERN_A is set by the first call to install the gpt.
|
|
local koffset="$(partoffset ${OUTPUT_DIR}/${image_name} 2)"
|
|
sudo dd if="${OUTPUT_DIR}/vmlinuz.image" of="${OUTPUT_DIR}/${image_name}" \
|
|
conv=notrunc bs=512 seek=${koffset}
|
|
|
|
# Update the bootloaders. For legacy/efi x86, the EFI system partition
|
|
# will be updated and for arm, the mbr will be updated (for u-boot).
|
|
local kernel_part=
|
|
local bootloader_to=
|
|
local bootloader_to_flags=
|
|
local usb_disk="${FLAGS_usb_disk}"
|
|
|
|
if [[ "${ARCH}" = "x86" ]]; then
|
|
# x86 should update the esp in place in the image.
|
|
bootloader_to="${OUTPUT_DIR}/${image_name}"
|
|
local esp_offset="$(partoffset ${OUTPUT_DIR}/${image_name} 12)"
|
|
esp_offset=$((esp_offset * 512)) # sectors to bytes
|
|
local esp_size="$(partsize ${OUTPUT_DIR}/${image_name} 12)"
|
|
esp_size=$((esp_size * 512)) # sectors to bytes
|
|
bootloader_to_flags="--to_offset=${esp_offset} --to_size=${esp_size}"
|
|
# Use the kernel partition to acquire configuration flags.
|
|
kernel_part="--kernel_partition='${OUTPUT_DIR}/vmlinuz.image'"
|
|
# Install syslinux on the EFI System Partition.
|
|
kernel_part="${kernel_part} --install_syslinux"
|
|
elif [[ "${ARCH}" = "arm" ]]; then
|
|
# TODO(wad) mmcblk1p3 is hardcoded for arm for now!
|
|
usb_disk="/dev/mmcblk1p3"
|
|
# ARM doesn't support using the kernel image for kernel cmdline flags yet.
|
|
kernel_part="--kernel_cmdline=\"${FLAGS_arm_extra_bootargs}\" "
|
|
# TODO(wad) Integrate dmtable extraction into the arm build
|
|
# E.g. $(cat ${OUTPUT_DIR}/boot.config | tr -s '\n' ' ')"
|
|
local kpart_offset="--kernel_partition_offset=${koffset}"
|
|
local kpart_size="--kernel_partition_sectors="
|
|
kpart_size="${kpart_size}$(partsize ${OUTPUT_DIR}/${image_name} 2)"
|
|
kernel_part="${kernel_part} ${kpart_size} ${kpart_offset}"
|
|
info "Using addition bootloader arguments: ${kernel_part}"
|
|
bootloader_to="${OUTPUT_DIR}/arm.mbr"
|
|
fi
|
|
|
|
# Update partition 12 / legacy bootloaders and arm.
|
|
${SCRIPTS_DIR}/update_bootloaders.sh \
|
|
--arch=${ARCH} \
|
|
--to="${bootloader_to}" \
|
|
--from="${OUTPUT_DIR}"/boot \
|
|
--vmlinuz="${OUTPUT_DIR}"/boot/vmlinuz \
|
|
--usb_disk="${usb_disk}" \
|
|
${bootloader_to_flags} \
|
|
$kernel_part
|
|
|
|
if [[ "${ARCH}" == "arm" ]]; then
|
|
sudo dd bs=1 conv=notrunc if="${bootloader_to}" \
|
|
of="${OUTPUT_DIR}/${image_name}"
|
|
sudo rm "${bootloader_to}"
|
|
fi
|
|
|
|
trap - EXIT
|
|
${SCRIPTS_DIR}/mount_gpt_image.sh -u -r "${ROOT_FS_DIR}" \
|
|
-s "${STATEFUL_FS_DIR}"
|
|
}
|
|
|
|
# Modifies an existing image to add development packages
|
|
update_dev_packages() {
|
|
local image_name=$1
|
|
|
|
echo "Adding developer packages to ${image_name}"
|
|
|
|
trap "mount_gpt_cleanup" EXIT
|
|
|
|
${SCRIPTS_DIR}/mount_gpt_image.sh --from "${OUTPUT_DIR}" \
|
|
--image "${image_name}" -r "${ROOT_FS_DIR}" \
|
|
-s "${STATEFUL_FS_DIR}" -e "${ESP_FS_DIR}"
|
|
|
|
# Determine the root dir for developer packages.
|
|
local root_dev_dir="${ROOT_FS_DIR}"
|
|
[ ${FLAGS_statefuldev} -eq ${FLAGS_TRUE} ] && \
|
|
root_dev_dir="${ROOT_FS_DIR}/usr/local"
|
|
|
|
# Install developer packages described in chromeos-dev.
|
|
sudo INSTALL_MASK="${INSTALL_MASK}" ${EMERGE_BOARD_CMD} \
|
|
--root="${root_dev_dir}" --root-deps=rdeps \
|
|
--usepkg -uDNv chromeos-dev ${EMERGE_JOBS}
|
|
|
|
if [[ $FLAGS_preserve -eq ${FLAGS_TRUE} ]] ; then
|
|
# Clean out unused packages
|
|
sudo INSTALL_MASK="${INSTALL_MASK}" ${EMERGE_BOARD_CMD} \
|
|
--root="${ROOT_FS_DIR}" --root-deps=rdeps \
|
|
--usepkg --depclean ${EMERGE_JOBS}
|
|
fi
|
|
|
|
# Re-run ldconfig to fix /etc/ldconfig.so.cache.
|
|
sudo /sbin/ldconfig -r "${ROOT_FS_DIR}"
|
|
|
|
# Mark the image as a developer image (input to chromeos_startup).
|
|
sudo mkdir -p "${ROOT_FS_DIR}/root"
|
|
sudo touch "${ROOT_FS_DIR}/root/.dev_mode"
|
|
|
|
# Additional changes to developer image.
|
|
|
|
# The ldd tool is a useful shell script but lives in glibc; just copy it.
|
|
sudo cp -a "$(which ldd)" "${root_dev_dir}/usr/bin"
|
|
|
|
# If vim is installed, then a vi symlink would probably help.
|
|
if [[ -x "${ROOT_FS_DIR}/usr/local/bin/vim" ]]; then
|
|
sudo ln -sf vim "${ROOT_FS_DIR}/usr/local/bin/vi"
|
|
fi
|
|
|
|
# If pygtk is installed in stateful-dev, then install a path.
|
|
if [[ -d \
|
|
"${ROOT_FS_DIR}/usr/local/lib/python2.6/site-packages/gtk-2.0" ]]; then
|
|
sudo bash -c "\
|
|
echo gtk-2.0 > \
|
|
${ROOT_FS_DIR}/usr/local/lib/python2.6/site-packages/pygtk.pth"
|
|
fi
|
|
|
|
# Check that the image has been correctly created. Only do it if not
|
|
# building a factory install image, as the INSTALL_MASK for it will
|
|
# make test_image fail.
|
|
if [[ ${FLAGS_factory_install} -eq ${FLAGS_FALSE} ]] ; then
|
|
"${SCRIPTS_DIR}/test_image" \
|
|
--root="${ROOT_FS_DIR}" \
|
|
--target="${ARCH}"
|
|
fi
|
|
echo "Developer image built and stored at ${image_name}"
|
|
|
|
trap - EXIT
|
|
${SCRIPTS_DIR}/mount_gpt_image.sh -u -r "${ROOT_FS_DIR}" \
|
|
-s "${STATEFUL_FS_DIR}" -e "${ESP_FS_DIR}"
|
|
}
|
|
|
|
# Update the base package on an existing image.
|
|
update_base_packages() {
|
|
local image_name=$1
|
|
|
|
echo "Updating base packages on ${image_name}"
|
|
|
|
# Create stateful partition of the same size as the rootfs.
|
|
trap "mount_gpt_cleanup" EXIT
|
|
|
|
${SCRIPTS_DIR}/mount_gpt_image.sh --from "${OUTPUT_DIR}" \
|
|
--image "${image_name}" -r "${ROOT_FS_DIR}" \
|
|
-s "${STATEFUL_FS_DIR}" -e "${ESP_FS_DIR}"
|
|
|
|
# Emerge updated packages, exactly like when creating base image
|
|
sudo INSTALL_MASK="${INSTALL_MASK}" ${EMERGE_BOARD_CMD} \
|
|
--root="${ROOT_FS_DIR}" --root-deps=rdeps \
|
|
--usepkg -uDNv chromeos ${EMERGE_JOBS}
|
|
|
|
# Clean out unused packages
|
|
sudo INSTALL_MASK="${INSTALL_MASK}" ${EMERGE_BOARD_CMD} \
|
|
--root="${ROOT_FS_DIR}" --root-deps=rdeps \
|
|
--usepkg --depclean ${EMERGE_JOBS}
|
|
|
|
trap - EXIT
|
|
${SCRIPTS_DIR}/mount_gpt_image.sh -u -r "${ROOT_FS_DIR}" \
|
|
-s "${STATEFUL_FS_DIR}" -e "${ESP_FS_DIR}"
|
|
}
|
|
|
|
create_base_image() {
|
|
local image_name=$1
|
|
|
|
trap "cleanup && delete_prompt" EXIT
|
|
|
|
UUID=$(uuidgen)
|
|
|
|
# Create and format the root file system.
|
|
|
|
# Check for loop device before creating image.
|
|
LOOP_DEV=$(sudo losetup -f)
|
|
if [ -z "${LOOP_DEV}" ] ; then
|
|
echo "No free loop device. Free up a loop device or reboot. exiting. "
|
|
exit 1
|
|
fi
|
|
|
|
# Create root file system disk image to fit on a 1GB memory stick.
|
|
# 1 GB in hard-drive-manufacturer-speak is 10^9, not 2^30. 950MB < 10^9 bytes.
|
|
if [[ ${FLAGS_factory_install} -eq ${FLAGS_TRUE} ]] ; then
|
|
ROOT_SIZE_BYTES=$((1024 * 1024 * 300))
|
|
else
|
|
ROOT_SIZE_BYTES=$((1024 * 1024 * ${FLAGS_rootfs_size}))
|
|
fi
|
|
|
|
# Pad out for the hash tree.
|
|
ROOT_HASH_PAD=$((FLAGS_rootfs_hash_pad * 1024 * 1024))
|
|
info "Padding the rootfs image by ${ROOT_HASH_PAD} bytes for hash data"
|
|
|
|
dd if=/dev/zero of="${ROOT_FS_IMG}" bs=1 count=1 \
|
|
seek=$((ROOT_SIZE_BYTES + ROOT_HASH_PAD - 1))
|
|
sudo losetup "${LOOP_DEV}" "${ROOT_FS_IMG}"
|
|
# Specify a block size and block count to avoid using the hash pad.
|
|
sudo mkfs.ext3 -b 4096 "${LOOP_DEV}" "$((ROOT_SIZE_BYTES / 4096))"
|
|
|
|
# Tune and mount rootfs.
|
|
# TODO(wad) rename the disk label to match the GPT since we
|
|
# can't change it later.
|
|
DISK_LABEL="C-KEYFOB"
|
|
sudo tune2fs -L "${DISK_LABEL}" -U "${UUID}" -c 0 -i 0 "${LOOP_DEV}"
|
|
sudo mount "${LOOP_DEV}" "${ROOT_FS_DIR}"
|
|
|
|
# Create stateful partition of the same size as the rootfs.
|
|
STATEFUL_LOOP_DEV=$(sudo losetup -f)
|
|
if [ -z "${STATEFUL_LOOP_DEV}" ] ; then
|
|
echo "No free loop device. Free up a loop device or reboot. exiting. "
|
|
exit 1
|
|
fi
|
|
STATEFUL_SIZE_BYTES=$((1024 * 1024 * ${FLAGS_statefulfs_size}))
|
|
dd if=/dev/zero of="${STATEFUL_FS_IMG}" bs=1 count=1 \
|
|
seek=$((STATEFUL_SIZE_BYTES - 1))
|
|
|
|
# Tune and mount the stateful partition.
|
|
UUID=$(uuidgen)
|
|
DISK_LABEL="C-STATE"
|
|
sudo losetup "${STATEFUL_LOOP_DEV}" "${STATEFUL_FS_IMG}"
|
|
sudo mkfs.ext3 "${STATEFUL_LOOP_DEV}"
|
|
sudo tune2fs -L "${DISK_LABEL}" -U "${UUID}" -c 0 -i 0 "${STATEFUL_LOOP_DEV}"
|
|
sudo mount "${STATEFUL_LOOP_DEV}" "${STATEFUL_FS_DIR}"
|
|
|
|
# Create OEM partner partition.
|
|
OEM_LOOP_DEV=$(sudo losetup -f)
|
|
if [ -z "${OEM_LOOP_DEV}" ] ; then
|
|
echo "No free loop device. Free up a loop device or reboot. exiting. "
|
|
exit 1
|
|
fi
|
|
OEM_SIZE_BYTES=$((1024 * 1024 * 16))
|
|
dd if=/dev/zero of="${OEM_FS_IMG}" bs=1 count=1 seek=$((OEM_SIZE_BYTES - 1))
|
|
|
|
# Tune and mount OEM partner partition.
|
|
UUID=$(uuidgen)
|
|
DISK_LABEL="C-OEM"
|
|
sudo losetup "${OEM_LOOP_DEV}" "${OEM_FS_IMG}"
|
|
sudo mkfs.ext3 "${OEM_LOOP_DEV}"
|
|
sudo tune2fs -L "${DISK_LABEL}" -U "${UUID}" -c 0 -i 0 "${OEM_LOOP_DEV}"
|
|
sudo mount "${OEM_LOOP_DEV}" "${OEM_FS_DIR}"
|
|
|
|
# Populate OEM partner partition.
|
|
if [ -n ${FLAGS_oem_customization} ]; then
|
|
if [ ! -d ${FLAGS_oem_customization} ]; then
|
|
echo "Specified OEM content directory does not exist. exiting."
|
|
exit 1
|
|
fi
|
|
for ITEM in `ls -A ${FLAGS_oem_customization}`
|
|
do sudo cp -a "${FLAGS_oem_customization}/$ITEM" "${OEM_FS_DIR}"
|
|
done
|
|
sudo find "${OEM_FS_DIR}" -type d -exec chmod 755 "{}" \;
|
|
sudo find "${OEM_FS_DIR}" -type f -exec chmod 644 "{}" \;
|
|
sudo chown -R root:root "${OEM_FS_DIR}"
|
|
fi
|
|
|
|
# -- Install packages into the root file system --
|
|
|
|
# We need to install libc manually from the cross toolchain.
|
|
# TODO: Improve this? We only want libc and not the whole toolchain.
|
|
PKGDIR="/var/lib/portage/pkgs/cross/"
|
|
sudo tar jxvpf \
|
|
"${PKGDIR}/${CHOST}/cross-${CHOST}"/glibc-${LIBC_VERSION}.tbz2 \
|
|
-C "${ROOT_FS_DIR}" --strip-components=3 \
|
|
--exclude=usr/include --exclude=sys-include --exclude=*.a --exclude=*.o
|
|
|
|
# We need to install libstdc++ manually from the cross toolchain.
|
|
# TODO: Figure out a better way of doing this?
|
|
sudo cp -a "${BOARD_ROOT}"/lib/libgcc_s.so* "${ROOT_FS_DIR}/lib"
|
|
sudo cp -a "${BOARD_ROOT}"/usr/lib/libstdc++.so* "${ROOT_FS_DIR}/usr/lib"
|
|
|
|
# Prepare stateful partition with some pre-created directories.
|
|
sudo mkdir -p "${DEV_IMAGE_ROOT}"
|
|
sudo mkdir -p "${STATEFUL_FS_DIR}/var"
|
|
|
|
# Create symlinks so that /usr/local/usr based directories are symlinked to
|
|
# /usr/local/ directories e.g. /usr/local/usr/bin -> /usr/local/bin, etc.
|
|
setup_symlinks_on_root "${DEV_IMAGE_ROOT}" "${STATEFUL_FS_DIR}/var" \
|
|
"${STATEFUL_FS_DIR}"
|
|
|
|
# Perform binding rather than symlinking because directories must exist
|
|
# on rootfs so that we can bind at run-time since rootfs is read-only.
|
|
echo "Binding directories from stateful partition onto the rootfs"
|
|
sudo mkdir -p "${ROOT_FS_DIR}/usr/local"
|
|
sudo mount --bind "${DEV_IMAGE_ROOT}" "${ROOT_FS_DIR}/usr/local"
|
|
sudo mkdir -p "${ROOT_FS_DIR}/var"
|
|
sudo mount --bind "${STATEFUL_FS_DIR}/var" "${ROOT_FS_DIR}/var"
|
|
sudo mkdir -p "${ROOT_FS_DIR}/dev"
|
|
|
|
# We "emerge --root=${ROOT_FS_DIR} --root-deps=rdeps --usepkg" all of the
|
|
# runtime packages for chrome os. This builds up a chrome os image from
|
|
# binary packages with runtime dependencies only. We use INSTALL_MASK to
|
|
# trim the image size as much as possible.
|
|
sudo INSTALL_MASK="${INSTALL_MASK}" ${EMERGE_BOARD_CMD} \
|
|
--root="${ROOT_FS_DIR}" --root-deps=rdeps \
|
|
--usepkg chromeos ${EMERGE_JOBS}
|
|
|
|
# Perform any customizations on the root file system that are needed.
|
|
"${SCRIPTS_DIR}/customize_rootfs" \
|
|
--root="${ROOT_FS_DIR}" \
|
|
--target="${ARCH}" \
|
|
--board="${BOARD}"
|
|
|
|
# Populates the root filesystem with legacy bootloader templates
|
|
# appropriate for the platform. The autoupdater and installer will
|
|
# use those templates to update the legacy boot partition (12/ESP)
|
|
# on update.
|
|
# (This script does not populate vmlinuz.A and .B needed by syslinux.)
|
|
enable_rootfs_verification=
|
|
if [[ ${FLAGS_enable_rootfs_verification} -eq ${FLAGS_TRUE} ]]; then
|
|
enable_rootfs_verification="--enable_rootfs_verification"
|
|
fi
|
|
|
|
${SCRIPTS_DIR}/create_legacy_bootloader_templates.sh \
|
|
--arch=${ARCH} \
|
|
--to="${ROOT_FS_DIR}"/boot \
|
|
--install \
|
|
${enable_rootfs_verification}
|
|
|
|
# Create a working copy so we don't need the rootfs mounted
|
|
sudo mkdir -p "${OUTPUT_DIR}"/boot
|
|
# This will include any built files dropped in /boot as well.
|
|
# Like the current vmlinuz.
|
|
sudo cp -r "${ROOT_FS_DIR}"/boot/. "${OUTPUT_DIR}"/boot/
|
|
sudo chmod -R a+r "${OUTPUT_DIR}"/boot/
|
|
|
|
# Don't test the factory install shim.
|
|
if [[ ${FLAGS_factory_install} -eq ${FLAGS_FALSE} ]] ; then
|
|
# Check that the image has been correctly created.
|
|
"${SCRIPTS_DIR}/test_image" \
|
|
--root="${ROOT_FS_DIR}" \
|
|
--target="${ARCH}"
|
|
fi
|
|
|
|
# Clean up symlinks so they work on a running target rooted at "/".
|
|
# Here development packages are rooted at /usr/local. However, do not
|
|
# create /usr/local or /var on host (already exist on target).
|
|
setup_symlinks_on_root "/usr/local" "/var" "${STATEFUL_FS_DIR}"
|
|
|
|
# make_image_bootable will clobber vmlinuz.image for x86.
|
|
# Until then, just copy the kernel to vmlinuz.image. It is
|
|
# expected in build_gpt.sh and needed by ARM until it supports the
|
|
# full, signed kernel partition format.
|
|
cp "${ROOT_FS_DIR}/boot/vmlinuz" "${OUTPUT_DIR}/vmlinuz.image"
|
|
|
|
# Create an empty esp image to be updated in by update_bootloaders.sh.
|
|
${SCRIPTS_DIR}/create_esp.sh --to="${ESP_FS_IMG}"
|
|
|
|
cleanup
|
|
|
|
trap delete_prompt EXIT
|
|
|
|
# Create the GPT-formatted image.
|
|
${SCRIPTS_DIR}/build_gpt.sh \
|
|
--arch=${ARCH} \
|
|
--board=${FLAGS_board} \
|
|
--arm_extra_bootargs="${FLAGS_arm_extra_bootargs}" \
|
|
--rootfs_partition_size=${FLAGS_rootfs_partition_size} \
|
|
"${OUTPUT_DIR}" \
|
|
"${OUTPUT_DIR}/${image_name}"
|
|
|
|
trap - EXIT
|
|
}
|
|
|
|
# Create the output directory.
|
|
mkdir -p "${OUTPUT_DIR}"
|
|
mkdir -p "${ROOT_FS_DIR}"
|
|
mkdir -p "${STATEFUL_FS_DIR}"
|
|
mkdir -p "${OEM_FS_DIR}"
|
|
mkdir -p "${ESP_FS_DIR}"
|
|
|
|
# Preserve old images by copying them forward for --preserve.
|
|
if [[ $FLAGS_preserve -eq ${FLAGS_TRUE} ]] ; then
|
|
if [[ -f ${PREVIOUS_DIR}/${PRISTINE_IMAGE_NAME} ]] ; then
|
|
# Copy forward pristine image, and associated files
|
|
cp ${PREVIOUS_DIR}/*.sh ${PREVIOUS_DIR}/config.txt ${OUTPUT_DIR}
|
|
cp ${PREVIOUS_DIR}/${PRISTINE_IMAGE_NAME} ${OUTPUT_DIR}
|
|
cp -r ${PREVIOUS_DIR}/boot ${OUTPUT_DIR}/boot
|
|
|
|
# Copy forward the developer image, if we already copied forward the base.
|
|
if [[ ${FLAGS_withdev} -eq ${FLAGS_TRUE} ]] && \
|
|
[[ -f ${PREVIOUS_DIR}/${DEVELOPER_IMAGE_NAME} ]] ; then
|
|
cp ${PREVIOUS_DIR}/${DEVELOPER_IMAGE_NAME} ${OUTPUT_DIR}
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if [[ -f ${PRISTINE_IMG} ]] ; then
|
|
update_base_packages ${PRISTINE_IMAGE_NAME}
|
|
else
|
|
create_base_image ${PRISTINE_IMAGE_NAME}
|
|
fi
|
|
make_image_bootable ${PRISTINE_IMAGE_NAME}
|
|
|
|
# FIXME: only signing things for x86 right now.
|
|
if [[ "${ARCH}" = "x86" ]]; then
|
|
# Verify the final image.
|
|
load_kernel_test "${OUTPUT_DIR}/${PRISTINE_IMAGE_NAME}" \
|
|
"${DEVKEYSDIR}/recovery_key.vbpubk"
|
|
fi
|
|
|
|
# Create a developer image based on the chromium os base image.
|
|
if [ "${FLAGS_withdev}" -eq "${FLAGS_TRUE}" ] ; then
|
|
if [[ ! -f ${DEVELOPER_IMG} ]] ; then
|
|
echo "Creating developer image from base image ${PRISTINE_IMAGE_NAME}"
|
|
cp ${PRISTINE_IMG} ${DEVELOPER_IMG}
|
|
fi
|
|
|
|
update_dev_packages ${DEVELOPER_IMAGE_NAME}
|
|
make_image_bootable ${DEVELOPER_IMAGE_NAME}
|
|
fi
|
|
|
|
# Clean up temporary files.
|
|
rm -f "${ROOT_FS_IMG}" "${STATEFUL_FS_IMG}" "${OUTPUT_DIR}/vmlinuz.image" \
|
|
"${ESP_FS_IMG}" "${OEM_FS_IMG}" "${OUTPUT_DIR}/vmlinuz_hd.vblock"
|
|
rmdir "${ROOT_FS_DIR}" "${STATEFUL_FS_DIR}" "${OEM_FS_DIR}" "${ESP_FS_DIR}"
|
|
|
|
echo "Done. Image created in ${OUTPUT_DIR}"
|
|
echo "Chromium OS image created as ${PRISTINE_IMAGE_NAME}"
|
|
if [ "${FLAGS_withdev}" -eq "${FLAGS_TRUE}" ]; then
|
|
echo "Developer image created as ${DEVELOPER_IMAGE_NAME}"
|
|
fi
|
|
|
|
print_time_elapsed
|
|
|
|
echo "To copy to USB keyfob, OUTSIDE the chroot, do something like:"
|
|
echo " ./image_to_usb.sh --from=${OUTSIDE_OUTPUT_DIR} --to=/dev/sdX"
|
|
echo "To convert to VMWare image, INSIDE the chroot, do something like:"
|
|
echo " ./image_to_vm.sh --from=${OUTSIDE_OUTPUT_DIR}"
|
|
echo "from the scripts directory where you entered the chroot."
|