| #!/bin/sh | |
| # Copyright 2017 The ChromiumOS Authors | |
| # Use of this source code is governed by a BSD-style license that can be | |
| # found in the LICENSE file. | |
| # Utility functions for chromeos_startup to run in developer mode. | |
| STATEFUL_PARTITION="/mnt/stateful_partition" | |
| PRESERVE_DIR="${STATEFUL_PARTITION}/unencrypted/preserve" | |
| # These paths will be preserved through clobbering. | |
| PATHS_TO_PRESERVE="" | |
| PATHS_TO_PRESERVE="${PATHS_TO_PRESERVE} /var/lib/servod" | |
| PATHS_TO_PRESERVE="${PATHS_TO_PRESERVE} /usr/local/servod" | |
| PATHS_TO_PRESERVE="${PATHS_TO_PRESERVE} /var/lib/device_health_profile" | |
| PATHS_TO_PRESERVE="${PATHS_TO_PRESERVE} /usr/local/etc/wifi_creds" | |
| # Returns if we are running on a debug build. | |
| dev_is_debug_build() { | |
| crossystem 'debug_build?1' | |
| } | |
| # Updates stateful partition if pending update is available. | |
| # shellcheck disable=SC2120 | |
| dev_update_stateful_partition() { | |
| local stateful_update_args | |
| local stateful_update_file="${STATEFUL_PARTITION}/.update_available" | |
| if [ $# -gt 0 ]; then | |
| stateful_update_args="$*" | |
| elif [ -f "${stateful_update_file}" ]; then | |
| stateful_update_args="$(cat "${stateful_update_file}")" | |
| else | |
| return | |
| fi | |
| # 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. | |
| local var_target="${STATEFUL_PARTITION}/var" | |
| local var_new="${var_target}_new" | |
| local var_target="${var_target}_overlay" | |
| local developer_target="${STATEFUL_PARTITION}/dev_image" | |
| local developer_new="${developer_target}_new" | |
| # Only replace the developer and var_overlay directories if new replacements | |
| # are available. | |
| if [ -d "${developer_new}" ] && [ -d "${var_new}" ]; then | |
| clobber-log -- "Updating from ${developer_new} && ${var_new}." | |
| mkdir -p "${developer_target}" "${var_target}" | |
| find "${developer_target}" "${var_target}" -mindepth 1 -maxdepth 1 \ | |
| -exec rm -rf {} + | |
| find "${var_new}" -mindepth 1 -maxdepth 1 -exec mv {} "${var_target}" \; | |
| find "${developer_new}" -mindepth 1 -maxdepth 1 -exec mv {} \ | |
| "${developer_target}" \; | |
| rmdir "${developer_new}" "${var_new}" | |
| 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 | |
| # 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 "${STATEFUL_PARTITION}" -depth -mindepth 1 \ | |
| -not -path "${STATEFUL_PARTITION}/.labmachine" \ | |
| -not -path "${developer_target}/*" \ | |
| -not -path "${var_target}/*" \ | |
| -not -path "${PRESERVE_DIR}/*" \ | |
| -not -type d -print0 | xargs -0 -r rm -f | |
| find "${STATEFUL_PARTITION}" -depth -mindepth 1 \ | |
| -not -path "${developer_target}/*" \ | |
| -not -path "${var_target}/*" \ | |
| -not -path "${PRESERVE_DIR}/*" \ | |
| -type d -print0 | xargs -0 -r rmdir --ignore-fail-on-non-empty | |
| # Let's really be done before coming back. | |
| sync | |
| fi | |
| rm -rf "${stateful_update_file}" | |
| } | |
| # Keep this list in sync with the var_overlay elements in the DIRLIST | |
| # found in chromeos-install from chromeos-base/chromeos-installer. | |
| MOUNTDIRS=" | |
| db/pkg | |
| lib/portage | |
| cache/dlc-images | |
| " | |
| # Mount stateful partition for dev packages. | |
| dev_mount_packages() { | |
| # Optionally pass a device to mount the dev-image from. | |
| local device=$1 | |
| # 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 "${STATEFUL_PARTITION}/dev_image" ]; then | |
| mkdir -p -m 0755 "${STATEFUL_PARTITION}/dev_image" | |
| fi | |
| # Mount the dev image if there is a separate device available. | |
| lvchange -ay "${device}" | |
| if [ -n "${device}" ]; then | |
| mount -n "${device}" "${STATEFUL_PARTITION}/dev_image" \ | |
| -o "nodev,noexec,nosuid,noatime,discard" | |
| fi | |
| # Checks and updates stateful partition. | |
| dev_update_stateful_partition | |
| # Mount and then remount to enable exec/suid. | |
| mount_or_fail --bind "${STATEFUL_PARTITION}/dev_image" /usr/local | |
| mount -n -o remount,exec,suid /usr/local | |
| if [ ! -e /usr/share/cros/startup/disable_stateful_security_hardening ]; then | |
| # Add exceptions to allow symlink traversal and opening of FIFOs in the | |
| # dev_image subtree. | |
| local d | |
| for d in "/mnt/stateful_partition/dev_image" "/var/tmp/portage"; do | |
| mkdir -p "${d}" | |
| allow_symlink "${d}" | |
| allow_fifo "${d}" | |
| done | |
| fi | |
| # Set up /var elements needed by gmerge. | |
| # TODO(keescook) Use dev/test package installs instead of piling more | |
| # things here (crosbug.com/14091). | |
| local base | |
| base="${STATEFUL_PARTITION}/var_overlay" | |
| if [ -d "${base}" ]; then | |
| local dir dest | |
| echo "${MOUNTDIRS}" | while read -r dir ; do | |
| if [ -n "${dir}" ]; then | |
| if [ ! -d "${base}/${dir}" ]; then | |
| continue | |
| fi | |
| dest="/var/${dir}" | |
| # Previous versions of this script created a symlink instead of setting | |
| # up a bind mount, so get rid of the symlink if it exists. | |
| (rm -f "${dest}" && mkdir -p "${dest}") || : | |
| mount_or_fail --bind "${base}/${dir}" "${dest}" | |
| fi | |
| done | |
| fi | |
| } | |
| # Unmount stateful partition for dev packages. | |
| dev_unmount_packages() { | |
| # Unmount bind mounts for /var elements needed by gmerge. | |
| local base="/var" | |
| if [ -d "${base}" ]; then | |
| echo "${MOUNTDIRS}" | while read -r dir ; do | |
| if [ -n "${dir}" ]; then | |
| if [ ! -d "${base}/${dir}" ]; then | |
| continue | |
| fi | |
| umount -n "${base}/${dir}" | |
| fi | |
| done | |
| fi | |
| # unmount /usr/local to match dev_mount_package. | |
| umount -n /usr/local | |
| # If the dev image is mounted using a logical volume, unmount it. | |
| if mountpoint -q /mnt/stateful_partition/dev_image; then | |
| umount /mnt/stateful_partition/dev_image | |
| fi | |
| } | |
| # Copy contents in src path to dst path if it exists. | |
| copy_path() { | |
| local src_path="$1" | |
| local dst_path="$2" | |
| if [ -d "${src_path}" ]; then | |
| mkdir -p "${dst_path}" | |
| cp -a "${src_path}/"* "${dst_path}" | |
| fi | |
| } | |
| # Pushes the array of paths to preserve to protected path. | |
| dev_push_paths_to_preserve() { | |
| local path | |
| for path in ${PATHS_TO_PRESERVE}; do | |
| copy_path "${path}" "${PRESERVE_DIR}/${path}" | |
| done | |
| } | |
| # Pops the array of paths to preserve from protected path. | |
| dev_pop_paths_to_preserve() { | |
| local path | |
| for path in ${PATHS_TO_PRESERVE}; do | |
| local src_path="${PRESERVE_DIR}/${path}" | |
| copy_path "${src_path}" "${path}" | |
| rm -rf "${src_path}" | |
| done | |
| } |