ask where the ballast goes and how big it is

Three questions instead of one, because the two the original never asked are the
two that decide whether the thing is useful.

  1. Do you want one, with the explanation first.

  2. Where. Home (easiest to find again months from now), beside Officer, or a
     path typed in. This is not tidiness: the checker measures its own directory,
     so a ballast only protects the filesystem it sits on. Choosing where it goes
     is choosing which mount is covered.

  3. How much, as 5/10/20% — with the actual numbers, and with what would be LEFT
     rather than only what is taken:

       [1]   5%  — reserves 2.9GB    leaving 54.3GB free
       [2]  10%  — reserves 5.8GB    leaving 51.4GB free
       [3]  20%  — reserves 11.5GB   leaving 45.7GB free

     A percentage on its own is unanswerable. The number that decides it is the
     one on the right: the reserve has to be big enough to matter and small
     enough not to be the thing that filled the disk.

The size is computed against the filesystem the chosen path lands on, after the
location is known, so the percentages are of the right disk. ballast_free_kb
walks up to a directory that exists, since nothing has created the target yet.

An existing ballast in either default location is found and left alone rather
than a second one being made beside it.

Verified end to end in a temp home: created at the chosen path, 2.9G for 5% of
57.1GB, checker installed and reporting it present.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-12 18:05:42 +00:00
co-authored by Claude Opus 5
parent d4b9b7b334
commit d19dc5a92a
2 changed files with 111 additions and 26 deletions
+18 -7
View File
@@ -196,22 +196,33 @@ swappiness_set() {
# already has passwordless sudo, but wrong, and not something to carry forward.
# Both the script and the file now live in root-owned system paths.
BALLAST_FILE=/var/lib/machine-setup/ballast.bin
# Where it goes is asked rather than decided. A ballast only protects the
# filesystem it is ON — the checker measures its own directory — so the choice is
# also a choice of which mount is being protected. Defaults to the user's home,
# which on most machines is the same filesystem as / and is the easiest place to
# find it again months later.
BALLAST_FILE=""
BALLAST_CHECKER=/usr/local/sbin/emergency-disk-check
BALLAST_CRON=/etc/cron.d/emergency-disk-check
BALLAST_THRESHOLD=10
BALLAST_NAME=emergency-disk-ballast.bin
ballast_exists() { [[ -f "$BALLAST_FILE" ]]; }
ballast_exists() { [[ -n "$BALLAST_FILE" && -f "$BALLAST_FILE" ]]; }
ballast_size_human() { du -h "$BALLAST_FILE" 2>/dev/null | cut -f1; }
# 10% of what is free right now, in MiB.
ballast_size_mb() {
local free_kb
free_kb="$(df -Pk /var/lib | awk 'NR == 2 { print $4 }')"
echo $((free_kb / 1024 / 10))
# The nearest directory that exists, walking up. A path being chosen for the
# ballast does not mean anything has created it yet, and df cannot measure a
# directory that is not there.
existing_ancestor() {
local dir="$1"
while [[ ! -d "$dir" && "$dir" != "/" ]]; do dir="$(dirname "$dir")"; done
echo "$dir"
}
# Free space in KiB on whichever filesystem would hold this path.
ballast_free_kb() { df -Pk "$(existing_ancestor "$1")" | awk 'NR == 2 { print $4 }'; }
ballast_create() {
local mb="$1"
mkdir -p "$(dirname "$BALLAST_FILE")"