blob: 8b90575fdba3f1d4c1567ef68d14c3452bf882b4 [file] [log] [blame]
# Copyright (c) 2012 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.
. "${SRC_ROOT}/platform/dev/" || exit 1
cleanup_mounts() {
local prev_ret=$?
# Disable die on error.
set +e
# See if we ran out of space. Only show if we errored out via a trap.
if [[ ${prev_ret} -ne 0 ]]; then
local df=$(df -B 1M "${root_fs_dir}")
if [[ ${df} == *100%* ]]; then
error "Here are the biggest files (by disk usage):"
# Send final output to stderr to match `error` behavior.
sudo find "${root_fs_dir}" -xdev -type f -printf '%b %P\n' | \
awk '$1 > 16 { $1 = $1 * 512; print }' | sort -n | tail -100 1>&2
error "Target image has run out of space:"
error "${df}"
echo "Cleaning up mounts"
safe_umount_tree "${root_fs_dir}"
safe_umount_tree "${stateful_fs_dir}"
safe_umount_tree "${esp_fs_dir}"
safe_umount_tree "${oem_fs_dir}"
# Turn die on error back on.
set -e
zero_free_space() {
local fs_mount_point=$1
info "Zeroing freespace in ${fs_mount_point}"
# dd is a silly thing and will produce a "No space left on device" message
# that cannot be turned off and is confusing to unsuspecting victims.
info "${fs_mount_point}/filler"
( sudo dd if=/dev/zero of="${fs_mount_point}/filler" bs=4096 conv=fdatasync \
status=noxfer || true ) 2>&1 | grep -v "No space left on device"
sudo rm "${fs_mount_point}/filler"
# Usage: mk_fs <image_type> <partition_num> <image_file> <fs_uuid> <mount_dir>
# Args:
# image_type: The layout name used to look up partition info in disk layout.
# partition_num: The partition to look up in the disk layout.
# image_file: The file to write the fs image to.
# fs_uuid: 'clear' will set the UUID to all zeros. 'random' will initialize
# the UUID with a new random value.
# mount_dir: (optional) Where to mount the image after creating it
# Note: After we mount the fs, we will attempt to reset the root dir ownership
# to 0:0 to workaround a bug in mke2fs (fixed in upstream git now).
mk_fs() {
local image_type=$1
local part_num=$2
local fs_img=$3
local fs_uuid=$4
local mount_dir=$5
# These are often not in non-root $PATH, but they contain tools that
# we can run just fine w/non-root users when we work on plain files.
local p
for p in /sbin /usr/sbin; do
if [[ ":${PATH}:" != *:${p}:* ]]; then
# Keep `local` decl split from assignment so return code is checked.
local fs_bytes fs_label fs_format fs_block_size
fs_bytes=$(get_filesystem_size ${image_type} ${part_num})
fs_label=$(get_label ${image_type} ${part_num})
fs_format=$(get_filesystem_format ${image_type} ${part_num})
if [[ ${fs_bytes} -eq 0 ]]; then
info "Skipping ${fs_img} (size == 0 bytes)"
return 0
info "Building ${fs_img}"
truncate -s ${fs_bytes} "${fs_img}"
case ${fs_format} in
mkfs.${fs_format} -F -q -U 00000000-0000-0000-0000-000000000000 \
-b ${fs_block_size} "${fs_img}" "$((fs_bytes / fs_block_size))"
tune2fs -L "${fs_label}" \
-U "${fs_uuid}" \
-c 0 \
-i 0 \
-T 20091119110000 \
-m 0 \
-r 0 \
-e remount-ro \
mkfs.vfat -F ${fs_format#fat} -n "${fs_label}" "${fs_img}"
mkfs.vfat -n "${fs_label}" "${fs_img}"
die "Unknown fs format '${fs_format}' for part ${part_num}";;
if [[ -n ${mount_dir} ]]; then
mkdir -p "${mount_dir}"
local cmds=(
"mount -o loop '${fs_img}' '${mount_dir}'"
# mke2fs is funky and sets the root dir owner to current uid:gid.
"chown 0:0 '${mount_dir}' 2>/dev/null || :"
sudo_multi "${cmds[@]}"
create_base_image() {
local image_name=$1
local rootfs_verification_enabled=$2
local bootcache_enabled=$3
local image_type="usb"
if [[ "${FLAGS_disk_layout}" != "default" ]]; then
if should_build_image ${CHROMEOS_FACTORY_INSTALL_SHIM_NAME}; then
check_valid_layout "base"
check_valid_layout ${image_type}
info "Using image type ${image_type}"
info "Using disk layout ${DISK_LAYOUT_PATH}"
trap "cleanup_mounts && delete_prompt" EXIT
cleanup_mounts &> /dev/null
local root_fs_img="${BUILD_DIR}/rootfs.image"
local stateful_fs_img="${BUILD_DIR}/stateful.image"
local esp_fs_img="${BUILD_DIR}/esp.image"
local oem_fs_img="${BUILD_DIR}/oem.image"
# Build the various FS images.
mk_fs "${image_type}" 3 "${root_fs_img}" clear "${root_fs_dir}"
df -h "${root_fs_dir}"
mk_fs "${image_type}" 1 "${stateful_fs_img}" random "${stateful_fs_dir}"
mk_fs "${image_type}" 12 "${esp_fs_img}" clear
mk_fs "${image_type}" 8 "${oem_fs_img}" random "${oem_fs_dir}"
# Prepare stateful partition with some pre-created directories.
sudo mkdir "${stateful_fs_dir}/dev_image" "${stateful_fs_dir}/var_overlay"
# 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 "${stateful_fs_dir}/dev_image" \
"${stateful_fs_dir}/var_overlay" "${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.
info "Binding directories from stateful partition onto the rootfs"
sudo mkdir -p "${root_fs_dir}/mnt/stateful_partition"
sudo mount --bind "${stateful_fs_dir}" "${root_fs_dir}/mnt/stateful_partition"
sudo mkdir -p "${root_fs_dir}/usr/local"
sudo mount --bind "${stateful_fs_dir}/dev_image" "${root_fs_dir}/usr/local"
sudo mkdir -p "${root_fs_dir}/var"
sudo mount --bind "${stateful_fs_dir}/var_overlay" "${root_fs_dir}/var"
sudo mkdir -p "${root_fs_dir}/dev"
local oem_fs_bytes=$(get_filesystem_size ${image_type} 8)
if [[ ${oem_fs_bytes} -gt 0 ]]; then
info "Binding directories from OEM partition onto the rootfs"
sudo mkdir -p "${root_fs_dir}/usr/share/oem"
sudo mount --bind "${oem_fs_dir}" "${root_fs_dir}/usr/share/oem"
# We need to install libc manually from the cross toolchain.
# TODO: Improve this? It would be ideal to use emerge to do this.
if ! [[ -e ${LIBC_PATH} ]]; then
die_notrace \
"${LIBC_PATH} does not exist. Try running ./setup_board" \
"--board=${BOARD} to update the version of libc installed on that board."
# Strip out files we don't need in the final image at runtime.
local libc_excludes=(
# Compile-time headers.
'usr/include' 'sys-include'
# Link-time objects.
pbzip2 -dc --ignore-trailing-garbage=1 "${LIBC_PATH}" | \
sudo tar xpf - -C "${root_fs_dir}" ./usr/${CHOST} \
--strip-components=3 "${libc_excludes[@]/#/--exclude=}"
if should_build_image ${CHROMEOS_FACTORY_INSTALL_SHIM_NAME}; then
# Install our custom factory install kernel with the appropriate use flags
# to the image.
emerge_custom_kernel "${root_fs_dir}"
# We "emerge --root=${root_fs_dir} --root-deps=rdeps --usepkgonly" 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.
emerge_to_image --root="${root_fs_dir}" ${BASE_PACKAGE}
# Run ldconfig to create /etc/
sudo ldconfig -r "${root_fs_dir}"
# Set /etc/lsb-release on the image.
"${OVERLAY_CHROMEOS_DIR}/scripts/cros_set_lsb_release" \
--root="${root_fs_dir}" \
# Create the boot.desc file which stores the build-time configuration
# information needed for making the image bootable after creation with
# cros_make_image_bootable.
create_boot_desc "${image_type}"
# Write out the GPT creation script.
# This MUST be done before writing bootloader templates else we'll break
# the hash on the root FS.
write_partition_script "${image_type}" \
# 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.)
# Factory install shims may be booted from USB by legacy EFI BIOS, which does
# not support verified boot yet (see
# so rootfs verification is disabled if we are building with --factory_install
local enable_rootfs_verification=
if [[ ${rootfs_verification_enabled} -eq ${FLAGS_TRUE} ]]; then
local enable_bootcache=
if [[ ${bootcache_enabled} -eq ${FLAGS_TRUE} ]]; then
--arch=${ARCH} \
--to="${root_fs_dir}"/boot \
--boot_args="${FLAGS_boot_args}" \
${enable_rootfs_verification} \
# Don't test the factory install shim
if ! should_build_image ${CHROMEOS_FACTORY_INSTALL_SHIM_NAME}; then
if [[ ${skip_test_image_content} -ne 1 ]]; then
# Check that the image has been correctly created.
test_image_content "$root_fs_dir"
# 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}"
# Our masking of files will implicitly leave behind a bunch of empty
# dirs. We can't differentiate between empty dirs we want and empty
# dirs we don't care about, so just prune ones we know are OK.
sudo find "${root_fs_dir}/usr/include" -depth -type d -exec rmdir {} + \
2>/dev/null || :
# Zero rootfs free space to make it more compressible so auto-update
# payloads become smaller
zero_free_space "${root_fs_dir}"
# Create the GPT-formatted image.
build_gpt "${BUILD_DIR}/${image_name}" \
"${root_fs_img}" \
"${stateful_fs_img}" \
"${esp_fs_img}" \
# Clean up temporary files.
rm -f "${root_fs_img}" "${stateful_fs_img}" "${esp_fs_img}" "{oem_fs_img}"
# Emit helpful scripts for testers, etc.
emit_gpt_scripts "${BUILD_DIR}/${image_name}" "${BUILD_DIR}"
if should_build_image ${CHROMEOS_FACTORY_INSTALL_SHIM_NAME}; then
if [[ ${skip_kernelblock_install} -ne 1 ]]; then
# Place flags before positional args.
${SCRIPTS_DIR}/bin/cros_make_image_bootable "${BUILD_DIR}" \
${image_name} ${USE_DEV_KEYS} --adjust_part="${FLAGS_adjust_part}"