Merge pull request #147 from marineam/disk_util

New approach to disk/partition handling, disk_util
This commit is contained in:
Michael Marineau 2014-01-05 20:37:38 -08:00
commit a63b07ea76
17 changed files with 1091 additions and 1993 deletions

View File

@ -1,299 +0,0 @@
#!/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=("$(dirname "$(readlink -f "$0")")/.." /usr/lib/crosutils)
local path
SCRIPT_ROOT="${common_paths[0]}"
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" || 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 || exit 1
. "${BUILD_LIBRARY_DIR}/toolchain_util.sh" || exit 1
. "${BUILD_LIBRARY_DIR}/build_image_util.sh" || exit 1
. "${BUILD_LIBRARY_DIR}/disk_layout_util.sh" || exit 1
switch_to_strict_mode
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 adjust_part "" \
"Adjustments to apply to the partition table"
DEFINE_string board "${DEFAULT_BOARD}" \
"Board we're building for."
DEFINE_string output_dir "/tmp" \
"Directory to place output in."
DEFINE_string image "coreos_base.img" \
"Full path to the coreos image to make bootable."
DEFINE_string arch "x86" \
"Architecture to make bootable for: arm, x86, or amd64"
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_boolean enable_rootfs_verification ${FLAGS_FALSE} \
"Default all bootloaders to NOT use kernel-based root fs integrity checking."
DEFINE_string keys_dir "/usr/share/vboot/devkeys" \
"Directory containing the signing keys."
DEFINE_string au_key "" \
"Filename of the au_key to install"
DEFINE_string production_track "" \
"Use production values and a given track for update service."
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(pkumar): Remove once known that no images are using this flag
DEFINE_boolean crosbug12352_arm_kernel_signing ${FLAGS_FALSE} \
"This flag is deprecated but the bots still need parse old images."
# TODO(sosa): Remove once known images no longer use this in their config.
DEFINE_string arm_extra_bootargs "" "DEPRECATED FLAG. Do not use."
DEFINE_boolean force_developer_mode ${FLAGS_FALSE} \
"Add cros_debug to boot args."
DEFINE_boolean enable_squashfs ${FLAGS_FALSE} \
"Make the rootfs of the image squashfs."
DEFINE_string squash_sort_file "" \
"Specify the priority of files when squashing the rootfs."
# Parse the boot.desc and any overrides
eval set -- "${BOOT_DESC} ${FLAG_OVERRIDES}"
FLAGS "${@}" || exit 1
. "${BUILD_LIBRARY_DIR}/board_options.sh" || exit 1
# Only now can we die on error. shflags functions leak non-zero error codes,
# so will die prematurely if 'switch_to_strict_mode' is specified before now.
switch_to_strict_mode -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=
# Default to non-verified
local enable_rootfs_verification_flag=--noenable_rootfs_verification
if [[ ${FLAGS_enable_rootfs_verification} -eq ${FLAGS_TRUE} ]]; then
enable_rootfs_verification_flag=--enable_rootfs_verification
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}"
legacy_offset_size_export ${image}
if [ -n "${FLAGS_production_track}" ]; then
# Replace /etc/lsb-release on the image.
"${BUILD_LIBRARY_DIR}/set_lsb_release" \
--production_track="${FLAGS_production_track}" \
--root="${FLAGS_rootfs_mountpoint}" \
--board="${BOARD}"
fi
# Install an auto update key on the root before sealing it off
if [ ! -z "${FLAGS_au_key}" ]; then
local key_location=${FLAGS_rootfs_mountpoint}"/usr/share/update_engine/"
sudo mkdir -p "${key_location}"
sudo cp "${FLAGS_au_key}" "$key_location/update-payload-key.pub.pem"
sudo chown root:root "$key_location/update-payload-key.pub.pem"
sudo chmod 644 "$key_location/update-payload-key.pub.pem"
echo "AU verification key was installed. Do not forget to resign the image!"
fi
# 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}"
# Newer `mount` will decode the filename backing the loop device,
# so we need to dig deeper and find the answer ourselves.
root_dev=$(awk -v mnt="${FLAGS_rootfs_mountpoint}" \
'$2 == mnt { print $1 }' /proc/mounts)
# 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
if [ ${FLAGS_force_developer_mode} -eq ${FLAGS_TRUE} ]; then
FLAGS_boot_args="${FLAGS_boot_args} cros_debug"
fi
local squash_sort_flag=
if [ -n "${FLAGS_squash_sort_file}" ]; then
squash_sort_flag="-sort ${FLAGS_squash_sort_file}"
fi
if [ $FLAGS_enable_squashfs -eq $FLAGS_TRUE ]; then
local squashfs_img="${FLAGS_output_dir}/squashfs.image"
sudo mksquashfs "${FLAGS_rootfs_mountpoint}" ${squashfs_img} -comp lzo \
-noI -noF -ef ${SCRIPTS_DIR}/exclude-list -wildcards ${squash_sort_flag}
root_dev=$squashfs_img
fi
# We should update the esp in place in the image.
local bootloader_to="${image}"
local esp_offset="$(partoffset ${image} ${NUM_ESP})"
esp_offset=$((esp_offset * 512)) # sectors to bytes
local esp_size="$(partsize ${image} ${NUM_ESP})"
esp_size=$((esp_size * 512)) # sectors to bytes
local bootloader_to_flags="--to_offset=${esp_offset} --to_size=${esp_size}"
# Update ESP partition
# NOTE: Boot kernel is identical to regular kernel for now
${SCRIPTS_DIR}/update_bootloaders.sh \
--arch=${FLAGS_arch} \
--to="${bootloader_to}" \
--from="${FLAGS_rootfs_mountpoint}"/boot \
--vmlinuz_boot_kernel="${FLAGS_rootfs_mountpoint}"/boot/vmlinuz \
--vmlinuz="${FLAGS_rootfs_mountpoint}"/boot/vmlinuz \
${bootloader_to_flags}
trap - EXIT
${SCRIPTS_DIR}/mount_gpt_image.sh -u -r "${FLAGS_rootfs_mountpoint}" \
-s "${FLAGS_statefulfs_mountpoint}"
# I can only copy the squashfs image to the image only when it is umounted.
if [ $FLAGS_enable_squashfs -eq $FLAGS_TRUE ]; then
# copy the squashfs image to the partition
info "copy the squashfs to the partition"
local part_offset="$(partoffset ${image} ${NUM_ROOTFS_A})"
sudo dd bs=512 if="${squashfs_img}" of="${image}" \
seek=${part_offset} conv=notrunc status=none
sudo rm "${squashfs_img}"
fi
}
verify_image_rootfs() {
local image=$1
local rootfs_offset="$(partoffset ${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}" \
status=none
# 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"
}
# Store output and temporary files next to image.
FLAGS_output_dir="${IMAGE_DIR}"
FLAGS_rootfs_mountpoint="${IMAGE_DIR}/rootfs_dir"
FLAGS_statefulfs_mountpoint="${IMAGE_DIR}/stateful_dir"
FLAGS_espfs_mountpoint="${IMAGE_DIR}/esp"
# 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}"
# We can't verify the image if squashfs is enabled because the kernel
# on the host does not support squashfs with LZO
if [ ${FLAGS_fsck_rootfs} -eq ${FLAGS_TRUE} \
-a ${FLAGS_enable_squashfs} -eq ${FLAGS_FALSE} ]; 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

View File

@ -10,11 +10,9 @@
# build the Chrome OS image using only pre-built binary packages. # build the Chrome OS image using only pre-built binary packages.
SCRIPT_ROOT=$(dirname $(readlink -f "$0")) SCRIPT_ROOT=$(dirname $(readlink -f "$0"))
. "${SCRIPT_ROOT}/build_library/build_common.sh" || exit 1 . "${SCRIPT_ROOT}/common.sh" || exit 1
# Developer-visible flags. # Developer-visible flags.
DEFINE_string adjust_part "" \
"Adjustments to apply to the partition table"
DEFINE_string board "${DEFAULT_BOARD}" \ DEFINE_string board "${DEFAULT_BOARD}" \
"The board to build an image for." "The board to build an image for."
DEFINE_string boot_args "" \ DEFINE_string boot_args "" \
@ -23,7 +21,7 @@ DEFINE_boolean enable_rootfs_verification ${FLAGS_TRUE} \
"Default all bootloaders to use kernel-based root fs integrity checking." "Default all bootloaders to use kernel-based root fs integrity checking."
DEFINE_string output_root "${DEFAULT_BUILD_ROOT}/images" \ DEFINE_string output_root "${DEFAULT_BUILD_ROOT}/images" \
"Directory in which to place image result directories (named by version)" "Directory in which to place image result directories (named by version)"
DEFINE_string disk_layout "default" \ DEFINE_string disk_layout "base" \
"The disk layout type to use for this image." "The disk layout type to use for this image."
# include upload options # include upload options
@ -75,10 +73,9 @@ check_gsutil_opts
# some of the files contain initialization used by later files. # some of the files contain initialization used by later files.
. "${BUILD_LIBRARY_DIR}/toolchain_util.sh" || exit 1 . "${BUILD_LIBRARY_DIR}/toolchain_util.sh" || exit 1
. "${BUILD_LIBRARY_DIR}/board_options.sh" || exit 1 . "${BUILD_LIBRARY_DIR}/board_options.sh" || exit 1
. "${BUILD_LIBRARY_DIR}/disk_layout_util.sh" || exit 1
. "${BUILD_LIBRARY_DIR}/mount_gpt_util.sh" || exit 1
. "${BUILD_LIBRARY_DIR}/build_image_util.sh" || exit 1 . "${BUILD_LIBRARY_DIR}/build_image_util.sh" || exit 1
. "${BUILD_LIBRARY_DIR}/base_image_util.sh" || exit 1 . "${BUILD_LIBRARY_DIR}/base_image_util.sh" || exit 1
. "${BUILD_LIBRARY_DIR}/prod_image_util.sh" || exit 1
. "${BUILD_LIBRARY_DIR}/dev_image_util.sh" || exit 1 . "${BUILD_LIBRARY_DIR}/dev_image_util.sh" || exit 1
. "${BUILD_LIBRARY_DIR}/test_image_content.sh" || exit 1 . "${BUILD_LIBRARY_DIR}/test_image_content.sh" || exit 1
@ -123,7 +120,7 @@ fi
mkdir -p "${BUILD_DIR}" mkdir -p "${BUILD_DIR}"
# Create the base image. # Create the base image.
create_base_image ${PRISTINE_IMAGE_NAME} ${FLAGS_enable_rootfs_verification} create_base_image ${PRISTINE_IMAGE_NAME}
if should_build_image ${PRISTINE_IMAGE_NAME}; then if should_build_image ${PRISTINE_IMAGE_NAME}; then
upload_image "${BUILD_DIR}/${PRISTINE_IMAGE_NAME}" upload_image "${BUILD_DIR}/${PRISTINE_IMAGE_NAME}"
fi fi
@ -148,11 +145,8 @@ fi
if should_build_image ${COREOS_PRODUCTION_IMAGE_NAME}; then if should_build_image ${COREOS_PRODUCTION_IMAGE_NAME}; then
copy_image ${CHROMEOS_BASE_IMAGE_NAME} ${COREOS_PRODUCTION_IMAGE_NAME} copy_image ${CHROMEOS_BASE_IMAGE_NAME} ${COREOS_PRODUCTION_IMAGE_NAME}
${SCRIPTS_DIR}/bin/cros_make_image_bootable \ setup_prod_image ${COREOS_PRODUCTION_IMAGE_NAME} "dev-channel" \
"${BUILD_DIR}" \ ${SRC_ROOT}/third_party/coreos-overlay/coreos-base/coreos-au-key/files/update-payload-key.pub.pem
${COREOS_PRODUCTION_IMAGE_NAME} \
--production_track="dev-channel" \
--au_key=${SRC_ROOT}/third_party/coreos-overlay/coreos-base/coreos-au-key/files/update-payload-key.pub.pem
upload_image "${BUILD_DIR}/${COREOS_PRODUCTION_IMAGE_NAME}" upload_image "${BUILD_DIR}/${COREOS_PRODUCTION_IMAGE_NAME}"
fi fi

View File

