# CachyOS Btrfs + Limine Reference

## Typical Drive Layout

CachyOS uses a two-NVMe setup on this system:
- **nvme0n1** — System drive (Sabrent, ~954GB)
  - nvme0n1p1: 4GB vfat — ESP/EFI (boot partition, has `limine.conf`)
  - nvme0n1p2: ~950GB btrfs — Root + home + everything else
- **nvme1n1** — Data/gaming drive (Sabrent Rocket 4.0 Plus, ~932GB)
  - nvme1n1p1: 200MB vfat — Windows EFI
  - nvme1n1p2: 16MB — MS reserved
  - nvme1n1p3: ~465GB ntfs — Windows data
  - nvme1n1p4: ~780MB ntfs — Windows recovery

**⚠️ Drive ordering (`nvme0` vs `nvme1`) can swap between live session boots.** Always use `lsblk -o MODEL` for identification.

## Btrfs Subvolumes (on system drive)

| ID | Path | Purpose |
|----|------|---------|
| 256 | `@` | Root filesystem (/) |
| 257 | `@home` | Home directories (/home) |
| 258 | `@root` | Root user's home |
| 259 | `@srv` | Server data |
| 260 | `@cache` | Package cache |
| 261 | `@tmp` | Temp files |
| 262 | `@log` | Log files |
| 265 | `@/.snapshots/` | Snapper snapshots |

## Mounting from a Live Environment

```bash
# Mount root @ subvol
mount -t btrfs -o subvol=@ /dev/nvme0n1p2 /mnt/root

# Mount home (already mounted at /home in live env, but chroot needs bind)
mount -t btrfs -o subvol=@home /dev/nvme0n1p2 /mnt/root/home

# Mount boot/ESP
mount /dev/nvme0n1p1 /mnt/root/boot

# Mount cache for pacman operations
mount -t btrfs -o subvol=@cache /dev/nvme0n1p2 /mnt/root/var/cache

# Mount log for journal operations
mount -t btrfs -o subvol=@log /dev/nvme0n1p2 /mnt/root/var/log

# For chroot operations (systemctl, limine-entry-tool):
mount --bind /dev /mnt/root/dev
mount --bind /proc /mnt/root/proc
mount --bind /sys /mnt/root/sys
```

## Limine Bootloader

CachyOS uses **Limine**, not GRUB. Config at `/boot/limine.conf` (on ESP partition).

### Entry Index (0-based)

| Index | Entry | Purpose |
|-------|-------|---------|
| 0 | `linux-cachyos` | Main kernel (e.g. 7.0.12) |
| 1 | `linux-cachyos-lts` | LTS fallback (e.g. 6.18.35) |
| 2 | Snapshots | Snapper snapshot entries |
| 3+ | Other systems | Windows, EFI fallback |

### Common Fixes

**Fix default entry to normal system (not snapshots):**
```bash
sed -i 's/default_entry: 2/default_entry: 0/' /boot/limine.conf
```

**Remove stale snapshot entries from boot menu** (after deleting old snapshots):
```bash
# Keeps everything from start to the //Snapshots header, trims the rest
sed -n '1,/^\/\/Snapshots/p' /boot/limine.conf > /tmp/limine_clean.conf
# If there are entries after Snapshots you want to keep, extract them manually
```

**Regenerate limine config** (from booted system, not chroot):
```bash
sudo limine-entry-tool
```

**⚠️ `limine-entry-tool` fails in chroot** without `/dev` bind-mounted — it needs `/dev/fd/63` and other device files. Prefer direct `sed` on the config from a live environment.

### Sample Kernel Cmdline (normal boot)
```
quiet nowatchdog split_lock_detect=off nvme_core.default_ps_max_latency_us=0 splash rw rootflags=subvol=/@ root=UUID=<UUID>
```

The `rootflags=subvol=/@` directs the kernel to mount the `@` subvolume as root.

## Snapshot Management

Snapshots are stored at `@/.snapshots/<N>/snapshot` and managed by snapper.

**List snapshots:**
```bash
btrfs subvolume list / | grep snapshot
# or
snapper list
```

**Restore a snapshot:**
```bash
snapper undochange <from>..<to>
```

**⚠️ Snapper restore can fail with hook error if ESP isn't at /boot:**
```
ERROR: FAT32 boot partition is not mounted at '/boot'.
WARNING: pre hook failed (exit code 1): /etc/boot/hooks/pre.d/10-limine-reset-enroll
The file: /boot/limine.conf is not found.
```

This is because the CachyOS `cachyos-sbctl` package installs a pre-hook that:
1. Checks if a FAT32 partition (ESP) is mounted at `/boot`
2. Checks if `/boot/limine.conf` exists
3. Resets secure boot enrollment keys before the snapshot rollback

### Fatal: the `/boot` fstab entry can be **commented out** by the installer

This is the most common hidden cause. Check first:

```bash
grep -E "(boot|vfat)" /mnt/root/etc/fstab
```

If you see `#UUID=... /boot vfat ...` — it's commented out. Uncomment it:

```bash
sed -i 's|^#UUID=F594-D99E|UUID=F594-D99E|' /mnt/root/etc/fstab
```

The CachyOS Calamares installer sometimes leaves this entry commented, so the ESP never mounts on boot. The snapper hook then fails because `/boot` is empty. This is the root cause of "snapshot restore had worked before but suddenly fails after a reboot."

### Hook file structure

