mirror of
https://github.com/flatcar/scripts.git
synced 2025-08-12 15:36:58 +02:00
Merge pull request #147 from marineam/disk_util
New approach to disk/partition handling, disk_util
This commit is contained in:
commit
a63b07ea76
@ -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
|
|
18
build_image
18
build_image
@ -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
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
|
||||||
}
|
|
@ -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() {
|
||||||
|
@ -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)
|
|
@ -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."
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
859
build_library/disk_util
Executable 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)
|
@ -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":{
|
||||||
"num": 9,
|
"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"
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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=( )
|
|
||||||
}
|
|
47
build_library/prod_image_util.sh
Executable file
47
build_library/prod_image_util.sh
Executable 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
|
||||||
|
}
|
@ -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}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
|
@ -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
|
|
Loading…
Reference in New Issue
Block a user