| #!/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 |
| } |