#!/bin/bash # -*- mode: sh; indent-tabs-mode: nil; sh-basic-offset: 4; -*- # vim: et sts=4 sw=4 # SPDX-License-Identifier: LGPL-2.1+ # # Copyright © 2019-2020 Collabora Ltd. # Copyright © 2019-2020 Valve Corporation. # # This file is part of steamos-customizations. # # steamos-customizations is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License as # published by the Free Software Foundation; either version 2.1 of the License, # or (at your option) any later version. set -e set -u DISK= # --disk= PARTSET= # --partset= SYMLINKS_DIR=/dev/disk/by-partsets [ -e /usr/lib/steamos/steamos-partitions-lib ] && \ . /usr/lib/steamos/steamos-partitions-lib || \ { echo "Failed to source '/usr/lib/steamos/steamos-partitions-lib'"; exit 1; } # Helpers fail() { echo >&2 "$@"; exit 1; } usage() { local status=${1-2} if [ $status -ne 0 ]; then exec >&2 fi echo echo "Usage: $(basename $0) [--disk=DISK] --partset=A|B|self|other -- COMMAND" echo echo "Run COMMAND in a chroot. The chroot is SteamOS specific, which means" echo "that the various SteamOS partitions are mounted within the chroot." echo echo "There are two use-cases for this command:" echo echo "1. Chroot into the 'other' SteamOS, useful for atomic update. For example:" echo echo " $(basename $0) --partset other -- COMMAND" echo echo "2. Chroot into another SteamOS install, on another disk. For example:" echo echo " $(basename $0) --disk /dev/sdb --partset A -- COMMAND" echo echo "The second mode works if SteamOS is installed alone on the disk, however" echo "if there are other partitions on the disk then it's not guaranteed to work." echo "This is because we rely on partition labels to find SteamOS partitions and" echo "setup the chroot. This is too fragile, and if we're unlucky there could be" echo "another partition that doesn't belong to SteamOS but uses the same partition" echo "label." echo exit $status } while [ $# -gt 0 ]; do case "$1" in -h|--help) usage 0 ;; -d|--disk) shift [ "${1:-}" ] || usage 1 DISK=$1 shift ;; -p|--partset) shift [ "${1:-}" ] || usage 1 PARTSET=$1 shift ;; --) shift break ;; *) usage 1 ;; esac done [ "$PARTSET" ] || usage 1 # Find devices ESP_DEVICE= EFI_DEVICE= ROOTFS_DEVICE= if [ "$DISK" ]; then # A disk was provided, which means that we're chrooting into another # SteamOS install. We do our best to 'guess' the partitions, ie. we # mostly rely on partition labels. This is OK if SteamOS is alone on # this disk, but it's fragile if it's installed along another OS. [ -b "$DISK" ] || fail "'$DISK' is not a block device" ROOTFS_DEVICE=$(get_device_by_partlabel $DISK rootfs-$PARTSET) EFI_DEVICE=$(get_device_by_partlabel $DISK efi-$PARTSET) VAR_DEVICE=$(get_device_by_partlabel $DISK var-$PARTSET) ESP_DEVICE=$(get_device_by_partlabel $DISK esp) if [ -z "$ESP_DEVICE" ]; then ESP_DEVICE=$(get_device_by_typeuuid $DISK \ c12a7328-f81f-11d2-ba4b-00a0c93ec93b) fi else # No disk was provided, which means that we're chrooting into the # 'other' install, A or B. We can find these partitions reliably # using the existing symlinks. ROOTFS_DEVICE=$(realpath $SYMLINKS_DIR/$PARTSET/rootfs) EFI_DEVICE=$(realpath $SYMLINKS_DIR/$PARTSET/efi) ESP_DEVICE=$(realpath $SYMLINKS_DIR/shared/esp) VAR_DEVICE=$(realpath $SYMLINKS_DIR/$PARTSET/var) fi [ -b "$ROOTFS_DEVICE" ] || fail "'$ROOTFS_DEVICE' is not a block device" [ -b "$EFI_DEVICE" ] || fail "'$EFI_DEVICE' is not a block device" [ -b "$ESP_DEVICE" ] || fail "'$ESP_DEVICE' is not a block device" [ -b "$VAR_DEVICE" ] || fail "'$VAR_DEVICE' is not a block device" # Do the job prepare_chroot_at() { local dir=$1 mount -o ro "$ROOTFS_DEVICE" $dir mount --bind /dev $dir/dev mount --bind /proc $dir/proc mount --bind /run $dir/run mount --bind /sys $dir/sys mount --bind /sys/firmware/efi/efivars $dir/sys/firmware/efi/efivars mount -t tmpfs -o size=128M tmpfs $dir/tmp mount "$EFI_DEVICE" $dir/efi mount "$ESP_DEVICE" $dir/esp mount "$VAR_DEVICE" $dir/var if [ -d $dir/var/boot ]; then mount --bind $dir/var/boot $dir/boot fi } cleanup_chroot_at() { local dir=$1 if mountpoint -q $dir/boot; then umount $dir/boot || : fi umount $dir/var || : umount $dir/esp || : umount $dir/efi || : umount $dir/tmp || : umount $dir/sys/firmware/efi/efivars || : umount -R $dir/sys || : umount $dir/run || : umount -R $dir/proc || : umount $dir/dev || : umount $dir || : rmdir $dir } CHROOTDIR=$(mktemp -d) trap "cleanup_chroot_at $CHROOTDIR" EXIT prepare_chroot_at $CHROOTDIR chroot $CHROOTDIR "$@"