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 149 additions and 73 deletions

View File

@@ -1,4 +1,4 @@
#! /usr/bin/env sh #! /usr/bin/env bash
# #
# Written by: Antynea # Written by: Antynea
# BTC donation address: 1Lbvz244WA8xbpHek9W2Y12cakM6rDe5Rt # BTC donation address: 1Lbvz244WA8xbpHek9W2Y12cakM6rDe5Rt
@@ -41,16 +41,14 @@ set -e
sysconfdir="/etc" sysconfdir="/etc"
grub_btrfs_config="${sysconfdir}/default/grub-btrfs/config" grub_btrfs_config="${sysconfdir}/default/grub-btrfs/config"
# shellcheck disable=SC1090
[ -f "$grub_btrfs_config" ] && . "$grub_btrfs_config" [ -f "$grub_btrfs_config" ] && . "$grub_btrfs_config"
# shellcheck disable=SC1091
[ -f "${sysconfdir}/default/grub" ] && . "${sysconfdir}/default/grub" [ -f "${sysconfdir}/default/grub" ] && . "${sysconfdir}/default/grub"
## Error Handling ## Error Handling
print_error() print_error()
{ {
err_msg="$*" local err_msg="$*"
bug_report="If you think an error has occurred, please file a bug report at \"https://github.com/Antynea/grub-btrfs\"" local bug_report="If you think an error has occurred, please file a bug report at \"https://github.com/Antynea/grub-btrfs\""
printf "%s\n" "${err_msg}" "${bug_report}" >&2 ; printf "%s\n" "${err_msg}" "${bug_report}" >&2 ;
exit 0 exit 0
} }
@@ -79,10 +77,8 @@ done
## Exit the script, if: ## Exit the script, if:
[ "$(echo "$GRUB_BTRFS_DISABLE" | tr '[:upper:]' '[:lower:]')" = 'true' ] && print_error "GRUB_BTRFS_DISABLE is set to true (default=false)" [ "$(echo "$GRUB_BTRFS_DISABLE" | tr '[:upper:]' '[:lower:]')" = 'true' ] && print_error "GRUB_BTRFS_DISABLE is set to true (default=false)"
if ! type btrfs >/dev/null 2>&1; then print_error "btrfs-progs isn't installed"; fi if ! type btrfs >/dev/null 2>&1; then print_error "btrfs-progs isn't installed"; fi
# shellcheck disable=SC1090,SC2015
[ -f "${GRUB_BTRFS_MKCONFIG_LIB:-/usr/share/grub/grub-mkconfig_lib}" ] && . "${GRUB_BTRFS_MKCONFIG_LIB:-/usr/share/grub/grub-mkconfig_lib}" || print_error "grub-mkconfig_lib couldn't be found" [ -f "${GRUB_BTRFS_MKCONFIG_LIB:-/usr/share/grub/grub-mkconfig_lib}" ] && . "${GRUB_BTRFS_MKCONFIG_LIB:-/usr/share/grub/grub-mkconfig_lib}" || print_error "grub-mkconfig_lib couldn't be found"
# shellcheck disable=SC2005 [[ "$(btrfs filesystem df / 2>&1)" == *"not a btrfs filesystem"* ]] && print_error "Root filesystem isn't btrfs"
if echo "$(btrfs filesystem df / 2>&1)" | grep "not a btrfs filesystem" >/dev/null 2>&1; then print_error "Root filesystem isn't btrfs"; fi
printf "Detecting snapshots ...\n" >&2 ; printf "Detecting snapshots ...\n" >&2 ;
@@ -111,32 +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" # -----------------------------------------------------------
# shellcheck disable=SC2154 # grub_probe is provided by grub environment
root_device=$(${grub_probe} --target=device /) # Root device # ---------- Root partition ----------
# shellcheck disable=SC2086 # we actually need word splitting here if we have several root devices (e.g. RAID) root_device="$(${grub_probe} --target=device /)" # e.g. /dev/mapper/enc
root_uuid=$(${grub_probe} --device ${root_device} --target="fs_uuid" 2>/dev/null) # UUID of the root device root_uuid="$(${grub_probe} --device "${root_device}" --target=fs_uuid 2>/dev/null)" || true
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_uuid_subvolume=$(awk -F":" 'match($1, /(^[ \t]+UUID)/) {sub(/^[ \t]+/, "", $2); print $2}' <<EOF # Fallback when grub-probe fails (encrypted container, detached header…)
"$root_uuid_subvolume" if [ -z "$root_uuid" ]; then
EOF root_uuid="$(blkid -s UUID -o value "${root_device}" 2>/dev/null)"
) # UUID of the root subvolume ' fi
# Probe info "Boot partition" [ -z "$root_uuid" ] && print_error "Cannot determine UUID of ${root_device}"
# shellcheck disable=SC2086 # we actually need word splitting here if we have several devices
boot_device=$(${grub_probe} --target=device ${boot_directory}) # Boot device # Root subvolume UUID
# shellcheck disable=SC2086 # we actually need word splitting here if we have several devices root_uuid_subvolume="$(btrfs subvolume show / 2>/dev/null | \
boot_uuid=$(${grub_probe} --device ${boot_device} --target="fs_uuid" 2>/dev/null) # UUID of the boot device awk -F':' '/^\s*UUID/ {gsub(/^[ \t]+/, "", $2); print $2}')"
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 [ -z "$root_uuid_subvolume" ] && print_error "UUID of the root subvolume is not available"
boot_uuid_subvolume=$(awk -F":" 'match($1, /(^[ \t]+UUID)/) {sub(/^[ \t]+/, "", $2); print $2}' <<EOF
"$boot_uuid_subvolume" # ---------- Boot partition ----------
EOF boot_device="$(${grub_probe} --target=device "${boot_directory}")" # e.g. /dev/sdb1
) # UUID of the boot subvolume ' boot_uuid="$(${grub_probe} --device "${boot_device}" --target=fs_uuid 2>/dev/null)" || true
# shellcheck disable=SC2086 # we actually need word splitting here if we have several devices
boot_hs=$(${grub_probe} --device ${boot_device} --target="hints_string" 2>/dev/null) # hints string # Fallback for boot UUID
# shellcheck disable=SC2086 # we actually need word splitting here if we have several devices if [ -z "$boot_uuid" ]; then
boot_fs=$(${grub_probe} --device ${boot_device} --target="fs" 2>/dev/null) # Type filesystem of boot device 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"
@@ -159,7 +208,7 @@ fi
## Detect rootflags ## Detect rootflags
detect_rootflags() detect_rootflags()
{ {
fstabflags=$(grep -oE '^\s*[^#][[:graph:]]+\s+/\s+btrfs\s+[[:graph:]]+' "${grub_btrfs_mount_point}/${snap_dir_name_trim}/etc/fstab" \ local fstabflags=$(grep -oE '^\s*[^#][[:graph:]]+\s+/\s+btrfs\s+[[:graph:]]+' "${grub_btrfs_mount_point}/${snap_dir_name_trim}/etc/fstab" \
| sed -E 's/^.*[[:space:]]([[:graph:]]+)$/\1/;s/,?subvol(id)?=[^,$]+//g;s/^,//') | sed -E 's/^.*[[:space:]]([[:graph:]]+)$/\1/;s/,?subvol(id)?=[^,$]+//g;s/^,//')
rootflags="rootflags=${fstabflags:+$fstabflags,}${GRUB_BTRFS_ROOTFLAGS:+$GRUB_BTRFS_ROOTFLAGS,}" rootflags="rootflags=${fstabflags:+$fstabflags,}${GRUB_BTRFS_ROOTFLAGS:+$GRUB_BTRFS_ROOTFLAGS,}"
} }
@@ -167,8 +216,8 @@ detect_rootflags()
unmount_grub_btrfs_mount_point() unmount_grub_btrfs_mount_point()
{ {
if [ -d "$grub_btrfs_mount_point" ]; then if [ -d "$grub_btrfs_mount_point" ]; then
wait=true local wait=true
wait_max=0 local wait_max=0
printf "Unmount %s .." "$grub_btrfs_mount_point" >&2; printf "Unmount %s .." "$grub_btrfs_mount_point" >&2;
while $wait; do while $wait; do
if grep -qs "$grub_btrfs_mount_point" /proc/mounts; then if grep -qs "$grub_btrfs_mount_point" /proc/mounts; then
@@ -216,7 +265,6 @@ make_menu_entries()
# prefix_i=${i%%"-"*} # prefix_i=${i%%"-"*}
suffix_i=${i#*"-"} suffix_i=${i#*"-"}
# alt_suffix_i=${i##*"-"} # alt_suffix_i=${i##*"-"}
# shellcheck disable=SC2269 # this is a way to exit the if..elif..
if [ "${kversion}" = "${suffix_i}" ]; then i="${i}"; if [ "${kversion}" = "${suffix_i}" ]; then i="${i}";
elif [ "${kversion}.img" = "${suffix_i}" ]; then i="${i}"; elif [ "${kversion}.img" = "${suffix_i}" ]; then i="${i}";
elif [ "${kversion}-fallback.img" = "${suffix_i}" ]; then i="${i}"; elif [ "${kversion}-fallback.img" = "${suffix_i}" ]; then i="${i}";
@@ -235,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}"\""
@@ -276,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}"\""
@@ -296,21 +358,21 @@ make_menu_entries()
## Trim a string from leading and trailing whitespaces ## Trim a string from leading and trailing whitespaces
trim() { trim() {
var="$*" local var="$*"
var="${var#"${var%%[![:space:]]*}"}" var="${var#"${var%%[![:space:]]*}"}"
var="${var%"${var##*[![:space:]]}"}" var="${var%"${var##*[![:space:]]}"}"
printf '%s' "$var" echo -n "$var"
} }
## List of snapshots on filesystem ## List of snapshots on filesystem
snapshot_list() snapshot_list()
{ {
snapper_info="info.xml" local snapper_info="info.xml"
timeshift_info="info.json" local timeshift_info="info.json"
date_snapshots=() local date_snapshots=()
path_snapshots=() local path_snapshots=()
type_snapshots=() local type_snapshots=()
description_snapshots=() local description_snapshots=()
IFS=$'\n' IFS=$'\n'
for snap in $(btrfs subvolume list -sa "${btrfs_subvolume_sort}" /); do # Parse btrfs snapshots for snap in $(btrfs subvolume list -sa "${btrfs_subvolume_sort}" /); do # Parse btrfs snapshots
IFS=$oldIFS IFS=$oldIFS
@@ -335,10 +397,9 @@ 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 local yabsnap_info="$grub_btrfs_mount_point/${path_snapshot%"/"*}/$(echo "${snap[13]}" | awk -F'/' '{print $3 "-meta.json"}')"
yabsnap_info="$grub_btrfs_mount_point/${path_snapshot%"/"*}/$(echo "${snap[13]}" | awk -F'/' '{print $3 "-meta.json"}')"
if [[ -s "$grub_btrfs_mount_point/${path_snapshot%"/"*}/$snapper_info" ]] ; then if [[ -s "$grub_btrfs_mount_point/${path_snapshot%"/"*}/$snapper_info" ]] ; then
type_snapshot=$(awk -F"<|>" 'match($2, /^type/) {print $3}' "$grub_btrfs_mount_point/${path_snapshot%"/"*}/$snapper_info") # search matching string beginning "type" type_snapshot=$(awk -F"<|>" 'match($2, /^type/) {print $3}' "$grub_btrfs_mount_point/${path_snapshot%"/"*}/$snapper_info") # search matching string beginning "type"
@@ -417,31 +478,30 @@ parse_snapshot_list()
snap_dir_name="$(echo "$item" | cut -d'|' -f2)" # column_2 snap_dir_name="$(echo "$item" | cut -d'|' -f2)" # column_2
snap_dir_name_trim="$(trim "$snap_dir_name")" snap_dir_name_trim="$(trim "$snap_dir_name")"
# shellcheck disable=SC2034 # Used by "title_format" function snap_snapshot="$snap_dir_name" # Used by "title_format" function
snap_snapshot="$snap_dir_name"
# shellcheck disable=SC2034 # Used by "title_format" function
snap_type="$(echo "$item" | cut -d'|' -f3)" # column_3 snap_type="$(echo "$item" | cut -d'|' -f3)" # column_3
# shellcheck disable=SC2034 # Used by "title_format" function
snap_description="$(echo "$item" | cut -d'|' -f4)" # column_4 snap_description="$(echo "$item" | cut -d'|' -f4)" # column_4
} }
## Detect kernels in "boot_directory" ## Detect kernels in "boot_directory"
detect_kernel() detect_kernel()
{ {
list_kernel="" list_kernel=()
# Original kernel (auto-detect) # Original kernel (auto-detect)
for okernel in "${boot_dir}"/vmlinuz-* \ for okernel in "${boot_dir}"/vmlinuz-* \
"${boot_dir}"/vmlinux-* \ "${boot_dir}"/vmlinux-* \
"${boot_dir}"/kernel-* ; do "${boot_dir}"/kernel-* ; do
[ ! -f "${okernel}" ] && continue; [ ! -f "${okernel}" ] && continue;
list_kernel="${list_kernel} $okernel" list_kernel+=("$okernel")
done done
# Custom name kernel in "GRUB_BTRFS_NKERNEL" # Custom name kernel in "GRUB_BTRFS_NKERNEL"
if [ -n "${GRUB_BTRFS_NKERNEL}" ] ; then if [ -n "${GRUB_BTRFS_NKERNEL}" ] ; then
for ckernel in "${boot_dir}"/${GRUB_BTRFS_NKERNEL} ; do for ckernel in "${boot_dir}/${GRUB_BTRFS_NKERNEL[@]}" ; do
[ ! -f "${ckernel}" ] && continue; [ ! -f "${ckernel}" ] && continue;
list_kernel="${list_kernel} $okernel" list_kernel+=("$ckernel")
done done
fi fi
} }
@@ -474,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
@@ -487,7 +543,7 @@ detect_microcode()
# Custom name microcode in "GRUB_BTRFS_CUSTOM_MICROCODE" # Custom name microcode in "GRUB_BTRFS_CUSTOM_MICROCODE"
if [ -n "${GRUB_BTRFS_CUSTOM_MICROCODE}" ] ; then if [ -n "${GRUB_BTRFS_CUSTOM_MICROCODE}" ] ; then
for cucode in "${boot_dir}/${GRUB_BTRFS_CUSTOM_MICROCODE[@]}" ; do for cucode in "${boot_dir}/${GRUB_BTRFS_CUSTOM_MICROCODE[@]}" ; do
[ ! -f "${cucode}" ] && continue [[ ! -f "${cucode}" ]] && continue
list_ucode+=("$cucode") list_ucode+=("$cucode")
done done
fi fi
@@ -500,7 +556,7 @@ title_format()
{ {
title_menu="|" # "|" is for visuals only title_menu="|" # "|" is for visuals only
title_submenu="|" # "|" is for visuals only title_submenu="|" # "|" is for visuals only
[ -z "${GRUB_BTRFS_TITLE_FORMAT}" ] && GRUB_BTRFS_TITLE_FORMAT=("date" "snapshot" "type" "description"); # Default parameters [[ -z "${GRUB_BTRFS_TITLE_FORMAT}" ]] && GRUB_BTRFS_TITLE_FORMAT=("date" "snapshot" "type" "description"); # Default parameters
for key in "${!GRUB_BTRFS_TITLE_FORMAT[@]}"; do for key in "${!GRUB_BTRFS_TITLE_FORMAT[@]}"; do
[[ ${GRUB_BTRFS_TITLE_FORMAT[$key],,} != "${title_column[${GRUB_BTRFS_TITLE_FORMAT[$key]}],,}" ]] && continue; # User used wrong parameter [[ ${GRUB_BTRFS_TITLE_FORMAT[$key],,} != "${title_column[${GRUB_BTRFS_TITLE_FORMAT[$key]}],,}" ]] && continue; # User used wrong parameter
declare -n var="snap_${GRUB_BTRFS_TITLE_FORMAT[$key],,}" # $var is a indirect variable declare -n var="snap_${GRUB_BTRFS_TITLE_FORMAT[$key],,}" # $var is a indirect variable
@@ -515,15 +571,15 @@ title_format()
# Adds a header to the grub-btrfs.cfg file # Adds a header to the grub-btrfs.cfg file
header_menu() header_menu()
{ {
header_entry="" local header_entry=""
[ -z "${GRUB_BTRFS_TITLE_FORMAT}" ] && GRUB_BTRFS_TITLE_FORMAT=("date" "snapshot" "type" "description"); # Default parameters [[ -z "${GRUB_BTRFS_TITLE_FORMAT}" ]] && GRUB_BTRFS_TITLE_FORMAT=("date" "snapshot" "type" "description"); # Default parameters
for key in "${!GRUB_BTRFS_TITLE_FORMAT[@]}"; do for key in "${!GRUB_BTRFS_TITLE_FORMAT[@]}"; do
[[ ${GRUB_BTRFS_TITLE_FORMAT[$key],,} != "${title_column[${GRUB_BTRFS_TITLE_FORMAT[$key]}],,}" ]] && continue; # User used wrong parameter [[ ${GRUB_BTRFS_TITLE_FORMAT[$key],,} != "${title_column[${GRUB_BTRFS_TITLE_FORMAT[$key]}],,}" ]] && continue; # User used wrong parameter
declare -n var="snap_${GRUB_BTRFS_TITLE_FORMAT[$key],,}" # $var is a indirect variable declare -n var="snap_${GRUB_BTRFS_TITLE_FORMAT[$key],,}" # $var is a indirect variable
# Center alignment, needed for pretty formatting # Center alignment, needed for pretty formatting
local lenght_title_column_left=$((${#var}-${#title_column[${GRUB_BTRFS_TITLE_FORMAT[$key],,}]})) local lenght_title_column_left=$((${#var}-${#title_column[${GRUB_BTRFS_TITLE_FORMAT[$key],,}]}))
((lenght_title_column_left%2)) && lenght_title_column_left=$((lenght_title_column_left+1)); # If the difference is an odd number, add an extra space ((lenght_title_column_left%2)) && lenght_title_column_left=$((lenght_title_column_left+1)); # If the difference is an odd number, add an extra space
lenght_title_column_left=$(((lenght_title_column_left/2)+${#title_column[${GRUB_BTRFS_TITLE_FORMAT[$key],,}]})); lenght_title_column_left=$((((lenght_title_column_left/2)+${#title_column[${GRUB_BTRFS_TITLE_FORMAT[$key],,}]})));
local lenght_title_column_right=$(((${#var}-lenght_title_column_left)+1)) #+1 is necessary for extra "|" character local lenght_title_column_right=$(((${#var}-lenght_title_column_left)+1)) #+1 is necessary for extra "|" character
header_entry+=$(printf "%${lenght_title_column_left}s%${lenght_title_column_right}s" "${title_column[${GRUB_BTRFS_TITLE_FORMAT[$key],,}]}" "|") # Final "|" is for visuals only header_entry+=$(printf "%${lenght_title_column_left}s%${lenght_title_column_right}s" "${title_column[${GRUB_BTRFS_TITLE_FORMAT[$key],,}]}" "|") # Final "|" is for visuals only
done done
@@ -542,7 +598,6 @@ boot_bounded()
boot_dir="$grub_btrfs_mount_point/$snap_dir_name_trim$boot_directory" boot_dir="$grub_btrfs_mount_point/$snap_dir_name_trim$boot_directory"
detect_kernel detect_kernel
if [ -z "${list_kernel}" ]; then continue; fi if [ -z "${list_kernel}" ]; then continue; fi
# TODO
name_kernel=("${list_kernel[@]##*"/"}") name_kernel=("${list_kernel[@]##*"/"}")
detect_initramfs detect_initramfs
name_initramfs=("${list_initramfs[@]##*"/"}") name_initramfs=("${list_initramfs[@]##*"/"}")
@@ -551,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
@@ -634,7 +690,6 @@ if [ "${count_limit_snap}" = "0" ] || [ -z "${count_limit_snap}" ]; then
fi fi
# Move "grub-btrfs.new" to "grub-btrfs.cfg" # Move "grub-btrfs.new" to "grub-btrfs.cfg"
header_menu header_menu
# shellcheck disable=SC2154 # bindir is provided by grub environment
if "${bindir}/${GRUB_BTRFS_SCRIPT_CHECK:-grub-script-check}" "$grub_btrfs_directory/grub-btrfs.new"; then if "${bindir}/${GRUB_BTRFS_SCRIPT_CHECK:-grub-script-check}" "$grub_btrfs_directory/grub-btrfs.new"; then
cat "$grub_btrfs_directory/grub-btrfs.new" > "$grub_btrfs_directory/grub-btrfs.cfg" cat "$grub_btrfs_directory/grub-btrfs.new" > "$grub_btrfs_directory/grub-btrfs.cfg"
rm -f "$grub_btrfs_directory/grub-btrfs.new" "$grub_btrfs_directory/grub-btrfs.cfg.bkp" rm -f "$grub_btrfs_directory/grub-btrfs.new" "$grub_btrfs_directory/grub-btrfs.cfg.bkp"

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).

8
config
View File

@@ -1,7 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
GRUB_BTRFS_VERSION=4.13-fix-bashisms-2024-03-27T20:48:48+00:00 GRUB_BTRFS_VERSION=4.13-yabsnap_info_support-2024-03-06T13:43:57+00:00
# Disable grub-btrfs. # Disable grub-btrfs.
# Default: "false" # Default: "false"
@@ -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~