Compare commits

...

7 Commits

Author SHA1 Message Date
Jalopy
7e8a45ca9c Fix boot_uuid_subvolume fallback (#415)
Fix regression affecting systems where boot directory is not a Btrfs subvolume.
2026-02-19 09:21:08 +01:00
Jalopy
14fa71c994 add support for detached LUKS header and UUID fallbacks (#378)
Two hard failures prevented 41-snapshots-btrfs from generating a
snapshot submenu when the root LUKS header is detached and cryptdevice=
uses a /dev/disk/by-id path:

* grub-probe --target=fs_uuid aborted on detached headers.
* grep-based extraction of UUID from GRUB_CMDLINE_LINUX_DEFAULT failed
  when cryptdevice= did not contain “UUID=…”.

This patch:

1. Wraps grub-probe in a try/blkid/lsblk cascade that always returns the
   filesystem UUID or prints a clear error.
2. Replaces the fixed “cryptomount -u $(grep …UUID=…)” line with logic
   that:
   • accepts both UUID=… and /dev/disk/by-id/… syntaxes,
   • resolves paths to a canonical UUID with blkid,
   • emits ‘cryptomount -u <uuid>’ when possible,
   • falls back to ‘cryptomount -a’ only if no UUID can be extracted.
3. Keeps the previous behavior unchanged for unencrypted systems or for
   installations that already worked.

Result: snapshot menu is produced and boots correctly on standard
(setup with inline header), detached-header, and by-id configurations;
no regression for existing users.
2026-01-02 10:52:31 +01:00
Wayne Galen
2fcfbe9676 Ignore Podman container images (#380)
Same basic pattern as with Docker, but Podman uses a slightly different
path for this
2025-09-17 07:41:59 +02:00
TNE
9e171282da Get default early initrd list from GRUB_EARLY_INITRD_LINUX_STOCK (#389)
This mimics the behavior of grub more precisely

Fixes #388
2025-09-17 07:41:05 +02:00
cip91sk
b509fcaf61 add support for booting snapshots on LUKS encrypted disk (#333)
* add support for booting snapshots on LUKS encrypted disk

* documentation for booting from LUKS encrypted devices

* better detecting cryptdevice UUID
2025-01-06 08:11:45 +01:00
Pascal J
f682e17b30 Merge pull request #321 from StollD/set-subvolid
Add support for GRUB patches from SUSE
2024-04-08 17:02:40 +02:00
Dorian Stoll
ece8d87151 Add support for GRUB patches from SUSE
Some GRUBs out there (Fedora, openSUSE) have an option that makes all
paths relative to the default subvolume of the filesystem. This can be
used to include /boot in your snapshots and roll them back without
having to regenerate grub.cfg.

However, enabling that option will break grub-btrfs, because loading the
kernel from a different snapshot requires the paths to be absolute.

To make this work, GRUB has to be told explicitly to access the root
subvolume when booting to a snapshot.
2024-03-10 12:56:37 +01:00
5 changed files with 120 additions and 23 deletions

View File

@@ -107,20 +107,85 @@ esac
if [ -n "${GRUB_BTRFS_PROTECTION_AUTHORIZED_USERS}" ] ; then if [ -n "${GRUB_BTRFS_PROTECTION_AUTHORIZED_USERS}" ] ; then
protection_authorized_users="--users ${GRUB_BTRFS_PROTECTION_AUTHORIZED_USERS} " protection_authorized_users="--users ${GRUB_BTRFS_PROTECTION_AUTHORIZED_USERS} "
fi fi
## Probe information of Root and Boot devices ## Probe information of Root and Boot devices
# Probe info "Root partition" # -----------------------------------------------------------
root_device=$(${grub_probe} --target=device /) # Root device
root_uuid=$(${grub_probe} --device ${root_device} --target="fs_uuid" 2>/dev/null) # UUID of the root device # ---------- Root partition ----------
root_uuid_subvolume=$(btrfs subvolume show / 2>/dev/null) || print_error "UUID of the root subvolume is not available"; # If UUID of root subvolume is not available, then exit root_device="$(${grub_probe} --target=device /)" # e.g. /dev/mapper/enc
root_uuid_subvolume=$(awk -F":" 'match($1, /(^[ \t]+UUID)/) {sub(/^[ \t]+/, "", $2); print $2}' <<< "$root_uuid_subvolume") # UUID of the root subvolume ' root_uuid="$(${grub_probe} --device "${root_device}" --target=fs_uuid 2>/dev/null)" || true
# Probe info "Boot partition"
boot_device=$(${grub_probe} --target=device ${boot_directory}) # Boot device # Fallback when grub-probe fails (encrypted container, detached header…)
boot_uuid=$(${grub_probe} --device ${boot_device} --target="fs_uuid" 2>/dev/null) # UUID of the boot device if [ -z "$root_uuid" ]; then
boot_uuid_subvolume=$(btrfs subvolume show "$boot_directory" 2>/dev/null) || boot_uuid_subvolume=" UUID: $root_uuid_subvolume"; # If boot folder isn't a subvolume, then UUID=root_uuid_subvolume root_uuid="$(blkid -s UUID -o value "${root_device}" 2>/dev/null)"
boot_uuid_subvolume=$(awk -F":" 'match($1, /(^[ \t]+UUID)/) {sub(/^[ \t]+/, "", $2); print $2}' <<< "$boot_uuid_subvolume") # UUID of the boot subvolume ' fi
boot_hs=$(${grub_probe} --device ${boot_device} --target="hints_string" 2>/dev/null) # hints string [ -z "$root_uuid" ] && print_error "Cannot determine UUID of ${root_device}"
boot_fs=$(${grub_probe} --device ${boot_device} --target="fs" 2>/dev/null) # Type filesystem of boot device
# Root subvolume UUID
root_uuid_subvolume="$(btrfs subvolume show / 2>/dev/null | \
awk -F':' '/^\s*UUID/ {gsub(/^[ \t]+/, "", $2); print $2}')"
[ -z "$root_uuid_subvolume" ] && print_error "UUID of the root subvolume is not available"
# ---------- Boot partition ----------
boot_device="$(${grub_probe} --target=device "${boot_directory}")" # e.g. /dev/sdb1
boot_uuid="$(${grub_probe} --device "${boot_device}" --target=fs_uuid 2>/dev/null)" || true
# Fallback for boot UUID
if [ -z "$boot_uuid" ]; then
boot_uuid="$(blkid -s UUID -o value "${boot_device}" 2>/dev/null)"
fi
[ -z "$boot_uuid" ] && print_error "Cannot determine UUID of ${boot_device}"
# If /boot is not a Btrfs subvolume, reuse root subvol UUID
boot_uuid_subvolume="$(
btrfs subvolume show "${boot_directory}" 2>/dev/null | \
awk -F':' '/^\s*UUID/ {gsub(/^[ \t]+/, "", $2); print $2; exit}'
)"
[ -z "$boot_uuid_subvolume" ] && boot_uuid_subvolume="$root_uuid_subvolume"
# Extra data for GRUB commands
boot_hs="$(${grub_probe} --device "${boot_device}" --target=hints_string 2>/dev/null)"
boot_fs="$(${grub_probe} --device "${boot_device}" --target=fs 2>/dev/null)"
# -----------------------------------------------------------
## 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
# Emit 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}")
;;
esac
## Parameters passed to the kernel ## Parameters passed to the kernel
kernel_parameters="$GRUB_CMDLINE_LINUX $GRUB_CMDLINE_LINUX_DEFAULT $GRUB_BTRFS_SNAPSHOT_KERNEL_PARAMETERS" kernel_parameters="$GRUB_CMDLINE_LINUX $GRUB_CMDLINE_LINUX_DEFAULT $GRUB_BTRFS_SNAPSHOT_KERNEL_PARAMETERS"
@@ -218,13 +283,22 @@ make_menu_entries()
if [ x\$feature_all_video_module = xy ]; then if [ x\$feature_all_video_module = xy ]; then
insmod all_video insmod all_video
fi fi
set gfxpayload=keep set gfxpayload=keep"
insmod ${boot_fs} for j in "${insmods[@]}"; do
entry "\
${j}"
done
entry "\
if [ x\$feature_platform_search_hint = xy ]; then if [ x\$feature_platform_search_hint = xy ]; then
search --no-floppy --fs-uuid --set=root ${boot_hs} ${boot_uuid} search --no-floppy --fs-uuid --set=root ${boot_hs} ${boot_uuid}
else else
search --no-floppy --fs-uuid --set=root ${boot_uuid} search --no-floppy --fs-uuid --set=root ${boot_uuid}
fi"
if [ "${SUSE_BTRFS_SNAPSHOT_BOOTING:-"false"}" = "true" ]; then
entry "\
set btrfs_subvolid=5"
fi fi
entry "\
echo 'Loading Snapshot: "${snap_date_trim}" "${snap_dir_name_trim}"' echo 'Loading Snapshot: "${snap_date_trim}" "${snap_dir_name_trim}"'
echo 'Loading Kernel: "${k}" ...' echo 'Loading Kernel: "${k}" ...'
linux \"${boot_dir_root_grub}/"${k}"\" root="${LINUX_ROOT_DEVICE}" ${kernel_parameters} ${rootflags}subvol=\""${snap_dir_name_trim}"\"" linux \"${boot_dir_root_grub}/"${k}"\" root="${LINUX_ROOT_DEVICE}" ${kernel_parameters} ${rootflags}subvol=\""${snap_dir_name_trim}"\""
@@ -259,7 +333,12 @@ make_menu_entries()
search --no-floppy --fs-uuid --set=root ${boot_hs} ${boot_uuid} search --no-floppy --fs-uuid --set=root ${boot_hs} ${boot_uuid}
else else
search --no-floppy --fs-uuid --set=root ${boot_uuid} search --no-floppy --fs-uuid --set=root ${boot_uuid}
fi"
if [ "${SUSE_BTRFS_SNAPSHOT_BOOTING:-"false"}" = "true" ]; then
entry "\
set btrfs_subvolid=5"
fi fi
entry "\
echo 'Loading Snapshot: "${snap_date_trim}" "${snap_dir_name_trim}"' echo 'Loading Snapshot: "${snap_date_trim}" "${snap_dir_name_trim}"'
echo 'Loading Kernel: "${k}" ...' echo 'Loading Kernel: "${k}" ...'
linux \"${boot_dir_root_grub}/"${k}"\" root="${LINUX_ROOT_DEVICE}" ${kernel_parameters} ${rootflags}subvol=\""${snap_dir_name_trim}"\"" linux \"${boot_dir_root_grub}/"${k}"\" root="${LINUX_ROOT_DEVICE}" ${kernel_parameters} ${rootflags}subvol=\""${snap_dir_name_trim}"\""
@@ -318,7 +397,7 @@ snapshot_list()
# Parse Snapper & timeshift & yabsnap information # Parse Snapper & timeshift & yabsnap information
local type_snapshot="N/A" local type_snapshot="N/A"
local description_snapshot="N/A" local description_snapshot="N/A"
# path to yabsnap snapshot meta data # path to yabsnap snapshot meta data
local yabsnap_info="$grub_btrfs_mount_point/${path_snapshot%"/"*}/$(echo "${snap[13]}" | awk -F'/' '{print $3 "-meta.json"}')" local yabsnap_info="$grub_btrfs_mount_point/${path_snapshot%"/"*}/$(echo "${snap[13]}" | awk -F'/' '{print $3 "-meta.json"}')"
@@ -455,12 +534,8 @@ detect_microcode()
list_ucode=() list_ucode=()
# Original intel/amd microcode (auto-detect) # Original intel/amd microcode (auto-detect)
# See "https://www.gnu.org/software/grub/manual/grub/html_node/Simple-configuration.html" # See "https://www.gnu.org/software/grub/manual/grub/html_node/Simple-configuration.html"
for oiucode in "${boot_dir}"/intel-uc.img \ for oiucode in ${GRUB_EARLY_INITRD_LINUX_STOCK} ; do
"${boot_dir}"/intel-ucode.img \ oiucode="${boot_dir}/${oiucode}"
"${boot_dir}"/amd-uc.img \
"${boot_dir}"/amd-ucode.img \
"${boot_dir}"/early_ucode.cpio \
"${boot_dir}"/microcode.cpio; do
[ ! -f "${oiucode}" ] && continue; [ ! -f "${oiucode}" ] && continue;
list_ucode+=("$oiucode") list_ucode+=("$oiucode")
done done
@@ -531,6 +606,7 @@ boot_bounded()
detect_rootflags detect_rootflags
title_format title_format
boot_dir_root_grub="$(make_system_path_relative_to_its_root "${boot_dir}")" # convert "boot_directory" to root of GRUB (e.g /boot become /) boot_dir_root_grub="$(make_system_path_relative_to_its_root "${boot_dir}")" # convert "boot_directory" to root of GRUB (e.g /boot become /)
insmods=("${list_insmods[@]##*"/"}")
make_menu_entries make_menu_entries
# show snapshot found during run "grub-mkconfig" # show snapshot found during run "grub-mkconfig"
if [ "${GRUB_BTRFS_SHOW_SNAPSHOTS_FOUND:-"true"}" = "true" ]; then if [ "${GRUB_BTRFS_SHOW_SNAPSHOTS_FOUND:-"true"}" = "true" ]; then

View File

@@ -274,6 +274,10 @@ After that, the daemon should be restarted with:
sudo rc-service grub-btrfsd restart sudo rc-service grub-btrfsd restart
``` ```
##### 🔒 Snapshots on LUKS encrypted devices
By default, grub-btrfs generates entries that does not load modules for dealing with encrypted devices.
Enable the `GRUB_BTRFS_ENABLE_CRYPTODISK` variable in `/etc/default/grub-btrfs/config` to load said modules and then execute the steps to mount encrypted root after selecting the snapshot.
- - - - - -
### Troubleshooting ### Troubleshooting
If you experience problems with grub-btrfs don't hesitate [to file an issue](https://github.com/Antynea/grub-btrfs/issues/new/choose). If you experience problems with grub-btrfs don't hesitate [to file an issue](https://github.com/Antynea/grub-btrfs/issues/new/choose).

6
config
View File

@@ -74,7 +74,7 @@ GRUB_BTRFS_IGNORE_SPECIFIC_PATH=("@")
# Any path starting with the specified string will be ignored. # Any path starting with the specified string will be ignored.
# e.g : if `prefix path` = @, all snapshots beginning with "@/..." will be ignored. # e.g : if `prefix path` = @, all snapshots beginning with "@/..." will be ignored.
# Default: ("var/lib/docker" "@var/lib/docker" "@/var/lib/docker") # Default: ("var/lib/docker" "@var/lib/docker" "@/var/lib/docker")
GRUB_BTRFS_IGNORE_PREFIX_PATH=("var/lib/docker" "@var/lib/docker" "@/var/lib/docker") GRUB_BTRFS_IGNORE_PREFIX_PATH=("var/lib/docker" "@var/lib/docker" "@/var/lib/docker" "var/lib/containers" "@var/lib/containers" "@/var/lib/containers")
# Ignore specific type/tag of snapshot during run "grub-mkconfig". # Ignore specific type/tag of snapshot during run "grub-mkconfig".
# For snapper: # For snapper:
@@ -158,3 +158,7 @@ GRUB_BTRFS_IGNORE_PREFIX_PATH=("var/lib/docker" "@var/lib/docker" "@/var/lib/doc
# doesn't work if GRUB_BTRFS_PROTECTION_AUTHORIZED_USERS isn't empty # doesn't work if GRUB_BTRFS_PROTECTION_AUTHORIZED_USERS isn't empty
# Default: "false" # Default: "false"
#GRUB_BTRFS_DISABLE_PROTECTION_SUBMENU="true" #GRUB_BTRFS_DISABLE_PROTECTION_SUBMENU="true"
# Enable booting from snapshots stored on LUKS encrypted devices
# Default: "false"
#GRUB_BTRFS_ENABLE_CRYPTODISK="true"

View File

@@ -102,6 +102,14 @@ Default: “false”
.IP \(em 4 .IP \(em 4
Example: \fCGRUB_BTRFS_OVERRIDE_BOOT_PARTITION_DETECTION="true"\fP Example: \fCGRUB_BTRFS_OVERRIDE_BOOT_PARTITION_DETECTION="true"\fP
.SS "\GRUB_BTRFS_ENABLE_CRYPTODISK\fP"
.PP
Enable booting from snapshots stored on LUKS encrypted devices
.IP \(em 4
Default: “false”
.IP \(em 4
Example: \GRUB_BTRFS_ENABLE_CRYPTODISK="true"\fP
.SS "CUSTOM KERNELS" .SS "CUSTOM KERNELS"
.SS "\fCGRUB_BTRFS_NKERNEL\fP / \fCGRUB_BTRFS_NINIT\fP / \fCGRUB_BTRFS_CUSTOM_MICROCODE\fP" .SS "\fCGRUB_BTRFS_NKERNEL\fP / \fCGRUB_BTRFS_NINIT\fP / \fCGRUB_BTRFS_CUSTOM_MICROCODE\fP"
.PP .PP

View File

@@ -73,6 +73,11 @@ Change to "true" if your boot partition is not detected as separate.
- Default: "false" - Default: "false"
- Example: ~GRUB_BTRFS_OVERRIDE_BOOT_PARTITION_DETECTION="true"~ - Example: ~GRUB_BTRFS_OVERRIDE_BOOT_PARTITION_DETECTION="true"~
*** ~GRUB_BTRFS_ENABLE_CRYPTODISK~
Enable booting from snapshots stored on LUKS encrypted devices
- Default: "false"
- Example: ~GRUB_BTRFS_ENABLE_CRYPTODISK="true"~
** CUSTOM KERNELS ** CUSTOM KERNELS
*** ~GRUB_BTRFS_NKERNEL~ / ~GRUB_BTRFS_NINIT~ / ~GRUB_BTRFS_CUSTOM_MICROCODE~ *** ~GRUB_BTRFS_NKERNEL~ / ~GRUB_BTRFS_NINIT~ / ~GRUB_BTRFS_CUSTOM_MICROCODE~