Compare commits

..

1 Commits

Author SHA1 Message Date
Pascal Jäger
2886ad7b3c fix bashisms
Signed-off-by: Pascal Jäger <pascal.jaeger@leimstift.de>
2024-03-28 10:00:33 +01:00
10 changed files with 85 additions and 265 deletions

View File

@@ -1,34 +0,0 @@
name: Bump version on merge
on:
push:
branches:
- master
permissions:
contents: write
jobs:
bump-version:
name: Bump version in config
runs-on: ubuntu-latest
if: github.actor != 'github-actions[bot]'
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: master
- name: Update version in config
run: |
version="master-$(date -u -Iseconds)"
sed -i "s/GRUB_BTRFS_VERSION=.*/GRUB_BTRFS_VERSION=${version}/" config
echo "VERSION=${version}" >> $GITHUB_ENV
- name: Commit updated config
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add config
git commit -m "chore: bump version to ${{ env.VERSION }}"
git push origin master

View File

@@ -1,31 +0,0 @@
name: Bump version on tag
on:
push:
tags:
- '*'
permissions:
contents: write
jobs:
bump-version:
name: Bump version in config
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: master
- name: Update version in config
run: |
sed -i "s/GRUB_BTRFS_VERSION=.*/GRUB_BTRFS_VERSION=${{ github.ref_name }}/" config
- name: Commit updated config
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add config
git commit -m "chore: bump version to ${{ github.ref_name }}"
git push origin master

View File

@@ -14,8 +14,9 @@ jobs:
codespell: codespell:
name: Check for spelling errors name: Check for spelling errors
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: Codespell - name: Codespell
uses: codespell-project/actions-codespell@v2 uses: codespell-project/actions-codespell@v2

View File