The hook at `/etc/boot/hooks/pre.d/10-limine-reset-enroll` is a **symlink** to `/usr/bin/limine-reset-enroll`:

```bash
ls -la /etc/boot/hooks/pre.d/10-limine-reset-enroll
# → ../usr/bin/limine-reset-enroll
```

It sources `/usr/lib/limine/limine-common-functions` which provides:
- `load_config()` — reads `/etc/default/limine`, `/etc/limine-entry-tool.conf`, etc.
- `check_esp()` — verifies the path is a mounted vfat partition
- `initialize_header()` — orchestrates the check
- `reset_enroll_config()` — resets secure boot enrollment in the Limine EFI binary

### ESP_PATH override via `/etc/default/limine`

The hook's `ESP_PATH` is **forced to `/boot`** via `/etc/default/limine`:

```
ESP_PATH="/boot"
```

This overrides any auto-detection via `bootctl --print-esp-path`. So even when the ESP is correctly mounted at `/boot/efi`, the hook checks `/boot` specifically and fails. The only fix is to mount the ESP at `/boot`.

### Disk identification via UUID (not partition number)

In a live/rescue environment, NVMe device ordering can shuffle (`nvme0n1` vs `nvme1n1`). Always identify partitions by UUID via `blkid`:

```bash
# Find the btrfs root partition
sudo blkid | grep btrfs | grep -v nvme1n1  # exclude Windows drive

# The ESP is the one whose UUID matches the commented/uncommented fstab entry
# In this system: nvme0n1p1 → UUID=F594-D99E, nvme0n1p2 → UUID=ac4ef4ba (btrfs root)
```

For this system:
| UUID | Partition | FS | Role |
|------|-----------|----|------|
| F594-D99E | nvme0n1p1 | vfat 4GB | CachyOS ESP (/boot) |
| ac4ef4ba | nvme0n1p2 | btrfs 950GB | Root with subvols @, @home, @/.snapshots |

### Full fix sequence (from live/rescue environment)

```bash
# 1. Mount the root @ subvol
sudo mount -o subvol=/@ /dev/nvme0n1p2 /mnt/root

# 2. Mount the ESP at /boot
sudo mount /dev/nvme0n1p1 /mnt/root/boot

# 3. Verify the hook can find what it needs
ls /mnt/root/boot/limine.conf
# → must show the file, not "not found"

# 4. Fix the commented-out fstab entry (if present)
grep -E "(boot|vfat)" /mnt/root/etc/fstab
sudo sed -i 's|^#UUID=F594-D99E|UUID=F594-D99E|' /mnt/root/etc/fstab

# 5. Chroot and run the restore
sudo arch-chroot /mnt/root
# snapper -c root undochange <NUM>..0
```

### Quick workaround (skip the hook)

If you just need to get the restore done and will fix fstab later:

```bash
chmod -x /etc/boot/hooks/pre.d/10-limine-reset-enroll
# Run the restore
chmod +x /etc/boot/hooks/pre.d/10-limine-reset-enroll
```

**⚠️ This must be done from within the mounted root** (`/mnt/root/etc/boot/...`), not the live environment's `/etc/...` which is ephemeral overlay.

### Direct Btrfs Subvolume Swap (when snapper undochange won't work)

If snapper `undochange` fails with `subvolume is not a btrfs subvolume` (common in overlay/chroot), replace `@` directly:

```bash
# 1. Mount btrfs top-level
sudo mount -o subvolid=5 /dev/nvme0n1p2 /mnt/btrfs

# 2. Backup broken @
sudo mv /mnt/btrfs/@ /mnt/btrfs/@.broken

# 3. Create writable snapshot from a working one as new @
sudo btrfs subvolume snapshot /mnt/btrfs/@.broken/.snapshots/164/snapshot /mnt/btrfs/@

# 4. Fix fstab in new @ (old broken fstab carried over)
sudo sed -i 's|^#UUID=F594-D99E|UUID=F594-D99E|' /mnt/btrfs/@/etc/fstab

# 5. Mount ESP and verify
sudo mount /dev/nvme0n1p1 /mnt/btrfs/@/boot
ls /mnt/btrfs/@/boot/limine.conf
```

**Caveat:** The `.snapshots` directory is a nested btrfs subvolume (ID 265). In the new `@`, it becomes an empty directory — snapshot history is lost. Old snapshots remain under `@.broken/.snapshots/` if you need them. Delete `@.broken` only after confirming the new `@` boots: `sudo btrfs subvolume delete /mnt/btrfs/@.broken`.

**Delete old snapshots** (from live env, mount @ subvol first):
```bash
for i in $(seq 144 159); do
  btrfs subvolume delete /mnt/real/.snapshots/$i/snapshot 2>/dev/null
  rmdir /mnt/real/.snapshots/$i 2>/dev/null
done
```

Keep at least 5 recent snapshots as rollback targets.

## Live Environment vs Installed System

When booting from a CachyOS live USB:

```
/ → overlay (lowerdir=/sysroot, upperdir=/cowspace_xxx/upper)
/home → btrfs @home subvol (real)
```

- Changes to `/` are lost on reboot (overlay is ephemeral)
- Changes to `/home` persist (it's the real btrfs subvol)
- System config fixes must be written to `/mnt/real/etc/...` after mounting `@`
- User config fixes (KDE, fish, SSH keys) go to `/home/<user>/...` directly
- SSH server config (`/etc/ssh/sshd_config`) in the live environment is ephemeral — to fix SSH for the installed system, edit `/mnt/real/etc/ssh/sshd_config`
