From 5b37c5443a811246ad3cebdc4e341ac4c696264c Mon Sep 17 00:00:00 2001 From: Liam McLoughlin Date: Thu, 16 Aug 2012 11:09:37 -0700 Subject: [PATCH] Simplify and add flexibility to image creation process This change adds support for building the disk layout from a configuration file. It also cleans up much of the image creation code. install_gpt no longer exists, and has been replaced by cgpt.py's write action. This spits out a file that has two functions that can be called to write a partition layout to a disk/file. This gets rid of the gigantic nest of calculations that built the layout previously. All instances of partition/filesystem sizes in build scripts should now be gone in favour of calls to the cgpt.py tool. create_boot_desc has moved inside the base image creation, in an effort to simplify build_image. load_kernel_test is gone since it's apparently not supposed to be called here anyway (asked wfrichar/rspangler about this one). Base image creation now uses files rather than loop devices when building an image. This means we can simply umount them once we're done and not worry about cleaning up the loop device, since it's been done for us. Hash pad calculation has been removed. This is now set manually inside the partition config file. Hybrid MBR creation is gone, since it's now possible to do that in a board specific hook (see overlay-beaglebone/scripts/board_specific_setup.sh). OEM partition now has a filesystem, which is mounted at /usr/share/oem during emerge so that packages can stash files here. root_fs_dir and friends are still globals, but the long-term idea is to make this not the case. BUG=chromium-os:33817 TEST=All types of images and their respective flows (VM, recovery, test, factory etc) Change-Id: I8a596728a4d1845c930e837bea627f5b6a11c098 Reviewed-on: https://gerrit.chromium.org/gerrit/29931 Commit-Ready: Liam McLoughlin Reviewed-by: Liam McLoughlin Tested-by: Liam McLoughlin --- bin/cros_make_image_bootable | 22 +- build_image | 98 +------ build_library/base_image_util.sh | 283 +++++++++--------- build_library/build_common.sh | 30 +- build_library/build_gpt.sh | 88 ------ build_library/build_image_util.sh | 30 +- build_library/cgpt.py | 407 ++++++++++++++++++++++++++ build_library/cgpt_shell.sh | 37 +++ build_library/dev_image_util.sh | 50 ++-- build_library/disk_layout_util.sh | 201 +++++++++++++ build_library/legacy_disk_layout.json | 152 ++++++++++ build_library/test_image_util.sh | 84 +----- common.sh | 1 + image_to_vm.sh | 77 +++-- mod_image_for_recovery.sh | 91 +++--- 15 files changed, 1127 insertions(+), 524 deletions(-) delete mode 100755 build_library/build_gpt.sh create mode 100755 build_library/cgpt.py create mode 100644 build_library/cgpt_shell.sh create mode 100644 build_library/disk_layout_util.sh create mode 100644 build_library/legacy_disk_layout.json diff --git a/bin/cros_make_image_bootable b/bin/cros_make_image_bootable index 71d9db3504..99d33afb9c 100755 --- a/bin/cros_make_image_bootable +++ b/bin/cros_make_image_bootable @@ -33,7 +33,8 @@ assert_inside_chroot # Load functions and constants for chromeos-install . /usr/lib/installer/chromeos-common.sh || exit 1 -. "${SCRIPTS_DIR}/build_library/build_image_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 @@ -70,7 +71,10 @@ locate_gpt set +e # Now parse the build settings from ${OUTPUT_DIR}/boot.desc - +DEFINE_string board "${DEFAULT_BOARD}" \ + "Board we're building for." +DEFINE_string image_type "base" \ + "Type of image we're building for (base/factory_install)." DEFINE_string output_dir "/tmp" \ "Directory to place output in." DEFINE_string image "chromiumos_base.img" \ @@ -141,6 +145,8 @@ FLAGS "${@}" || exit 1 [ -z "${FLAGS_verity_salt}" ] && FLAGS_verity_salt=$(make_salt) +. "${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 @@ -238,10 +244,15 @@ make_image_bootable() { fi local rootfs_hash_size=$(stat -c '%s' ${FLAGS_rootfs_hash}) + local rootfs_fs_size=$(get_filesystem_size ${FLAGS_image_type} 3) + local rootfs_partition_size=$(get_partition_size ${FLAGS_image_type} 3) + local rootfs_hash_pad=$(( rootfs_partition_size - rootfs_fs_size )) info "Appending rootfs.hash (${rootfs_hash_size} bytes) to the root fs" - if [[ ${rootfs_hash_size} -gt $((FLAGS_rootfs_hash_pad * 1024 * 1024)) ]] + if [[ ${rootfs_hash_size} -gt ${rootfs_hash_pad} ]] then - die "--rootfs_hash_pad reserves less than the needed ${rootfs_hash_size}" + die "rootfs_partition_size - rootfs_fs_size is less than the needed " \ + "rootfs_hash_size (${rootfs_hash_size}), update your disk layout " \ + "configuration" fi # Unfortunately, mount_gpt_image uses mount and not losetup to create the # loop devices. This means that they are not the correct size. We have to @@ -251,7 +262,7 @@ make_image_bootable() { rootfs_file_size=$(stat -c '%s' ${root_dev}) hash_offset=$((hash_offset + (${rootfs_file_size} / 512))) else - hash_offset=$((hash_offset + ((1024 * 1024 * ${FLAGS_rootfs_size}) / 512))) + hash_offset=$((hash_offset + (${rootfs_fs_size} / 512))) fi sudo dd bs=512 \ seek=${hash_offset} \ @@ -267,7 +278,6 @@ make_image_bootable() { sudo cp "${FLAGS_output_dir}/vmlinuz_hd.vblock" \ "${FLAGS_statefulfs_mountpoint}" - # START_KERN_A is set by the first call to install the gpt. local koffset="$(partoffset ${image} 2)" sudo dd if="${FLAGS_output_dir}/vmlinuz.image" of="${image}" \ conv=notrunc bs=512 seek=${koffset} diff --git a/build_image b/build_image index f0a19cfeb6..047c9ba82f 100755 --- a/build_image +++ b/build_image @@ -12,7 +12,6 @@ SCRIPT_ROOT=$(dirname $(readlink -f "$0")) . "${SCRIPT_ROOT}/build_library/build_common.sh" || exit 1 - # Developer-visible flags. DEFINE_string board "${DEFAULT_BOARD}" \ "The board to build an image for." @@ -20,22 +19,12 @@ DEFINE_string boot_args "noinitrd" \ "Additional boot arguments to pass to the commandline" DEFINE_boolean enable_rootfs_verification ${FLAGS_TRUE} \ "Default all bootloaders to use kernel-based root fs integrity checking." -DEFINE_boolean full "${FLAGS_FALSE}" "Build full image with all partitions." DEFINE_string output_root "${DEFAULT_BUILD_ROOT}/images" \ "Directory in which to place image result directories (named by version)" -DEFINE_integer rootfs_hash_pad 8 \ - "MiBs reserved at the end of the rootfs image. \ - ceil(0.01 * rootfs_size) is a good minimum." -DEFINE_integer rootfs_partition_size 1024 \ - "rootfs partition size in MiBs." -DEFINE_integer rootfs_size 850 \ - "rootfs filesystem size in MiBs." -DEFINE_integer rootfs_boost_size 0 \ - "MiBs by which to increase the rootfs allocations." +DEFINE_string disk_layout "default" \ + "The board to build an image for." DEFINE_boolean standard_backdoor ${FLAGS_TRUE} \ "Install standard backdoor credentials for testing" -DEFINE_integer statefulfs_size 1024 \ - "stateful filesystem size in MiBs." DEFINE_string usb_disk /dev/sdb3 \ "Path syslinux should use to do a usb boot. Default: /dev/sdb3" @@ -65,23 +54,12 @@ show_help_if_requested "$@" # not needed for the typical developer workflow. DEFINE_integer build_attempt 1 \ "The build attempt for this image build." -DEFINE_boolean hybrid_mbr ${FLAGS_FALSE} \ - "Creates a hybrid MBR rather than a protective one" DEFINE_integer jobs -1 \ "How many packages to build in parallel at maximum." DEFINE_boolean replace ${FLAGS_FALSE} \ "Overwrite existing output, if any." DEFINE_string symlink "latest" \ "Symlink name to use for this image." -DEFINE_integer verity_error_behavior 3 \ - "Kernel verified boot error behavior (0: I/O errors, 1: panic, 2: nothing, \ -3: cros) Default: 3" -DEFINE_integer verity_max_ios -1 \ - "Number of outstanding I/O operations dm-verity caps at. Default: -1" -DEFINE_string verity_algorithm "sha1" \ - "Cryptographic hash algorithm used for kernel vboot. Default : sha1" -DEFINE_string verity_salt "" \ - "Root filesystem salt. Default: randomly generated." DEFINE_string version "" \ "Overrides version number in name to this version." @@ -99,7 +77,7 @@ OVERLAY_CHROMEOS_DIR="${SRC_ROOT}/third_party/chromiumos-overlay/chromeos" # N.B. Ordering matters for some of the libraries below, because # some of the files contain initialization used by later files. . "${BUILD_LIBRARY_DIR}/board_options.sh" || exit 1 -. "${BUILD_LIBRARY_DIR}/build_gpt.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}/base_image_util.sh" || exit 1 @@ -116,25 +94,7 @@ for overlay in $(cros_overlay_list --board "$BOARD"); do fi done -# Tweak sizes defaulted or given based on any boost given (or defaulted). -# This is syntactic sugar to separate concerns in callers, and unify -# default values here. -if [[ $FLAGS_rootfs_boost_size -gt 0 ]]; then - max() { - echo $(( $1 > $2 ? $1 : $2 )) - } - root_boost() { - echo $(( $1 + ( $1 * $FLAGS_rootfs_boost_size + $FLAGS_rootfs_size - 1 ) \ - / $FLAGS_rootfs_size )) - } - FLAGS_rootfs_hash_pad=$(root_boost $FLAGS_rootfs_hash_pad) - FLAGS_rootfs_partition_size=$(root_boost $FLAGS_rootfs_partition_size) - FLAGS_rootfs_size=$(root_boost $FLAGS_rootfs_size) - FLAGS_rootfs_boost_size=0 - # Push up to the recommended minimum if we did not get there. - FLAGS_rootfs_hash_pad=$(max $FLAGS_rootfs_hash_pad \ - $(( ( $FLAGS_rootfs_size + 99 ) / 100 ))) -fi +# TODO: # Tweak flags, configure extra USE flags, and add packages for the factory # install shim. @@ -143,16 +103,6 @@ if should_build_image ${CHROMEOS_FACTORY_INSTALL_SHIM_NAME}; then # TODO: Build a separated ebuild for the install shim to reduce size. INSTALL_MASK="${FACTORY_SHIM_INSTALL_MASK}" - # Reduce the size of factory install shim. Note that 400M is too much, it - # should be smaller, see http://crosbug.com/34167 - FLAGS_rootfs_size=400 - FLAGS_rootfs_partition_size=420 - FLAGS_statefulfs_size=140 - info "Fixing the rootfs size at ${FLAGS_rootfs_partition_size} MiB " \ - "for install shim" - info "Fixing the statefulfs size at ${FLAGS_statefulfs_size} MiB " \ - "for install shim" - # Add the cros_factory_install boot arg. FLAGS_boot_args="${FLAGS_boot_args} cros_factory_install" @@ -172,11 +122,7 @@ if should_build_image ${CHROMEOS_FACTORY_INSTALL_SHIM_NAME}; then fi fi -if [ $((FLAGS_rootfs_size + FLAGS_rootfs_hash_pad)) -gt \ - ${FLAGS_rootfs_partition_size} ] ; then - die_notrace "rootfs ($((FLAGS_rootfs_size + FLAGS_rootfs_hash_pad)) MiB) is" \ - "bigger than partition (${FLAGS_rootfs_partition_size} MiB)." -fi +# TODO: # If we are creating a developer image, also create a pristine image with a # different name. @@ -187,17 +133,8 @@ else PRISTINE_IMAGE_NAME=${CHROMEOS_BASE_IMAGE_NAME} fi -ROOT_FS_DIR="${BUILD_DIR}/rootfs" -STATEFUL_FS_DIR="${BUILD_DIR}/stateful_partition" -ESP_FS_DIR=${BUILD_DIR}/esp - DEVKEYSDIR="/usr/share/vboot/devkeys" -# ${DEV_IMAGE_ROOT} specifies the location of where developer packages will -# be installed on the stateful dir. On a Chromium OS system, this will -# translate to /usr/local. -DEV_IMAGE_ROOT="${STATEFUL_FS_DIR}/dev_image" - eclean-$BOARD -d packages if [[ ${skip_blacklist_check} -ne 1 ]]; then @@ -228,29 +165,10 @@ fi # Create the output directory and temporary mount points. mkdir -p "${BUILD_DIR}" -mkdir -p "${ROOT_FS_DIR}" "${STATEFUL_FS_DIR}" "${ESP_FS_DIR}" - -# Create the boot.desc file which stores the build-time configuration -# information needed for making the image bootable after creation with -# cros_make_image_bootable. -create_boot_desc +# Create the base image. create_base_image ${PRISTINE_IMAGE_NAME} - -BOOT_FLAG= -if should_build_image ${CHROMEOS_FACTORY_INSTALL_SHIM_NAME}; then - BOOT_FLAG="-b 1" # BOOT_FLAG_DEVELOPER value defined in load_kernel_fw.h - info "--factory_install set, pass BOOT_FLAG_DEVELOPER flag to" \ - "load_kernel_test" -fi - -# Verify pristine image if we built it. -if should_build_image "${PRISTINE_IMAGE_NAME}"; then - load_kernel_test "${BUILD_DIR}/${PRISTINE_IMAGE_NAME}" \ - "${DEVKEYSDIR}/recovery_key.vbpubk" ${BOOT_FLAG} -fi - # Running board-specific setup if any exists. if type board_setup &>/dev/null; then board_setup "${BUILD_DIR}/${PRISTINE_IMAGE_NAME}" @@ -274,8 +192,6 @@ if should_build_image ${CHROMEOS_FACTORY_TEST_IMAGE_NAME}; then mod_image_for_test ${CHROMEOS_FACTORY_TEST_IMAGE_NAME} fi -rmdir "${ROOT_FS_DIR}" "${STATEFUL_FS_DIR}" "${ESP_FS_DIR}" - # Generating AU generator zip file to run outside chroot generate_au_zip || echo "Failed generating AU zip file - ignoring Error..." @@ -283,7 +199,7 @@ generate_au_zip || echo "Failed generating AU zip file - ignoring Error..." LINK_NAME="${FLAGS_output_root}/${BOARD}/${FLAGS_symlink}" ln -sfT $(basename ${BUILD_DIR}) ${LINK_NAME} -echo "Done. Image(s) created in ${BUILD_DIR}" +echo "Done. Image(s) created in ${BUILD_DIR}" # Print out the images we generated. if should_build_image ${CHROMEOS_BASE_IMAGE_NAME}; then diff --git a/build_library/base_image_util.sh b/build_library/base_image_util.sh index 0e548d6b9b..dd9a6ecb3d 100755 --- a/build_library/base_image_util.sh +++ b/build_library/base_image_util.sh @@ -2,54 +2,29 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -# Shell function library and global variable initialization for -# creating an initial base image. The main function for export in -# this library is 'create_base_image'; the remainder of the code is -# not used outside this file. +. "${SRC_ROOT}/platform/dev/toolchain_utils.sh" || exit 1 +cleanup_mounts() { + # Disable die on error. + set +e -ROOT_LOOP_DEV= -STATEFUL_LOOP_DEV= - -ROOT_FS_IMG="${BUILD_DIR}/rootfs.image" -STATEFUL_FS_IMG="${BUILD_DIR}/stateful_partition.image" -ESP_FS_IMG=${BUILD_DIR}/esp.image - -cleanup_rootfs_loop() { # See if we ran out of space. - local df=$(df -B 1M "${ROOT_FS_DIR}") + 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' | \ + 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 - sudo umount -d "${ROOT_FS_DIR}" -} -cleanup_stateful_fs_loop() { - sudo umount "${ROOT_FS_DIR}/usr/local" - sudo umount "${ROOT_FS_DIR}/var" - sudo umount -d "${STATEFUL_FS_DIR}" -} + echo "Cleaning up mounts" + safe_umount_tree "${root_fs_dir}" + safe_umount_tree "${stateful_fs_dir}" + safe_umount_tree "${esp_fs_dir}" -loopback_cleanup() { - # Disable die on error. - set +e - - if [[ -n "${STATEFUL_LOOP_DEV}" ]]; then - cleanup_stateful_fs_loop - STATEFUL_LOOP_DEV= - fi - - if [[ -n "${ROOT_LOOP_DEV}" ]]; then - cleanup_rootfs_loop - ROOT_LOOP_DEV= - fi - - # Turn die on error back on. + # Turn die on error back on. set -e } @@ -58,43 +33,62 @@ zero_free_space() { info "Zeroing freespace in ${fs_mount_point}" # dd is a silly thing and will produce a "No space left on device" message # that cannot be turned off and is confusing to unsuspecting victims. + info "${fs_mount_point}/filler" ( sudo dd if=/dev/zero of="${fs_mount_point}/filler" bs=4096 conv=fdatasync \ status=noxfer || true ) 2>&1 | grep -v "No space left on device" sudo rm "${fs_mount_point}/filler" } -# Takes as an arg the name of the image to be created. create_base_image() { local image_name=$1 + local rootfs_verification_enabled=$2 + local image_type="base" - trap "loopback_cleanup && delete_prompt" EXIT - - # Create and format the root file system. - - # Create root file system disk image. - ROOT_SIZE_BYTES=$((1024 * 1024 * ${FLAGS_rootfs_size})) - - # Pad out for the hash tree. - ROOT_HASH_PAD=$((FLAGS_rootfs_hash_pad * 1024 * 1024)) - info "Padding the rootfs image by ${ROOT_HASH_PAD} bytes for hash data" - - dd if=/dev/zero of="${ROOT_FS_IMG}" bs=1 count=1 \ - seek=$((ROOT_SIZE_BYTES + ROOT_HASH_PAD - 1)) status=noxfer - - ROOT_LOOP_DEV=$(sudo losetup --show -f "${ROOT_FS_IMG}") - if [ -z "${ROOT_LOOP_DEV}" ] ; then - die_notrace \ - "No free loop device. Free up a loop device or reboot. exiting. " + if [[ "${FLAGS_disk_layout}" != "default" ]]; then + image_type="${FLAGS_disk_layout}" + else + if should_build_image ${CHROMEOS_FACTORY_INSTALL_SHIM_NAME}; then + image_type="factory_install" + fi fi - # Specify a block size and block count to avoid using the hash pad. - sudo mkfs.ext2 -q -b 4096 "${ROOT_LOOP_DEV}" "$((ROOT_SIZE_BYTES / 4096))" + info "Using image type ${image_type}" - # Tune and mount rootfs. - DISK_LABEL="C-ROOT" - # Disable checking and minimize metadata differences across builds - # and wasted reserved space. - sudo tune2fs -L "${DISK_LABEL}" \ + root_fs_dir="${BUILD_DIR}/rootfs" + stateful_fs_dir="${BUILD_DIR}/stateful" + 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_img="${BUILD_DIR}/rootfs.image" + local root_fs_bytes=$(get_filesystem_size ${image_type} 3) + local root_fs_label=$(get_label ${image_type} 3) + + local stateful_fs_img="${BUILD_DIR}/stateful.image" + local stateful_fs_bytes=$(get_filesystem_size ${image_type} 1) + local stateful_fs_label=$(get_label ${image_type} 1) + local stateful_fs_uuid=$(uuidgen) + + local esp_fs_img="${BUILD_DIR}/esp.image" + local esp_fs_bytes=$(get_filesystem_size ${image_type} 12) + local esp_fs_label=$(get_label ${image_type} 12) + + local oem_fs_img="${BUILD_DIR}/oem.image" + local oem_fs_bytes=$(get_filesystem_size ${image_type} 8) + local oem_fs_label=$(get_label ${image_type} 8) + local oem_fs_uuid=$(uuidgen) + + local block_size=$(get_fs_block_size) + + # Build root FS image. + info "Building ${root_fs_img}" + dd if=/dev/zero of="${root_fs_img}" bs=1 count=1 \ + seek=$((root_fs_bytes - 1)) status=noxfer + sudo mkfs.ext2 -F -q -b $block_size "${root_fs_img}" \ + "$((root_fs_bytes / block_size))" + sudo tune2fs -L "${root_fs_label}" \ -U clear \ -T 20091119110000 \ -c 0 \ @@ -102,46 +96,59 @@ create_base_image() { -m 0 \ -r 0 \ -e remount-ro \ - "${ROOT_LOOP_DEV}" - # TODO(wad) call tune2fs prior to finalization to set the mount count to 0. - sudo mount -t ext2 "${ROOT_LOOP_DEV}" "${ROOT_FS_DIR}" + "${root_fs_img}" + mkdir -p "${root_fs_dir}" + sudo mount -o loop "${root_fs_img}" "${root_fs_dir}" - # Create stateful partition of the same size as the rootfs. - STATEFUL_SIZE_BYTES=$((1024 * 1024 * ${FLAGS_statefulfs_size})) - dd if=/dev/zero of="${STATEFUL_FS_IMG}" bs=1 count=1 \ - seek=$((STATEFUL_SIZE_BYTES - 1)) status=noxfer + df -h "${root_fs_dir}" - # Tune and mount the stateful partition. - UUID=$(uuidgen) - DISK_LABEL="C-STATE" - STATEFUL_LOOP_DEV=$(sudo losetup --show -f "${STATEFUL_FS_IMG}") - if [ -z "${STATEFUL_LOOP_DEV}" ] ; then - die_notrace \ - "No free loop device. Free up a loop device or reboot. exiting. " - fi - sudo mkfs.ext4 -q "${STATEFUL_LOOP_DEV}" - sudo tune2fs -L "${DISK_LABEL}" -U "${UUID}" -c 0 -i 0 "${STATEFUL_LOOP_DEV}" - sudo mount -t ext4 "${STATEFUL_LOOP_DEV}" "${STATEFUL_FS_DIR}" + # Build stateful FS disk image. + info "Building ${stateful_fs_img}" + dd if=/dev/zero of="${stateful_fs_img}" bs=1 count=1 \ + seek=$((stateful_fs_bytes - 1)) status=noxfer + sudo mkfs.ext4 -F -q "${stateful_fs_img}" + sudo tune2fs -L "${stateful_fs_label}" -U "${stateful_fs_uuid}" \ + -c 0 -i 0 "${stateful_fs_img}" + mkdir -p "${stateful_fs_dir}" + sudo mount -o loop "${stateful_fs_img}" "${stateful_fs_dir}" - # -- Install packages into the root file system -- + # Build ESP disk image. + info "Building ${esp_fs_img}" + dd if=/dev/zero of="${esp_fs_img}" bs=1 count=1 \ + seek=$((esp_fs_bytes - 1)) status=noxfer + sudo mkfs.vfat "${esp_fs_img}" + + # Build OEM FS disk image. + info "Building ${oem_fs_img}" + dd if=/dev/zero of="${oem_fs_img}" bs=1 count=1 \ + seek=$((oem_fs_bytes - 1)) status=noxfer + sudo mkfs.ext4 -F -q "${oem_fs_img}" + sudo 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 stateful partition with some pre-created directories. - sudo mkdir -p "${DEV_IMAGE_ROOT}" - sudo mkdir -p "${STATEFUL_FS_DIR}/var_overlay" + sudo mkdir "${stateful_fs_dir}/dev_image" + sudo mkdir "${stateful_fs_dir}/var_overlay" # Create symlinks so that /usr/local/usr based directories are symlinked to # /usr/local/ directories e.g. /usr/local/usr/bin -> /usr/local/bin, etc. - setup_symlinks_on_root "${DEV_IMAGE_ROOT}" "${STATEFUL_FS_DIR}/var_overlay" \ - "${STATEFUL_FS_DIR}" + setup_symlinks_on_root "${stateful_fs_dir}/dev_image" \ + "${stateful_fs_dir}/var_overlay" "${stateful_fs_dir}" # Perform binding rather than symlinking because directories must exist # on rootfs so that we can bind at run-time since rootfs is read-only. info "Binding directories from stateful partition onto the rootfs" - sudo mkdir -p "${ROOT_FS_DIR}/usr/local" - sudo mount --bind "${DEV_IMAGE_ROOT}" "${ROOT_FS_DIR}/usr/local" - sudo mkdir -p "${ROOT_FS_DIR}/var" - sudo mount --bind "${STATEFUL_FS_DIR}/var_overlay" "${ROOT_FS_DIR}/var" - sudo mkdir -p "${ROOT_FS_DIR}/dev" + sudo mkdir -p "${root_fs_dir}/usr/local" + sudo mount --bind "${stateful_fs_dir}/dev_image" "${root_fs_dir}/usr/local" + sudo mkdir -p "${root_fs_dir}/var" + sudo mount --bind "${stateful_fs_dir}/var_overlay" "${root_fs_dir}/var" + sudo mkdir -p "${root_fs_dir}/dev" + + info "Binding directories from OEM partition onto the rootfs" + sudo mkdir -p "${root_fs_dir}/usr/share/oem" + sudo mount --bind "${oem_fs_dir}" "${root_fs_dir}/usr/share/oem" # We need to install libc manually from the cross toolchain. # TODO: Improve this? It would be ideal to use emerge to do this. @@ -150,37 +157,47 @@ create_base_image() { LIBC_PATH="${PKGDIR}/cross-${CHOST}/${LIBC_TAR}" if ! [[ -e ${LIBC_PATH} ]]; then - die_notrace \ - "${LIBC_PATH} does not exist. Try running ./setup_board" \ - "--board=${BOARD} to update the version of libc installed on that board." + die_notrace \ + "${LIBC_PATH} does not exist. Try running ./setup_board" \ + "--board=${BOARD} to update the version of libc installed on that board." fi - sudo tar jxpf "${LIBC_PATH}" -C "${ROOT_FS_DIR}" ./usr/${CHOST} \ - --strip-components=3 --exclude=usr/include --exclude=sys-include \ - --exclude=*.a --exclude=*.o + sudo tar jxpf "${LIBC_PATH}" -C "${root_fs_dir}" ./usr/${CHOST} \ + --strip-components=3 --exclude=usr/include --exclude=sys-include \ + --exclude=*.a --exclude=*.o - . "${SRC_ROOT}/platform/dev/toolchain_utils.sh" board_ctarget=$(get_ctarget_from_board "${BOARD}") for atom in $(portageq match / cross-$board_ctarget/gcc); do - copy_gcc_libs "${ROOT_FS_DIR}" $atom + copy_gcc_libs "${root_fs_dir}" $atom done if should_build_image ${CHROMEOS_FACTORY_INSTALL_SHIM_NAME}; then # Install our custom factory install kernel with the appropriate use flags # to the image. - emerge_custom_kernel "${ROOT_FS_DIR}" + emerge_custom_kernel "${root_fs_dir}" fi - # We "emerge --root=${ROOT_FS_DIR} --root-deps=rdeps --usepkgonly" all of the + # We "emerge --root=${root_fs_dir} --root-deps=rdeps --usepkgonly" all of the # runtime packages for chrome os. This builds up a chrome os image from # binary packages with runtime dependencies only. We use INSTALL_MASK to # trim the image size as much as possible. - emerge_to_image --root="${ROOT_FS_DIR}" chromeos ${EXTRA_PACKAGES} + emerge_to_image --root="${root_fs_dir}" chromeos ${EXTRA_PACKAGES} # Set /etc/lsb-release on the image. "${OVERLAY_CHROMEOS_DIR}/scripts/cros_set_lsb_release" \ - --root="${ROOT_FS_DIR}" \ - --board="${BOARD}" + --root="${root_fs_dir}" \ + --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 "${image_type}" + + # Write out the GPT creation script. + # This MUST be done before writing bootloader templates else we'll break + # the hash on the root FS. + write_partition_script "${image_type}" \ + "${root_fs_dir}/${PARTITION_SCRIPT_PATH}" # Populates the root filesystem with legacy bootloader templates # appropriate for the platform. The autoupdater and installer will @@ -191,79 +208,53 @@ create_base_image() { # not support verified boot yet (see create_legacy_bootloader_templates.sh) # so rootfs verification is disabled if we are building with --factory_install local enable_rootfs_verification= - if [[ ${FLAGS_enable_rootfs_verification} -eq ${FLAGS_TRUE} ]]; then - enable_rootfs_verification="--enable_rootfs_verification" + if [[ ${rootfs_verification_enabled} -eq 1 ]]; then + enable_rootfs_verification="--enable_rootfs_verification" fi ${BUILD_LIBRARY_DIR}/create_legacy_bootloader_templates.sh \ --arch=${ARCH} \ - --to="${ROOT_FS_DIR}"/boot \ - --boot_args="${FLAGS_boot_args}" \ - ${enable_rootfs_verification} + --to="${root_fs_dir}"/boot \ + ${enable_rootfs_verification} # Don't test the factory install shim if ! should_build_image ${CHROMEOS_FACTORY_INSTALL_SHIM_NAME}; then - if [[ ${skip_test_image_content} -ne 1 ]]; then + if [[ ${skip_test_image_content} -ne 1 ]]; then # Check that the image has been correctly created. - test_image_content "$ROOT_FS_DIR" + test_image_content "$root_fs_dir" fi fi # Clean up symlinks so they work on a running target rooted at "/". # Here development packages are rooted at /usr/local. However, do not # create /usr/local or /var on host (already exist on target). - setup_symlinks_on_root "/usr/local" "/var" "${STATEFUL_FS_DIR}" - - # 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, unless - # we are building a factory shim, in which case a larger room is - # needed to allow two kernel blobs (each including initramfs) in - # one EFI partition. - if should_build_image ${CHROMEOS_FACTORY_INSTALL_SHIM_NAME}; then - /usr/sbin/mkfs.vfat -C "${ESP_FS_IMG}" 32768 - else - /usr/sbin/mkfs.vfat -C "${ESP_FS_IMG}" 16384 - fi + setup_symlinks_on_root "/usr/local" "/var" "${stateful_fs_dir}" # Zero rootfs free space to make it more compressible so auto-update # payloads become smaller - zero_free_space "${ROOT_FS_DIR}" - loopback_cleanup - trap delete_prompt EXIT + zero_free_space "${root_fs_dir}" - if [[ ${FLAGS_full} -eq ${FLAGS_TRUE} ]]; then - dd if=/dev/zero of="${BUILD_DIR}/${image_name}" bs=1M count=3584 - fi + cleanup_mounts # Create the GPT-formatted image. build_gpt "${BUILD_DIR}/${image_name}" \ - "${ROOT_FS_IMG}" \ - "${STATEFUL_FS_IMG}" \ - "${ESP_FS_IMG}" + "${root_fs_img}" \ + "${stateful_fs_img}" \ + "${esp_fs_img}" + # Clean up temporary files. - rm -f "${ROOT_FS_IMG}" "${STATEFUL_FS_IMG}" "${ESP_FS_IMG}" + rm -f "${root_fs_img}" "${stateful_fs_img}" "${esp_fs_img}" "{oem_fs_img}" # Emit helpful scripts for testers, etc. emit_gpt_scripts "${BUILD_DIR}/${image_name}" "${BUILD_DIR}" - trap - EXIT - USE_DEV_KEYS= if should_build_image ${CHROMEOS_FACTORY_INSTALL_SHIM_NAME}; then USE_DEV_KEYS="--use_dev_keys" fi # Place flags before positional args. - if should_build_image ${image_name}; then - ${SCRIPTS_DIR}/bin/cros_make_image_bootable "${BUILD_DIR}" \ - ${image_name} \ - ${USE_DEV_KEYS} - fi - - # Setup hybrid MBR if it was enabled - if [[ ${FLAGS_hybrid_mbr} -eq ${FLAGS_TRUE} ]]; then - install_hybrid_mbr "${BUILD_DIR}/${image_name}" - fi + ${SCRIPTS_DIR}/bin/cros_make_image_bootable "${BUILD_DIR}" \ + ${image_name} \ + ${USE_DEV_KEYS} } diff --git a/build_library/build_common.sh b/build_library/build_common.sh index e3f217e257..a6a9cfa0ac 100644 --- a/build_library/build_common.sh +++ b/build_library/build_common.sh @@ -17,5 +17,33 @@ restart_in_chroot_if_needed "$@" INSTALLER_ROOT=/usr/lib/installer . "${INSTALLER_ROOT}/chromeos-common.sh" || exit 1 -BUILD_LIBRARY_DIR=${SCRIPTS_DIR}/build_library 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..." + $COMMON_PV_CAT "${src}" >"${dst}" || die "Cannot copy $1 to $2" + else + mv "${src}" "${dst}" || die "Cannot move $1 to $2" + fi +} diff --git a/build_library/build_gpt.sh b/build_library/build_gpt.sh deleted file mode 100755 index 74285d376b..0000000000 --- a/build_library/build_gpt.sh +++ /dev/null @@ -1,88 +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. - -emit_gpt_scripts() { - local image="$1" - local dir="$2" - - local pack="$dir/pack_partitions.sh" - local unpack="$dir/unpack_partitions.sh" - - cat >"$unpack" <
&2 - exit 1 -fi -set -x -HEADER - - $GPT show "$image" | sed -e 's/^/# /' >>"$unpack" - cp "$unpack" "$pack" - - $GPT show -q "$image" | - while read start size part x; do - local file="part_$part" - local target="\"\$TARGET\"" - local dd_args="bs=512 count=$size" - echo "dd if=$target of=$file $dd_args skip=$start" >>"$unpack" - echo "dd if=$file of=$target $dd_args seek=$start conv=notrunc" \ - >>"$pack" - done - - chmod +x "$unpack" "$pack" -} - - -build_gpt() { - local outdev="$1" - local rootfs_img="$2" - local stateful_img="$3" - local esp_img="$4" - - # We'll need some code to put in the PMBR, for booting on legacy BIOS. - local pmbr_img - if [ "$ARCH" = "arm" ]; then - pmbr_img=/dev/zero - elif [[ "$ARCH" = "x86" || "$ARCH" = "amd64" ]]; then - pmbr_img=$(readlink -f /usr/share/syslinux/gptmbr.bin) - else - error "Unknown architecture: $ARCH" - return 1 - fi - - GPT_FULL="false" - [ "${FLAGS_full}" -eq "${FLAGS_TRUE}" ] && GPT_FULL="true" - - # Create the GPT. This has the side-effect of setting some global vars - # describing the partition table entries (see the comments in the source). - install_gpt "$outdev" $(numsectors "$rootfs_img") \ - $(numsectors "$stateful_img") $pmbr_img $(numsectors "$esp_img") \ - $GPT_FULL $FLAGS_rootfs_partition_size - - local sudo= - if [ ! -w "$outdev" ] ; then - # use sudo when writing to a block device. - sudo=sudo - fi - - # Now populate the partitions. - info "Copying stateful partition..." - $sudo dd if="$stateful_img" of="$outdev" conv=notrunc bs=512 \ - seek=$START_STATEFUL - - info "Copying rootfs..." - $sudo dd if="$rootfs_img" of="$outdev" conv=notrunc bs=512 \ - seek=$START_ROOTFS_A - - info "Copying EFI system partition..." - $sudo dd if="$esp_img" of="$outdev" conv=notrunc bs=512 \ - seek=$START_ESP - - # Pre-set "sucessful" bit in gpt, so we will never mark-for-death - # a partition on an SDCard/USB stick. - $GPT add -i 2 -S 1 "$outdev" -} diff --git a/build_library/build_image_util.sh b/build_library/build_image_util.sh index d355ad89e3..90c2c61f3b 100755 --- a/build_library/build_image_util.sh +++ b/build_library/build_image_util.sh @@ -22,6 +22,14 @@ BUILD_DIR="${FLAGS_output_root}/${BOARD}/${IMAGE_SUBDIR}" OUTSIDE_OUTPUT_DIR="../build/images/${BOARD}/${IMAGE_SUBDIR}" IMAGES_TO_BUILD= +EMERGE_BOARD_CMD="$GCLIENT_ROOT/chromite/bin/parallel_emerge" +EMERGE_BOARD_CMD="$EMERGE_BOARD_CMD --board=$BOARD" + +export INSTALL_MASK="${DEFAULT_INSTALL_MASK}" + +if [[ $FLAGS_jobs -ne -1 ]]; then + EMERGE_JOBS="--jobs=$FLAGS_jobs" +fi # Populates list of IMAGES_TO_BUILD from args passed in. # Arguments should be the shortnames of images we want to build. @@ -106,9 +114,9 @@ make_salt() { xxd -l 32 -p -c 32 /dev/urandom } -# Takes no arguments and populates the configuration for -# cros_make_image_bootable. create_boot_desc() { + local image_type=$1 + local enable_rootfs_verification_flag="" if [[ ${FLAGS_enable_rootfs_verification} -eq ${FLAGS_TRUE} ]]; then enable_rootfs_verification_flag="--enable_rootfs_verification" @@ -116,17 +124,13 @@ create_boot_desc() { [ -z "${FLAGS_verity_salt}" ] && FLAGS_verity_salt=$(make_salt) cat < ${BUILD_DIR}/boot.desc + --board=${BOARD} + --image_type=${image_type} --arch="${ARCH}" - --boot_args="${FLAGS_boot_args}" - --rootfs_size="${FLAGS_rootfs_size}" - --rootfs_hash_pad="${FLAGS_rootfs_hash_pad}" - --verity_error_behavior="${FLAGS_verity_error_behavior}" - --verity_max_ios="${FLAGS_verity_max_ios}" - --verity_algorithm="${FLAGS_verity_algorithm}" - --verity_salt="${FLAGS_verity_salt}" --keys_dir="${DEVKEYSDIR}" --usb_disk="${FLAGS_usb_disk}" --nocleanup_dirs + --verity_algorithm=sha1 ${enable_rootfs_verification_flag} EOF } @@ -159,3 +163,11 @@ generate_au_zip () { info "Running ${lgenerateauzip} ${largs} for generating AU updater zip file" $lgenerateauzip $largs } + +# Basic command to emerge binary packages into the target image. +# Arguments to this command are passed as addition options/arguments +# to the basic emerge command. +emerge_to_image() { + sudo -E ${EMERGE_BOARD_CMD} --root-deps=rdeps --usepkgonly -v \ + "$@" ${EMERGE_JOBS} +} diff --git a/build_library/cgpt.py b/build_library/cgpt.py new file mode 100755 index 0000000000..56d176ec8d --- /dev/null +++ b/build_library/cgpt.py @@ -0,0 +1,407 @@ +#!/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 copy +import json +import os +import sys + +# First sector we can use. +START_SECTOR = 64 + +class ConfigNotFound(Exception): + pass +class PartitionNotFound(Exception): + pass +class InvalidLayout(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 + """ + + if not os.path.exists(filename): + raise ConfigNotFound("Partition config %s was not found!" % filename) + with open(filename) as f: + config = json.load(f) + f.close() + + metadata = config["metadata"] + metadata["block_size"] = int(metadata["block_size"]) + + for layout_name, layout in config["layouts"].items(): + for part in layout: + 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") + + return config + + +def GetTableTotals(config, partitions): + """Calculates total sizes/counts for a partition table. + + Args: + config: Partition configuration file object + partitions: List of partitions to process + Returns: + Dict containing totals data + """ + + ret = { + "expand_count": 0, + "expand_min": 0, + "block_count": START_SECTOR * config["metadata"]["block_size"] + } + + # Total up the size of all non-expanding partitions to get the minimum + # required disk size. + for partition in partitions: + if "features" in partition and "expand" in partition["features"]: + ret["expand_count"] += 1 + ret["expand_min"] += partition["blocks"] + else: + ret["block_count"] += partition["blocks"] + + # At present, only one expanding partition is permitted. + # Whilst it'd be possible to have two, we don't need this yet + # and it complicates things, so it's been left out for now. + if ret["expand_count"] > 1: + raise InvalidLayout("1 expand partition allowed, %d requested" + % ret["expand_count"]) + + ret["min_disk_size"] = ret["block_count"] + ret["expand_min"] + + return ret + + +def GetPartitionTable(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: + 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"] + + 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 + + return partitions + + +def GetScriptShell(): + """Loads and returns the skeleton script for our output script. + + Returns: + A string containg the skeleton script + """ + + script_shell_path = os.path.join(os.path.dirname(__file__), "cgpt_shell.sh") + with open(script_shell_path, "r") as f: + script_shell = "".join(f.readlines()) + f.close() + + # Before we return, insert the path to this tool so somebody reading the + # script later can tell where it was generated. + script_shell = script_shell.replace("@SCRIPT_GENERATOR@", script_shell_path) + + return script_shell + + +def WriteLayoutFunction(sfile, func_name, image_type, config): + """Writes a shell script function to write out a given partition table. + + Args: + sfile: File handle we're writing to + func_name: Function name to write out for specified layout + image_type: Type of image eg base/test/dev/factory_install + config: Partition configuration file object + """ + + partitions = GetPartitionTable(config, image_type) + partition_totals = GetTableTotals(config, partitions) + + sfile.write("%s() {\ncreate_image $1 %d %s\n" % ( + func_name, partition_totals["min_disk_size"], + config["metadata"]["block_size"])) + + sfile.write("CURR=%d\n" % START_SECTOR) + sfile.write("$GPT create $1\n") + + # Pass 1: Set up the expanding partition size. + for partition in partitions: + if partition["type"] != "blank": + partition["var"] = partition["blocks"] + + if partition["num"] == 1: + if "features" in partition and "expand" in partition["features"]: + sfile.write("if [ -b $1 ]; then\n") + sfile.write("STATEFUL_SIZE=$(( $(numsectors $1) - %d))\n" % + partition_totals["block_count"]) + sfile.write("else\n") + sfile.write("STATEFUL_SIZE=%s\n" % partition["blocks"]) + sfile.write("fi\n") + partition["var"] = "$STATEFUL_SIZE" + + # Pass 2: Write out all the cgpt add commands. + for partition in partitions: + if partition["type"] != "blank": + sfile.write("$GPT add -i %d -b $CURR -s %s -t %s -l %s $1 && " % ( + partition["num"], str(partition["var"]), partition["type"], + partition["label"])) + + # Increment the CURR counter ready for the next partition. + sfile.write("CURR=$(( $CURR + %s ))\n" % partition["blocks"]) + + # Set default priorities on kernel partitions + sfile.write("$GPT add -i 2 -S 0 -T 15 -P 15 $1\n") + sfile.write("$GPT add -i 4 -S 0 -T 15 -P 0 $1\n") + sfile.write("$GPT add -i 6 -S 0 -T 15 -P 0 $1\n") + + sfile.write("$GPT boot -p -b $2 -i 12 $1\n") + sfile.write("$GPT show $1\n") + sfile.write("}\n") + + +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 WritePartitionScript(image_type, layout_filename, sfilename): + """Writes a shell script with functions for the base and requested layouts. + + Args: + image_type: Type of image eg base/test/dev/factory_install + layout_filename: Path to partition configuration file + sfilename: Filename to write the finished script to + """ + + config = LoadPartitionConfig(layout_filename) + + sfile = open(sfilename, "w") + script_shell = GetScriptShell() + sfile.write(script_shell) + + WriteLayoutFunction(sfile, "write_base_table", "base", config) + WriteLayoutFunction(sfile, "write_partition_table", image_type, config) + + sfile.close() + + +def GetBlockSize(layout_filename): + """Returns the partition table block size. + + Args: + 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(layout_filename): + """Returns the filesystem block size. + + 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(image_type, layout_filename, num): + """Returns the partition size of a given partition for a given layout type. + + Args: + 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 + """ + + config = LoadPartitionConfig(layout_filename) + partitions = GetPartitionTable(config, image_type) + partition = GetPartitionByNumber(partitions, num) + + return partition["bytes"] + + +def GetFilesystemSize(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: + 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 + """ + + config = LoadPartitionConfig(layout_filename) + partitions = GetPartitionTable(config, image_type) + partition = GetPartitionByNumber(partitions, num) + + if "fs_bytes" in partition: + return partition["fs_bytes"] + else: + return partition["bytes"] + + +def GetLabel(image_type, layout_filename, num): + """Returns the label for a given partition. + + Args: + 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 + """ + + config = LoadPartitionConfig(layout_filename) + partitions = GetPartitionTable(config, image_type) + partition = GetPartitionByNumber(partitions, num) + + if "label" in partition: + return partition["label"] + else: + return "UNTITLED" + + +def DoDebugOutput(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: + image_type: Type of image eg base/test/dev/factory_install + layout_filename: Path to partition configuration file + """ + config = LoadPartitionConfig(layout_filename) + partitions = GetPartitionTable(config, 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 main(argv): + action_map = { + "write": { + "argc": 4, + "usage": " ", + "func": WritePartitionScript + }, + "readblocksize": { + "argc": 2, + "usage": "", + "func": GetBlockSize + }, + "readfsblocksize": { + "argc": 2, + "usage": "", + "func": GetFilesystemBlockSize + }, + "readpartsize": { + "argc": 4, + "usage": " ", + "func": GetPartitionSize + }, + "readfssize": { + "argc": 4, + "usage": " ", + "func": GetFilesystemSize + }, + "readlabel": { + "argc": 4, + "usage": " ", + "func": GetLabel + }, + "debug": { + "argc": 3, + "usage": " ", + "func": DoDebugOutput + } + } + + if len(sys.argv) < 2 or sys.argv[1] not in action_map: + print "Usage: %s \n" % sys.argv[0] + print "Valid actions are:" + for action in action_map: + print " %s %s" % (action, action_map[action]["usage"]) + sys.exit(1) + else: + action_name = sys.argv[1] + action = action_map[action_name] + if action["argc"] == len(sys.argv) - 1: + print action["func"](*sys.argv[2:]) + else: + sys.exit("Usage: %s %s %s" % (sys.argv[0], sys.argv[1], action["usage"])) + +if __name__ == "__main__": + main(sys.argv) diff --git a/build_library/cgpt_shell.sh b/build_library/cgpt_shell.sh new file mode 100644 index 0000000000..68298f113d --- /dev/null +++ b/build_library/cgpt_shell.sh @@ -0,0 +1,37 @@ +#!/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. + +# This script is automatically generated by @SCRIPT_GENERATOR@ +# Do not edit! + +if ! type numsectors &>/dev/null; then + if [[ -f "/usr/sbin/chromeos-common.sh" ]]; then + . "/usr/sbin/chromeos-common.sh" + else + die "Can't load chromeos-common.sh, dying!" + fi +fi +locate_gpt + +# Usage: create_image +# If is a block device, wipes out the GPT +# If it's not, it creates a new file of the requested size +create_image() { + local dev=$1 + local min_disk_size=$2 + local block_size=$3 + if [[ -b "${dev}" ]]; then + # Zap any old partitions (otherwise gpt complains). + dd if=/dev/zero of="${dev}" conv=notrunc bs=512 count=32 + dd if=/dev/zero of="${dev}" conv=notrunc bs=512 \ + seek=$((${min_disk_size} - 1 - 33)) count=33 + else + if [[ ! -e "${dev}" ]]; then + dd if=/dev/zero of="${dev}" bs=${block_size} count=1 \ + seek=$((${min_disk_size} - 1)) + fi + fi +} + diff --git a/build_library/dev_image_util.sh b/build_library/dev_image_util.sh index d80a0993ea..1e9f1c1e5f 100755 --- a/build_library/dev_image_util.sh +++ b/build_library/dev_image_util.sh @@ -16,73 +16,73 @@ install_dev_packages() { trap "unmount_image ; delete_prompt" EXIT - mount_image "${BUILD_DIR}/${image_name}" "${ROOT_FS_DIR}" \ - "${STATEFUL_FS_DIR}" "${ESP_FS_DIR}" + mount_image "${BUILD_DIR}/${image_name}" "${root_fs_dir}" \ + "${stateful_fs_dir}" "${esp_fs_dir}" # Determine the root dir for developer packages. - local root_dev_dir="${ROOT_FS_DIR}/usr/local" + local root_dev_dir="${root_fs_dir}/usr/local" # Install dev-specific init scripts into / from chromeos-dev-init. - emerge_to_image --root="${ROOT_FS_DIR}" chromeos-dev-init + emerge_to_image --root="${root_fs_dir}" chromeos-dev-init # Install developer packages described in chromeos-dev. emerge_to_image --root="${root_dev_dir}" chromeos-dev # Copy over the libc debug info so that gdb # works with threads and also for a better debugging experience. - sudo mkdir -p "${ROOT_FS_DIR}/usr/local/usr/lib/debug" + sudo mkdir -p "${root_fs_dir}/usr/local/usr/lib/debug" pbzip2 -dc --ignore-trailing-garbage=1 "${LIBC_PATH}" | \ - sudo tar xpf - -C "${ROOT_FS_DIR}/usr/local/usr/lib/debug" \ + sudo tar xpf - -C "${root_fs_dir}/usr/local/usr/lib/debug" \ ./usr/lib/debug/usr/${CHOST} --strip-components=6 # Since gdb only looks in /usr/lib/debug, symlink the /usr/local # path so that it is found automatically. - sudo ln -s /usr/local/usr/lib/debug "${ROOT_FS_DIR}/usr/lib/debug" + sudo ln -s /usr/local/usr/lib/debug "${root_fs_dir}/usr/lib/debug" # Install the bare necessary files so that the "emerge" command works - sudo cp -a ${root_dev_dir}/share/portage ${ROOT_FS_DIR}/usr/share + sudo cp -a ${root_dev_dir}/share/portage ${root_fs_dir}/usr/share sudo sed -i s,/usr/bin/wget,wget, \ - ${ROOT_FS_DIR}/usr/share/portage/config/make.globals + ${root_fs_dir}/usr/share/portage/config/make.globals - sudo mkdir -p ${ROOT_FS_DIR}/etc/make.profile + sudo mkdir -p ${root_fs_dir}/etc/make.profile # Re-run ldconfig to fix /etc/ldconfig.so.cache. - sudo /sbin/ldconfig -r "${ROOT_FS_DIR}" + sudo /sbin/ldconfig -r "${root_fs_dir}" # Mark the image as a developer image (input to chromeos_startup). # TODO(arkaitzr): Remove this file when applications no longer rely on it # (crosbug.com/16648). The preferred way of determining developer mode status # is via crossystem cros_debug?1 (checks boot args for "cros_debug"). - sudo mkdir -p "${ROOT_FS_DIR}/root" - sudo touch "${ROOT_FS_DIR}/root/.dev_mode" + sudo mkdir -p "${root_fs_dir}/root" + sudo touch "${root_fs_dir}/root/.dev_mode" # Additional changes to developer image. # Leave core files for developers to inspect. - sudo touch "${ROOT_FS_DIR}/root/.leave_core" + sudo touch "${root_fs_dir}/root/.leave_core" # This hack is only needed for devs who have old versions of glibc, which # filtered out ldd when cross-compiling. TODO(davidjames): Remove this hack # once everybody has upgraded to a new version of glibc. - if [[ ! -x "${ROOT_FS_DIR}/usr/bin/ldd" ]]; then - sudo cp -a "$(which ldd)" "${ROOT_FS_DIR}/usr/bin" + if [[ ! -x "${root_fs_dir}/usr/bin/ldd" ]]; then + sudo cp -a "$(which ldd)" "${root_fs_dir}/usr/bin" fi # If vim is installed, then a vi symlink would probably help. - if [[ -x "${ROOT_FS_DIR}/usr/local/bin/vim" ]]; then - sudo ln -sf vim "${ROOT_FS_DIR}/usr/local/bin/vi" + if [[ -x "${root_fs_dir}/usr/local/bin/vim" ]]; then + sudo ln -sf vim "${root_fs_dir}/usr/local/bin/vi" fi # If pygtk is installed in stateful-dev, then install a path. if [[ -d \ - "${ROOT_FS_DIR}/usr/local/lib/python2.6/site-packages/gtk-2.0" ]]; then + "${root_fs_dir}/usr/local/lib/python2.6/site-packages/gtk-2.0" ]]; then sudo bash -c "\ echo gtk-2.0 > \ - ${ROOT_FS_DIR}/usr/local/lib/python2.6/site-packages/pygtk.pth" + ${root_fs_dir}/usr/local/lib/python2.6/site-packages/pygtk.pth" fi # File searches /usr/share by default, so add a wrapper script so it # can find the right path in /usr/local. - local path="${ROOT_FS_DIR}/usr/local/bin/file" + local path="${root_fs_dir}/usr/local/bin/file" if [[ -x ${path} ]]; then sudo mv "${path}" "${path}.bin" sudo_clobber "${path}" <"$unpack" <
> "$unpack" + + cat >>"${unpack}" <
&2 + exit 1 +fi +set -x +HEADER + + $GPT show "${image}" | sed -e 's/^/# /' >>"${unpack}" + cp "${unpack}" "${pack}" + + $GPT show -q "${image}" | + while read start size part x; do + local file="part_${part}" + local target="\"\${TARGET}\"" + local dd_args="bs=512 count=${size}" + echo "dd if=${target} of=${file} ${dd_args} skip=${start}" >>"${unpack}" + echo "dd if=${file} of=${target} ${dd_args} seek=${start} conv=notrunc" \ + >>"${pack}" + done + + chmod +x "${unpack}" "${pack}" +} + +write_partition_script() { + local image_type=$1 + local partition_script_path=$2 + get_disk_layout_path + + sudo mkdir -p "$(dirname "${partition_script_path}")" + + sudo "${BUILD_LIBRARY_DIR}/cgpt.py" "write" \ + "${image_type}" "${DISK_LAYOUT_PATH}" "${partition_script_path}" +} + +run_partition_script() { + local outdev=$1 + local root_fs_img=$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 + + sudo mount -o loop "${root_fs_img}" "${root_fs_dir}" + . "${root_fs_dir}/${PARTITION_SCRIPT_PATH}" + write_partition_table "${outdev}" "${pmbr_img}" + sudo umount "${root_fs_dir}" +} + +get_fs_block_size() { + get_disk_layout_path + + echo $(${CGPT_PY} readfsblocksize ${DISK_LAYOUT_PATH}) +} + +get_block_size() { + get_disk_layout_path + + echo $(${CGPT_PY} readblocksize ${DISK_LAYOUT_PATH}) +} + +get_partition_size() { + local image_type=$1 + local part_id=$2 + get_disk_layout_path + + echo $(${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 + + echo $(${CGPT_PY} readfssize ${image_type} ${DISK_LAYOUT_PATH} ${part_id}) +} + +get_label() { + local image_type=$1 + local part_id=$2 + get_disk_layout_path + + echo $(${CGPT_PY} readlabel ${image_type} ${DISK_LAYOUT_PATH} ${part_id}) +} + +get_disk_layout_type() { + DISK_LAYOUT_TYPE="base" + if should_build_image ${CHROMEOS_FACTORY_INSTALL_SHIM_NAME}; then + DISK_LAYOUT_TYPE="factory_install" + fi +} + +emit_gpt_scripts() { + local image="$1" + local dir="$2" + + local pack="${dir}/pack_partitions.sh" + local unpack="${dir}/unpack_partitions.sh" + + cat >"${unpack}" <
&2 + exit 1 +fi +set -x +HEADER + + $GPT show "${image}" | sed -e 's/^/# /' >>"${unpack}" + cp "${unpack}" "${pack}" + + $GPT show -q "${image}" | + while read start size part x; do + local file="part_${part}" + local target="\"\$TARGET\"" + local dd_args="bs=512 count=${size}" + echo "dd if=${target} of=${file} ${dd_args} skip=${start}" >>"${unpack}" + echo "dd if=${file} of=${target} ${dd_args} seek=${start} conv=notrunc" \ + >>"${pack}" + done + + chmod +x "${unpack}" "${pack}" +} + +build_gpt() { + local outdev="$1" + local rootfs_img="$2" + local stateful_img="$3" + local esp_img="$4" + + get_disk_layout_type + run_partition_script "${outdev}" "${rootfs_img}" + + local sudo= + if [ ! -w "$outdev" ] ; then + # use sudo when writing to a block device. + sudo=sudo + fi + + # Now populate the partitions. + info "Copying stateful partition..." + $sudo dd if="$stateful_img" of="$outdev" conv=notrunc bs=512 \ + seek=$(partoffset ${outdev} 1) + + info "Copying rootfs..." + $sudo dd if="$rootfs_img" of="$outdev" conv=notrunc bs=512 \ + seek=$(partoffset ${outdev} 3) + + info "Copying EFI system partition..." + $sudo dd if="$esp_img" of="$outdev" conv=notrunc bs=512 \ + seek=$(partoffset ${outdev} 12) + + # 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" +} diff --git a/build_library/legacy_disk_layout.json b/build_library/legacy_disk_layout.json new file mode 100644 index 0000000000..766b0de64b --- /dev/null +++ b/build_library/legacy_disk_layout.json @@ -0,0 +1,152 @@ +{ + "metadata":{ + "block_size": 512, + "fs_block_size": 4096 + }, + "layouts":{ + "base":[ + { + "num": 11, + "label":"RWFW", + "type":"firmware", + "blocks":"16384" + }, + { + "num": 6, + "label":"KERN-C", + "type":"kernel", + "blocks":"1" + }, + { + "num": 7, + "label":"ROOT-C", + "type":"rootfs", + "blocks":"1" + }, + { + "num": 9, + "type":"reserved", + "label":"reserved", + "blocks":"1" + }, + { + "num": 10, + "type":"reserved", + "label":"reserved", + "blocks":"1" + }, + { + "type":"blank", + "blocks":"4028" + }, + { + "num": 2, + "label":"KERN-A", + "type":"kernel", + "blocks":"32768" + }, + { + "num": 4, + "label":"KERN-B", + "type":"kernel", + "blocks":"32768" + }, + { + "num": 8, + "label":"OEM", + "type":"data", + "blocks":"32768" + }, + { + "type":"blank", + "blocks":"131072" + }, + { + "num": 12, + "label":"EFI-SYSTEM", + "type":"efi", + "blocks":"32768" + }, + { + "num": 1, + "label":"STATE", + "type":"data", + "blocks":"2097152", + "features":["expand"] + }, + { + "num": 5, + "label":"ROOT-B", + "type":"rootfs", + "blocks":"1" + }, + { + "num": 3, + "label":"ROOT-A", + "type":"rootfs", + "blocks":"1757184", + "fs_blocks":"217600" + } + ], + "factory_install": [ + { + "num": 1, + "label":"STATE", + "type":"data", + "blocks":"286720" + }, + { + "num": 3, + "label":"ROOT-A", + "type":"rootfs", + "blocks":"860160", + "fs_blocks":"102400" + }, + { + "num": 12, + "label":"EFI-SYSTEM", + "type":"efi", + "blocks":"65536" + } + ], + "vm": [ + { + "num": 1, + "label":"STATE", + "type":"data", + "blocks":"6291456" + }, + { + "num": 3, + "label":"ROOT-A", + "type":"rootfs", + "blocks":"2097152", + "fs_blocks":"217600" + }, + { + "num": 5, + "label":"ROOT-B", + "type":"rootfs", + "blocks":"2097152", + "fs_blocks":"217600" + } + ], + "recovery": [ + { + "num": 1, + "label":"STATE", + "type":"data", + "blocks":"4096" + } + ], + "pgo": [ + { + "num": 3, + "label":"ROOT-A", + "type":"rootfs", + "blocks":"2662400", + "fs_blocks":"320000" + } + ] + } +} diff --git a/build_library/test_image_util.sh b/build_library/test_image_util.sh index 112114c30a..a83588a83f 100755 --- a/build_library/test_image_util.sh +++ b/build_library/test_image_util.sh @@ -7,77 +7,13 @@ # functions and initialization shared between build_image and # mod_image_for_test.sh. # -# TODO(jrbarnette): The two halves of this file aren't particularly -# related; they're together merely to consolidate the shared code in -# one file. Arguably, they should be broken up. - - -# ---- -# The initialization and functions below are shared between -# build_image and mod_image_for_test.sh. The code is not used -# by the mod_image_for_test function. - -EMERGE_BOARD_CMD="$GCLIENT_ROOT/chromite/bin/parallel_emerge" -EMERGE_BOARD_CMD="$EMERGE_BOARD_CMD --board=$BOARD" - -if [ $FLAGS_jobs -ne -1 ]; then - EMERGE_JOBS="--jobs=$FLAGS_jobs" -fi - -export INSTALL_MASK="${DEFAULT_INSTALL_MASK}" - - -# 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..." - $COMMON_PV_CAT "$src" >"$dst" || die "Cannot copy $1 to $2" - else - mv "${src}" "${dst}" || die "Cannot move $1 to $2" - fi -} - -# Basic command to emerge binary packages into the target image. -# Arguments to this command are passed as addition options/arguments -# to the basic emerge command. -emerge_to_image() { - sudo -E ${EMERGE_BOARD_CMD} --root-deps=rdeps --usepkgonly -v \ - "$@" ${EMERGE_JOBS} -} - -# Returns 0 if any of the images was requested to be built, 1 otherwise. -# $@ The name(s) of the images to check. -should_build_image() { - # Fast pass back if we should build all incremental images. - local images="$@" - local image_name - local image_to_build - - for image_name in ${images}; do - for image_to_build in ${IMAGES_TO_BUILD}; do - [ ${image_to_build} = ${image_name} ] && return 0 - done - done - - return 1 -} - -# ---- -# From here down, the main exported function is -# 'mod_image_for_test'. The remainder of the code is not used -# outside this file. # Emerges chromeos-test onto the image. emerge_chromeos_test() { # Determine the root dir for test packages. - local root_dev_dir="${ROOT_FS_DIR}/usr/local" + local root_dev_dir="${root_fs_dir}/usr/local" - emerge_to_image --root="${ROOT_FS_DIR}" chromeos-test-init + emerge_to_image --root="${root_fs_dir}" chromeos-test-init emerge_to_image --root="${root_dev_dir}" chromeos-test } @@ -104,7 +40,7 @@ mod_image_for_test () { trap unmount_image EXIT mount_image "${BUILD_DIR}/${image_name}" \ - "${ROOT_FS_DIR}" "${STATEFUL_FS_DIR}" + "${root_fs_dir}" "${stateful_fs_dir}" emerge_chromeos_test @@ -115,8 +51,8 @@ mod_image_for_test () { local mod_test_script="${SCRIPTS_DIR}/mod_for_test_scripts/test_setup.sh" # Run test setup script to modify the image - sudo -E GCLIENT_ROOT="${GCLIENT_ROOT}" ROOT_FS_DIR="${ROOT_FS_DIR}" \ - STATEFUL_DIR="${STATEFUL_FS_DIR}" ARCH="${ARCH}" BACKDOOR="${BACKDOOR}" \ + sudo -E GCLIENT_ROOT="${GCLIENT_ROOT}" ROOT_FS_DIR="${root_fs_dir}" \ + STATEFUL_DIR="${stateful_fs_dir}" ARCH="${ARCH}" BACKDOOR="${BACKDOOR}" \ BOARD_ROOT="${BOARD_ROOT}" \ "${mod_test_script}" @@ -125,9 +61,9 @@ mod_image_for_test () { if [ ${FLAGS_factory} -eq ${FLAGS_TRUE} ] || should_build_image "${CHROMEOS_FACTORY_TEST_IMAGE_NAME}"; then - emerge_to_image --root="${ROOT_FS_DIR}" factorytest-init + emerge_to_image --root="${root_fs_dir}" factorytest-init INSTALL_MASK="${FACTORY_TEST_INSTALL_MASK}" - emerge_to_image --root="${ROOT_FS_DIR}/usr/local" \ + emerge_to_image --root="${root_fs_dir}/usr/local" \ chromeos-base/autotest chromeos-base/autotest-all \ chromeos-base/chromeos-factory prepare_hwid_for_factory "${BUILD_DIR}" @@ -135,14 +71,14 @@ mod_image_for_test () { local mod_factory_script mod_factory_script="${SCRIPTS_DIR}/mod_for_factory_scripts/factory_setup.sh" # Run factory setup script to modify the image - sudo -E GCLIENT_ROOT="${GCLIENT_ROOT}" ROOT_FS_DIR="${ROOT_FS_DIR}" \ + sudo -E GCLIENT_ROOT="${GCLIENT_ROOT}" ROOT_FS_DIR="${root_fs_dir}" \ BOARD="${BOARD}" "${mod_factory_script}" fi # Re-run ldconfig to fix /etc/ldconfig.so.cache. - sudo ldconfig -r "${ROOT_FS_DIR}" + sudo ldconfig -r "${root_fs_dir}" - unmount_image + cleanup_mounts trap - EXIT # Now make it bootable with the flags from build_image. diff --git a/common.sh b/common.sh index 81ff811bab..daa916e76a 100644 --- a/common.sh +++ b/common.sh @@ -225,6 +225,7 @@ GCLIENT_ROOT=$(readlink -f "$GCLIENT_ROOT") SRC_ROOT="$GCLIENT_ROOT/src" SRC_INTERNAL="$GCLIENT_ROOT/src-internal" SCRIPTS_DIR="$SRC_ROOT/scripts" +BUILD_LIBRARY_DIR=${SCRIPTS_DIR}/build_library # Load developer's custom settings. Default location is in scripts dir, # since that's available both inside and outside the chroot. By convention, diff --git a/image_to_vm.sh b/image_to_vm.sh index 69baea065b..5c27c87708 100755 --- a/image_to_vm.sh +++ b/image_to_vm.sh @@ -10,6 +10,8 @@ # Helper scripts should be run from the same location as this script. SCRIPT_ROOT=$(dirname "$(readlink -f "$0")") . "${SCRIPT_ROOT}/common.sh" || exit 1 +. "${SCRIPT_ROOT}/build_library/disk_layout_util.sh" || exit 1 +. "${SCRIPT_ROOT}/build_library/build_common.sh" || exit 1 # Need to be inside the chroot to load chromeos-common.sh assert_inside_chroot @@ -33,30 +35,34 @@ DEFINE_string format "qemu" \ "Output format, either qemu, vmware or virtualbox" DEFINE_string from "" \ "Directory containing rootfs.image and mbr.image" -DEFINE_boolean full "${FLAGS_FALSE}" "Build full image with all partitions." DEFINE_boolean make_vmx ${FLAGS_TRUE} \ "Create a vmx file for use with vmplayer (vmware only)." DEFINE_integer mem "${DEFAULT_MEM}" \ "Memory size for the vm config in MBs (vmware only)." -DEFINE_integer rootfs_partition_size 1024 \ - "rootfs parition size in MBs." DEFINE_string state_image "" \ "Stateful partition image (defaults to creating new statful partition)" -DEFINE_integer statefulfs_size 2048 \ - "Stateful partition size in MBs." DEFINE_boolean test_image "${FLAGS_FALSE}" \ "Copies normal image to ${CHROMEOS_TEST_IMAGE_NAME}, modifies it for test." DEFINE_string to "" \ "Destination folder for VM output file(s)" DEFINE_string vbox_disk "${DEFAULT_VBOX_DISK}" \ "Filename for the output disk (virtualbox only)." -DEFINE_integer vdisk_size 3072 \ - "virtual disk size in MBs." DEFINE_string vmdk "${DEFAULT_VMDK}" \ "Filename for the vmware disk image (vmware only)." DEFINE_string vmx "${DEFAULT_VMX}" \ "Filename for the vmware config (vmware only)." +# The following arguments are ignored. +# They are here as part of a transition for CL #29931 beacuse the buildbots +# specify these arguments. +DEFINE_integer vdisk_size 3072 \ + "virtual disk size in MBs." +DEFINE_boolean full "${FLAGS_FALSE}" "Build full image with all partitions." +DEFINE_integer rootfs_partition_size 1024 \ + "rootfs parition size in MBs." +DEFINE_integer statefulfs_size 2048 \ + "Stateful partition size in MBs." + # Parse command line FLAGS "$@" || exit 1 eval set -- "${FLAGS_ARGV}" @@ -68,15 +74,7 @@ if [ -z "${FLAGS_board}" ] ; then die_notrace "--board is required." fi -if [ "${FLAGS_full}" -eq "${FLAGS_TRUE}" ] && \ - [[ ${FLAGS_vdisk_size} < ${MIN_VDISK_SIZE_FULL} || \ - ${FLAGS_statefulfs_size} < ${MIN_STATEFUL_FS_SIZE_FULL} ]]; then - warn "Disk is too small for full, using minimum: vdisk size equal to \ -${MIN_VDISK_SIZE_FULL} and statefulfs size equal to \ -${MIN_STATEFUL_FS_SIZE_FULL}." - FLAGS_vdisk_size=${MIN_VDISK_SIZE_FULL} - FLAGS_statefulfs_size=${MIN_STATEFUL_FS_SIZE_FULL} -fi +BOARD="$FLAGS_board" IMAGES_DIR="${DEFAULT_BUILD_ROOT}/images/${FLAGS_board}" # Default to the most recent image @@ -125,20 +123,17 @@ TEMP_KERN="${TEMP_DIR}"/part_2 if [ -n "${FLAGS_state_image}" ]; then TEMP_STATE="${FLAGS_state_image}" else - # If we have a stateful fs size specified create a new state partition - # of the specified size. - if [ "${FLAGS_statefulfs_size}" -ne -1 ]; then - STATEFUL_SIZE_BYTES=$((1024 * 1024 * ${FLAGS_statefulfs_size})) - original_image_size=$(stat -c%s "${TEMP_STATE}") - if [ "${original_image_size}" -gt "${STATEFUL_SIZE_BYTES}" ]; then - die "Cannot resize stateful image to smaller than original. Exiting." - fi - - echo "Resizing stateful partition to ${FLAGS_statefulfs_size}MB" - # Extend the original file size to the new size. - sudo e2fsck -pf "${TEMP_STATE}" - sudo resize2fs "${TEMP_STATE}" ${FLAGS_statefulfs_size}M + STATEFUL_SIZE_BYTES=$(get_filesystem_size vm 1) + STATEFUL_SIZE_MEGABYTES=$(( STATEFUL_SIZE_BYTES / 1024 / 1024 )) + original_image_size=$(stat -c%s "${TEMP_STATE}") + if [ "${original_image_size}" -gt "${STATEFUL_SIZE_BYTES}" ]; then + die "Cannot resize stateful image to smaller than original. Exiting." fi + + echo "Resizing stateful partition to ${STATEFUL_SIZE_MEGABYTES}MB" + # Extend the original file size to the new size. + sudo e2fsck -pf "${TEMP_STATE}" + sudo resize2fs "${TEMP_STATE}" ${STATEFUL_SIZE_MEGABYTES}M fi TEMP_PMBR="${TEMP_DIR}"/pmbr dd if="${SRC_IMAGE}" of="${TEMP_PMBR}" bs=512 count=1 @@ -164,26 +159,22 @@ sudo sed -i -e 's/sdb3/sda3/g' "${TEMP_MNT}/boot/syslinux/usb.A.cfg" trap - INT TERM EXIT cleanup -# TOOD(adlr): pick a size that will for sure accomodate the partitions. -dd if=/dev/zero of="${TEMP_IMG}" bs=1 count=1 \ - seek=$((${FLAGS_vdisk_size} * 1024 * 1024 - 1)) +# Set up a new partition table +PARTITION_SCRIPT_PATH=$( tempfile ) +write_partition_script "vm" "${PARTITION_SCRIPT_PATH}" +. "${PARTITION_SCRIPT_PATH}" +write_partition_table "${TEMP_IMG}" "${TEMP_PMBR}" +rm "${PARTITION_SCRIPT_PATH}" -GPT_FULL="false" -[ "${FLAGS_full}" -eq "${FLAGS_TRUE}" ] && GPT_FULL="true" - -# Set up the partition table -install_gpt "${TEMP_IMG}" "$(numsectors $TEMP_ROOTFS)" \ - "$(numsectors $TEMP_STATE)" "${TEMP_PMBR}" "$(numsectors $TEMP_ESP)" \ - "${GPT_FULL}" ${FLAGS_rootfs_partition_size} # Copy into the partition parts of the file dd if="${TEMP_ROOTFS}" of="${TEMP_IMG}" conv=notrunc bs=512 \ - seek="${START_ROOTFS_A}" + seek=$(partoffset ${TEMP_IMG} 3) dd if="${TEMP_STATE}" of="${TEMP_IMG}" conv=notrunc bs=512 \ - seek="${START_STATEFUL}" + seek=$(partoffset ${TEMP_IMG} 1) dd if="${TEMP_KERN}" of="${TEMP_IMG}" conv=notrunc bs=512 \ - seek="${START_KERN_A}" + seek=$(partoffset ${TEMP_IMG} 2) dd if="${TEMP_ESP}" of="${TEMP_IMG}" conv=notrunc bs=512 \ - seek="${START_ESP}" + seek=$(partoffset ${TEMP_IMG} 12) # Make the built-image bootable and ensure that the legacy default usb boot # uses /dev/sda instead of /dev/sdb3. diff --git a/mod_image_for_recovery.sh b/mod_image_for_recovery.sh index 1094562fdb..6be4613a94 100755 --- a/mod_image_for_recovery.sh +++ b/mod_image_for_recovery.sh @@ -11,6 +11,7 @@ SCRIPT_ROOT=$(dirname $(readlink -f "$0")) . "${SCRIPT_ROOT}/build_library/build_common.sh" || exit 1 +. "${SCRIPT_ROOT}/build_library/disk_layout_util.sh" || exit 1 # Default recovery kernel name. RECOVERY_KERNEL_NAME=recovery_vmlinuz.image @@ -126,8 +127,8 @@ BOAT create_recovery_kernel_image() { local sysroot="$FACTORY_ROOT" local vmlinuz="$sysroot/boot/vmlinuz" - local root_offset=$(partoffset "$FLAGS_image" 3) - local root_size=$(partsize "$FLAGS_image" 3) + local root_offset=$(partoffset "${RECOVERY_IMAGE}" 3) + local root_size=$(partsize "${RECOVERY_IMAGE}" 3) local enable_rootfs_verification_flag=--noenable_rootfs_verification if grep -q enable_rootfs_verification "${IMAGE_DIR}/boot.desc"; then @@ -144,12 +145,12 @@ create_recovery_kernel_image() { # recovery image generation. (Alternately, it means an image can be created, # modified for recovery, then passed to a signer which can then sign both # partitions appropriately without needing any external dependencies.) - local kern_offset=$(partoffset "$FLAGS_image" 2) - local kern_size=$(partsize "$FLAGS_image" 2) + local kern_offset=$(partoffset "${RECOVERY_IMAGE}" 2) + local kern_size=$(partsize "${RECOVERY_IMAGE}" 2) local kern_tmp=$(mktemp) local kern_hash= - dd if="$FLAGS_image" bs=512 count=$kern_size \ + dd if="${RECOVERY_IMAGE}" bs=512 count=$kern_size \ skip=$kern_offset of="$kern_tmp" 1>&2 # We're going to use the real signing block. if [ $FLAGS_sync_keys -eq $FLAGS_TRUE ]; then @@ -170,24 +171,23 @@ create_recovery_kernel_image() { --keys_dir="${FLAGS_keys_dir}" \ ${enable_rootfs_verification_flag} \ --nouse_dev_keys 1>&2 || failboat "build_kernel_image" - sudo mount | sed 's/^/16651 /' - sudo losetup -a | sed 's/^/16651 /' + #sudo mount | sed 's/^/16651 /' + #sudo losetup -a | sed 's/^/16651 /' trap - RETURN # Update the EFI System Partition configuration so that the kern_hash check # passes. - local efi_offset=$(partoffset "$FLAGS_image" 12) - local efi_size=$(partsize "$FLAGS_image" 12) + local block_size=$(get_block_size) + + local efi_offset=$(partoffset "${RECOVERY_IMAGE}" 12) + local efi_size=$(partsize "${RECOVERY_IMAGE}" 12) + local efi_offset_bytes=$(( $efi_offset * $block_size )) + local efi_size_bytes=$(( $efi_size * $block_size )) - local efi_dev=$(sudo losetup --show -f \ - -o $((efi_offset * 512)) \ - --sizelimit $((efi_size * 512)) \ - "$FLAGS_image") local efi_dir=$(mktemp -d) - trap "sudo losetup -d $efi_dev && rmdir \"$efi_dir\"" EXIT - echo "16651 mount: $efi_dev -> $efi_dir" - sudo mount "$efi_dev" "$efi_dir" - sudo mount | sed 's/^/16651 /' + sudo mount -o loop,offset=${efi_offset_bytes},sizelimit=${efi_size_bytes} \ + "${RECOVERY_IMAGE}" "${efi_dir}" + sudo sed -i -e "s/cros_legacy/cros_legacy kern_b_hash=$kern_hash/g" \ "$efi_dir/syslinux/usb.A.cfg" || true # This will leave the hash in the kernel for all boots, but that should be @@ -195,8 +195,6 @@ create_recovery_kernel_image() { sudo sed -i -e "s/cros_efi/cros_efi kern_b_hash=$kern_hash/g" \ "$efi_dir/efi/boot/grub.cfg" || true safe_umount "$efi_dir" - sudo losetup -a | sed 's/^/16651 /' - sudo losetup -d "$efi_dev" rmdir "$efi_dir" trap - EXIT } @@ -269,48 +267,59 @@ update_partition_table() { local resized_sectors=$3 # number of sectors in resized stateful partition local temp_img=$4 - local kern_a_offset=$(partoffset ${src_img} 2) + local kern_a_dst_offset=$(partoffset ${temp_img} 2) + local kern_a_src_offset=$(partoffset ${src_img} 2) local kern_a_count=$(partsize ${src_img} 2) - local kern_b_offset=$(partoffset ${src_img} 4) + + local kern_b_dst_offset=$(partoffset ${temp_img} 4) + local kern_b_src_offset=$(partoffset ${src_img} 4) local kern_b_count=$(partsize ${src_img} 4) - local rootfs_offset=$(partoffset ${src_img} 3) + + local rootfs_dst_offset=$(partoffset ${temp_img} 3) + local rootfs_src_offset=$(partoffset ${src_img} 3) local rootfs_count=$(partsize ${src_img} 3) - local oem_offset=$(partoffset ${src_img} 8) + + local oem_dst_offset=$(partoffset ${temp_img} 8) + local oem_src_offset=$(partoffset ${src_img} 8) local oem_count=$(partsize ${src_img} 8) - local esp_offset=$(partoffset ${src_img} 12) + + local esp_dst_offset=$(partoffset ${temp_img} 12) + local esp_src_offset=$(partoffset ${src_img} 12) local esp_count=$(partsize ${src_img} 12) + local state_dst_offset=$(partoffset ${temp_img} 1) + local temp_pmbr=$(mktemp "/tmp/pmbr.XXXXXX") dd if="${src_img}" of="${temp_pmbr}" bs=512 count=1 &>/dev/null trap "rm -rf \"${temp_pmbr}\"" EXIT # Set up a new partition table - install_gpt "${temp_img}" "${rootfs_count}" "${resized_sectors}" \ - "${temp_pmbr}" "${esp_count}" false \ - $(((rootfs_count * 512)/(1024 * 1024))) + PARTITION_SCRIPT_PATH=$( tempfile ) + write_partition_script "recovery" "${PARTITION_SCRIPT_PATH}" + . "${PARTITION_SCRIPT_PATH}" + write_partition_table "${temp_img}" "${temp_pmbr}" + echo "${PARTITION_SCRIPT_PATH}" + #rm "${PARTITION_SCRIPT_PATH}" # Copy into the partition parts of the file dd if="${src_img}" of="${temp_img}" conv=notrunc bs=512 \ - seek="${START_ROOTFS_A}" skip=${rootfs_offset} count=${rootfs_count} + seek=${kern_a_dst_offset} skip=${kern_a_src_offset} count=${rootfs_count} dd if="${temp_state}" of="${temp_img}" conv=notrunc bs=512 \ - seek="${START_STATEFUL}" + seek=${state_dst_offset} # Copy the full kernel (i.e. with vboot sections) dd if="${src_img}" of="${temp_img}" conv=notrunc bs=512 \ - seek="${START_KERN_A}" skip=${kern_a_offset} count=${kern_a_count} + seek=${kern_a_dst_offset} skip=${kern_a_src_offset} count=${kern_a_count} dd if="${src_img}" of="${temp_img}" conv=notrunc bs=512 \ - seek="${START_KERN_B}" skip=${kern_b_offset} count=${kern_b_count} + seek=${kern_b_dst_offset} skip=${kern_b_src_offset} count=${kern_b_count} dd if="${src_img}" of="${temp_img}" conv=notrunc bs=512 \ - seek="${START_OEM}" skip=${oem_offset} count=${oem_count} + seek=${oem_dst_offset} skip=${oem_src_offset} count=${oem_count} dd if="${src_img}" of="${temp_img}" conv=notrunc bs=512 \ - seek="${START_ESP}" skip=${esp_offset} count=${esp_count} + seek=${esp_dst_offset} skip=${esp_src_offset} count=${esp_count} } maybe_resize_stateful() { # If we're not minimizing, then just copy and go. if [ $FLAGS_minimize_image -eq $FLAGS_FALSE ]; then - if [ "$FLAGS_image" != "$RECOVERY_IMAGE" ]; then - cp "$FLAGS_image" "$RECOVERY_IMAGE" - fi return 0 fi @@ -337,7 +346,7 @@ maybe_resize_stateful() { # Create a recovery image of the right size # TODO(wad) Make the developer script case create a custom GPT with # just the kernel image and stateful. - update_partition_table "$FLAGS_image" "$small_stateful" 4096 \ + update_partition_table "${RECOVERY_IMAGE}" "$small_stateful" 4096 \ "$RECOVERY_IMAGE" 1>&2 return $err } @@ -395,6 +404,10 @@ if [ $FLAGS_modify_in_place -eq $FLAGS_TRUE ]; then die_notrace "Cannot use --modify_in_place and --minimize_image together." fi RECOVERY_IMAGE="${FLAGS_image}" +else + if [[ ${FLAGS_modify_in_place} -eq ${FLAGS_FALSE} ]]; then + cp "${FLAGS_image}" "${RECOVERY_IMAGE}" + fi fi echo "Creating recovery image from ${FLAGS_image}" @@ -422,10 +435,6 @@ if [ $FLAGS_kernel_image_only -eq $FLAGS_TRUE ]; then exit 0 fi -if [ $FLAGS_modify_in_place -eq $FLAGS_FALSE ]; then - rm "$RECOVERY_IMAGE" || true # Start fresh :) -fi - trap cleanup EXIT maybe_resize_stateful # Also copies the image if needed.