v0.75.0: gate userdata MkdirAll on a live mountpoint (no writes into an absent drive)

Belt (ensureUserdataMounts) + FileBrowser sync skip ensure/mount when an external
drive root is not a live mountpoint -> no 'mkdir userdata: permission denied' + no
rootfs-shadow during a drive-absent window. System/local path never gated. Reuses
system.IsMountPoint; matches planDriveGates external-only rule. T1-T4 + red-proofs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-22 20:47:08 +02:00
parent 9d47232d7f
commit 0b2605c5a8
5 changed files with 150 additions and 4 deletions
+21
View File
@@ -1,5 +1,26 @@
## Changelog
### v0.75.0 — gate userdata MkdirAll on a live mountpoint (no writes into an absent drive) (2026-06-22)
**Bugfix — two `MkdirAll`-into-`<drive>/userdata` sites fired without checking the drive was mounted**,
producing `mkdir …/userdata: permission denied` + transient `Created` flapping during a drive-absent
window (campaign-#2 findings #2/#3). Worse than noise: writing into an unmounted mountpoint lands app
data on the guest **rootfs**, shadowed when the drive returns (data-integrity + rootfs-fill hazard).
- `internal/stacks/manager.go``ensureUserdataMounts` (the deploy belt) now skips when the
`HDD_PATH` drive root is an **external** path (not `sysDataPath`) that is **not a live mountpoint**;
the app is held by `planDriveGates` instead. New injectable `Manager.isMountPoint` seam (defaults to
`system.IsMountPoint`) for testability. The system/local path is never gated (it's legitimately not a
mountpoint).
- `internal/web/handlers.go` — the FileBrowser sync loop skips (and does not mount) a registered path
under `StableParentDir` that isn't a live mountpoint, via a new pure `skipFileBrowserPath` helper.
Matches `planDriveGates`' external-only rule.
- `EnsureUserdataDir`/`EnsureUserdataSkeleton`/`planDriveGates` unchanged (gated the callers).
- Tests: `TestEnsureUserdataMounts_{SkipsAbsentExternalDrive,EnsuresWhenMounted,SystemPathNeverSkipped}`
+ `TestSkipFileBrowserPath` (both red-proofed against the pre-fix code).
- **Boot-time** occurrence (docker boot-restore starting drive-backed apps before the agent mounts the
drives) is a separate cause — documented as a design note (CONTEXT.md), not changed here.
### v0.74.0 — fix the controller→agent connection leak (per-call agentapi client) (2026-06-22)
**Bugfix — agent local-API socket leak that took down the whole agent-backed feature set after ~5 days.**