diff --git a/chroot_upgrade.d/1_init b/chroot_upgrade.d/1_init new file mode 100644 index 0000000000..dad7d515d2 --- /dev/null +++ b/chroot_upgrade.d/1_init @@ -0,0 +1,6 @@ +# 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. + +info "You have just been upgraded to chroot version 1! Congratulations!" +exit 0 diff --git a/cros_upgrade_chroot b/cros_upgrade_chroot new file mode 100755 index 0000000000..b872cb28d6 --- /dev/null +++ b/cros_upgrade_chroot @@ -0,0 +1,141 @@ +#!/bin/bash + +# 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. + +# Load common CrOS utilities. Inside the chroot this file is installed in +# /usr/lib/crosutils. Outside the chroot we find it relative to the script's +# location. +. "$(dirname "$0")/common.sh" || ! echo "Unable to load common.sh" || exit 1 + +# Script must run inside the chroot. +assert_inside_chroot + +# May not be run as root. +assert_not_root_user + +DEFINE_string version "" \ + "Assume current chroot version is this." +DEFINE_boolean force_latest "${FLAGS_FALSE}" \ + "Assume latest version and recreate the version file" +DEFINE_boolean skipfirst "${FLAGS_FALSE}" \ + "Skip the first upgrade. This may be dangerous." + +FLAGS "$@" || exit 1 + +VERSION_FILE=/etc/cros_chroot_version +UPGRADE_D="$(dirname ${0})/chroot_upgrade.d" + +function update_version() { + sudo touch ${VERSION_FILE} + sudo chown ${USER} ${VERSION_FILE} + echo "${1}" > "${VERSION_FILE}" +} + +###################################################################### + +# Sanity checks: +if [ -n "${FLAGS_version}" ] && \ + ( [ "${FLAGS_skipfirst}" == "${FLAGS_TRUE}" ] || \ + [ "${FLAGS_force_latest}" == "${FLAGS_TRUE}" ] ); then + error "The option --version cannot be combined with either" + error "--skipfirst or --force_latest." + exit 1 +fi + +if [ "${FLAGS_skipfirst}" == "${FLAGS_TRUE}" ] && + [ "${FLAGS_force_latest}" == "${FLAGS_TRUE}" ]; then + error "--skipfirst and --force_latest cannot be combined." + exit 1 +fi + +# Latest version is the version of last upgrade.d file. +# Name format is ${number}_${short_description} +# Versions must be -n sorted, that is, the first continuous sequence +# of numbers is what counts. 12_ is before 111_, etc. +LATEST_VERSION=$( + ls "${UPGRADE_D}" | grep "^[0-9]*_" | \ + sort -n | tail -n 1 | cut -f1 -d'_' +) + +if [ "${FLAGS_force_latest}" == "${FLAGS_TRUE}" ]; then + update_version "${LATEST_VERSION}" + exit 0 +fi + +# If the file does not exist at all, chroot is old and does not have a version. +# default goes here +if ! [ -f "${VERSION_FILE}" ]; then + info "Chroot of unknown version, initializing to 0" + update_version 0 +fi + +CHROOT_VERSION=$(cat "${VERSION_FILE}") +# Check if version is a number. +if ! [ "${CHROOT_VERSION}" -ge "0" ] &> /dev/null; then + error "Your chroot version file ${VERSION_FILE} is bogus: ${CHROOT_VERSION}" + exit 1 +fi + +if [ "${FLAGS_skipfirst}" == "${FLAGS_TRUE}" ]; then + if [ "${CHROOT_VERSION}" -lt "${LATEST_VERSION}" ]; then + # if the new one is latest, this becomes noop + CHROOT_VERSION=$(expr ${CHROOT_VERSION} + 1) + update_version "${CHROOT_VERSION}" + else + error "Nothing to skip" + exit 1 + fi +fi + +if [ -n "${FLAGS_version}" ]; then + # Check if it's a number. + if ! [ "${FLAGS_version}" -ge "0" ] &> /dev/null; then + error "Trying to force invalid version: ${FLAGS_version}" + exit 1 + fi + + if [ "${FLAGS_version}" -gt "${LATEST_VERSION}" ]; then + error "Forcing nonexistant version: ${FLAGS_version}" + exit 1 + fi + + CHROOT_VERSION="${FLAGS_version}" +fi + + +if [ "${LATEST_VERSION}" -gt "${CHROOT_VERSION}" ]; then + info "Old chroot version (${CHROOT_VERSION}) found, running upgrade hooks" + + pushd "${UPGRADE_D}" 1> /dev/null + for n in $(seq "$(expr ${CHROOT_VERSION} + 1)" "${LATEST_VERSION}"); do + # Deprecation check; Deprecation can be done by removing old upgrade + # scripts and causing too old chroots to have to start over. + # Upgrades have to form a continuous sequence. + if ! [ -f ${n}_* ]; then + error "Fatal: Upgrade ${n} doesn't exist." + error "Your chroot is so old, that some updates have been deprecated!" + error "You need to re-create it!" + exit 1 + fi + + info "Rollup $(echo ${n}_*)" + + # Attempt the upgrade. + # NOTE: We source the upgrade scripts because: + # 1) We can impose set -something on them. + # 2) They can reuse local variables and functions (fe. from common.sh) + # Side effect is that the scripts have to be internally enclosed in + # a code block, otherwise simply running "exit" in any of them would + # terminate the master script, so we call it in a subshell. + if ! ( source ${n}_* ); then + error "Fatal: failed to upgrade ${n}!" + exit 1 + fi + # Each upgrade is atomic. If a middle upgrade fails, we won't retry + # all the ones that passed on a previous run. + update_version "${n}" + done + popd 1> /dev/null +fi