@ -4,142 +4,22 @@
. "${SRC_ROOT}/platform/dev/toolchain_utils.sh" || exit 1 . "${SRC_ROOT}/platform/dev/toolchain_utils.sh" || exit 1
# Overlays are parts of the disk that live on the state partition
ROOT_OVERLAYS=(var opt srv home usr/local)
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}"
fi
fi
echo "Cleaning up mounts"
safe_umount_tree "${root_fs_dir}"
safe_umount_tree "${state_fs_dir}"
safe_umount_tree "${esp_fs_dir}"
safe_umount_tree "${oem_fs_dir}"
# Turn die on error back on.
set -e
}
create_base_image() { create_base_image() {
local image_name=$1 local image_name=$1
local rootfs_verification_enabled=$2 local rootfs_verification_enabled=$2
get_disk_layout_type local disk_layout="${FLAGS_disk_layout:-base}"
local image_type="${DISK_LAYOUT_TYPE}" local disk_img="${BUILD_DIR}/${image_name}"
local mbr_img="/usr/share/syslinux/gptmbr.bin"
local root_fs_dir="${BUILD_DIR}/rootfs"
check_valid_layout "base" info "Using image type ${disk_layout}"
check_valid_layout ${image_type} "${BUILD_LIBRARY_DIR}/disk_util" --disk_layout="${disk_layout}" \
format --mbr_boot_code="${mbr_img}" "${disk_img}"
info "Using image type ${image_type}" "${BUILD_LIBRARY_DIR}/disk_util" --disk_layout="${disk_layout}" \
mount "${disk_img}" "${root_fs_dir}"
root_fs_dir="${BUILD_DIR}/rootfs" trap "cleanup_mounts '${root_fs_dir}' && delete_prompt" EXIT
state_fs_dir="${BUILD_DIR}/state"
esp_fs_dir="${BUILD_DIR}/esp"
oem_fs_dir="${BUILD_DIR}/oem"
trap "cleanup_mounts && delete_prompt" EXIT
cleanup_mounts &> /dev/null
local root_fs_label="ROOT-A"
local root_fs_num=$(get_num ${image_type} ${root_fs_label})
local root_fs_img="${BUILD_DIR}/rootfs.image"
local root_fs_bytes=$(get_filesystem_size ${image_type} ${root_fs_num})
local state_fs_label="STATE"
local state_fs_num=$(get_num ${image_type} ${state_fs_label})
local state_fs_img="${BUILD_DIR}/state.image"
local state_fs_bytes=$(get_filesystem_size ${image_type} ${state_fs_num})
local state_fs_uuid=$(uuidgen)
local esp_fs_label="EFI-SYSTEM"
local esp_fs_num=$(get_num ${image_type} ${esp_fs_label})
local esp_fs_img="${BUILD_DIR}/esp.image"
local esp_fs_bytes=$(get_filesystem_size ${image_type} ${esp_fs_num})
local oem_fs_label="OEM"
local oem_fs_num=$(get_num ${image_type} ${oem_fs_label})
local oem_fs_img="${BUILD_DIR}/oem.image"
local oem_fs_bytes=$(get_filesystem_size ${image_type} ${oem_fs_num})
local oem_fs_uuid=$(uuidgen)
local fs_block_size=$(get_fs_block_size)
# Build root FS image.
info "Building ${root_fs_img}"
truncate --size="${root_fs_bytes}" "${root_fs_img}"
/sbin/mkfs.ext2 -F -q -b ${fs_block_size} "${root_fs_img}" \
"$((root_fs_bytes / fs_block_size))"
/sbin/tune2fs -L "${root_fs_label}" \
-U clear \
-T 20091119110000 \
-c 0 \
-i 0 \
-m 0 \
-r 0 \
-e remount-ro \
"${root_fs_img}"
mkdir -p "${root_fs_dir}"
sudo mount -o loop "${root_fs_img}" "${root_fs_dir}"
df -h "${root_fs_dir}"
# Build state FS disk image.
info "Building ${state_fs_img}"
truncate --size="${state_fs_bytes}" "${state_fs_img}"
/sbin/mkfs.ext4 -F -q "${state_fs_img}"
/sbin/tune2fs -L "${state_fs_label}" -U "${state_fs_uuid}" \
-c 0 -i 0 "${state_fs_img}"
mkdir -p "${state_fs_dir}"
sudo mount -o loop "${state_fs_img}" "${state_fs_dir}"
# Build ESP disk image.
info "Building ${esp_fs_img}"
truncate --size="${esp_fs_bytes}" "${esp_fs_img}"
/usr/sbin/mkfs.vfat "${esp_fs_img}"
# Build OEM FS disk image.
info "Building ${oem_fs_img}"
truncate --size="${oem_fs_bytes}" "${oem_fs_img}"
/sbin/mkfs.ext4 -F -q "${oem_fs_img}"
/sbin/tune2fs -L "${oem_fs_label}" -U "${oem_fs_uuid}" \
-c 0 -i 0 "${oem_fs_img}"
mkdir -p "${oem_fs_dir}"
sudo mount -o loop "${oem_fs_img}" "${oem_fs_dir}"
# Prepare state partition with some pre-created directories.
info "Binding directories from state partition onto the rootfs"
for i in "${ROOT_OVERLAYS[@]}"; do
sudo mkdir -p "${state_fs_dir}/overlays/$i"
sudo mkdir -p "${root_fs_dir}/$i"
sudo mount --bind "${state_fs_dir}/overlays/$i" "${root_fs_dir}/$i"
done
# TODO(bp): remove these temporary fixes for /mnt/stateful_partition going moving
sudo mkdir -p "${root_fs_dir}/mnt/stateful_partition/"
sudo ln -s /media/state/overlays/usr/local "${root_fs_dir}/mnt/stateful_partition/dev_image"
sudo ln -s /media/state/overlays/home "${root_fs_dir}/mnt/stateful_partition/home"
sudo ln -s /media/state/overlays/var "${root_fs_dir}/mnt/stateful_partition/var_overlay"
sudo ln -s /media/state/etc "${root_fs_dir}/mnt/stateful_partition/etc"
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"
# First thing first, install baselayout with USE=build to create a # First thing first, install baselayout with USE=build to create a
# working directory tree. Don't use binpkgs due to the use flag change. # working directory tree. Don't use binpkgs due to the use flag change.
@ -171,53 +51,19 @@ create_base_image() {
--root="${root_fs_dir}" \ --root="${root_fs_dir}" \
--board="${BOARD}" --board="${BOARD}"
# 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
# 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.
local enable_rootfs_verification=
if [[ ${rootfs_verification_enabled} -eq ${FLAGS_TRUE} ]]; then
enable_rootfs_verification="--enable_rootfs_verification"
fi
${BUILD_LIBRARY_DIR}/create_legacy_bootloader_templates.sh \ ${BUILD_LIBRARY_DIR}/create_legacy_bootloader_templates.sh \
--arch=${ARCH} \ --arch=${ARCH} \
--to="${root_fs_dir}"/boot \ --boot_dir="${root_fs_dir}"/boot \
--boot_args="${FLAGS_boot_args}" \ --esp_dir="${root_fs_dir}"/boot/efi \
${enable_rootfs_verification} --boot_args="${FLAGS_boot_args}"
if [[ ${skip_test_image_content} -ne 1 ]]; then
# Check that the image has been correctly created.
test_image_content "$root_fs_dir"
fi
# Zero all fs free space to make it more compressible so auto-update # Zero all fs free space to make it more compressible so auto-update
# payloads become smaller, not fatal since it won't work on linux < 3.2 # payloads become smaller, not fatal since it won't work on linux < 3.2
sudo fstrim "${root_fs_dir}" || true sudo fstrim "${root_fs_dir}" || true
sudo fstrim "${state_fs_dir}" || true if [[ -d "${root_fs_dir}/media/state" ]]; then
sudo fstrim "${root_fs_dir}/media/state" || true
cleanup_mounts fi
# Create the GPT-formatted image.
build_gpt "${BUILD_DIR}/${image_name}" \
"${root_fs_img}" \
"${state_fs_img}" \
"${esp_fs_img}" \
"${oem_fs_img}"
# Clean up temporary files.
rm -f "${root_fs_img}" "${state_fs_img}" "${esp_fs_img}" "{oem_fs_img}"
# Emit helpful scripts for testers, etc.
emit_gpt_scripts "${BUILD_DIR}/${image_name}" "${BUILD_DIR}"
${SCRIPTS_DIR}/bin/cros_make_image_bootable "${BUILD_DIR}" \
${image_name} --adjust_part="${FLAGS_adjust_part}"
cleanup_mounts "${root_fs_dir}"
trap - EXIT trap - EXIT
} }

View File

@ -1,49 +0,0 @@
# 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.
# Common library file to be sourced by build_image,
# mod_image_for_test.sh, and mod_image_for_recovery.sh. This
# file ensures that library source files needed by all the scripts
# are included once, and also takes care of certain bookeeping tasks
# common to all the scripts.
# SCRIPT_ROOT must be set prior to sourcing this file
. "${SCRIPT_ROOT}/common.sh" || exit 1
# All scripts using this file must be run inside the chroot.
restart_in_chroot_if_needed "$@"
INSTALLER_ROOT=/usr/lib/installer
. "${INSTALLER_ROOT}/chromeos-common.sh" || exit 1
locate_gpt
should_build_image() {
# Fast pass back if we should build all incremental images.
local image_name
local image_to_build
for image_name in "$@"; do
for image_to_build in ${IMAGES_TO_BUILD}; do
[ "${image_to_build}" = "${image_name}" ] && return 0
done
done
return 1
}
# Utility function for creating a copy of an image prior to
# modification from the BUILD_DIR:
# $1: source filename
# $2: destination filename
copy_image() {
local src="${BUILD_DIR}/$1"
local dst="${BUILD_DIR}/$2"
if should_build_image $1; then
echo "Creating $2 from $1..."
cp --sparse=always "${src}" "${dst}" || die "Cannot copy $1 to $2"
else
mv "${src}" "${dst}" || die "Cannot move $1 to $2"
fi
}

View File

@ -55,6 +55,35 @@ parse_build_image_args() {
get_images_to_build ${FLAGS_ARGV} get_images_to_build ${FLAGS_ARGV}
} }
should_build_image() {
# Fast pass back if we should build all incremental images.
local image_name
local image_to_build
for image_name in "$@"; do
for image_to_build in ${IMAGES_TO_BUILD}; do
[ "${image_to_build}" = "${image_name}" ] && return 0
done
done
return 1
}
# Utility function for creating a copy of an image prior to
# modification from the BUILD_DIR:
# $1: source filename
# $2: destination filename
copy_image() {
local src="${BUILD_DIR}/$1"
local dst="${BUILD_DIR}/$2"
if should_build_image $1; then
echo "Creating $2 from $1..."
cp --sparse=always "${src}" "${dst}" || die "Cannot copy $1 to $2"
else
mv "${src}" "${dst}" || die "Cannot move $1 to $2"
fi
}
check_blacklist() { check_blacklist() {
info "Verifying that the base image does not contain a blacklisted package." info "Verifying that the base image does not contain a blacklisted package."
info "Generating list of packages for ${BASE_PACKAGE}." info "Generating list of packages for ${BASE_PACKAGE}."
@ -80,20 +109,9 @@ make_salt() {
xxd -l 32 -p -c 32 /dev/urandom xxd -l 32 -p -c 32 /dev/urandom
} }
create_boot_desc() { cleanup_mounts() {
local enable_rootfs_verification_flag="" echo "Cleaning up mounts"
if [[ ${FLAGS_enable_rootfs_verification} -eq ${FLAGS_TRUE} ]]; then "${BUILD_LIBRARY_DIR}/disk_util" umount "$1" || true
enable_rootfs_verification_flag="--enable_rootfs_verification"
fi
cat <<EOF > ${BUILD_DIR}/boot.desc
--board=${BOARD}
--arch="${ARCH}"
--keys_dir="${DEVKEYSDIR}"
--boot_args="${FLAGS_boot_args}"
--nocleanup_dirs
${enable_rootfs_verification_flag}
EOF
} }
delete_prompt() { delete_prompt() {

View File

@ -1,580 +0,0 @@
#!/usr/bin/python
# 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.
import json
import os
import re
import subprocess
import sys
import uuid
from optparse import OptionParser
# First sector we can use.
GPT_RESERVED_SECTORS = 34
class ConfigNotFound(Exception):
pass
class PartitionNotFound(Exception):
pass
class InvalidLayout(Exception):
pass
class InvalidAdjustment(Exception):
pass
def LoadPartitionConfig(filename):
"""Loads a partition tables configuration file into a Python object.
Args:
filename: Filename to load into object
Returns:
Object containing disk layout configuration
"""
valid_keys = set(('_comment', 'metadata', 'layouts'))
valid_layout_keys = set((
'_comment', 'type', 'num', 'label', 'blocks', 'block_size', 'fs_blocks',
'fs_block_size', 'features', 'uuid', 'alignment'))
if not os.path.exists(filename):
raise ConfigNotFound('Partition config %s was not found!' % filename)
with open(filename) as f:
config = json.load(f)
try:
metadata = config['metadata']
for key in ('alignment', 'block_size', 'fs_block_size'):
metadata[key] = int(metadata[key])
# Sometimes qemu-img expects disks sizes aligned to 64k
align_bytes = metadata['alignment'] * metadata['block_size']
if align_bytes < 65536 or align_bytes % 65536 != 0:
raise InvalidLayout('Invalid alignment, 64KB or better required')
unknown_keys = set(config.keys()) - valid_keys
if unknown_keys:
raise InvalidLayout('Unknown items: %r' % unknown_keys)
if len(config['layouts']) <= 0:
raise InvalidLayout('Missing "layouts" entries')
for layout_name, layout in config['layouts'].items():
for part in layout:
unknown_keys = set(part.keys()) - valid_layout_keys
if unknown_keys:
raise InvalidLayout('Unknown items in layout %s: %r' %
(layout_name, unknown_keys))
if part['type'] != 'blank':
for s in ('num', 'label'):
if not s in part:
raise InvalidLayout('Layout "%s" missing "%s"' % (layout_name, s))
part['alignment'] = int(part.get('alignment', metadata['alignment']))
part['blocks'] = int(part['blocks'])
part['bytes'] = part['blocks'] * metadata['block_size']
if 'fs_blocks' in part:
part['fs_blocks'] = int(part['fs_blocks'])
part['fs_bytes'] = part['fs_blocks'] * metadata['fs_block_size']
if part['fs_bytes'] > part['bytes']:
raise InvalidLayout(
'Filesystem may not be larger than partition: %s %s: %d > %d' %
(layout_name, part['label'], part['fs_bytes'], part['bytes']))
if 'uuid' in part:
try:
# double check the string formatting
part['uuid'] = str(uuid.UUID(part['uuid']))
except ValueError as e:
raise InvalidLayout('Invalid uuid %r: %s' % (part['uuid'], e))
else:
part['uuid'] = str(uuid.uuid4())
except KeyError as e:
raise InvalidLayout('Layout is missing required entries: %s' % e)
return config
def GetPartitionTable(options, config, image_type):
"""Generates requested image_type layout from a layout configuration.
This loads the base table and then overlays the requested layout over
the base layout.
Args:
options: Flags passed to the script
config: Partition configuration file object
image_type: Type of image eg base/test/dev/factory_install
Returns:
Object representing a selected partition table
"""
partitions = config['layouts']['base']
metadata = config['metadata']
if image_type != 'base':
for partition_t in config['layouts'][image_type]:
for partition in partitions:
if partition['type'] == 'blank' or partition_t['type'] == 'blank':
continue
if partition_t['num'] == partition['num']:
for k, v in partition_t.items():
partition[k] = v
for adjustment_str in options.adjust_part.split():
adjustment = adjustment_str.split(':')
if len(adjustment) < 2:
raise InvalidAdjustment('Adjustment specified was incomplete')
label = adjustment[0]
operator = adjustment[1][0]
operand = adjustment[1][1:]
ApplyPartitionAdjustment(partitions, metadata, label, operator, operand)
return partitions
def ApplyPartitionAdjustment(partitions, metadata, label, operator, operand):
"""Applies an adjustment to a partition specified by label
Args:
partitions: Partition table to modify
metadata: Partition table metadata
label: The label of the partition to adjust
operator: Type of adjustment (+/-/=)
operand: How much to adjust by
"""
partition = GetPartitionByLabel(partitions, label)
operand_digits = re.sub('\D', '', operand)
size_factor = block_factor = 1
suffix = operand[len(operand_digits):]
if suffix:
size_factors = { 'B': 0, 'K': 1, 'M': 2, 'G': 3, 'T': 4, }
try:
size_factor = size_factors[suffix[0].upper()]
except KeyError:
raise InvalidAdjustment('Unknown size type %s' % suffix)
if size_factor == 0 and len(suffix) > 1:
raise InvalidAdjustment('Unknown size type %s' % suffix)
block_factors = { '': 1024, 'B': 1000, 'IB': 1024, }
try:
block_factor = block_factors[suffix[1:].upper()]
except KeyError:
raise InvalidAdjustment('Unknown size type %s' % suffix)
operand_bytes = int(operand_digits) * pow(block_factor, size_factor)
if operand_bytes % metadata['block_size'] == 0:
operand_blocks = operand_bytes / metadata['block_size']
else:
raise InvalidAdjustment('Adjustment size not divisible by block size')
if operator == '+':
partition['blocks'] += operand_blocks
partition['bytes'] += operand_bytes
elif operator == '-':
partition['blocks'] -= operand_blocks
partition['bytes'] -= operand_bytes
elif operator == '=':
partition['blocks'] = operand_blocks
partition['bytes'] = operand_bytes
else:
raise ValueError('unknown operator %s' % operator)
if partition['type'] == 'rootfs':
# If we're adjusting a rootFS partition, we assume the full partition size
# specified is being used for the filesytem, minus the space reserved for
# the hashpad.
partition['fs_bytes'] = partition['bytes']
partition['fs_blocks'] = partition['fs_bytes'] / metadata['fs_block_size']
partition['blocks'] = int(partition['blocks'] * 1.15)
partition['bytes'] = partition['blocks'] * metadata['block_size']
def GetPartitionTableFromConfig(options, layout_filename, image_type):
"""Loads a partition table and returns a given partition table type
Args:
options: Flags passed to the script
layout_filename: The filename to load tables from
image_type: The type of partition table to return
"""
config = LoadPartitionConfig(layout_filename)
partitions = GetPartitionTable(options, config, image_type)
return partitions
def WritePartitionTable(options, image_type, layout_filename, disk_filename):
"""Writes the given partition table to a disk image or device.
Args:
options: Flags passed to the script
image_type: Type of image eg base/test/dev/factory_install
layout_filename: Path to partition configuration file
disk_filename: Path to disk image or device file
"""
def Cgpt(*args):
subprocess.check_call(['cgpt'] + [str(a) for a in args])
def Align(count, alignment):
offset = count % alignment
if offset:
count += alignment - offset
return count
config = LoadPartitionConfig(layout_filename)
metadata = config['metadata']
partitions = GetPartitionTable(options, config, image_type)
disk_block_count = GPT_RESERVED_SECTORS
for partition in partitions:
disk_block_count = Align(disk_block_count, partition['alignment'])
disk_block_count += partition['blocks']
disk_block_count += GPT_RESERVED_SECTORS
# Sometimes qemu-img expects disks sizes aligned to 64k
disk_block_count = Align(disk_block_count, config['metadata']['alignment'])
Cgpt('create', '-c', '-s', disk_block_count, disk_filename)
sector = GPT_RESERVED_SECTORS
for partition in partitions:
sector = Align(sector, partition['alignment'])
if partition['type'] != 'blank':
Cgpt('add', '-i', partition['num'],
'-b', sector,
'-s', partition['blocks'],
'-t', partition['type'],
'-l', partition['label'],
'-u', partition['uuid'],
disk_filename)
sector += partition['blocks']
Cgpt('show', disk_filename)
def WriteMbrBoot(options, image_type, layout_filename,
disk_filename, mbr_filename):
"""Writes the protective MBR with the given boot code.
The EFI System Partition will be marked as the 'boot' partition.
Args:
options: Flags passed to the script
image_type: Type of image eg base/test/dev/factory_install
layout_filename: Path to partition configuration file
disk_filename: Path to disk image or device file
mbr_filename: Path to boot code, usually gptmbr.bin from syslinux.
"""
config = LoadPartitionConfig(layout_filename)
partitions = GetPartitionTable(options, config, image_type)
esp_number = None
for partition in partitions:
if partition['type'] == 'efi':
esp_number = partition['num']
break
if esp_number is None:
raise InvalidLayout('Table does not include an EFI partition.')
subprocess.check_call(['cgpt', 'boot', '-p', '-b', mbr_filename,
'-i', str(partition['num']), disk_filename])
def GetPartitionByNumber(partitions, num):
"""Given a partition table and number returns the partition object.
Args:
partitions: List of partitions to search in
num: Number of partition to find
Returns:
An object for the selected partition
"""
for partition in partitions:
if partition['type'] == 'blank':
continue
if partition['num'] == int(num):
return partition
raise PartitionNotFound('Partition not found')
def GetPartitionByLabel(partitions, label):
"""Given a partition table and label returns the partition object.
Args:
partitions: List of partitions to search in
label: Label of partition to find
Returns:
An object for the selected partition
"""
for partition in partitions:
if 'label' not in partition:
continue
if partition['label'] == label:
return partition
raise PartitionNotFound('Partition not found')
def GetBlockSize(options, layout_filename):
"""Returns the partition table block size.
Args:
options: Flags passed to the script
layout_filename: Path to partition configuration file
Returns:
Block size of all partitions in the layout
"""
config = LoadPartitionConfig(layout_filename)
return config['metadata']['block_size']
def GetFilesystemBlockSize(options, layout_filename):
"""Returns the filesystem block size.
Args:
options: Flags passed to the script
This is used for all partitions in the table that have filesystems.
Args:
layout_filename: Path to partition configuration file
Returns:
Block size of all filesystems in the layout
"""
config = LoadPartitionConfig(layout_filename)
return config['metadata']['fs_block_size']
def GetPartitionSize(options, image_type, layout_filename, num):
"""Returns the partition size of a given partition for a given layout type.
Args:
options: Flags passed to the script
image_type: Type of image eg base/test/dev/factory_install
layout_filename: Path to partition configuration file
num: Number of the partition you want to read from
Returns:
Size of selected partition in bytes
"""
partitions = GetPartitionTableFromConfig(options, layout_filename, image_type)
partition = GetPartitionByNumber(partitions, num)
return partition['bytes']
def GetFilesystemSize(options, image_type, layout_filename, num):
"""Returns the filesystem size of a given partition for a given layout type.
If no filesystem size is specified, returns the partition size.
Args:
options: Flags passed to the script
image_type: Type of image eg base/test/dev/factory_install
layout_filename: Path to partition configuration file
num: Number of the partition you want to read from
Returns:
Size of selected partition filesystem in bytes
"""
partitions = GetPartitionTableFromConfig(options, layout_filename, image_type)
partition = GetPartitionByNumber(partitions, num)
if 'fs_bytes' in partition:
return partition['fs_bytes']
else:
return partition['bytes']
def GetLabel(options, image_type, layout_filename, num):
"""Returns the label for a given partition.
Args:
options: Flags passed to the script
image_type: Type of image eg base/test/dev/factory_install
layout_filename: Path to partition configuration file
num: Number of the partition you want to read from
Returns:
Label of selected partition, or 'UNTITLED' if none specified
"""
partitions = GetPartitionTableFromConfig(options, layout_filename, image_type)
partition = GetPartitionByNumber(partitions, num)
if 'label' in partition:
return partition['label']
else:
return 'UNTITLED'
def GetNum(options, image_type, layout_filename, label):
"""Returns the number for a given label.
Args:
options: Flags passed to the script
image_type: Type of image eg base/test/dev/factory_install
layout_filename: Path to partition configuration file
label: Label of the partition you want to read from
Returns:
Number of selected partition, or '-1' if there is no number
"""
partitions = GetPartitionTableFromConfig(options, layout_filename, image_type)
partition = GetPartitionByLabel(partitions, label)
if 'num' in partition:
return partition['num']
else:
return '-1'
def GetUuid(options, image_type, layout_filename, label):
"""Returns the unique partition UUID for a given label.
Note: Only useful if the UUID is specified in the config file, otherwise
the value returned unlikely to be what is actually used in the image.
Args:
options: Flags passed to the script
image_type: Type of image eg base/test/dev/prod
layout_filename: Path to partition configuration file
label: Label of the partition you want to read from
Returns:
String containing the requested UUID
"""
partitions = GetPartitionTableFromConfig(options, layout_filename, image_type)
partition = GetPartitionByLabel(partitions, label)
return partition['uuid']
def DoDebugOutput(options, image_type, layout_filename):
"""Prints out a human readable disk layout in on-disk order.
This will round values larger than 1MB, it's exists to quickly
visually verify a layout looks correct.
Args:
options: Flags passed to the script
image_type: Type of image eg base/test/dev/factory_install
layout_filename: Path to partition configuration file
"""
partitions = GetPartitionTableFromConfig(options, layout_filename, image_type)
for partition in partitions:
if partition['bytes'] < 1024 * 1024:
size = '%d bytes' % partition['bytes']
else:
size = '%d MB' % (partition['bytes'] / 1024 / 1024)
if 'label' in partition:
if 'fs_bytes' in partition:
if partition['fs_bytes'] < 1024 * 1024:
fs_size = '%d bytes' % partition['fs_bytes']
else:
fs_size = '%d MB' % (partition['fs_bytes'] / 1024 / 1024)
print '%s - %s/%s' % (partition['label'], fs_size, size)
else:
print '%s - %s' % (partition['label'], size)
else:
print 'blank - %s' % size
def DoParseOnly(options, image_type, layout_filename):
"""Parses a layout file only, used before reading sizes to check for errors.
Args:
options: Flags passed to the script
image_type: Type of image eg base/test/dev/factory_install
layout_filename: Path to partition configuration file
"""
partitions = GetPartitionTableFromConfig(options, layout_filename, image_type)
def main(argv):
action_map = {
'write_gpt': {
'usage': ['<image_type>', '<partition_config_file>', '<disk_image>'],
'func': WritePartitionTable,
},
'write_mbr': {
'usage': ['<image_type>', '<partition_config_file>', '<disk_image>',
'<mbr_boot_code>'],
'func': WriteMbrBoot,
},
'readblocksize': {
'usage': ['<partition_config_file>'],
'func': GetBlockSize,
},
'readfsblocksize': {
'usage': ['<partition_config_file>'],
'func': GetFilesystemBlockSize,
},
'readpartsize': {
'usage': ['<image_type>', '<partition_config_file>', '<partition_num>'],
'func': GetPartitionSize,
},
'readfssize': {
'usage': ['<image_type>', '<partition_config_file>', '<partition_num>'],
'func': GetFilesystemSize,
},
'readlabel': {
'usage': ['<image_type>', '<partition_config_file>', '<partition_num>'],
'func': GetLabel,
},
'readnum': {
'usage': ['<image_type>', '<partition_config_file>', '<label>'],
'func': GetNum,
},
'readuuid': {
'usage': ['<image_type>', '<partition_config_file>', '<label>'],
'func': GetUuid,
},
'debug': {
'usage': ['<image_type>', '<partition_config_file>'],
'func': DoDebugOutput,
},
'parseonly': {
'usage': ['<image_type>', '<partition_config_file>'],
'func': DoParseOnly,
}
}
parser = OptionParser()
parser.add_option("--adjust_part", dest="adjust_part",
help="adjust partition sizes", default="")
(options, args) = parser.parse_args()
if len(args) < 1 or args[0] not in action_map:
print 'Usage: %s <action>\n' % sys.argv[0]
print 'Valid actions are:'
for action in action_map:
print ' %s %s' % (action, ' '.join(action_map[action]['usage']))
sys.exit(1)
else:
action_name = args[0]
action = action_map[action_name]
if len(action['usage']) == len(args) - 1:
print action['func'](options, *args[1:])
else:
sys.exit('Usage: %s %s %s' % (sys.argv[0], args[0],
' '.join(action['usage'])))
if __name__ == '__main__':
main(sys.argv)

View File

@ -16,37 +16,34 @@ assert_inside_chroot
# Flags. # Flags.
DEFINE_string arch "x86" \ DEFINE_string arch "x86" \
"The boot architecture: arm or x86. (Default: x86)" "The boot architecture: arm or x86. (Default: x86)"
DEFINE_string to "/tmp/boot" \ DEFINE_string boot_dir "/tmp/boot" \
"Path to populate with bootloader templates (Default: /tmp/boot)" "Path to boot directory in root filesystem (Default: /tmp/boot)"
DEFINE_string esp_dir "" \
"Path to ESP partition mount point (Default: none)"
DEFINE_string boot_args "" \ DEFINE_string boot_args "" \
"Additional boot arguments to pass to the commandline (Default: '')" "Additional boot arguments to pass to the commandline (Default: '')"
DEFINE_boolean enable_rootfs_verification ${FLAGS_FALSE} \
"Controls if verity is used for root filesystem checking (Default: false)"
# Parse flags # Parse flags
FLAGS "$@" || exit 1 FLAGS "$@" || exit 1
eval set -- "${FLAGS_ARGV}" eval set -- "${FLAGS_ARGV}"
switch_to_strict_mode switch_to_strict_mode
# Useful for getting partition UUID values
. "${BUILD_LIBRARY_DIR}/disk_layout_util.sh" || exit 1
# Common kernel command-line args # Common kernel command-line args
common_args="console=tty0 ro noswap cros_legacy" common_args="console=tty0 ro noswap cros_legacy"
common_args="${common_args} ${FLAGS_boot_args}" common_args="${common_args} ${FLAGS_boot_args}"
# Populate the x86 rootfs to support legacy and EFI bios config templates. # Get partition UUIDs from the json config
# The templates are used by the installer to populate partition 12 with get_uuid() {
# the correct bootloader configuration. "${BUILD_LIBRARY_DIR}/disk_util" readuuid "$1"
if [[ "${FLAGS_arch}" = "x86" || "${FLAGS_arch}" = "amd64" ]]; then }
sudo mkdir -p ${FLAGS_to} ROOTA="PARTUUID=$(get_uuid ROOT-A)"
ROOTB="PARTUUID=$(get_uuid ROOT-B)"
# Get partition UUIDs from the json config GRUB_DIR="${FLAGS_boot_dir}/grub"
ROOTA="PARTUUID=$(get_uuid base ROOT-A)" SYSLINUX_DIR="${FLAGS_boot_dir}/syslinux"
ROOTB="PARTUUID=$(get_uuid base ROOT-B)"
# Build configuration files for pygrub/pvgrub # Build configuration files for pygrub/pvgrub
GRUB_DIR="${FLAGS_to}/grub" configure_grub() {
sudo mkdir -p "${GRUB_DIR}" sudo mkdir -p "${GRUB_DIR}"
# Add hvc0 for hypervisors # Add hvc0 for hypervisors
@ -71,8 +68,10 @@ EOF
sudo_append "${GRUB_DIR}/menu.lst.B" <"${GRUB_DIR}/menu.lst.A" sudo_append "${GRUB_DIR}/menu.lst.B" <"${GRUB_DIR}/menu.lst.A"
info "Emitted ${GRUB_DIR}/menu.lst.B" info "Emitted ${GRUB_DIR}/menu.lst.B"
sudo cp ${GRUB_DIR}/menu.lst.A ${GRUB_DIR}/menu.lst sudo cp ${GRUB_DIR}/menu.lst.A ${GRUB_DIR}/menu.lst
}
SYSLINUX_DIR="${FLAGS_to}/syslinux" # Build configuration files for syslinux
configure_syslinux() {
sudo mkdir -p "${SYSLINUX_DIR}" sudo mkdir -p "${SYSLINUX_DIR}"
# Add ttyS0 as a secondary console, useful for qemu -nographic # Add ttyS0 as a secondary console, useful for qemu -nographic
@ -121,8 +120,32 @@ label coreos.B
append ${syslinux_args} root=${ROOTB} append ${syslinux_args} root=${ROOTB}
EOF EOF
info "Emitted ${SYSLINUX_DIR}/root.B.cfg" info "Emitted ${SYSLINUX_DIR}/root.B.cfg"
}
exit 0 # Copy configurations to the ESP, this is what is actually used to boot
copy_to_esp() {
if ! mountpoint -q "${FLAGS_esp_dir}"; then
die "${FLAGS_esp_dir} is not a mount point."
fi
sudo mkdir -p "${FLAGS_esp_dir}"/{syslinux,boot/grub,EFI/boot}
sudo cp -r "${GRUB_DIR}/." "${FLAGS_esp_dir}/boot/grub"
sudo cp -r "${SYSLINUX_DIR}/." "${FLAGS_esp_dir}/syslinux"
# Stage all kernels with the only one we built.
for kernel in syslinux/{vmlinuz-boot_kernel,vmlinuz.A,vmlinuz.B} \
EFI/boot/bootx64.efi
do
sudo cp "${FLAGS_boot_dir}/vmlinuz" "${FLAGS_esp_dir}/${kernel}"
done
}
if [[ "${FLAGS_arch}" = "x86" || "${FLAGS_arch}" = "amd64" ]]; then
configure_grub
configure_syslinux
if [[ -n "${FLAGS_esp_dir}" ]]; then
copy_to_esp
fi
else
error "No bootloader configuration for ${FLAGS_arch}"
fi fi
info "The target platform does not use bootloader templates."

View File

@ -13,11 +13,12 @@ install_dev_packages() {
local image_name=$1 local image_name=$1
info "Adding developer packages to ${image_name}" info "Adding developer packages to ${image_name}"
local disk_layout="${FLAGS_disk_layout:-base}"
local root_fs_dir="${BUILD_DIR}/rootfs"
trap "unmount_image ; delete_prompt" EXIT "${BUILD_LIBRARY_DIR}/disk_util" --disk_layout="${disk_layout}" \
mount "${BUILD_DIR}/${image_name}" "${root_fs_dir}"
mount_image "${BUILD_DIR}/${image_name}" "${root_fs_dir}" \ trap "cleanup_mounts '${root_fs_dir}' && delete_prompt" EXIT
"${state_fs_dir}" "${esp_fs_dir}"
# Install developer packages described in coreos-dev. # Install developer packages described in coreos-dev.
emerge_to_image --root="${root_fs_dir}" coreos-base/coreos-dev emerge_to_image --root="${root_fs_dir}" coreos-base/coreos-dev
@ -37,16 +38,12 @@ install_dev_packages() {
# Zero all fs free space, not fatal since it won't work on linux < 3.2 # Zero all fs free space, not fatal since it won't work on linux < 3.2
sudo fstrim "${root_fs_dir}" || true sudo fstrim "${root_fs_dir}" || true
sudo fstrim "${state_fs_dir}" || true if [[ -d "${root_fs_dir}/media/state" ]]; then
sudo fstrim "${root_fs_dir}/media/state" || true
fi
info "Developer image built and stored at ${image_name}" info "Developer image built and stored at ${image_name}"
cleanup_mounts cleanup_mounts "${root_fs_dir}"
if should_build_image ${image_name}; then
${SCRIPTS_DIR}/bin/cros_make_image_bootable "${BUILD_DIR}" \
${image_name} --force_developer_mode --noenable_rootfs_verification
fi
trap - EXIT trap - EXIT
} }

View File

@ -1,305 +0,0 @@
# 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.
CGPT_PY="${BUILD_LIBRARY_DIR}/cgpt.py"
cgpt_py() {
if [[ -n "${FLAGS_adjust_part-}" ]]; then
set -- --adjust_part "${FLAGS_adjust_part}" "$@"
if [[ ! -t 0 ]]; then
warn "The --adjust_part flag was passed." \
"This option must ONLY be used interactively. If" \
"you need to pass a size from another script, you're" \
"doing it wrong and should be using a disk layout type."
fi
fi
"${CGPT_PY}" "$@"
}
get_disk_layout_path() {
DISK_LAYOUT_PATH="${BUILD_LIBRARY_DIR}/legacy_disk_layout.json"
}
write_partition_table() {
local image_type=$1
local outdev=$2
local pmbr_img
case ${ARCH} in
arm)
pmbr_img=/dev/zero
;;
amd64|x86)
pmbr_img=$(readlink -f /usr/share/syslinux/gptmbr.bin)
;;
*)
error "Unknown architecture: $ARCH"
return 1
;;
esac
get_disk_layout_path
cgpt_py write_gpt "${image_type}" "${DISK_LAYOUT_PATH}" "${outdev}"
cgpt_py write_mbr \
"${image_type}" "${DISK_LAYOUT_PATH}" "${outdev}" "${pmbr_img}"
}
get_fs_block_size() {
get_disk_layout_path
cgpt_py readfsblocksize "${DISK_LAYOUT_PATH}"
}
get_block_size() {
get_disk_layout_path
cgpt_py readblocksize "${DISK_LAYOUT_PATH}"
}
get_partition_size() {
local image_type=$1
local part_id=$2
get_disk_layout_path
cgpt_py readpartsize "${image_type}" "${DISK_LAYOUT_PATH}" ${part_id}
}
get_filesystem_size() {
local image_type=$1
local part_id=$2
get_disk_layout_path
cgpt_py readfssize "${image_type}" "${DISK_LAYOUT_PATH}" ${part_id}
}
get_label() {
local image_type=$1
local part_id=$2
get_disk_layout_path
cgpt_py readlabel "${image_type}" "${DISK_LAYOUT_PATH}" ${part_id}
}
get_num() {
local image_type=$1
local label=$2
get_disk_layout_path
cgpt_py readnum "${image_type}" "${DISK_LAYOUT_PATH}" ${label}
}
get_uuid() {
local image_type=$1
local label=$2
get_disk_layout_path
cgpt_py readuuid "${image_type}" "${DISK_LAYOUT_PATH}" ${label}
}
check_valid_layout() {
local image_type=$1
get_disk_layout_path
cgpt_py parseonly "${image_type}" "${DISK_LAYOUT_PATH}" > /dev/null
}
get_disk_layout_type() {
DISK_LAYOUT_TYPE="base"
if [[ -n "${FLAGS_disk_layout}" && \
"${FLAGS_disk_layout}" != "default" ]]; then
DISK_LAYOUT_TYPE="${FLAGS_disk_layout}"
fi
}
emit_gpt_scripts() {
local image="$1"
local dir="$2"
local pack="${dir}/pack_partitions.sh"
local unpack="${dir}/unpack_partitions.sh"
local mount="${dir}/mount_image.sh"
local umount="${dir}/umount_image.sh"
local start size part x
cat >"${unpack}" <<EOF
#!/bin/bash -eu
# File automatically generated. Do not edit.
TARGET=\${1:-}
if [[ -z \${TARGET} ]]; then
echo "Usage: \$0 <image>" 1>&2
echo "Example: \$0 $COREOS_IMAGE_NAME" 1>&2
exit 1
fi
set -x
$(${GPT} show "${image}" | sed -e 's/^/# /')
EOF
for x in "${pack}" "${mount}" "${umount}"; do
cp "${unpack}" "${x}"
done
while read start size part x; do
local file="part_${part}"
local dir="dir_${part}"
local target='"${TARGET}"'
local dd_args="bs=512 count=${size} conv=sparse"
local start_b=$(( start * 512 ))
local size_b=$(( size * 512 ))
echo "dd if=${target} of=${file} ${dd_args} skip=${start}" >>"${unpack}"
echo "dd if=${file} of=${target} ${dd_args} seek=${start} conv=notrunc" \
>>"${pack}"
if [[ ${size} -gt 1 ]]; then
cat <<-EOF >>"${mount}"
mkdir -p ${dir}
m=( sudo mount -o loop,offset=${start_b},sizelimit=${size_b} ${target} ${dir} )
if ! "\${m[@]}"; then
if ! "\${m[@]}" -o ro; then
rmdir ${dir}
fi
fi
EOF
cat <<-EOF >>"${umount}"
if [[ -d ${dir} ]]; then
sudo umount ${dir} || :
rmdir ${dir}
fi
EOF
fi
done < <(${GPT} show -q "${image}")
chmod +x "${unpack}" "${pack}" "${mount}" "${umount}"
}
build_gpt() {
local outdev="$1"
local rootfs_img="$2"
local stateful_img="$3"
local esp_img="$4"
local oem_img="$5"
get_disk_layout_type
write_partition_table "${DISK_LAYOUT_TYPE}" "${outdev}"
local sudo=
if [ ! -w "$outdev" ] ; then
# use sudo when writing to a block device.
sudo=sudo
fi
local root_fs_label="ROOT-A"
local root_fs_num=$(get_num ${image_type} ${root_fs_label})
local stateful_fs_label="STATE"
local stateful_fs_num=$(get_num ${image_type} ${stateful_fs_label})
local esp_fs_label="EFI-SYSTEM"
local esp_fs_num=$(get_num ${image_type} ${esp_fs_label})
local oem_fs_label="OEM"
local oem_fs_num=$(get_num ${image_type} ${oem_fs_label})
# Now populate the partitions.
info "Copying stateful partition..."
$sudo dd if="$stateful_img" of="$outdev" conv=notrunc,sparse bs=512 \
seek=$(partoffset ${outdev} ${stateful_fs_num}) status=none
info "Copying rootfs..."
$sudo dd if="$rootfs_img" of="$outdev" conv=notrunc,sparse bs=512 \
seek=$(partoffset ${outdev} ${root_fs_num}) status=none
info "Copying EFI system partition..."
$sudo dd if="$esp_img" of="$outdev" conv=notrunc,sparse bs=512 \
seek=$(partoffset ${outdev} ${esp_fs_num}) status=none
info "Copying OEM partition..."
$sudo dd if="$oem_img" of="$outdev" conv=notrunc,sparse bs=512 \
seek=$(partoffset ${outdev} ${oem_fs_num}) status=none
# Pre-set "sucessful" bit in gpt, so we will never mark-for-death
# a partition on an SDCard/USB stick.
cgpt add -i 2 -S 1 "$outdev"
}
# Rebuild an image's partition table with new stateful size.
# $1: source image filename
# $2: source stateful partition image filename
# $3: number of sectors to allocate to the new stateful partition
# $4: destination image filename
# Used by dev/host/tests/mod_recovery_for_decryption.sh and
# mod_image_for_recovery.sh.
update_partition_table() {
local src_img=$1 # source image
local src_state=$2 # stateful partition image
local dst_stateful_blocks=$3 # number of blocks in resized stateful partition
local dst_img=$4
rm -f "${dst_img}"
# Calculate change in image size.
local src_stateful_blocks=$(cgpt show -i 1 -s ${src_img})
local delta_blocks=$(( dst_stateful_blocks - src_stateful_blocks ))
local dst_stateful_bytes=$(( dst_stateful_blocks * 512 ))
local src_stateful_bytes=$(( src_stateful_blocks * 512 ))
local src_size=$(stat -c %s ${src_img})
local dst_size=$(( src_size - src_stateful_bytes + dst_stateful_bytes ))
truncate -s ${dst_size} ${dst_img}
# Copy MBR, initialize GPT.
dd if="${src_img}" of="${dst_img}" conv=notrunc bs=512 count=1 status=none
cgpt create ${dst_img}
# Find partition number of STATE (really should always be "1")
local part=0
local label=""
while [ "${label}" != "STATE" ]; do
part=$(( part + 1 ))
local label=$(cgpt show -i ${part} -l ${src_img})
local src_start=$(cgpt show -i ${part} -b ${src_img})
if [ ${src_start} -eq 0 ]; then
echo "Could not find 'STATE' partition" >&2
return 1
fi
done
local src_state_start=$(cgpt show -i ${part} -b ${src_img})
# Duplicate each partition entry.
part=0
while :; do
part=$(( part + 1 ))
local src_start=$(cgpt show -i ${part} -b ${src_img})
if [ ${src_start} -eq 0 ]; then
# No more partitions to copy.
break
fi
local dst_start=${src_start}
# Load source partition details.
local size=$(cgpt show -i ${part} -s ${src_img})
local label=$(cgpt show -i ${part} -l ${src_img})
local attr=$(cgpt show -i ${part} -A ${src_img})
local tguid=$(cgpt show -i ${part} -t ${src_img})
local uguid=$(cgpt show -i ${part} -u ${src_img})
# Change size of stateful.
if [ "${label}" = "STATE" ]; then
size=${dst_stateful_blocks}
fi
# Partitions located after STATE need to have their start moved.
if [ ${src_start} -gt ${src_state_start} ]; then
dst_start=$(( dst_start + delta_blocks ))
fi
# Add this partition to the destination.
cgpt add -i ${part} -b ${dst_start} -s ${size} -l "${label}" -A ${attr} \
-t ${tguid} -u ${uguid} ${dst_img}
if [ "${label}" != "STATE" ]; then
# Copy source partition as-is.
dd if="${src_img}" of="${dst_img}" conv=notrunc,sparse bs=512 \
skip=${src_start} seek=${dst_start} count=${size} status=none
else
# Copy new stateful partition into place.
dd if="${src_state}" of="${dst_img}" conv=notrunc,sparse bs=512 \
seek=${dst_start} status=none
fi
done
return 0
}

