From 1e2df5c220a218831688d0a6c593c08dcb331624 Mon Sep 17 00:00:00 2001 From: tohurtv Date: Wed, 19 Nov 2025 11:44:03 -0700 Subject: [PATCH] First Commit --- 41_snapshots-btrfs | 578 +++++++++++++++++++ Makefile | 86 +++ config | 132 +++++ debian/changelog | 44 ++ debian/control | 18 + debian/copyright | 17 + debian/docs | 1 + debian/gbp.conf | 10 + debian/grub-btrfs.path | 1 + debian/grub-btrfs.postinst | 16 + debian/grub-btrfs.postrm | 16 + debian/grub-btrfs.service | 1 + debian/kali-ci.yml | 7 + debian/rules | 10 + debian/source/format | 1 + debian/upstream/metadata | 5 + debian/watch | 4 + grub-btrfs-openrc | 6 + grub-btrfs.path | 12 + grub-btrfs.service | 11 + grub-btrfsd | 14 + initramfs/Arch Linux/overlay_snap_ro-hook | 15 + initramfs/Arch Linux/overlay_snap_ro-install | 18 + initramfs/readme.md | 45 ++ 24 files changed, 1068 insertions(+) create mode 100644 41_snapshots-btrfs create mode 100644 Makefile create mode 100644 config create mode 100644 debian/changelog create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/docs create mode 100644 debian/gbp.conf create mode 120000 debian/grub-btrfs.path create mode 100644 debian/grub-btrfs.postinst create mode 100644 debian/grub-btrfs.postrm create mode 120000 debian/grub-btrfs.service create mode 100644 debian/kali-ci.yml create mode 100755 debian/rules create mode 100644 debian/source/format create mode 100644 debian/upstream/metadata create mode 100644 debian/watch create mode 100755 grub-btrfs-openrc create mode 100644 grub-btrfs.path create mode 100644 grub-btrfs.service create mode 100755 grub-btrfsd create mode 100644 initramfs/Arch Linux/overlay_snap_ro-hook create mode 100644 initramfs/Arch Linux/overlay_snap_ro-install create mode 100644 initramfs/readme.md diff --git a/41_snapshots-btrfs b/41_snapshots-btrfs new file mode 100644 index 0000000..a274a14 --- /dev/null +++ b/41_snapshots-btrfs @@ -0,0 +1,578 @@ +#! /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 the type/tags and descriptions/comments of snapper/timeshift snapshots. +# - 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 + +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 "${GRUB_BTRFS_MKCONFIG_LIB:-/usr/share/grub/grub-mkconfig_lib}" ]] && . "${GRUB_BTRFS_MKCONFIG_LIB:-/usr/share/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 + +## 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 +} + +printf "Detecting snapshots ...\n" >&2 ; + +## Submenu name +distro=$(awk -F "=" '/^NAME=/ {gsub(/"/, "", $2); print $2}' /etc/os-release) +submenuname=${GRUB_BTRFS_SUBMENUNAME:-"${distro:-Linux} snapshots"} +## Limit snapshots to show in the Grub menu (default=50) +limit_snap_show="${GRUB_BTRFS_LIMIT:-50}" +## How to sort snapshots list +btrfs_subvolume_sort="--sort=${GRUB_BTRFS_SUBVOLUME_SORT:-"-rootid"}" +## 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,,}" 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 +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}' <<< "$root_uuid_subvolume") # UUID of the root subvolume +# 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_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}' <<< "$boot_uuid_subvolume") # UUID of the boot subvolume +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 +grub_btrfs_mount_point=$(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:]]+' "${grub_btrfs_mount_point}/${snap_dir_name_trim}/etc/fstab" \ + | sed -E 's/^.*[[:space:]]([[:graph:]]+)$/\1/;s/,?subvol(id)?=[^,$]+//g;s/^,//') + rootflags="rootflags=${fstabflags:+$fstabflags,}${GRUB_BTRFS_ROOTFLAGS:+$GRUB_BTRFS_ROOTFLAGS,}" +} + +unmount_grub_btrfs_mount_point() +{ +if [[ -d "$grub_btrfs_mount_point" ]]; then + local wait=true + local wait_max=0 + printf "Unmount %s .." "$grub_btrfs_mount_point" >&2; + while $wait; do + if grep -qs "$grub_btrfs_mount_point" /proc/mounts; then + wait_max=$((1+wait_max)) + if umount "$grub_btrfs_mount_point" >/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" "$grub_btrfs_mount_point" >&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 "$grub_btrfs_mount_point" >/dev/null 2>&1; then + printf "Unable to delete %s: Device or ressource is busy\n" "$grub_btrfs_mount_point" >&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_submenu}' { 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_trim}" "${snap_dir_name_trim}"' + echo 'Loading Kernel: "${k}" ...' + linux \"${boot_dir_root_grub}/"${k}"\" root="${LINUX_ROOT_DEVICE}" ${kernel_parameters} ${rootflags}subvol=\""${snap_dir_name_trim}"\"" + 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_trim}" "${snap_dir_name_trim}"' + echo 'Loading Kernel: "${k}" ...' + linux \"${boot_dir_root_grub}/"${k}"\" root="${LINUX_ROOT_DEVICE}" ${kernel_parameters} ${rootflags}subvol=\""${snap_dir_name_trim}"\"" + 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() +{ + local snapper_info="info.xml" + local timeshift_info="info.json" + local date_snapshots=() + local path_snapshots=() + local type_snapshots=() + local description_snapshots=() + IFS=$'\n' + for snap in $(btrfs subvolume list -sa "${btrfs_subvolume_sort}" /); do # Parse btrfs snapshots + IFS=$oldIFS + snap=($snap) + local path_snapshot=${snap[@]:13:${#snap[@]}} + if [ "$path_snapshot" = "DELETED" ]; then continue; fi # Discard deleted snapshots + [[ ${path_snapshot%%"/"*} == "" ]] && path_snapshot=${path_snapshot#*"/"} # Remove the "" string at the beginning of the path + + # ignore specific path during run "grub-mkconfig" + if [ -n "${GRUB_BTRFS_IGNORE_SPECIFIC_PATH}" ] ; then + for isp in "${GRUB_BTRFS_IGNORE_SPECIFIC_PATH[@]}" ; do + [[ "${path_snapshot}" == "${isp}" ]] && continue 2; + done + fi + if [ -n "${GRUB_BTRFS_IGNORE_PREFIX_PATH}" ] ; then + for isp in "${GRUB_BTRFS_IGNORE_PREFIX_PATH[@]}" ; do + [[ "${path_snapshot}" == "${isp}"/* ]] && continue 2; + done + fi + [[ ! -d "$grub_btrfs_mount_point/$path_snapshot/boot" ]] && continue; # Discard snapshots without /boot folder + + # Parse Snapper & timeshift informations + local type_snapshot="N/A" + local description_snapshot="N/A" + 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" + description_snapshot=$(awk -F"<|>" 'match($2, /^description/) {print $3}' "$grub_btrfs_mount_point/${path_snapshot%"/"*}/$snapper_info") # search matching string beginning "description" + elif [[ -s "$grub_btrfs_mount_point/${path_snapshot%"/"*}/$timeshift_info" ]] ; then + type_snapshot=$(awk -F" : " 'match($1, /^[ \t]+"tags"/) {gsub(/"|,/,"");print $2}' "$grub_btrfs_mount_point/${path_snapshot%"/"*}/$timeshift_info") # search matching string beginning "tags" + description_snapshot=$(awk -F" : " 'match($1, /^[ \t]+"comments"/) {gsub(/"|,/,"");print $2}' "$grub_btrfs_mount_point/${path_snapshot%"/"*}/$timeshift_info") # search matching string beginning "comments" + fi + [[ -z "$type_snapshot" ]] && type_snapshot=("N/A") + [[ -z "$description_snapshot" ]] && description_snapshot=("N/A") + + # ignore specific {type,tag,description} of snapshot during run "grub-mkconfig" + if [ -n "${GRUB_BTRFS_IGNORE_SNAPSHOT_TYPE}" ] ; then + for ist in "${GRUB_BTRFS_IGNORE_SNAPSHOT_TYPE[@]}" ; do + [[ "${type_snapshot}" == "${ist}" ]] && continue 2; + done + fi + if [ -n "${GRUB_BTRFS_IGNORE_SNAPSHOT_DESCRIPTION}" ] ; then + for isd in "${GRUB_BTRFS_IGNORE_SNAPSHOT_DESCRIPTION[@]}" ; do + [[ "${description_snapshot}" == "${isd}" ]] && continue 2; + done + fi + + local date_snapshot="${snap[@]:10:2}" + date_snapshots+=("$date_snapshot") + path_snapshots+=("$path_snapshot") + type_snapshots+=("$type_snapshot") + description_snapshots+=("$description_snapshot") + done + + # Find max length of a snapshot date, needed for pretty formatting + local max_date_length=0 + for i in "${date_snapshots[@]}"; do + local length="${#i}" + [[ "$length" -gt "$max_date_length" ]] && max_date_length=$length + done + + # Find max length of a snapshot name, needed for pretty formatting + local max_path_length=0 + for i in "${path_snapshots[@]}"; do + local length="${#i}" + [[ "$length" -gt "$max_path_length" ]] && max_path_length=$length + done + + # Find max length of a snapshot type, needed for pretty formatting + local max_type_length=0 + for i in "${type_snapshots[@]}"; do + local length="${#i}" + [[ "$length" -gt "$max_type_length" ]] && max_type_length=$length + done + + # Find max length of a snapshot description, needed for pretty formatting + local max_description_length=0 + for i in "${description_snapshots[@]}"; do + local length="${#i}" + [[ "$length" -gt "$max_description_length" ]] && max_description_length=$length + done + + for i in "${!path_snapshots[@]}"; do + printf -v entry "%-${max_date_length}s | %-${max_path_length}s | %-${max_type_length}s | %-${max_description_length}s |" "${date_snapshots[$i]}" "${path_snapshots[$i]}" "${type_snapshots[$i]}" "${description_snapshots[$i]}" + echo "$entry" + done + + IFS=$oldIFS +} + +## Parse snapshots in snapshot_list +parse_snapshot_list() +{ + snap_date=" $(echo "$item" | cut -d'|' -f1)" # column_1, first space is necessary for pretty formatting + snap_date_trim="$(trim "$snap_date")" + + snap_dir_name="$(echo "$item" | cut -d'|' -f2)" # column_2 + snap_dir_name_trim="$(trim "$snap_dir_name")" + snap_snapshot="$snap_dir_name" # Used by "title_format" function + + snap_type="$(echo "$item" | cut -d'|' -f3)" # column_3 + + snap_description="$(echo "$item" | cut -d'|' -f4)" # column_4 +} + +## Detect kernels in "boot_directory" +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_directory" +detect_initramfs() +{ + list_initramfs=() + # Original initramfs (auto-detect) + for oinitramfs in "${boot_dir}"/initrd.img-* \ + "${boot_dir}"/initramfs-* \ + "${boot_dir}"/initrd-* ; 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_directory" +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 +} + +## Title format in Grub-menu +declare -A title_column=( [date]=Date [snapshot]=Snapshot [type]=Type [description]=Description ) # Column title that appears in the header +title_format() +{ + title_menu="|" # "|" 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 + 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 + declare -n var="snap_${GRUB_BTRFS_TITLE_FORMAT[$key],,}" # $var is a indirect variable + if [[ "${#var}" -lt "${#title_column[${GRUB_BTRFS_TITLE_FORMAT[$key],,}]}" ]]; then # Add extra spaces if length of $var is smaller than the length of column, needed for pretty formatting + printf -v var "%-$(((${#title_column[${GRUB_BTRFS_TITLE_FORMAT[$key],,}]}-${#var})+${#var}))s" "${var}"; + fi + title_menu+="${var}|" + title_submenu+=" $(trim "${var}") |" + done +} +# Adds a header to the grub-btrfs.cfg file +header_menu() +{ + local header_entry="" + [[ -z "${GRUB_BTRFS_TITLE_FORMAT}" ]] && GRUB_BTRFS_TITLE_FORMAT=("date" "snapshot" "type" "description"); # Default parameters + 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 + declare -n var="snap_${GRUB_BTRFS_TITLE_FORMAT[$key],,}" # $var is a indirect variable + # Center alignment, needed for pretty formatting + 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=$((((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 + 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 + sed -i "1imenuentry '|${header_entry}' { echo }" "$grub_directory/grub-btrfs.new" # First "|" is for visuals only +} + +## 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 + parse_snapshot_list + boot_dir="$grub_btrfs_mount_point/$snap_dir_name_trim$boot_directory" + detect_kernel + if [ -z "${list_kernel}" ]; then continue; fi + name_kernel=("${list_kernel[@]##*"/"}") + detect_initramfs + name_initramfs=("${list_initramfs[@]##*"/"}") + detect_microcode + name_microcode=("${list_ucode[@]##*"/"}") + detect_rootflags + 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 /) + make_menu_entries + # show snapshot found during run "grub-mkconfig" + if [[ "${GRUB_BTRFS_SHOW_SNAPSHOTS_FOUND:-"true"}" = "true" ]]; then + printf "Found snapshot: %s\n" "$item" >&2 ; + fi + # 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}" + boot_dir_root_grub="$(make_system_path_relative_to_its_root "${boot_dir}")" # convert "boot_directory" to root of GRUB (e.g /boot become /) + detect_kernel + if [ -z "${list_kernel}" ]; then print_error "Kernels not found."; fi + name_kernel=("${list_kernel[@]##*"/"}") + detect_initramfs + name_initramfs=("${list_initramfs[@]##*"/"}") + 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 + parse_snapshot_list + detect_rootflags + title_format + make_menu_entries + # show snapshot found during run "grub-mkconfig" + if [[ "${GRUB_BTRFS_SHOW_SNAPSHOTS_FOUND:-"true"}" = "true" ]]; then + printf "Found snapshot: %s\n" "$item" >&2 ; + fi + # 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 +} + +rm -f "$grub_directory/grub-btrfs.new" +> "$grub_directory/grub-btrfs.new" # Create a "grub-btrfs.new" file in "grub_directory" +# Create mount point then mounting +[[ ! -d $grub_btrfs_mount_point ]] && mkdir -p "$grub_btrfs_mount_point" +mount -o ro,subvolid=5 /dev/disk/by-uuid/"$root_uuid" "$grub_btrfs_mount_point/" +trap "unmount_grub_btrfs_mount_point" EXIT # unmounting mount point on EXIT signal +count_warning_menuentries=0 # Count menuentries +count_limit_snap=0 # Count snapshots +check_uuid_required +# Detects if /boot is a separate partition +[[ "${GRUB_BTRFS_OVERRIDE_BOOT_PARTITION_DETECTION,,}" == "true" ]] && printf "Override boot partition detection : enable \n" >&2 && boot_separate; +if [[ "$root_uuid" != "$boot_uuid" ]] || [[ "$root_uuid_subvolume" != "$boot_uuid_subvolume" ]]; then boot_separate ; else boot_bounded ; 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" +header_menu +if "${bindir}/${GRUB_BTRFS_SCRIPT_CHECK:-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 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f0ae86a --- /dev/null +++ b/Makefile @@ -0,0 +1,86 @@ +PKGNAME ?= grub-btrfs +PREFIX ?= /usr + +INITCPIO ?= false +SYSTEMD ?= true +OPENRC ?= false + +SHARE_DIR = $(DESTDIR)$(PREFIX)/share +LIB_DIR = $(DESTDIR)$(PREFIX)/lib +BIN_DIR = $(DESTDIR)$(PREFIX)/bin + +.PHONY: install uninstall help + +install: + @if test "$(shell id -u)" != 0; then \ + echo "You are not root, run this target as root please."; \ + exit 1; \ + fi + @install -Dm755 -t "$(DESTDIR)/etc/grub.d/" 41_snapshots-btrfs + @install -Dm644 -t "$(DESTDIR)/etc/default/grub-btrfs/" config + @# Systemd init system + @if test "$(SYSTEMD)" = true; then \ + install -Dm644 -t "$(LIB_DIR)/systemd/system/" grub-btrfs.path; \ + install -Dm644 -t "$(LIB_DIR)/systemd/system/" grub-btrfs.service; \ + fi + @# OpenRC init system + @if test "$(OPENRC)" = true; then \ + install -Dm744 -t "$(BIN_DIR)/" grub-btrfs-openrc; \ + install -Dm744 -t "$(DESTDIR)/etc/init.d/" grub-btrfsd; \ + fi + @# Arch Linux like distros only : + @if test "$(INITCPIO)" = true; then \ + install -Dm644 "initramfs/Arch Linux/overlay_snap_ro-install" "$(LIB_DIR)/initcpio/install/grub-btrfs-overlayfs"; \ + install -Dm644 "initramfs/Arch Linux/overlay_snap_ro-hook" "$(LIB_DIR)/initcpio/hooks/grub-btrfs-overlayfs"; \ + fi + @install -Dm644 -t "$(SHARE_DIR)/licenses/$(PKGNAME)/" LICENSE + @install -Dm644 -t "$(SHARE_DIR)/doc/$(PKGNAME)/" README.md + @install -Dm644 "initramfs/readme.md" "$(SHARE_DIR)/doc/$(PKGNAME)/initramfs-overlayfs.md" + +uninstall: + @if test "$(shell id -u)" != 0; then \ + echo "You are not root, run this target as root please."; \ + exit 1; \ + fi + @grub_dirname="$$(grep -oP '^[[:space:]]*GRUB_BTRFS_GRUB_DIRNAME=\K.*' "$(DESTDIR)/etc/default/grub-btrfs/config" | sed "s|\s*#.*||;s|(\s*\(.\+\)\s*)|\1|;s|['\"]||g")"; \ + rm -f "$${grub_dirname:-/boot/grub}/grub-btrfs.cfg" + @rm -f "$(DESTDIR)/etc/default/grub-btrfs/config" + @rm -f "$(DESTDIR)/etc/grub.d/41_snapshots-btrfs" + @rm -f "$(LIB_DIR)/systemd/system/grub-btrfs.path" + @rm -f "$(LIB_DIR)/systemd/system/grub-btrfs.service" + @rm -f "$(BIN_DIR)/grub-btrfs-openrc;" + @rm -f "$(DESTDIR)/etc/init.d/grub-btrfsd;" + @rm -f "$(LIB_DIR)/initcpio/install/grub-btrfs-overlayfs" + @rm -f "$(LIB_DIR)/initcpio/hooks/grub-btrfs-overlayfs" + @# Arch Linux UNlike distros only : + @if test "$(INITCPIO)" != true && test -d "$(LIB_DIR)/initcpio"; then \ + rmdir --ignore-fail-on-non-empty "$(LIB_DIR)/initcpio/install" || :; \ + rmdir --ignore-fail-on-non-empty "$(LIB_DIR)/initcpio/hooks" || :; \ + rmdir --ignore-fail-on-non-empty "$(LIB_DIR)/initcpio" || :; \ + fi + @rm -f "$(SHARE_DIR)/doc/$(PKGNAME)/README.md" + @rm -f "$(SHARE_DIR)/doc/$(PKGNAME)/initramfs-overlayfs.md" + @rm -f "$(SHARE_DIR)/licenses/$(PKGNAME)/LICENSE" + @rmdir --ignore-fail-on-non-empty "$(SHARE_DIR)/doc/$(PKGNAME)/" || : + @rmdir --ignore-fail-on-non-empty "$(SHARE_DIR)/licenses/$(PKGNAME)/" || : + @rmdir --ignore-fail-on-non-empty "$(DESTDIR)/etc/default/grub-btrfs" || : + +help: + @echo + @echo "Usage: $(MAKE) [ = ... ] [ ]" + @echo + @echo " actions: install" + @echo " uninstall" + @echo " help" + @echo + @echo " parameter | type | description | defaults" + @echo " ----------+------+--------------------------------+----------------------------" + @echo " DESTDIR | path | install destination | " + @echo " PREFIX | path | system tree prefix | '/usr'" + @echo " SHARE_DIR | path | shared data location | '\$$(DESTDIR)\$$(PREFIX)/share'" + @echo " LIB_DIR | path | system libraries location | '\$$(DESTDIR)\$$(PREFIX)/lib'" + @echo " PKGNAME | name | name of the ditributed package | 'grub-btrfs'" + @echo " INITCPIO | bool | include mkinitcpio hook | false" + @echo " SYSTEMD | bool | include unit files | true" + @echo " OPENRC | bool | include OpenRc daemon | false" + @echo \ No newline at end of file diff --git a/config b/config new file mode 100644 index 0000000..2f25d41 --- /dev/null +++ b/config @@ -0,0 +1,132 @@ +#!/usr/bin/env bash + +# Disable grub-btrfs. +# Default: "false" +#GRUB_BTRFS_DISABLE="true" + +# Name appearing in the Grub menu. +# Default: "Use distribution information from /etc/os-release." +#GRUB_BTRFS_SUBMENUNAME="Arch Linux snapshots" + +# Custom title. +# Shows/Hides "date" "snapshot" "type" "description" in the Grub menu, custom order available. +# Default: ("date" "snapshot" "type" "description") +#GRUB_BTRFS_TITLE_FORMAT=("date" "snapshot" "type" "description") + +# Limit the number of snapshots populated in the GRUB menu. +# Default: "50" +#GRUB_BTRFS_LIMIT="50" + +# Sort the found subvolumes by "ogeneration" or "generation" or "path" or "rootid". +# # See Sorting section to https://btrfs.wiki.kernel.org/index.php/Manpage/btrfs-subvolume#SUBCOMMAND +# "-rootid" means list snapshot by new ones first. +# Default: "-rootid" +#GRUB_BTRFS_SUBVOLUME_SORT="+ogen,-gen,path,rootid" + +# Show snapshots found during run "grub-mkconfig" +# Default: "true" +#GRUB_BTRFS_SHOW_SNAPSHOTS_FOUND="false" + +# Show Total of snapshots found during run "grub-mkconfig" +# Default: "true" +#GRUB_BTRFS_SHOW_TOTAL_SNAPSHOTS_FOUND="true" + +# By default, "grub-btrfs" automatically detects most existing kernels. +# If you have one or more custom kernels, you can add them here. +# Default: ("") +#GRUB_BTRFS_NKERNEL=("kernel-custom" "vmlinux-custom") + +# By default, "grub-btrfs" automatically detects most existing initramfs. +# If you have one or more custom initramfs, you can add them here. +# Default: ("") +#GRUB_BTRFS_NINIT=("initramfs-custom.img" "initrd-custom.img" "otherinit-custom.gz") + +# By default, "grub-btrfs" automatically detects most existing microcodes. +# If you have one or more custom microcodes, you can add them here. +# Default: ("") +#GRUB_BTRFS_CUSTOM_MICROCODE=("custom-ucode.img" "custom-uc.img "custom_ucode.cpio") + +# Comma seperated mount options to be used when booting a snapshot. +# They can be defined here as well as in the "/" line inside the respective snapshots' +# "/etc/fstab" files. Mount options found in both places are combined, and this variable +# takes priority over `fstab` entries. +# NB: Do NOT include "subvol=..." or "subvolid=..." here. +# Default: "" +#GRUB_BTRFS_ROOTFLAGS="space_cache,commit=10,norecovery" + +# Ignore specific path during run "grub-mkconfig". +# Only exact paths are ignored. +# e.g : if `specific path` = @, only `@` snapshot will be ignored. +# Default: ("@") +GRUB_BTRFS_IGNORE_SPECIFIC_PATH=("@") + +# Ignore prefix path during run "grub-mkconfig". +# Any path starting with the specified string 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") +GRUB_BTRFS_IGNORE_PREFIX_PATH=("var/lib/docker" "@var/lib/docker" "@/var/lib/docker") + +# Ignore specific type/tag of snapshot during run "grub-mkconfig". +# For snapper: +# Type = single, pre, post. +# For Timeshift: +# Tag = boot, ondemand, hourly, daily, weekly, monthly. +# Default: ("") +#GRUB_BTRFS_IGNORE_SNAPSHOT_TYPE=("") + +# Ignore specific description of snapshot during run "grub-mkconfig". +# e.g: timeline +# Default: ("") +#GRUB_BTRFS_IGNORE_SNAPSHOT_DESCRIPTION=("") + +# By default "grub-btrfs" automatically detects your boot partition, +# either located at the system root or on a separate partition or in a subvolume, +# Change to "true" if your boot partition isn't detected as separate. +# Default: "false" +#GRUB_BTRFS_OVERRIDE_BOOT_PARTITION_DETECTION="true" + +# Location of the folder containing the "grub.cfg" file. +# Use by grub-btrfs to save the file "grub-btrfs.cfg". +# Might be grub2 on some systems. +# For example, on Fedora with EFI : "/boot/efi/EFI/fedora" +# Default: "/boot/grub" +#GRUB_BTRFS_GRUB_DIRNAME="/boot/grub2" + +# Location of kernels/initramfs/microcode. +# Use by "grub-btrfs" to detect the boot partition and the location of kernels/initrafms/microcodes. +# Default: "/boot" +#GRUB_BTRFS_BOOT_DIRNAME="/boot" + +# Name/path of grub-mkconfig command, use by "grub-btrfs.service" +# Might be 'grub2-mkconfig' on some systems (Fedora ...) +# Default paths are /sbin:/bin:/usr/sbin:/usr/bin, +# if your path is missing, report it on the upstream project. +# For example, on Fedora : "/sbin/grub2-mkconfig" +# You can use only name or full path. +# Default: grub-mkconfig +#GRUB_BTRFS_MKCONFIG=/usr/bin/grub2-mkconfig + +# Name of grub-script-check command, use by "grub-btrfs" +# Might be 'grub2-script-check' on some systems (Fedora ...) +# For example, on Fedora : "grub2-script-check" +# Default: grub-script-check +#GRUB_BTRFS_SCRIPT_CHECK=grub2-script-check + +# Path of grub-mkconfig_lib file, use by "grub-btrfs" +# Might be '/usr/share/grub2/grub-mkconfig_lib' on some systems (Opensuse ...) +# Default: /usr/share/grub/grub-mkconfig_lib +#GRUB_BTRFS_MKCONFIG_LIB=/usr/share/grub2/grub-mkconfig_lib + +# Password protection management for submenu,snapshots +# Refer to the Grub documentation https://www.gnu.org/software/grub/manual/grub/grub.html#Authentication-and-authorisation +# and this comment https://github.com/Antynea/grub-btrfs/issues/95#issuecomment-682295660 +# +# Add authorized usernames separate by comma (foo,bar) +# When Grub's password protection is enabled, the superuser is authorized by default, it isn't necessary to add it +# Default: "" +#GRUB_BTRFS_PROTECTION_AUTHORIZED_USERS="foo,bar" +# +# Disable authentication support for submenu of Grub-btrfs only (--unrestricted) +# doesn't work if GRUB_BTRFS_PROTECTION_AUTHORIZED_USERS isn't empty +# Default: "false" +#GRUB_BTRFS_DISABLE_PROTECTION_SUBMENU="true" diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..3a91d5e --- /dev/null +++ b/debian/changelog @@ -0,0 +1,44 @@ +grub-btrfs (4.11-1~1rakuos2) UNRELEASED; urgency=medium + + * Build for RakuOS + + -- Tohur Sat, 08 Nov 2025 14:17:54 -0700 + +grub-btrfs (4.11-1~1siduction1) unstable; urgency=medium + + * Non-maintainer upload. + * rebuild for siduction + + -- Torsten Wohlfarth Mon, 06 Jan 2025 16:44:38 +0100 + +grub-btrfs (4.11-0kali3) kali-dev; urgency=medium + + [ Kali Janitor ] + * Set upstream metadata fields: Repository-Browse. + * Update standards version to 4.6.1, no changes needed. + * Add ITP bugs in 4.11-0kali1. + * Set upstream metadata fields: Repository. + * Set upstream metadata fields: Bug-Database, Bug-Submit. + * Update standards version to 4.6.2, no changes needed. + + [ Steev Klimaszewski ] + * ci: Disable piuparts since the install always fails in a container. + + [ Arnaud Rebillout ] + * Drop comments and useless boilerplate in maintscripts + * Ensure update-grub is installed before using it (thx ak-li1) + * Update control file + + -- Arnaud Rebillout Mon, 06 Jan 2025 09:45:00 +0700 + +grub-btrfs (4.11-0kali2) kali-dev; urgency=medium + + * Depend on grub2-common not grub-common + + -- Steev Klimaszewski Tue, 01 Nov 2022 23:19:46 -0500 + +grub-btrfs (4.11-0kali1) kali-dev; urgency=medium + + * Initial release. Closes: #941627 + + -- Carsten Boeving Fri, 04 Feb 2022 17:55:12 +0700 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..e244ef1 --- /dev/null +++ b/debian/control @@ -0,0 +1,18 @@ +Source: grub-btrfs +Section: utils +Priority: optional +Maintainer: Kali Developers +Uploaders: Carsten Boeving , Arnaud Rebillout +Rules-Requires-Root: no +Build-Depends: debhelper-compat (= 13) +Standards-Version: 4.7.0 +Homepage: https://github.com/Antynea/grub-btrfs +Vcs-Git: https://gitlab.com/kalilinux/packages/grub-btrfs.git +Vcs-Browser: https://gitlab.com/kalilinux/packages/grub-btrfs + +Package: grub-btrfs +Architecture: all +Depends: ${misc:Depends}, btrfs-progs, grub2-common, gawk +Description: adds btrfs snapshots to the grub menu + This package improves the grub bootloader by adding a btrfs snapshots + sub-menu, allowing the user to boot into snapshots. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..5a18b5b --- /dev/null +++ b/debian/copyright @@ -0,0 +1,17 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: grub-btrfs +Source: https://github.com/Antynea/grub-btrfs + +Files: * +Copyright: + Copyright 2014-2022 Antynea +License: GPL-3 + +Files: debian/* +Copyright: 2022 Carsten Boeving +License: GPL-3 +Comment: Debian packaging is licensed under the same terms as upstream + +License: GPL-3 + On Debian systems, the complete text of the GPL-3 can be found in + /usr/share/common-licenses/GPL-3. diff --git a/debian/docs b/debian/docs new file mode 100644 index 0000000..b43bf86 --- /dev/null +++ b/debian/docs @@ -0,0 +1 @@ +README.md diff --git a/debian/gbp.conf b/debian/gbp.conf new file mode 100644 index 0000000..d9115b4 --- /dev/null +++ b/debian/gbp.conf @@ -0,0 +1,10 @@ +[DEFAULT] +debian-branch = kali/master +debian-tag = kali/%(version)s +pristine-tar = True + +[pq] +patch-numbers = False + +[dch] +multimaint-merge = True diff --git a/debian/grub-btrfs.path b/debian/grub-btrfs.path new file mode 120000 index 0000000..888e66a --- /dev/null +++ b/debian/grub-btrfs.path @@ -0,0 +1 @@ +../grub-btrfs.path \ No newline at end of file diff --git a/debian/grub-btrfs.postinst b/debian/grub-btrfs.postinst new file mode 100644 index 0000000..c7c25e0 --- /dev/null +++ b/debian/grub-btrfs.postinst @@ -0,0 +1,16 @@ +#!/bin/sh +# see: dh_installdeb(1) + +set -e + +case "$1" in + configure) + if command -v update-grub >/dev/null; then + update-grub + fi + ;; +esac + +#DEBHELPER# + +# vim:set et ts=4 sw=4: diff --git a/debian/grub-btrfs.postrm b/debian/grub-btrfs.postrm new file mode 100644 index 0000000..8a9a781 --- /dev/null +++ b/debian/grub-btrfs.postrm @@ -0,0 +1,16 @@ +#!/bin/sh +# see: dh_installdeb(1) + +set -e + +case "$1" in + purge) + if command -v update-grub >/dev/null; then + update-grub + fi + ;; +esac + +#DEBHELPER# + +# vim:set et ts=4 sw=4: diff --git a/debian/grub-btrfs.service b/debian/grub-btrfs.service new file mode 120000 index 0000000..a3d4390 --- /dev/null +++ b/debian/grub-btrfs.service @@ -0,0 +1 @@ +../grub-btrfs.service \ No newline at end of file diff --git a/debian/kali-ci.yml b/debian/kali-ci.yml new file mode 100644 index 0000000..28df2f4 --- /dev/null +++ b/debian/kali-ci.yml @@ -0,0 +1,7 @@ +include: + - https://gitlab.com/kalilinux/tools/kali-ci-pipeline/raw/master/recipes/kali.yml + +# The installation attempts to run grub-probe to figure out what device is / +# which doesn't exist in the CI containers, so just disable it in the CI. +variables: + SALSA_CI_DISABLE_PIUPARTS: 1 diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..638f239 --- /dev/null +++ b/debian/rules @@ -0,0 +1,10 @@ +#!/usr/bin/make -f + +%: + dh $@ --buildsystem=none + +override_dh_auto_install: + install -D -m 0755 41_snapshots-btrfs debian/grub-btrfs/etc/grub.d/41_snapshots-btrfs + install -D -m 0644 config debian/grub-btrfs/etc/default/grub-btrfs/config + # Remove the shebang, this file is meant to be sourced + sed -i '1 s/^#!.*//' debian/grub-btrfs/etc/default/grub-btrfs/config diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..163aaf8 --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/debian/upstream/metadata b/debian/upstream/metadata new file mode 100644 index 0000000..0aea16a --- /dev/null +++ b/debian/upstream/metadata @@ -0,0 +1,5 @@ +--- +Bug-Database: https://github.com/Antynea/grub-btrfs/issues +Bug-Submit: https://github.com/Antynea/grub-btrfs/issues/new +Repository: https://github.com/Antynea/grub-btrfs.git +Repository-Browse: https://github.com/Antynea/grub-btrfs diff --git a/debian/watch b/debian/watch new file mode 100644 index 0000000..33f22e2 --- /dev/null +++ b/debian/watch @@ -0,0 +1,4 @@ +version=4 +opts="filenamemangle=s%(?:.*?)?v?(\d[\d.]*@ARCHIVE_EXT@)%@PACKAGE@-$1%" \ + https://github.com/Antynea/grub-btrfs/tags \ + (?:.*?/)?v?@ANY_VERSION@@ARCHIVE_EXT@ diff --git a/grub-btrfs-openrc b/grub-btrfs-openrc new file mode 100755 index 0000000..57e1a55 --- /dev/null +++ b/grub-btrfs-openrc @@ -0,0 +1,6 @@ +#!/bin/sh +echo $$ > /run/grub-btrfsd.pid + +while true; do sleep 1 && inotifywait -e create -e delete /run/timeshift/backup/timeshift-btrfs/snapshots && sleep 5 && if [ -s "${GRUB_BTRFS_GRUB_DIRNAME:-/boot/grub}/grub-btrfs.cfg" ]; then /etc/grub.d/41_snapshots-btrfs; else ${GRUB_BTRFS_MKCONFIG:-grub-mkconfig} -o ${GRUB_BTRFS_GRUB_DIRNAME:-/boot/grub}/grub.cfg; fi ; done + + diff --git a/grub-btrfs.path b/grub-btrfs.path new file mode 100644 index 0000000..b624a95 --- /dev/null +++ b/grub-btrfs.path @@ -0,0 +1,12 @@ +[Unit] +Description=Monitors for new snapshots +DefaultDependencies=no +Requires=\x2esnapshots.mount +After=\x2esnapshots.mount +BindsTo=\x2esnapshots.mount + +[Path] +PathModified=/.snapshots + +[Install] +WantedBy=\x2esnapshots.mount diff --git a/grub-btrfs.service b/grub-btrfs.service new file mode 100644 index 0000000..90d0ef9 --- /dev/null +++ b/grub-btrfs.service @@ -0,0 +1,11 @@ +[Unit] +Description=Regenerate grub-btrfs.cfg + +[Service] +Type=oneshot +# Set the possible paths for `grub-mkconfig` +Environment="PATH=/sbin:/bin:/usr/sbin:/usr/bin" +# Load environment variables from the configuration +EnvironmentFile=/etc/default/grub-btrfs/config +# Regenerate just '/boot/grub/grub-btrfs.cfg' if it exists and is not empty, else regenerate the whole grub menu +ExecStart=bash -c 'if [ -s "${GRUB_BTRFS_GRUB_DIRNAME:-/boot/grub}/grub-btrfs.cfg" ]; then /etc/grub.d/41_snapshots-btrfs; else ${GRUB_BTRFS_MKCONFIG:-grub-mkconfig} -o ${GRUB_BTRFS_GRUB_DIRNAME:-/boot/grub}/grub.cfg; fi' diff --git a/grub-btrfsd b/grub-btrfsd new file mode 100755 index 0000000..971aa62 --- /dev/null +++ b/grub-btrfsd @@ -0,0 +1,14 @@ +#!/sbin/openrc-run +# Copyright 1999-2021 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +name="grub-btrfs daemon" +command="/usr/bin/grub-btrfs-openrc" +pidfile="/run/{RC_SVCNAME}.pid" +command_background=true + +depend() { + use localmount +} + + diff --git a/initramfs/Arch Linux/overlay_snap_ro-hook b/initramfs/Arch Linux/overlay_snap_ro-hook new file mode 100644 index 0000000..07e3b8b --- /dev/null +++ b/initramfs/Arch Linux/overlay_snap_ro-hook @@ -0,0 +1,15 @@ +#!/usr/bin/ash + +run_latehook() { + local root_mnt="/new_root" + local current_dev=$(resolve_device "$root"); # resolve devices for blkid + if [[ $(blkid "${current_dev}" -s TYPE -o value) = "btrfs" ]] && [[ $(btrfs property get ${root_mnt} ro) != "ro=false" ]]; then # run only on a read only snapshot + local lower_dir=$(mktemp -d -p /) + local ram_dir=$(mktemp -d -p /) + mount --move ${root_mnt} ${lower_dir} # move new_root to lower_dir + mount -t tmpfs cowspace ${ram_dir} #meuh!!! space, you can't test ! + mkdir -p ${ram_dir}/upper + mkdir -p ${ram_dir}/work + mount -t overlay -o lowerdir=${lower_dir},upperdir=${ram_dir}/upper,workdir=${ram_dir}/work rootfs ${root_mnt} + fi +} diff --git a/initramfs/Arch Linux/overlay_snap_ro-install b/initramfs/Arch Linux/overlay_snap_ro-install new file mode 100644 index 0000000..2c16291 --- /dev/null +++ b/initramfs/Arch Linux/overlay_snap_ro-install @@ -0,0 +1,18 @@ +#!/bin/bash + +build() { + add_module btrfs + add_module overlay + add_binary btrfs + add_binary btrfsck + add_binary blkid + add_runscript +} + +help() { + cat <