command -v mdconfig &>/dev/null || skip
command -v newfs &>/dev/null || skip
-cleanup=()
-do_cleanup() {
- # Run cleanup hooks in reverse order
- while ((${#cleanup[@]} > 0)); do
- cmd="${cleanup[-1]}"
- unset 'cleanup[-1]'
- eval "bfs_sudo $cmd"
- done
-}
-trap do_cleanup EXIT
-
clean_scratch
# Create a ramdisk
truncate -s1M scratch/img
md=$(bfs_sudo mdconfig scratch/img) || skip
-cleanup+=("mdconfig -du $md")
+defer bfs_sudo mdconfig -du "$md"
# Make an ffs filesystem
bfs_sudo newfs -n "/dev/$md" >&2 || skip
# Mount it
bfs_sudo mount "/dev/$md" scratch/mnt || skip
-cleanup+=("umount scratch/mnt")
+defer bfs_sudo umount scratch/mnt
# Make it owned by us
bfs_sudo chown "$(id -u):$(id -g)" scratch/mnt
# Mount a union filesystem within it
bfs_sudo mount -t unionfs -o below scratch/mnt/{lower,upper}
-cleanup+=("umount scratch/mnt/upper")
+defer bfs_sudo umount scratch/mnt/upper
# Create a whiteout
rm scratch/mnt/upper/bar
# So this is not the same as test 1
invoke_bfs scratch/mnt \( -type w -or -not -type w \) -ls | munge_ls 3
# Unmount the unionfs
- bfs_sudo umount scratch/mnt/upper
- unset 'cleanup[-1]'
+ pop_defer
# Now repeat the same tests
invoke_bfs scratch/mnt -ls | munge_ls 4
invoke_bfs scratch/mnt -type w -ls | munge_ls 5
mkdir scratch/{foo,mnt}
bfs_sudo mount -t tmpfs tmpfs scratch/mnt || skip
-trap "bfs_sudo umount scratch/mnt" EXIT
+defer bfs_sudo umount scratch/mnt
ln -s ../mnt scratch/foo/bar
"$XTOUCH" scratch/mnt/baz
"$XTOUCH" scratch/{foo,bar}
bfs_sudo mount --bind scratch/{foo,bar} || skip
-trap "bfs_sudo umount scratch/bar" EXIT
+defer bfs_sudo umount scratch/bar
bfs_diff scratch -inum "$(inum scratch/bar)"
mkdir scratch/{foo,mnt}
bfs_sudo mount -t tmpfs tmpfs scratch/mnt || skip
-trap "bfs_sudo umount scratch/mnt" EXIT
+defer bfs_sudo umount scratch/mnt
bfs_diff scratch -inum "$(inum scratch/mnt)"
mkdir scratch/{foo,mnt}
bfs_sudo mount -t tmpfs tmpfs scratch/mnt || skip
-trap "bfs_sudo umount scratch/mnt" EXIT
+defer bfs_sudo umount scratch/mnt
"$XTOUCH" scratch/foo/bar scratch/mnt/baz
mkdir scratch/mnt
bfs_sudo mount -t tmpfs tmpfs scratch/mnt || skip
-trap "bfs_sudo umount scratch/mnt" EXIT
+defer bfs_sudo umount scratch/mnt
bfs_sudo mount -t ramfs ramfs scratch/mnt || skip
-trap "bfs_sudo umount scratch/mnt; bfs_sudo umount scratch/mnt" EXIT
+defer bfs_sudo umount scratch/mnt
bfs_diff scratch/mnt -fstype ramfs -print -o -printf '%p: %F\n'
mkdir scratch/tmp
bfs_sudo mount -t tmpfs tmpfs scratch/tmp || skip
-trap "bfs_sudo umount -R scratch/tmp" EXIT
+defer bfs_sudo umount -R scratch/tmp
mkdir scratch/tmp/ram
bfs_sudo mount -t ramfs ramfs scratch/tmp/ram || skip
mkdir scratch/{foo,automnt}
bfs_sudo systemd-mount -A -o bind basic scratch/automnt || skip
-trap "bfs_sudo systemd-umount scratch/automnt" EXIT
+defer bfs_sudo systemd-umount scratch/automnt
before=$(inum scratch/automnt)
bfs_diff scratch -inum "$before" -prune
ln -s foo/bar scratch/bar
chmod -x scratch/foo
-trap "chmod +x scratch/foo" EXIT
+defer chmod +x scratch/foo
! bfs_diff scratch -printf '(%p) (%l) %y %Y\n'
ln -s /dev/null scratch/link
bfs_sudo mount --bind /dev/null scratch/null || skip
-trap "bfs_sudo umount scratch/null" EXIT
+defer bfs_sudo umount scratch/null
bfs_diff -L scratch -type c
mkdir scratch/{foo,mnt}
bfs_sudo mount -t tmpfs tmpfs scratch/mnt || skip
-trap "bfs_sudo umount scratch/mnt" EXIT
+defer bfs_sudo umount scratch/mnt
ln -s ../mnt scratch/foo/bar
"$XTOUCH" scratch/mnt/baz
"$XTOUCH" -p scratch/foo/bar
chmod a-r scratch/foo
-trap "chmod +r scratch/foo" EXIT
+defer chmod +r scratch/foo
! bfs_diff scratch -depth
test "$UNAME" = "Linux" || skip
+
clean_scratch
"$XTOUCH" -p scratch/{lower/{foo,bar,baz},upper/{bar,baz/qux}}
+
mkdir -p scratch/{work,merged}
bfs_sudo mount -t overlay overlay -olowerdir=scratch/lower,upperdir=scratch/upper,workdir=scratch/work scratch/merged || skip
-trap "bfs_sudo umount scratch/merged; bfs_sudo rm -rf scratch/work" EXIT
+defer bfs_sudo rm -rf scratch/work
+defer bfs_sudo umount scratch/merged
+
bfs_diff scratch/merged
) &
# Kill the parent cat on exit
-trap "kill -9 %1" EXIT
+defer kill -9 %1
# Read the child PID
read -r pid <scratch/pid
"$XTOUCH" scratch/{file,null}
bfs_sudo mount --bind /dev/null scratch/null || skip
-trap "bfs_sudo umount scratch/null" EXIT
+defer bfs_sudo umount scratch/null
bfs_diff scratch -type c
clean_scratch
"$XTOUCH" -p scratch/{lower/{foo,bar,baz},upper/{bar,baz/qux}}
bfs_sudo mount -t unionfs -o below scratch/{lower,upper} || skip
-trap "bfs_sudo umount scratch/upper" EXIT
+defer bfs_sudo umount scratch/upper
bfs_diff scratch
mkdir scratch/{foo,mnt}
bfs_sudo mount -t tmpfs tmpfs scratch/mnt || skip
-trap "bfs_sudo umount scratch/mnt" EXIT
+defer bfs_sudo umount scratch/mnt
"$XTOUCH" scratch/foo/bar scratch/mnt/baz
exit 1
fi
+DEFER=()
+
+# Run a command when this (sub)shell exits
+function defer() {
+ trap -- KILL
+ if ! trap -p EXIT | grep -q pop_defers; then
+ DEFER=()
+ trap pop_defers EXIT
+ fi
+ DEFER+=("$(printf '%q ' "$@")")
+}
+
+function pop_defer() {
+ local cmd="${DEFER[-1]}"
+ unset "DEFER[-1]"
+ eval "$cmd"
+}
+
+function pop_defers() {
+ local ret=0
+
+ while ((${#DEFER[@]} > 0)); do
+ pop_defer || ret=$?
+ done
+
+ return $ret
+}
+
function bfs_sudo() {
if ((${#SUDO[@]})); then
"${SUDO[@]}" "$@"
}
if [ "$CLEAN" ]; then
- trap cleanup EXIT
+ defer cleanup
else
echo "Test files saved to $TMP"
fi