-
Couldn't load subscription status.
- Fork 90
Description
Environment
| Item | Value |
|---|---|
| Distro | Arch Linux |
| grub-btrfs | 4.13-yabsnap_info_support-2024-03-06 |
| GRUB | grub-improved-luks2-git 2.12.r303.g86e8f2c4b-1 |
| btrfs-progs | 6.15 |
| Snapper | 0.12.2 |
| Layout | single encrypted container on SSD, no partitions, formatted with Btrfs → LUKS (header on USB) → same USB stick holds /boot (vfat) |
| Devices | /dev/disk/by-id/<REDACTED> → /dev/mapper/enc (UUID =REDACTED)/dev/sdb1 (USB, UUID =REDACTED) → /boot & header |
[user@hostname ~]$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 953.9G 0 disk
└─enc 254:0 0 953.9G 0 crypt /.snapshots
/var
/home
/pkg
/swap
/
sdb 8:16 1 29.1G 0 disk
└─sdb1 8:17 1 1G 0 part /boot
Symptom
Running
sudo grub-mkconfig -o /boot/grub/grub.cfgterminates during execution of 41_snapshots-btrfs with only
Detecting snapshots ...When set -x tracing is enabled the last command executed is
root_uuid=$(${grub_probe} --device ${root_device} --target=fs_uuid)which fails with
/usr/bin/grub-probe: error: cannot get cryptodisk from source disk `hostdisk//dev/sda'.
Internal error: Unreleased memory pool(s) found.No grub-btrfs.cfg is produced, so the snapshot submenu never appears
Why grub-probe --target=fs_uuid can fail
I would first like to explain why grub-probe --target=fs_uuid can fail in this scenario
-
grub-probe --target=fs_uuidtries to parse the LUKS header in-place on the disk. -
When it finds the disk it expects to find a LUKS header in the
first sector (inline header).
With a detached header that sector is just random data, so parsing fails
andgrub-probeexits.
My solution to grub-probe --target=fs_uuid failing
# 1. Try the original, fast path first
root_uuid=$(${grub_probe} --device "${root_device}" --target=fs_uuid 2>/dev/null) || true
# └──────────────────┬───────────────────────────────────────────────┘
# may fail → we ignore the exit code with `|| true`
# 2. Fallback 1: blkid
[ -z "$root_uuid" ] && root_uuid=$(blkid -s UUID -o value "${root_device}")
# 3. Fallback 2: lsblk (rarely needed, but covers busybox systems without blkid)
[ -z "$root_uuid" ] && root_uuid=$(lsblk -no UUID "${root_device}")
# 4. Hard stop if we *still* have nothing
[ -z "$root_uuid" ] && print_error "Cannot determine UUID of ${root_device}"
Same block is applied to boot_uuid.
This does not change the underlying logic so script will continue on both inline-header and detached-header systems.
What this achieves
-
Normal systems (inline header) still use GRUB’s own probe
-
Detached-header or exotic file-system configurations fall back to kernel knowledge
-
Script exits with a error message only if all methods fail
Continuing the debugging.
After applying the above patch I re-ran 41_snapshots-btrfs with the set -x flag to continue monitoring execution tracing.
I was then caught up on this command
list_insmods+=("cryptomount -u $(echo $GRUB_CMDLINE_LINUX_DEFAULT | grep -o -P '(?<=cryptdevice=UUID=).*(?=:cryptdev)')")It was failing to grep the UUID from GRUB_CMDLINE_LINUX_DEFAULT string in /etc/default/grub because it didnt exist there. GRUB_CMDLINE_LINUX_DEFAULT on my system is only loglevel=3 quiet so it has no cryptdevice= string.
My cryptdevice= string is part of GRUB_CMDLINE_LINUX.
also with a detached header Arch recommends:
cryptdevice=/dev/disk/by-id/<SSD>:enc:headerso grep '(?<=cryptdevice=UUID=)…' would return nothing anyways and grep exits so script
dies again.
You may see why this caused incompatibility with the script so the following patch block was made.
Cryptomount patch block and changes
The following code block snippet
# Enable LUKS encrypted devices support
case "$(echo "$GRUB_BTRFS_ENABLE_CRYPTODISK" | tr '[:upper:]' '[:lower:]')" in
true)
list_insmods=()
list_insmods+=("insmod gzio")
list_insmods+=("insmod part_gpt")
list_insmods+=("insmod cryptodisk")
list_insmods+=("insmod luks")
list_insmods+=("insmod gcry_rijndael")
list_insmods+=("insmod gcry_rijndael")
list_insmods+=("insmod gcry_sha256")
list_insmods+=("insmod ${boot_fs}")
list_insmods+=("cryptomount -u $(echo $GRUB_CMDLINE_LINUX_DEFAULT | grep -o -P '(?<=cryptdevice=UUID=).*(?=:cryptdev)')")
;;
*)
list_insmods=("insmod ${boot_fs}")
;;
esacis replaced with
## Enable LUKS encrypted devices support
case "$(echo "$GRUB_BTRFS_ENABLE_CRYPTODISK" | tr '[:upper:]' '[:lower:]')" in
true)
list_insmods=(
"insmod gzio"
"insmod part_gpt"
"insmod cryptodisk"
"insmod luks"
"insmod gcry_rijndael"
"insmod gcry_rijndael"
"insmod gcry_sha256"
"insmod ${boot_fs}"
)
# Extract the <source> field of cryptdevice=<source>:<name>[:header]
crypt_source="$(printf '%s %s\n' "$GRUB_CMDLINE_LINUX_DEFAULT" "$GRUB_CMDLINE_LINUX" \
| grep -o -P 'cryptdevice=\K[^:]+' || true)"
# Turn the source into a UUID that cryptomount -u understands
crypt_uuid=""
if [[ "$crypt_source" =~ ^UUID=.* ]]; then # already UUID=…
crypt_uuid="${crypt_source#UUID=}"
elif [[ "$crypt_source" == /dev/* ]]; then # path → resolve → blkid
real_dev=$(readlink -f "$crypt_source" 2>/dev/null || true)
[ -b "$real_dev" ] && crypt_uuid=$(blkid -s UUID -o value "$real_dev" 2>/dev/null || true)
fi
# Exec the proper cryptomount command
if [[ "$crypt_uuid" =~ ^[0-9a-fA-F-]{36}$ ]]; then
list_insmods+=("cryptomount -u ${crypt_uuid}")
else
# last-resort: scan all crypto containers (works but a bit slower)
list_insmods+=("cryptomount -a")
fi
;;
*)
list_insmods=("insmod ${boot_fs}")
;;
esacThis section -
crypt_source="$(printf '%s %s\n' "$GRUB_CMDLINE_LINUX_DEFAULT" \
"$GRUB_CMDLINE_LINUX" \
| grep -o -P 'cryptdevice=\K[^:]+' || true)"-
Extracts the
<source>part ofcryptdevice= -
Combines both GRUB cmdline variables (
DEFAULTandLINUX). -
grep -o -P 'cryptdevice=\K[^:]+'keeps everything between
cryptdevice=and the next:.
Matches both forms:-
cryptdevice=UUID=1111-2222-…:→ returnsUUID=1111-2222-… -
cryptdevice=/dev/disk/by-id/nvme-SN123:→ returns/dev/disk/by-id/…
-
-
|| truepreventsset -efrom killing the script if no match is found.
For this snippet -
crypt_uuid=""
if [[ "$crypt_source" =~ ^UUID=.* ]]; then
crypt_uuid="${crypt_source#UUID=}" # strip leading “UUID=”
elif [[ "$crypt_source" == /dev/* ]]; then
real_dev=$(readlink -f "$crypt_source" 2>/dev/null || true) # resolve symlinks
[ -b "$real_dev" ] && # ensure block device
crypt_uuid=$(blkid -s UUID -o value "$real_dev" 2>/dev/null || true)
fiWe try to extract a usable UUID that grub can understand.
For instance,
-
We resolved a
UUID=…so we just remove the prefix. -
we've resolved a device path (
/dev/disk/by-id/…):
check symlinks and askblkidfor its file-system UUID.. -
If neither yields a UUID,
crypt_uuidremains empty.
Next we have -
if [[ "$crypt_uuid" =~ ^[0-9a-fA-F-]{36}$ ]]; then
list_insmods+=("cryptomount -u ${crypt_uuid}") # fast, direct
else
list_insmods+=("cryptomount -a") # fallback autoscan
fiHere we append the correct cryptomount command depending on if we found a UUID or not.
cryptomount -u <uuid>if we found a UUID
orcryptomount -aif we didn't.
cryptomount -a scans every disk for LUKS headers. This is kinda like a last resort
Outcome
Boom
[user@hostname ~]$ sudo grub-mkconfig -o /boot/grub/grub.cfg
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-linux-hardened
Found initrd image: /boot/initramfs-linux-hardened.img
Found fallback initrd image(s) in /boot: initramfs-linux-hardened-fallback.img
Warning: os-prober will be executed to detect other bootable partitions.
Its output will be used to detect bootable binaries on them and create new boot entries.
Found Windows Boot Manager on /dev/nvme0n1p1@/efi/Microsoft/Boot/bootmgfw.efi
Adding boot menu entry for UEFI Firmware Settings ...
Detecting snapshots ...
/usr/bin/grub-probe: error: cannot get cryptodisk from source disk `hostdisk//dev/sda'.
You have a memory leak (not released memory pool):
[0x78b0c2866b0] dtree
Internal error: Unreleased memory pool(s) found.
Found snapshot: 2025-07-08 03:00:41 | @snapshots/17/snapshot | single | timeline |
Found snapshot: 2025-07-08 02:00:01 | @snapshots/16/snapshot | single | timeline |
Found snapshot: 2025-07-08 01:00:46 | @snapshots/15/snapshot | single | timeline |
Found snapshot: 2025-07-08 00:00:57 | @snapshots/14/snapshot | single | timeline |
Found snapshot: 2025-07-07 23:00:27 | @snapshots/13/snapshot | single | timeline |
Found snapshot: 2025-07-07 22:00:27 | @snapshots/12/snapshot | single | timeline |
Found snapshot: 2025-07-07 21:50:35 | @snapshots/11/snapshot | single | timeline |
Found snapshot: 2025-07-07 16:00:01 | @snapshots/10/snapshot | single | timeline |
Found snapshot: 2025-07-07 15:00:26 | @snapshots/9/snapshot | single | timeline |
Found snapshot: 2025-07-07 14:00:04 | @snapshots/8/snapshot | single | timeline |
Found snapshot: 2025-07-07 13:00:15 | @snapshots/7/snapshot | single | timeline |
Found snapshot: 2025-07-07 12:00:37 | @snapshots/6/snapshot | single | timeline |
Found snapshot: 2025-07-07 11:00:07 | @snapshots/5/snapshot | single | timeline |
Found snapshot: 2025-07-07 10:00:08 | @snapshots/4/snapshot | single | timeline |
Found snapshot: 2025-07-07 09:33:14 | timeshift-btrfs/snapshots/2025-07-07_09-33-14/@ | ondemand | {timeshift-autosnap} {created before upgrade} |
Found snapshot: 2025-07-07 09:12:08 | @snapshots/3/snapshot | single | test snapshot 2 |
Found snapshot: 2025-07-07 09:00:10 | @snapshots/2/snapshot | single | timeline |
Found snapshot: 2025-07-07 08:59:43 | @snapshots/1/snapshot | single | test snapshot |
Found 18 snapshot(s)
Unmount /tmp/grub-btrfs.tAroDMMCMK .. Success
doneMenu generation succeeds, 18 snapshot entries appear.
grub-btrfs.cfg is created and passed script checks.
Im sure you've noticed there's that same grub-probe error from before but its no longer an issue because we extract the UUID using blkid in my scenario, for normal users with inline-header that command wont fail the check and that error wont appear.
I've been struggling with this issue for a couple years now and finally learned enough to remedy the problem. I'll be making a pull request soon but knowing it might not get merged soon or even at all I've decided to make this issue to hopefully help the next person or somebody that's facing a similar issue.