| #!/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 to convert the output of build_image.sh to a usb or SD image. |
| |
| SCRIPT_ROOT=$(dirname $(readlink -f "$0")) |
| . "${SCRIPT_ROOT}/common.sh" || exit 1 |
| |
| # Load functions and constants for chromeos-install |
| [ -f /usr/lib/installer/chromeos-common.sh ] && \ |
| INSTALLER_ROOT=/usr/lib/installer || \ |
| INSTALLER_ROOT=$(dirname "$(readlink -f "$0")") |
| |
| . "${INSTALLER_ROOT}/chromeos-common.sh" || exit 1 |
| |
| # In case chromeos-common.sh doesn't support MMC yet |
| declare -F list_mmc_disks >/dev/null || list_mmc_disks() { true; } |
| |
| # Flags |
| DEFINE_string board "${DEFAULT_BOARD}" \ |
| "board for which the image was built" |
| DEFINE_string from "" \ |
| "directory containing the image, or image full pathname (empty: latest found)" |
| DEFINE_string to "" \ |
| "write to a specific disk or image file (empty: auto-detect)" |
| DEFINE_string to_product "" \ |
| "find target device with product name matching a string (accepts wildcards)" |
| DEFINE_boolean yes ${FLAGS_FALSE} \ |
| "don't ask questions, just write to the target device specified by --to" \ |
| y |
| DEFINE_boolean force_copy ${FLAGS_FALSE} \ |
| "always rebuild test image" |
| DEFINE_boolean force_non_usb ${FLAGS_FALSE} \ |
| "force writing even if target device doesn't appear to be a USB/MMC disk" |
| DEFINE_boolean factory_install ${FLAGS_FALSE} \ |
| "Install the factory install shim" |
| DEFINE_boolean factory ${FLAGS_FALSE} \ |
| "Install a factory test image" |
| DEFINE_boolean copy_kernel ${FLAGS_FALSE} \ |
| "copy the kernel to the fourth partition" |
| DEFINE_boolean test_image "${FLAGS_FALSE}" \ |
| "Install a test image" |
| DEFINE_string image_name "" \ |
| "image base name (empty: auto-detect)" \ |
| i |
| DEFINE_boolean install ${FLAGS_FALSE} \ |
| "install to the USB/MMC device" |
| DEFINE_string arch "" \ |
| "architecture for which the image was built (derived from board if empty)" |
| |
| # Parse command line |
| FLAGS "$@" || exit 1 |
| eval set -- "${FLAGS_ARGV}" |
| |
| if [ $# -gt 0 ]; then |
| die_notrace "Arguments aren't currently supported in image_to_usb." |
| fi |
| |
| # Generates a descriptive string of a removable device. Includes the |
| # manufacturer (if non-empty), product and a human-readable size. |
| get_disk_string() { |
| local disk="${1##*/}" |
| local manufacturer_string=$(get_disk_info $disk manufacturer) |
| local product_string=$(get_disk_info $disk product) |
| local disk_size=$(sudo fdisk -l /dev/$disk 2>/dev/null | grep Disk | |
| head -n 1 | cut -d' ' -f3-4 | sed 's/,//g') |
| # I've seen one case where manufacturer only contains spaces, hence the test. |
| if [ -n "${manufacturer_string// }" ]; then |
| echo -n "${manufacturer_string} " |
| fi |
| echo "${product_string}, ${disk_size}" |
| } |
| |
| # Prompt for user confirmation. Default is no, which will gracefully terminate |
| # the script. |
| are_you_sure() { |
| local sure |
| read -p "Are you sure (y/N)? " sure |
| if [ "${sure}" != "y" ]; then |
| echo "Ok, better safe than sorry." |
| exit |
| fi |
| } |
| |
| |
| # Prohibit mutually exclusive factory/install flags. |
| if [ ${FLAGS_factory} -eq ${FLAGS_TRUE} -a \ |
| ${FLAGS_factory_install} -eq ${FLAGS_TRUE} ] ; then |
| die_notrace "Factory test image is incompatible with factory install shim" |
| fi |
| |
| # Allow --from /foo/file.bin |
| if [ -f "${FLAGS_from}" ]; then |
| pathname=$(dirname "${FLAGS_from}") |
| filename=$(basename "${FLAGS_from}") |
| FLAGS_image_name="${filename}" |
| FLAGS_from="${pathname}" |
| fi |
| |
| # Require autotest for manucaturing image. |
| if [ ${FLAGS_factory} -eq ${FLAGS_TRUE} ] ; then |
| echo "Factory image requires --test_image, setting." |
| FLAGS_test_image=${FLAGS_TRUE} |
| fi |
| |
| # Require test for for factory install shim. |
| if [ ${FLAGS_factory_install} -eq ${FLAGS_TRUE} ] ; then |
| echo "Factory install shim requires --test_image, setting." |
| FLAGS_test_image=${FLAGS_TRUE} |
| fi |
| |
| |
| # Die on any errors. |
| switch_to_strict_mode |
| |
| # No board, no default and no image set then we can't find the image |
| if [ -z ${FLAGS_from} ] && [ -z ${FLAGS_board} ] ; then |
| setup_board_warning |
| exit 1 |
| fi |
| |
| # No board set during install |
| if [ -z "${FLAGS_board}" ] && [ ${FLAGS_install} -eq ${FLAGS_TRUE} ]; then |
| setup_board_warning |
| exit 1 |
| fi |
| |
| # Install can only be done from inside the chroot. |
| if [ ${FLAGS_install} -eq ${FLAGS_TRUE} ] && [ ${INSIDE_CHROOT} -ne 1 ]; then |
| die_notrace "--install can only be used inside the chroot" |
| fi |
| |
| # We have a board name but no image set. Use image at default location |
| if [ -z "${FLAGS_from}" ]; then |
| FLAGS_from="$($SCRIPT_ROOT/get_latest_image.sh --board=${FLAGS_board})" |
| fi |
| |
| if [ ! -d "${FLAGS_from}" ] ; then |
| die_notrace "Cannot find image directory ${FLAGS_from}" |
| fi |
| |
| # TODO(garnold) This code reinstates the previous default value for --to, which |
| # some users relied upon to trigger target device auto-detection. It should be |
| # removed once we're sure that all users have adapted to simply not specifying |
| # --to. The instructions emitted by build_image were changed accordingly. |
| if [ "${FLAGS_to}" == "/dev/sdX" ]; then |
| warn "the use of --to=/dev/sdX is deprecated, just omit --to instead" |
| FLAGS_to="" |
| fi |
| |
| # No target provided, attempt autodetection. |
| if [ -z "${FLAGS_to}" ]; then |
| if [ ${FLAGS_yes} -eq ${FLAGS_TRUE} ]; then |
| die_notrace "For your own safety, --yes can only be used with --to" |
| fi |
| |
| if [ -z "${FLAGS_to_product}" ]; then |
| echo "No target device specified, autodetecting..." |
| else |
| echo "Looking for target devices matching '${FLAGS_to_product}'..." |
| fi |
| |
| # Obtain list of USB and MMC device names. |
| disk_list=( $(list_usb_disks) $(list_mmc_disks) ) |
| |
| # Build list of descriptive strings for detected devices. |
| unset disk_string_list |
| for disk in "${disk_list[@]}"; do |
| # If --to_product was used, match against provided string. |
| # Note: we intentionally use [[ ... != ... ]] to allow pattern matching on |
| # the product string. |
| if [ -n "${FLAGS_to_product}" ] && |
| [[ "$(get_disk_info ${disk} product)" != ${FLAGS_to_product} ]]; then |
| continue |
| fi |
| |
| disk_string=$(get_disk_string /dev/${disk}) |
| disk_string_list=( "${disk_string_list[@]}" |
| "/dev/${disk}: ${disk_string}" ) |
| done |
| |
| # If no (matching) devices found, quit. |
| if (( ! ${#disk_string_list[*]} )); then |
| if [ -z "${FLAGS_to_product}" ]; then |
| die_notrace "No USB/MMC devices could be detected" |
| else |
| die_notrace "No matching USB/MMC devices could be detected" |
| fi |
| fi |
| |
| # Prompt for selection, or autoselect if only one device was found. |
| if (( ${#disk_string_list[*]} > 1 )); then |
| PS3="Select a target device: " |
| select disk_string in "${disk_string_list[@]}"; do |
| if [ -z "${disk_string}" ]; then |
| die_notrace "Invalid selection" |
| fi |
| break |
| done |
| else |
| disk_string="${disk_string_list}" |
| echo "Found ${disk_string}" |
| fi |
| |
| FLAGS_to="${disk_string%%:*}" |
| elif [ -n "${FLAGS_to_product}" ]; then |
| die_notrace "Cannot specify both --to and --to_product" |
| fi |
| |
| # Guess ARCH if it's unset |
| if [ "${FLAGS_arch}" = "" ]; then |
| if echo "${FLAGS_board}" | grep -qs "x86"; then |
| FLAGS_arch=INTEL |
| else |
| FLAGS_arch=ARM |
| fi |
| fi |
| |
| # Convert args to paths. Need eval to un-quote the string so that shell |
| # chars like ~ are processed; just doing FOO=`readlink -f ${FOO}` won't work. |
| FLAGS_from=`eval readlink -f ${FLAGS_from}` |
| FLAGS_to=`eval readlink -f ${FLAGS_to}` |
| |
| # Check whether target device is USB/MMC, and obtain a string descriptor for it. |
| unset disk_string |
| if [ -b "${FLAGS_to}" -o -c "${FLAGS_to}" ]; then |
| if list_usb_disks | grep -q '^'${FLAGS_to##*/}'$' || |
| list_mmc_disks | grep -q '^'${FLAGS_to##*/}'$'; then |
| disk_string=$(get_disk_string ${FLAGS_to}) |
| elif [ ${FLAGS_force_non_usb} -ne ${FLAGS_TRUE} ]; then |
| # Safeguard against writing to a real non-USB disk or non-SD disk |
| die_notrace "${FLAGS_to} does not appear to be a USB/MMC disk," \ |
| "use --force_non_usb to override" |
| fi |
| fi |
| |
| STATEFUL_DIR="${FLAGS_from}/stateful_partition" |
| mkdir -p "${STATEFUL_DIR}" |
| |
| # Figure out which image to use. |
| if [ ${FLAGS_factory} -eq ${FLAGS_TRUE} ]; then |
| SRC_IMAGE="${FLAGS_from}/${CHROMEOS_FACTORY_TEST_IMAGE_NAME}" |
| elif [ ${FLAGS_test_image} -eq ${FLAGS_TRUE} ]; then |
| SRC_IMAGE="${FLAGS_from}/${CHROMEOS_TEST_IMAGE_NAME}" |
| else |
| # Auto-detect and select an image name if none provided. |
| if [ -z "${FLAGS_image_name}" ]; then |
| echo "No image name specified, autodetecting..." |
| |
| # Resolve the default image full path (see though symlinks), make sure |
| # it's present. |
| default_image_path=$(readlink -f "${FLAGS_from}/${CHROMEOS_IMAGE_NAME}") |
| if [ ! -f "${default_image_path}" ]; then |
| default_image_path="MISSING" |
| fi |
| |
| # The list of candidate image names. |
| image_candidate_list=( "${CHROMEOS_DEVELOPER_IMAGE_NAME}" |
| "${CHROMEOS_RECOVERY_IMAGE_NAME}" |
| "${CHROMEOS_TEST_IMAGE_NAME}" |
| "${CHROMEOS_FACTORY_TEST_IMAGE_NAME}" |
| "${CHROMEOS_FACTORY_INSTALL_SHIM_NAME}" ) |
| |
| # Obtain list of available images that can be used. |
| unset image_list |
| is_got_default=0 |
| for image_candidate in "${image_candidate_list[@]}"; do |
| image_candidate_path="${FLAGS_from}/${image_candidate}" |
| if [ -f "${image_candidate_path}" ]; then |
| if [ "${image_candidate_path}" == "${default_image_path}" ]; then |
| # This is the default image, list it first. |
| image_list=( "${image_candidate}" "${image_list[@]}" ) |
| is_got_default=1 |
| else |
| image_list=( "${image_list[@]}" "${image_candidate}" ) |
| fi |
| fi |
| done |
| |
| # Figure out what to do with the resulting list of images. |
| declare -i num_images=${#image_list[*]} |
| if (( num_images == 0 )); then |
| die_notrace "No candidate images could be detected" |
| elif (( num_images == 1 )) && [ ${is_got_default} == 1 ]; then |
| # Found a single image that is the default image, just select it. |
| image="${image_list[0]}" |
| echo "Found default image ${image}" |
| else |
| # Select one from a list of available images; default to the first. |
| PS3="Select an image [1]: " |
| choose image "${image_list[0]}" "ERROR" "${image_list[@]}" |
| if [ "${image}" == "ERROR" ]; then |
| die_notrace "Invalid selection" |
| fi |
| fi |
| |
| FLAGS_image_name="${image}" |
| fi |
| |
| # Use the selected image. |
| SRC_IMAGE="${FLAGS_from}/${FLAGS_image_name}" |
| fi |
| |
| # Make sure that the selected image exists. |
| if [ ! -f "${SRC_IMAGE}" ]; then |
| die_notrace "Image not found: ${SRC_IMAGE}" |
| fi |
| |
| # Let's do it. |
| if [ -b "${FLAGS_to}" -o -c "${FLAGS_to}" ]; then |
| # Output to a block device (i.e., a real USB key / SD card), so need sudo dd |
| if [ ${FLAGS_install} -ne ${FLAGS_TRUE} ]; then |
| echo "Copying image ${SRC_IMAGE} to device ${FLAGS_to}..." |
| else |
| echo "Installing image ${SRC_IMAGE} to device ${FLAGS_to}..." |
| fi |
| |
| # Warn if it looks like they supplied a partition as the destination. |
| if echo "${FLAGS_to}" | grep -q '[0-9]$'; then |
| drive=$(echo "${FLAGS_to}" | sed -re 's/[0-9]+$//') |
| if [ -b "${drive}" ]; then |
| warn "${FLAGS_to} looks like a partition; did you mean ${drive}?" |
| fi |
| fi |
| |
| # Make sure this is really what the user wants, before nuking the device. |
| if [ ${FLAGS_yes} -ne ${FLAGS_TRUE} ]; then |
| warning_str="this will erase all data on ${FLAGS_to}" |
| if [ -n "${disk_string}" ]; then |
| warning_str="${warning_str}: ${disk_string}" |
| else |
| warning_str="${warning_str}, which does not appear to be a USB/MMC disk!" |
| fi |
| warn "${warning_str}" |
| are_you_sure |
| fi |
| |
| mount_list=$(mount | grep ^"${FLAGS_to}" | awk '{print $1}') |
| if [ -n "${mount_list}" ]; then |
| echo "Attempting to unmount any mounts on the target device..." |
| for i in ${mount_list}; do |
| if safe_umount "$i" 2>&1 >/dev/null | grep "not found"; then |
| die_notrace "$i needs to be unmounted outside the chroot" |
| fi |
| done |
| sleep 3 |
| fi |
| |
| if [ ${FLAGS_install} -ne ${FLAGS_TRUE} ]; then |
| sudo $(pv_cat_cmd) "${SRC_IMAGE}" | |
| sudo dd of="${FLAGS_to}" bs=4M oflag=sync status=noxfer |
| sync |
| else |
| "/build/${FLAGS_board}/usr/sbin/chromeos-install" \ |
| --yes \ |
| --skip_src_removable \ |
| --skip_dst_removable \ |
| --arch="${FLAGS_arch}" \ |
| --payload_image="${SRC_IMAGE}" \ |
| --dst="${FLAGS_to}" |
| fi |
| elif [[ "${FLAGS_to}" == /dev/* ]]; then |
| # Did the user attempt to write to a non-existent block device? |
| die_notrace "Target device ${FLAGS_to} does not exist" |
| else |
| # Output to a file, so just make a copy. |
| if [ "${SRC_IMAGE}" != "${FLAGS_to}" ]; then |
| echo "Copying image ${SRC_IMAGE} to file ${FLAGS_to}..." |
| $(pv_cat_cmd) "${SRC_IMAGE}" >"${FLAGS_to}" |
| fi |
| |
| info "To copy onto a USB/MMC drive /dev/sdX, use: " |
| info " sudo dd if=${FLAGS_to} of=/dev/sdX bs=4M oflag=sync" |
| fi |
| |
| echo "Done." |