diff --git a/bin/backends/herdr.sh b/bin/backends/herdr.sh index c5f270bdaf..796f718a3b 100644 --- a/bin/backends/herdr.sh +++ b/bin/backends/herdr.sh @@ -661,7 +661,7 @@ fm_backend_herdr_presentation_lock_namespace() { fm_backend_herdr_presentation_lock_namespace_mode() { if [ "$(uname -s 2>/dev/null)" = Darwin ]; then - stat -f '%Lp' "$1" 2>/dev/null + "${FM_BSD_STAT:-/usr/bin/stat}" -f '%Lp' "$1" 2>/dev/null else stat -c '%a' "$1" 2>/dev/null fi @@ -669,7 +669,7 @@ fm_backend_herdr_presentation_lock_namespace_mode() { fm_backend_herdr_presentation_lock_namespace_uid() { if [ "$(uname -s 2>/dev/null)" = Darwin ]; then - stat -f '%u' "$1" 2>/dev/null + "${FM_BSD_STAT:-/usr/bin/stat}" -f '%u' "$1" 2>/dev/null else stat -c '%u' "$1" 2>/dev/null fi diff --git a/bin/fm-backlog-receive.sh b/bin/fm-backlog-receive.sh index 15d9bde99a..52bf8250c4 100755 --- a/bin/fm-backlog-receive.sh +++ b/bin/fm-backlog-receive.sh @@ -57,7 +57,7 @@ list_keys() { # lock_age() { local modified now if [ "$(uname 2>/dev/null)" = Darwin ]; then - modified=$(stat -f '%m' "$1" 2>/dev/null) || return 1 + modified=$("${FM_BSD_STAT:-/usr/bin/stat}" -f '%m' "$1" 2>/dev/null) || return 1 else modified=$(stat -c '%Y' "$1" 2>/dev/null) || return 1 fi diff --git a/bin/fm-bootstrap.sh b/bin/fm-bootstrap.sh index 47203fc17b..ff7e9738af 100755 --- a/bin/fm-bootstrap.sh +++ b/bin/fm-bootstrap.sh @@ -834,14 +834,14 @@ x_mode_write_if_changed() { [ "$parent" != "$dest" ] || return 1 [ -d "$parent" ] && [ ! -L "$parent" ] || return 1 if [ "$(uname)" = Darwin ]; then - parent_device=$(stat -f %d "$parent" 2>/dev/null) || return 1 + parent_device=$("${FM_BSD_STAT:-/usr/bin/stat}" -f %d "$parent" 2>/dev/null) || return 1 else parent_device=$(stat -c %d "$parent" 2>/dev/null) || return 1 fi if [ -e "$dest" ] || [ -L "$dest" ]; then fmx_single_link_file_valid "$dest" "$parent_device" || return 1 if [ "$(uname)" = Darwin ]; then - current_mode=$(stat -f %Lp "$dest" 2>/dev/null) || return 1 + current_mode=$("${FM_BSD_STAT:-/usr/bin/stat}" -f %Lp "$dest" 2>/dev/null) || return 1 else current_mode=$(stat -c %a "$dest" 2>/dev/null) || return 1 fi diff --git a/bin/fm-busy-event.sh b/bin/fm-busy-event.sh index 0abcab8ee3..f3eaf4e6c3 100755 --- a/bin/fm-busy-event.sh +++ b/bin/fm-busy-event.sh @@ -103,7 +103,7 @@ LOCK="$REC.lock" # gets a non-numeric token. Detect the platform once and pick the right form, # exactly as bin/fm-watch.sh does. if [ "$(uname)" = Darwin ]; then - lock_mtime() { stat -f %m "$1" 2>/dev/null; } + lock_mtime() { "${FM_BSD_STAT:-/usr/bin/stat}" -f %m "$1" 2>/dev/null; } else lock_mtime() { stat -c %Y "$1" 2>/dev/null; } fi diff --git a/bin/fm-classify-lib.sh b/bin/fm-classify-lib.sh index 9915ece7d2..65f1fe7d0c 100755 --- a/bin/fm-classify-lib.sh +++ b/bin/fm-classify-lib.sh @@ -539,7 +539,7 @@ FM_OPEN_DECISIONS_FOLD_VERSION=4 _fm_open_decisions_file_ident() { # -> "dev:inode", empty on I/O failure local f=$1 if [ "$(uname -s 2>/dev/null)" = Darwin ]; then - LC_ALL=C stat -f '%d:%i' "$f" 2>/dev/null + LC_ALL=C "${FM_BSD_STAT:-/usr/bin/stat}" -f '%d:%i' "$f" 2>/dev/null else LC_ALL=C stat -c '%d:%i' "$f" 2>/dev/null fi diff --git a/bin/fm-config-inherit-lib.sh b/bin/fm-config-inherit-lib.sh index 0b3ec94f09..489d2e370a 100644 --- a/bin/fm-config-inherit-lib.sh +++ b/bin/fm-config-inherit-lib.sh @@ -95,7 +95,7 @@ fm_config_inherit_items() { fm_inherit_file_mode() { if [ "$(uname)" = Darwin ]; then - stat -f %Lp "$1" 2>/dev/null + "${FM_BSD_STAT:-/usr/bin/stat}" -f %Lp "$1" 2>/dev/null else stat -c %a "$1" 2>/dev/null fi @@ -103,7 +103,7 @@ fm_inherit_file_mode() { fm_inherit_file_device() { if [ "$(uname)" = Darwin ]; then - stat -f %d "$1" 2>/dev/null + "${FM_BSD_STAT:-/usr/bin/stat}" -f %d "$1" 2>/dev/null else stat -c %d "$1" 2>/dev/null fi @@ -111,7 +111,7 @@ fm_inherit_file_device() { fm_inherit_file_link_count() { if [ "$(uname)" = Darwin ]; then - stat -f %l "$1" 2>/dev/null + "${FM_BSD_STAT:-/usr/bin/stat}" -f %l "$1" 2>/dev/null else stat -c %h "$1" 2>/dev/null fi diff --git a/bin/fm-fleet-snapshot.sh b/bin/fm-fleet-snapshot.sh index c257d110be..44693c76c4 100755 --- a/bin/fm-fleet-snapshot.sh +++ b/bin/fm-fleet-snapshot.sh @@ -820,8 +820,8 @@ case "$FM_SNAPSHOT_SECONDMATE_LANDED_PER_HOME" in ''|*[!0-9]*) FM_SNAPSHOT_SECON # pollute arithmetic input before failing. Select the platform syntax once. if [ "$(uname 2>/dev/null || true)" = Darwin ]; then SNAPSHOT_STAT_STYLE=bsd - file_mtime_epoch() { stat -f '%m' "$1" 2>/dev/null || true; } - file_mode_octal() { stat -f '%Lp' "$1" 2>/dev/null || true; } + file_mtime_epoch() { "${FM_BSD_STAT:-/usr/bin/stat}" -f '%m' "$1" 2>/dev/null || true; } + file_mode_octal() { "${FM_BSD_STAT:-/usr/bin/stat}" -f '%Lp' "$1" 2>/dev/null || true; } else SNAPSHOT_STAT_STYLE=gnu file_mtime_epoch() { stat -c '%Y' "$1" 2>/dev/null || true; } @@ -953,7 +953,7 @@ bounded_parent_activities_json() { # stat_style=$6 . "$classify" if [ "$stat_style" = bsd ]; then - size=$(stat -f "%z" "$f" 2>/dev/null) || exit 3 + size=$("${FM_BSD_STAT:-/usr/bin/stat}" -f "%z" "$f" 2>/dev/null) || exit 3 else size=$(stat -c "%s" "$f" 2>/dev/null) || exit 3 fi diff --git a/bin/fm-inactive-reconcile.sh b/bin/fm-inactive-reconcile.sh index 30d451db5a..131d7d7320 100755 --- a/bin/fm-inactive-reconcile.sh +++ b/bin/fm-inactive-reconcile.sh @@ -91,7 +91,7 @@ if [ "$FM_INACTIVE_RECONCILE_BUDGET_SECS" -gt 30 ]; then fi if [ "$(uname)" = Darwin ]; then - file_mtime() { stat -f %m "$1" 2>/dev/null; } + file_mtime() { "${FM_BSD_STAT:-/usr/bin/stat}" -f %m "$1" 2>/dev/null; } else file_mtime() { stat -c %Y "$1" 2>/dev/null; } fi diff --git a/bin/fm-lock-lib.sh b/bin/fm-lock-lib.sh index f3b070ec8c..f3b9eeccf5 100644 --- a/bin/fm-lock-lib.sh +++ b/bin/fm-lock-lib.sh @@ -24,7 +24,7 @@ fm_lock_log() { # no wake-queue machinery when a caller only needs the staleness proof. fm_lock_path_mtime() { if [ "$(uname)" = Darwin ]; then - stat -f %m "$1" 2>/dev/null + "${FM_BSD_STAT:-/usr/bin/stat}" -f %m "$1" 2>/dev/null else stat -c %Y "$1" 2>/dev/null fi diff --git a/bin/fm-pending-reply-lib.sh b/bin/fm-pending-reply-lib.sh index 8db770288a..b155a0ff5d 100755 --- a/bin/fm-pending-reply-lib.sh +++ b/bin/fm-pending-reply-lib.sh @@ -536,7 +536,7 @@ fm_pending_reply_file_signature() { # local path=$1 [ -f "$path" ] || { printf 'missing'; return 0; } if [ "$(uname -s 2>/dev/null)" = Darwin ]; then - LC_ALL=C stat -f '%d:%i:%z:%m:%c' "$path" 2>/dev/null || printf 'unreadable' + LC_ALL=C "${FM_BSD_STAT:-/usr/bin/stat}" -f '%d:%i:%z:%m:%c' "$path" 2>/dev/null || printf 'unreadable' else LC_ALL=C stat -c '%d:%i:%s:%Y:%Z' "$path" 2>/dev/null || printf 'unreadable' fi diff --git a/bin/fm-pr-lib.sh b/bin/fm-pr-lib.sh index b8ea9eb8fd..fc95bc4f77 100755 --- a/bin/fm-pr-lib.sh +++ b/bin/fm-pr-lib.sh @@ -215,7 +215,7 @@ fm_pr_head_valid() { fm_pr_file_mode() { if [ "$(uname)" = Darwin ]; then - stat -f %Lp "$1" 2>/dev/null + "${FM_BSD_STAT:-/usr/bin/stat}" -f %Lp "$1" 2>/dev/null else stat -c %a "$1" 2>/dev/null fi @@ -223,7 +223,7 @@ fm_pr_file_mode() { fm_pr_file_device() { if [ "$(uname)" = Darwin ]; then - stat -f %d "$1" 2>/dev/null + "${FM_BSD_STAT:-/usr/bin/stat}" -f %d "$1" 2>/dev/null else stat -c %d "$1" 2>/dev/null fi @@ -231,7 +231,7 @@ fm_pr_file_device() { fm_pr_file_link_count() { if [ "$(uname)" = Darwin ]; then - stat -f %l "$1" 2>/dev/null + "${FM_BSD_STAT:-/usr/bin/stat}" -f %l "$1" 2>/dev/null else stat -c %h "$1" 2>/dev/null fi @@ -239,7 +239,7 @@ fm_pr_file_link_count() { fm_pr_file_inode() { if [ "$(uname)" = Darwin ]; then - stat -f %i "$1" 2>/dev/null + "${FM_BSD_STAT:-/usr/bin/stat}" -f %i "$1" 2>/dev/null else stat -c %i "$1" 2>/dev/null fi diff --git a/bin/fm-remote-file.sh b/bin/fm-remote-file.sh index 34a993db5b..e6adfad68a 100755 --- a/bin/fm-remote-file.sh +++ b/bin/fm-remote-file.sh @@ -77,7 +77,7 @@ snapshot_bounded_file() { # directory_identity() { if [ "$(uname)" = Darwin ]; then - stat -f '%d:%i' . 2>/dev/null + "${FM_BSD_STAT:-/usr/bin/stat}" -f '%d:%i' . 2>/dev/null else stat -c '%d:%i' . 2>/dev/null fi diff --git a/bin/fm-remote-inherit-push.sh b/bin/fm-remote-inherit-push.sh index f0d6f416d4..34795192d6 100755 --- a/bin/fm-remote-inherit-push.sh +++ b/bin/fm-remote-inherit-push.sh @@ -27,7 +27,7 @@ sha256_file() { if command -v shasum >/dev/null 2>&1; then shasum -a 256 "$1" | awk '{print $1}'; else sha256sum "$1" | awk '{print $1}'; fi } file_link_count() { - if [ "$(uname)" = Darwin ]; then stat -f %l "$1" 2>/dev/null; else stat -c %h "$1" 2>/dev/null; fi + if [ "$(uname)" = Darwin ]; then "${FM_BSD_STAT:-/usr/bin/stat}" -f %l "$1" 2>/dev/null; else stat -c %h "$1" 2>/dev/null; fi } shared_captain_header_valid() { local head diff --git a/bin/fm-remote-inherit.sh b/bin/fm-remote-inherit.sh index be995d75c7..44c6b11460 100755 --- a/bin/fm-remote-inherit.sh +++ b/bin/fm-remote-inherit.sh @@ -22,7 +22,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" die() { printf 'error: %s\n' "$1" >&2; exit 1; } usage() { sed -n '2,10p' "$0" | sed 's/^# \{0,1\}//'; exit 2; } file_link_count() { - if [ "$(uname)" = Darwin ]; then stat -f %l "$1" 2>/dev/null; else stat -c %h "$1" 2>/dev/null; fi + if [ "$(uname)" = Darwin ]; then "${FM_BSD_STAT:-/usr/bin/stat}" -f %l "$1" 2>/dev/null; else stat -c %h "$1" 2>/dev/null; fi } sha256_file() { if command -v shasum >/dev/null 2>&1; then shasum -a 256 "$1" | awk '{print $1}'; else sha256sum "$1" | awk '{print $1}'; fi diff --git a/bin/fm-remote-job-lib.sh b/bin/fm-remote-job-lib.sh index 25d7bb73b4..4a893692f1 100755 --- a/bin/fm-remote-job-lib.sh +++ b/bin/fm-remote-job-lib.sh @@ -597,7 +597,7 @@ fm_remote_job_reap() { # ; only removes an exact completed re fm_remote_job_path_mtime() { # # The platform override controls worker shape in isolated tests, not the host # kernel's stat syntax. - if [ "$(uname -s 2>/dev/null || true)" = Darwin ]; then stat -f %m "$1" 2>/dev/null; else stat -c %Y "$1" 2>/dev/null; fi + if [ "$(uname -s 2>/dev/null || true)" = Darwin ]; then "${FM_BSD_STAT:-/usr/bin/stat}" -f %m "$1" 2>/dev/null; else stat -c %Y "$1" 2>/dev/null; fi } fm_remote_job_reap_stale() { # diff --git a/bin/fm-startup-memory-budget-lib.sh b/bin/fm-startup-memory-budget-lib.sh index f2c06014b8..df77d3209f 100644 --- a/bin/fm-startup-memory-budget-lib.sh +++ b/bin/fm-startup-memory-budget-lib.sh @@ -23,7 +23,7 @@ fm_startup_memory_budget_fail() { fm_startup_memory_budget_link_count() { if [ "$(uname)" = Darwin ]; then - stat -f %l "$1" 2>/dev/null + "${FM_BSD_STAT:-/usr/bin/stat}" -f %l "$1" 2>/dev/null else stat -c %h "$1" 2>/dev/null fi diff --git a/bin/fm-supervise-daemon.sh b/bin/fm-supervise-daemon.sh index 86bad52b44..29115d83c2 100755 --- a/bin/fm-supervise-daemon.sh +++ b/bin/fm-supervise-daemon.sh @@ -233,7 +233,7 @@ _state_root() { printf '%s' "${FM_STATE_OVERRIDE:-$FM_HOME/state}"; } # --- portable stat (same trap as fm-watch.sh: no `stat -f || stat -c`) ------- if [ "$(uname)" = Darwin ]; then - _stat_file_mtime() { stat -f %m "$1" 2>/dev/null; } + _stat_file_mtime() { "${FM_BSD_STAT:-/usr/bin/stat}" -f %m "$1" 2>/dev/null; } else _stat_file_mtime() { stat -c %Y "$1" 2>/dev/null; } fi diff --git a/bin/fm-supervision-lib.sh b/bin/fm-supervision-lib.sh index 3bbb13bdf8..5c89856d48 100644 --- a/bin/fm-supervision-lib.sh +++ b/bin/fm-supervision-lib.sh @@ -15,7 +15,7 @@ # Portable mtime; Linux stat lacks -f, macOS stat lacks -c. fm_sup_stat_mtime() { if [ "$(uname)" = Darwin ]; then - stat -f %m "$1" 2>/dev/null + "${FM_BSD_STAT:-/usr/bin/stat}" -f %m "$1" 2>/dev/null else stat -c %Y "$1" 2>/dev/null fi diff --git a/bin/fm-test-isolation-proof.sh b/bin/fm-test-isolation-proof.sh index 137aff8b26..a99116d26d 100755 --- a/bin/fm-test-isolation-proof.sh +++ b/bin/fm-test-isolation-proof.sh @@ -206,8 +206,8 @@ EOF dir_mode() { local path=$1 - if stat -f %Lp "$path" >/dev/null 2>&1; then - stat -f %Lp "$path" + if "${FM_BSD_STAT:-/usr/bin/stat}" -f %Lp "$path" >/dev/null 2>&1; then + "${FM_BSD_STAT:-/usr/bin/stat}" -f %Lp "$path" else stat -c %a "$path" fi diff --git a/bin/fm-test-run.sh b/bin/fm-test-run.sh index 1e90445ceb..5f232bd8f3 100755 --- a/bin/fm-test-run.sh +++ b/bin/fm-test-run.sh @@ -1656,7 +1656,7 @@ else if [ -s "$out" ]; then cat "$out" fi - mode=$(stat -c %a "$work" 2>/dev/null || stat -f %Lp "$work" 2>/dev/null || echo unknown) + mode=$(stat -c %a "$work" 2>/dev/null || "${FM_BSD_STAT:-/usr/bin/stat}" -f %Lp "$work" 2>/dev/null || echo unknown) case "$mode" in 700|0700) ;; *) diff --git a/bin/fm-wake-lib.sh b/bin/fm-wake-lib.sh index 8ce2195ac8..5d8b1c1bdd 100755 --- a/bin/fm-wake-lib.sh +++ b/bin/fm-wake-lib.sh @@ -66,7 +66,7 @@ fm_pid_identity() { fm_path_mtime() { if [ "$_FM_UNAME" = Darwin ]; then - stat -f %m "$1" 2>/dev/null + "${FM_BSD_STAT:-/usr/bin/stat}" -f %m "$1" 2>/dev/null else stat -c %Y "$1" 2>/dev/null fi @@ -1284,7 +1284,7 @@ fm_wake_print_deduped() { fm_wake_signal_sig() { # -> "size:mtime" if [ "$_FM_UNAME" = Darwin ]; then - stat -f '%z:%Fm' "$1" 2>/dev/null + "${FM_BSD_STAT:-/usr/bin/stat}" -f '%z:%Fm' "$1" 2>/dev/null else stat -c '%s:%Y' "$1" 2>/dev/null fi diff --git a/bin/fm-watch.sh b/bin/fm-watch.sh index f5a714b4c7..25840f71d1 100755 --- a/bin/fm-watch.sh +++ b/bin/fm-watch.sh @@ -136,8 +136,15 @@ WATCHER_STALE_GRACE=${FM_WATCHER_STALE_GRACE:-${FM_GUARD_GRACE:-300}} # appended to that garbage. Arithmetic under `set -u` then aborts on the stray # token (e.g. the word "File" read as an unset variable), which silently kills the # watcher mid-cycle. Detect the platform once and pick the right form. +# +# Detecting the platform is necessary but NOT sufficient: `uname` tells you the OS, +# not which `stat` wins on PATH. A Mac with Homebrew coreutils puts GNU stat ahead +# of /usr/bin, so `uname` says Darwin while `stat` is GNU 9.x - and the exact +# stdout-garbage failure described above fires on macOS too. Resolve BSD stat by +# path rather than trusting PATH. FM_BSD_STAT overrides it for hosts that keep BSD +# stat elsewhere. if [ "$(uname)" = Darwin ]; then - stat_mtime() { stat -f %m "$1" 2>/dev/null; } # epoch seconds of mtime + stat_mtime() { "${FM_BSD_STAT:-/usr/bin/stat}" -f %m "$1" 2>/dev/null; } # epoch seconds of mtime else stat_mtime() { stat -c %Y "$1" 2>/dev/null; } fi diff --git a/bin/fm-x-lib.sh b/bin/fm-x-lib.sh index 447d7cd440..17e40da5c9 100644 --- a/bin/fm-x-lib.sh +++ b/bin/fm-x-lib.sh @@ -91,8 +91,8 @@ fmx_single_link_file_valid() { local file=$1 expected_device=${2-} links device [ -f "$file" ] && [ ! -L "$file" ] || return 1 if [ "$(uname)" = Darwin ]; then - links=$(stat -f %l "$file" 2>/dev/null) || return 1 - device=$(stat -f %d "$file" 2>/dev/null) || return 1 + links=$("${FM_BSD_STAT:-/usr/bin/stat}" -f %l "$file" 2>/dev/null) || return 1 + device=$("${FM_BSD_STAT:-/usr/bin/stat}" -f %d "$file" 2>/dev/null) || return 1 else links=$(stat -c %h "$file" 2>/dev/null) || return 1 device=$(stat -c %d "$file" 2>/dev/null) || return 1 @@ -105,7 +105,7 @@ fmx_single_link_file_mode_valid() { local file=$1 expected_mode=$2 expected_device=${3-} mode fmx_single_link_file_valid "$file" "$expected_device" || return 1 if [ "$(uname)" = Darwin ]; then - mode=$(stat -f %Lp "$file" 2>/dev/null) || return 1 + mode=$("${FM_BSD_STAT:-/usr/bin/stat}" -f %Lp "$file" 2>/dev/null) || return 1 else mode=$(stat -c %a "$file" 2>/dev/null) || return 1 fi @@ -116,8 +116,8 @@ fmx_private_artifact_dir_device() { local dir=$1 mode device [ -d "$dir" ] && [ ! -L "$dir" ] || return 1 if [ "$(uname)" = Darwin ]; then - mode=$(stat -f %Lp "$dir" 2>/dev/null) || return 1 - device=$(stat -f %d "$dir" 2>/dev/null) || return 1 + mode=$("${FM_BSD_STAT:-/usr/bin/stat}" -f %Lp "$dir" 2>/dev/null) || return 1 + device=$("${FM_BSD_STAT:-/usr/bin/stat}" -f %d "$dir" 2>/dev/null) || return 1 else mode=$(stat -c %a "$dir" 2>/dev/null) || return 1 device=$(stat -c %d "$dir" 2>/dev/null) || return 1 @@ -418,7 +418,7 @@ fmx_request_relay_context() { fmx_context_registry_mtime() { local file=$1 mtime - mtime=$(stat -f '%m' "$file" 2>/dev/null) || mtime=$(stat -c '%Y' "$file" 2>/dev/null) || return 1 + mtime=$("${FM_BSD_STAT:-/usr/bin/stat}" -f '%m' "$file" 2>/dev/null) || mtime=$(stat -c '%Y' "$file" 2>/dev/null) || return 1 case "$mtime" in ''|*[!0-9]*) return 1 ;; esac