mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-09-07 15:07:17 +00:00
fix(install): preflight disk-space check before replacing the live binary
The gzip fix in the previous commit only helps once a backup is being made; it doesn't address the actual moment that broke on real hardware: the cross-device mv/copy of the new binary into place ran out of space mid-write, leaving a truncated, non-executable binary as the live one. UBIFS is a log-structured flash filesystem, so space "freed" by overwriting the old binary isn't guaranteed reusable in time for the new one to land -- this happened on a device with 15.7MB available against a ~14.8MB binary. Add a preflight check before downloading anything: fetch the new binary's real size via a HEAD request (adapts automatically as binaries grow, instead of a threshold that goes stale every release) and compare against available space plus a flat 5MB safety margin. - Comfortably enough room for old + new + a compressed backup: proceed exactly as before, silently. - Enough for old + new but not enough extra for a backup: warn interactively and require explicit confirmation before proceeding without one. Reads from /dev/tty since the script is normally piped via `curl | sh` (stdin is consumed by the script itself). Defaults to the safe choice (abort) on empty input, matching the [y/N] prompt. AFTERTOUCH_FORCE_NO_BACKUP=yes overrides for non-interactive/scripted use. - Not enough room even for the replace itself: abort before starting the download, rather than attempting a doomed download/replace that could leave a truncated live binary. - No TTY available and the operator didn't set the override: abort rather than silently guessing. - HEAD request fails for any reason: skip the check with a warning rather than blocking the install on it. Verified: all five decision branches (plenty of room, warn+decline, warn+confirm, warn+forced-override, hard abort) produce the correct result under both dash and a real BusyBox v1.38.0 container, including the gzip/gunzip streaming backup and glob-based pruning from the previous commit. The HEAD-request size lookup was separately verified against a live GitHub release URL with real curl -- catching and fixing a bug where naively taking the first "content-length:" header grabbed the 302 redirect's (0), not the actual asset's, size. Not yet re-tested end-to-end on real hardware. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
e8f1b53992
commit
ec340847f2
@@ -106,6 +106,86 @@ for f in "$INSTALL_DIR/aftertouch-service".*.backup \
|
||||
done
|
||||
echo "Disk usage after pre-install GC:"; df -h "$INSTALL_DIR"
|
||||
|
||||
# --- Preflight disk-space check ------------------------------------------
|
||||
# /mnt/nv is small (tens of MB) and binaries keep growing (Go 1.27 alone
|
||||
# added ~640KB to this binary via its own new stdlib defaults, unrelated to
|
||||
# this project's code). A prior attempt on real hardware ran out of space
|
||||
# mid-replace and left a truncated, non-executable binary in place: UBIFS is
|
||||
# a log-structured flash filesystem, so space freed by overwriting the old
|
||||
# binary isn't necessarily reusable by the time the new one needs to land.
|
||||
# Check upfront, with a safety margin, instead of discovering this mid-write.
|
||||
#
|
||||
# The new binary's size comes from a HEAD request rather than a hardcoded
|
||||
# threshold, so this doesn't go stale as binaries grow across releases.
|
||||
NEW_BINARY_BYTES=$(curl -sSLI --fail "$BINARY_URL" 2>/dev/null \
|
||||
| tr -d '\r' \
|
||||
| awk 'tolower($1) == "content-length:" {v=$2} END {print v}') || true
|
||||
|
||||
AVAILABLE_KB=$(df -Pk "$INSTALL_DIR" | awk 'NR==2 {print $4}')
|
||||
|
||||
CURRENT_BINARY_KB=0
|
||||
if [ -f "$INSTALL_DIR/aftertouch-service" ]; then
|
||||
CURRENT_BINARY_KB=$(du -k "$INSTALL_DIR/aftertouch-service" | awk '{print $1}')
|
||||
fi
|
||||
|
||||
# Flat margin, not a percentage: covers UBIFS's own reserved/GC headroom on
|
||||
# this log-structured flash filesystem plus general slack.
|
||||
SAFETY_MARGIN_KB=5120 # 5 MB
|
||||
|
||||
SKIP_BACKUP=no
|
||||
|
||||
if [ -n "$NEW_BINARY_BYTES" ]; then
|
||||
NEW_BINARY_KB=$((NEW_BINARY_BYTES / 1024))
|
||||
# Backups compress to roughly 70% of the original size in practice
|
||||
# (observed: a ~14.8MB binary gzipped to ~10.1MB); used as a conservative
|
||||
# estimate since the real ratio isn't known until compression actually runs.
|
||||
BACKUP_ESTIMATE_KB=$((CURRENT_BINARY_KB * 7 / 10))
|
||||
|
||||
NEEDED_WITH_BACKUP_KB=$((NEW_BINARY_KB + BACKUP_ESTIMATE_KB + SAFETY_MARGIN_KB))
|
||||
NEEDED_NO_BACKUP_KB=$((NEW_BINARY_KB + SAFETY_MARGIN_KB))
|
||||
|
||||
if [ "$AVAILABLE_KB" -ge "$NEEDED_WITH_BACKUP_KB" ]; then
|
||||
: # plenty of room; proceed normally, with a backup
|
||||
elif [ "$AVAILABLE_KB" -ge "$NEEDED_NO_BACKUP_KB" ]; then
|
||||
echo "WARNING: not enough free space on $INSTALL_DIR to keep a rollback" >&2
|
||||
echo "backup this time (${AVAILABLE_KB}KB available; ~${NEEDED_WITH_BACKUP_KB}KB" >&2
|
||||
echo "wanted with a backup, ~${NEEDED_NO_BACKUP_KB}KB without one)." >&2
|
||||
echo "Continuing will replace the current binary with NO way to" >&2
|
||||
echo "automatically undo it if something goes wrong." >&2
|
||||
if [ -n "${AFTERTOUCH_FORCE_NO_BACKUP:-}" ]; then
|
||||
echo "Proceeding without a backup (AFTERTOUCH_FORCE_NO_BACKUP is set)." >&2
|
||||
SKIP_BACKUP=yes
|
||||
elif [ -r /dev/tty ] && [ -w /dev/tty ]; then
|
||||
printf 'Continue without a backup? [y/N] ' > /dev/tty
|
||||
REPLY=""
|
||||
read -r REPLY < /dev/tty || true
|
||||
case "$REPLY" in
|
||||
[Yy]*) SKIP_BACKUP=yes ;;
|
||||
*)
|
||||
echo "Aborting: refusing to proceed without a backup. Free up space" >&2
|
||||
echo "on $INSTALL_DIR and try again, or set AFTERTOUCH_FORCE_NO_BACKUP=yes" >&2
|
||||
echo "to proceed without one non-interactively." >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
else
|
||||
echo "No interactive terminal available to confirm; aborting." >&2
|
||||
echo "Set AFTERTOUCH_FORCE_NO_BACKUP=yes to proceed without a backup" >&2
|
||||
echo "non-interactively." >&2
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "ERROR: not enough free space on $INSTALL_DIR to install AfterTouch" >&2
|
||||
echo "$VERSION safely (${AVAILABLE_KB}KB available, ~${NEEDED_NO_BACKUP_KB}KB" >&2
|
||||
echo "needed). Free up space and try again." >&2
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "WARNING: could not determine the new binary's size ahead of time" >&2
|
||||
echo "(HEAD request to $BINARY_URL failed); skipping the preflight" >&2
|
||||
echo "disk-space check." >&2
|
||||
fi
|
||||
|
||||
curl \
|
||||
-sSL \
|
||||
-o "$UPDATE_TMP_DIR/binary" \
|
||||
@@ -115,8 +195,11 @@ curl \
|
||||
# Back up the current binary before overwriting so a one-step rollback
|
||||
# is always available. The version string comes from the binary itself;
|
||||
# if it is absent (very old build or corrupted) we fall back to a timestamp.
|
||||
# Skipped entirely when the preflight check above decided (with the
|
||||
# operator's explicit confirmation, or AFTERTOUCH_FORCE_NO_BACKUP) that
|
||||
# there isn't room for one.
|
||||
BACKUP_FILE=""
|
||||
if [ -f "$INSTALL_DIR/aftertouch-service" ]; then
|
||||
if [ -f "$INSTALL_DIR/aftertouch-service" ] && [ "$SKIP_BACKUP" != "yes" ]; then
|
||||
current_version=$("$INSTALL_DIR/aftertouch-service" --version 2>/dev/null \
|
||||
| awk '{print $NF}') || true
|
||||
if [ -z "$current_version" ] || [ "$current_version" = "dev" ]; then
|
||||
|
||||
Reference in New Issue
Block a user