@@ -1,4 +1,4 @@
#! /usr/bin/env bash #! /usr/bin/env sh
# #
# Written by: Antynea # Written by: Antynea
# BTC donation address: 1Lbvz244WA8xbpHek9W2Y12cakM6rDe5Rt # BTC donation address: 1Lbvz244WA8xbpHek9W2Y12cakM6rDe5Rt
@@ -41,24 +41,17 @@ 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"
## Logging ## Error Handling
print_info()
{
printf "grub-btrfs: %s\n" "$*" >&2
}
print_warning()
{
printf "grub-btrfs: Warning: %s\n" "$*" >&2
}
print_error() print_error()
{ {
local bug_report="If you think an error has occurred, please file a bug report at \"https://github.com/Antynea/grub-btrfs\"" err_msg="$*"
printf "grub-btrfs: Error: %s\n" "$*" "${bug_report}" >&2 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 ;
exit 0 exit 0
} }
@@ -84,12 +77,14 @@ while getopts :V-: opt; do
done done
## Exit the script, if: ## Exit the script, if:
[ "$(echo "$GRUB_BTRFS_DISABLE" | tr '[:upper:]' '[:lower:]')" = 'true' ] && { print_info "GRUB_BTRFS_DISABLE is set to true (default=false)"; exit 0; } [ "$(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_info "btrfs-progs isn't installed"; exit 0; 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"
[[ "$(btrfs filesystem df / 2>&1)" == *"not a btrfs filesystem"* ]] && { print_info "Root filesystem isn't btrfs"; exit 0; } # shellcheck disable=SC2005
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
print_info "Detecting snapshots ..." printf "Detecting snapshots ...\n" >&2 ;
## Submenu name ## Submenu name
distro=$(awk -F "=" '/^NAME=/ {gsub(/"/, "", $2); print $2}' /etc/os-release) # escape ' distro=$(awk -F "=" '/^NAME=/ {gsub(/"/, "", $2); print $2}' /etc/os-release) # escape '
@@ -116,85 +111,32 @@ 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 partition ---------- root_device=$(${grub_probe} --target=device /) # Root device
root_device="$(${grub_probe} --target=device /)" # e.g. /dev/mapper/enc # shellcheck disable=SC2086 # we actually need word splitting here if we have several root devices (e.g. RAID)
root_uuid="$(${grub_probe} --device "${root_device}" --target=fs_uuid 2>/dev/null)" || true root_uuid=$(${grub_probe} --device ${root_device} --target="fs_uuid" 2>/dev/null) # UUID of the root device
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
# Fallback when grub-probe fails (encrypted container, detached header…) root_uuid_subvolume=$(awk -F":" 'match($1, /(^[ \t]+UUID)/) {sub(/^[ \t]+/, "", $2); print $2}' <<EOF
if [ -z "$root_uuid" ]; then "$root_uuid_subvolume"
root_uuid="$(blkid -s UUID -o value "${root_device}" 2>/dev/null)" EOF
fi ) # UUID of the root subvolume '
[ -z "$root_uuid" ] && print_error "Cannot determine UUID of ${root_device}" # Probe info "Boot partition"
# shellcheck disable=SC2086 # we actually need word splitting here if we have several devices
# Root subvolume UUID boot_device=$(${grub_probe} --target=device ${boot_directory}) # Boot device
root_uuid_subvolume="$(btrfs subvolume show / 2>/dev/null | \ # shellcheck disable=SC2086 # we actually need word splitting here if we have several devices
awk -F':' '/^\s*UUID/ {gsub(/^[ \t]+/, "", $2); print $2}')" boot_uuid=$(${grub_probe} --device ${boot_device} --target="fs_uuid" 2>/dev/null) # UUID of the boot device
[ -z "$root_uuid_subvolume" ] && print_error "UUID of the root subvolume is not available" 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
boot_uuid_subvolume=$(awk -F":" 'match($1, /(^[ \t]+UUID)/) {sub(/^[ \t]+/, "", $2); print $2}' <<EOF
# ---------- Boot partition ---------- "$boot_uuid_subvolume"
boot_device="$(${grub_probe} --target=device "${boot_directory}")" # e.g. /dev/sdb1 EOF
boot_uuid="$(${grub_probe} --device "${boot_device}" --target=fs_uuid 2>/dev/null)" || true ) # UUID of the boot subvolume '
# shellcheck disable=SC2086 # we actually need word splitting here if we have several devices
# Fallback for boot UUID boot_hs=$(${grub_probe} --device ${boot_device} --target="hints_string" 2>/dev/null) # hints string
if [ -z "$boot_uuid" ]; then # shellcheck disable=SC2086 # we actually need word splitting here if we have several devices
boot_uuid="$(blkid -s UUID -o value "${boot_device}" 2>/dev/null)" boot_fs=$(${grub_probe} --device ${boot_device} --target="fs" 2>/dev/null) # Type filesystem of boot device
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"
@@ -217,7 +159,7 @@ fi
## Detect rootflags ## Detect rootflags
detect_rootflags() detect_rootflags()
{ {
local fstabflags=$(grep -oE '^\s*[^#][[:graph:]]+\s+/\s+btrfs\s+[[:graph:]]+' "${grub_btrfs_mount_point}/${snap_dir_name_trim}/etc/fstab" \ 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,}"
} }
@@ -225,8 +167,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
local wait=true wait=true
local wait_max=0 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
@@ -274,6 +216,7 @@ 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}";
@@ -292,22 +235,13 @@ 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
for j in "${insmods[@]}"; do insmod ${boot_fs}
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}"\""
@@ -342,12 +276,7 @@ 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}"\""
@@ -367,21 +296,21 @@ make_menu_entries()
## Trim a string from leading and trailing whitespaces ## Trim a string from leading and trailing whitespaces
trim() { trim() {
local var="$*" var="$*"
var="${var#"${var%%[![:space:]]*}"}" var="${var#"${var%%[![:space:]]*}"}"
var="${var%"${var##*[![:space:]]}"}" var="${var%"${var##*[![:space:]]}"}"
echo -n "$var" printf '%s' "$var"
} }
## List of snapshots on filesystem ## List of snapshots on filesystem
snapshot_list() snapshot_list()
{ {
local snapper_info="info.xml" snapper_info="info.xml"
local timeshift_info="info.json" timeshift_info="info.json"
local date_snapshots=() date_snapshots=()
local path_snapshots=() path_snapshots=()
local type_snapshots=() type_snapshots=()
local description_snapshots=() 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
@@ -408,7 +337,8 @@ snapshot_list()
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
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"
@@ -487,30 +417,31 @@ 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")"
snap_snapshot="$snap_dir_name" # Used by "title_format" function # shellcheck disable=SC2034 # 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+=("$okernel") list_kernel="${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+=("$ckernel") list_kernel="${list_kernel} $okernel"
done done
fi fi
} }
@@ -543,8 +474,12 @@ 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 ${GRUB_EARLY_INITRD_LINUX_STOCK} ; do for oiucode in "${boot_dir}"/intel-uc.img \
oiucode="${boot_dir}/${oiucode}" "${boot_dir}"/intel-ucode.img \
"${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
@@ -552,7 +487,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
@@ -565,7 +500,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
@@ -580,15 +515,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()
{ {
local header_entry="" 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
@@ -607,6 +542,7 @@ 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[@]##*"/"}")
@@ -615,7 +551,6 @@ 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
@@ -695,10 +630,11 @@ fi
# if no snapshot found, delete the "$grub_btrfs_directory/grub-btrfs.new" file and the "$grub_btrfs_directory/grub-btrfs.cfg.bkp" file and exit # if no snapshot found, delete the "$grub_btrfs_directory/grub-btrfs.new" file and the "$grub_btrfs_directory/grub-btrfs.cfg.bkp" file and exit
if [ "${count_limit_snap}" = "0" ] || [ -z "${count_limit_snap}" ]; then if [ "${count_limit_snap}" = "0" ] || [ -z "${count_limit_snap}" ]; then
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"
print_info "No snapshots found."; exit 0 print_error "No snapshots found."
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

@@ -240,7 +240,7 @@ sudo rc-service grub-btrfsd start
To activate it during system startup, run: To activate it during system startup, run:
```bash ```bash
sudo rc-update add grub-btrfsd default sudo rc-config add grub-btrfsd default
``` ```
##### 💼 Snapshots not in `/.snapshots` for OpenRC ##### 💼 Snapshots not in `/.snapshots` for OpenRC
@@ -274,10 +274,6 @@ 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=master-2026-08-05T07:40:52+00:00 GRUB_BTRFS_VERSION=4.13-fix-bashisms-2024-03-27T20:48:48+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" "var/lib/containers" "@var/lib/containers" "@/var/lib/containers") GRUB_BTRFS_IGNORE_PREFIX_PATH=("var/lib/docker" "@var/lib/docker" "@/var/lib/docker")
# 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,7 +158,3 @@ 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

@@ -5,7 +5,7 @@
name="grub-btrfs daemon" name="grub-btrfs daemon"
command="/usr/bin/grub-btrfsd" command="/usr/bin/grub-btrfsd"
command_args="$optional_args ${snapshots}" command_args="$optional_args ${snapshots}"
pidfile="/run/${RC_SVCNAME}.pid" pidfile="/run/{RC_SVCNAME}.pid"
command_background=true command_background=true
depend() { depend() {

View File

@@ -40,39 +40,8 @@ Re-generate your initramfs
`mkinitcpio -P` (option -P means, all preset present in `/etc/mkinitcpio.d`) `mkinitcpio -P` (option -P means, all preset present in `/etc/mkinitcpio.d`)
#### Dracut based distros #### Dracut based distros
Distributions that use Dracut to make their initramfs (many of the Fedora based Distros) have to include the `overlayfs` module into the initramfs and pass either `rd.live.overlay.readonly=1` (to boot into the snapshot read only) or `rd.live.overlay.overlayfs=1` (to act like a livedisk, that is files can be changed but changes will be lost on the next boot) to their kernel command line in grub. Distributions that use Dracut to make their initramfs (many of the Fedora based Distros) simply have to pass either `rd.live.overlay.readonly=1` (to boot into the snapshot read only) or `rd.live.overlay.overlayfs=1` (to act like a livedisk, that is files can be changed but changes will be lost on the next boot) to their kernel command line in grub.
Grub-btrfs provides the variable `GRUB_BTRFS_SNAPSHOT_KERNEL_PARAMETERS` to add any command to the kernel command line. Set it to `GRUB_BTRFS_SNAPSHOT_KERNEL_PARAMETERS="rd.live.overlay.overlayfs=1"` to make snapshots immutable when booted into.
1. Include `overlayfs` module into dracut
```bash
echo 'add_dracutmodules+=" overlayfs "' | sudo tee /etc/dracut.conf.d/overlayfs.conf
```
2. Fix for dracut <109
Unfortunately, dracut version <109 has a bug that prevents overlayfs mount from properly working. [A fix has been committed into dracut version 109](https://github.com/dracut-ng/dracut-ng/commit/deeb670c28d12a478bbea95e29677e436d1912fb). If you are still on version < 109, instead of editing `/usr/lib/dracut/modules.d/70overlayfs/mount-overlayfs.sh` directly on your system, which will get overwritten on updates to the dracut package, we can duplicate the whole module with a lower priority number and carry out the patch there. As explained in this [very old notes about dracut](https://wwoods.fedorapeople.org/doc/dracut-notes.html):
> dracut refuses to overwrite files when installing things into the initramfs, so things installed by the the lower numbered modules have priority over the higher ones.
```bash
sudo cp -r /usr/lib/dracut/modules.d/70overlayfs /usr/lib/dracut/modules.d/69overlayfs
sudoedit /usr/lib/dracut/modules.d/69overlayfs/mount-overlayfs.sh
```
Edit the file as per [the commit](https://github.com/dracut-ng/dracut/commit/deeb670c28d12a478bbea95e29677e436d1912fb)
3. Regenerate the initramfs
```
# for current kernel's initramfs
sudo dracut -f
# for all kernels
sudo dracut -f --regenerate-all
```
4. Add kernel parameter and regenerate grub snapshot-submenu
Grub-btrfs provides the variable `GRUB_BTRFS_SNAPSHOT_KERNEL_PARAMETERS` in `/etc/default/grub-btrfs/config` to add any command to the kernel command line. Set it to `GRUB_BTRFS_SNAPSHOT_KERNEL_PARAMETERS="rd.live.overlay.overlayfs=1"` to make snapshots immutable when booted into.
After changing this run `sudo /etc/grub.d/41_snapshots-btrfs` to generate a new snapshot-submenu with the parameter added. After changing this run `sudo /etc/grub.d/41_snapshots-btrfs` to generate a new snapshot-submenu with the parameter added.
#### Other distribution #### Other distribution

View File

@@ -102,14 +102,6 @@ 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,11 +73,6 @@ 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~