push() { $(ARGS_NE mnt src dst_dir) now=$(date +%F.%H%M%S) || die snap_dir=$mnt/snapshot.$now prev_dir=$mnt/snapshot.prev local BTRFS_RECEIVE_DESTINATION_PATH="$dst_dir" push_helper true "$snap_dir" "$prev_dir" "$src" local_btrfs_receiver } push_simple() { $(ARGS_NE mnt src dst_dir) local BTRFS_RECEIVE_DESTINATION_PATH="$dst_dir" push_helper false "$mnt" "$src" local_btrfs_receiver } sex() { (set -x; "$@") } local_btrfs_receiver() { btrfs receive "$BTRFS_RECEIVE_DESTINATION_PATH" } shellescape() { if [ "$BASH_VERSION" ]; then printf %q "$1" else bash -c 'printf %q "$1"' bash "$1" fi } remote_btrfs_receiver() { ssh "$BTRFS_RECEIVE_DESTINATION_HOST" -- "btrfs receive $(shellescape "$BTRFS_RECEIVE_DESTINATION_PATH")" } push_helper() { $(ARGS keep_as_prev snap_dir prev_dir src dst_pipe) $(NONEMPTY keep_as_prev snap_dir src dst_pipe) local full_dest rw_dest btrfs subvolume snapshot -r "$src" "$snap_dir" || die if [ "$prev_dir" -a -d "$prev_dir" ]; then btrfs send -p "$prev_dir" "$snap_dir" else btrfs send "$snap_dir" fi | "$dst_pipe" || die if [ "$dst_pipe" = local_btrfs_receiver ]; then local dst="$BTRFS_RECEIVE_DESTINATION_PATH" full_dest=$dst/$(basename "$snap_dir") rw_dest=$full_dest.rw btrfs subvolume snapshot "$full_dest" "$rw_dest" || die btrfs_replace_default_subvolume_with "$rw_dest" fi if $keep_as_prev && [ "$prev_dir" ] then # keep the pushed snapshot in order to reuse it on subsequent pushes. with_dir "$prev_dir" btrfs subvolume delete || die sex mv "$snap_dir" "$prev_dir" || die else btrfs subvolume delete "$snap_dir" fi } btrfs_mountpoint() { $(ARGS_NE dir) btrfs filesystem show -m "$dir" >/dev/null 2>&1 } btrfs_get_mountpoint() { $(ARGS_NE dir) while [ "$dir" -a "$dir" != '.' ]; do if btrfs_mountpoint "$dir" then printf '%s\n' "$dir" return fi dir=$(dirname "$dir") done false } btrfs_show_default_path() { $(ARGS_NE mp) local path mp=$(btrfs_get_mountpoint "$mp") || die # TODO: fix caller? btrfs_mountpoint "$mp" || die "not a mountpoint: $mp" path=$(btrfs subvolume get-default "$mp"/|sed -n -e 's/.* path //p') if [ "$path" ]; then printf '%s\n' "$mp/$path" else printf '%s\n' "$mp" fi } btrfs_show_default_id() { $(ARGS_NE mp) local id mp=$(btrfs_get_mountpoint "$mp") || die # TODO: fix caller? btrfs_mountpoint "$mp" || die "not a mountpoint: $mp" id=$(btrfs subvolume get-default "$mp"/|sed -n -e 's/^ID \([^ ]*\) .*/\1/p') [ "$id" ] || return echo $id } btrfs_replace_default_subvolume_with() { $(ARGS_NE new_default) local old_default old_default_id new_default_id old_default_id=$(btrfs_show_default_id "$new_default") || die new_default_id=$(btrfs_show_subvolume_id "$new_default") || die [ "$new_default_id" = "$old_default_id" ] && return if [ "$old_default_id" != 5 ]; then old_default=$(btrfs_show_default_path "$new_default") || die else old_default= fi btrfs subvolume set-default "$new_default_id" "$new_default" || die if [ "$old_default" ]; then btrfs subvolume delete "$old_default" sex mv "$new_default" "$old_default" fi } btrfs_show_subvolume_id() { $(ARGS_NE path) local result result=$(btrfs subvolume show "$path" | sed -n -e 's/^[ \t]*Subvolume ID:[ \t]*//p; s/.*is toplevel subvolume/5/p') if [ "$result" ] then printf '%s\n' "$result" else false fi } with_dir() { $(ARGS_NE d) shift [ -d "$d" ] || return 0 "$@" "$d" }