uninstall: read the storage registry before deleting it
The report added last commit never fired. It reads /usr/local/lib/libreportal/storage.roots to list which locations still hold app data — and that directory is removed earlier in the same function, so it found an empty registry and printed nothing. Which is precisely the silence it was written to prevent, and worse than not having it: indistinguishable from "there was nothing left". Caught on a clean teardown that left an app on each of two test disks and said so about neither. Snapshot the registry before the removal and report from that. scripts/dev/lp-uninstall-report-test runs the real capture and report fragments from init.sh with the deletion between them, so what it guards is the ORDER rather than the wording. Verified by moving the capture back after the delete. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
0aaae01833
commit
9d8b92367b
48
init.sh
48
init.sh
@ -2262,6 +2262,22 @@ runFullUninstall()
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# The storage registry lives inside the helpers directory about to be
|
||||||
|
# removed, so read it NOW — the summary at the end of this function reports
|
||||||
|
# which locations still hold app data, and reading it afterwards found
|
||||||
|
# nothing and said nothing, which is the exact silence it exists to prevent.
|
||||||
|
local _lp_left_report=""
|
||||||
|
if [[ -r /usr/local/lib/libreportal/storage.roots ]]; then
|
||||||
|
local _rid _rpath _rrest _rn
|
||||||
|
while IFS=$'\t' read -r _rid _rpath _rrest || [[ -n "$_rid" ]]; do
|
||||||
|
[[ -z "$_rpath" || "$_rid" == \#* ]] && continue
|
||||||
|
[[ -d "$_rpath" ]] || continue
|
||||||
|
_rn=$(find "$_rpath" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l)
|
||||||
|
(( _rn > 0 )) || continue
|
||||||
|
_lp_left_report+=" $(printf '%-40s %s app director%s' "$_rpath" "$_rn" "$( (( _rn == 1 )) && echo y || echo ies )")"$'\n'
|
||||||
|
done < /usr/local/lib/libreportal/storage.roots
|
||||||
|
fi
|
||||||
|
|
||||||
# 3. Remove the out-of-/docker footprint (see docs/architecture/system-footprint.md).
|
# 3. Remove the out-of-/docker footprint (see docs/architecture/system-footprint.md).
|
||||||
rm -f /usr/local/bin/libreportal /usr/local/bin/libreportal-uninstall /usr/local/bin/libreportal-relocate
|
rm -f /usr/local/bin/libreportal /usr/local/bin/libreportal-uninstall /usr/local/bin/libreportal-relocate
|
||||||
rm -rf /usr/local/lib/libreportal
|
rm -rf /usr/local/lib/libreportal
|
||||||
@ -2323,29 +2339,15 @@ runFullUninstall()
|
|||||||
# — but doing one silently and the other silently is the one option nobody
|
# — but doing one silently and the other silently is the one option nobody
|
||||||
# can predict. So say exactly what is still there.
|
# can predict. So say exactly what is still there.
|
||||||
#
|
#
|
||||||
# It also stops being obvious: the directories are owned by a uid that no
|
# Read from the snapshot taken before the registry was deleted, above.
|
||||||
# longer maps to a user once the container account is gone, and each location
|
if [[ -n "$_lp_left_report" ]]; then
|
||||||
# keeps its .libreportal-storage marker, which a later install will recognise
|
echo ""
|
||||||
# and adopt.
|
isNotice "App data left on these storage locations — nothing here was deleted:"
|
||||||
local _reg="/usr/local/lib/libreportal/storage.roots"
|
printf '%s' "$_lp_left_report"
|
||||||
[[ -r "$_reg" ]] || _reg="${lp_lib_dir:-/usr/local/lib/libreportal}/storage.roots"
|
echo ""
|
||||||
if [[ -r "$_reg" ]]; then
|
printf ' %s\n' "Remove them by hand if you meant to, or leave them — a reinstall can adopt"
|
||||||
local _id _path _rest _left=0
|
printf ' %s\n' "the location again and the apps restore back onto it."
|
||||||
while IFS=$'\t' read -r _id _path _rest || [[ -n "$_id" ]]; do
|
echo ""
|
||||||
[[ -z "$_path" || "$_id" == \#* ]] && continue
|
|
||||||
[[ -d "$_path" ]] || continue
|
|
||||||
local _n; _n=$(find "$_path" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l)
|
|
||||||
(( _n > 0 )) || continue
|
|
||||||
(( _left == 0 )) && { echo ""; isNotice "App data left on these storage locations — nothing here was deleted:"; }
|
|
||||||
printf ' %-40s %s app director%s\n' "$_path" "$_n" "$( (( _n == 1 )) && echo y || echo ies )"
|
|
||||||
_left=1
|
|
||||||
done < "$_reg"
|
|
||||||
if (( _left )); then
|
|
||||||
echo ""
|
|
||||||
printf ' %s\n' "Remove them by hand if you meant to, or leave them — a reinstall can adopt"
|
|
||||||
printf ' %s\n' "the location again and the apps restore back onto it."
|
|
||||||
echo ""
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
73
scripts/dev/lp-uninstall-report-test
Executable file
73
scripts/dev/lp-uninstall-report-test
Executable file
@ -0,0 +1,73 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Does the teardown still say what it left behind, after deleting the thing it
|
||||||
|
# reads that from?
|
||||||
|
#
|
||||||
|
# scripts/dev/lp-uninstall-report-test
|
||||||
|
#
|
||||||
|
# A full uninstall removes the primary containers root and everything in it, but
|
||||||
|
# app data on an ADDITIONAL storage location survives. Leaving it is right —
|
||||||
|
# deleting someone's data off a separate disk unasked is worse — but saying
|
||||||
|
# nothing about it is not, especially as those directories end up owned by a uid
|
||||||
|
# that stops mapping to a user once the container account is gone.
|
||||||
|
#
|
||||||
|
# The report reads the storage registry, which lives INSIDE the helpers
|
||||||
|
# directory the same function deletes. Written after that deletion it found an
|
||||||
|
# empty registry and printed nothing: the exact silence it exists to prevent,
|
||||||
|
# and indistinguishable from "there was nothing left". So what this guards is
|
||||||
|
# not the wording but the ORDER — the capture has to happen before the delete.
|
||||||
|
#
|
||||||
|
# Runs the real fragments from init.sh with the deletion between them.
|
||||||
|
|
||||||
|
REPO="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||||
|
BASE="$(mktemp -d "${TMPDIR:-/tmp}/lp-unreport-XXXXXX")"
|
||||||
|
trap 'rm -rf "$BASE"' EXIT
|
||||||
|
|
||||||
|
fail=0
|
||||||
|
chk(){ if [[ "$2" == "$3" ]]; then echo " ok $1"; else echo " FAIL $1: got '$2' want '$3'"; fail=1; fi; }
|
||||||
|
|
||||||
|
CAPTURE=$(awk '/^\t# The storage registry lives inside the helpers directory/,/^\tfi$/' "$REPO/init.sh")
|
||||||
|
REPORT=$(awk '/^\t# App data on an ADDITIONAL storage location is not removed/,/^\tfi$/' "$REPO/init.sh")
|
||||||
|
[[ -n "$CAPTURE" ]] || { echo " FAIL could not extract the capture block from init.sh"; exit 1; }
|
||||||
|
[[ -n "$REPORT" ]] || { echo " FAIL could not extract the report block from init.sh"; exit 1; }
|
||||||
|
|
||||||
|
# A registry where the helpers live, and data on one of the two locations.
|
||||||
|
mkdir -p "$BASE/lib" "$BASE/loc1/appA" "$BASE/loc1/appB" "$BASE/loc2" "$BASE/gone"
|
||||||
|
printf '1\t%s\t1\tuuid\n2\t%s\t2\tuuid\n3\t%s\t3\tuuid\n' \
|
||||||
|
"$BASE/loc1" "$BASE/loc2" "$BASE/nonexistent" > "$BASE/lib/storage.roots"
|
||||||
|
|
||||||
|
run() {
|
||||||
|
local tmp; tmp=$(mktemp)
|
||||||
|
{
|
||||||
|
echo 'isNotice(){ echo "NOTICE: $*"; }'
|
||||||
|
# The real paths are absolute; point them at the fixture.
|
||||||
|
printf '%s\n' "$CAPTURE" | sed "s#/usr/local/lib/libreportal#$BASE/lib#g"
|
||||||
|
# The deletion that sits between them in the real function.
|
||||||
|
echo "rm -rf \"$BASE/lib\""
|
||||||
|
printf '%s\n' "$REPORT"
|
||||||
|
} > "$tmp"
|
||||||
|
bash "$tmp" 2>&1
|
||||||
|
rm -f "$tmp"
|
||||||
|
}
|
||||||
|
|
||||||
|
out=$(run)
|
||||||
|
|
||||||
|
echo "--- it reports what survived, after the registry is gone ---"
|
||||||
|
chk "registry really was deleted" "$([[ -e "$BASE/lib" ]] && echo yes || echo no)" "no"
|
||||||
|
chk "says something" "$(grep -c 'App data left' <<< "$out")" "1"
|
||||||
|
chk "names the location" "$(grep -c "$BASE/loc1" <<< "$out")" "1"
|
||||||
|
chk "counts its apps" "$(grep -c '2 app directories' <<< "$out")" "1"
|
||||||
|
|
||||||
|
echo "--- and stays quiet about locations with nothing on them ---"
|
||||||
|
chk "empty location omitted" "$(grep -c "$BASE/loc2" <<< "$out")" "0"
|
||||||
|
chk "missing path omitted" "$(grep -c 'nonexistent' <<< "$out")" "0"
|
||||||
|
|
||||||
|
echo "--- nothing left anywhere: no report at all ---"
|
||||||
|
rm -rf "$BASE/loc1/appA" "$BASE/loc1/appB"
|
||||||
|
mkdir -p "$BASE/lib"
|
||||||
|
printf '1\t%s\t1\tuuid\n' "$BASE/loc1" > "$BASE/lib/storage.roots"
|
||||||
|
out=$(run)
|
||||||
|
chk "silent when there is nothing to say" "$(grep -c 'App data left' <<< "$out")" "0"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
if (( fail )); then echo "FAILED"; exit 1; fi
|
||||||
|
echo "All uninstall-report checks passed."
|
||||||
Loading…
x
Reference in New Issue
Block a user