859
build_library/disk_util Executable file
View File

@ -0,0 +1,859 @@
#!/usr/bin/python
# 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.
import argparse
import contextlib
import json
import os
import subprocess
import sys
import tempfile
import uuid
# First sector we can use.
GPT_RESERVED_SECTORS = 34
class ConfigNotFound(Exception):
pass
class PartitionNotFound(Exception):
pass
class InvalidLayout(Exception):
pass
class InvalidAdjustment(Exception):
pass
def LoadPartitionConfig(options):
"""Loads a partition tables configuration file into a Python object.
Args:
options: Flags passed to the script
Returns:
Object containing disk layout configuration
"""
valid_keys = set(('_comment', 'metadata', 'layouts'))
valid_layout_keys = set((
'_comment', 'type', 'num', 'label', 'blocks', 'block_size', 'fs_blocks',
'fs_block_size', 'fs_type', 'features', 'uuid', 'alignment', 'mount',
'binds'))
integer_layout_keys = set((
'blocks', 'block_size', 'fs_blocks', 'fs_block_size', 'alignment'))
required_layout_keys = set(('type', 'num', 'label', 'blocks'))
filename = options.disk_layout_file
if not os.path.exists(filename):
raise ConfigNotFound('Partition config %s was not found!' % filename)
with open(filename) as f:
config = json.load(f)
unknown_keys = set(config.keys()) - valid_keys
if unknown_keys:
raise InvalidLayout('Unknown items: %s' % ' '.join(unknown_keys))
try:
metadata = config['metadata']
base = config['layouts']['base']
for key in ('alignment', 'block_size', 'fs_block_size'):
metadata[key] = int(metadata[key])
except KeyError as e:
raise InvalidLayout('Metadata is missing required entries: %s' % e)
# Sometimes qemu-img expects disks sizes aligned to 64k
align_bytes = metadata['alignment'] * metadata['block_size']
if align_bytes < 65536 or align_bytes % 65536 != 0:
raise InvalidLayout('Invalid alignment, 64KB or better required')
def VerifyLayout(layout_name, layout, base=None):
for part_num, part in layout.iteritems():
part['num'] = int(part_num)
part_keys = set(part.iterkeys())
unknown_keys = part_keys - valid_layout_keys
if unknown_keys:
raise InvalidLayout('Unknown items in partition %s %s: %r' %
(layout_name, part_num, ' '.join(unknown_keys)))
for int_key in integer_layout_keys.intersection(part_keys):
part[int_key] = int(part[int_key])
if base:
part_base = base.get(part_num, {})
part_keys.update(part_base.iterkeys())
if part.get('type', None) == 'blank':
continue
missing_keys = required_layout_keys - part_keys
if missing_keys:
raise InvalidLayout('Missing items in partition %s %s: %s' %
(layout_name, part_num, ' '.join(missing_keys)))
if 'uuid' in part:
try:
# double check the string formatting
part['uuid'] = str(uuid.UUID(part['uuid']))
except ValueError as e:
raise InvalidLayout('Invalid uuid %r: %s' % (part['uuid'], e))
if 'fs_type' in part:
if part['fs_type'] not in ('ext2', 'ext4', 'vfat'):
raise InvalidLayout('Invalid fs_type: %r' % part['fs_type'])
def Align(count, alignment):
offset = count % alignment
if offset:
count += alignment - offset
return count
def FillExtraValues(layout_name, layout, base=None):
# Reserved size for first GPT
disk_block_count = GPT_RESERVED_SECTORS
# Fill in default values from base,
# dict doesn't have a update()+setdefault() method so this looks tedious
if base:
for part_num, base_part in base.iteritems():
part = layout.setdefault(part_num, {})
for base_key, base_value in base_part.iteritems():
part.setdefault(base_key, base_value)
for part_num, part in layout.iteritems():
if part['type'] == 'blank':
continue
part.setdefault('alignment', metadata['alignment'])
part['bytes'] = part['blocks'] * metadata['block_size']
part.setdefault('fs_block_size', metadata['fs_block_size'])
part.setdefault('fs_blocks', part['bytes'] // part['fs_block_size'])
part['fs_bytes'] = part['fs_blocks'] * part['fs_block_size']
if part['fs_bytes'] > part['bytes']:
raise InvalidLayout(
'Filesystem may not be larger than partition: %s %s: %d > %d' %
(layout_name, part_num, part['fs_bytes'], part['bytes']))
disk_block_count = Align(disk_block_count, part['alignment'])
part['first_block'] = disk_block_count
part['first_byte'] = disk_block_count * metadata['block_size']
disk_block_count += part['blocks']
part.setdefault('uuid', str(uuid.uuid4()))
# Reserved size for second GPT plus align disk image size
disk_block_count += GPT_RESERVED_SECTORS
disk_block_count = Align(disk_block_count, metadata['alignment'])
# If this is the requested layout stash the disk size into the global
# metadata. Kinda odd but the best place I've got with this data structure.
if layout_name == options.disk_layout:
metadata['blocks'] = disk_block_count
# Verify 'base' before other layouts because it is inherited by the others
# Fill in extra/default values in base last so they aren't inherited
VerifyLayout('base', base)
for layout_name, layout in config['layouts'].iteritems():
if layout_name == 'base':
continue
VerifyLayout(layout_name, layout, base)
FillExtraValues(layout_name, layout, base)
FillExtraValues('base', base)
return config, config['layouts'][options.disk_layout]
def GetPartitionTableFromConfig(options):
"""Loads a partition table and returns a given partition table type
Args:
options: Flags passed to the script
Returns:
A list defining all known partitions.
"""
config, partitions = LoadPartitionConfig(options)
return partitions
def GetPartitionTableFromImage(options, config, partitions):
"""Loads very basic partition table info from an existing image.
Currently only includes blocks and first_block values.
Args:
options: Flags passed to the script
Returns:
A list defining all existing partitions.
"""
block_size = config['metadata']['block_size']
cgpt_show = subprocess.check_output(
['cgpt', 'show', '-q', options.disk_image])
for line in cgpt_show.split('\n'):
if not line.strip():
continue
fields = line.split(None, 3)
if len(fields) != 4 or not all(f.isdigit() for f in fields[:3]):
raise Exception('Invalid output from cgpt show -q: %r' % line)
part = partitions.setdefault(fields[2], {})
part['image_first_block'] = int(fields[0])
part['image_first_byte'] = int(fields[0]) * block_size
part['image_blocks'] = int(fields[1])
part['image_bytes'] = int(fields[1]) * block_size
# Pre-compute whether the image and config are compatible.
# The image is compatible with the config if each partition:
# - starts at the same position
# - is the same size or larger in th layout config
part['image_exists'] = True
if part.get('type', 'blank') == 'blank':
part['image_compat'] = False
elif part['first_block'] == part['image_first_block']:
part['image_compat'] = part['blocks'] >= part['image_blocks']
else:
part['image_compat'] = False
# Set compat flags for any partition not in the image
for part in partitions.itervalues():
part.setdefault('image_exists', False)
if part.get('type', 'blank') == 'blank':
part.setdefault('image_compat', True)
else:
part.setdefault('image_compat', False)
def WritePartitionTable(options, config=None, partitions=None):
"""Writes the given partition table to a disk image or device.
Args:
options: Flags passed to the script
config: Complete layout configuration file object
partitions: Selected layout configuration object
"""
def Cgpt(*args):
subprocess.check_call(['cgpt'] + [str(a) for a in args])
if not (config and partitions):
config, partitions = LoadPartitionConfig(options)
# If we are not creating a fresh image all partitions must be compatible.
if not options.create:
GetPartitionTableFromImage(options, config, partitions)
if not all(p['image_compat'] for p in partitions.itervalues()):
raise InvalidLayout("New disk layout is incompatible existing image")
Cgpt('create', '-c', '-s', config['metadata']['blocks'], options.disk_image)
esp_number = None
for partition in partitions.itervalues():
if partition['type'] != 'blank':
Cgpt('add', '-i', partition['num'],
'-b', partition['first_block'],
'-s', partition['blocks'],
'-t', partition['type'],
'-l', partition['label'],
'-u', partition['uuid'],
options.disk_image)
if partition['type'] == 'efi':
esp_number = partition['num']
if esp_number is None:
raise InvalidLayout('Table does not include an EFI partition.')
if options.mbr_boot_code:
Cgpt('boot', '-p',
'-b', options.mbr_boot_code,
'-i', esp_number,
options.disk_image)
Cgpt('show', options.disk_image)
def Sudo(cmd, stdout_null=False):
"""Little wrapper around sudo with support for redirecting to /dev/null
Some tools like tune2fs don't have a quiet mode which just adds
useless noise to our build output, drowning out what may be more
interesting news.
Args:
cmd: a command and arguments to run.
stdout_null: bool to enable redirecting stdout to /dev/null.
"""
null = None
if stdout_null:
null = open('/dev/null', 'w')
try:
subprocess.check_call(['sudo'] + [str(c) for c in cmd], stdout=null)
finally:
if null:
null.close()
def FormatExt(part, device):
"""Format an ext2 or ext4 filesystem.
Args:
part: dict defining the partition
device: name of the block device to format
"""
Sudo(['mke2fs', '-q',
'-t', part['fs_type'],
'-b', part['fs_block_size'],
device,
part['fs_blocks']])
# TODO(marineam): Make more of these fs options configurable.
Sudo(['tune2fs', '-L', part['label'],
'-U', 'clear',
'-T', '20091119110000',
'-c', '0', '-i', '0', # Disable auto fsck
'-m', '0', '-r', '0', # Disable reserve blocks
'-e', 'remount-ro',
device],
stdout_null=True)
def FormatFat(part, device):
"""Format a FAT filesystem.
Args:
part: dict defining the partition
device: name of the block device to format
"""
# The block-count argument to mkfs.vfat is in units of 1k
vfat_block_size = 1024
vfat_blocks = part['bytes'] // vfat_block_size
Sudo(['mkfs.vfat', '-n', part['label'],
device,
vfat_blocks],
stdout_null=True)
if 'syslinux' in part.get('features', []):
# The syslinux directory must exist before installing ldlinux.sys to it.
vfat_mount = tempfile.mkdtemp()
Sudo(['mount', '-t', 'vfat', device, vfat_mount])
try:
Sudo(['mkdir', os.path.join(vfat_mount, 'syslinux')])
finally:
Sudo(['umount', vfat_mount])
os.rmdir(vfat_mount)
Sudo(['syslinux', '-d', '/syslinux', device])
print "Installed SYSLINUX to %s" % part['label']
@contextlib.contextmanager
def PartitionLoop(options, partition):
"""Allocate (and automatically free) loop devices for a partition."""
loop_dev = subprocess.check_output(['sudo', 'losetup',
'--offset', str(partition['first_byte']),
'--sizelimit', str(partition['bytes']),
'--find', '--show', options.disk_image])
loop_dev = loop_dev.strip()
try:
yield loop_dev
finally:
Sudo(['losetup', '--detach', loop_dev])
def Format(options):
"""Writes the given partition table and initialize fresh filesystems.
Args:
options: Flags passed to the script
"""
# Note on using sudo: We don't really need to do this stuff as root
# but mke2fs and friends doesn't have an option to make filesystems at
# arbitrary offsets but using loop devices makes that possible.
config, partitions = LoadPartitionConfig(options)
WritePartitionTable(options, config, partitions)
for part in partitions.itervalues():
if 'fs_type' not in part:
continue
print "Formatting partition %s (%s) as %s" % (
part['num'], part['label'], part['fs_type'])
with PartitionLoop(options, part) as loop_dev:
if part['fs_type'] in ('ext2', 'ext4'):
FormatExt(part, loop_dev)
elif part['fs_type'] == 'vfat':
FormatFat(part, loop_dev)
else:
raise Exception("Unhandled fs type %s" % part['fs_type'])
def Resize(options):
"""Writes the given partition table and resize ext[234] filesystems.
Args:
options: Flags passed to the script
"""
config, partitions = LoadPartitionConfig(options)
WritePartitionTable(options, config, partitions)
for part in partitions.itervalues():
if part.get('fs_type', None) not in ('ext2', 'ext4'):
continue
elif part['bytes'] == part['image_bytes']:
continue
elif not IsE2fsReadWrite(options, part):
continue
print "Resizing partition %s (%s) to %s bytes" % (
part['num'], part['label'], part['fs_bytes'])
with PartitionLoop(options, part) as loop_dev:
Sudo(['e2fsck', '-p', '-f', loop_dev], stdout_null=True)
Sudo(['resize2fs', loop_dev, str(part['fs_blocks'])])
Sudo(['tune2fs', '-U', 'clear', loop_dev], stdout_null=True)
def Mount(options):
"""Mount the given disk image.
The existing partition table is used to determine what exists but the
disk layout config is used to look up mount points and binds.
Args:
options: Flags passed to the script
"""
config, partitions = LoadPartitionConfig(options)
GetPartitionTableFromImage(options, config, partitions)
mounts = {}
for part_num, part in partitions.iteritems():
path = part.get('mount', None)
if not path or not path.startswith('/'):
continue
if not part.get('image_exists', False):
continue
mounts[path] = part
if '/' not in mounts:
raise InvalidLayout('No partition defined to mount on /')
def DoMount(mount):
full_path = os.path.realpath(options.mount_dir + mount['mount'])
mount_opts = ['loop',
'offset=%d' % mount['image_first_byte'],
'sizelimit=%d' % mount['image_bytes']]
if options.read_only:
mount_opts.append('ro')
elif (mount.get('fs_type', None) in ('ext2', 'ext4') and
not IsE2fsReadWrite(options, mount)):
mount_opts.append('ro')
Sudo(['mkdir', '-p', full_path])
Sudo(['mount', '-t', mount.get('fs_type', 'auto'),
'-o', ','.join(mount_opts),
options.disk_image, full_path])
for src, dst in mount.get('binds', {}).iteritems():
# src may be relative or absolute, os.path.join handles this.
full_src = os.path.realpath(
options.mount_dir + os.path.join(mount['mount'], src))
full_dst = os.path.realpath(options.mount_dir + dst)
Sudo(['mkdir', '-p', full_src, full_dst])
Sudo(['mount', '--bind', full_src, full_dst])
for mount in sorted(mounts, key=len):
DoMount(mounts[mount])
def Umount(options):
"""Unmount the given path.
Args:
options: Flags passed to the script
"""
Sudo(['umount', '--recursive', '--detach-loop', options.mount_dir])
def Tune2fsReadWrite(options, partition):
"""Enable/Disable read-only hack.
From common.sh:
This helper clobbers the ro compat value in our root filesystem.
When the system is built with --enable_rootfs_verification, bit-precise
integrity checking is performed. That precision poses a usability issue on
systems that automount partitions with recognizable filesystems, such as
ext2/3/4. When the filesystem is mounted 'rw', ext2 metadata will be
automatically updated even if no other writes are performed to the
filesystem. In addition, ext2+ does not support a "read-only" flag for a
given filesystem. That said, forward and backward compatibility of
filesystem features are supported by tracking if a new feature breaks r/w or
just write compatibility. We abuse the read-only compatibility flag[1] in
the filesystem header by setting the high order byte (le) to FF. This tells
the kernel that features R24-R31 are all enabled. Since those features are
undefined on all ext-based filesystem, all standard kernels will refuse to
mount the filesystem as read-write -- only read-only[2].
[1] 32-bit flag we are modifying:
http://git.chromium.org/cgi-bin/gitweb.cgi?p=kernel.git;a=blob;f=include/linux/ext2_fs.h#l417
[2] Mount behavior is enforced here:
http://git.chromium.org/cgi-bin/gitweb.cgi?p=kernel.git;a=blob;f=fs/ext2/super.c#l857
N.B., if the high order feature bits are used in the future, we will need to
revisit this technique.
Args:
options: Flags passed to the script
partition: Config for partition to manipulate
"""
if options.disable2fs_rw:
print "Disabling read-write mounting of partition %s (%s)" % (
partition['num'], partition['label'])
else:
print "Enabling read-write mounting of partition %s (%s)" % (
partition['num'], partition['label'])
# offset of ro_compat, highest order byte (le 32 bit field)
flag_offset = 0x464 + 3
flag_value = 0xff if options.disable2fs_rw else 0x00
with open(options.disk_image, 'r+') as image:
image.seek(partition['first_byte'] + flag_offset)
image.write(chr(flag_value))
def IsE2fsReadWrite(options, partition):
"""Returns True if FS is read-write, False if hack is active.
Args:
options: Flags passed to the script
partition: Config for partition to query
"""
# offset of ro_compat, highest order byte (le 32 bit field)
flag_offset = 0x464 + 3
with open(options.disk_image, 'r') as image:
image.seek(partition['first_byte'] + flag_offset)
flag_value = image.read(1)
return ord(flag_value) == 0
def Tune(options):
"""Tweak some filesystem options.
Args:
options: Flags passed to the script
"""
config, partitions = LoadPartitionConfig(options)
GetPartitionTableFromImage(options, config, partitions)
part = GetPartition(partitions, options.partition)
if not part['image_compat']:
raise InvalidLayout("Disk layout is incompatible existing image")
if options.disable2fs_rw is not None:
if part.get('fs_type', None) not in ('ext2', 'ext4'):
raise Exception("Partition %s is not a ext2 or ext4" % options.partition)
Tune2fsReadWrite(options, part)
else:
raise Exception("No options specified!")
def GetPartitionByNumber(partitions, num):
"""Given a partition table and number returns the partition object.
Args:
partitions: List of partitions to search in
num: Number of partition to find
Returns:
An object for the selected partition
"""
partition = partitions.get(str(num), None)
if not partition or partition['type'] == 'blank':
raise PartitionNotFound('Partition not found')
return partition
def GetPartitionByLabel(partitions, label):
"""Given a partition table and label returns the partition object.
Args:
partitions: List of partitions to search in
label: Label of partition to find
Returns:
An object for the selected partition
"""
for partition in partitions.itervalues():
if partition['type'] == 'blank':
continue
elif partition['label'] == label:
return partition
raise PartitionNotFound('Partition not found')
def GetPartition(partitions, part_id):
"""Given a partition table and label or num returns the partition object.
Args:
partitions: List of partitions to search in
part_id: Label or number of partition to find
Returns:
An object for the selected partition
"""
if str(part_id).isdigit():
return GetPartitionByNumber(partitions, part_id)
else:
return GetPartitionByLabel(partitions, part_id)
def GetBlockSize(options):
"""Returns the partition table block size.
Args:
options: Flags passed to the script
Prints:
Block size of all partitions in the layout
"""
config, partitions = LoadPartitionConfig(options)
print config['metadata']['block_size']
def GetFilesystemBlockSize(options):
"""Returns the filesystem block size.
This is used for all partitions in the table that have filesystems.
Args:
options: Flags passed to the script
Prints:
Block size of all filesystems in the layout
"""
config, partitions = LoadPartitionConfig(options)
print config['metadata']['fs_block_size']
def GetPartitionSize(options):
"""Returns the partition size of a given partition for a given layout type.
Args:
options: Flags passed to the script
Prints:
Size of selected partition in bytes
"""
partitions = GetPartitionTableFromConfig(options)
partition = GetPartitionByNumber(partitions, options.partition_num)
print partition['bytes']
def GetFilesystemSize(options):
"""Returns the filesystem size of a given partition for a given layout type.
If no filesystem size is specified, returns the partition size.
Args:
options: Flags passed to the script
Prints:
Size of selected partition filesystem in bytes
"""
partitions = GetPartitionTableFromConfig(options)
partition = GetPartitionByNumber(partitions, options.partition_num)
print partition.get('fs_bytes', partition['bytes'])
def GetLabel(options):
"""Returns the label for a given partition.
Args:
options: Flags passed to the script
Prints:
Label of selected partition, or 'UNTITLED' if none specified
"""
partitions = GetPartitionTableFromConfig(options)
partition = GetPartitionByNumber(partitions, options.partition_num)
print partition.get('label', 'UNTITLED')
def GetNum(options):
"""Returns the number for a given label.
Args:
options: Flags passed to the script
Prints:
Number of selected partition, or '-1' if there is no number
"""
partitions = GetPartitionTableFromConfig(options)
partition = GetPartitionByLabel(partitions, options.label)
print partition.get('num', '-1')
def GetUuid(options):
"""Returns the unique partition UUID for a given label.
Args:
options: Flags passed to the script
Prints:
String containing the requested UUID
"""
partitions = GetPartitionTableFromConfig(options)
partition = GetPartitionByLabel(partitions, options.label)
print partition.get('uuid', '')
def DoDebugOutput(options):
"""Prints out a human readable disk layout in on-disk order.
This will round values larger than 1MB, it's exists to quickly
visually verify a layout looks correct.
Args:
options: Flags passed to the script
"""
partitions = GetPartitionTableFromConfig(options)
for num, partition in sorted(partitions.iteritems()):
if partition['type'] != 'blank':
if partition['bytes'] < 1024 * 1024:
size = '%d bytes' % partition['bytes']
else:
size = '%d MB' % (partition['bytes'] / 1024 / 1024)
if 'fs_bytes' in partition:
if partition['fs_bytes'] < 1024 * 1024:
fs_size = '%d bytes' % partition['fs_bytes']
else:
fs_size = '%d MB' % (partition['fs_bytes'] / 1024 / 1024)
print '%s: %s - %s/%s' % (num, partition['label'], fs_size, size)
else:
print '%s: %s - %s' % (num, partition['label'], size)
else:
print '%s: blank' % num
def DoParseOnly(options):
"""Parses a layout file only, used before reading sizes to check for errors.
Args:
options: Flags passed to the script
"""
GetPartitionTableFromConfig(options)
def main(argv):
default_layout_file = os.environ.get('DISK_LAYOUT_FILE',
os.path.join(os.path.dirname(__file__), 'legacy_disk_layout.json'))
default_layout_type = os.environ.get('DISK_LAYOUT_TYPE', 'base')
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('--disk_layout_file', default=default_layout_file,
help='path to disk layout json file')
parser.add_argument('--disk_layout', default=default_layout_type,
help='disk layout type from the json file')
actions = parser.add_subparsers(title='actions')
a = actions.add_parser('write_gpt', help='write/update partition table')
a.add_argument('--create', action='store_true', default=True,
help='initialize new partition table')
a.add_argument('--update', action='store_false', dest='create',
help='update existing partition table')
a.add_argument('--mbr_boot_code',
help='path to mbr boot block, such as syslinux/gptmbr.bin')
a.add_argument('disk_image', help='path to disk image file')
a.set_defaults(func=WritePartitionTable)
a = actions.add_parser('format', help='write gpt and filesystems to image')
a.add_argument('--mbr_boot_code',
help='path to mbr boot block, such as syslinux/gptmbr.bin')
a.add_argument('disk_image', help='path to disk image file')
a.set_defaults(func=Format, create=True)
a = actions.add_parser('resize', help='write gpt and resize filesystems')
a.add_argument('--mbr_boot_code',
help='path to mbr boot block, such as syslinux/gptmbr.bin')
a.add_argument('disk_image', help='path to disk image file')
a.set_defaults(func=Resize, create=False)
a = actions.add_parser('mount', help='mount filesystems in image')
a.add_argument('--read_only', '-r', action='store_true',
help='mount filesystems read-only')
a.add_argument('disk_image', help='path to disk image file')
a.add_argument('mount_dir', help='path to root filesystem mount point')
a.set_defaults(func=Mount)
a = actions.add_parser('umount', help='unmount a image mount point')
a.add_argument('mount_dir', help='path to root filesystem mount point')
a.set_defaults(func=Umount)
a = actions.add_parser('tune', help='tweak filesystem options')
a.add_argument('--disable2fs_rw', action='store_true', default=None,
help='disable mounting ext2 filesystems read-write')
a.add_argument('--enable2fs_rw', action='store_false', dest='disable2fs_rw',
help='re-enable mounting ext2 filesystems read-write')
a.add_argument('disk_image', help='path to disk image file')
a.add_argument('partition', help='number or label of partition to edit')
a.set_defaults(func=Tune)
a = actions.add_parser('readblocksize', help='get device block size')
a.set_defaults(func=GetBlockSize)
a = actions.add_parser('readfsblocksize', help='get filesystem block size')
a.set_defaults(func=GetFilesystemBlockSize)
a = actions.add_parser('readpartsize', help='get partition size')
a.add_argument('partition_num', type=int, help='partition number')
a.set_defaults(func=GetPartitionSize)
a = actions.add_parser('readfssize', help='get filesystem size')
a.add_argument('partition_num', type=int, help='partition number')
a.set_defaults(func=GetFilesystemSize)
a = actions.add_parser('readlabel', help='get partition label')
a.add_argument('partition_num', type=int, help='partition number')
a.set_defaults(func=GetLabel)
a = actions.add_parser('readnum', help='get partition number')
a.add_argument('label', help='partition label')
a.set_defaults(func=GetNum)
a = actions.add_parser('readuuid', help='get partition uuid')
a.add_argument('label', help='partition label')
a.set_defaults(func=GetUuid)
a = actions.add_parser('debug', help='dump debug output')
a.set_defaults(func=DoDebugOutput)
a = actions.add_parser('parseonly', help='validate config')
a.set_defaults(func=DoParseOnly)
options = parser.parse_args(argv[1:])
options.func(options)
if __name__ == '__main__':
main(sys.argv)

View File

@ -6,82 +6,87 @@
"fs_block_size": 4096 "fs_block_size": 4096
}, },
"layouts":{ "layouts":{
"base":[ "base":{
{ "1":{
"num": 1,
"label":"EFI-SYSTEM", "label":"EFI-SYSTEM",
"type":"efi", "type":"efi",
"blocks":"262144" "blocks":"262144",
"fs_type":"vfat",
"mount":"/boot/efi",
"features": ["syslinux"]
}, },
{ "2":{
"num": 2,
"label":"BOOT-B", "label":"BOOT-B",
"type":"coreos-reserved", "type":"coreos-reserved",
"blocks":"131072" "blocks":"131072"
}, },
{ "3":{
"num": 3,
"label":"ROOT-A", "label":"ROOT-A",
"uuid":"7130c94a-213a-4e5a-8e26-6cce9662f132", "uuid":"7130c94a-213a-4e5a-8e26-6cce9662f132",
"type":"coreos-rootfs", "type":"coreos-rootfs",
"blocks":"2097152", "blocks":"2097152",
"fs_blocks":"262144" "fs_blocks":"262144",
"fs_type":"ext2",
"mount":"/"
}, },
{ "4":{
"num": 4,
"label":"ROOT-B", "label":"ROOT-B",
"uuid":"e03dd35c-7c2d-4a47-b3fe-27f15780a57c", "uuid":"e03dd35c-7c2d-4a47-b3fe-27f15780a57c",
"type":"coreos-rootfs", "type":"coreos-rootfs",
"blocks":"2097152", "blocks":"2097152",
"fs_blocks":"262144" "fs_blocks":"262144"
}, },
{ "5":{
"num": 5,
"label":"ROOT-C", "label":"ROOT-C",
"uuid":"d82521b4-07ac-4f1c-8840-ddefedc332f3", "uuid":"d82521b4-07ac-4f1c-8840-ddefedc332f3",
"type":"blank", "type":"blank",
"blocks":"0" "blocks":"0"
}, },
{ "6":{
"num": 6,
"label":"OEM", "label":"OEM",
"type":"data", "type":"data",
"blocks":"262144" "blocks":"262144",
"fs_type":"ext4",
"mount":"/usr/share/oem"
}, },
{ "7":{
"num": 7,
"type":"blank", "type":"blank",
"label":"coreos-reserved", "label":"coreos-reserved",
"blocks":"0" "blocks":"0"
}, },
{ "8":{
"num": 8,
"type":"blank", "type":"blank",
"label":"coreos-reserved", "label":"coreos-reserved",
"blocks":"0" "blocks":"0"
}, },
{ "9":{
"num": 9,
"label":"STATE", "label":"STATE",
"type":"data", "type":"data",
"blocks":"1048576" "blocks":"1048576",
"fs_type":"ext4",
"mount":"/media/state",
"binds":{
"overlays/home":"/home",
"overlays/opt":"/opt",
"overlays/srv":"/srv",
"overlays/usr/local":"/usr/local",
"overlays/var":"/var"
}
} }
], },
"vm": [ "vm":{
{ "9":{
"num": 9,
"label":"STATE", "label":"STATE",
"type":"data", "type":"data",
"blocks":"6291456" "blocks":"6291456"
} }
], },
"vagrant": [ "vagrant":{
{ "9":{
"num": 9,
"label":"STATE", "label":"STATE",
"type":"data", "type":"data",
"blocks":"33587200" "blocks":"33587200"
} }
] }
} }
} }

