blob: 3439c4c29e257a019657a1dfb1884cef3caf32b2 [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
if mount "$@" ; 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 mounts /proc, /sys and /dev during initialization, prior
# to starting init.
mount -n -t tmpfs -o nodev,noexec,nosuid tmp /tmp
# Mount debugfs as crossystem requires chromeos_arm driver interface.
mount -n -t debugfs debugfs /sys/kernel/debug
# bootstat writes timings to both tmpfs and debugfs.
bootstat pre-startup
IS_FACTORY_MODE=
if [ -f /root/.factory_test -o -f /root/.factory_installer ]; then
IS_FACTORY_MODE=1
fi
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
# Prepare to mount stateful partition
ROOT_DEV=$(rootdev -s)
ROOTDEV_RET_CODE=$?
ROOTDEV_TYPE=${ROOT_DEV%[0-9]*}
# 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.
STATE_DEV=${ROOTDEV_TYPE}1
# 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 ext4 -o nodev,noexec,nosuid,commit=600 \
"$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 ext4 -o nodev,noexec,nosuid,commit=600 \
"$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_DEV=${ROOTDEV_TYPE}8
mount -n -t ext4 -o nodev,noexec,nosuid,commit=600,ro \
"$OEM_DEV" /usr/share/oem
fi
# Sanity check the date (crosbug.com/13200)
if [ $(date +%Y) -lt 1970 ]; then
date 010200001970.00
fi
# DEV_MODE is used to determine if we're booted in developer mode,
# by checking only the developer and recovery switch states.
# Values:
# 0=recovery/non-dev mode,
# 1=dev (SSD) mode,
# 2=we've rebooted to handle a request for stateful wipe because of
# factory or power wash.
# Of these the check below assigns 0 or 1. The remaining value(s) will
# be assigned later.
crossystem "devsw_boot?1" && ! crossystem "mainfw_act?recovery"
DEV_MODE=$((! $?))
# 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 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" = "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" = 1 ] && tput clear >/dev/tty1 || true
fi
fi
# If we've switched from verified mode to developer mode, clobber the state.
# The reverse is not possible to detect reliably, but we try.
# In addition, normal and unsupported are treated are equivalent otherwise
# booting the first time on a legacy device would clobber state for no reason.
# (NOTE, this will not be triggered in recovery mode.)
# File used to trigger a stateful reset
RESET_FILE="/mnt/stateful_partition/factory_install_reset"
# File defined by clobber-state
DEV_MODE_FILE="/mnt/stateful_partition/.developer_mode"
# If in factory mode or a wipe process is already scheduled,
# set DEV_MODE to 2 so that the right thing happens later.
if [ -n "$IS_FACTORY_MODE" -o -O "$RESET_FILE" ]; then
DEV_MODE=2
fi
if [ ${DEV_MODE} -eq 1 ]; then
FW_TYPE=$(crossystem mainfw_type)
# For platforms using separated normal/developer firmware, we need to display
# an extra page for developer mode warning.
if [ "${FW_TYPE}" != "developer" ]; then
chromeos-boot-alert warn_dev /dev/tty1
fi
fi
# Transition to devmode
if [ ${DEV_MODE} -eq 1 -a ! -O ${DEV_MODE_FILE} ]; then
# Delay the first developer mode boot by 5 minutes unless the images is a
# developer image. We check the dev image here to avoid a stat(2) on normal
# boots
# TODO(wad) just have the dev_mode build stamp the stateful.
if [ ! -f /root/.dev_mode ]; then
# TODO(wad) Throw up a splash.
chromeos-boot-alert enter_dev /dev/tty1
# TODO(wad,wfrichar) Have user input sudo/vt2 password here.
echo "keepimg" > ${RESET_FILE}
clobber-log -- "Enter developer mode"
fi
# Transition from devmode
elif [ ${DEV_MODE} -eq 0 -a -O ${DEV_MODE_FILE} ]; then
# When coming back from developer mode, we don't need to clobber
# as aggressively. Fast will do the trick.
if [ ! -f /root/.dev_mode ]; then
chromeos-boot-alert leave_dev /dev/tty1
echo "fast keepimg" > ${RESET_FILE}
clobber-log -- "Leave developer mode"
fi
fi
# Check if we have an update to stateful pending.
# Note: THIS MUST BE DONE BEFORE CLOBBER STATE.
STATEFUL_UPDATE="/mnt/stateful_partition/.update_available"
if [ -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")
# Remove old state in case this process got interrupted by a reset, this is
# usually a no-op.
rm -rf "$DEVELOPER_OLD" "$VAR_OLD"
# The moves can fail if they were previously interrupted mid-op.
mv "$DEVELOPER_TARGET" "$DEVELOPER_OLD" || true
mv "$VAR_TARGET" "$VAR_OLD" || true
mv "$DEVELOPER_NEW" "$DEVELOPER_TARGET" || true
mv "$VAR_NEW" "$VAR_TARGET" || true
# Check for clobber.
if [ "$STATEFUL_UPDATE_ARGS" = "clobber" ]; then
# Removes all dirs except the new dev_image and var_overlay.
find /mnt/stateful_partition -mindepth 1 -maxdepth 1 |
grep -v -e dev_image\$ -e var_overlay\$ |
xargs -d "\n" rm -rf
# Let's really be done before coming back.
sync
else
# Backgrounded to take off boot path.
rm -rf "$STATEFUL_UPDATE" "$DEVELOPER_OLD" "$VAR_OLD" &
fi
fi
# Check if the stateful partition has requested self-destruction. If DEV_MODE is
# 0 or 1, we've already displayed the correct localized image earlier. The only
# case we've to handle is when DEV_MODE is 2.
# If it's after factory finalization, then the wipe_splash.png should be used
# to display the message. This image will contain the text to display as per
# the factory's locale. Otherwise, we display the powerwash message.
if [ -O "$RESET_FILE" ]; then
if [ ${DEV_MODE} -eq 2 ]; then
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
fi
ARGS="$(cat ${RESET_FILE})"
exec clobber-state "$ARGS"
fi
# Make sure unencrypted stateful partition has the needed directories.
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 -n --bind /mnt/stateful_partition/home /home
# Create, possibly migrate from, the unencrypted stateful partition,
# and bind mount the /var and /home/chronos mounts from the encrypted
# filesystem /mnt/stateful_partition/encrypted, all managed by the
# "mount-encrypted" helper.
# Since /var is managed by mount-encrypted, it should not be created
# in the unencrypted stateful partition. Its mount point in the root
# filesystem exists already from the rootfs image.
# Since /home is still mounted from the unencrypted stateful partition,
# having /home/chronos already doesn't matter. It will be created by
# mount-encrypted if it is missing.
# These mounts inherit nodev,noexec,nosuid from the encrypted filesystem
# /mnt/stateful_partition/encrypted.
remember_mount /var
remember_mount /home/chronos
mount-encrypted ${IS_FACTORY_MODE:+factory} >/tmp/mount-encrypted.log 2>&1
if [ $? -ne 0 ]; then
cleanup_mounts
fi
# Make sure required /var subdirectories exist.
mkdir -p -m 0755 /var/cache /var/db /var/empty /var/lib/ureadahead \
/var/lock /var/log/metrics /var/log/window_manager \
/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 /var/log/window_manager
# 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 -n -t tmpfs -o mode=0755,nodev,noexec,nosuid varrun /var/run
touch /var/run/.ramfs # TODO: Is this needed?
mount_or_fail -n -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
# TODO(arkaitzr@chromium.org) - Only use CROS_DEBUG when cros_debug is
# correctly set in a test VM after updating itself to itself (that is, it does
# not fail cros_au_test_harness).
if [ "$CROS_DEBUG" = "1" -o -f /root/.dev_mode ]; 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 -n --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
# Enable crash reporting for all future exec'd processes, even those that have
# gone through setuid.
echo 2 > /proc/sys/fs/suid_dumpable
# Check for extra steps needed for OOBE. These steps are expensive
# so they only happen once; when OOBE is complete the OOBE_COMPLETE
# file gets created to block this code from running again.
#
# We also skip the extra steps for factory images (check for
# ROOTDEV).
OOBE_COMPLETE=/home/chronos/.oobe_completed
if [ "$ROOTDEV_RET_CODE" = "0" -a ! -e $OOBE_COMPLETE ]; then
# This script dumps VPD RO/RW data into /var/log/vpd_2.0.txt
# so that OOBE process can read the default locale setting
# from VPD.
if [ -x /usr/sbin/dump_vpd_log ]; then
/usr/sbin/dump_vpd_log
fi
# TODO(yjlou): We are developing a more generic hook mechanism
# (i.e. pre-OOBE-oem.sh), and will move this block
# to new hook point.
#
# The activate_date script is installed in partner's
# board-specific private overlay. Hence the vpd utility
# dependency is described in private overlay, instead of
# chromeos-init.
#
# If the activate date script is executable (installed from
# the private overlay), run it.
if [ -x /usr/sbin/activate_date ]; then
# Since this script will invoke flash read/write (around 20 secs),
# run it in background.
(/usr/sbin/activate_date ; /usr/sbin/dump_vpd_log --force) &
fi
fi
#
# Note that ureadahead depends on some of the operations above.
# Notably, the command requires /var to be mounted, because the pack
# file is located in /var/lib/ureadahead, and ureadahead tracing
# requires /sys/kernel/debug be mounted.
#
ureadahead &
bootstat post-startup
# Always return success to avoid killing init
exit 0