X-Git-Url: https://iankelling.org/git/?a=blobdiff_plain;f=mount-latest-subvol;h=5cb226c5676b9539ea96ebab8241b5ccc6ebbec4;hb=460cc07230c2040305068f17a687e06c7bc13dfe;hp=359b534337669090503de236c2ab30467c35b4dd;hpb=3174b9cb665b9a7b7ac8413a8e236cd236786632;p=distro-setup diff --git a/mount-latest-subvol b/mount-latest-subvol index 359b534..5cb226c 100644 --- a/mount-latest-subvol +++ b/mount-latest-subvol @@ -13,36 +13,35 @@ # See the License for the specific language governing permissions and # limitations under the License. -# usage: mount-latest-subvol -# -# Note, at source location, intentionally not executable, run and read -# install-my-scripts. - +script=$(readlink -f -- "$BASH_SOURCE") cd / -[[ $EUID == 0 ]] || exec sudo -E "$BASH_SOURCE" "$@" +[[ $EUID == 0 ]] || exec sudo -E "$script" "$@" -errcatch() { - set -E; shopt -s extdebug - _err-trap() { - err=$? - exec >&2 - set +x - echo "${BASH_SOURCE[1]}:${BASH_LINENO[0]}:in \`$BASH_COMMAND' returned $err" - bash-trace 2 - echo "$0: exiting with code $err" - exit $err - } - trap _err-trap ERR - set -o pipefail + +usage() { + cat < 1 )); then source="${BASH_SOURCE[frame+1]}:${BASH_LINENO[frame]}:" fi - indent=$((frame-start+1)) + indent=$((frame-start + 1)) indent=$((indent < max_indent ? indent : max_indent)) printf "%${indent}s↳%sin \`%s" '' "$source" "${FUNCNAME[frame]}" if $extdebug; then @@ -60,8 +59,25 @@ bash-trace() { fi echo \' done + return 0 +} +err-catch() { + set -E; shopt -s extdebug + _err-trap() { + err=$? + exec >&2 + set +x + echo "${BASH_SOURCE[1]}:${BASH_LINENO[0]}: \`$BASH_COMMAND' returned $err" + err-bash-trace 2 + set -e # err trap does not work within an error trap + "${_errcatch_cleanup[@]:-:}" # note :-: is to be compatible with set -u + echo "$0: exiting with code $err" + exit $err + } + trap _err-trap ERR + set -o pipefail } -errcatch +err-catch tu() { while read -r line; do @@ -69,12 +85,22 @@ tu() { grep -xFq "$line" "$file" || tee -a "$file"<<<"$line" done } -e() { printf "%s\n" "$*"; "$@"; } +m() { + if $verbose; then + printf "%s\n" "$*" + fi + "$@" +} +x() { + printf "%s\n" "$*" + "$@" +} + mnt() { dir=$1 - if ! mountpoint $dir &>/dev/null; then + if ! mountpoint -q $dir; then mkdir -p $dir - e mount $dir + m mount $dir fi } fstab() { @@ -121,10 +147,26 @@ kill-dir() { return 1 } +##### begin command line parsing ######## + +# you can remove this if you do not have options which can have args with spaces or empty. + +verbose=false force=false -if [[ $1 == -f ]]; then - force=true -fi +temp=$(getopt -l help,force,verbose hfv "$@") || usage 1 +eval set -- "$temp" +while true; do + case $1 in + -f|--force) force=true ;; + -v|--verbose) verbose=true ;; + -h|--help) usage ;; + --) shift; break ;; + *) echo "$0: unexpected args: $*" >&2 ; usage 1 ;; + esac + shift +done + +##### end command line parsing ######## ret=0 @@ -132,7 +174,7 @@ ret=0 root_dev=$(awk '$2 == "/" {print $1}' /etc/mtab) if [[ $root_dev == /dev/dm-* ]]; then for d in /dev/mapper/*; do - if [[ $(readlink -f $d) == $root_dev ]]; then + if [[ $(readlink -f $d) == "$root_dev" ]]; then root_dev=$d break fi @@ -163,7 +205,7 @@ shopt -s nullglob # ownership, and ssh doesn\'t allow any group writable parent # directories, so we are forced to use a directory structure similar # to home directories -f=(/mnt/root/btrbk/q.*) +f=(/mnt/root/btrbk/q.*); f=${f[0]} if [[ -e $f ]]; then fstab </dev/null); then mnt $d @@ -239,15 +281,15 @@ for vol in q a o i; do umount_ret=true unmounted=() for dir in $(echo $d ${binds[*]}\ |tac -s\ ); do - if mountpoint $dir; then - if e umount -R $dir; then + if mountpoint -q $dir; then + if m umount -R $dir; then unmounted+=($dir) else if ! kill-dir TERM TERM TERM INT INT HUP HUP; then if $force; then kill-dir KILL; fi fi - if e umount -R $dir; then + if m umount -R $dir; then unmounted+=($dir) else echo "$0: failed to umount $dir" @@ -259,6 +301,7 @@ for vol in q a o i; do fi done + # if we unmounted some but not all, restore them and move on if ! $umount_ret; then for dir in ${unmounted[@]}; do mnt $dir @@ -266,27 +309,71 @@ for vol in q a o i; do continue fi + #### begin dealing with leaf vols #### # todo: decipher /mnt/root, like we do in check-subvol-stale cd /mnt/root if [[ -e $vol ]]; then - e mv $vol $vol.leaf.$(date +%Y-%m-%dT%H:%M:%S%z) + leaf=$vol.leaf.$(date +%Y-%m-%dT%H:%M:%S%z) + m mv $vol $leaf + m btrfs property set -ts $leaf ro true + + ### begin check if leaf is different, delete it if not ### + if [[ -e /a/opt/btrfs-snapshots-diff/btrfs-snapshots-diff.py ]]; then + source /a/bin/distro-functions/src/package-manager-abstractions + pi python-jmespath # dependency + parentid=$(btrfs sub show $leaf | awk '$1 == "Parent" && $2 == "UUID:" {print $3}') + bsubs=(/mnt/root/btrbk/$vol.*) + bsub= + # go in reverse order as its more likely to be at the end + for ((i=${#bsubs[@]}-1; i>=0; i--)); do + if [[ $parentid == $(btrfs sub show ${bsubs[i]} | awk '$1 == "UUID:" {print $2}') ]]; then + bsub=${bsubs[i]} + break + fi + done + if [[ $bsub ]]; then + tmp=$(mktemp) + # in testing, same subvol is 136 bytes. allow some overhead + btrfs send --no-data -p $bsub $leaf | head -c 1000 > $tmp || [[ $? == 141 ]] + if (( $(stat -c%s $tmp) < 1000)); then + # example output for an empty diff: + # Found a valid Btrfs stream header, version 1 + # o.leaf.2019-05-15T14:00:50-0400;snapshot: uuid=ba045ea30737dd449003f1ee40ec12d0, ctrasid=109533, clone_uuid=3c7e3544e486834aa71d89e5b8f30056, clone_ctransid=109533 + lines=$(/a/opt/btrfs-snapshots-diff/btrfs-snapshots-diff.py -s -f $tmp | \ + grep -vxF "Found a valid Btrfs stream header, version 1" | \ + grep -cv "^[^;]*;snapshot: ") ||: + if [[ $lines == 0 ]]; then + x btrfs sub del $leaf + fi + fi + fi + fi + ### end check if leaf is different, delete it if not ### + + ## begin expire leaf vols ## leaf_vols=($vol.leaf.*) + count=1 for leaf in ${leaf_vols[@]}; do leaf_secs=$(date -d ${leaf#$vol.leaf.} +%s) - if (( $(date +%s) - 60*60*24*60 > leaf_secs )); then # 60 days - e btrfs sub del $leaf + if (( $(date +%s) - 60*60*24*60 > leaf_secs || count > 200 )); then # 60 days + x btrfs sub del $leaf fi + count=$((count+1)) done + ## end expire leaf vols ## fi + #### end dealing with leaf vols #### + # Note, we make a few assumptions in this script, like # $d was not a different subvol id than $vol, and # things otherwise didn't get mounted very strangely. - e btrfs sub snapshot $fresh_snap $vol + m btrfs sub snapshot $fresh_snap $vol for dir in $d ${binds[@]}; do - e mnt $dir + m mnt $dir done stale_dir=/nocow/btrfs-stale rm -f $stale_dir/$d + done ### disabled