blob: bfce8f0afcf926128fc12fab494bb2d8981415d7 [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.
. /usr/share/misc/chromeos-common.sh
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
clobber-log -- \
"Self-repair incoherent stateful partition: $*. History: ${UNDO_MOUNTS}"
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 "failed to mount $*"
}
# Assert that the argument is a directory.
# On failure, clobbers the stateful partition.
check_directory()
{
local path="$1"
if [ ! -d "${path}" ]; then
cleanup_mounts "${d} is not a directory"
fi
}
# Mount debugfs as bootstat depends on /sys/kernel/debug
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/pts /dev/shm
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 --system
# CROS_DEBUG equals one if we've booted in developer mode or we've
# booted a developer image.
crossystem "cros_debug?1"
CROS_DEBUG=$((! $?))
# 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 (ex, factory installer images. Note recovery
# image also uses initramfs but it never reach here). When using initrd+tftpboot
# (some old netboot factory installer), 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/ubi${PARTITION_NUM_STATE}_0"
else
DIRTY_EXPIRE_CENTISECS=$(sysctl -n vm.dirty_expire_centisecs)
COMMIT_INTERVAL=$(( DIRTY_EXPIRE_CENTISECS / 100 ))
STATE_DEV=${ROOTDEV_TYPE}${PARTITION_NUM_STATE}
STATE_FLAGS="${STATE_FLAGS},commit=${COMMIT_INTERVAL}"
fi
# Mount stateful partition from STATE_DEV.
if ! 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
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/ubi${PARTITION_NUM_OEM}_0"
else
OEM_DEV=${ROOTDEV_TYPE}${PARTITION_NUM_OEM}
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
# This file indicates a blocked developer mode transition attempt has occurred.
BLOCKED_DEV_MODE_FILE="/mnt/stateful_partition/.blocked_dev_mode"
# Check whether the device is allowed to boot in dev mode. If a debug
# build is already installed on the system, ignore block_devmode. It
# is pointless in this case, 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 ] && crossystem "devsw_boot?1" "debug_build?0"; then
# Checks ordered by run time: First try reading VPD through sysfs.
if [ -f /sys/firmware/vpd/rw/block_devmode ]; then
BLOCK_DEVMODE=1
# Second try crossystem.
elif crossystem "block_devmode?1"; then
BLOCK_DEVMODE=1
# Third re-read VPD directly from SPI flash (slow!) but only for systems that
# don't have VPD in sysfs and only when NVRAM indicates that it has been
# cleared.
elif [ ! -d /sys/firmware/vpd/rw ] &&
crossystem "nvram_cleared?1" &&
[ "$(vpd -i RW_VPD -g block_devmode)" = "1" ]; then
BLOCK_DEVMODE=1
fi
if [ -n "${BLOCK_DEVMODE}" ]; then
# Put a flag file into place that will trigger a stateful partition wipe
# after reboot in verified mode.
touch ${BLOCKED_DEV_MODE_FILE}
chromeos-boot-alert block_devmode
fi
fi
# 'firmware-boot-update' is provided by chromeos-firmware for legacy systems.
# On most new boards, it should be simply an empty file.
firmware-boot-update
# 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
# 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 message localized for the factory personnel.
WIPE_MESSAGE_FILE=/mnt/stateful_partition/wipe_message.txt
if [ -O "${WIPE_MESSAGE_FILE}" ]; then
chromeos-boot-alert wipe "${WIPE_MESSAGE_FILE}"
else
chromeos-boot-alert power_wash
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
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
echo "keepimg" > ${RESET_FILE}
clobber-log -- "Enter developer mode"
fi
elif [ -O ${DEV_MODE_FILE} -o -O ${BLOCKED_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
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 unencrypted/cache unencrypted/preserve; do
mkdir -p -m 0755 "/mnt/stateful_partition/${d}"
check_directory "/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 "var and home"
# For dev/test images, if .gatherme presents, copy files listed in .gatherme to
# /mnt/stateful_partition/unencrypted/prior_logs.
LAB_PRESERVE_LOGS="/mnt/stateful_partition/.gatherme"
PRIOR_LOG_DIR="/mnt/stateful_partition/unencrypted/prior_logs"
if [ ${CROS_DEBUG} -eq 1 -a -f "${LAB_PRESERVE_LOGS}" ]; then
for log_path in $(sed -e '/^#/ d' -e '/^$/ d' "${LAB_PRESERVE_LOGS}"); do
if [ -d "${log_path}" ]; then
cp -a -r --parents "${log_path}" "${PRIOR_LOG_DIR}" || true
elif [ -f "${log_path}" ]; then
cp -a "${log_path}" "${PRIOR_LOG_DIR}" || true
fi
done
rm -rf /var/*
rm -rf /home/chronos/*
rm "${LAB_PRESERVE_LOGS}"
fi
# /run is now tmpfs used for runtime data. Make sure /var/run and /var/lock
# are sym links to /run and /run/lock respectively for backwards compatibility.
rm -rf /var/run /var/lock || \
cleanup_mounts "failed to delete /var/run and /var/lock"
ln -s /run /var/run
ln -s /run/lock /var/lock
# Make sure required /var subdirectories exist.
mkdir -p -m 0755 /var/cache /var/db /var/empty /var/log/metrics \
/var/tmp
# Before operating on them, verify that all stateful partition paths are
# directories (as opposed to say, symlinks).
for d in /var/cache /var/db /var/empty /var/log /var/log/metrics \
/var/tmp /home/chronos /home/root; do
check_directory "${d}"
done
# /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
# /var/cache, /var/db, and /var/empty may already exist with wrong permissions.
# Force the correct ones.
chmod 0755 /var/cache /var/db /var/empty
# "--make-shared" to let ARC container access mount points under /media.
mount --make-shared -n -t tmpfs -o nodev,noexec,nosuid media /media
# Mount stateful partition for dev packages.
if [ ${CROS_DEBUG} -eq 1 ]; then
# Set up the logging dir that ASAN compiled programs will write to. We want
# any privileged account to be able to write here so unittests need not worry
# about settings things up ahead of time. See crbug.com/453579 for details.
mkdir -p /var/log/asan
chmod 1777 /var/log/asan
# 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