diff --git a/src/main/daemon/daemon-pid-publish-atomicity.test.ts b/src/main/daemon/daemon-pid-publish-atomicity.test.ts index 086f5432ed2..b6316da1ac1 100644 --- a/src/main/daemon/daemon-pid-publish-atomicity.test.ts +++ b/src/main/daemon/daemon-pid-publish-atomicity.test.ts @@ -170,6 +170,28 @@ describe('publishDaemonPidFile atomicity', () => { expect(entries).toEqual([]) }) + it('stays silent when the degraded write itself loses the ownership race', () => { + // Same catch arm as the degrade, but nothing was published: announcing lost + // torn-write protection for a record that was never written is a false alarm. + const entries: string[] = [] + publishDaemonPidFile(pidPath, { pid: 1, startedAtMs: 5, launchNonce: 'other' }) + fsFaults.linkError = Object.assign(new Error('injected ENOTSUP'), { code: 'ENOTSUP' }) + + expect(() => + publishDaemonPidFile(pidPath, RECORD, { log: (event) => entries.push(event) }) + ).toThrow(/EEXIST/) + expect(warnSpy).not.toHaveBeenCalled() + expect(entries).toEqual([]) + }) + + it('names the errno even when the link error carries none', () => { + fsFaults.linkError = new Error('injected link failure with no code') + + publishDaemonPidFile(pidPath, RECORD) + + expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('no errno')) + }) + it('keeps exclusive-create semantics on the degraded path too', () => { publishDaemonPidFile(pidPath, { pid: 1, startedAtMs: 5, launchNonce: 'other' }) fsFaults.linkError = Object.assign(new Error('injected ENOTSUP'), { code: 'ENOTSUP' }) diff --git a/src/main/daemon/daemon-spawner.ts b/src/main/daemon/daemon-spawner.ts index 4892e758b38..6212f1d46c2 100644 --- a/src/main/daemon/daemon-spawner.ts +++ b/src/main/daemon/daemon-spawner.ts @@ -204,6 +204,10 @@ export function publishDaemonPidFile( // instead of trading a torn-write window for no pid record at all. Warn because the // degrade is otherwise invisible: an operator on such a volume (network mount, // container overlay) should be able to tell the atomic path is not in use. + writeFileSync(pidPath, serializeDaemonPidFile(pidFile), { mode: 0o600, flag: 'wx' }) + // Announced only once the degraded write has actually landed: this same arm is + // reached by a lost ownership race, where the write throws EEXIST and nothing + // was published — claiming torn-write exposure for that would be a lie. // Both channels, matching daemon-endpoint-lifecycle.ts: inside the detached // daemon stdio is 'ignore' and its startup stderr pipe is destroyed once it is // up, so console alone reaches nobody on the path this warning exists for. @@ -212,7 +216,6 @@ export function publishDaemonPidFile( `[daemon] pid-record hard-link publish failed (${describeErrnoCode(error)}); ` + 'degrading to the non-atomic exclusive write — torn-write protection is off for this record' ) - writeFileSync(pidPath, serializeDaemonPidFile(pidFile), { mode: 0o600, flag: 'wx' }) } } finally { try {