View File

@ -1,35 +0,0 @@
# 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.
# This global array variable is used to remember options from
# mount_image so that unmount_image can do its job.
MOUNT_GPT_OPTIONS=( )
# mount_image - Mount the root, stateful, and optionally ESP partitions
# in a Chromium OS image.
# $1: path to image to be mounted
# $2: path to root fs mount point
# $3: path to stateful fs mount point
# $4: path to ESP fs mount point; if empty the ESP will not be mounted
mount_image() {
local image_dir="$(dirname $1)"
local image="$(basename $1)"
MOUNT_GPT_OPTIONS=( -r "$2" -s "$3" )
if [ $# -ge 4 ]; then
MOUNT_GPT_OPTIONS=( "${MOUNT_GPT_OPTIONS[@]}" -e "$4" )
fi
"${SCRIPTS_DIR}/mount_gpt_image.sh" --from="$image_dir" --image="$image" \
"${MOUNT_GPT_OPTIONS[@]}"
}
# unmount_image - Unmount the file systems mounted in the previous
# call to mount_image.
# No arguments
unmount_image() {
"${SCRIPTS_DIR}/mount_gpt_image.sh" -u "${MOUNT_GPT_OPTIONS[@]}"
MOUNT_GPT_OPTIONS=( )
}

View File

@ -0,0 +1,47 @@
# Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
# Copyright (c) 2013 The CoreOS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
setup_prod_image() {
local image_name="$1"
local update_track="$2"
local au_key="$3"
info "Configuring production image ${image_name}"
local disk_layout="${FLAGS_disk_layout:-base}"
local root_fs_dir="${BUILD_DIR}/rootfs"
local enable_rootfs_verification_flag=--noenable_rootfs_verification
if [[ ${FLAGS_enable_rootfs_verification} -eq ${FLAGS_TRUE} ]]; then
enable_rootfs_verification_flag=--enable_rootfs_verification
fi
"${BUILD_LIBRARY_DIR}/disk_util" --disk_layout="${disk_layout}" \
mount "${BUILD_DIR}/${image_name}" "${root_fs_dir}"
trap "cleanup_mounts '${root_fs_dir}' && delete_prompt" EXIT
# Replace /etc/lsb-release on the image.
"${BUILD_LIBRARY_DIR}/set_lsb_release" \
--production_track="${update_track}" \
--root="${root_fs_dir}" \
--board="${BOARD}"
# Install an auto update key on the root before sealing it off
local key_location=${root_fs_dir}"/usr/share/update_engine/"
sudo mkdir -p "${key_location}"
sudo cp "${au_key}" "$key_location/update-payload-key.pub.pem"
sudo chown root:root "$key_location/update-payload-key.pub.pem"
sudo chmod 644 "$key_location/update-payload-key.pub.pem"
# Make the filesystem un-mountable as read-write.
if [ ${FLAGS_enable_rootfs_verification} -eq ${FLAGS_TRUE} ]; then
warn "Disabling r/w mount of the root filesystem"
sudo mount -o remount,ro "${root_fs_dir}"
root_dev=$(awk -v mnt="${root_fs_dir}" \
'$2 == mnt { print $1 }' /proc/mounts)
disable_rw_mount "$root_dev"
fi
cleanup_mounts "${root_fs_dir}"
trap - EXIT
}

View File

@ -27,6 +27,7 @@ VM_IMG_TYPE=DEFAULT
VM_SRC_IMG= VM_SRC_IMG=
VM_TMP_IMG= VM_TMP_IMG=
VM_TMP_DIR= VM_TMP_DIR=
VM_TMP_ROOT=
VM_DST_IMG= VM_DST_IMG=
VM_README= VM_README=
VM_NAME= VM_NAME=
@ -149,6 +150,7 @@ set_vm_paths() {
VM_DST_IMG="${dst_dir}/${dst_name}" VM_DST_IMG="${dst_dir}/${dst_name}"
VM_TMP_DIR="${dst_dir}/${dst_name}.vmtmpdir" VM_TMP_DIR="${dst_dir}/${dst_name}.vmtmpdir"
VM_TMP_IMG="${VM_TMP_DIR}/disk_image.bin" VM_TMP_IMG="${VM_TMP_DIR}/disk_image.bin"
VM_TMP_ROOT="${VM_TMP_DIR}/rootfs"
VM_NAME="$(_src_to_dst_name "${src_name}" "")-${COREOS_VERSION_STRING}" VM_NAME="$(_src_to_dst_name "${src_name}" "")-${COREOS_VERSION_STRING}"
VM_UUID=$(uuidgen) VM_UUID=$(uuidgen)
VM_README="${dst_dir}/$(_src_to_dst_name "${src_name}" ".README")" VM_README="${dst_dir}/$(_src_to_dst_name "${src_name}" ".README")"
@ -195,104 +197,46 @@ _disk_ext() {
esac esac
} }
# Unpack the source disk to individual partitions, optionally using an setup_disk_image() {
# alternate filesystem image for the state partition instead of the one
# from VM_SRC_IMG. Start new image using the given disk layout.
unpack_source_disk() {
local disk_layout="${1:-$(_get_vm_opt DISK_LAYOUT)}" local disk_layout="${1:-$(_get_vm_opt DISK_LAYOUT)}"
local alternate_state_image="$2"
if [[ -n "${alternate_state_image}" && ! -f "${alternate_state_image}" ]]
then
die "State image does not exist: $alternate_state_image"
fi
info "Unpacking source image to $(relpath "${VM_TMP_DIR}")"
rm -rf "${VM_TMP_DIR}" rm -rf "${VM_TMP_DIR}"
mkdir -p "${VM_TMP_DIR}" mkdir -p "${VM_TMP_DIR}" "${VM_TMP_ROOT}"
pushd "${VM_TMP_DIR}" >/dev/null info "Initializing new disk image..."
local src_dir=$(dirname "${VM_SRC_IMG}") cp --sparse=always "${VM_SRC_IMG}" "${VM_TMP_IMG}"
"${src_dir}/unpack_partitions.sh" "${VM_SRC_IMG}"
popd >/dev/null
# Partition paths that have been unpacked from VM_SRC_IMG
TEMP_ESP="${VM_TMP_DIR}"/part_${NUM_ESP}
TEMP_OEM="${VM_TMP_DIR}"/part_${NUM_OEM}
TEMP_ROOTFS="${VM_TMP_DIR}"/part_${NUM_ROOTFS_A}
TEMP_STATE="${VM_TMP_DIR}"/part_${NUM_STATEFUL}
# Copy the replacement STATE image if it is set
if [[ -n "${alternate_state_image}" ]]; then
cp --sparse=always "${alternate_state_image}" "${TEMP_STATE}"
fi
if [[ $(_get_vm_opt PARTITIONED_IMG) -eq 1 ]]; then if [[ $(_get_vm_opt PARTITIONED_IMG) -eq 1 ]]; then
info "Initializing new partition table..." # TODO(marineam): Fix this so --mbr_boot_code isn't required
write_partition_table "${disk_layout}" "${VM_TMP_IMG}" local mbr_img="/usr/share/syslinux/gptmbr.bin"
fi "${BUILD_LIBRARY_DIR}/disk_util" --disk_layout="${disk_layout}" \
} resize --mbr_boot_code="${mbr_img}" "${VM_TMP_IMG}"
resize_state_partition() {
local disk_layout="${1:-$(_get_vm_opt DISK_LAYOUT)}"
local size_in_bytes=$(get_filesystem_size "${disk_layout}" ${NUM_STATEFUL})
local size_in_sectors=$(( size_in_bytes / 512 ))
local size_in_mb=$(( size_in_bytes / 1024 / 1024 ))
local original_size=$(stat -c%s "${TEMP_STATE}")
if [[ "${original_size}" -gt "${size_in_bytes}" ]]; then
die "Cannot resize state image to smaller than original."
fi fi
info "Resizing state partition to ${size_in_mb}MB" info "Mounting image to $(relpath "${VM_TMP_ROOT}")"
/sbin/e2fsck -pf "${TEMP_STATE}" "${BUILD_LIBRARY_DIR}/disk_util" --disk_layout="${disk_layout}" \
/sbin/resize2fs "${TEMP_STATE}" "${size_in_sectors}s" mount "${VM_TMP_IMG}" "${VM_TMP_ROOT}"
} }
# If the current type defines a oem package install it to the given fs image. # If the current type defines a oem package install it to the given fs image.
install_oem_package() { install_oem_package() {
local oem_pkg=$(_get_vm_opt OEM_PACKAGE) local oem_pkg=$(_get_vm_opt OEM_PACKAGE)
local oem_mnt="${VM_TMP_DIR}/oem" local oem_mnt="${VM_TMP_ROOT}/usr/share/oem"
if [[ -z "${oem_pkg}" ]]; then if [[ -z "${oem_pkg}" ]]; then
return 0 return 0
fi fi
mkdir -p "${oem_mnt}"
# Install directly to root if this is not a partitioned image
if [[ $(_get_vm_opt PARTITIONED_IMG) -eq 0 ]]; then
emerge_oem_package "${oem_mnt}"
return 0
fi
sudo mount -o loop "${TEMP_OEM}" "${oem_mnt}"
emerge_oem_package ${oem_mnt}
sudo umount "${oem_mnt}"
rm -rf "${oem_mnt}"
}
emerge_oem_package() {
local oem_pkg=$(_get_vm_opt OEM_PACKAGE)
info "Installing ${oem_pkg} to OEM partition" info "Installing ${oem_pkg} to OEM partition"
# TODO(polvi): figure out how to keep portage from putting these emerge-${BOARD} --root="${oem_mnt}" --root-deps=rdeps "${oem_pkg}"
# portage files on disk, we don't need or want them. sudo rm -rf "${oem_mnt}/var" # clean out /var/pkg/db and friends
emerge-${BOARD} --root="$1" --root-deps=rdeps "${oem_pkg}"
} }
# Write the vm disk image to the target directory in the proper format # Write the vm disk image to the target directory in the proper format
write_vm_disk() { write_vm_disk() {
if [[ $(_get_vm_opt PARTITIONED_IMG) -eq 1 ]]; then if [[ $(_get_vm_opt PARTITIONED_IMG) -eq 1 ]]; then
info "Writing partitions to new disk image" # unmount before creating block device images
dd if="${TEMP_ROOTFS}" of="${VM_TMP_IMG}" conv=notrunc,sparse \ cleanup_mounts "${VM_TMP_ROOT}"
bs=512 seek=$(partoffset ${VM_TMP_IMG} ${NUM_ROOTFS_A})
dd if="${TEMP_STATE}" of="${VM_TMP_IMG}" conv=notrunc,sparse \
bs=512 seek=$(partoffset ${VM_TMP_IMG} ${NUM_STATEFUL})
dd if="${TEMP_ESP}" of="${VM_TMP_IMG}" conv=notrunc,sparse \
bs=512 seek=$(partoffset ${VM_TMP_IMG} ${NUM_ESP})
dd if="${TEMP_OEM}" of="${VM_TMP_IMG}" conv=notrunc,sparse \
bs=512 seek=$(partoffset ${VM_TMP_IMG} ${NUM_OEM})
fi fi
if [[ $(_get_vm_opt HYBRID_MBR) -eq 1 ]]; then if [[ $(_get_vm_opt HYBRID_MBR) -eq 1 ]]; then
@ -338,60 +282,29 @@ _write_vmdk_scsi_disk() {
qemu-img convert -f raw "$1" -O vmdk -o adapter_type=lsilogic "$2" qemu-img convert -f raw "$1" -O vmdk -o adapter_type=lsilogic "$2"
} }
# the cpio is a little complicated because everything gets put into one # The cpio "disk" is a bit special,
# ramfs. So, it does a lot more work at the write disk step. # consists of a kernel+initrd not a block device
_write_cpio_disk() { _write_cpio_disk() {
local cpio_target="${VM_TMP_DIR}/rootcpio" local cpio_target="${VM_TMP_DIR}/rootcpio"
if [ -f "$2" ]; then
rm $2
fi
# Create the cpio with squashfs embedded
_write_squashfs_root ${cpio_target}
# Create the cpio and gzip it
local cpio=${VM_TMP_DIR}/root.cpio
_write_dir_to_cpio "${cpio_target}" "${cpio}"
gzip < ${cpio} > $2
rm -rf "${cpio_target}"
}
_write_dir_to_cpio() {
pushd "$1" >/dev/null
append_flag=""
if [ -f "$2" ]; then
append_flag="-A"
fi
sudo find ./ | sudo cpio -o ${append_flag} -H newc -O "$2"
popd >/dev/null
}
_write_squashfs_root() {
local cpio_target="$1"
local root_mnt="${VM_TMP_DIR}/rootfs"
local oem_mnt="${VM_TMP_DIR}/oem"
local dst_dir=$(_dst_dir) local dst_dir=$(_dst_dir)
local vmlinuz_name="$(_dst_name ".vmlinuz")" local vmlinuz_name="$(_dst_name ".vmlinuz")"
mkdir -p "${root_mnt}" # The STATE partition and all of its bind mounts shouldn't be
# packed into the squashfs image. Just ROOT and OEM.
if mountpoint -q "${VM_TMP_ROOT}/media/state"; then
sudo umount --all-targets "${VM_TMP_ROOT}/media/state"
fi
# Build the squashfs, embed squashfs into a gzipped cpio
mkdir -p "${cpio_target}" mkdir -p "${cpio_target}"
pushd "${cpio_target}" >/dev/null
sudo mksquashfs "${VM_TMP_ROOT}" ./newroot.squashfs
echo ./newroot.squashfs | cpio -o -H newc | gzip > "$2"
popd >/dev/null
# Roll the rootfs into the build dir # Pull the kernel out of the root filesystem
sudo mount -o loop,ro "${TEMP_ROOTFS}" "${root_mnt}" cp "${VM_TMP_ROOT}"/boot/vmlinuz "${dst_dir}/${vmlinuz_name}"
VM_GENERATED_FILES+=( "${dst_dir}/${vmlinuz_name}" )
# Roll the OEM into the build dir
sudo mount --bind "${oem_mnt}" "${root_mnt}"/usr/share/oem
# Build the squashfs
sudo mksquashfs "${root_mnt}" "${cpio_target}"/newroot.squashfs
cp "${root_mnt}"/boot/vmlinuz "${dst_dir}/${vmlinuz_name}"
sudo umount "${root_mnt}"/usr/share/oem
sudo umount "${root_mnt}"
sudo rm -rf "${root_mnt}"
} }
# If a config format is defined write it! # If a config format is defined write it!
@ -438,7 +351,6 @@ EOF
_write_pxe_conf() { _write_pxe_conf() {
local dst_name=$(basename "$VM_DST_IMG") local dst_name=$(basename "$VM_DST_IMG")
local dst_dir=$(_dst_dir)
local vmlinuz_name="$(_dst_name ".vmlinuz")" local vmlinuz_name="$(_dst_name ".vmlinuz")"
cat >"${VM_README}" <<EOF cat >"${VM_README}" <<EOF
@ -449,7 +361,7 @@ If you have qemu installed (or in the SDK), you can start the image with:
EOF EOF
VM_GENERATED_FILES+=( "${dst_dir}/${vmlinuz_name}" "${VM_README}" ) VM_GENERATED_FILES+=( "${VM_README}" )
} }
# Generate the vmware config file # Generate the vmware config file
@ -712,6 +624,9 @@ _write_gce_conf() {
vm_cleanup() { vm_cleanup() {
info "Cleaning up temporary files" info "Cleaning up temporary files"
if mountpoint -q "${VM_TMP_ROOT}"; then
cleanup_mounts "${VM_TMP_ROOT}"
fi
sudo rm -rf "${VM_TMP_DIR}" sudo rm -rf "${VM_TMP_DIR}"
} }

View File

@ -13,21 +13,11 @@
SCRIPT_ROOT=$(dirname "$(readlink -f "$0")") SCRIPT_ROOT=$(dirname "$(readlink -f "$0")")
. "${SCRIPT_ROOT}/common.sh" || exit 1 . "${SCRIPT_ROOT}/common.sh" || exit 1
. "${BUILD_LIBRARY_DIR}/toolchain_util.sh" || exit 1 . "${BUILD_LIBRARY_DIR}/toolchain_util.sh" || exit 1
. "${BUILD_LIBRARY_DIR}/disk_layout_util.sh" || exit 1
. "${BUILD_LIBRARY_DIR}/build_common.sh" || exit 1
. "${BUILD_LIBRARY_DIR}/build_image_util.sh" || exit 1 . "${BUILD_LIBRARY_DIR}/build_image_util.sh" || exit 1
. "${BUILD_LIBRARY_DIR}/vm_image_util.sh" || exit 1 . "${BUILD_LIBRARY_DIR}/vm_image_util.sh" || exit 1
# 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 || exit 1
. "${SCRIPT_ROOT}/lib/cros_vm_constants.sh" || exit 1 . "${SCRIPT_ROOT}/lib/cros_vm_constants.sh" || exit 1
# Flags # Flags
DEFINE_string adjust_part "" \
"Adjustments to apply to the partition table"
DEFINE_string board "${DEFAULT_BOARD}" \ DEFINE_string board "${DEFAULT_BOARD}" \
"Board for which the image was built" "Board for which the image was built"
@ -41,8 +31,6 @@ DEFINE_string disk_layout "" \
"The disk layout type to use for this image." "The disk layout type to use for this image."
DEFINE_integer mem "${DEFAULT_MEM}" \ DEFINE_integer mem "${DEFAULT_MEM}" \
"Memory size for the vm config in MBs." "Memory size for the vm config in MBs."
DEFINE_string state_image "" \
"Stateful partition image (defaults to creating new statful partition)"
DEFINE_boolean prod_image "${FLAGS_FALSE}" \ DEFINE_boolean prod_image "${FLAGS_FALSE}" \
"Use the production image instead of the default developer image." "Use the production image instead of the default developer image."
DEFINE_string to "" \ DEFINE_string to "" \
@ -101,16 +89,11 @@ else
set_vm_paths "${FLAGS_from}" "${FLAGS_to}" "${CHROMEOS_IMAGE_NAME}" set_vm_paths "${FLAGS_from}" "${FLAGS_to}" "${CHROMEOS_IMAGE_NAME}"
fi fi
locate_gpt
legacy_offset_size_export ${VM_SRC_IMG}
# Make sure things are cleaned up on failure # Make sure things are cleaned up on failure
trap vm_cleanup EXIT trap vm_cleanup EXIT
# Unpack image, using alternate state image if defined # Setup new (raw) image, possibly resizing filesystems
# Resize to use all available space in new disk layout setup_disk_image "${FLAGS_disk_layout}"
unpack_source_disk "${FLAGS_disk_layout}" "${FLAGS_state_image}"
resize_state_partition "${FLAGS_disk_layout}"
# Optionally install any OEM packages # Optionally install any OEM packages
install_oem_package install_oem_package

View File

@ -1,194 +0,0 @@
#!/bin/bash
# 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.
# Helper script that mounts chromium os image from a device or directory
# and creates mount points for /var and /usr/local (if in dev_mode).
# Helper scripts should be run from the same location as this script.
SCRIPT_ROOT=$(dirname "$(readlink -f "$0")")
. "${SCRIPT_ROOT}/common.sh" || exit 1
if [ $INSIDE_CHROOT -ne 1 ]; then
INSTALL_ROOT="$SRC_ROOT/platform/installer/"
else
INSTALL_ROOT=/usr/lib/installer/
fi
# Load functions and constants for chromeos-install
. "${INSTALL_ROOT}/chromeos-common.sh" || exit 1
locate_gpt
# Flags.
DEFINE_string board "$DEFAULT_BOARD" \
"The board for which the image was built." b
DEFINE_boolean read_only $FLAGS_FALSE \
"Mount in read only mode -- skips stateful items."
DEFINE_boolean safe $FLAGS_FALSE \
"Mount rootfs in read only mode."
DEFINE_boolean unmount $FLAGS_FALSE \
"Unmount previously mounted dir." u
DEFINE_string from "/dev/sdc" \
"Directory, image, or device with image on it" f
DEFINE_string image "$COREOS_IMAGE_NAME" \
"Name of the bin file if a directory is specified in the from flag" i
DEFINE_string "rootfs_mountpt" "/tmp/m" "Mount point for rootfs" "r"
DEFINE_string "stateful_mountpt" "/tmp/s" \
"Mount point for stateful partition" "s"
DEFINE_string "esp_mountpt" "" \
"Mount point for esp partition" "e"
DEFINE_boolean most_recent ${FLAGS_FALSE} "Use the most recent image dir" m
# Parse flags
FLAGS "$@" || exit 1
eval set -- "${FLAGS_ARGV}"
# Die on error
switch_to_strict_mode
# Find the last image built on the board.
if [ ${FLAGS_most_recent} -eq ${FLAGS_TRUE} ] ; then
FLAGS_from="$(${SCRIPT_ROOT}/get_latest_image.sh --board="${FLAGS_board}")"
fi
# Check for conflicting args.
# If --from is a block device, --image can't also be specified.
if [ -b "${FLAGS_from}" ]; then
if [ "${FLAGS_image}" != "$COREOS_IMAGE_NAME" ]; then
die_notrace "-i ${FLAGS_image} can't be used with block device ${FLAGS_from}"
fi
fi
# Allow --from /foo/file.bin
if [ -f "${FLAGS_from}" ]; then
# If --from is specified as a file, --image cannot be also specified.
if [ "${FLAGS_image}" != "$COREOS_IMAGE_NAME" ]; then
die_notrace "-i ${FLAGS_image} can't be used with --from file ${FLAGS_from}"
fi
pathname=$(dirname "${FLAGS_from}")
filename=$(basename "${FLAGS_from}")
FLAGS_image="${filename}"
FLAGS_from="${pathname}"
fi
# Common unmounts for either a device or directory
unmount_image() {
info "Unmounting image from ${FLAGS_stateful_mountpt}" \
"and ${FLAGS_rootfs_mountpt}"
# Don't die on error to force cleanup
set +e
safe_umount "${FLAGS_rootfs_mountpt}/usr/local"
safe_umount "${FLAGS_rootfs_mountpt}/var"
if [[ -n "${FLAGS_esp_mountpt}" ]]; then
safe_umount "${FLAGS_esp_mountpt}"
fi
safe_umount "${FLAGS_stateful_mountpt}"
safe_umount "${FLAGS_rootfs_mountpt}"
switch_to_strict_mode
}
get_usb_partitions() {
local ro_flag=""
local safe_flag=""
[ ${FLAGS_read_only} -eq ${FLAGS_TRUE} ] && ro_flag="-o ro"
[ ${FLAGS_read_only} -eq ${FLAGS_TRUE} -o \
${FLAGS_safe} -eq ${FLAGS_TRUE} ] && safe_flag="-o ro -t ext2"
sudo mount ${safe_flag} "${FLAGS_from}4" "${FLAGS_rootfs_mountpt}"
sudo mount ${ro_flag} "${FLAGS_from}10" "${FLAGS_stateful_mountpt}"
if [[ -n "${FLAGS_esp_mountpt}" ]]; then
sudo mount ${ro_flag} "${FLAGS_from}2" "${FLAGS_esp_mountpt}"
fi
}
get_gpt_partitions() {
local filename="${FLAGS_image}"
legacy_offset_size_export "${FLAGS_from}/${FLAGS_image}"
# Mount the rootfs partition using a loopback device.
local offset=$(partoffset "${FLAGS_from}/${filename}" ${NUM_ROOTFS_A})
local ro_flag=""
local safe_flag=""
if [ ${FLAGS_read_only} -eq ${FLAGS_TRUE} ]; then
ro_flag=",ro"
fi
if [ ${FLAGS_read_only} -eq ${FLAGS_TRUE} -o \
${FLAGS_safe} -eq ${FLAGS_TRUE} ]; then
safe_flag=",ro"
else
# Make sure any callers can actually mount and modify the fs
# if desired.
# cros_make_image_bootable should restore the bit if needed.
enable_rw_mount "${FLAGS_from}/${filename}" "$(( offset * 512 ))"
fi
if ! sudo mount -o loop,offset=$(( offset * 512 ))${safe_flag} \
"${FLAGS_from}/${filename}" "${FLAGS_rootfs_mountpt}" ; then
error "mount failed: options=${safe_flag} offset=$(( offset * 512 ))" \
"target=${FLAGS_rootfs_mountpt}"
return 1
fi
# Mount the stateful partition using a loopback device.
offset=$(partoffset "${FLAGS_from}/${filename}" ${NUM_STATEFUL})
if ! sudo mount -o loop,offset=$(( offset * 512 ))${ro_flag} \
"${FLAGS_from}/${filename}" "${FLAGS_stateful_mountpt}" ; then
error "mount failed: options=${ro_flag} offset=$(( offset * 512 ))" \
"target=${FLAGS_stateful_mountpt}"
return 1
fi
# Mount the esp partition using a loopback device.
if [[ -n "${FLAGS_esp_mountpt}" ]]; then
offset=$(partoffset "${FLAGS_from}/${filename}" ${NUM_ESP})
if ! sudo mount -o loop,offset=$(( offset * 512 ))${ro_flag} \
"${FLAGS_from}/${filename}" "${FLAGS_esp_mountpt}" ; then
error "mount failed: options=${ro_flag} offset=$(( offset * 512 ))" \
"target=${FLAGS_esp_mountpt}"
return 1
fi
fi
}
# Mount a gpt based image.
mount_image() {
mkdir -p "${FLAGS_rootfs_mountpt}"
mkdir -p "${FLAGS_stateful_mountpt}"
if [[ -n "${FLAGS_esp_mountpt}" ]]; then
mkdir -p "${FLAGS_esp_mountpt}"
fi
# Get the partitions for the image / device.
if [ -b ${FLAGS_from} ] ; then
get_usb_partitions
elif ! get_gpt_partitions ; then
echo "Current loopback device status:"
sudo losetup --all | sed 's/^/ /'
die "Failed to mount all partitions in ${FLAGS_from}/${FLAGS_image}"
fi
# Mount directories and setup symlinks.
sudo mount --bind "${FLAGS_stateful_mountpt}/overlays/var" \
"${FLAGS_rootfs_mountpt}/var"
sudo mount --bind "${FLAGS_stateful_mountpt}/overlays/usr/local" \
"${FLAGS_rootfs_mountpt}/usr/local"
info "Image specified by ${FLAGS_from} mounted at"\
"${FLAGS_rootfs_mountpt} successfully."
}
# Turn paths into absolute paths.
FLAGS_from=`eval readlink -f ${FLAGS_from}`
FLAGS_rootfs_mountpt=`eval readlink -f ${FLAGS_rootfs_mountpt}`
FLAGS_stateful_mountpt=`eval readlink -f ${FLAGS_stateful_mountpt}`
# Perform desired operation.
if [ ${FLAGS_unmount} -eq ${FLAGS_TRUE} ] ; then
unmount_image
else
mount_image
fi

View File

@ -1,127 +0,0 @@
#!/bin/bash
# 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.
# Helper script that generates the legacy/efi bootloader partitions.
# It does not populate the templates, but can update a loop device.
SCRIPT_ROOT=$(dirname $(readlink -f "$0"))
. "${SCRIPT_ROOT}/common.sh" || exit 1
# 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 || exit 1
# Flags.
DEFINE_string arch "x86" \
"The boot architecture: arm or x86. (Default: x86)"
DEFINE_string from "/tmp/boot" \
"Path the legacy bootloader templates are copied from. (Default /tmp/boot)"
DEFINE_string to "/tmp/esp.img" \
"Path to esp image (Default: /tmp/esp.img)"
DEFINE_integer to_offset 0 \
"Offset in bytes into 'to' if it is a file (Default: 0)"
DEFINE_integer to_size -1 \
"Size in bytes of 'to' to use if it is a file. -1 is ignored. (Default: -1)"
DEFINE_string vmlinuz "/tmp/vmlinuz" \
"Path to the vmlinuz file to use (Default: /tmp/vmlinuz)"
DEFINE_string vmlinuz_boot_kernel "/tmp/vmlinuz-boot_kernel" \
"Path to the vmlinuz-boot_kernel file to use (Default: /tmp/vmlinuz)"
# Parse flags
FLAGS "$@" || exit 1
eval set -- "${FLAGS_ARGV}"
switch_to_strict_mode
part_index_to_uuid() {
local image="$1"
local index="$2"
cgpt show -i "$index" -u "$image"
}
ESP_DEV=
if [[ ! -e "${FLAGS_to}" ]]; then
error "The ESP doesn't exist"
# This shouldn't happen.
info "Creating a new esp image at ${FLAGS_to}" anyway.
# Create EFI System Partition to boot stock EFI BIOS (but not ChromeOS EFI
# BIOS). ARM uses this space to determine which partition is bootable.
# NOTE: The size argument for mkfs.vfat is in 1024-byte blocks.
# We'll hard-code it to 16M for now.
ESP_BLOCKS=16384
/usr/sbin/mkfs.vfat -C "${FLAGS_to}" ${ESP_BLOCKS}
ESP_DEV=$(sudo losetup --show -f "${FLAGS_to}")
if [ -z "${ESP_DEV}" ]; then
die "No free loop devices."
fi
else
if [[ -f "${FLAGS_to}" ]]; then
esp_offset="--offset ${FLAGS_to_offset}"
esp_size="--sizelimit ${FLAGS_to_size}"
if [ ${FLAGS_to_size} -lt 0 ]; then
esp_size=
fi
ESP_DEV=$(sudo losetup --show -f ${esp_offset} ${esp_size} "${FLAGS_to}")
if [ -z "${ESP_DEV}" ]; then
die "No free loop devices."
fi
else
# If it is a block device or something else, try to mount it anyway.
ESP_DEV="${FLAGS_to}"
fi
fi
ESP_FS_DIR=$(mktemp -d /tmp/esp.XXXXXX)
cleanup() {
set +e
if ! safe_umount "${ESP_FS_DIR}"; then
# There is a race condition possible on some ubuntu setups
# with mounting and unmounting a device very quickly
# Doing a quick sleep/retry as a temporary workaround
warn "Initial unmount failed. Possibly crosbug.com/23443. Retrying"
sleep 5
safe_umount "${ESP_FS_DIR}"
fi
if [[ -n "${ESP_DEV}" && -z "${ESP_DEV//\/dev\/loop*}" ]]; then
sudo losetup -d "${ESP_DEV}"
fi
rm -rf "${ESP_FS_DIR}"
}
trap cleanup EXIT
sudo mount "${ESP_DEV}" "${ESP_FS_DIR}"
if [[ "${FLAGS_arch}" = "x86" || "${FLAGS_arch}" = "amd64" ]]; then
# Copy over the grub configurations for cloud machines and the
# kernel into both the A and B slot
sudo mkdir -p "${ESP_FS_DIR}"/boot/grub
sudo cp -r "${FLAGS_from}"/grub/. "${ESP_FS_DIR}"/boot/grub
sudo cp -r "${FLAGS_from}"/grub/menu.lst.A "${ESP_FS_DIR}"/boot/grub/menu.lst
# Prepopulate the syslinux directories too and update for verified boot values
# after the rootfs work is done.
sudo mkdir -p "${ESP_FS_DIR}"/syslinux
sudo cp -r "${FLAGS_from}"/syslinux/. "${ESP_FS_DIR}"/syslinux
# Stage both kernels with the only one we built.
sudo cp -f "${FLAGS_vmlinuz_boot_kernel}" "${ESP_FS_DIR}"/syslinux/vmlinuz-boot_kernel
sudo cp -f "${FLAGS_vmlinuz}" "${ESP_FS_DIR}"/syslinux/vmlinuz.A
sudo cp -f "${FLAGS_vmlinuz}" "${ESP_FS_DIR}"/syslinux/vmlinuz.B
# create the EFI directory tree as a fall-back for UEFI builds and put a copy
# of the kernel in there.
sudo mkdir -p "${ESP_FS_DIR}"/EFI/boot
sudo cp -f "${FLAGS_vmlinuz}" "${ESP_FS_DIR}"/EFI/boot/bootx64.efi
safe_umount "${ESP_FS_DIR}"
sudo syslinux -d /syslinux "${ESP_DEV}"
# mount again for cleanup to free resource gracefully
sudo mount -o ro "${ESP_DEV}" "${ESP_FS_DIR}"
else
die "Unknown arch ${FLAGS_arch}"
fi
set +e