X-Git-Url: https://iankelling.org/git/?a=blobdiff_plain;f=mount-latest-subvol;h=433c7c2a06c4ed7c2a465476d1670ac375a3b514;hb=HEAD;hp=894ece520546cf9b099072ba00327df8b12ea5b0;hpb=12cab163424e3a7b0815646d1d4407f9b5839bcb;p=distro-setup diff --git a/mount-latest-subvol b/mount-latest-subvol index 894ece5..433c7c2 100644 --- a/mount-latest-subvol +++ b/mount-latest-subvol @@ -1,35 +1,42 @@ #!/bin/bash -# Copyright (C) 2016 Ian Kelling -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -script=$(readlink -f -- "$BASH_SOURCE") + +# On Ian's computers, mount received subvolumes after btrbk. +# Copyright (C) 2024 Ian Kelling + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# SPDX-License-Identifier: GPL-3.0-or-later + +this_file="$(readlink -f -- "${BASH_SOURCE[0]}")" +readonly this_file cd / -[[ $EUID == 0 ]] || exec sudo -E "$script" "$@" +[[ $EUID == 0 ]] || exec sudo -E "$this_file" "$@" -source /usr/local/lib/err +set -e; . /usr/local/lib/bash-bear; set +e +shopt -s nullglob usage() { cat < 0 )); then + all_vols+=(ar) + fi +fi + ##### end command line parsing ######## ret=0 @@ -187,6 +206,9 @@ 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 @@ -198,6 +220,8 @@ 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 @@ -210,7 +234,7 @@ fi # dont tax the cpus of old laptops -if ((`nproc` > 2)); then +if (( $(nproc) > 2)); then mopts+=,compress=zstd fi @@ -218,43 +242,59 @@ fstab </dev/null; then + if ! awk '$3 == "btrfs" {print $2}' /etc/fstab | grep -xF $d &>/dev/null; then continue fi @@ -280,7 +320,7 @@ for vol in q a o i; do # eg. when r=/q/p, for lines like # /q/p /p none bind 0 0 # output /p - new_roots+=($(sed -rn "s#^$r/\S+\s+(\S+)\s+none\s+(\S+,|)bind[[:space:],].*#\1#p" /etc/fstab)) + new_roots+=("$(sed -rn "s#^$r/\S+\s+(\S+)\s+none\s+(\S+,|)bind[[:space:],].*#\1#p" /etc/fstab)") done (( ${#new_roots} )) || break binds+=(${new_roots[@]}) @@ -290,6 +330,7 @@ for vol in q a o i; do ##### end building up list of bind mounts ###### + # 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 @@ -299,7 +340,7 @@ for vol in q a o i; do for b in ${binds[@]}; do if mountpoint -q $b; then bid=$(stat -c%d $b) - if [[ $did != $bid ]]; then + if [[ $did != "$bid" ]]; then umount-kill $b fi fi @@ -308,10 +349,33 @@ for vol in q a o i; do continue fi - if [[ $vol == q ]]; then - # allow to fail, user might not be logged in - x sudo -u $(id -nu 1000) XDG_RUNTIME_DIR=/run/user/1000 systemctl --user stop arbtt ||: + + ##### 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 + break + fi + done + if $found_loop; then + break + fi + done + if $found_loop; then + continue fi + ##### end end checking loopback mounts #### + + + ## not using arbtt at the moment + # if [[ $vol == q ]]; then + # ## allow to fail, user might not be logged in + # x sudo -u $(id -nu 1000) XDG_RUNTIME_DIR=/run/user/1000 systemctl --user stop arbtt ||: + # fi umount_ret=true unmounted=() for dir in $(echo $d ${binds[*]}\ |tac -s\ ); do @@ -354,19 +418,18 @@ for vol in q a o i; do ### end getting root_dir cd $root_dir - if [[ -e $vol ]]; then - 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 of btrfs-snapshots-diff - # todo: need python3 port of btrfs-snapshots-diff, py2 no exist on nabia + if [[ -e $vol ]]; then + if [[ $vol == qd ]]; then + m btrfs sub del qd + else + 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 ### parentid=$(btrfs sub show $leaf | awk '$1 == "Parent" && $2 == "UUID:" {print $3}') - bsubs=(/mnt/root/btrbk/$vol.*) - bsub= + 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 @@ -375,50 +438,34 @@ for vol in q a o i; do fi done if [[ $bsub ]]; then - tmp=$(mktemp) # in testing, same subvol is 136 bytes. allow some overhead. 32 happens sometimes under systemd. # $ errno 32 # EPIPE 32 Broken pipe - btrfs send --no-data -p $bsub $leaf | head -c 1000 > $tmp || [[ $? == 141 || ${PIPESTATUS[0]} == 32 ]] - 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 - # rotate in case we find a bug, weve got 2 old ones - tmpleaf=($vol.tmpleaf2.*) - if (( ${#tmpleaf[@]} )); then - x btrfs sub del ${tmpleaf[@]} - fi - tmpleaf=($vol.tmpleaf1.*) - if (( ${#tmpleaf[@]} )); then - x mv ${tmpleaf[0]} $vol.tmpleaf2.${tmpleaf[0]#$vol.tmpleaf1.} - fi - echo suspected identical: $bsub $leaf - x mv $leaf $vol.tmpleaf1.${leaf#$vol.leaf.} - fi + 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 + x 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 + x btrfs sub del $leaf + fi + count=$((count-1)) + done 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=$(( $(date +%s) - 60*60*24*60 )) # 60 days - leaf_new_limit_time=$(( $(date +%s) - 60*60*24 )) # 1 day - # this goes backwards from oldest. leaf_new_limit_time is just in case - # the order gets screwed up or something. - 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 > 15 ) )); then - x btrfs sub del $leaf - fi - count=$((count-1)) - done ## end expire leaf vols ## fi #### end dealing with leaf vols #### @@ -430,17 +477,20 @@ for vol in q a o i; do for dir in $d ${binds[@]}; do m mnt $dir done - if [[ $vol == q ]]; then - # maybe this will fail if X is not running - x sudo -u $(id -nu 1000) XDG_RUNTIME_DIR=/run/user/1000 systemctl --user start arbtt ||: - fi + + ## arbtt disabled for now + # if [[ $vol == q ]]; then + # # maybe this will fail if X is not running + # x sudo -u $(id -nu 1000) XDG_RUNTIME_DIR=/run/user/1000 systemctl --user start arbtt ||: + # fi + stale_dir=/nocow/btrfs-stale rm -f $stale_dir/$d done -for dir in /mnt/r7/amy/{root,boot}_ubuntubionic /mnt/{root2/root,boot2/boot}_ubuntubionic; do +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 @@ -463,6 +513,9 @@ for dir in /mnt/r7/amy/{root,boot}_ubuntubionic /mnt/{root2/root,boot2/boot}_ubu rm -f /nocow/btrfs-stale/$vol done +if (( ret >= 1 )); then + echo "$0: exit status $ret. see error above" +fi exit $ret