diff --git a/common.sh b/common.sh index 818f82f..6ca91ba 100755 --- a/common.sh +++ b/common.sh @@ -396,6 +396,59 @@ kb_to_gb() { awk "BEGIN {printf \"%.${decimals}f\", $kb / 1048576}" } +# ── Disk probes ─────────────────────────────────────────────────────────────────────────────── +# Everything below reports MEGABYTES, matching vv_df() in Plugin/unraid/include/common.php. The +# two languages answer the same question and must answer it in the same unit; a page and a script +# disagreeing about a threshold is the harder bug to see. +# +# These exist because the bash side had no disk helper at all — 46 call sites across 19 files +# each reached for df or du directly, in six different flag styles (df -BG, df -P, df -h, du -sh, +# du -sm, du -sk). That is not a preference, it is six chances for a unit mismatch. +# +# du measures ALLOCATED BLOCKS; stat -c%s measures APPARENT SIZE. They differ on sparse files and +# by up to a block elsewhere. dir_size_mb() is the du basis. If a caller needs apparent size it +# wants stat, and should say so — not quietly pick the other one. + +# Filesystem capacity for the path's mount, in MB. +# Usage: disk_df /mnt/user → "size_mb used_mb free_mb" (empty if it cannot be read) +disk_df() { + local path="${1:-/}" + [[ -e "$path" ]] || return 1 + df -BM --output=size,used,avail "$path" 2>/dev/null | tail -1 | tr -dc '0-9 \n' | awk 'NF==3' +} + +# Free megabytes on the filesystem holding a path. Empty and non-zero if undeterminable — +# never 0, which a caller would read as "full" and act on. +# Usage: free=$(disk_free_mb /mnt/cache) || warn "could not read free space" +disk_free_mb() { + local out + out=$(disk_df "${1:-/}") || return 1 + [[ -z "$out" ]] && return 1 + awk '{print $3}' <<< "$out" +} + +# Size of a file or directory in MB, du basis. Empty and non-zero if unreadable. +# Usage: mb=$(dir_size_mb "$path") || mb=0 +dir_size_mb() { + local path="$1" out + [[ -e "$path" ]] || return 1 + out=$(du -sm "$path" 2>/dev/null | cut -f1) + [[ -z "$out" ]] && return 1 + echo "$out" +} + +# Human-readable string from a megabyte count — so a caller that already has MB never runs a +# second traversal just to print it. format_bytes() is the byte-input equivalent. +# Usage: format_mb 2048 → 2.0GB +format_mb() { + local mb=${1:-0} + if (( mb >= 1024 )); then + awk "BEGIN {printf \"%.1fGB\", $mb / 1024}" + else + echo "${mb}MB" + fi +} + # ============================================================================================== # ── ARG PARSER ──────────────────────────────────────────────────────────────────────────────── # ==============================================================================================