blob: a286a62888a5a86c4f379d09cf91d3663237ca7f [file] [log] [blame]
# 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/
# UNDO_MOUNTS stores the mounts that have currently been mounted. In case the
# startup operations fail (from unrecoverable state on disk), the mounts
# in UNDO_MOUNTS are unmounted in reverse order.
# ENCRYPTED_STATEFUL_MNT stores the path to the initial mount point for the
# encrypted stateful partition.
# Unmounts the incomplete mount setup during the failure path. Failure to
# set up mounts in this script result in the entire stateful partition getting
# wiped using clobber-state.
cleanup_mounts() {
# On failure unmount all saved mount points and repair stateful
for mount_point in ${UNDO_MOUNTS}; do
if [ "${mount_point}" = "${ENCRYPTED_STATEFUL_MNT}" ]; then
umount -n "${mount_point}"
# 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"
# Adds mounts to UNDO_MOUNT.
remember_mount() {
# Used to mount essential mount points for the system from the stateful
# or encrypted stateful partition.
# On failure, clobbers the stateful partition.
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"
cleanup_mounts "failed to mount $*"
# Assert that the argument is a directory.
# On failure, clobbers the stateful partition.
check_directory() {
local path="$1"
if [ -L "${path}" ] || [ ! -d "${path}" ]; then
cleanup_mounts "${path} is not a directory"
# Checks if /var is close to being full.
# Returns exit code of 0 (boolean true) if there is less than 10MB of
# free space left in /var or if there are less than 100 inodes available on
# the underlying filesystem.
is_var_full() {
local avail_space
local avail_inodes
avail_space="$(df -k --output=avail /var | grep -E -o '[0-9]+')"
avail_inodes="$(df -k --output=iavail /var | grep -E -o '[0-9]+')"
[ "${avail_space}" -lt 10000 ] || [ "${avail_inodes}" -lt 100 ]
# Returns if the TPM is owned or couldn't determine.
is_tpm_owned() {
local tpm_owned
# Depending on the kernel version, the file containing tpm owned information
# can be in one of two locations. Specifically, for kernel versions 3.10 and
# 3.14 the folder misc is used (/sys/class/misc/tpm0/device/owned). Starting
# from version 3.18 the folder tpm is used.
if [ -e /sys/class/misc/tpm0/device/owned ]; then
tpm_owned="$(cat /sys/class/misc/tpm0/device/owned)"
tpm_owned="$(cat /sys/class/tpm/tpm0/device/owned)"
[ "${tpm_owned}" != "0" ]
# Sanity check the date in case the RTC battery died and it initialized to
# something old ( This doesn't need to be perfect,
# just somewhat recent/sane. We'll recover later via tlsdate.
sanity_check_clock() {
# We manage this base timestamp by hand. It isolates us from bad clocks on
# the system where this image was built/modified, and on the runtime image
# (in case a dev modified random paths while the clock was out of sync).
# Calculated using: date -d"01 Jan $(date +%Y) UTC" +%s
local year="2020"
local base_secs="1577836800"
# See if the current time is older than our sanity time. If so, pull up.
if [ "$(date -u +%s)" -lt "${base_secs}" ]; then
date -u "01020000${year}"
add_clobber_crash_report() {
crash_reporter --early --log_to_stderr --mount_failure --mount_device="$1"
mount -n -t debugfs -o nodev,noexec,nosuid,mode=0750,uid=0,gid=debugfs-access \
debugfs /sys/kernel/debug
# bootstat writes timings to tmpfs.
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/
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
# Mount configfs, if present.
if [ -d /sys/kernel/config ]; then
mount -n -t configfs -o nodev,nosuid,noexec configfs \
if [ -e /usr/share/cros/startup/disable_stateful_security_hardening ]; then
# Mount securityfs as it is used to configure inode security policies below.
mount -n -t securityfs -o nodev,noexec,nosuid securityfs /sys/kernel/security
# 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="$((! $?))"
# Developer mode functions (defined in and will be loaded
# only when CROS_DEBUG=1).
dev_check_block_dev_mode() { true; }
dev_update_stateful_partition() { true; }
dev_gather_logs() { true; }
dev_mount_packages() { true; }
dev_is_debug_build() { false; }
# do_* are wrapper functions that may be redefined in developer mode or test
# images. Find more implementation in {dev,test,factory}
do_mount_var_and_home_chronos() { mount_var_and_home_chronos; }
do_umount_var_and_home_chronos() { umount_var_and_home_chronos; }
if [ "${CROS_DEBUG}" -eq 1 ]; then
. /usr/share/cros/
# Create a directory where PID files can be placed to override suspend and
# shutdown (e.g. during firmware updates). This needs to happen early since
# flashrom may run before the powerd job has started.
mkdir -p "${POWER_OVERRIDE_DIR}"
chmod 1777 "${POWER_OVERRIDE_DIR}"
# Create a directory where we can keep persistent references to namespaces.
mkdir /run/namespaces
mount --bind /run/namespaces /run/namespaces
mount --make-private /run/namespaces
# Create a file where we can bind-mount the Chrome mount namespace.
touch /run/namespaces/mnt_chrome
# Create a directory for mount-encrypted related logs and metrics.
mkdir /run/mount_encrypted
# Prepare to mount stateful partition
ROOT_DEV="$(rootdev -s)"
# 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_REMOVABLE="$(cat "/sys/block/${ROOTDEV_NAME}/removable")"
# Load the GPT helper functions and the image settings.
. "/usr/sbin/"
if [ "${ROOTDEV_REMOVABLE}" = "1" ]; then
# Path to the securityfs directory for configuring inode security policies.
# Block symlink and FIFO access on the given path.
block_symlink_and_fifo() {
printf "%s" "$1" > "${LSM_INODE_POLICIES}/block_symlink"
printf "%s" "$1" > "${LSM_INODE_POLICIES}/block_fifo"
# Allow symlink access on the given path.
allow_symlink() {
printf "%s" "$1" > "${LSM_INODE_POLICIES}/allow_symlink"
# Allow fifo access on the given path.
allow_fifo() {
printf "%s" "$1" > "${LSM_INODE_POLICIES}/allow_fifo"
# Configure chromiumos LSM security policies for process management.
. /usr/share/cros/
# Make sure our clock is somewhat up-to-date.
# 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" ] && [ "$ROOTDEV_TYPE" != "/dev/ram" ]; then
# Find our stateful partition mount point.
# To support multiple volumes on a single UBI device, if the stateful
# partition is not found on ubi${PARTITION_NUM_STATE}_0, check
if [ "${FORMAT_STATE}" = "ubi" ]; then
if [ ! -e "${STATE_DEV}" ]; then
if [ "${FS_FORMAT_STATE}" = "ext4" ]; then
DIRTY_EXPIRE_CENTISECS="$(sysctl -n vm.dirty_expire_centisecs)"
# Check if we enable ext4 features.
if [ "${FS_FORMAT_STATE}" = "ext4" ]; then
# Enable directory encryption for existing install.
if ! dumpe2fs -h "${STATE_DEV}" 2>/dev/null | \
grep -qe "^Filesystem features:.* encrypt.*"; then
# The stateful partition is not set for encryption.
# Check if we should migrate.
if ext4_dir_encryption_supported; then
# Ensure to replay the journal first so it doesn't overwrite the flag.
e2fsck -p -E journal_only "${STATE_DEV}" || :
# The kernel support encryption, do it!
tune2fs -O encrypt "${STATE_DEV}" || :
# Enable fs-verity for existing install.
if ! dumpe2fs -h "${STATE_DEV}" 2>/dev/null | \
grep -qe "^Filesystem features:.* verity.*"; then
# The stateful partition is not set for fs-verity.
# Check if we should migrate.
if ext4_fsverity_supported; then
# Ensure to replay the journal first so it doesn't overwrite the flag.
e2fsck -p -E journal_only "${STATE_DEV}" || :
# The kernel support fs-verity, do it!
tune2fs -O verity "${STATE_DEV}" || :
# Enable/disable quota feature.
if ! dumpe2fs -h "${STATE_DEV}" 2>/dev/null | \
grep -qe "^Reserved blocks gid.*20119"; then
# Add Android's AID_RESERVED_DISK to resgid.
tune2fs -g 20119 "${STATE_DEV}" || :
if [ -d /proc/sys/fs/quota ]; then
# Quota is enabled in the kernel, make sure that quota is enabled in the
# filesystem.
if ! dumpe2fs -h "${STATE_DEV}" 2>/dev/null | \
grep -qe "^Filesystem features:.* quota.*"; then
tune2fs -Oquota -Qusrquota,grpquota "${STATE_DEV}" || :
# Quota is not enabled in the kernel, make sure that quota is disabled in
# the filesystem.
if dumpe2fs -h "${STATE_DEV}" 2>/dev/null | \
grep -qe "^Filesystem features:.* quota.*"; then
tune2fs -O^quota -Q^usrquota,^grpquota "${STATE_DEV}" || :
# 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. (Not using fast
# mode out of security consideration: the device might have gotten into this
# state through power loss during dev mode transition).
chromeos-boot-alert self_repair
clobber-log --repair "${STATE_DEV}" \
"Self-repair corrupted stateful partition"
dumpe2fs -fh "${STATE_DEV}" > /run/dumpe2fs_stateful.log
add_clobber_crash_report "stateful"
exec clobber-state "keepimg"
# Block symlink traversal and opening of FIFOs on stateful. Note that we set
# up exceptions for developer mode later on.
block_symlink_and_fifo /mnt/stateful_partition
# Mount the OEM partition.
# mount_or_fail isn't used since this partition only has a filesystem
# on some boards.
if [ "${FORMAT_OEM}" = "ubi" ]; then
if [ ! -e "${OEM_DEV}" ]; then
mount -n -t "${FS_FORMAT_OEM}" -o "${OEM_FLAGS}" "${OEM_DEV}" /usr/share/oem
# This file is created by clobber-state after the transition
# to dev mode.
# Flag file indicating that encrypted stateful should be preserved across TPM
# clear. If the file is present, it's expected that TPM is not owned.
# This file is created after the TPM is initialized and the device is owned.
# Checks if developer mode is blocked.
dev_check_block_dev_mode "${DEV_MODE_FILE}"
# 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.
# Returns if device needs to clobber even though there's no devmode file present
# and boot is in verified mode.
needs_clobber_without_devmode_file() {
! is_tpm_owned && [ ! -O "${PRESERVATION_REQUEST_FILE}" ] &&
# Check for whether we need a stateful wipe, and alert the user as
# necessary. We can wipe for several different reasons:
# + User requested "power wash" which will create ${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 a non-recovery image in recovery mode (for example, doing
# Esc-F3-Power on a Chromebook with DEV-signed firmware); this protects various
# development use cases, most especially prototype units or booting Chromium OS
# on non-Chrome hardware. And because crossystem is slow on some platforms, we
# want to do the additional checks only after verified DEV_MODE_FILE existence.
if [ -L "${RESET_FILE}" ] || [ -e "${RESET_FILE}" ]; then
chromeos-boot-alert power_wash
# If it's not a plain file owned by us, force a powerwash.
if [ ! -O "${RESET_FILE}" ] || [ ! -f "${RESET_FILE}" ]; then
elif [ -z "${STATE_DEV}" ] || dev_is_debug_build; then
# No physical stateful partition available, usually due to initramfs
# (recovery image, factory install shim or netboot), or running from a
# debug build image. Do not wipe.
elif crossystem 'devsw_boot?0' && ! crossystem 'mainfw_type?recovery'; then
if [ -O "${DEV_MODE_FILE}" ] || needs_clobber_without_devmode_file; 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
CLOBBER_ARGS='fast keepimg'
if [ -O "${DEV_MODE_FILE}" ]; then
clobber-log -- "Leave developer mode, dev_mode file present"
clobber-log -- "Leave developer mode, no dev_mode file"
elif crossystem 'devsw_boot?1' && ! crossystem 'mainfw_type?recovery'; then
if [ ! -O "${DEV_MODE_FILE}" ]; then
# We're transitioning from verified boot to dev mode.
chromeos-boot-alert enter_dev
clobber-log -- "Enter developer mode"
if [ -n "${CLOBBER_ARGS}" ]; then
exec clobber-state "${CLOBBER_ARGS}"
# Walk the specified path and reset any file attributes (like immutable bit).
force_clean_file_attrs() {
local path="$1"
# In case the dir doesn't yet exist.
if [ ! -d "${path}" ]; then
# No physical stateful partition available, usually due to initramfs
# (recovery image, factory install shim or netboot). Do not check.
if [ -z "${STATE_DEV}" ]; then
if ! file_attrs_cleaner_tool "${path}"; then
chromeos-boot-alert self_repair
clobber-log -- "Bad file attrs under ${path}"
exec clobber-state "keepimg"
force_clean_file_attrs /mnt/stateful_partition/unencrypted
# Checks and updates stateful partition.
# 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}"
# Mount /home. This mount inherits nodev,noexec,nosuid from
# /mnt/stateful_partition above.
mount_or_fail --bind /mnt/stateful_partition/home /home
if ! do_mount_var_and_home_chronos; then
add_clobber_crash_report "encstateful"
cleanup_mounts "var and home"
remember_mount "${ENCRYPTED_STATEFUL_MNT}"
# Setup the encrypted reboot vault once the encrypted stateful partition
# is available. If unlocking the encrypted reboot vault failed (due to power
# loss/reboot/invalid vault), attempt to recreate the encrypted reboot vault.
if ! encrypted-reboot-vault --action=unlock; then
encrypted-reboot-vault --action=create
force_clean_file_attrs /var
force_clean_file_attrs /home/chronos
# If /var is too full, delete the logs so the device can boot successfully.
# It is possible that the fullness of /var was not due to logs, but that
# is very unlikely. If such a thing happens, we have a serious problem
# which should not be covered up here.
if is_var_full; then
rm -r -f /var/log
echo "Startup.ReclaimFullVar" > /mnt/stateful_partition/.reclaim_full_var
# Gather logs if needed. This might clear /var, so all init has to be after
# this.
# Collect crash reports from early boot/mount failures.
crash_reporter --ephemeral_collect
# Set up symlink traversal and FIFO blocking policy for /var, which may reside
# on a separate file system than /mnt/stateful_partition. Block symlink
# traversal and opening of FIFOs by default, but allow exceptions in the few
# instances where they are used intentionally.
block_symlink_and_fifo /var
# Generic symlink exceptions.
for symlink_exception in /var/cache/echo /var/cache/vpd /var/lib/timezone \
/var/log /home; do
mkdir -p "${symlink_exception}"
allow_symlink "${symlink_exception}"
# Project-specific symlink exceptions. Projects may add exceptions by adding a
# file under /usr/share/cros/startup/symlink_exceptions/ whose contents
# contains a list of paths (one per line) for which an exception should be
# made. File name should use the following format:
# <project-name>-symlink-exceptions.txt
for path_file in /usr/share/cros/startup/symlink_exceptions/*; do
if [ -f "${path_file}" ]; then
while read -r path; do
case "${path}" in
# Ignore blank lines.
"") ;;
# Ignore comments.
"#"*) ;;
mkdir -p "${path}"
allow_symlink "${path}"
done < "${path_file}"
# Project-specific FIFO exceptions. Projects may add exceptions by adding a
# file under /usr/share/cros/startup/fifo_exceptions/ whose contents contains
# a list of paths (one per line) for which an exception should be made. File
# name should use the following format: <project-name>-fifo-exceptions.txt
for path_file in /usr/share/cros/startup/fifo_exceptions/*; do
if [ -f "${path_file}" ]; then
while read -r path; do
case "${path}" in
# Ignore blank lines.
"") ;;
# Ignore comments.
"#"*) ;;
mkdir -p "${path}"
allow_fifo "${path}"
done < "${path_file}"
# /run is now tmpfs used for runtime data. Make sure /var/run and /var/lock are
# bind-mounted to /run and /run/lock respectively for backwards compatibility.
# We recreate these all the time in case they were corrupted to point somewhere
# else than what we want.
rm -rf /var/run /var/lock || \
cleanup_mounts "failed to delete /var/run and /var/lock"
mkdir -m 0755 /var/run /var/lock
mount -o bind /run /var/run
remember_mount /var/run
mount -o bind /run/lock /var/lock
remember_mount /var/lock
# Create daemon store folders.
# See
mkdir -m 0755 /run/daemon-store
for etc_daemon_store in /etc/daemon-store/*; do
# If /etc/daemon-store is empty, $etc_daemon_store is /etc/daemon-store/*.
# This if statement filters that out.
if [ -d "$etc_daemon_store" ]; then
mkdir -m 0755 "${run_daemon_store}"
mount -o bind --make-shared "${run_daemon_store}" "${run_daemon_store}"
# Make sure required /var subdirectories exist.
mkdir -p -m 0755 /var/cache /var/db /var/log/metrics \
/var/spool /var/tmp /var/lib/misc
# 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/log /var/log/metrics \
/var/spool /var/tmp /var/lib /var/lib/misc /home/chronos /home/root; do
check_directory "${d}"
# Remove /var/empty if it exists. Use /mnt/empty instead.
if [ -e /var/empty ]; then
chattr -i /var/empty || :
rm -rf /var/empty
# Make sure that what gets written to /var/log stays in /var/log. Some notes on
# subtleties in the command below:
# 1. find's -exec with the '+' argument passes multiple found paths per shell
# script invocation.
# 2. sh -c expects a command name before parameters, so we pass a dummy name.
# Failure to do so would omit the first symlink since "$0" is not in "$@".
# 3. set -e causes the shell to bail out on rm failure, so we can err on the
# safe side and do a stateful wipe instead of leaving symlinks around.
# 4. If readlink fails (e.g. due to dangling symlinks) grep will still return
# false and so will the pipeline, so dangling symlinks get removed.
# 5. Find doesn't follow symlinks and we pass -xdev to make sure to only
# examine symlinks in the /var/log subtree.
find /var/log -xdev -type l -exec sh -e -c '
for path; do
if ! readlink -f "${path}" | grep -q "^/var/log/"; then
rm "${path}"
' 'nuke_symlinks' '{}' '+'
# Bail and wipe on failure to remove a symlink.
if [ "$?" -ne 0 ]; then
cleanup_mounts "Failed to remove symlinks under /var/log"
# /var/tmp must be world-writable and sticky
chmod 1777 /var/tmp
# /home/root must be sticky
chmod 1751 /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, and /var/db may already exist with wrong permissions.
# Force the correct ones.
chmod 0755 /var/cache /var/db /var/spool /var/lib /var/lib/misc
# "--make-shared" to let ARC container access mount points under /media.
mount --make-shared -n -t tmpfs -o nodev,noexec,nosuid media /media
# Restore file contexts for /var.
# TODO(fqj): use type_transition to correctly label directories at creation so
# relabel need only be started if SELinux policy updates.
if [ -f /sys/fs/selinux/enforce ]; then
restorecon -R -D /var
# Restoring file contexts for sysfs. tracefs is excluded from this invocation
# and delayed in a separate job to improve boot time.
restorecon -R /sys -e /sys/kernel/debug/tracing
# We cannot do recursive for .shadow since userdata is encrypted (including
# file names) before user logs-in. Restoring context for it may mislabel files
# if encrypted filename happens to match something.
restorecon /home /home/.shadow /home/.shadow/* /home/.shadow/.* /home/.shadow/*/*
# It's safe to recursvely restorecon /home/{user,root,chronos} since userdir
# is not bind-mounted here before logging in.
restorecon -R -D /home/user /home/root /home/chronos
# Mount dev packages.
# Unmount securityfs so that further modifications to inode security policies
# are not possible.
umount /sys/kernel/security
# Skip rollback restore check if not supported on this device.
if [ -x "$(command -v rollback_finish_restore)" ]; then
# If a rollback is in progress, restores preserved data.
# If a rollback is not in progress, it just exits.
bootstat post-startup
# Always return success to avoid killing init
exit 0