#!/usr/bin/env bash # reset-arch.sh — Reset the root btrfs subvolume while preserving user home data. # # What this does: # 1. Detects LUKS encryption; unlocks via FIDO2 token and/or passphrase # 2. Saves user credentials and system config from the old @ subvolume # 3. Clears app configs (~/.config) from @home, preserving auth keys (Yubico/) # 4. Deletes and recreates the @ (root) btrfs subvolume # 5. Reinstalls the base system via pacstrap # 6. Restores credentials, PAM, fstab, mkinitcpio, GRUB config # 7. Regenerates initramfs and GRUB menu from chroot so the system boots cleanly set -euo pipefail TMPDIR=$(mktemp -d /tmp/arch-reset.XXXXXX) trap 'rm -rf "$TMPDIR"' EXIT pause() { read -rp "Press ENTER to continue..."; } echo "=======================================" echo " M-Archy System Reset" echo "=======================================" echo "This will:" echo " • Delete and recreate the root (@) btrfs subvolume" echo " • Reinstall base system packages from scratch" echo " • Clear all user ~/.config directories (auth keys preserved)" echo " • Preserve home directories, passwords, and FIDO2 login keys" echo "" # ── Required tools in live environment ────────────────────────────────────── pacman -Syd --noconfirm cryptsetup btrfs-progs jq libfido2 # ── Drive selection ────────────────────────────────────────────────────────── lsblk echo "" read -rp "Enter drive to reset (e.g., /dev/sda): " DRIVE ROOT_PART="${DRIVE}2" EFI_PART="${DRIVE}1" echo "" echo "WARNING: The root subvolume on $ROOT_PART will be DELETED and reinstalled." echo " User home directories will be preserved." echo " App configs (~/.config) will be wiped (Yubico auth keys excepted)." echo "" read -rp "Type YES to continue: " _CONFIRM [[ "$_CONFIRM" == "YES" ]] || { echo "Aborted."; exit 1; } # ── LUKS detection and unlock ──────────────────────────────────────────────── MAPPER_DEV="$ROOT_PART" if cryptsetup isLuks "$ROOT_PART" 2>/dev/null; then echo "" echo "Partition $ROOT_PART is LUKS2-encrypted." echo "Select unlock method:" echo " 1) Try enrolled token (FIDO2/TPM2) first, fall back to passphrase [recommended]" echo " 2) Passphrase only" echo " 3) Enrolled token only (FIDO2/TPM2)" read -rp "Choice [1]: " _UNLOCK _UNLOCK="${_UNLOCK:-1}" case "$_UNLOCK" in 1) echo "Insert FIDO2 key if using one, then press ENTER..." pause if ! cryptsetup open --token-only "$ROOT_PART" cryptroot 2>/dev/null; then echo "Token unlock failed — enter passphrase..." cryptsetup open "$ROOT_PART" cryptroot fi ;; 2) cryptsetup open "$ROOT_PART" cryptroot ;; 3) echo "Insert FIDO2 key and press ENTER..." pause cryptsetup open --token-only "$ROOT_PART" cryptroot ;; *) echo "Invalid choice, using passphrase..." cryptsetup open "$ROOT_PART" cryptroot ;; esac MAPPER_DEV="/dev/mapper/cryptroot" echo "Partition unlocked." fi # ── Detect installed kernel from EFI partition ─────────────────────────────── TMPBOOT=$(mktemp -d /tmp/arch-reset-boot.XXXXXX) mount "$EFI_PART" "$TMPBOOT" KERNEL_PKG="linux" for _img in "$TMPBOOT"/vmlinuz-*; do [[ -f "$_img" ]] && KERNEL_PKG=$(basename "$_img" | sed 's/^vmlinuz-//') && break done umount "$TMPBOOT"; rmdir "$TMPBOOT" echo "Detected kernel: $KERNEL_PKG" # ── Detect GPU from live hardware ──────────────────────────────────────────── GPU_INFO=$(lspci 2>/dev/null | grep -E "VGA|3D" || true) GPU_PKGS="" if echo "$GPU_INFO" | grep -qi nvidia; then GPU_PKGS="nvidia-open" elif echo "$GPU_INFO" | grep -qi amd; then GPU_PKGS="xf86-video-amdgpu" elif echo "$GPU_INFO" | grep -qi intel; then GPU_PKGS="xf86-video-intel" fi # ── Mount btrfs top-level ──────────────────────────────────────────────────── BTRFS_MNT="$TMPDIR/btrfs" mkdir -p "$BTRFS_MNT" mount -o subvolid=5 "$MAPPER_DEV" "$BTRFS_MNT" # ── Save critical configuration from @ ────────────────────────────────────── echo "Saving system configuration..." SAVED="$TMPDIR/saved" _save() { local src="$BTRFS_MNT/@/etc/$1" local dst="$SAVED/etc/$1" [[ -e "$src" ]] || return 0 mkdir -p "$(dirname "$dst")" cp -a "$src" "$dst" } _save passwd _save shadow _save group _save gshadow _save sudoers _save sudoers.d _save pam.d _save hostname _save locale.conf _save locale.gen _save vconsole.conf _save fstab _save mkinitcpio.conf _save mkinitcpio.conf.d _save default/grub _save NetworkManager # Save timezone symlink target as plain text (the symlink itself can't cross roots) { readlink "$BTRFS_MNT/@/etc/localtime" 2>/dev/null || echo "/usr/share/zoneinfo/UTC"; } \ > "$SAVED/timezone" # ── Clear ~/.config in @home (preserve auth-critical subdirs) ──────────────── echo "Clearing user app configs..." # Yubico/ holds U2F/FIDO2 PAM keys — deleting these would break FIDO2 login PRESERVED_CONFIG_DIRS=("Yubico" "pam-u2f") for _homedir in "$BTRFS_MNT/@home"/*/; do [[ -d "$_homedir" ]] || continue _user=$(basename "$_homedir") _cfgdir="$_homedir/.config" [[ -d "$_cfgdir" ]] || continue echo " Clearing ~/.config for: $_user" # Top-level files in .config find "$_cfgdir" -mindepth 1 -maxdepth 1 ! -type d -delete # Subdirectories, skipping preserved ones while IFS= read -r -d '' _subdir; do _dname=$(basename "$_subdir") _skip=false for _keep in "${PRESERVED_CONFIG_DIRS[@]}"; do [[ "$_dname" == "$_keep" ]] && _skip=true && break done $_skip || rm -rf "$_subdir" done < <(find "$_cfgdir" -mindepth 1 -maxdepth 1 -type d -print0) done # ── Delete @ and recreate fresh ────────────────────────────────────────────── echo "Deleting root subvolume @..." btrfs subvolume delete "$BTRFS_MNT/@" echo "Creating fresh root subvolume @..." btrfs subvolume create "$BTRFS_MNT/@" # ── Mount for installation ─────────────────────────────────────────────────── umount "$BTRFS_MNT" mount -o subvol=@ "$MAPPER_DEV" /mnt mkdir -p /mnt/home mount -o subvol=@home "$MAPPER_DEV" /mnt/home mkdir -p /mnt/boot mount "$EFI_PART" /mnt/boot # ── Pacstrap base system ───────────────────────────────────────────────────── echo "Reinstalling base system (this will take a while)..." # shellcheck disable=SC2086 pacstrap /mnt \ base base-devel "$KERNEL_PKG" linux-firmware vim zsh git networkmanager grub efibootmgr \ btrfs-progs cryptsetup libfido2 pam-u2f sudo less jq $GPU_PKGS # ── Restore saved configuration ────────────────────────────────────────────── echo "Restoring system configuration..." _restore() { local src="$SAVED/etc/$1" local dst="/mnt/etc/$1" [[ -e "$src" ]] || return 0 mkdir -p "$(dirname "$dst")" cp -a "$src" "$dst" } # Auth files — explicit permissions [[ -f "$SAVED/etc/passwd" ]] && install -m 644 "$SAVED/etc/passwd" /mnt/etc/passwd [[ -f "$SAVED/etc/group" ]] && install -m 644 "$SAVED/etc/group" /mnt/etc/group [[ -f "$SAVED/etc/shadow" ]] && install -m 000 "$SAVED/etc/shadow" /mnt/etc/shadow [[ -f "$SAVED/etc/gshadow" ]] && install -m 000 "$SAVED/etc/gshadow" /mnt/etc/gshadow _restore sudoers _restore sudoers.d _restore pam.d _restore hostname _restore locale.conf _restore locale.gen _restore vconsole.conf _restore fstab _restore mkinitcpio.conf _restore mkinitcpio.conf.d _restore default/grub _restore NetworkManager TZ_TARGET=$(cat "$SAVED/timezone") ln -sf "$TZ_TARGET" /mnt/etc/localtime # ── Chroot: regenerate initramfs, GRUB menu, services ─────────────────────── echo "Finalizing inside chroot..." arch-chroot /mnt /bin/bash <<'CHROOT_EOF' set -euo pipefail locale-gen hwclock --systohc systemctl enable NetworkManager mkinitcpio -P grub-mkconfig -o /boot/grub/grub.cfg # Re-apply correct ownership for user home directories using restored UIDs while IFS=: read -r _uname _ _uid _gid _ _home _; do (( _uid >= 1000 )) && [[ -d "$_home" ]] && chown -R "${_uid}:${_gid}" "$_home" || true done < /etc/passwd CHROOT_EOF echo "" echo "=======================================" echo " Reset complete!" echo "=======================================" echo " umount -R /mnt && reboot"