blob: 22e762e527af6fca17c541b0c48f28e3f9683bb2 [file] [log] [blame]
#!/bin/sh
# 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.
UNDO_MOUNTS=
cleanup_mounts()
{
# On failure unmount all saved mount points and repair stateful
for mount_point in ${UNDO_MOUNTS}; do
umount -n ${mount_point}
done
# Leave /mnt/stateful_partition mounted for clobber-state to handle.
chromeos-boot-alert self_repair /dev/tty1
clobber-log --repair "$STATE_DEV" "Self-repair incoherent stateful partition"
exec clobber-state "fast keepimg"
}
remember_mount()
{
UNDO_MOUNTS="$1 ${UNDO_MOUNTS}"
}
mount_or_fail()
{
local mount_point
# -c: Never canonicalize: it is a hazard to resolve symlinks.
# -n: Do not write to mtab: we don't use it.
if mount -c -n "$@" ; then
# Last parameter contains the mount point
shift $(( $# - 1 ))
# Push it on the undo stack if we fail later
remember_mount "$1"
return
fi
cleanup_mounts
}
# The kernel and /sbin/init mount /proc, /sys and /dev before
# we get here.
mount -n -t tmpfs -o nodev,noexec,nosuid tmp /tmp
# Mount debugfs as crossystem requires chromeos_arm driver interface.
mount -n -t debugfs -o nodev,noexec,nosuid,mode=0750,uid=0,gid=debugfs-access \
debugfs /sys/kernel/debug
# bootstat writes timings to both tmpfs and debugfs.
bootstat pre-startup
# Some startup functions are split into a separate library which may be
# different for different targets (e.g., regular Chrome OS vs. embedded).
. /usr/share/cros/startup_utils.sh
# Factory related functions
. /usr/share/cros/factory_utils.sh
mkdir -p /dev/shm /dev/pts
mount -n -t tmpfs -o nodev,noexec,nosuid shmfs /dev/shm
mount -n -t devpts -o noexec,nosuid,gid=5,mode=0620 devpts /dev/pts
# Initialize kernel sysctl settings early so that they take effect for boot
# processes.
sysctl -q -p /etc/sysctl.conf
# CROS_DEBUG equals one if we've booted in developer mode or we've
# booted a developer image.
crossystem "cros_debug?1"
CROS_DEBUG=$((! $?))
# Check whether the device is allowed to boot in dev mode. If firmware write
# protect is off or a debug build is already installed on the system, ignore
# block_devmode. It is pointless in these cases, as the device is already in a
# state where the local user has full control.
#
# The up-front CROS_DEBUG check avoids forking a crossystem process in verified
# mode, thus keeping the check as lightweight as possible for normal boot.
if [ $CROS_DEBUG -eq 1 ]; then
if crossystem "block_devmode?1" "wpsw_boot?1" "debug_build?0" \
"devsw_boot?1"; then
chromeos-boot-alert block_devmode /dev/tty1
fi
fi
# Prepare to mount stateful partition
ROOT_DEV=$(rootdev -s)
ROOTDEV_RET_CODE=$?
# Example root dev types we need to handle: /dev/sda2 -> /dev/sda,
# /dev/mmcblk0p0 -> /dev/mmcblk0p, /dev/ubi2_1 -> /dev/ubi
ROOTDEV_TYPE=$(echo $ROOT_DEV | sed 's/[0-9_]*$//')
ROOTDEV_NAME=${ROOTDEV_TYPE##/dev/}
ROOTDEV_REMOVABLE=$(cat "/sys/block/${ROOTDEV_NAME}/removable")
# Load the GPT helper functions and the image settings.
. "/usr/sbin/write_gpt.sh"
if [ "${ROOTDEV_REMOVABLE}" = "1" ]; then
load_partition_vars
else
load_base_vars
fi
# Check if we are booted on physical media. rootdev will fail if we are in
# an initramfs or tmpfs rootfs (used for x86 factory images). When using
# initrd+tftpboot (used for ARM factory images), ROOTDEV_TYPE will be
# /dev/ram.
if [ "$ROOTDEV_RET_CODE" = "0" -a "$ROOTDEV_TYPE" != "/dev/ram" ]; then
# Find our stateful partition. It's always partition 1.
# Unless we're on UBI, when it's ubi1_0.
STATE_FLAGS="nodev,noexec,nosuid"
if [ "${FORMAT_STATE}" = "ubi" ]; then
STATE_DEV="/dev/ubi1_0"
else
STATE_DEV=${ROOTDEV_TYPE}1
STATE_FLAGS="${STATE_FLAGS},commit=600"
fi
# For factory install shim, we never want to write to the SDCard.
# In some cases, we boot the factory install shim directly from removable
# media like an SDCard. In those cases we don't want to write to that
# removable media.
if [ -f /root/.factory_installer ]; then
mount -n -t tmpfs -o nodev,noexec,nosuid,mode=0755 tmp \
/mnt/stateful_partition
# Fetch our writeable lsb-release from the stateful
# partition if available.
TMP_STATEFUL="$(mktemp -d)"
FACTORY_LSB_REL="dev_image/etc/lsb-factory"
mkdir -p /mnt/stateful_partition/dev_image/etc
mount -n -t ${FS_FORMAT_STATE} -o ${STATE_FLAGS} \
"$STATE_DEV" "$TMP_STATEFUL"
if [ -f "${TMP_STATEFUL}/${FACTORY_LSB_REL}" ]; then
cp -a "${TMP_STATEFUL}/${FACTORY_LSB_REL}" \
/mnt/stateful_partition/${FACTORY_LSB_REL}
fi
umount "$TMP_STATEFUL"
rmdir "$TMP_STATEFUL"
# For all other cases, mount stateful partition from STATE_DEV.
elif ! mount -n -t ${FS_FORMAT_STATE} -o ${STATE_FLAGS} \
"$STATE_DEV" /mnt/stateful_partition; then
# Try to rebuild the stateful partition by clobber-state
# (for security concern, we don't use fast mode)
chromeos-boot-alert self_repair /dev/tty1
clobber-log --repair "$STATE_DEV" "Self-repair corrupted stateful partition"
exec clobber-state "keepimg"
fi
# Mount the OEM partition.
# mount_or_fail isn't used since this partition only has a filesystem
# on some boards.
OEM_FLAGS="ro,nodev,noexec,nosuid"
if [ "${FORMAT_OEM}" = "ubi" ]; then
OEM_DEV="/dev/ubi8_0"
else
OEM_DEV=${ROOTDEV_TYPE}8
OEM_FLAGS="${OEM_FLAGS},commit=600"
fi
mount -n -t ${FS_FORMAT_OEM} -o ${OEM_FLAGS} ${OEM_DEV} /usr/share/oem
fi
# Sanity check the date (crosbug.com/13200)
if [ $(date +%Y) -lt 1970 ]; then
date 010200001970.00
fi
# Now that stateful partition is mounted, we can check if we are in factory
# mode.
FACTORY_MODE=
if is_factory_mode; then
FACTORY_MODE=factory
fi
# Check if we need to perform firmware update: only if fwupdate_tries is
# non-zero or if not using normal (ex, developer or recovery) firmware.
FIRMWARE_UPDATE_SCRIPT=/usr/sbin/chromeos-firmwareupdate
if ! crossystem "fwupdate_tries?0" "mainfw_type?normal" &&
[ -x "$FIRMWARE_UPDATE_SCRIPT" ]; then
# This file will be collected by chromeos-setgoodkernel
FIRMWARE_UPDATE_LOGS=/mnt/stateful_partition/update_firmware.log
tries="$(crossystem fwupdate_tries || echo 0)"
# Special updating rules for legacy systems.
case "$(mosys platform name 2>/dev/null)" in
Alex | ZGB )
if [ "$(crossystem mainfw_type)" = developer ]; then
tries=0 # Prevents updates if we're in developer mode
chromeos-boot-alert dev_fwcheck /dev/tty1
fi
;;
esac
if [ $tries -gt 0 ]; then
crossystem fwupdate_tries=$((tries - 1))
# More messages on console for developer mode and dev builds.
[ $CROS_DEBUG -eq 1 ] &&
FIRMWARE_UPDATE_LOGS="/dev/tty1 $FIRMWARE_UPDATE_LOGS" || true
chromeos-boot-alert update_firmware /dev/tty1
(date && "$FIRMWARE_UPDATE_SCRIPT" --mode=startup 2>&1) |
tee -a $FIRMWARE_UPDATE_LOGS
# Sends "clear screen" terminal command to clear TTY frame buffer, to
# prevent user seeing the messages again on system shutdown.
[ $CROS_DEBUG -eq 1 ] && tput clear >/dev/tty1 || true
fi
fi
# File used to trigger a stateful reset. Contains arguments for
# the "clobber-state" call. This file may exist at boot time, as
# some use cases operate by creating this file with the necessary
# arguments and then rebooting.
RESET_FILE="/mnt/stateful_partition/factory_install_reset"
# This file is created by clobber-state after the transition
# to dev mode.
DEV_MODE_FILE="/mnt/stateful_partition/.developer_mode"
FIRMWARE_TYPE=$(crossystem mainfw_type)
# Check for whether we need a stateful wipe, and alert the user as
# necessary. We can wipe for several different reasons:
# + Wipe for the factory build process. This is signaled by
# the existence of ${RESET_FILE} prior to reboot; the arguments
# for clobber-state are set up before rebooting.
# + User requested "power wash". This is signaled in the same
# way as the factory reset, but with different arguments in
# ${RESET_FILE}.
# + Switch from verified mode to dev mode. We do this if we're in
# dev mode, and ${DEV_MODE_FILE} doesn't exist. clobber-state
# in this case will create the file, to prevent re-wipe.
# + Switch from dev mode to verified mode. We do this if we're in
# verified mode, and ${DEV_MODE_FILE} still exists. (This check
# isn't necessarily reliable.)
#
# Stateful wipe for dev mode switching is skipped if the build
# is a debug build or if we've booted in recovery mode (meaning
# from USB); this protects various development use cases, most
# especially booting Chromium OS on non-Chrome hardware.
#
if [ -O ${RESET_FILE} ]; then
# Wipe requested on previous boot. In the case of a factory wipe,
# there can be a special splash screen with a message localized for
# the factory personnel.
ALTERNATE_WIPE_SCREEN=/mnt/stateful_partition/wipe_splash.png
if [ -O "$ALTERNATE_WIPE_SCREEN" ]; then
chromeos-boot-alert wipe /dev/tty1 "$ALTERNATE_WIPE_SCREEN"
else
chromeos-boot-alert power_wash /dev/tty1
fi
elif [ -z "$FACTORY_MODE" -a "$FIRMWARE_TYPE" != "recovery" ]; then
if crossystem "devsw_boot?1" ; then
# We've booted in dev mode. For platforms using separated
# normal/developer firmware, we need to display an extra boot
# alert for the developer mode warning plus the 30-second delay.
# Note that we want this message and the delay regardless of
# whether we plan to wipe.
if [ "$FIRMWARE_TYPE" != "developer" ]; then
chromeos-boot-alert warn_dev /dev/tty1
fi
if [ ! -O ${DEV_MODE_FILE} ] && crossystem "debug_build?0"; then
# We're transitioning from verified boot to dev mode.
# TODO(wad,wfrichar) Have user provide sudo/vt2 password here.
chromeos-boot-alert enter_dev /dev/tty1
echo "keepimg" > ${RESET_FILE}
clobber-log -- "Enter developer mode"
fi
elif [ -O ${DEV_MODE_FILE} ] && crossystem "debug_build?0"; then
# We're transitioning from dev mode to verified boot.
# When coming back from developer mode, we don't need to
# clobber as aggressively. Fast will do the trick.
chromeos-boot-alert leave_dev /dev/tty1
echo "fast keepimg" > ${RESET_FILE}
clobber-log -- "Leave developer mode"
fi
fi
if [ -O ${RESET_FILE} ]; then
ARGS="$(cat ${RESET_FILE})"
exec clobber-state "$ARGS"
fi
# Check if we have an update to stateful pending.
STATEFUL_UPDATE="/mnt/stateful_partition/.update_available"
if [ $CROS_DEBUG -eq 1 -a -f "$STATEFUL_UPDATE" ] ; then
# To remain compatible with the prior update_stateful tarballs, expect
# the "var_new" unpack location, but move it into the new "var_overlay"
# target location.
VAR_TARGET="/mnt/stateful_partition/var"
VAR_NEW="${VAR_TARGET}_new"
VAR_OLD="${VAR_TARGET}_old"
VAR_TARGET="${VAR_TARGET}_overlay"
DEVELOPER_TARGET="/mnt/stateful_partition/dev_image"
DEVELOPER_NEW="${DEVELOPER_TARGET}_new"
DEVELOPER_OLD="${DEVELOPER_TARGET}_old"
STATEFUL_UPDATE_ARGS=$(cat "$STATEFUL_UPDATE")
# Only replace the developer and var_overlay directories if new replacements
# are available.
if [ -d "$DEVELOPER_NEW" -a -d "$VAR_NEW" ]; then
clobber-log -- "Updating from $DEVELOPER_NEW && $VAR_NEW."
rm -rf "$DEVELOPER_OLD" "$VAR_OLD"
mv "$VAR_TARGET" "$VAR_OLD" || true
mv "$DEVELOPER_TARGET" "$DEVELOPER_OLD" || true
mv "$VAR_NEW" "$VAR_TARGET"
mv "$DEVELOPER_NEW" "$DEVELOPER_TARGET"
else
clobber-log -- "Stateful update did not find $DEVELOPER_NEW && $VAR_NEW."
clobber-log -- "Keeping old development tools."
fi
# Check for clobber.
if [ "$STATEFUL_UPDATE_ARGS" = "clobber" ] ; then
PRESERVE_DIR="/mnt/stateful_partition/unencrypted/preserve"
# Find everything in stateful and delete it, except for protected paths, and
# non-empty directories. The non-empty directories contain protected content
# or they would already be empty from depth first traversal.
find "/mnt/stateful_partition" -depth -mindepth 1 \
-not -path "/mnt/stateful_partition/.labmachine" \
-not -path "${DEVELOPER_TARGET}/*" \
-not -path "${VAR_TARGET}/*" \
-not -path "${PRESERVE_DIR}/*" \
-not -type d -print0 | xargs --null -r rm -f
find "/mnt/stateful_partition" -depth -mindepth 1 \
-not -path "${DEVELOPER_TARGET}/*" \
-not -path "${VAR_TARGET}/*" \
-not -path "${PRESERVE_DIR}/*" \
-type d -print0 | xargs --null -r rmdir --ignore-fail-on-non-empty
# Let's really be done before coming back.
sync
fi
# Backgrounded to take off boot path.
rm -rf "$STATEFUL_UPDATE" "$DEVELOPER_OLD" "$VAR_OLD" &
fi
# Make sure unencrypted stateful partition has the needed common directories.
# Any non-common directories should be created in the device implementation of
# "mount_var_and_home_chronos".
for d in home home/chronos home/root home/user \
unencrypted/cache unencrypted/preserve; do
mkdir -p -m 0755 /mnt/stateful_partition/$d
done
# Mount /home. This mount inherits nodev,noexec,nosuid from
# /mnt/stateful_partition above.
mount_or_fail --bind /mnt/stateful_partition/home /home
remember_mount /var
remember_mount /home/chronos
mount_var_and_home_chronos ${FACTORY_MODE} || cleanup_mounts
# Make sure required /var subdirectories exist.
mkdir -p -m 0755 /var/cache /var/db /var/empty /var/lock /var/log/metrics \
/var/run /var/tmp
# /var/tmp must be world-writable and sticky
chmod 1777 /var/tmp
# /home/root must be group-writable and sticky
chmod 1771 /home/root
# Selected directories must belong to the chronos user.
chown chronos:chronos /home/chronos /var/log/metrics
# rsyslog needs to be able to create new logfiles, but not delete other logs
chgrp syslog /var/log
chmod 1775 /var/log
mount_or_fail -t tmpfs -o mode=0755,nodev,noexec,nosuid varrun /var/run
touch /var/run/.ramfs # TODO: Is this needed?
mount_or_fail -t tmpfs -o mode=1777,nodev,noexec,nosuid varlock /var/lock
touch /var/lock/.ramfs # TODO: Is this needed?
mount -n -t tmpfs -o nodev,noexec,nosuid media /media
# Mount stateful partition for dev packages.
if [ $CROS_DEBUG -eq 1 ]; then
# Capture a snapshot of "normal" mount state here, for auditability,
# before we start applying devmode-specific changes.
cat /proc/mounts > /var/log/mount_options.log
# Create dev_image directory in base images in developer mode.
if [ ! -d /mnt/stateful_partition/dev_image ]; then
mkdir -p -m 0755 /mnt/stateful_partition/dev_image
fi
# Mount and then remount to enable exec/suid.
mount_or_fail --bind /mnt/stateful_partition/dev_image /usr/local
mount -n -o remount,exec,suid /usr/local
# Set up /var elements needed by gmerge.
# TODO(keescook) Use dev/test package installs instead of piling more
# things here (crosbug.com/14091).
BASE=/mnt/stateful_partition/var_overlay
if [ -d ${BASE} ]; then
# Keep this list in sync with the var_overlay elements in the DIRLIST
# found in chromeos-install from chromeos-base/chromeos-installer.
DIRLIST="
db/pkg
lib/portage
"
for DIR in ${DIRLIST}; do
if [ ! -d ${BASE}/${DIR} ]; then
continue
fi
DEST=/var/${DIR}
if [ -e ${DEST} ]; then
continue
fi
PARENT=$(dirname ${DEST})
mkdir -p ${PARENT}
ln -sf ${BASE}/${DIR} ${DEST}
done
fi
fi
bootstat post-startup
# Always return success to avoid killing init
exit 0