Skip to content

Patch the file a symlinked source names - #845

Merged
SimonCropp merged 2 commits into
mainfrom
fix-symlink-source
Aug 23, 2026
Merged

Patch the file a symlinked source names#845
SimonCropp merged 2 commits into
mainfrom
fix-symlink-source

Conversation

@SimonCropp

Copy link
Copy Markdown
Member

The applier rewrites the whole file through a temporary and swaps it in with
File.Replace, which on Linux and macOS is a rename. A rename replaces the name it is
given, so for a source reached through a symlink - a worktree, a shared checkout, a
vendored copy - the link itself was replaced by a regular file: the link stopped
being one, and the file it named still held the snapshot that had failed. Every
later run reported the same snapshot again, against source the compiler was still
reading from the target.

The link is followed before anything else, so the lock, the mutex, the read and the
swap all name the file that actually holds the source. Two links to one file get the
same lock as a side effect.

The rename also leaves the temporary's permissions on the file. A source that was
executable, or group writable, came back as whatever this process's umask said, so
the destination's mode is carried onto the temporary first. Windows keeps the
destination's ACLs across a Replace and needs none of this.

Both tests are Unix only: a symlink needs elevation on Windows and a file mode is
not a thing it has. The two behaviours behind them were confirmed against rename(2)
directly.

The applier rewrites the whole file through a temporary and swaps it in with
File.Replace, which on Linux and macOS is a rename. A rename replaces the name it is
given, so for a source reached through a symlink - a worktree, a shared checkout, a
vendored copy - the link itself was replaced by a regular file: the link stopped
being one, and the file it named still held the snapshot that had failed. Every
later run reported the same snapshot again, against source the compiler was still
reading from the target.

The link is followed before anything else, so the lock, the mutex, the read and the
swap all name the file that actually holds the source. Two links to one file get the
same lock as a side effect.

The rename also leaves the temporary's permissions on the file. A source that was
executable, or group writable, came back as whatever this process's umask said, so
the destination's mode is carried onto the temporary first. Windows keeps the
destination's ACLs across a Replace and needs none of this.

Both tests are Unix only: a symlink needs elevation on Windows and a file mode is
not a thing it has. The two behaviours behind them were confirmed against rename(2)
directly.
@SimonCropp
SimonCropp merged commit e6de342 into main Aug 23, 2026
8 checks passed
@SimonCropp
SimonCropp deleted the fix-symlink-source branch August 23, 2026 09:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant