mirror of
https://github.com/flatcar/scripts.git
synced 2025-09-24 15:11:19 +02:00
This change makes more of the root filesyste metadata static across builds, but more can be done there. It also changes the root filesystem to use ext2 as we don't need journaling in normal mode. Optionally we could use ext3 for non-verified if desired (it's an easy change). In particular, this change cleans up the following: - clears the rootfs uuid - labels it C-ROOT (instead of C-KEYFOB) - removes reserved inodes and blocks The major feature of this change, however, is that it adds two simple helpers to common.sh: disable_rw_mount and enable_rw_mount. They will set high order byte (le) in the ro compat field to be 0xff. This will tell the kernel that the filesystem uses features R24-R31 which are safe for use on the running kernel iff the filesystem is mounted read-only. These functions are called in cros_make_image_bootable and mount_gpt_image, respectively. mount_gpt_image will always enable_rw_mount and cros_make_image_bootable will disable_rw_mount if --enable_rootfs_verification is true. The approach is ugly but reasonably well contained. If ext2 ever gets a new revision and new features in the same range are introduced, then we would be getting inconsistent behavior. That said, it is unlikely that that churmn will happen and if the impact is negative, it will ideally show up during testing. N.B., this will likely result in changes needing to be made to the signing scripts in vboot_reference to ensure that rw mounting is enabled/disabled in the same way (E.g., during stamping). BUG=chromium-os:7468 TEST=- built x86-generic, imaged to usb stick, attempted to mount rw /dev/sdc3 on the host and was properly bounced. - booted to the image just fine on a dogfood device. - mod'd for recovery, then installed and booted. - mod_image_for_test runs with no errors; booted the resulting image as well - booted a factory_install with the pending dm changes - BVT passed with build_image x86-generic (vboot enabled) - [in progress] autotest that checks if the rootdev = /dev/dm-0 and then does a dumpe2fs | grep -q FEATURE_R31 Review URL: http://codereview.chromium.org/3916002 Change-Id: If4dcba7568a110f4e32627c916d9e5741e5e5414
275 lines
9.7 KiB
Bash
Executable File
275 lines
9.7 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.
|
|
|
|
# Load common constants. This should be the first executable line.
|
|
# The path to common.sh should be relative to your script's location.
|
|
COMMON_PATH="$(dirname "$0")/../common.sh"
|
|
if [ ! -r "${COMMON_PATH}" ]; then
|
|
echo "ERROR! Cannot find common.sh: ${COMMON_PATH}" 1>&2
|
|
exit 1
|
|
fi
|
|
|
|
. "$(dirname "$0")/../common.sh"
|
|
|
|
# Script must be run inside the chroot.
|
|
assert_inside_chroot
|
|
|
|
set -e
|
|
. "$(dirname "$0")/../chromeos-common.sh" # for partoffset and partsize
|
|
|
|
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_depth 1 \
|
|
"Kernel verified boot hash tree depth"
|
|
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)"
|
|
|
|
# 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
|
|
|
|
# $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=
|
|
|
|
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_tree_depth=${FLAGS_verity_depth} \
|
|
--verity_max_ios=${FLAGS_verity_max_ios} \
|
|
--verity_error_behavior=${FLAGS_verity_error_behavior} \
|
|
--root=${cros_root} \
|
|
--keys_dir="${FLAGS_keys_dir}" \
|
|
${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}"
|
|
}
|
|
|
|
# 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_cleanup_dirs} -eq ${FLAGS_TRUE} ]; then
|
|
rmdir ${FLAGS_rootfs_mountpoint}
|
|
rmdir ${FLAGS_statefulfs_mountpoint}
|
|
rmdir ${FLAGS_espfs_mountpoint}
|
|
fi
|