blob: d2c50207124e9dc40ca77895060deb3837169807 [file] [log] [blame]
#!/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/share/misc/chromeos-common.sh ] && \
INSTALLER_ROOT=/usr/share/misc || \
INSTALLER_ROOT=$(dirname "$(readlink -f "$0")")
. "${INSTALLER_ROOT}/chromeos-common.sh" || exit 1
# 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)"
warn "You are using a deprecated script!"
warn "Please use 'cros flash' in the future. See 'cros flash -h' for the usage."
warn "More information is available at:"
warn "http://www.chromium.org/chromium-os/build/cros-flash"
# 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
get_disk_info() {
# look for a "given" file somewhere in the path upwards from the device
local dev=$1
local info=$2
local dev_path="/sys/block/${dev}/device"
while [ -d "${dev_path}" ] && [ "${dev_path}" != "/sys" ]; do
if [ -f "${dev_path}/${info}" ]; then
cat "${dev_path}/${info}"
return
fi
dev_path=$(readlink -f "${dev_path}/..")
done
echo '[Unknown]'
}
# 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=$(grep ^"${FLAGS_to}" /proc/mounts | 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" ; then
die_notrace "$i could not be unmounted; aborting."
fi
done
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}" \
--skip_postinstall
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
command_completed
echo "Done."