mirror of
https://github.com/Antynea/grub-btrfs.git
synced 2026-03-04 13:05:00 +08:00
* Update disable_script function: * A function is no longer required since the commands have been moved to the beginning of the script.
605 lines
23 KiB
Bash
Executable File
605 lines
23 KiB
Bash
Executable File
#! /usr/bin/env bash
|
|
#
|
|
# Written by: Antynea
|
|
# BTC donation address: 1Lbvz244WA8xbpHek9W2Y12cakM6rDe5Rt
|
|
# Github: https://github.com/Antynea/grub-btrfs
|
|
#
|
|
# Purpose:
|
|
# Improves Grub by adding "btrfs snapshots" to the Grub menu.
|
|
# You can boot your system on a "snapshot" from the Grub menu.
|
|
# Supports manual snapshots, snapper, timeshift ...
|
|
# Warning : booting on read-only snapshots can be tricky.
|
|
# (Read about it, https://github.com/Antynea/grub-btrfs#warning-booting-on-read-only-snapshots-can-be-tricky)
|
|
#
|
|
# What this script does:
|
|
# - Automatically List snapshots existing on root partition (btrfs).
|
|
# - Automatically Detect if "/boot" is in separate partition.
|
|
# - Automatically Detect kernel, initramfs and intel/amd microcode in "/boot" directory on snapshots.
|
|
# - Automatically Create corresponding "menuentry" in grub.cfg.
|
|
# - Automatically detect snapper and use snapper's snapshot description if available.
|
|
# - Automatically generate grub.cfg if you use the provided systemd service.
|
|
#
|
|
# Installation:
|
|
# - Refer to https://github.com/Antynea/grub-btrfs#installation-
|
|
#
|
|
# Customization:
|
|
# You have the possibility to modify many parameters in /etc/default/grub-btrfs/config.
|
|
#
|
|
# Automatically update Grub
|
|
# If you would like grub-btrfs menu to automatically update when a snapshot is created or deleted:
|
|
# - Refer to https://github.com/Antynea/grub-btrfs#automatically-update-grub.
|
|
#
|
|
# Special thanks for assistance and contributions:
|
|
# - My friends
|
|
# - All contributors on Github
|
|
#
|
|
|
|
set -e
|
|
|
|
datarootdir="/usr/share"
|
|
sysconfdir="/etc"
|
|
grub_btrfs_config="${sysconfdir}/default/grub-btrfs/config"
|
|
|
|
[[ -f "$grub_btrfs_config" ]] && . "$grub_btrfs_config"
|
|
[[ -f "${sysconfdir}/default/grub" ]] && . "${sysconfdir}/default/grub"
|
|
|
|
## Exit the script, if:
|
|
[[ "${GRUB_BTRFS_DISABLE,,}" == "true" ]] && exit 0 # Disable Grub-btrfs is set to true (default=false)
|
|
if ! type btrfs >/dev/null 2>&1; then exit 0; fi # btrfs-progs isn't installed
|
|
[[ -f "$datarootdir/grub/grub-mkconfig_lib" ]] && . "$datarootdir/grub/grub-mkconfig_lib" || exit 0 # grub-mkconfig_lib couldn't be found
|
|
# Root filesystem isn't btrfs
|
|
root_fs=$(${grub_probe} --target="fs" / 2>/dev/null)
|
|
[[ "$root_fs" != "btrfs" ]] && exit 0
|
|
|
|
## Submenu name
|
|
distro=$(awk -F "=" '/^NAME=/ {gsub(/"/, "", $2); print $2}' /etc/os-release)
|
|
submenuname=${GRUB_BTRFS_SUBMENUNAME:-"${distro:-Linux} snapshots"}
|
|
## Prefix entry
|
|
prefixentry=${GRUB_BTRFS_PREFIXENTRY:-"Snapshot:"}
|
|
## Limit snapshots to show in the Grub menu (default=50)
|
|
limit_snap_show="${GRUB_BTRFS_LIMIT:-50}"
|
|
## How to sort snapshots list
|
|
btrfssubvolsort=(--sort="${GRUB_BTRFS_SUBVOLUME_SORT:-"-rootid"}")
|
|
## Snapper's config name
|
|
snapper_config=${GRUB_BTRFS_SNAPPER_CONFIG:-"root"}
|
|
## Customize GRUB directory, where "grub.cfg" file is saved
|
|
grub_directory=${GRUB_BTRFS_GRUB_DIRNAME:-"/boot/grub"}
|
|
## Customize BOOT directory, where kernels/initrams/microcode is saved.
|
|
boot_directory=${GRUB_BTRFS_BOOT_DIRNAME:-"/boot"}
|
|
## Password protection management for submenu
|
|
# Protection support for submenu (--unrestricted)
|
|
case "${GRUB_BTRFS_DISABLE_PROTECTION_SUBMENU:-"false"}" in
|
|
true) unrestricted_access_submenu="--unrestricted ";;
|
|
*) unrestricted_access_submenu=""
|
|
esac
|
|
# Authorized users (--users foo,bar)
|
|
if [ -n "${GRUB_BTRFS_PROTECTION_AUTHORIZED_USERS}" ] ; then
|
|
protection_authorized_users="--users ${GRUB_BTRFS_PROTECTION_AUTHORIZED_USERS} "
|
|
fi
|
|
|
|
## Probe informations 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
|
|
# Probe info "Boot partition"
|
|
boot_device=$(${grub_probe} --target=device ${boot_directory}) # Boot device
|
|
boot_uuid=$(${grub_probe} --device ${boot_device} --target="fs_uuid" 2>/dev/null) # UUID of the boot device
|
|
boot_hs=$(${grub_probe} --device ${boot_device} --target="hints_string" 2>/dev/null) # hints string
|
|
boot_fs=$(${grub_probe} --device ${boot_device} --target="fs" 2>/dev/null) # Type filesystem of boot device
|
|
|
|
## Parameters passed to the kernel
|
|
kernel_parameters="$GRUB_CMDLINE_LINUX $GRUB_CMDLINE_LINUX_DEFAULT"
|
|
## Mount point location
|
|
gbgmp=$(mktemp -dt grub-btrfs.XXXXXXXXXX)
|
|
## Class for theme
|
|
CLASS="--class snapshots --class gnu-linux --class gnu --class os"
|
|
## save IFS
|
|
oldIFS=$IFS
|
|
## Detect uuid requirement (lvm,btrfs...)
|
|
check_uuid_required() {
|
|
if [ "x${root_uuid}" = "x" ] || [ "x${GRUB_DISABLE_LINUX_UUID}" = "xtrue" ] \
|
|
|| ! test -e "/dev/disk/by-uuid/${root_uuid}" \
|
|
|| ( test -e "${root_device}" && uses_abstraction "${root_device}" lvm ); then
|
|
LINUX_ROOT_DEVICE=${root_device}
|
|
else
|
|
LINUX_ROOT_DEVICE=UUID=${root_uuid}
|
|
fi
|
|
}
|
|
## Detect rootflags
|
|
detect_rootflags()
|
|
{
|
|
local fstabflags=$(grep -oE '^\s*[^#][[:graph:]]+\s+/\s+btrfs\s+[[:graph:]]+' "${gbgmp}/${snap_dir_name}/etc/fstab" \
|
|
| sed -E 's/^.*[[:space:]]([[:graph:]]+)$/\1/;s/,?subvol(id)?=[^,$]+//g;s/^,//')
|
|
rootflags="rootflags=${fstabflags:+$fstabflags,}${GRUB_BTRFS_ROOTFLAGS:+$GRUB_BTRFS_ROOTFLAGS,}"
|
|
}
|
|
## Path to grub-script-check
|
|
grub_script_check="${bindir}/grub-script-check"
|
|
|
|
## Error Handling
|
|
print_error()
|
|
{
|
|
local err_msg="$*"
|
|
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 ;
|
|
exit 0
|
|
}
|
|
|
|
unmount_gbgmp()
|
|
{
|
|
if [[ -d "$gbgmp" ]]; then
|
|
local wait=true
|
|
local wait_max=0
|
|
printf "Unmount %s .." "$gbgmp" >&2;
|
|
while $wait; do
|
|
if grep -qs "$gbgmp" /proc/mounts; then
|
|
wait_max=$((1+$wait_max))
|
|
if umount "$gbgmp" >/dev/null 2>&1; then
|
|
wait=false # umount successful
|
|
printf " Success\n" >&2;
|
|
elif [[ $wait_max = 10 ]]; then
|
|
printf "\nWarning: Unable to unmount %s in %s\n" "$root_device" "$gbgmp" >&2;
|
|
break;
|
|
else
|
|
printf "." >&2 ; # output to show that the script is alive
|
|
sleep 2 # wait 2 seconds before retry
|
|
fi
|
|
else
|
|
wait=false # not mounted
|
|
printf " Success\n" >&2;
|
|
fi
|
|
done
|
|
if [[ "$wait" != true ]]; then
|
|
if ! rm -d "$gbgmp" >/dev/null 2>&1; then
|
|
printf "Unable to delete %s: Device or ressource is busy\n" "$gbgmp" >&2;
|
|
fi
|
|
fi
|
|
fi
|
|
}
|
|
|
|
## Create entry
|
|
entry()
|
|
{
|
|
echo "$@" >> "$grub_directory/grub-btrfs.new"
|
|
}
|
|
|
|
## menu entries
|
|
make_menu_entries()
|
|
{
|
|
## \" required for snap,kernels,init,microcode with space in their name
|
|
entry "submenu '$title_menu' {
|
|
submenu '---> $title_menu <---' { echo }"
|
|
for k in "${name_kernel[@]}"; do
|
|
[[ ! -f "${boot_dir}"/"${k}" ]] && continue;
|
|
kversion=${k#*"-"}
|
|
for i in "${name_initramfs[@]}"; do
|
|
if [[ "${name_initramfs}" != "x" ]] ; then
|
|
prefix_i=${i%%"-"*}
|
|
suffix_i=${i#*"-"}
|
|
alt_suffix_i=${i##*"-"}
|
|
if [ "${kversion}" = "${suffix_i}" ]; then i="${i}";
|
|
elif [ "${kversion}.img" = "${suffix_i}" ]; then i="${i}";
|
|
elif [ "${kversion}-fallback.img" = "${suffix_i}" ]; then i="${i}";
|
|
elif [ "${kversion}.gz" = "${suffix_i}" ]; then i="${i}";
|
|
else continue;
|
|
fi
|
|
for u in "${name_microcode[@]}"; do
|
|
if [[ "${name_microcode}" != "x" ]] ; then
|
|
entry "
|
|
menuentry '"${k}" & "${i}" & "${u}"' ${CLASS} "\$menuentry_id_option" 'gnulinux-snapshots-$boot_uuid' {"
|
|
else
|
|
entry "
|
|
menuentry '"${k}" & "${i}"' ${CLASS} "\$menuentry_id_option" 'gnulinux-snapshots-$boot_uuid' {"
|
|
fi
|
|
entry "\
|
|
if [ x\$feature_all_video_module = xy ]; then
|
|
insmod all_video
|
|
fi
|
|
set gfxpayload=keep
|
|
insmod ${boot_fs}
|
|
if [ x\$feature_platform_search_hint = xy ]; then
|
|
search --no-floppy --fs-uuid --set=root ${boot_hs} ${boot_uuid}
|
|
else
|
|
search --no-floppy --fs-uuid --set=root ${boot_uuid}
|
|
fi
|
|
echo 'Loading Snapshot: "${snap_date_time}" "${snap_dir_name}"'
|
|
echo 'Loading Kernel: "${k}" ...'
|
|
linux \"${boot_dir_root_grub}/"${k}"\" root="${LINUX_ROOT_DEVICE}" ${kernel_parameters} ${rootflags}subvol=\""${snap_dir_name}"\""
|
|
if [[ "${name_microcode}" != "x" ]] ; then
|
|
entry "\
|
|
echo 'Loading Microcode & Initramfs: "${u}" "${i}" ...'
|
|
initrd \"${boot_dir_root_grub}/"${u}"\" \"${boot_dir_root_grub}/"${i}"\""
|
|
else
|
|
entry "\
|
|
echo 'Loading Initramfs: "${i}" ...'
|
|
initrd \"${boot_dir_root_grub}/"${i}"\""
|
|
fi
|
|
entry " }"
|
|
count_warning_menuentries=$((1+$count_warning_menuentries))
|
|
done
|
|
else
|
|
for u in "${name_microcode[@]}"; do
|
|
if [[ "${name_microcode}" != "x" ]] ; then
|
|
entry "
|
|
menuentry '"${k}" & "${u}"' ${CLASS} "\$menuentry_id_option" 'gnulinux-snapshots-$boot_uuid' {"
|
|
else
|
|
entry "
|
|
menuentry '"${k}"' ${CLASS} "\$menuentry_id_option" 'gnulinux-snapshots-$boot_uuid' {"
|
|
fi
|
|
entry "\
|
|
if [ x\$feature_all_video_module = xy ]; then
|
|
insmod all_video
|
|
fi
|
|
set gfxpayload=keep
|
|
insmod ${boot_fs}
|
|
if [ x\$feature_platform_search_hint = xy ]; then
|
|
search --no-floppy --fs-uuid --set=root ${boot_hs} ${boot_uuid}
|
|
else
|
|
search --no-floppy --fs-uuid --set=root ${boot_uuid}
|
|
fi
|
|
echo 'Loading Snapshot: "${snap_date_time}" "${snap_dir_name}"'
|
|
echo 'Loading Kernel: "${k}" ...'
|
|
linux \"${boot_dir_root_grub}/"${k}"\" root="${LINUX_ROOT_DEVICE}" ${kernel_parameters} ${rootflags}subvol=\""${snap_dir_name}"\""
|
|
if [[ "${name_microcode}" != "x" ]] ; then
|
|
entry "\
|
|
echo 'Loading Microcode: "${u}" ...'
|
|
initrd \"${boot_dir_root_grub}/"${u}"\""
|
|
fi
|
|
entry " }"
|
|
count_warning_menuentries=$((1+$count_warning_menuentries))
|
|
done
|
|
fi
|
|
done
|
|
done
|
|
entry "}"
|
|
}
|
|
|
|
## Trim a string from leading and trailing whitespaces
|
|
trim() {
|
|
local var="$*"
|
|
var="${var#"${var%%[![:space:]]*}"}"
|
|
var="${var%"${var##*[![:space:]]}"}"
|
|
echo -n "$var"
|
|
}
|
|
|
|
## List of snapshots on filesystem
|
|
snapshot_list()
|
|
{
|
|
# Query info from snapper if it is installed
|
|
if type snapper >/dev/null 2>&1; then
|
|
if [ -s "/etc/snapper/configs/$snapper_config" ]; then
|
|
printf "Info: snapper detected, using config: %s\n" "$snapper_config" >&2
|
|
local snapper_ids=($(snapper --no-dbus -t 0 -c "$snapper_config" list --disable-used-space | tail -n +3 | cut -d'|' -f 1))
|
|
local snapper_types=($(snapper --no-dbus -t 0 -c "$snapper_config" list --disable-used-space | tail -n +3 | cut -d'|' -f 2))
|
|
|
|
IFS=$'\n'
|
|
local snapper_descriptions=($(snapper --no-dbus -t 0 -c "$snapper_config" list --disable-used-space | tail -n +3 | rev | cut -d'|' -f 2 | rev))
|
|
else
|
|
printf "Warning: snapper detected but config: %s does not exist\n" "$snapper_config" >&2
|
|
fi
|
|
fi
|
|
|
|
IFS=$'\n'
|
|
|
|
# Parse btrfs snapshots
|
|
local entries=()
|
|
local ids=()
|
|
local max_entry_length=0
|
|
for snap in $(btrfs subvolume list -sa "${btrfssubvolsort}" /); do
|
|
IFS=$oldIFS
|
|
snap=($snap)
|
|
local snap_path_name=${snap[@]:13:${#snap[@]}}
|
|
|
|
# Discard deleted snapshots
|
|
if [ "$snap_path_name" = "DELETED" ]; then continue; fi
|
|
[[ ${snap_path_name%%"/"*} == "<FS_TREE>" ]] && snap_path_name=${snap_path_name#*"/"}
|
|
|
|
# ignore specific path during run "grub-mkconfig"
|
|
if [ -n "${GRUB_BTRFS_IGNORE_SPECIFIC_PATH}" ] ; then
|
|
for isp in ${GRUB_BTRFS_IGNORE_SPECIFIC_PATH[@]} ; do
|
|
[[ "${snap_path_name}" == "${isp}" ]] && continue 2;
|
|
done
|
|
fi
|
|
if [ -n "${GRUB_BTRFS_IGNORE_PREFIX_PATH}" ] ; then
|
|
for isp in ${GRUB_BTRFS_IGNORE_PREFIX_PATH[@]} ; do
|
|
[[ "${snap_path_name}" == "${isp}"/* ]] && continue 2;
|
|
done
|
|
fi
|
|
|
|
# detect if /boot directory exists
|
|
[[ ! -d "$gbgmp/$snap_path_name/boot" ]] && continue;
|
|
|
|
local id="${snap_path_name//[!0-9]}" # brutal way to get id: remove everything non-numeric
|
|
ids+=("$id")
|
|
|
|
local entry="${snap[@]:10:2} | ${snap_path_name}"
|
|
entries+=("$entry")
|
|
|
|
# Find max length of a snapshot entry, needed for pretty formatting
|
|
local length="${#entry}"
|
|
[[ "$length" -gt "$max_entry_length" ]] && max_entry_length=$length
|
|
done
|
|
|
|
# Find max length of a snapshot type, needed for pretty formatting
|
|
local max_type_length=0
|
|
for id in "${ids[@]}"; do
|
|
for j in "${!snapper_ids[@]}"; do
|
|
local snapper_id="${snapper_ids[$j]//[[:space:]]/}"
|
|
if [[ "$snapper_id" == "$id" ]]; then
|
|
local snapper_type=$(trim "${snapper_types[$j]}")
|
|
local length="${#snapper_type}"
|
|
[[ "$length" -gt "$max_type_length" ]] && max_type_length=$length
|
|
fi
|
|
done
|
|
done
|
|
|
|
for i in "${!entries[@]}"; do
|
|
local id="${ids[$i]}"
|
|
local entry="${entries[$i]}"
|
|
for j in "${!snapper_ids[@]}"; do
|
|
local snapper_id="${snapper_ids[$j]//[[:space:]]/}"
|
|
# remove other non numeric characters
|
|
snapper_id="${snapper_id//\*/}"
|
|
snapper_id="${snapper_id//\+/}"
|
|
snapper_id="${snapper_id//-/}"
|
|
if [[ "$snapper_id" == "$id" ]]; then
|
|
local snapper_type=$(trim "${snapper_types[$j]}")
|
|
local snapper_description=$(trim "${snapper_descriptions[$j]}")
|
|
|
|
# ignore snapper_type or snapper_description during run "grub-mkconfig"
|
|
if [ -n "${GRUB_BTRFS_IGNORE_SNAPPER_TYPE}" ] ; then
|
|
for ist in ${GRUB_BTRFS_IGNORE_SNAPPER_TYPE[@]} ; do
|
|
[[ "${snapper_type}" == "${ist}" ]] && continue 3;
|
|
done
|
|
fi
|
|
if [ -n "${GRUB_BTRFS_IGNORE_SNAPPER_DESCRIPTION}" ] ; then
|
|
for isd in ${GRUB_BTRFS_IGNORE_SNAPPER_DESCRIPTION[@]} ; do
|
|
[[ "${snapper_description}" == "${isd}" ]] && continue 3;
|
|
done
|
|
fi
|
|
printf -v entry "%-${max_entry_length}s | %-${max_type_length}s | %s" "$entry" "$snapper_type" "$snapper_description"
|
|
break
|
|
fi
|
|
done
|
|
echo "$entry"
|
|
done
|
|
|
|
IFS=$oldIFS
|
|
}
|
|
|
|
## Detect kernels in "/boot"
|
|
detect_kernel()
|
|
{
|
|
list_kernel=()
|
|
# Original kernel (auto-detect)
|
|
for okernel in "${boot_dir}"/vmlinuz-* \
|
|
"${boot_dir}"/vmlinux-* \
|
|
"${boot_dir}"/kernel-* ; do
|
|
[[ ! -f "${okernel}" ]] && continue;
|
|
list_kernel+=("$okernel")
|
|
done
|
|
|
|
# Custom name kernel in GRUB_BTRFS_NKERNEL
|
|
if [ -n "${GRUB_BTRFS_NKERNEL}" ] ; then
|
|
for ckernel in "${boot_dir}/${GRUB_BTRFS_NKERNEL[@]}" ; do
|
|
[[ ! -f "${ckernel}" ]] && continue;
|
|
list_kernel+=("$ckernel")
|
|
done
|
|
fi
|
|
}
|
|
|
|
## Detect initramfs in "/boot"
|
|
detect_initramfs()
|
|
{
|
|
list_initramfs=()
|
|
# Original initramfs (auto-detect)
|
|
for oinitramfs in "${boot_dir}"/initrd.img-* \
|
|
"${boot_dir}"/initrd-*.img \
|
|
"${boot_dir}"/initrd-*.gz \
|
|
"${boot_dir}"/initramfs-*.img \
|
|
"${boot_dir}"/initramfs-*.gz ; do
|
|
[[ ! -f "${oinitramfs}" ]] && continue;
|
|
list_initramfs+=("$oinitramfs")
|
|
done
|
|
|
|
# Custom name initramfs in GRUB_BTRFS_NINIT
|
|
if [ -n "${GRUB_BTRFS_NINIT}" ] ; then
|
|
for cinitramfs in "${boot_dir}/${GRUB_BTRFS_NINIT[@]}" ; do
|
|
[[ ! -f "${cinitramfs}" ]] && continue;
|
|
list_initramfs+=("$cinitramfs")
|
|
done
|
|
fi
|
|
if [ -z "${list_initramfs}" ]; then list_initramfs=(x); fi
|
|
}
|
|
|
|
## Detect microcode in "/boot"
|
|
detect_microcode()
|
|
{
|
|
list_ucode=()
|
|
# Original intel/amd microcode (auto-detect)
|
|
# See "https://www.gnu.org/software/grub/manual/grub/html_node/Simple-configuration.html"
|
|
for oiucode in "${boot_dir}"/intel-uc.img \
|
|
"${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;
|
|
list_ucode+=("$oiucode")
|
|
done
|
|
|
|
# Custom name microcode in GRUB_BTRFS_CUSTOM_MICROCODE
|
|
if [ -n "${GRUB_BTRFS_CUSTOM_MICROCODE}" ] ; then
|
|
for cucode in "${boot_dir}/${GRUB_BTRFS_CUSTOM_MICROCODE[@]}" ; do
|
|
[[ ! -f "${cucode}" ]] && continue
|
|
list_ucode+=("$cucode")
|
|
done
|
|
fi
|
|
if [ -z "${list_ucode}" ]; then list_ucode=(x); fi
|
|
}
|
|
|
|
## Show full path snapshot or only name
|
|
path_snapshot()
|
|
{
|
|
case "${GRUB_BTRFS_DISPLAY_PATH_SNAPSHOT:-"true"}" in
|
|
true) name_snapshot=("${snap_full_name}");;
|
|
*) name_snapshot=("${snap_full_name#*"/"}")
|
|
esac
|
|
}
|
|
|
|
## Title format in grub-menu
|
|
title_format()
|
|
{
|
|
case "${GRUB_BTRFS_TITLE_FORMAT:-"p/d/n"}" in
|
|
p/n/d) title_menu="${prefixentry} ${name_snapshot} ${snap_date_time}";;
|
|
p/d) title_menu="${prefixentry} ${snap_date_time}";;
|
|
p/n) title_menu="${prefixentry} ${name_snapshot}";;
|
|
d/n) title_menu="${snap_date_time} ${name_snapshot}";;
|
|
n/d) title_menu="${name_snapshot} ${snap_date_time}";;
|
|
p) title_menu="${prefixentry}";;
|
|
d) title_menu="${snap_date_time}";;
|
|
n) title_menu="${name_snapshot}";;
|
|
*) title_menu="${prefixentry} ${snap_date_time} ${name_snapshot}"
|
|
esac
|
|
}
|
|
|
|
## List of kernels, initramfs and microcode in snapshots
|
|
boot_bounded()
|
|
{
|
|
# Initialize menu entries
|
|
IFS=$'\n'
|
|
for item in $(snapshot_list); do
|
|
[[ ${limit_snap_show} -le 0 ]] && break; # fix: limit_snap_show=0
|
|
IFS=$oldIFS
|
|
snap_full_name="$(echo "$item" | cut -d'|' -f2-)" # do not trim it to keep nice formatting
|
|
snap_dir_name="$(echo "$item" | cut -d'|' -f2)"
|
|
snap_dir_name="$(trim "$snap_dir_name")"
|
|
snap_date_time="$(echo "$item" | cut -d' ' -f1-2)"
|
|
snap_date_time="$(trim "$snap_date_time")"
|
|
|
|
boot_dir="$gbgmp/$snap_dir_name$boot_directory"
|
|
# Kernel (Original + custom kernel)
|
|
detect_kernel
|
|
if [ -z "${list_kernel}" ]; then continue; fi
|
|
name_kernel=("${list_kernel[@]##*"/"}")
|
|
# Detect rootflags
|
|
detect_rootflags
|
|
# Initramfs (Original + custom initramfs)
|
|
detect_initramfs
|
|
name_initramfs=("${list_initramfs[@]##*"/"}")
|
|
# microcode (auto-detect + custom microcode)
|
|
detect_microcode
|
|
name_microcode=("${list_ucode[@]##*"/"}")
|
|
# show snapshot found during run "grub-mkconfig"
|
|
if [[ "${GRUB_BTRFS_SHOW_SNAPSHOTS_FOUND:-"true"}" = "true" ]]; then
|
|
printf "Found snapshot: %s\n" "$item" >&2 ;
|
|
fi
|
|
# Show full path snapshot or only name
|
|
path_snapshot
|
|
# Title format in grub-menu
|
|
title_format
|
|
# convert /boot directory to root of GRUB (e.g /boot become /)
|
|
boot_dir_root_grub="$(make_system_path_relative_to_its_root "${boot_dir}")"
|
|
# Make menuentries
|
|
make_menu_entries
|
|
### Limit snapshots found during run "grub-mkconfig"
|
|
count_limit_snap=$((1+$count_limit_snap))
|
|
[[ $count_limit_snap -ge $limit_snap_show ]] && break;
|
|
done
|
|
IFS=$oldIFS
|
|
}
|
|
|
|
boot_separate()
|
|
{
|
|
boot_dir="${boot_directory}"
|
|
# convert /boot directory to root of GRUB (e.g /boot become /)
|
|
boot_dir_root_grub="$(make_system_path_relative_to_its_root "${boot_dir}")"
|
|
|
|
# Kernel (Original + custom kernel)
|
|
detect_kernel
|
|
if [ -z "${list_kernel}" ]; then print_error "Kernels not found."; fi
|
|
name_kernel=("${list_kernel[@]##*"/"}")
|
|
# Initramfs (Original + custom initramfs)
|
|
detect_initramfs
|
|
name_initramfs=("${list_initramfs[@]##*"/"}")
|
|
# microcode (auto-detect + custom microcode)
|
|
detect_microcode
|
|
name_microcode=("${list_ucode[@]##*"/"}")
|
|
|
|
# Initialize menu entries
|
|
IFS=$'\n'
|
|
for item in $(snapshot_list); do
|
|
[[ ${limit_snap_show} -le 0 ]] && break; # fix: limit_snap_show=0
|
|
IFS=$oldIFS
|
|
snap_full_name="$(echo "$item" | cut -d'|' -f2-)" # do not trim it to keep nice formatting
|
|
snap_dir_name="$(echo "$item" | cut -d'|' -f2)"
|
|
snap_dir_name="$(trim "$snap_dir_name")"
|
|
snap_date_time="$(echo "$item" | cut -d' ' -f1-2)"
|
|
snap_date_time="$(trim "$snap_date_time")"
|
|
# Detect rootflags
|
|
detect_rootflags
|
|
# show snapshot found during run "grub-mkconfig"
|
|
if [[ "${GRUB_BTRFS_SHOW_SNAPSHOTS_FOUND:-"true"}" = "true" ]]; then
|
|
printf "Found snapshot: %s\n" "$item" >&2 ;
|
|
fi
|
|
# Show full path snapshot or only name
|
|
path_snapshot
|
|
# Title format in grub-menu
|
|
title_format
|
|
# Make menuentries
|
|
make_menu_entries
|
|
# Limit snapshots found during run "grub-mkconfig"
|
|
count_limit_snap=$((1+$count_limit_snap))
|
|
[[ $count_limit_snap -ge $limit_snap_show ]] && break;
|
|
done
|
|
IFS=$oldIFS
|
|
}
|
|
|
|
printf "Detecting snapshots ...\n" >&2 ;
|
|
rm -f "$grub_directory/grub-btrfs.new"
|
|
> "$grub_directory/grub-btrfs.new"
|
|
# Create mount point then mounting
|
|
[[ ! -d $gbgmp ]] && mkdir -p "$gbgmp"
|
|
mount -o ro,subvolid=5 /dev/disk/by-uuid/"$root_uuid" "$gbgmp/"
|
|
trap "unmount_gbgmp" EXIT # unmounting mount point on EXIT signal
|
|
# Count menuentries
|
|
count_warning_menuentries=0
|
|
# Count snapshots
|
|
count_limit_snap=0
|
|
# detect uuid requirement
|
|
check_uuid_required
|
|
# Detects if /boot is a separate partition
|
|
if [[ "${GRUB_BTRFS_OVERRIDE_BOOT_PARTITION_DETECTION:-"false"}" == "true" ]]; then
|
|
printf "Info: Override boot partition detection : enable \n" >&2 ;
|
|
boot_separate
|
|
else
|
|
if [[ "$root_uuid" != "$boot_uuid" ]]; then
|
|
printf "Info: Separate boot partition detected \n" >&2 ;
|
|
boot_separate
|
|
else
|
|
printf "Info: Separate boot partition not detected \n" >&2 ;
|
|
boot_bounded
|
|
fi
|
|
fi
|
|
# Show warn, menuentries exceeds 250 entries
|
|
[[ $count_warning_menuentries -ge 250 ]] && printf "Generated %s total GRUB entries. You might experience issues loading snapshots menu in GRUB.\n" "${count_warning_menuentries}" >&2 ;
|
|
# Show total found snapshots
|
|
if [[ "${GRUB_BTRFS_SHOW_TOTAL_SNAPSHOTS_FOUND:-"true"}" = "true" && -n "${count_limit_snap}" && "${count_limit_snap}" != "0" ]]; then
|
|
printf "Found %s snapshot(s)\n" "${count_limit_snap}" >&2 ;
|
|
fi
|
|
# if no snapshot found, exit
|
|
if [[ "${count_limit_snap}" = "0" || -z "${count_limit_snap}" ]]; then
|
|
print_error "No snapshots found."
|
|
fi
|
|
# Make a submenu in GRUB (grub.cfg) and move "grub-btrfs.new" to "grub-btrfs.cfg"
|
|
if ${grub_script_check} "$grub_directory/grub-btrfs.new"; then
|
|
cat "$grub_directory/grub-btrfs.new" > "$grub_directory/grub-btrfs.cfg"
|
|
rm -f "$grub_directory/grub-btrfs.new"
|
|
cat << EOF
|
|
submenu '${submenuname}' ${protection_authorized_users}${unrestricted_access_submenu}{
|
|
configfile "\${prefix}/grub-btrfs.cfg"
|
|
}
|
|
EOF
|
|
else
|
|
print_error "Syntax errors are detected in generated grub-btrfs.cfg file."
|
|
fi
|