fix(plugins): keep the salt working where hardlinks are not supported

Self-review of the atomic-publish fix. Some network mounts and container volumes
reject os.link, and the outer handler swallowed that into "no salt", which meant
repo_hash and session_hash were dropped on every run for that whole cohort. The
race being closed is narrow; losing the hashes for an entire filesystem is not a
fair trade.

Falls back to claiming the name with O_CREAT|O_EXCL and writing, which is what
this did before. The empty-file window reopens there, but it is benign now: a
reader landing in it gets "" and omits the hash for that process rather than
caching a guessable path digest, which was the actual defect.

266 passed, 8 skipped.

Claude-Session: https://claude.ai/code/session_01C7tEmH86HAr7GoAAKCEHZb
This commit is contained in:
Saket Aryan
2026-09-16 21:08:43 +05:30
parent 7710a4e180
commit 3fd4949040
8 changed files with 111 additions and 0 deletions
@@ -147,6 +147,19 @@ def _install_salt() -> str:
os.link(temporary, path)
except FileExistsError:
pass
except OSError:
# No hardlinks here (some network mounts, some container volumes).
# Claim the name directly instead. That reopens the empty-file
# window, but the window is now benign: a reader that lands in it
# gets "" and omits the hash for that process rather than caching a
# guessable one. Losing the hashes on every run of an entire
# filesystem is the worse failure.
try:
fallback = os.open(path, os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0o600)
with os.fdopen(fallback, "w", encoding="utf-8") as stream:
stream.write(temporary.read_text(encoding="utf-8"))
except OSError:
pass
except OSError:
pass
finally: