diff --git a/makechrootpkg.in b/makechrootpkg.in index 5c4b530..01e9e96 100644 --- a/makechrootpkg.in +++ b/makechrootpkg.in @@ -80,6 +80,61 @@ load_vars() { return 0 } +# Usage: btrfs_subvolume_id $SUBVOLUME +btrfs_subvolume_id() ( + set -o pipefail + LC_ALL=C btrfs subvolume show "$1" | sed -n 's/^\tSubvolume ID:\s*//p' +) + +# Usage: btrfs_subvolume_list_all $FILEPATH +# +# Given $FILEPATH somewhere on a mounted btrfs filesystem, print the +# ID and full path of every subvolume on the filesystem, one per line +# in the format "$ID $PATH". +btrfs_subvolume_list_all() ( + set -o pipefail + local mountpoint + mountpoint="$(df --output=target "$1" | sed 1d)" || return $? + LC_ALL=C btrfs subvolume list -a "$mountpoint" | sed -r 's|^ID ([0-9]+) .* path (/)?(\S*).*|\1 \3|' +) + +# Usage: btrfs_subvolume_list $SUBVOLUME +# +# Assuming that $SUBVOLUME is a btrfs subvolume, list all child +# subvolumes; from most deeply nested to most shallowly nested. +# +# This is intended to be a sane version of `btrfs subvolume list`. +btrfs_subvolume_list() { + local subvolume=$1 + + local id all path subpath + id="$(btrfs_subvolume_id "$subvolume")" || return $? + all="$(btrfs_subvolume_list_all "$subvolume")" || return $? + path="$(sed -n "s/^$id //p" <<<"$all")" + while read -r id subpath; do + if [[ "$subpath" = "$path"/* ]]; then + printf '%s\n' "${subpath#"${path}/"}" + fi + done <<<"$all" | LC_ALL=C sort --reverse +} + +# Usage: btrfs_subvolume_delete $SUBVOLUME +# +# Assuming that $SUBVOLUME is a btrfs subvolume, delete it and all +# subvolumes below it. +# +# This is intended to be a recursive version of +# `btrfs subvolume delete`. +btrfs_subvolume_delete() { + local dir="$1" + local subvolumes subvolume + subvolumes=($(btrfs_subvolume_list "$dir")) || return $? + for subvolume in "${subvolumes[@]}"; do + btrfs subvolume delete "$dir/$subvolume" || return $? + done + btrfs subvolume delete "$dir" +} + create_chroot() { # Lock the chroot we want to use. We'll keep this lock until we exit. lock 9 "$copydir.lock" "Locking chroot copy [%s]" "$copy" @@ -92,7 +147,7 @@ create_chroot() { stat_busy "Creating clean working copy [%s]" "$copy" if [[ "$chroottype" == btrfs ]] && ! mountpoint -q "$copydir"; then if [[ -d $copydir ]]; then - btrfs subvolume delete "$copydir" >/dev/null || + btrfs_subvolume_delete "$copydir" >/dev/null || die "Unable to delete subvolume %s" "$copydir" fi btrfs subvolume snapshot "$chrootdir/root" "$copydir" >/dev/null || @@ -114,7 +169,7 @@ create_chroot() { clean_temporary() { stat_busy "Removing temporary copy [%s]" "$copy" if [[ "$chroottype" == btrfs ]] && ! mountpoint -q "$copydir"; then - btrfs subvolume delete "$copydir" >/dev/null || + btrfs_subvolume_delete "$copydir" >/dev/null || die "Unable to delete subvolume %s" "$copydir" else # avoid change of filesystem in case of an umount failure