this_file="$(readlink -f -- "${BASH_SOURCE[0]}")"
readonly this_file this_dir="${this_file%/*}"
-
cd /
[[ $EUID == 0 ]] || exec sudo -E "$this_file" "$@"
-
set -e; . /usr/local/lib/bash-bear; set +e
-shopt -s nullglob
+
+shopt -s nullglob
+# shellcheck source=/a/bin/ds/filesystem/usr/local/bin/umount-funcs
source "$this_dir/umount-funcs"
usage() {
exit $1
}
-pre="mount-latest-subvol:${SSH_CLIENT:+ $HOSTNAME:}"
+readonly pre="mount-latest-subvol:${SSH_CLIENT:+ $HOSTNAME:}"
log_path=/var/log/btrbk/$(date +%F_%H_%M_%S%:::z).log
+readonly log_path
+# tee unique lines.
tu() {
+ local file line
while read -r line; do
file="$1"
grep -xFq "$line" "$file" || tee -a "$file"<<<"$line"
done
}
+# debug: echo when $verbose
d() {
if $verbose; then
printf "$pre %s\n" "$*"
fi
}
+# btrfs log command
b() {
printf "$pre running: %s\n" "$*" |& pee cat 'ts "%F %T" >>'$log_path
"$@" |& pee cat 'ts "%F %T" >>'$log_path
}
+# if $verbose, print command before executing.
m() {
if $verbose; then
printf "$pre %s\n" "$*"
"$@"
}
+# mkdir + mount from fstab.
mnt() {
- dir=$1
- if ! mountpoint -q $dir; then
- mkdir -p $dir
- m mount $dir
- fi
+ local dir
+ for dir; do
+ # note: if we had umount-kill keep track of failed umounts, we would
+ # not need to check if it is a mountpoint.
+ if ! mountpoint -q $dir; then
+ mkdir -p $dir
+ m mount $dir
+ fi
+ done
}
+# write piped lines to fstab, wiping out any existing lines that have
+# the same first 2 fields.
fstab() {
- while read -r start mpoint end; do
- l="$start $mpoint $end"
+ local mount_source mpoint line_end l
+ while read -r mount_source mpoint line_end; do
+ l="$mount_source $mpoint $line_end"
# kill off any lines that duplicate the mount point.
sed --follow-symlinks -ri "\%$l%b;\%^\s*\S+\s+$mpoint\s%d" /etc/fstab
tu /etc/fstab <<<"$l"
done
}
-
-# duplicated in check-subvol
-# Reassign $1 var from /dev/dm- to corresponding /dev/mapper/
+# Note: duplicated in check-subvol-stale.
+# Change the variable $1 references from /dev/dm- to corresponding /dev/mapper/
mapper-dev() {
local mapdev
local -n devref=$1
}
-##### begin command line parsing ########
-
-# you can remove this if you do not have options which can have args with spaces or empty.
-
-verbose=true
-force=false
-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
-
-if (( $# )); then
- all_vols=( "$@" )
-else
- all_vols=(q a o i qd qr)
- ar_snaps=(/mnt/root/btrbk/ar.*)
- if [[ -e /mnt/root/ar ]] || (( ${#ar_snaps[@]} > 0 )); then
- all_vols+=(ar)
+# Input vars: $d, eg: /a
+#
+# Output vars: $root_dir, eg: /mnt/root
+#
+# Note: code-organizational only function.
+get-btrfs-root-mountpoint() {
+ local dev devx
+ ### this is duplicated in check-subvol-stale
+ dev=$(sed -rn "s,^\s*([^#]\S*)\s+$d\s.*,\1,p" /etc/fstab /etc/mtab|head -n1)
+ # example dev: /dev/mapper/crypt-vgDISK_BY_ID_NAME-root
+ d dev=$dev
+ # $devx are the devices of the btrfs filesystem. they include $dev.
+ #
+ # Note, we can't just use $d in this sed because it might not be mounted. We do this loop
+ # because the device in fstab for the rootfs mountpoint can be different than $d.
+ for devx in $(btrfs fil show $dev| sed -rn 's#.*path (\S+)$#\1#p'); do
+ if [[ $devx == dm-* ]]; then
+ devx=/dev/$devx
+ mapper-dev devx
+ fi
+ d devx=$devx
+ root_dir=$(sed -rn "s,^\s*$devx\s+(\S+).*\bsubvolid=[05]\b.*,\1,p" /etc/mtab /etc/fstab|head -n1)
+ if [[ $root_dir ]]; then
+ d root_dir=$root_dir
+ break
+ fi
+ done
+ if [[ ! $root_dir ]]; then
+ echo "$0: error could not find root subvol mount for $dev" >&2
+ exit 1
fi
-fi
-
-##### end command line parsing ########
-
-ret=0
-mkdir -p /var/log/btrbk
-
-##### begin setup fstab for subvols we care about ######
+}
-if [[ -e /mnt/root/root2-crypttab ]]; then
- tu /etc/crypttab </mnt/root/root2-crypttab
- while read -r mapper_dev _; do
- if [[ ! -e /dev/mapper/$mapper_dev ]]; then
- m cryptdisks_start $mapper_dev
+# Input vars: $vol $root_dir (from get-btrfs-root-mountpoint)
+expire-leaf-vols() {
+ local leaf_vols count leaf_limit_time leaf_new_limit_time leaf
+ leaf_vols=($root_dir/$vol.leaf.*)
+ count=${#leaf_vols[@]}
+ leaf_limit_time=$(( EPOCHSECONDS - 60*60*24*60 )) # 60 days
+ leaf_new_limit_time=$(( EPOCHSECONDS - 60*60*24 * 5 )) # 5 days
+
+ # This goes backwards from oldest. leaf_new_limit_time is to avoid
+ # deleting very recent leafs.
+ for leaf in ${leaf_vols[@]}; do
+ leaf_time=$(date -d ${leaf##"$vol".leaf.} +%s)
+ if (( leaf_limit_time > leaf_time || ( leaf_new_limit_time > leaf_time && count > 30 ) )); then
+ b btrfs sub del $leaf
fi
- done < <(cat /mnt/root/root2-crypttab)
-fi
-if [[ -e /mnt/root/root2-fstab ]]; then
- tu /etc/fstab </mnt/root/root2-fstab
- mnt /mnt/root2
- mnt /mnt/boot2
-fi
-
-root_dev=$(awk '$2 == "/" {print $1}' /etc/mtab)
-mapper-dev root_dev
-o_dev=$(awk '$2 == "/mnt/o" {print $1}' /etc/mtab)
-mapper-dev o_dev
-
-
-# root2_dev=$(awk '$2 == "/mnt/root2" {print $1}' /etc/mtab)
-# mapper-dev root2_dev
-# # dont bother with the above for crypt2_dev
-# crypt2_dev=$root2_dev
-
+ count=$((count-1))
+ done
+}
-if cryptsetup status $root_dev &>/dev/null; then
- crypt_dev=$root_dev
-else # if we are in a recovery boot, find the next best crypt device
- mopts=,noauto
- # todo: I think I had an idea to not setup /o in this case,
- # but never finished implementing it
- for dev in $(dmsetup ls --target crypt | awk '{print $1}'); do
- dev=/dev/mapper/$dev
- if awk '{print $1}' /etc/mtab | grep -Fx $dev &>/dev/null; then
- crypt_dev=$dev
+# Check if subvol $1 is different than its btrbk parent subvol, delete it if not.
+#
+# Silently give up if $1 is not a child of a btrbk subvol.
+#
+# Input vars: $root_dir (from get-btrfs-root-mountpoint)
+dedupe-btrbk() {
+ local parentid subvol parent_sub i lines
+ local -a bsubs
+ subvol="$1"
+ parentid=$(btrfs sub show $subvol | awk '$1 == "Parent" && $2 == "UUID:" {print $3}')
+ bsubs=($root_dir/btrbk/$vol.*)
+ # 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
+ parent_sub=${bsubs[i]}
break
fi
done
-fi
-
-
-
-# dont tax the cpus of old laptops
-if (( $(nproc) > 2)); then
- mopts+=,compress=zstd
-fi
-
-fstab <<EOF
-$crypt_dev /a btrfs noatime,subvol=a$mopts 0 0
-EOF
-
-
-# ssh and probably some other things care about parent directory
-# ownership, and ssh doesn\'t allow any group writable parent
-# directories, so we are forced to use a directory structure similar
-# to home directories
-fa=(/mnt/root/btrbk/q.*); f=${fa[0]}
-if [[ -e $f ]]; then
- fstab <<EOF
-$crypt_dev /q btrfs noatime,subvol=q$mopts 0 0
-$crypt_dev /qd btrfs noatime,subvol=qd$mopts 0 0
-/q/p /p none bind$mopts 0 0
-EOF
-fi
-
-fa=(/mnt/root/btrbk/qr.*); f=${fa[0]}
-if [[ -e $f ]]; then
- fstab <<EOF
-$crypt_dev /qr btrfs noatime,subvol=qr$mopts 0 0
-EOF
-fi
+ if [[ ! $parent_sub ]]; then
+ d "Warning: failed to find parent of subvol=$subvol in dir: $root_dir/btrbk"
+ return 0
+ fi
+ # in testing, same subvol is 136 bytes. allow some overhead. 32 happens sometimes under systemd.
+ # $ errno 32
+ # EPIPE 32 Broken pipe
+ lines=$(btrfs send --no-data -p $parent_sub $subvol | btrfs receive --dump | head -n 100 | wc -l || [[ $? == 141 || ${PIPESTATUS[0]} == 32 ]])
+ if [[ $lines == 0 ]]; then
+ # example output of no differences:
+ # snapshot ./qrtest uuid=c41ff6b7-0527-f34d-95ac-190eecf54ff5 transid=2239 parent_uuid=64949e1b-4a3e-3945-9a8e-cd7b7c15d7d6 parent_transid=2239
+ echo suspected identical: $parent_sub $subvol
+ b btrfs sub del $subvol
+ fi
+}
-# not syncing ar at the moment
-# fa=(/mnt/root/btrbk/ar.*); f=${fa[0]}
-# if [[ -e $f ]]; then
-# fstab <<EOF
-# $crypt_dev /ar btrfs noatime,subvol=ar$mopts 0 0
-# EOF
-# fi
+# Maybe move volume to leaf.
+#
+# Input vars: $vol, $root_dir (from get-btrfs-root-mountpoint)
+#
+# Note: we cd $root_dir.
+mv-vol-to-leaf() {
+ local leaf
+ cd $root_dir
-fa=(/mnt/o/btrbk/o.*); f=${fa[0]}
-if [[ -e $f ]]; then
- if [[ $o_dev != "$root_dev" ]]; then
- # ,compress=zstd regardless of mopts since these are all text files
- # and it cuts disk use by about half.
- fstab <<EOF
-$o_dev /o btrfs noatime,subvol=o${mopts/,compress=zstd/},compress=zstd 0 0
-EOF
+ if [[ ! -e $vol ]]; then
+ return 0
fi
- fstab <<EOF
-/o/m /m none bind$mopts 0 0
-/o/debbugs /debbugs none bind$mopts 0 0
-EOF
-fi
+ # no leaf volumes for qd
+ if [[ $vol == qd ]]; then
+ b btrfs sub del qd
+ return 0
+ fi
-##### end setup fstab for subvols we care about ######
+ dedupe-btrbk $root_dir/$vol
+ leaf=$vol.leaf.$(date +%Y-%m-%dT%H:%M:%S%z)
+ b mv $vol $leaf
+ b btrfs property set -ts $leaf ro true
+}
-vol-setup() {
+# Input vars: $vol
+#
+# Output vars: $d ${binds[@]}
+#
+# setup $vol related variables.
+set-d-binds() {
+ local r
+ local -a new_roots roots
d=/$vol
- ##### begin building up list of bind mounts ######
- binds=() # list of bind mounts
+ binds=() # list of fstab mountpoints that are bind mounts of /d/..
roots=($d)
while true; do
new_roots=()
# roots is used to recursively find binds of binds if they exist.
roots=( ${new_roots[@]} )
done
- ##### end building up list of bind mounts ######
}
-
-### begin pruning volumes ###
-pruned_vols=()
-for vol in ${all_vols[@]}; do
- if ! awk '$3 == "btrfs" {print $2}' /etc/fstab | grep -xF /$vol &>/dev/null; then
- continue
+# Maybe add /mnt/{r,b}oot2 to fstab and mount them.
+# Input vars: none
+setup-root2() {
+ local mapper_dev
+ if [[ -e /mnt/root/root2-crypttab ]]; then
+ tu /etc/crypttab </mnt/root/root2-crypttab
+ while read -r mapper_dev _; do
+ if [[ ! -e /dev/mapper/$mapper_dev ]]; then
+ m cryptdisks_start $mapper_dev
+ fi
+ done < <(cat /mnt/root/root2-crypttab)
fi
- ##### begin checking for loopback mounts #####
- found_loop=false
- for l in $(losetup -ln|awk '{print $6}'); do
- for dir in $d ${binds[@]}; do
- if [[ $l == $dir* ]]; then
- echo "$0: found loopback mount $l. giving up on unmounting $dir"
- ret=1
- found_loop=true
+ if [[ -e /mnt/root/root2-fstab ]]; then
+ tu /etc/fstab </mnt/root/root2-fstab
+ mnt /mnt/{r,b}oot2
+ fi
+}
+
+# Setup fstab for subvols we care about.
+# Input vars: none
+setup-fstab() {
+ local root_dev o_dev crypt_dev mopts dev f fa
+ root_dev=$(awk '$2 == "/" {print $1}' /etc/mtab)
+ mapper-dev root_dev
+ o_dev=$(awk '$2 == "/mnt/o" {print $1}' /etc/mtab)
+ mapper-dev o_dev
+
+
+ # root2_dev=$(awk '$2 == "/mnt/root2" {print $1}' /etc/mtab)
+ # mapper-dev root2_dev
+ # # dont bother with the above for crypt2_dev
+ # crypt2_dev=$root2_dev
+
+
+ if cryptsetup status $root_dev &>/dev/null; then
+ crypt_dev=$root_dev
+ else # if we are in a recovery boot, find the next best crypt device
+ mopts=,noauto
+ # todo: I think I had an idea to not setup /o in this case,
+ # but never finished implementing it
+ for dev in $(dmsetup ls --target crypt | awk '{print $1}'); do
+ dev=/dev/mapper/$dev
+ if awk '{print $1}' /etc/mtab | grep -Fx $dev &>/dev/null; then
+ crypt_dev=$dev
break
fi
done
- if $found_loop; then
- break
- fi
- done
- if $found_loop; then
- continue
fi
- ##### end end checking loopback mounts #####
- pruned_vols+=("$vol")
-done
-### end pruning volumes ###
+ # dont tax the cpus of old laptops
+ if (( $(nproc) > 2)); then
+ mopts+=,compress=zstd
+ fi
-umount_dirs=()
-declare -A umount_vols
+ fstab <<EOF
+$crypt_dev /a btrfs noatime,subvol=a$mopts 0 0
+EOF
+ # ssh and probably some other things care about parent directory
+ # ownership, and ssh doesn\'t allow any group writable parent
+ # directories, so we are forced to use a directory structure similar
+ # to home directories
+ fa=(/mnt/root/btrbk/q.*); f=${fa[0]}
+ if [[ -e $f ]]; then
+ fstab <<EOF
+$crypt_dev /q btrfs noatime,subvol=q$mopts 0 0
+$crypt_dev /qd btrfs noatime,subvol=qd$mopts 0 0
+/q/p /p none bind$mopts 0 0
+EOF
+ fi
-for vol in ${pruned_vols[@]}; do
- vol-setup
+ fa=(/mnt/root/btrbk/qr.*); f=${fa[0]}
+ if [[ -e $f ]]; then
+ fstab <<EOF
+$crypt_dev /qr btrfs noatime,subvol=qr$mopts 0 0
+EOF
+ fi
- # if latest is already mounted, make sure binds are mounted and move on
- m check-subvol-stale $d
- # populated by check-subvol-stale if stale
- if [[ -s /nocow/btrfs-stale/$vol ]]; then
- for b in ${binds[@]}; do
- # note: if we ever did binds of binds, the ordering of umount_dirs
- # would need to be reversed here.
- if mountpoint -q $b; then
- umount_dirs+=($b)
+ # not syncing ar at the moment
+ # fa=(/mnt/root/btrbk/ar.*); f=${fa[0]}
+ # if [[ -e $f ]]; then
+ # fstab <<EOF
+ # $crypt_dev /ar btrfs noatime,subvol=ar$mopts 0 0
+ # EOF
+ # fi
+
+
+ fa=(/mnt/o/btrbk/o.*); f=${fa[0]}
+ if [[ -e $f ]]; then
+ if [[ $o_dev != "$root_dev" ]]; then
+ # ,compress=zstd regardless of mopts since these are all text files
+ # and it cuts disk use by about half.
+ fstab <<EOF
+$o_dev /o btrfs noatime,subvol=o${mopts/,compress=zstd/},compress=zstd 0 0
+EOF
+ fi
+ fstab <<EOF
+/o/m /m none bind$mopts 0 0
+/o/debbugs /debbugs none bind$mopts 0 0
+EOF
+ fi
+}
+
+# Prune from ${all_vols[@]}: vols that have loopback mounts within their
+# mountpoint and vols not in fstab.
+#
+# Input vars: ${all_vols[@]}
+# Output vars: ${pruned_vols[@]}
+prune-vols() {
+ local vol found_loop l dir d found_loop
+ pruned_vols=()
+ for vol in ${all_vols[@]}; do
+ set-d-binds
+ # skip if /$vol is not a mountpoint in /etc/fstab
+ if ! awk '$3 == "btrfs" {print $2}' /etc/fstab | grep -xF /$vol &>/dev/null; then
+ continue
+ fi
+
+ ##### begin skip $vol if it has a loopback mount within it #####
+ found_loop=false
+ for l in $(losetup -ln|awk '{print $6}'); do
+ for dir in $d ${binds[@]}; do
+ if [[ $l == $dir* ]]; then
+ echo "$0: found loopback mount $l. giving up on unmounting $dir"
+ ret=1
+ found_loop=true
+ break
+ fi
+ done
+ if $found_loop; then
+ break
fi
done
- umount_dirs+=($d)
- umount_vols[$vol]=t
- else
- mnt $d
- did=$(stat -c%d $d)
+ if $found_loop; then
+ continue
+ fi
+ ##### end #####
+
+ pruned_vols+=("$vol")
+ done
+}
+
+
+# Find and fix the case where a bind mount's original source directory
+# got remounted with a different filesystem.
+#
+# Input vars: ${pruned_vols[@]}
+bind-mismatch-fix() {
+ local vol d_id b b_id found_mismatch
+ local -a to_umount
+ for vol in ${pruned_vols[@]}; do
+ set-d-binds
+ found_mismatch=false
+ d_id=$(stat -c%d $d)
for b in ${binds[@]}; do
if mountpoint -q $b; then
- bid=$(stat -c%d $b)
- if [[ $did != "$bid" ]]; then
- umount_dirs+=($b)
- umount_vols[$vol]=t
+ b_id=$(stat -c%d $b)
+ if [[ $d_id != "$b_id" ]]; then
+ found_mismatch=false
+ to_umount+=($b)
fi
- else
- mnt $b
fi
done
- fi
-done
-
-if (( ${#umount_dirs[@]} >= 1 )); then
- umount_ret=true
- unmounted=()
- umount-kill ${umount_dirs[@]}
-
- if $umount_ret; then
- for vol in ${!umount_vols[@]}; do
- vol-setup
- fresh_snap=$(cat /nocow/btrfs-stale/$vol 2>/dev/null)
-
- #### begin dealing with leaf vols ####
-
- ### begin getting root_dir
- ### this is duplicated in check-subvol-stale
+ if $found_mismatch; then
+ d "WARNING: fixing bind mount(s): ${to_umount[*]}, pointing to old subvol."
+ umount-kill ${to_umount[@]}
+ m mnt ${to_umount[@]}
+ fi
+ done
+}
- dev=$(sed -rn "s,^\s*([^#]\S*)\s+$d\s.*,\1,p" /etc/fstab /etc/mtab|head -n1)
- d dev=$dev
- # note, we need $dev because $d might not be mounted, and we do this loop
- # because the device in fstab for the rootfs can be different.
- for devx in $(btrfs fil show $dev| sed -rn 's#.*path (\S+)$#\1#p'); do
- if [[ $devx == dm-* ]]; then
- devx=/dev/$devx
- mapper-dev devx
- fi
- d devx=$devx
- root_dir=$(sed -rn "s,^\s*$devx\s+(\S+).*\bsubvolid=[05]\b.*,\1,p" /etc/mtab /etc/fstab|head -n1)
- if [[ $root_dir ]]; then
- d root_dir=$root_dir
- break
+# Check for stale vols, store their mountpoints. Make sure others are mounted.
+#
+# Input vars: ${pruned_vols[@]}
+# Output vars:
+# ${to_umount[@]}: dirs to unmount
+# ${umount_vols[@]}: the underlying vols of to_umount.
+plan-umounts() {
+ local vol dir
+ # directories we want to umount
+ to_umount=()
+ # volumes and/or their bind mounts that we want to be umounted (and
+ # may already be that way).
+ umount_vols=()
+
+ for vol in ${pruned_vols[@]}; do
+ set-d-binds
+
+ # If latest is already mounted, make sure binds are mounted and move on.
+ m check-subvol-stale $d
+ if [[ -s /nocow/btrfs-stale/$vol ]]; then
+ umount_vols+=($vol)
+ for dir in ${binds[@]} $d; do
+ if mountpoint -q $dir; then
+ to_umount=($dir)
fi
done
- if [[ ! $root_dir ]]; then
- echo "$0: error could not find root subvol mount for $dev" >&2
- exit 1
- fi
- ### end getting root_dir
+ umount-dirs-add ${binds[@]} $d
+ else
+ mnt $d ${binds[@]}
+ fi
+ done
+}
- cd $root_dir
- if [[ -e $vol ]]; then
- if [[ $vol == qd ]]; then
- b btrfs sub del qd
- else
- leaf=$vol.leaf.$(date +%Y-%m-%dT%H:%M:%S%z)
- b mv $vol $leaf
- b btrfs property set -ts $leaf ro true
-
- ### begin check if leaf is different, delete it if not ###
- parentid=$(btrfs sub show $leaf | awk '$1 == "Parent" && $2 == "UUID:" {print $3}')
- bsubs=(btrbk/$vol.*)
- bsub= # base subvolume
- # 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
- # in testing, same subvol is 136 bytes. allow some overhead. 32 happens sometimes under systemd.
- # $ errno 32
- # EPIPE 32 Broken pipe
- lines=$(btrfs send --no-data -p $bsub $leaf | btrfs receive --dump | head -n 100 | wc -l || [[ $? == 141 || ${PIPESTATUS[0]} == 32 ]])
- if [[ $lines == 0 ]]; then
- # example output of no differences:
- # snapshot ./qrtest uuid=c41ff6b7-0527-f34d-95ac-190eecf54ff5 transid=2239 parent_uuid=64949e1b-4a3e-3945-9a8e-cd7b7c15d7d6 parent_transid=2239
- echo suspected identical: $bsub $leaf
- b btrfs sub del $leaf
- fi
- fi
- ### end check if leaf is different, delete it if not ###
-
- ## begin expire leaf vols ##
- leaf_vols=($vol.leaf.*)
- count=${#leaf_vols[@]}
- leaf_limit_time=$(( EPOCHSECONDS - 60*60*24*60 )) # 60 days
- leaf_new_limit_time=$(( EPOCHSECONDS - 60*60*24 * 5 )) # 5 days this
- # goes backwards from oldest. leaf_new_limit_time is a safety
- # measure to ensure we don't delete very recent leafs.
- for leaf in ${leaf_vols[@]}; do
- leaf_time=$(date -d ${leaf#"$vol".leaf.} +%s)
- if (( leaf_limit_time > leaf_time || ( leaf_new_limit_time > leaf_time && count > 30 ) )); then
- b btrfs sub del $leaf
- fi
- count=$((count-1))
- done
- fi
- ## end expire leaf vols ##
+# This is like mount-latest-subvol, but for some subvols that have no
+# mountpoint of their own.
+#
+# Input vars: none
+amy-root2-latest() {
+ local dir vol root_dir fresh_snap
+ local -a kill_dirs
+ for dir in /mnt/r7/amy/{root/root,boot/boot}_ubuntubionic /mnt/{root2/root,boot2/boot}_ubuntubionic; do
+ vol=${dir##*/} # eg: boot_ubuntubionic
+ root_dir=${dir%/*} # eg: /mnt/root2
+ if [[ ! -d $root_dir ]]; then
+ # currently, 2 dirs exist on one host, 2 on another.
+ continue
+ fi
+ # if latest is already mounted, make sure binds are mounted and move on
+ m check-subvol-stale -p $dir
+ # populated by check-subvol-stale if stale
+ if ! fresh_snap=$(cat /nocow/btrfs-stale/$vol 2>/dev/null); then
+ continue
+ fi
+ if [[ -d $dir ]]; then
+ kill_dirs=($dir)
+ if ! kill-dir TERM TERM TERM INT INT HUP HUP TERM TERM TERM INT INT HUP HUP; then
+ if $force; then kill-dir KILL; fi
+ fi
+ if ! b btrfs sub del $dir; then
+ echo "$0: ERROR: failed to delete subvolume $dir" >&2
+ ret=1
fi
- #### end dealing with leaf vols ####
+ fi
+
+ b btrfs sub snapshot $fresh_snap $dir
+ rm -f /nocow/btrfs-stale/$vol
+ done
+}
- # 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.
+
+# Input vars: ${to_umount[@]} ${umount_vols[@]}
+#
+# note: if we ever did binds of binds, the ordering of to_umount
+# would need some reversing.
+umount-then-mount-latest() {
+ local vol fresh_snap
+ umount-kill ${to_umount[@]}
+ for vol in ${umount_vols[@]}; do
+ set-d-binds
+ if $umount_ret; then
+ fresh_snap=$(cat /nocow/btrfs-stale/$vol 2>/dev/null)
+ get-btrfs-root-mountpoint
+ cd $root_dir
+ mv-vol-to-leaf
+ expire-leaf-vols
b btrfs sub snapshot $fresh_snap $vol
- for dir in $d ${binds[@]}; do
- m mnt $dir
- done
rm -f /nocow/btrfs-stale/$vol
- done
- else
- # If we unmounted some but not all, restore them.
- for dir in ${unmounted[@]}; do
- mnt $dir
- done
+ fi
+ m mnt $d ${binds[@]}
+ done
+}
+
+
+##### begin command line parsing ########
+
+# you can remove this if you do not have options which can have args with spaces or empty.
+
+verbose=true
+force=false
+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
+
+readonly verbose force
+
+if (( $# )); then
+ all_vols=( "$@" )
+else
+ all_vols=(q a o i qd qr)
+ ar_snaps=(/mnt/root/btrbk/ar.*)
+ if [[ -e /mnt/root/ar ]] || (( ${#ar_snaps[@]} > 0 )); then
+ all_vols+=(ar)
fi
fi
+##### end command line parsing ########
-for dir in /mnt/r7/amy/{root/root,boot/boot}_ubuntubionic /mnt/{root2/root,boot2/boot}_ubuntubionic; do
- vol=${dir##*/}
- root_dir=${dir%/*}
- if [[ ! -d $root_dir ]]; then
- # this only exists on one host.
- continue
- fi
- # if latest is already mounted, make sure binds are mounted and move on
- m check-subvol-stale -p $dir
- # populated by check-subvol-stale if stale
- if ! fresh_snap=$(cat /nocow/btrfs-stale/$vol 2>/dev/null); then
- continue
- fi
- if [[ -d $dir ]]; then
- kill_dirs=($dir)
- if ! kill-dir TERM TERM TERM INT INT HUP HUP TERM TERM TERM INT INT HUP HUP; then
- if $force; then kill-dir KILL; fi
- fi
- b btrfs sub del $dir
- fi
- b btrfs sub snapshot $fresh_snap $dir
- rm -f /nocow/btrfs-stale/$vol
-done
+ret=0
+mkdir -p /var/log/btrbk
+
+setup-root2
+setup-fstab
+prune-vols
+bind-mismatch-fix
+plan-umounts
+if (( ${#umount_vols[@]} >= 1 )); then
+ umount-then-mount-latest
+fi
+amy-root2-latest
if (( ret >= 1 )); then
- echo "$0: exit status $ret. see error above"
+ echo "$0: exit status $ret. see error(s) above" >&2
fi
exit $ret