From 4bc1f6a2964b7323fbfde35fe489dd264bc168b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorsten=20Sch=C3=B6ning?= Date: Sun, 24 Jul 2022 15:04:59 +0200 Subject: [PATCH 01/17] Map mountpoints and their corresponing subvolumes to be able to filter subvolumes being mountpoints already and take things like /@/usr/local vs. /usr/local into account. --- btrfs-auto-snapshot | 88 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 78 insertions(+), 10 deletions(-) diff --git a/btrfs-auto-snapshot b/btrfs-auto-snapshot index 3633ae4..96c38bb 100755 --- a/btrfs-auto-snapshot +++ b/btrfs-auto-snapshot @@ -19,6 +19,10 @@ # this program; if not, write to the Free Software Foundation, Inc., 59 Temple # Place, Suite 330, Boston, MA 02111-1307 USA +#set -o errexit +#set -o functrace +#set -o xtrace + # Define our version version=2.0.2 @@ -319,6 +323,38 @@ argsp_cmdline_exit_handler() exit ${argsp_cmdline_exit} } +## +# Calculate the BTRFS-mountpoints to start looking at things from. +# +# The important thing to note is that BTRFS layouts can be VERY different: Some systems use +# multiple mountpoints for multiple subvolumes, some use only one mountpoint, because all +# the subvolumes of interest are children of that path already, the names of subvolumes in +# BTRFS might be different from the path they are mounted too etc. Though, it's totally OK +# to mix mounted and unmounted subvolumes, while at some point we need to distinguish them. +# This is done by storing the mountpoints, which we assume to always be subvolumes, else +# individual mounpoints don't make too much sense, and their corresponding subvolume path. +# When retrieving all subvolumes of all mountpoints, this allows removing already known +# child subvolumes which are mountpoints on their own already and processed as such. +# +# @return Associative array mapping individual mountpoints and their subvolumes. +# +btrfs_mounts_calc() +{ + local -r mps="$(grep 'btrfs' '/proc/mounts')" + local -r sed_find='^.+,subvol=([^ ]+) .+$' + local -A ret_val + + while IFS= read -r mp + do + local path="$( echo "${mp}" | awk '{print $2}')" + local subvol="$(echo "${mp}" | sed -r "s/${sed_find}/\1/")" + + ret_val[${path}]="${subvol}" + done <<< "${mps}" + + declare -p ret_val | sed -e 's/^declare -A [^=]*=//' +} + ## # Calculate all BTRFS subvolumes based on all mounted BTRFS file systems. # @@ -328,35 +364,67 @@ argsp_cmdline_exit_handler() # well. By default, each and every subvolume simply gets a ".btrfs" directory to take # snapshots and snapshots themself are of course excluded here. # -# @stdin BTRFS file systems of interest, one per line. +# @stdin Associative array mapping mountpoints and their subvolume. # @return All subvolumes, one per line. # btrfs_subvols_calc() { local -a ret_val=() - local -r array_txt="$(argsp_stdin_to_array)" - eval "declare -a mps=${array_txt}" + eval "declare -A mps=$(cat '/dev/stdin')" # shellcheck disable=SC2154 - for mp in "${mps[@]}" + for mp in "${!mps[@]}" do # The mountpoint itself obviously is a subvolume of interest as well already. # shellcheck disable=SC2190 ret_val+=("${mp}") - local subvols; subvols="$(btrfs subvolume list "${mp}" | awk '{print $9}')" + # The following returns subvolume paths relative to the given path, which needs to + # be kept in mind when comparing things! + local subvols="$(btrfs subvolume list -o "${mp}" | awk '{print $9}')" # Subvolumes seem to have no parent UUID, while snapshots are "readonly" most # likely. So check for these attributes, which seems easier than to exclude all # currently available snapshots by their paths. That output doesn't include # leading slashes, their common directory might change its name etc. + # + # Some found subvolumes might be children of the current mountpoint, but still be + # mounted somewhere else on their own and therefore need to be ignored, as all + # mountpoints get processed individually already. To make things worse, paths in + # the filesystem expected by some BTRFS-tools might be different than the path of + # some subvolume from BTRFS's perspective and as output by some tools like "list". + # So resulting paths need to be build by using the current mountpoint, it's own + # subvolume path and the currently processed subvolume. + # + # mps[/]="/@" -> possibly containing "/btrfs_test" as "/@/btrfs_test" + # mps[/usr/local]="/@/usr/local" while IFS= read -r subvol do - local abs_path; abs_path="$(echo "${mp}"/"${subvol}" | sed -r 's!^//!/!')" - local show; show="$(btrfs subvolume show "${abs_path}")" + # TODO Should the following check for emptyness be necessary?! + if [ -z "${subvol}" ] + then + continue + fi + + # Ignore subvolumes being children and mountpoints at the same time. + if printf "%s\n" "${mps[@]}" | grep -F -f - -q -x <(echo "${subvol}") + then + continue + fi + if printf "%s\n" "${mps[@]}" | grep -F -f - -q -x <(echo "/${subvol}") + then + continue + fi + + # Map subvolume path to filesystem path. + local abs_path="$(echo "${mp}${subvol}")" + abs_path="$(echo "${abs_path}" | sed -r "s!^${mps[${mp}]}!${mp}!")" + abs_path="$(echo "${abs_path}" | sed -r 's!^//!/!')" + + local show="$(btrfs subvolume show "${abs_path}")" local sp='[[:space:]]+' - local no_parent_uuid; no_parent_uuid="$(echo "${show}" | grep --count -E "^${sp}Parent UUID:${sp}-$")" - local is_read_only; is_read_only="$( echo "${show}" | grep --count -E "^${sp}Flags:${sp}readonly$")" + local no_parent_uuid="$(echo "${show}" | grep --count -E "^${sp}Parent UUID:${sp}-$")" + local is_read_only="$( echo "${show}" | grep --count -E "^${sp}Flags:${sp}readonly$")" if [ "${no_parent_uuid}" = '1' ] && [ "${is_read_only}" = '0' ] then @@ -477,7 +545,7 @@ fi snapname=${prefix}_${label}_$(date +%F-%H%M) snapglob=".btrfs/${prefix}_${label}????????????????" -btrfs_list=$(grep btrfs /proc/mounts | awk '{print $2}' | btrfs_subvols_calc) +btrfs_list="$(btrfs_mounts_calc | btrfs_subvols_calc)" eval "declare -a btrfs_list=${btrfs_list}" if [ "${cmdline[paths]}" = '//' ] From a5c6bf52da2fd600afcaccaac637943ca50e05fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorsten=20Sch=C3=B6ning?= Date: Sun, 24 Jul 2022 15:47:16 +0200 Subject: [PATCH 02/17] Snapshots need to be ignored as well, because "list" provides those. Those are STARTING with mountpoints, not being identical to those. --- btrfs-auto-snapshot | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/btrfs-auto-snapshot b/btrfs-auto-snapshot index 96c38bb..d5cf9de 100755 --- a/btrfs-auto-snapshot +++ b/btrfs-auto-snapshot @@ -336,7 +336,7 @@ argsp_cmdline_exit_handler() # When retrieving all subvolumes of all mountpoints, this allows removing already known # child subvolumes which are mountpoints on their own already and processed as such. # -# @return Associative array mapping individual mountpoints and their subvolumes. +# @return Associative array mapping mountpoints and their subvolumes. # btrfs_mounts_calc() { @@ -364,7 +364,7 @@ btrfs_mounts_calc() # well. By default, each and every subvolume simply gets a ".btrfs" directory to take # snapshots and snapshots themself are of course excluded here. # -# @stdin Associative array mapping mountpoints and their subvolume. +# @stdin Associative array mapping mountpoints and their subvolumes # @return All subvolumes, one per line. # btrfs_subvols_calc() @@ -379,8 +379,8 @@ btrfs_subvols_calc() # shellcheck disable=SC2190 ret_val+=("${mp}") - # The following returns subvolume paths relative to the given path, which needs to - # be kept in mind when comparing things! + # The following seems to return relative paths based to the BTRFS root always, + # which might be empty or "@" or ... and is different to the filesystem root "/". local subvols="$(btrfs subvolume list -o "${mp}" | awk '{print $9}')" # Subvolumes seem to have no parent UUID, while snapshots are "readonly" most @@ -406,12 +406,12 @@ btrfs_subvols_calc() continue fi - # Ignore subvolumes being children and mountpoints at the same time. - if printf "%s\n" "${mps[@]}" | grep -F -f - -q -x <(echo "${subvol}") + # Ignore all subvolumes being children and handled as mountpoints already. + if printf "%s\n" "${mps[@]}" | grep -F -f - -q <(echo "${subvol}") then continue fi - if printf "%s\n" "${mps[@]}" | grep -F -f - -q -x <(echo "/${subvol}") + if printf "%s\n" "${mps[@]}" | grep -F -f - -q <(echo "/${subvol}") then continue fi @@ -473,8 +473,9 @@ btrfs_snaps_do() } ## -# Cleanup snapshots depending on how many to keep, if to cleanup at all. +# Cleanup snapshots depending on how many to keep and if to cleanup at all. # +# @stdin Associative array mapping mountpoints and their subvolumes. # @param[in] The paths to work with. # btrfs_snaps_rm_if() @@ -485,6 +486,7 @@ btrfs_snaps_rm_if() fi local -r fs_list="${1:?No paths given.}" + eval "declare -A mps=$(cat '/dev/stdin')" log info "Destroying all but the newest ${keep} snapshots" @@ -545,7 +547,8 @@ fi snapname=${prefix}_${label}_$(date +%F-%H%M) snapglob=".btrfs/${prefix}_${label}????????????????" -btrfs_list="$(btrfs_mounts_calc | btrfs_subvols_calc)" +btrfs_mounts="$(btrfs_mounts_calc)" +btrfs_list="$(echo "${btrfs_mounts}" | btrfs_subvols_calc)" eval "declare -a btrfs_list=${btrfs_list}" if [ "${cmdline[paths]}" = '//' ] @@ -556,6 +559,6 @@ else fi btrfs_snaps_do "${fs_list}" -btrfs_snaps_rm_if "${fs_list}" +btrfs_snaps_rm_if "${fs_list}" <<< "${btrfs_mounts}" # vim: set expandtab:ts=4:sw=4 From ba72e3f59c29b2dd78eb4b4961de537f0e850f0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorsten=20Sch=C3=B6ning?= Date: Sun, 24 Jul 2022 16:33:56 +0200 Subject: [PATCH 03/17] Cleaning up snaps seems to work again after chaning path handling mountpoints vs. subvols and the leading prefix like @ vs. /@ etc. --- btrfs-auto-snapshot | 51 +++++++++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/btrfs-auto-snapshot b/btrfs-auto-snapshot index d5cf9de..d563e70 100755 --- a/btrfs-auto-snapshot +++ b/btrfs-auto-snapshot @@ -146,9 +146,13 @@ argsp_stdin_to_array() while IFS= read -r line do - local is_blank; is_blank="$( echo "${line}" | grep --count -e '^[[:space:]]*$')" - local is_comment; is_comment="$(echo "${line}" | grep --count -e '^#')" - local is_empty; is_empty="$( echo "${line}" | grep --count -e '^$')" + local is_blank + local is_comment + local_is_empty + + is_blank="$( echo "${line}" | grep --count -e '^[[:space:]]*$')" + is_comment="$(echo "${line}" | grep --count -e '^#')" + is_empty="$( echo "${line}" | grep --count -e '^$')" if [ "${is_blank}" = '1' ] || [ "${is_comment}" = '1' ] || [ "${is_empty}" = '1' ] then @@ -346,8 +350,11 @@ btrfs_mounts_calc() while IFS= read -r mp do - local path="$( echo "${mp}" | awk '{print $2}')" - local subvol="$(echo "${mp}" | sed -r "s/${sed_find}/\1/")" + local path + local subvol + + path="$( echo "${mp}" | awk '{print $2}')" + subvol="$(echo "${mp}" | sed -r "s/${sed_find}/\1/")" ret_val[${path}]="${subvol}" done <<< "${mps}" @@ -375,13 +382,15 @@ btrfs_subvols_calc() # shellcheck disable=SC2154 for mp in "${!mps[@]}" do + local subvols + # The mountpoint itself obviously is a subvolume of interest as well already. # shellcheck disable=SC2190 ret_val+=("${mp}") # The following seems to return relative paths based to the BTRFS root always, # which might be empty or "@" or ... and is different to the filesystem root "/". - local subvols="$(btrfs subvolume list -o "${mp}" | awk '{print $9}')" + subvols="$(btrfs subvolume list -o "${mp}" | awk '{print $9}')" # Subvolumes seem to have no parent UUID, while snapshots are "readonly" most # likely. So check for these attributes, which seems easier than to exclude all @@ -416,15 +425,21 @@ btrfs_subvols_calc() continue fi + local abs_path + local show + local sp + local no_parenbt_uuid + local is_read_only + # Map subvolume path to filesystem path. - local abs_path="$(echo "${mp}${subvol}")" - abs_path="$(echo "${abs_path}" | sed -r "s!^${mps[${mp}]}!${mp}!")" - abs_path="$(echo "${abs_path}" | sed -r 's!^//!/!')" + abs_path="$(echo "${mp}${subvol}")" + abs_path="$(echo "${abs_path}" | sed -r "s!^${mps[${mp}]}!${mp}!")" + abs_path="$(echo "${abs_path}" | sed -r 's!^//!/!')" - local show="$(btrfs subvolume show "${abs_path}")" - local sp='[[:space:]]+' - local no_parent_uuid="$(echo "${show}" | grep --count -E "^${sp}Parent UUID:${sp}-$")" - local is_read_only="$( echo "${show}" | grep --count -E "^${sp}Flags:${sp}readonly$")" + show="$(btrfs subvolume show "${abs_path}")" + sp='[[:space:]]+' + no_parent_uuid="$(echo "${show}" | grep --count -E "^${sp}Parent UUID:${sp}-$")" + is_read_only="$( echo "${show}" | grep --count -E "^${sp}Flags:${sp}readonly$")" if [ "${no_parent_uuid}" = '1' ] && [ "${is_read_only}" = '0' ] then @@ -493,13 +508,18 @@ btrfs_snaps_rm_if() for i in $fs_list do fs_keep="${keep}" + subvol="${mps[${i}]}" + subvol="$(echo "${subvol}" | sed -r 's!^/!!')" + snaps="$(btrfs subvolume list -g -o -s --sort=gen "${i}")" paths="$(echo "${snaps}" | sort -r -n -k 4 | awk '{print $NF}')" + paths="$(echo "${paths}" | sed -r "s!^${subvol}!${i}!")" + paths="$(echo "${paths}" | sed -r "s!^//!/!")" paths="$(echo "${paths}" | sed -r 's!^[^/]+/.btrfs/!.btrfs/!')" while IFS= read -r j do - if [ -n "${j#"$snapglob"}" ] + if [ -z "${j#"$snapglob"}" ] then continue fi @@ -507,8 +527,7 @@ btrfs_snaps_rm_if() fs_keep=$(( fs_keep - 1 )) if [ ${fs_keep} -lt 0 ] then - log notice "$( ${dry_run} btrfs subvolume \ - delete -c "${i}"/"${j}" )" + log notice "$( ${dry_run} btrfs subvolume delete -c "${j}" )" fi done <<< "${paths}" done From 1cb0c4842701d6839304b1a01f1390f2349a954d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorsten=20Sch=C3=B6ning?= Date: Sun, 24 Jul 2022 16:51:39 +0200 Subject: [PATCH 04/17] Filtering subvolumes vs. mountpoints vs. snapshots was still broken. I accidently filteres e.f. /btrfs_test, even though that was NOT a mounpoint, but only a subvolume within /. --- btrfs-auto-snapshot | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/btrfs-auto-snapshot b/btrfs-auto-snapshot index d563e70..77ed99e 100755 --- a/btrfs-auto-snapshot +++ b/btrfs-auto-snapshot @@ -415,27 +415,22 @@ btrfs_subvols_calc() continue fi - # Ignore all subvolumes being children and handled as mountpoints already. - if printf "%s\n" "${mps[@]}" | grep -F -f - -q <(echo "${subvol}") - then - continue - fi - if printf "%s\n" "${mps[@]}" | grep -F -f - -q <(echo "/${subvol}") - then - continue - fi - local abs_path local show local sp - local no_parenbt_uuid + local no_parent_uuid local is_read_only # Map subvolume path to filesystem path. - abs_path="$(echo "${mp}${subvol}")" - abs_path="$(echo "${abs_path}" | sed -r "s!^${mps[${mp}]}!${mp}!")" + abs_path="$(echo "/${subvol}" | sed -r "s!^${mps[${mp}]}!${mp}!")" abs_path="$(echo "${abs_path}" | sed -r 's!^//!/!')" + # Ignore all subvolumes being children and handled as mountpoints already. + if printf "%s\n" "${!mps[@]}" | grep -F -f - -x -q <(echo "${abs_path}") + then + continue + fi + show="$(btrfs subvolume show "${abs_path}")" sp='[[:space:]]+' no_parent_uuid="$(echo "${show}" | grep --count -E "^${sp}Parent UUID:${sp}-$")" From 575ac662ce3806b47bccd3946c81625bb418ac61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorsten=20Sch=C3=B6ning?= Date: Sun, 24 Jul 2022 18:53:01 +0200 Subject: [PATCH 05/17] Cleanup of /btrfs_test didn't work properly as that has been filtered when taking mountpoints into accounts. Isn't necessary at all, we can simply assume naming convention because "list" only shows snaps of interest during cleanup. --- btrfs-auto-snapshot | 87 ++++++++++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 32 deletions(-) diff --git a/btrfs-auto-snapshot b/btrfs-auto-snapshot index 77ed99e..29c35f0 100755 --- a/btrfs-auto-snapshot +++ b/btrfs-auto-snapshot @@ -344,8 +344,10 @@ argsp_cmdline_exit_handler() # btrfs_mounts_calc() { + # Ignore the leading slash by purpose, as most BTRFS-tools output relativ paths to + # their own BTRFS-root or some given path as well. local -r mps="$(grep 'btrfs' '/proc/mounts')" - local -r sed_find='^.+,subvol=([^ ]+) .+$' + local -r sed_find='^.+,subvol=/([^ ]+) .+$' local -A ret_val while IFS= read -r mp @@ -354,7 +356,7 @@ btrfs_mounts_calc() local subvol path="$( echo "${mp}" | awk '{print $2}')" - subvol="$(echo "${mp}" | sed -r "s/${sed_find}/\1/")" + subvol="$(echo "${mp}" | sed -r "s!${sed_find}!\1!")" ret_val[${path}]="${subvol}" done <<< "${mps}" @@ -376,21 +378,23 @@ btrfs_mounts_calc() # btrfs_subvols_calc() { - local -a ret_val=() + local -a ret_val eval "declare -A mps=$(cat '/dev/stdin')" # shellcheck disable=SC2154 for mp in "${!mps[@]}" do - local subvols + local mp_subvol + local mp_subvols # The mountpoint itself obviously is a subvolume of interest as well already. # shellcheck disable=SC2190 + mp_subvol="${mps[${mp}]}" ret_val+=("${mp}") # The following seems to return relative paths based to the BTRFS root always, # which might be empty or "@" or ... and is different to the filesystem root "/". - subvols="$(btrfs subvolume list -o "${mp}" | awk '{print $9}')" + mp_subvols="$(btrfs subvolume list -o "${mp}" | awk '{print $9}')" # Subvolumes seem to have no parent UUID, while snapshots are "readonly" most # likely. So check for these attributes, which seems easier than to exclude all @@ -405,8 +409,8 @@ btrfs_subvols_calc() # So resulting paths need to be build by using the current mountpoint, it's own # subvolume path and the currently processed subvolume. # - # mps[/]="/@" -> possibly containing "/btrfs_test" as "/@/btrfs_test" - # mps[/usr/local]="/@/usr/local" + # mps[/]="@" -> possibly containing "/btrfs_test" as "@/btrfs_test" + # mps[/usr/local]="@/usr/local" while IFS= read -r subvol do # TODO Should the following check for emptyness be necessary?! @@ -422,7 +426,7 @@ btrfs_subvols_calc() local is_read_only # Map subvolume path to filesystem path. - abs_path="$(echo "/${subvol}" | sed -r "s!^${mps[${mp}]}!${mp}!")" + abs_path="$(echo "${subvol}" | sed -r "s!^${mp_subvol}!${mp}!")" abs_path="$(echo "${abs_path}" | sed -r 's!^//!/!')" # Ignore all subvolumes being children and handled as mountpoints already. @@ -441,31 +445,46 @@ btrfs_subvols_calc() # shellcheck disable=SC2190 ret_val+=("${abs_path}") fi - done <<< "${subvols}" + done <<< "${mp_subvols}" done declare -p ret_val | sed -e 's/^declare -a [^=]*=//' } ## -# Create snapshots using the paths +# Check if the given paths are BTRFS subvolumes at all. # -# @param[in] The paths to work with. +# @stdin Associative array mapping subvolumes and their parent mountpoints. +# @param[in] The paths to check. # -btrfs_snaps_do() +btrfs_wrk_paths_check() { - local -r fs_list="${1:?No paths given.}" - - log info "Doing snapshots of $fs_list" + local -r wrk_paths="${1:?No paths given.}" + eval "declare -a btrfs_subvols=$(cat '/dev/stdin')" - for i in $fs_list + for i in $wrk_paths do - if ! printf "%s\n" "${btrfs_list[@]}" | grep -F -f - -q -x <(echo "${i}") + if ! printf "%s\n" "${btrfs_subvols[@]}" | grep -F -f - -q -x <(echo "${i}") then log err "It appears that '${i}' is not a BTRFS filesystem!" exit ${ERR_FS_NO_BTRFS} fi + done +} + +## +# Create snapshots using the paths +# +# @param[in] The paths to work with. +# +btrfs_snaps_do() +{ + local -r wrk_paths="${1:?No paths given.}" + + log info "Doing snapshots of $wrk_paths" + for i in $wrk_paths + do if [ ! -d "${i}/.btrfs" ] then ${dry_run} mkdir "${i}/.btrfs" @@ -485,7 +504,6 @@ btrfs_snaps_do() ## # Cleanup snapshots depending on how many to keep and if to cleanup at all. # -# @stdin Associative array mapping mountpoints and their subvolumes. # @param[in] The paths to work with. # btrfs_snaps_rm_if() @@ -495,22 +513,26 @@ btrfs_snaps_rm_if() return fi - local -r fs_list="${1:?No paths given.}" - eval "declare -A mps=$(cat '/dev/stdin')" + local -r wrk_paths="${1:?No paths given.}" log info "Destroying all but the newest ${keep} snapshots" - for i in $fs_list + for i in $wrk_paths do fs_keep="${keep}" - subvol="${mps[${i}]}" - subvol="$(echo "${subvol}" | sed -r 's!^/!!')" + # We are only interested in snaps this time, which follow a hard-coded naming + # scheme currently. This makes it easy to ignore all subvolumes being children of + # the current path for some reason and therefore present in the output. We either + # don't care about those or handle them anyway as part of "//". So we only care + # about the output containing some special directory name. The args given to list + # make sure that we only get snaps for the subvolume of intrest and no others, so + # it's somewhat safe to remove based on conventions. snaps="$(btrfs subvolume list -g -o -s --sort=gen "${i}")" paths="$(echo "${snaps}" | sort -r -n -k 4 | awk '{print $NF}')" - paths="$(echo "${paths}" | sed -r "s!^${subvol}!${i}!")" - paths="$(echo "${paths}" | sed -r "s!^//!/!")" - paths="$(echo "${paths}" | sed -r 's!^[^/]+/.btrfs/!.btrfs/!')" + paths="$(echo "${paths}" | sed '#/.btrfs/#!d')" + paths="$(echo "${paths}" | sed -r "s!^.+/.btrfs/!${i}/.btrfs/!")" + paths="$(echo "${paths}" | sed -r "s!^//.btrfs/!/.btrfs/!")" while IFS= read -r j do @@ -562,17 +584,18 @@ snapname=${prefix}_${label}_$(date +%F-%H%M) snapglob=".btrfs/${prefix}_${label}????????????????" btrfs_mounts="$(btrfs_mounts_calc)" -btrfs_list="$(echo "${btrfs_mounts}" | btrfs_subvols_calc)" -eval "declare -a btrfs_list=${btrfs_list}" +btrfs_subvols_txt="$(echo "${btrfs_mounts}" | btrfs_subvols_calc)" +eval "declare -a btrfs_subvols=${btrfs_subvols_txt}" if [ "${cmdline[paths]}" = '//' ] then - fs_list="${btrfs_list[*]}" + wrk_paths="${btrfs_subvols[@]}" else - fs_list="${cmdline[paths]}" + wrk_paths="${cmdline[paths]}" fi -btrfs_snaps_do "${fs_list}" -btrfs_snaps_rm_if "${fs_list}" <<< "${btrfs_mounts}" +btrfs_wrk_paths_check "${wrk_paths}" <<< "${btrfs_subvols_txt}" +btrfs_snaps_do "${wrk_paths}" +btrfs_snaps_rm_if "${wrk_paths}" # vim: set expandtab:ts=4:sw=4 From 31cda24d944699e745c72b0f26247472032c2015 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorsten=20Sch=C3=B6ning?= Date: Sun, 24 Jul 2022 19:22:05 +0200 Subject: [PATCH 06/17] Checks of working paths didn't work anymore,m shelcheck errors. --- btrfs-auto-snapshot | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/btrfs-auto-snapshot b/btrfs-auto-snapshot index 29c35f0..82bc37f 100755 --- a/btrfs-auto-snapshot +++ b/btrfs-auto-snapshot @@ -378,7 +378,7 @@ btrfs_mounts_calc() # btrfs_subvols_calc() { - local -a ret_val + local -a ret_val=() eval "declare -A mps=$(cat '/dev/stdin')" # shellcheck disable=SC2154 @@ -454,17 +454,16 @@ btrfs_subvols_calc() ## # Check if the given paths are BTRFS subvolumes at all. # -# @stdin Associative array mapping subvolumes and their parent mountpoints. # @param[in] The paths to check. # btrfs_wrk_paths_check() { local -r wrk_paths="${1:?No paths given.}" - eval "declare -a btrfs_subvols=$(cat '/dev/stdin')" + local -r patterns="$(printf "%s\n" "${btrfs_subvols[@]}")" for i in $wrk_paths do - if ! printf "%s\n" "${btrfs_subvols[@]}" | grep -F -f - -q -x <(echo "${i}") + if ! echo "${patterns}" | grep -F -f - -q -x <(echo "${i}") then log err "It appears that '${i}' is not a BTRFS filesystem!" exit ${ERR_FS_NO_BTRFS} @@ -589,12 +588,12 @@ eval "declare -a btrfs_subvols=${btrfs_subvols_txt}" if [ "${cmdline[paths]}" = '//' ] then - wrk_paths="${btrfs_subvols[@]}" + wrk_paths="${btrfs_subvols[*]}" else wrk_paths="${cmdline[paths]}" fi -btrfs_wrk_paths_check "${wrk_paths}" <<< "${btrfs_subvols_txt}" +btrfs_wrk_paths_check "${wrk_paths}" btrfs_snaps_do "${wrk_paths}" btrfs_snaps_rm_if "${wrk_paths}" From cf035f132d13f8986a6a99a766e12aed2eeb6a03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorsten=20Sch=C3=B6ning?= Date: Sun, 24 Jul 2022 19:28:33 +0200 Subject: [PATCH 07/17] Disable some checks. --- btrfs-auto-snapshot | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/btrfs-auto-snapshot b/btrfs-auto-snapshot index 82bc37f..894a9b1 100755 --- a/btrfs-auto-snapshot +++ b/btrfs-auto-snapshot @@ -388,8 +388,8 @@ btrfs_subvols_calc() local mp_subvols # The mountpoint itself obviously is a subvolume of interest as well already. - # shellcheck disable=SC2190 mp_subvol="${mps[${mp}]}" + # shellcheck disable=SC2190 ret_val+=("${mp}") # The following seems to return relative paths based to the BTRFS root always, @@ -459,6 +459,7 @@ btrfs_subvols_calc() btrfs_wrk_paths_check() { local -r wrk_paths="${1:?No paths given.}" + # shellcheck disable=SC2154 local -r patterns="$(printf "%s\n" "${btrfs_subvols[@]}")" for i in $wrk_paths From 972c1b5daec3a04987d448f582dcda16e5734247 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorsten=20Sch=C3=B6ning?= Date: Sun, 24 Jul 2022 19:53:52 +0200 Subject: [PATCH 08/17] Some changes? --- btrfs-auto-snapshot | 1 + 1 file changed, 1 insertion(+) diff --git a/btrfs-auto-snapshot b/btrfs-auto-snapshot index 894a9b1..8c3836e 100755 --- a/btrfs-auto-snapshot +++ b/btrfs-auto-snapshot @@ -536,6 +536,7 @@ btrfs_snaps_rm_if() while IFS= read -r j do + if [ -z "${j#"$snapglob"}" ] then continue From cdc570bec3b49af1ce03a17efae5d3360a3a2403 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorsten=20Sch=C3=B6ning?= Date: Sun, 24 Jul 2022 19:55:06 +0200 Subject: [PATCH 09/17] Code style. --- btrfs-auto-snapshot | 1 - 1 file changed, 1 deletion(-) diff --git a/btrfs-auto-snapshot b/btrfs-auto-snapshot index 8c3836e..894a9b1 100755 --- a/btrfs-auto-snapshot +++ b/btrfs-auto-snapshot @@ -536,7 +536,6 @@ btrfs_snaps_rm_if() while IFS= read -r j do - if [ -z "${j#"$snapglob"}" ] then continue From af00f71e02f7014f0a89deae05a551c294117909 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorsten=20Sch=C3=B6ning?= Date: Sun, 24 Jul 2022 19:56:41 +0200 Subject: [PATCH 10/17] Code style. --- btrfs-auto-snapshot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/btrfs-auto-snapshot b/btrfs-auto-snapshot index 894a9b1..ba58081 100755 --- a/btrfs-auto-snapshot +++ b/btrfs-auto-snapshot @@ -148,7 +148,7 @@ argsp_stdin_to_array() do local is_blank local is_comment - local_is_empty + local is_empty is_blank="$( echo "${line}" | grep --count -e '^[[:space:]]*$')" is_comment="$(echo "${line}" | grep --count -e '^#')" From 312374fe7225c8302f4fb18ebbb2e513815201f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorsten=20Sch=C3=B6ning?= Date: Sun, 24 Jul 2022 23:47:41 +0200 Subject: [PATCH 11/17] Code style. --- btrfs-auto-snapshot | 46 ++++++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/btrfs-auto-snapshot b/btrfs-auto-snapshot index ba58081..b368312 100755 --- a/btrfs-auto-snapshot +++ b/btrfs-auto-snapshot @@ -40,15 +40,17 @@ ERR_FS_NO_BTRFS=135 trap argsp_cmdline_exit_handler SIGUSR1 # set defaults +DEF_SNAPS_DIR='.btrfs' + argsp_cmdline_exit="${ERR_SUCCESS}" debug='' dry_run='' keep='' label='' prefix='btrfs-auto-snap' +quiet='' use_syslog='' verbose='' -quiet='' writeable='-r' ## @@ -420,21 +422,27 @@ btrfs_subvols_calc() fi local abs_path - local show - local sp - local no_parent_uuid - local is_read_only + local pattern + local matches # Map subvolume path to filesystem path. abs_path="$(echo "${subvol}" | sed -r "s!^${mp_subvol}!${mp}!")" abs_path="$(echo "${abs_path}" | sed -r 's!^//!/!')" # Ignore all subvolumes being children and handled as mountpoints already. - if printf "%s\n" "${!mps[@]}" | grep -F -f - -x -q <(echo "${abs_path}") + pattern="$(printf "%s\n" "${!mps[@]}")" + matches="$(echo "${pattern}" | grep --count -F -f - -x <(echo "${abs_path}"))" + + if [ "${matches}" != '0' ] then continue fi + local show + local sp + local no_parent_uuid + local is_read_only + show="$(btrfs subvolume show "${abs_path}")" sp='[[:space:]]+' no_parent_uuid="$(echo "${show}" | grep --count -E "^${sp}Parent UUID:${sp}-$")" @@ -464,7 +472,10 @@ btrfs_wrk_paths_check() for i in $wrk_paths do - if ! echo "${patterns}" | grep -F -f - -q -x <(echo "${i}") + local matches + + matches="$(echo "${patterns}" | grep --count -F -f - -x <(echo "${i}"))" + if [ "${matches}" = '0' ] then log err "It appears that '${i}' is not a BTRFS filesystem!" exit ${ERR_FS_NO_BTRFS} @@ -485,9 +496,9 @@ btrfs_snaps_do() for i in $wrk_paths do - if [ ! -d "${i}/.btrfs" ] + if [ ! -d "${i}/${DEF_SNAPS_DIR}" ] then - ${dry_run} mkdir "${i}/.btrfs" + ${dry_run} mkdir "${i}/${DEF_SNAPS_DIR}" fi # TODO Creating snapshots too frequently so that names overlap with an existing @@ -497,7 +508,7 @@ btrfs_snaps_do() # already. log notice "$( ${dry_run} btrfs subvolume snapshot \ ${writeable} "${i}" \ - "${i%/}/.btrfs/${snapname}" )" + "${i%/}/${DEF_SNAPS_DIR}/${snapname}" )" done } @@ -530,9 +541,9 @@ btrfs_snaps_rm_if() # it's somewhat safe to remove based on conventions. snaps="$(btrfs subvolume list -g -o -s --sort=gen "${i}")" paths="$(echo "${snaps}" | sort -r -n -k 4 | awk '{print $NF}')" - paths="$(echo "${paths}" | sed '#/.btrfs/#!d')" - paths="$(echo "${paths}" | sed -r "s!^.+/.btrfs/!${i}/.btrfs/!")" - paths="$(echo "${paths}" | sed -r "s!^//.btrfs/!/.btrfs/!")" + paths="$(echo "${paths}" | sed "#/${DEF_SNAPS_DIR}/#!d")" + paths="$(echo "${paths}" | sed -r "s!^.+/${DEF_SNAPS_DIR}/!${i}/${DEF_SNAPS_DIR}/!")" + paths="$(echo "${paths}" | sed -r "s!^//${DEF_SNAPS_DIR}/!/${DEF_SNAPS_DIR}/!")" while IFS= read -r j do @@ -565,14 +576,15 @@ eval "declare -A cmdline=${cmdline}" debug="${cmdline[debug]}" dry_run="${cmdline[dry_run]}" +help="${cmdline[help]}" keep="${cmdline[keep]}" label="${cmdline[label]}" prefix="${cmdline[prefix]}" +quiet="${cmdline[quiet]}" use_syslog="${cmdline[use_syslog]}" verbose="${cmdline[verbose]}" -quiet="${cmdline[quiet]}" writeable="${cmdline[writable]}" -help="${cmdline[help]}" + if [ "$help" -eq 1 ] then @@ -581,7 +593,7 @@ then fi snapname=${prefix}_${label}_$(date +%F-%H%M) -snapglob=".btrfs/${prefix}_${label}????????????????" +snapglob="${DEF_SNAPS_DIR}/${prefix}_${label}????????????????" btrfs_mounts="$(btrfs_mounts_calc)" btrfs_subvols_txt="$(echo "${btrfs_mounts}" | btrfs_subvols_calc)" @@ -594,7 +606,7 @@ else wrk_paths="${cmdline[paths]}" fi -btrfs_wrk_paths_check "${wrk_paths}" +btrfs_wrk_paths_check "${wrk_paths}" <<< "${btrfs_subvols_txt}" btrfs_snaps_do "${wrk_paths}" btrfs_snaps_rm_if "${wrk_paths}" From db5006435ea24e5ada5567040128208b0cf00ea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorsten=20Sch=C3=B6ning?= Date: Sun, 24 Jul 2022 23:50:26 +0200 Subject: [PATCH 12/17] Code style. --- btrfs-auto-snapshot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/btrfs-auto-snapshot b/btrfs-auto-snapshot index b368312..172bc51 100755 --- a/btrfs-auto-snapshot +++ b/btrfs-auto-snapshot @@ -606,7 +606,7 @@ else wrk_paths="${cmdline[paths]}" fi -btrfs_wrk_paths_check "${wrk_paths}" <<< "${btrfs_subvols_txt}" +btrfs_wrk_paths_check "${wrk_paths}" btrfs_snaps_do "${wrk_paths}" btrfs_snaps_rm_if "${wrk_paths}" From a12b96e3de36d6f7f87605c2779fb13a6af7327f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorsten=20Sch=C3=B6ning?= Date: Mon, 25 Jul 2022 00:46:58 +0200 Subject: [PATCH 13/17] Changed iomplementation of deleting snaps to be a bit better debuggable with additional steps and stored paths. Thought the error message sometimes appearing might be related to wrong handling of globs etc. Don't think so anymore, as that error message seems to come before deleting at all. --- btrfs-auto-snapshot | 44 ++++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/btrfs-auto-snapshot b/btrfs-auto-snapshot index 172bc51..f08bbab 100755 --- a/btrfs-auto-snapshot +++ b/btrfs-auto-snapshot @@ -82,7 +82,7 @@ usage() -q, --quiet Suppress warning and notices on STDOUT -v, --verbose Print info messages -w, --writeable Create writeable snapshots instead of read-only - name Filesystem name(s), or '//' for all filesystems + name File system name(s), or '//' for all file systems " } @@ -291,7 +291,7 @@ argsp_cmdline() then if [ "${ret_val[help]}" -eq 0 ] then - log error "The filesystem argument list is empty." + log error "The file system argument list is empty." log error "Please see $0 --help." argsp_cmdline_exit=${ERR_FS_MISSING} kill -SIGUSR1 $$ @@ -395,7 +395,7 @@ btrfs_subvols_calc() ret_val+=("${mp}") # The following seems to return relative paths based to the BTRFS root always, - # which might be empty or "@" or ... and is different to the filesystem root "/". + # which might be empty or "@" or ... and is different to the file system root "/". mp_subvols="$(btrfs subvolume list -o "${mp}" | awk '{print $9}')" # Subvolumes seem to have no parent UUID, while snapshots are "readonly" most @@ -406,7 +406,7 @@ btrfs_subvols_calc() # Some found subvolumes might be children of the current mountpoint, but still be # mounted somewhere else on their own and therefore need to be ignored, as all # mountpoints get processed individually already. To make things worse, paths in - # the filesystem expected by some BTRFS-tools might be different than the path of + # the file system expected by some BTRFS-tools might be different than the path of # some subvolume from BTRFS's perspective and as output by some tools like "list". # So resulting paths need to be build by using the current mountpoint, it's own # subvolume path and the currently processed subvolume. @@ -425,7 +425,7 @@ btrfs_subvols_calc() local pattern local matches - # Map subvolume path to filesystem path. + # Map subvolume path to file system path. abs_path="$(echo "${subvol}" | sed -r "s!^${mp_subvol}!${mp}!")" abs_path="$(echo "${abs_path}" | sed -r 's!^//!/!')" @@ -477,7 +477,7 @@ btrfs_wrk_paths_check() matches="$(echo "${patterns}" | grep --count -F -f - -x <(echo "${i}"))" if [ "${matches}" = '0' ] then - log err "It appears that '${i}' is not a BTRFS filesystem!" + log err "It appears that '${i}' is not a BTRFS file system!" exit ${ERR_FS_NO_BTRFS} fi done @@ -487,10 +487,12 @@ btrfs_wrk_paths_check() # Create snapshots using the paths # # @param[in] The paths to work with. +# @param[in] Snapshot name to create. # btrfs_snaps_do() { local -r wrk_paths="${1:?No paths given.}" + local -r snap_name="${2:?No snap name given.}" log info "Doing snapshots of $wrk_paths" @@ -508,7 +510,7 @@ btrfs_snaps_do() # already. log notice "$( ${dry_run} btrfs subvolume snapshot \ ${writeable} "${i}" \ - "${i%/}/${DEF_SNAPS_DIR}/${snapname}" )" + "${i%/}/${DEF_SNAPS_DIR}/${snap_name}" )" done } @@ -516,6 +518,7 @@ btrfs_snaps_do() # Cleanup snapshots depending on how many to keep and if to cleanup at all. # # @param[in] The paths to work with. +# @param[in] Pattern to find snapshot names for the current prefix and label. # btrfs_snaps_rm_if() { @@ -525,13 +528,12 @@ btrfs_snaps_rm_if() fi local -r wrk_paths="${1:?No paths given.}" + local -r snap_patt="${2:?No snap pattern given.}" log info "Destroying all but the newest ${keep} snapshots" for i in $wrk_paths do - fs_keep="${keep}" - # We are only interested in snaps this time, which follow a hard-coded naming # scheme currently. This makes it easy to ignore all subvolumes being children of # the current path for some reason and therefore present in the output. We either @@ -541,22 +543,15 @@ btrfs_snaps_rm_if() # it's somewhat safe to remove based on conventions. snaps="$(btrfs subvolume list -g -o -s --sort=gen "${i}")" paths="$(echo "${snaps}" | sort -r -n -k 4 | awk '{print $NF}')" - paths="$(echo "${paths}" | sed "#/${DEF_SNAPS_DIR}/#!d")" + paths="$(echo "${paths}" | sed "\#/${DEF_SNAPS_DIR}/#!d")" paths="$(echo "${paths}" | sed -r "s!^.+/${DEF_SNAPS_DIR}/!${i}/${DEF_SNAPS_DIR}/!")" paths="$(echo "${paths}" | sed -r "s!^//${DEF_SNAPS_DIR}/!/${DEF_SNAPS_DIR}/!")" + paths="$(echo "${paths}" | sed -r "\#/${DEF_SNAPS_DIR}/${snap_patt}#!d")" + paths="$(echo "${paths}" | tail -n "+$((keep + 1))")" while IFS= read -r j do - if [ -z "${j#"$snapglob"}" ] - then - continue - fi - - fs_keep=$(( fs_keep - 1 )) - if [ ${fs_keep} -lt 0 ] - then - log notice "$( ${dry_run} btrfs subvolume delete -c "${j}" )" - fi + log notice "$( ${dry_run} btrfs subvolume delete -c "${j}" )" done <<< "${paths}" done } @@ -592,8 +587,9 @@ then exit $ERR_SUCCESS fi -snapname=${prefix}_${label}_$(date +%F-%H%M) -snapglob="${DEF_SNAPS_DIR}/${prefix}_${label}????????????????" +snap_name="${prefix}_${label}_$(date +%F-%H%M)" +snap_patt='[[:digit:]]{4}-[[:digit:]]{2}-[[:digit:]]{2}-[[:digit:]]{4}' +snap_patt="${prefix}_${label}_${snap_patt}" btrfs_mounts="$(btrfs_mounts_calc)" btrfs_subvols_txt="$(echo "${btrfs_mounts}" | btrfs_subvols_calc)" @@ -607,7 +603,7 @@ else fi btrfs_wrk_paths_check "${wrk_paths}" -btrfs_snaps_do "${wrk_paths}" -btrfs_snaps_rm_if "${wrk_paths}" +btrfs_snaps_do "${wrk_paths}" "${snap_name}" +btrfs_snaps_rm_if "${wrk_paths}" "${snap_patt}" # vim: set expandtab:ts=4:sw=4 From 5edac478b35f893d71a786d55fd2e38019524f3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorsten=20Sch=C3=B6ning?= Date: Mon, 25 Jul 2022 01:05:38 +0200 Subject: [PATCH 14/17] Ignore empty paths when deleting, which might be perfectly OK if there's nothing to delete. --- btrfs-auto-snapshot | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/btrfs-auto-snapshot b/btrfs-auto-snapshot index f08bbab..572a07e 100755 --- a/btrfs-auto-snapshot +++ b/btrfs-auto-snapshot @@ -551,6 +551,12 @@ btrfs_snaps_rm_if() while IFS= read -r j do + # TODO Should the following check for emptyness be necessary?! + if [ -z "${j}" ] + then + continue + fi + log notice "$( ${dry_run} btrfs subvolume delete -c "${j}" )" done <<< "${paths}" done From 0989fec319096bb04608b726c8b32fefc68ebd15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorsten=20Sch=C3=B6ning?= Date: Mon, 25 Jul 2022 11:01:54 +0200 Subject: [PATCH 15/17] Delete an already existing snapshot if it shares the same timestamp. This makes test easier until the problem gets fully discussed. --- btrfs-auto-snapshot | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/btrfs-auto-snapshot b/btrfs-auto-snapshot index 572a07e..29d9e84 100755 --- a/btrfs-auto-snapshot +++ b/btrfs-auto-snapshot @@ -498,19 +498,28 @@ btrfs_snaps_do() for i in $wrk_paths do - if [ ! -d "${i}/${DEF_SNAPS_DIR}" ] + local snaps_dir + local snap_path + local snap_opts + + snaps_dir="${i%/}/${DEF_SNAPS_DIR}" + snap_path="${snaps_dir}/${snap_name}" + snap_opts="${writeable} ${i} ${snap_path}" + + if [ ! -d "${snaps_dir}" ] then - ${dry_run} mkdir "${i}/${DEF_SNAPS_DIR}" + ${dry_run} mkdir "${snaps_dir}" fi - # TODO Creating snapshots too frequently so that names overlap with an existing - # one, result in some error message about read-only file system, not mentioning - # the actual snapshot itself at all. Is bit difficult to understand when - # happening especially during tests, so might check if the desired snapshot exists - # already. - log notice "$( ${dry_run} btrfs subvolume snapshot \ - ${writeable} "${i}" \ - "${i%/}/${DEF_SNAPS_DIR}/${snap_name}" )" + # TODO Creating snapshots too frequently might result in their directory names + # overlapping, which either results in error messages about read-only file systems + # or additional subdirs created in existing snapshot dirs. The latter is a problem + # as it prevents deletion of those snapshots because they contain non-snap data. + # The following is a workaround for those cases especially making tests easier. + # Some better fix might be to check for if the snapshot exists already and don't + # create it or use seconds in the names. Needs to be discussed further... + log notice "$( ${dry_run} btrfs subvolume delete -c "${snap_path}" 2> '/dev/null' )" + log notice "$( ${dry_run} btrfs subvolume snapshot ${snap_opts} )" done } From d044460cfc394e016f8b9075f3dfdbf621b625d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorsten=20Sch=C3=B6ning?= Date: Mon, 25 Jul 2022 11:31:01 +0200 Subject: [PATCH 16/17] Spelling error regarding writable snapshots. --- btrfs-auto-snapshot | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/btrfs-auto-snapshot b/btrfs-auto-snapshot index 29d9e84..a075b65 100755 --- a/btrfs-auto-snapshot +++ b/btrfs-auto-snapshot @@ -498,13 +498,13 @@ btrfs_snaps_do() for i in $wrk_paths do - local snaps_dir - local snap_path - local snap_opts + local snaps_dir + local snap_path + local -a snap_opts=() snaps_dir="${i%/}/${DEF_SNAPS_DIR}" snap_path="${snaps_dir}/${snap_name}" - snap_opts="${writeable} ${i} ${snap_path}" + snap_opts=(${writeable} "${i}" "${snap_path}") if [ ! -d "${snaps_dir}" ] then @@ -519,7 +519,7 @@ btrfs_snaps_do() # Some better fix might be to check for if the snapshot exists already and don't # create it or use seconds in the names. Needs to be discussed further... log notice "$( ${dry_run} btrfs subvolume delete -c "${snap_path}" 2> '/dev/null' )" - log notice "$( ${dry_run} btrfs subvolume snapshot ${snap_opts} )" + log notice "$( ${dry_run} btrfs subvolume snapshot "${snap_opts[@]}" )" done } @@ -593,8 +593,7 @@ prefix="${cmdline[prefix]}" quiet="${cmdline[quiet]}" use_syslog="${cmdline[use_syslog]}" verbose="${cmdline[verbose]}" -writeable="${cmdline[writable]}" - +writeable="${cmdline[writeable]}" if [ "$help" -eq 1 ] then From 9385f355944935dedbe4340afffc58d0f0c0147e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorsten=20Sch=C3=B6ning?= Date: Mon, 25 Jul 2022 11:46:39 +0200 Subject: [PATCH 17/17] Make shellcheck happier... --- btrfs-auto-snapshot | 1 + 1 file changed, 1 insertion(+) diff --git a/btrfs-auto-snapshot b/btrfs-auto-snapshot index a075b65..c34ed71 100755 --- a/btrfs-auto-snapshot +++ b/btrfs-auto-snapshot @@ -504,6 +504,7 @@ btrfs_snaps_do() snaps_dir="${i%/}/${DEF_SNAPS_DIR}" snap_path="${snaps_dir}/${snap_name}" + # shellcheck disable=SC2206 snap_opts=(${writeable} "${i}" "${snap_path}") if [ ! -d "${snaps_dir}" ]