Uh oh!
There was an error while loading. Please reload this page.
fix(preflight): match FUSE network mounts by normalised name, not by spelling (client#725) - #726
Conversation
…spelling (client#725) _pf_is_network_fstype matched the FUSE network filesystems only in their `fuse.`-prefixed spelling, and three of those six (sshfs, s3fs, rclone) had no bare twin. Which spelling arrives depends on which reader in _pf_fstype answered — and two of its three readers never produce a `fuse.` prefix, so the entries were unreachable there and a network HOST_DATA_DIR was accepted by the guard that exists to reject it. Measured on Ubuntu 24.04 (libfuse 3.14.0 / util-linux 2.39.3 / coreutils 9.4) and 20.04 (libfuse 2.9.9), mounting each filesystem for real: reader sshfs rclone bindfs findmnt -nro FSTYPE fuse.sshfs fuse.rclone fuse stat -f -c %T fuseblk fuseblk fuseblk df + mount (macOS path) macfuse macfuse macfuse So macOS — which has no findmnt and deliberately skips the stat reader — could never match ANY of the six, and the Linux stat fallback collapses every FUSE mount to `fuseblk`. Normalise instead of enumerate: strip a leading `fuse.` before matching and keep bare names only, so the list cannot regrow a prefixed entry with no bare twin. A subtype-erased reading (fuse / fuseblk / macfuse / osxfuse) is now classified as opaque FUSE and WARNS rather than hard failing — sshfs and a local ntfs-3g read identically there, so refusing would block legitimate local installs, and TRACEBLOC_ALLOW_NETWORK_FS is nonsense advice off a network FS. Silently reading as "Local storage" was the actual defect. Tests state the input names independently of the matcher (from the measured reader outputs, not from the case list) and cover both spellings, the local fuse.* filesystems that must NOT be swept up by the prefix strip, the opaque readings, and agreement between _pf_storage_type and early_data_dir_guard. All five new tests fail against the classifier as it ships on develop. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
LukasWodka
commented
Aug 14, 2026
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 021eb1f. Configure here.
saadqbal
left a comment
There was a problem hiding this comment.
Nice, careful PR 👍 Normalise-then-match is the right shape, and the opaque-FUSE warn tier is a good call — refusing every unnamed fuse/fuseblk/macfuse would block legit local FUSE. Tests are non-vacuous (names come from real reader output, and #85 greps the source to stop the fuse.-prefix regrowing). Ran the suite locally: 134/134 green, manifest hash matches. One optional nit inline.
Uh oh!
There was an error while loading. Please reload this page.
… review) saadqbal, flagged optional: `fuse.gcsfuse` (GCS) and `fuse.blobfuse2` (Azure) -- the cloud twins of the `s3fs` already covered -- were on neither list, so they stripped to a name nobody matched, missed the opaque tier (not bare `fuse`), and were announced as "Local storage". A hospital mounting object storage at HOST_DATA_DIR corrupts the database exactly as s3fs does. Both added, with `blobfuse` for the v1 driver. AND THE STRUCTURAL HALF, because a list only ever holds the drivers someone thought of and the next cloud FUSE ships without asking us: an unrecognised `fuse.<subtype>` is now OPAQUE rather than local. We have no evidence it is network -- so it stays a warning, not a hard fail -- but we have no evidence it is local either, and announcing "Local storage" was a confident answer about a mount we could not identify. TWO THINGS THE EXISTING SUITE CAUGHT, both worth more than the change itself: * `_pf_is_opaque_fuse_fstype` and `_pf_is_network_fstype` are MUTUALLY EXCLUSIVE by design, and bats asserts it. A bare `fuse.*) return 0` broke that: `fuse.sshfs` satisfied both, and the only thing keeping it a hard fail rather than a notice was the order the caller happens to ask in. A future caller asking "opaque?" first would silently downgrade a database-corrupting mount. The arm now negates the network classifier, so the answer is independent of call order. * The opaque warning says "the mount table doesn't say which one". True for `fuseblk`; FALSE for `fuse.gocryptfs`, where the table says precisely which one and the gap is that WE have no opinion. Shipping that sentence would have been the false-statement shape this repo keeps finding. The wording now branches on which unknown it is. Local FUSE (ntfs-3g, gocryptfs) moves from a silent pass to a notice. That is the honest reading -- we genuinely cannot tell them from a cloud mount by name -- and it is a warning, never a failure. Say so if you would rather have only the three names; the fuse.* arm is a one-line revert. 138 bats pass (was 134). Four mutations, each anchor asserted unique first: drop the cloud twins from the network list -> reddened unrecognised FUSE falls back to local again -> reddened drop the exclusivity negation -> reddened restore the false wording for a named subtype -> reddened manifest.sha256 regenerated.
LukasWodka
commented
Aug 19, 2026
/fr-pass Best-effort functional review passed (triage: merged clean, non-interactive; behavioral evidence limited while e2e journey is red — backend#2206). Advancing to Ready for prod. |
Fixes#725
Summary
_pf_is_network_fstype(shared by_pf_storage_typeand the pre-logearly_data_dir_guard, #432) matched the FUSE network filesystems only asfuse.sshfs/fuse.s3fs/fuse.rclone/fuse.glusterfs/fuse.ceph/fuse.davfs— and three of those six had no bare twin.Which spelling arrives depends on which reader in
_pf_fstypeanswered, and two of its three readers never emit afuse.prefix. Measured by mounting each filesystem for real on Ubuntu 24.04 (libfuse 3.14.0, util-linux 2.39.3, coreutils 9.4) and Ubuntu 20.04 (libfuse 2.9.9) — identical on both:_pf_fstypeorder)findmnt -nro FSTYPEfuse.sshfs✅fuse.rclone✅fuse❌stat -f -c %T(Linux fallback, no findmnt)fuseblk❌fuseblk❌fuseblk❌df+mount(the macOS path)macfuse❌macfuse❌macfuse❌So on macOS none of the six entries can ever match — macOS has no
findmntand deliberately skips thestatreader (BSDstat -fis a format string), so thedf+mountpath always answers, with the macFUSE vfs name. And whereverfindmntis absent on Linux,stat -f -c %Tcollapses every FUSE mount tofuseblk(thefuseandfuseblkkernel filesystems share one magic number). In both cases the guard read "Local storage" and MySQL was installed onto exactly the filesystem it exists to reject.The originally-suspected mechanism — a bare
sshfsin the mount table — did not reproduce on either libfuse generation; sshfs and rclone always set a subtype. The prefixed spellings are right forfindmnt; the defect is that they were the only spellings.Change
fuse.before matching; the case list now carries bare names only, so it cannot regrow a prefixed entry with no bare twin. A guard test asserts the list contains nofuse.-prefixed entry, and fails closed if it can't read the function.fuse,fuseblk,macfuse,osxfuse) — FUSE with the subtype erased — gets its own predicate and warns instead of hard failing. sshfs and a local ntfs-3g/gocryptfs are indistinguishable at that point, so a refusal would block legitimate local installs, andTRACEBLOC_ALLOW_NETWORK_FSreads as nonsense to someone who is not on a network filesystem. The silent "Local storage" pass was the defect; the warn names what it might be and continues.Deliberately not in this PR: recovering the real subtype when a reader degrades (readable from
/proc/mountson Linux and from thesshfs#…device field on macOS). That would let the named cases hard fail on every platform instead of only warning. Happy to file it as a follow-up.Test plan
New tests write their input filesystem names down from the measured reader outputs, not from the case list — a list checked against itself is self-consistent and blind. The measurement table is recorded in the test file header.
bats scripts/tests/preflight.bats— 134/134 pass (5 new tests, 0 failures)bats scripts/tests/bats-hygiene.bats— green; every new assertion is|| return 1-hardenedbats scripts/tests/copy-catalog.bats— green (no user-facing copy in the goldens changed)make check— green;make check-all— green (465 helm unit tests, 31 suites)scripts/gen-manifest.shre-run;scripts/manifest.sha256committed (R8 gate)Mutation-proof — each mutation was verified to have actually applied (anchor gone) before trusting the result:
fuse.stripfuse.sshfsentry, no baresshfs)developThe last row is the one that matters: the tests fail against the real pre-fix code, not just against a synthetic mutation.
Found while fact-checking tracebloc/e2e-test-agent#125, which documents this guard and flags the gap without fixing it (the defect is here, not there).
🤖 Generated with Claude Code
Note
Medium Risk
Changes install-time refusal/warning for MySQL data paths—affects whether installs proceed on FUSE/macOS/Linux—but behavior is well-tested and opaque FUSE stays advisory rather than blocking.
Overview
Fixes HOST_DATA_DIR preflight so network FUSE mounts are caught regardless of how
_pf_fstypespells them, and generic FUSE no longer passes as “local storage.”_pf_is_network_fstypenow lowercases and strips a leadingfuse.before matching a bare-name allowlist (adds gcsfuse, blobfuse, blobfuse2, etc.). Unlistedfuse.*subtypes are no longer treated as local._pf_is_opaque_fuse_fstypeand_pf_opaque_fuse_warnhandle subtype-erased readings (fuseblk,macfuse, barefuse) and unknown named FUSE drivers: warn and continue (not hard-fail), with wording that distinguishes “subtype unknown” vs “driver not recognised.”early_data_dir_guardand_pf_storage_typeuse the same classifiers.docs/INSTALL.mdchecklist andscripts/manifest.sha256are updated;scripts/tests/preflight.batsadds broad coverage (prefix invariance, mutual exclusivity, guard agreement).Reviewed by Cursor Bugbot for commit f802292. Bugbot is set up for automated code reviews on this repo. Configure here.