mirror of
https://github.com/flatcar/scripts.git
synced 2025-08-09 22:16:58 +02:00
cros_make_image_bootable is called up to 4 times in a typical workflow. The fsck check can take up to a minute or more because it first copies 2G of the image to run the fsck on. It adds several minutes to the build. The check itself is just a sanity check that shouldn't trigger in normal builds. BUG=none TEST=build_image Change-Id: I86512f7efc67027fe687f0c0ad8f6df32579dbe2 Reviewed-on: http://gerrit.chromium.org/gerrit/2250 Reviewed-by: Gaurav Shah <gauravsh@chromium.org> Reviewed-by: David James <davidjames@chromium.org> Tested-by: Antoine Labour <piman@chromium.org> Reviewed-by: Antoine Labour <piman@chromium.org>
323 lines
11 KiB
Bash
Executable File
323 lines
11 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# Copyright (c) 2010 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 which ensures that a given image has an up-to-date
|
|
# kernel partition, rootfs integrity hashes, and legacy bootloader configs.
|
|
|
|
# --- 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
|
|
|
|
SCRIPT_ROOT=
|
|
for path in "${common_paths[@]}"; do
|
|
if [ -r "${path}/common.sh" ]; then
|
|
SCRIPT_ROOT=${path}
|
|
break
|
|
fi
|
|
done
|
|
}
|
|
|
|
find_common_sh
|
|
. "${SCRIPT_ROOT}/common.sh" || (echo "Unable to load common.sh" && exit 1)
|
|
# --- END COMMON.SH BOILERPLATE ---
|
|
|
|
# Need to be inside the chroot to load chromeos-common.sh
|
|
assert_inside_chroot
|
|
|
|
# Load functions and constants for chromeos-install
|
|
. "/usr/lib/installer/chromeos-common.sh" || \
|
|
die "Unable to load /usr/lib/installer/chromeos-common.sh"
|
|
|
|
set -e
|
|
|
|
if [ $# -lt 2 ]; then
|
|
echo "Usage: ${0} /PATH/TO/IMAGE IMAGE.BIN [shflags overrides]"
|
|
exit 1
|
|
fi
|
|
|
|
IMAGE_DIR="$(readlink -f "${1}")"
|
|
BOOT_DESC_FILE="${IMAGE_DIR}/boot.desc"
|
|
IMAGE="${IMAGE_DIR}/${2}"
|
|
shift
|
|
shift
|
|
FLAG_OVERRIDES="${@}"
|
|
|
|
if [ ! -r "${BOOT_DESC_FILE}" ]; then
|
|
warn "${BOOT_DESC_FILE} cannot be read!"
|
|
warn "Falling back to command line parsing"
|
|
BOOT_DESC="${@}"
|
|
else
|
|
BOOT_DESC="$(cat ${BOOT_DESC_FILE} | tr -s '\n' ' ')"
|
|
info "Boot-time configuration for $(dirname "${IMAGE}"): "
|
|
cat ${BOOT_DESC_FILE} | while read line; do
|
|
info " ${line}"
|
|
done
|
|
fi
|
|
|
|
if [ ! -r "${IMAGE}" ]; then
|
|
die "${IMAGE} cannot be read!"
|
|
fi
|
|
|
|
|
|
locate_gpt
|
|
set +e
|
|
|
|
# Now parse the build settings from ${OUTPUT_DIR}/boot.desc
|
|
|
|
DEFINE_string output_dir "/tmp" \
|
|
"Directory to place output in."
|
|
DEFINE_string image "chromiumos_base.img" \
|
|
"Full path to the chromiumos image to make bootable."
|
|
DEFINE_string arch "x86" \
|
|
"Architecture to make bootable for: arm or x86"
|
|
DEFINE_string usb_disk "/dev/sdb3" \
|
|
"Path syslinux should use to do a usb boot."
|
|
DEFINE_boolean cleanup_dirs ${FLAGS_TRUE} \
|
|
"Whether the mount dirs should be removed on completion."
|
|
|
|
DEFINE_string boot_args "noinitrd" \
|
|
"Additional boot arguments to pass to the commandline"
|
|
|
|
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_string rootfs_hash "/tmp/rootfs.hash" \
|
|
"Path where the rootfs hash should be stored."
|
|
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)"
|
|
DEFINE_integer verity_max_ios 1024 \
|
|
"Number of outstanding I/O operations dm-verity caps at."
|
|
DEFINE_string verity_algorithm "sha1" \
|
|
"Cryptographic hash algorithm used for kernel vboot."
|
|
|
|
DEFINE_string keys_dir "/usr/share/vboot/devkeys" \
|
|
"Directory containing the signing keys."
|
|
|
|
DEFINE_string rootfs_mountpoint "/tmp/rootfs" \
|
|
"Path where the rootfs can be safely mounted"
|
|
DEFINE_string statefulfs_mountpoint "/tmp/statefulfs" \
|
|
"Path where the statefulfs can be safely mounted"
|
|
DEFINE_string espfs_mountpoint "/tmp/espfs" \
|
|
"Path where the espfs can be safely mounted"
|
|
|
|
DEFINE_boolean use_dev_keys ${FLAGS_FALSE} \
|
|
"Use developer keys for signing. (Default: false)"
|
|
|
|
DEFINE_boolean fsck_rootfs ${FLAGS_FALSE} \
|
|
"Check integrity of the rootfs on the modified image."
|
|
|
|
# TODO(clchiou): Remove this flag after arm verified boot is stable
|
|
DEFINE_boolean crosbug12352_arm_kernel_signing ${FLAGS_TRUE} \
|
|
"Sign kernel partition for ARM images (temporary hack)."
|
|
|
|
# TODO(sosa): Remove once known images no longer use this in their config.
|
|
DEFINE_string arm_extra_bootargs "" "DEPRECATED FLAG. Do not use."
|
|
|
|
# Parse the boot.desc and any overrides
|
|
eval set -- "${BOOT_DESC} ${FLAG_OVERRIDES}"
|
|
FLAGS "${@}" || exit 1
|
|
|
|
# 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 -u
|
|
|
|
if [[ ${FLAGS_crosbug12352_arm_kernel_signing} -eq ${FLAGS_TRUE} ]]; then
|
|
crosbug12352_flag="--crosbug12352_arm_kernel_signing"
|
|
else
|
|
crosbug12352_flag="--nocrosbug12352_arm_kernel_signing"
|
|
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-$FLAGS_rootfs_mountpoint}"
|
|
local statefs="${2-$FLAGS_statefulfs_mountpoint}"
|
|
local espfs="${3-$FLAGS_espfs_mountpoint}"
|
|
"${SCRIPTS_DIR}/mount_gpt_image.sh" \
|
|
-u -r "${rootfs}" -s "${statefs}" -e "${espfs}"
|
|
}
|
|
|
|
make_image_bootable() {
|
|
local image="$1"
|
|
local use_dev_keys=
|
|
|
|
# Default to non-verified, non-UUID boot unless verified.
|
|
# Long term we want root=PARTUUID=uuid+1.
|
|
cros_root=/dev/sd%D%P
|
|
if [[ "${FLAGS_arch}" = "arm" ]]; then
|
|
cros_root='/dev/${devname}${rootpart}'
|
|
fi
|
|
if [[ ${FLAGS_enable_rootfs_verification} -eq ${FLAGS_TRUE} ]]; then
|
|
cros_root=/dev/dm-0
|
|
fi
|
|
|
|
trap "mount_gpt_cleanup" EXIT
|
|
"${SCRIPTS_DIR}/mount_gpt_image.sh" --from "$(dirname "${image}")" \
|
|
--image "$(basename ${image})" -r "${FLAGS_rootfs_mountpoint}" \
|
|
-s "${FLAGS_statefulfs_mountpoint}"
|
|
|
|
# The rootfs should never be mounted rw again after this point without
|
|
# re-calling make_image_bootable.
|
|
sudo mount -o remount,ro "${FLAGS_rootfs_mountpoint}"
|
|
root_dev=$(mount | grep -- "on ${FLAGS_rootfs_mountpoint} type" |
|
|
cut -f1 -d' ' | tail -1)
|
|
|
|
# Make the filesystem un-mountable as read-write.
|
|
# mount_gpt_image.sh will undo this as needed.
|
|
# TODO(wad) make sure there is parity in the signing scripts.
|
|
if [ ${FLAGS_enable_rootfs_verification} -eq ${FLAGS_TRUE} ]; then
|
|
# TODO(wad) this would be a good place to reset any other ext2 metadata.
|
|
warn "Disabling r/w mount of the root filesystem"
|
|
disable_rw_mount "$root_dev"
|
|
fi
|
|
|
|
if [ ${FLAGS_use_dev_keys} -eq ${FLAGS_TRUE} ]; then
|
|
use_dev_keys="--use_dev_keys"
|
|
fi
|
|
|
|
# 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="${FLAGS_arch}" \
|
|
--to="${FLAGS_output_dir}/vmlinuz.image" \
|
|
--hd_vblock="${FLAGS_output_dir}/vmlinuz_hd.vblock" \
|
|
--vmlinuz="${FLAGS_rootfs_mountpoint}/boot/vmlinuz" \
|
|
--working_dir="${FLAGS_output_dir}" \
|
|
--boot_args="${FLAGS_boot_args}" \
|
|
--keep_work \
|
|
--rootfs_image=${root_dev} \
|
|
--rootfs_hash=${FLAGS_rootfs_hash} \
|
|
--verity_hash_alg=${FLAGS_verity_algorithm} \
|
|
--verity_max_ios=${FLAGS_verity_max_ios} \
|
|
--verity_error_behavior=${FLAGS_verity_error_behavior} \
|
|
--root=${cros_root} \
|
|
--keys_dir="${FLAGS_keys_dir}" \
|
|
${crosbug12352_flag} \
|
|
${use_dev_keys}
|
|
|
|
local rootfs_hash_size=$(stat -c '%s' ${FLAGS_rootfs_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 ${image} 3)"
|
|
hash_offset=$((hash_offset + ((1024 * 1024 * ${FLAGS_rootfs_size}) / 512)))
|
|
sudo dd bs=512 \
|
|
seek=${hash_offset} \
|
|
if="${FLAGS_rootfs_hash}" \
|
|
of="${image}" \
|
|
conv=notrunc
|
|
# We don't need to keep the file around anymore.
|
|
sudo rm "${FLAGS_rootfs_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
|
|
sudo cp "${FLAGS_output_dir}/vmlinuz_hd.vblock" \
|
|
"${FLAGS_statefulfs_mountpoint}"
|
|
|
|
# START_KERN_A is set by the first call to install the gpt.
|
|
local koffset="$(partoffset ${image} 2)"
|
|
sudo dd if="${FLAGS_output_dir}/vmlinuz.image" of="${image}" \
|
|
conv=notrunc bs=512 seek=${koffset}
|
|
|
|
# Update the bootloaders. The EFI system partition will be updated.
|
|
local kernel_part=
|
|
local usb_disk="${FLAGS_usb_disk}"
|
|
|
|
# We should update the esp in place in the image.
|
|
local bootloader_to="${image}"
|
|
local esp_offset="$(partoffset ${image} 12)"
|
|
esp_offset=$((esp_offset * 512)) # sectors to bytes
|
|
local esp_size="$(partsize ${image} 12)"
|
|
esp_size=$((esp_size * 512)) # sectors to bytes
|
|
local bootloader_to_flags="--to_offset=${esp_offset} --to_size=${esp_size}"
|
|
|
|
if [[ "${FLAGS_arch}" = "x86" ]]; then
|
|
# Use the kernel partition to acquire configuration flags.
|
|
kernel_part="--kernel_partition='${FLAGS_output_dir}/vmlinuz.image'"
|
|
# Install syslinux on the EFI System Partition.
|
|
kernel_part="${kernel_part} --install_syslinux"
|
|
elif [[ "${FLAGS_arch}" = "arm" ]]; then
|
|
# These flags are not used for ARM update_bootloaders.sh
|
|
kernel_part=""
|
|
fi
|
|
|
|
# Update partition 12
|
|
${SCRIPTS_DIR}/update_bootloaders.sh \
|
|
--arch=${FLAGS_arch} \
|
|
--to="${bootloader_to}" \
|
|
--from="${FLAGS_rootfs_mountpoint}"/boot \
|
|
--vmlinuz="${FLAGS_rootfs_mountpoint}"/boot/vmlinuz \
|
|
--usb_disk="${usb_disk}" \
|
|
${bootloader_to_flags} \
|
|
$kernel_part
|
|
|
|
trap - EXIT
|
|
${SCRIPTS_DIR}/mount_gpt_image.sh -u -r "${FLAGS_rootfs_mountpoint}" \
|
|
-s "${FLAGS_statefulfs_mountpoint}"
|
|
}
|
|
|
|
verify_image_rootfs() {
|
|
local image=$1
|
|
local rootfs_offset="$(partoffset ${image} 3)"
|
|
local rootfs_size="$(partsize ${image} 3)"
|
|
|
|
local rootfs_tmp_file=$(mktemp)
|
|
trap "rm ${rootfs_tmp_file}" EXIT
|
|
sudo dd if="${image}" of="${rootfs_tmp_file}" bs=512 skip="${rootfs_offset}"
|
|
|
|
# This flips the read-only compatibility flag, so that
|
|
# e2fsck does not complain about unknown file system capabilities.
|
|
enable_rw_mount "${rootfs_tmp_file}"
|
|
info "Running e2fsck to check root file system for errors"
|
|
sudo e2fsck -fn "${rootfs_tmp_file}" ||
|
|
die "Root file system has errors, please ensure boot.desc and/or \
|
|
command line parameters are correct"
|
|
}
|
|
|
|
# Use default of current image location if the output dir doesn't exist.
|
|
if [ ! -d ${FLAGS_output_dir} ]; then
|
|
warn "Output dir not found, using ${IMAGE_DIR}."
|
|
FLAGS_output_dir="${IMAGE_DIR}"
|
|
FLAGS_rootfs_hash="${IMAGE_DIR}/rootfs.hash"
|
|
FLAGS_rootfs_mountpoint="${IMAGE_DIR}/rootfs_dir"
|
|
FLAGS_statefulfs_mountpoint="${IMAGE_DIR}/stateful_dir"
|
|
FLAGS_espfs_mountpoint="${IMAGE_DIR}/esp"
|
|
fi
|
|
|
|
# Create the directories if they don't exist.
|
|
mkdir -p ${FLAGS_rootfs_mountpoint}
|
|
mkdir -p ${FLAGS_statefulfs_mountpoint}
|
|
mkdir -p ${FLAGS_espfs_mountpoint}
|
|
|
|
make_image_bootable "${IMAGE}"
|
|
if [ ${FLAGS_fsck_rootfs} -eq ${FLAGS_TRUE} ]; then
|
|
verify_image_rootfs "${IMAGE}"
|
|
fi
|
|
|
|
if [ ${FLAGS_cleanup_dirs} -eq ${FLAGS_TRUE} ]; then
|
|
rmdir ${FLAGS_rootfs_mountpoint}
|
|
rmdir ${FLAGS_statefulfs_mountpoint}
|
|
rmdir ${FLAGS_espfs_mountpoint}
|
|
fi
|