carlos / platform Public

The unit-file chain climb stops at the platform's own directory

Where a database's owner is the platform's user, the climb from the
file's directory had nowhere to stop short of the filesystem root, and
CI (and the gate under /var/tmp) refused every unit route's named file.
It now stops at the first parent that is root's or this process's
user's, which is then required to be the platform's, as before. A
tenant with its own user is checked exactly as it was.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 01f2b072bfaed763526e3641105deb4ac0dab395 parent b53f2a9
2 files changed, +12 −4
  • docs/superpowers/specs/DISPOSITIONS-metered-replicator.md +4 −0
  • internal/ops/litestream_owners.go +8 −4

CI passed — run details

diff --git a/docs/superpowers/specs/DISPOSITIONS-metered-replicator.md b/docs/superpowers/specs/DISPOSITIONS-metered-replicator.md
index 07406e6f1..4a5602cbb 100644
--- a/docs/superpowers/specs/DISPOSITIONS-metered-replicator.md
+++ b/docs/superpowers/specs/DISPOSITIONS-metered-replicator.md
@@ -98,3 +98,7 @@ Reports: `OPUS-REVIEW-metered-replicator-review3.md`, `CODEX-FINDINGS-metered-re
The fourth build is not read by a reviewer. The branch stays unwired and unattested, and item 1 of OUTSTANDING.md stays a launch blocker: nothing on any box can yet prove its replication writes.
+
+### CI on the fourth build (b53f2a93)
+
+Amadan CI failed on `b53f2a93`: `TestFleetDBOwnersNamesAnOwnerOrSaysThereIsNone` and `TestFleetDBOwnersCheckAUnitRoutesNamedFileLikeATree`, with "/ belongs to a user other than root or the platform's". The unit-file chain climb (`checkOwnedFile`, kind `tenantFile`) climbed while each parent belonged to the file's owner; where the file's owner is the platform's own user (every test, a single-user box) that climbs past the fixture to wherever the temp root's chain stops, which on the runner is `/` under a user namespace, and locally depends on `TMPDIR` (it passed under `~/.cache`, where the chain stops at `/home`, and fails under `/tmp`). The local gate on `b53f2a93` (`gate-replicator-b53f2a93.txt`, `TMPDIR` under `/var/tmp/ck`) failed the same way, so the fourth build's "ops tests pass" claim above held only for one temp root. Fixed in the next commit: the climb also stops at the first parent that is the platform's (root's or this process's user's), which is then required to be platform-owned as before. For a tenant whose user is not the platform's nothing changes: the chain still ends at the first directory that is not the tenant's, and that directory must be the platform's. The ops tests now pass under both `/tmp` and `~/.cache`. Rule kept for the morning: a package test must be run under the gate's temp root, not only the sandbox's, before its result is written down.
diff --git a/internal/ops/litestream_owners.go b/internal/ops/litestream_owners.go
index f57e32f1d..4a987e492 100644
--- a/internal/ops/litestream_owners.go
+++ b/internal/ops/litestream_owners.go
@@ -306,10 +306,14 @@ func checkOwnedFile(root, path string, kind claimKind) (fileFacts, string) {
case tenantFile:
// The root is not the file's directory but the top of the tenant's
// own chain: climb from the file's directory while the directory
- // above is the same user's and not a link. What is above that must
- // be the platform's, and nothing from the top of the chain down may
- // be a link, the top included (an operator's link is allowed for a
+ // above is the same user's, is not the platform's (root's or this
+ // process's user's), and is not a link. What is above that must be
+ // the platform's, and nothing from the top of the chain down may be
+ // a link, the top included (an operator's link is allowed for a
// tree claim's root, which the route names; this root is inferred).
+ // Stopping at the platform's own directory matters where the file's
+ // owner is the platform's user (tests, a single-user box): the
+ // chain would otherwise climb to a directory nobody here owns.
owner, lerr := os.Lstat(path)
if lerr != nil || !owner.Mode().IsRegular() {
return fileFacts{}, "it is not a regular file"
@@ -328,7 +332,7 @@ func checkOwnedFile(root, path string, kind claimKind) (fileFacts, string) {
return fileFacts{}, "a directory above it cannot be looked at"
}
pf, ok := factsOf(pfi)
- if !ok || pfi.Mode()&os.ModeSymlink != 0 || !pfi.IsDir() || pf.UID != ownerFacts.UID {
+ if !ok || pfi.Mode()&os.ModeSymlink != 0 || !pfi.IsDir() || pf.UID != ownerFacts.UID || pf.UID == 0 || int(pf.UID) == os.Geteuid() {
break
}
root = parent