_ _
| |_ ___| |_ _
| | . | | | |
|_|_|___|_|_ |
|___|
git mirror - github.com/owenewans/holy - branch master
file tests/directories.sh
#!/bin/sh
set -eu
bin=$(realpath "$1")
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT HUP INT TERM
umask 022
expect() {
want=$1
shift
if "$@" > "$tmp/out" 2> "$tmp/err"; then got=0; else got=$?; fi
if test "$got" -ne "$want"; then
printf 'expected %s, got %s: %s\n' "$want" "$got" "$*" >&2
cat "$tmp/out" "$tmp/err" >&2
exit 1
fi
}
for version in old new; do
tree="$tmp/$version"
mkdir -p "$tree/HOLY" "$tree/DATA/usr/share"
printf 'format holy-package-1\nname directories\nversion %s\nrelease 1\nos linux\narch noarch\nlibc nolibc\n' "$version" > "$tree/HOLY/meta"
for part in deps provides hooks origin transform; do : > "$tree/HOLY/$part"; done
printf '%s\n' "$version" > "$tree/DATA/usr/share/payload"
if test "$version" = new; then
mkdir -p "$tree/DATA/opt/apps/deep"
printf 'nested payload\n' > "$tree/DATA/opt/apps/deep/data"
ln -s data "$tree/DATA/opt/apps/deep/link"
chmod 750 "$tree/DATA/opt/apps"
fi
expect 0 "$bin" manifest generate "$tree" --output "$tmp/files"
mv "$tmp/files" "$tree/HOLY/files"
expect 0 "$bin" pack "$tree" --output "$tmp/$version.holy"
done
old=$(sha256sum "$tmp/old.holy" | cut -d ' ' -f 1)
new=$(sha256sum "$tmp/new.holy" | cut -d ' ' -f 1)
initialize() {
root=$1
mkdir "$root"
expect 0 "$bin" db init --root "$root"
expect 0 "$bin" cache stage "local:$tmp/old.holy" --root "$root"
expect 0 "$bin" cache stage "local:$tmp/new.holy" --root "$root"
}
plan_set() {
expect 0 "$bin" db plan-set "$1" --root "$root"
plan=$(sed -n 's/^plan-set .* sha256 \([0-9a-f]*\) read-only$/\1/p' "$tmp/out")
test "${#plan}" -eq 64
}
check_tree() {
expect 0 "$bin" db check --all --root "$root"
grep -qx 'nested payload' "$root/opt/apps/deep/data"
test "$(stat -c %a "$root/opt")" = 755
test "$(stat -c %a "$root/opt/apps")" = 750
test "$(readlink "$root/opt/apps/deep/link")" = data
test "$(find "$root" -name '.holy-dir-*' | wc -l)" -eq 0
}
initialize "$tmp/single"
expect 0 "$bin" db reserve "$new" --root "$root"
expect 0 "$bin" db plan --root "$root"
plan=$(sed -n 's/^plan .* sha256 \([0-9a-f]*\) read-only$/\1/p' "$tmp/out")
test ! -e "$root/opt"
expect 0 "$bin" db approve "$plan" --root "$root"
umask 077
expect 0 "$bin" db apply --root "$root"
umask 022
check_tree
rm "$root/opt/apps/deep/data" "$root/opt/apps/deep/link"
rmdir "$root/opt/apps/deep" "$root/opt/apps" "$root/opt"
expect 4 "$bin" db check --all --root "$root"
expect 0 "$bin" db repair-plan "$new" --root "$root"
plan=$(sed -n 's/^repair-plan .* sha256 \([0-9a-f]*\) missing-only read-only$/\1/p' "$tmp/out")
expect 0 "$bin" db repair "$new" --plan "$plan" --root "$root"
check_tree
printf user > "$root/opt/apps/deep/untracked"
expect 0 "$bin" db rm "$new" --root "$root"
grep -qx user "$root/opt/apps/deep/untracked"
test -d "$root/opt/apps/deep"
initialize "$tmp/update"
plan_set "$old"
expect 0 "$bin" db apply-set "$plan" "$old" --root "$root"
expect 0 "$bin" db plan-update "$old" "$new" --root "$root"
plan=$(sed -n 's/^plan-update sha256 \([0-9a-f]*\) read-only$/\1/p' "$tmp/out")
test ! -e "$root/opt"
expect 0 "$bin" db apply-update "$plan" "$old" "$new" --root "$root"
check_tree
initialize "$tmp/unsafe"
plan_set "$new"
mkdir "$tmp/outside"
printf sentinel > "$tmp/outside/marker"
ln -s "$tmp/outside" "$root/opt"
expect 4 "$bin" db apply-set "$plan" "$new" --root "$root"
grep -qx sentinel "$tmp/outside/marker"
test ! -e "$tmp/outside/apps"
test ! -f "$root/var/lib/holypkg/transactions/set-journal"
rm "$root/opt"
mkdir "$root/opt"
chmod 700 "$root/opt"
expect 4 "$bin" db plan-set "$new" --root "$root"
test "$(stat -c %a "$root/opt")" = 700
# payload order must not decide whether parent creation succeeds.
lz4 -dq "$tmp/new.holy" "$tmp/new.tar"
python3 - "$tmp/new.tar" "$tmp/reordered.tar" <<'PY'
import io, sys, tarfile
with tarfile.open(sys.argv[1]) as source, tarfile.open(sys.argv[2], "w", format=tarfile.PAX_FORMAT) as output:
members = source.getmembers()
data = [m for m in members if m.name.startswith("DATA/")]
order = [m for m in members if not m.name.startswith("DATA/")]
order += [m for m in data if not m.isdir()] + list(reversed([m for m in data if m.isdir()]))
for member in order:
output.addfile(member, source.extractfile(member) if member.isfile() else None)
PY
lz4 -q "$tmp/reordered.tar" "$tmp/reordered.holy"
initialize "$tmp/reordered"
expect 0 "$bin" cache stage "local:$tmp/reordered.holy" --root "$root"
reordered=$(sha256sum "$tmp/reordered.holy" | cut -d ' ' -f 1)
plan_set "$reordered"
expect 0 "$bin" db apply-set "$plan" "$reordered" --root "$root"
check_tree
if "$bin" elf "$bin" | grep -q '^interpreter /'; then
gcc -shared -fPIC -o "$tmp/fault.so" "$(dirname "$0")/update-fault.c" -ldl
injection="LD_PRELOAD=$tmp/fault.so"
elif test "${HOLY_TEST_STATIC_UPDATE_FAULT:-0}" = 1; then
injection="HOLY_STATIC_TEST=1"
else
printf 'directory fault injection skipped for uninstrumented static client\n'
printf 'directory transaction fixtures passed\n'
exit 0
fi
for operation in set update; do
for phase in directory-partial directory-ready directory-created payload-written; do
if test "$operation:$phase" = update:payload-written; then continue; fi
initialize "$tmp/$operation-$phase"
if test "$operation" = update; then
plan_set "$old"
expect 0 "$bin" db apply-set "$plan" "$old" --root "$root"
expect 0 "$bin" db plan-update "$old" "$new" --root "$root"
plan=$(sed -n 's/^plan-update sha256 \([0-9a-f]*\) read-only$/\1/p' "$tmp/out")
set -- db apply-update "$plan" "$old" "$new"
recovery=--update
else
plan_set "$new"
set -- db apply-set "$plan" "$new"
recovery=--continue-set
fi
if env "$injection" HOLY_UPDATE_FAULT="$phase" HOLY_DIRECTORY_PARENT="$root" HOLY_DIRECTORY_NAME=opt \
"$bin" "$@" --root "$root" > "$tmp/out" 2> "$tmp/err"; then exit 1; else test "$?" -eq 137; fi
expect 5 "$bin" db status --root "$root"
if test "$phase" = payload-written; then
grep -qx 'nested payload' "$root/opt/apps/deep/data"
printf changed > "$root/opt/apps/deep/data"
expect 5 "$bin" db recover "$recovery" --root "$root"
grep -qx changed "$root/opt/apps/deep/data"
printf 'nested payload\n' > "$root/opt/apps/deep/data"
fi
if test "$phase" = directory-partial; then
expect 5 "$bin" db recover "$recovery" --root "$root"
test ! -e "$root/opt"
stage="$root/.holy-dir-$(printf opt | sha256sum | cut -d ' ' -f 1)"
test -d "$stage"
test "$(stat -c %a "$stage")" = 700
rmdir "$stage"
fi
expect 0 "$bin" db recover "$recovery" --root "$root"
check_tree
done
done
printf 'directory transaction fixtures passed\n'