R-203: the export-mount resolver takes the namespace root too (its own commit)
gates / gates (push) Successful in 9s

ExportDataMounts lives in delete.go, which reads as a destructive path. IT IS NOT: its single
production caller is the .fab export adapter, and nothing deletes based on its result. The
delete path's own guard, ProtectedHDDPaths, is layout-agnostic by construction -- it protects
BOTH <hdd>/... and <hdd>/felhom-data/... -- so deletion was never affected by the
namespace-root defect. That scope note is now in the function's doc comment, because the file
placement will mislead the next reader exactly as it misled the spec for this change.

Separated into its own commit anyway, so a change to a function whose filename says "delete"
is reviewable on its own.

An empty nsRoot falls back to hddPath -- the pre-R-203 shape -- so any caller not yet updated
keeps working on enrolled drives.

Tests cover both drive kinds and assert the NEGATIVE: no emitted path lies outside the app's
own data roots. Red-proof: leaving the site bare fails the system-drive row, emitting
/mnt/sys_drive/userdata where the canonical root is /mnt/sys_drive/felhom-data/userdata.
This commit is contained in:
2026-08-04 18:21:17 +02:00
parent 73efb091d9
commit a96c3d9473
3 changed files with 85 additions and 9 deletions
+13 -2
View File
@@ -559,12 +559,23 @@ func ParseComposeUserdataMounts(composePath, userdataPath string) []string {
// already covers it (an app binding ${HDD_PATH} itself), and HDD mounts inside the userdata root
// are dropped when the root is added (a literal ${HDD_PATH}/userdata/x bind would otherwise
// double-tar and basename-collide with the root).
func ExportDataMounts(composePath, hddPath string) []string {
// R-203 — nsRoot is the app's felhom-data NAMESPACE ROOT, which is what UserdataDir takes. It is
// NOT hddPath: identical on an enrolled drive, one segment shorter on the system-data fallback.
//
// SCOPE NOTE, because this function lives in delete.go and that is misleading: it is EXPORT-only.
// Its single production caller is the .fab export adapter (cmd/controller/main.go, exportAdapter.
// GetStackDataMounts). Nothing deletes based on this result. The delete path's own guard,
// ProtectedHDDPaths above, is layout-agnostic by construction — it protects BOTH <hdd>/... and
// <hdd>/felhom-data/... — so it was never affected by the namespace-root defect.
func ExportDataMounts(composePath, hddPath, nsRoot string) []string {
if hddPath == "" {
return nil
}
hddMounts := ParseComposeHDDMounts(composePath, hddPath)
ud := appbackup.UserdataDir(filepath.Clean(hddPath))
if nsRoot == "" {
nsRoot = hddPath // an enrolled drive, or a caller with nothing better — the pre-R-203 shape
}
ud := appbackup.UserdataDir(filepath.Clean(nsRoot))
if len(ParseComposeUserdataMounts(composePath, ud)) == 0 {
return hddMounts
}