#!/usr/bin/env bash set -euo pipefail if [[ $# -lt 2 ]]; then >&2 echo "usage: repack.sh IN_ROOTFS OUT_DIR" exit 1 fi if [[ $EUID -ne 0 ]]; then >&2 echo "this script must be run as root" exit 1 fi set -x script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) in_rootfs="$1" out_dir="$2" out_tmp=$(mktemp -d -p "$(dirname "$out_dir")" .tmp.XXXXXXXXXX) chmod 0755 "$out_tmp" mountpoint=$(mktemp -d) _cleanup() { # FIXME: this doesn't handle `gpgconf --kill all` mountpoint -q "$mountpoint" && umount --recursive "$mountpoint" rmdir "$mountpoint" rm -rf "$out_tmp" } trap _cleanup EXIT cp "$in_rootfs" "$out_tmp"/rootfs.img # Randomize filesystem UUID fs_uuid=$(uuidgen) btrfstune -fU "$fs_uuid" "$out_tmp"/rootfs.img # Mount and mark read-only mount -o compress=zstd "$out_tmp"/rootfs.img "$mountpoint" btrfs property set -ts "$mountpoint" ro false # Mount /dev and /proc for mkinitcpio, etc. mount -t devtmpfs dev "$mountpoint"/dev mount -t proc proc "$mountpoint"/proc # Set up the usual tmpfs suspects mount -t tmpfs -o mode=755 tmpfs "$mountpoint"/run mount -t tmpfs tmpfs "$mountpoint"/tmp # Ensure no writes to /home or /var mount -t tmpfs -o mode=755 tmpfs "$mountpoint"/home mount -t tmpfs -o mode=755 tmpfs "$mountpoint"/var # Get network resolution working in the chroot. mount --bind "$(realpath /etc/resolv.conf)" "$mountpoint"/etc/resolv.conf # Modify /etc/os-release and /lib/steamos-atomupd/manifest.json. We want # VERSION_ID/version to remain the same as upstream so that Steam can still load # changelog entries. version=$(jq -r .version <"$mountpoint"/lib/steamos-atomupd/manifest.json) # We want to set our own BUILD_ID/buildid so that we can make multiple image # updates even if upstream doesn't make any changes. SteamOS requires the build # ID is of the form YYYYMMDD.N. The N must be an integer, at least as far as # Python 3's `int` built-in function is concerned; leading zeroes parse fine, # but when it's rendered back out to a string they get dropped, so we may as # well remove them ourselves. buildid=$(date +%Y%m%d.%H%M%S | sed -e 's/\.0\+\(.\+\)$/.\1/') jq --arg buildid "$buildid" '.buildid = $buildid' \ <"$mountpoint"/lib/steamos-atomupd/manifest.json >"$out_tmp"/manifest.json cat "$out_tmp"/manifest.json >"$mountpoint"/lib/steamos-atomupd/manifest.json sed -i -e "/^BUILD_ID=/c BUILD_ID=$buildid" "$mountpoint"/etc/os-release # For lack of a better place to make it incredibly obvious that this is an # unofficial image, modify NAME and PRETTY_NAME. (This sadly doesn't show up in # the Steam Deck UI, that seems to pull from /etc/lsb-release?) sed -i -e "/^NAME=/c NAME=\"SteamOS (iliana's version)\"" "$mountpoint"/etc/os-release sed -i -e "/^PRETTY_NAME=/c PRETTY_NAME=\"SteamOS (iliana's version)\"" "$mountpoint"/etc/os-release # Swap URLs in /etc/steamos-atomupd/client.conf. cat "$script_dir/atomupd-client.conf" >"$mountpoint"/etc/steamos-atomupd/client.conf # Replace /etc/rauc/keyring.pem. cat "$script_dir/iliana-rauc-keyring.pem" >"$mountpoint"/etc/rauc/keyring.pem # # We need a valid keyring to verify the signature of packages we want to # # install. We don't really want to mess with the state /etc/pacman.d/gnupg is in # # on the SteamOS rootfs, so we put one on /tmp. # chroot "$mountpoint" pacman-key --gpgdir /tmp/gnupg --init # chroot "$mountpoint" pacman-key --gpgdir /tmp/gnupg --populate # # Terminate GPG agent processes # chroot "$mountpoint" gpgconf --homedir /tmp/gnupg --kill all # Add our repo. sed -i -e '/\[testing\]/s;^;[fauxlo]\nServer = https://fauxlo.ili.fyi/pacman/$arch\nSigLevel = Never\n\n;' "$mountpoint"/etc/pacman.conf # Synchronize the fauxlo repo behind pacman's back. (-Sy would otherwise refresh # the other repos, which we want frozen in time.) curl -Ro "$mountpoint"/usr/lib/holo/pacmandb/sync/fauxlo.db https://fauxlo.ili.fyi/pacman/x86_64/fauxlo.db # Install packages. pacman --sysroot "$mountpoint" --gpgdir /tmp/gnupg --noconfirm -S \ linux-neptune-61 # Set FastConnectable=true in /etc/bluetooth, as we don't have any concern over # battery life sed -i -e '/^#\?FastConnectable/c FastConnectable = true' "$mountpoint"/etc/bluetooth/main.conf # Don't disable multicast DNS. rm "$mountpoint"/usr/lib/systemd/resolved.conf.d/00-disable-mdns.conf # Mark read-only and unmount btrfs property set -ts "$mountpoint" ro true fstrim -v "$mountpoint" umount --recursive "$mountpoint" # Prepare bundle mkdir "$out_tmp"/bundle echo "$fs_uuid" >"$out_tmp"/bundle/UUID casync make --store="$out_tmp/steamdeck-$buildid-$version.castr" "$out_tmp"/bundle/rootfs.img.caibx "$out_tmp"/rootfs.img cat >"$out_tmp"/bundle/manifest.raucm <