Repository navigation
linux: a file above a write deny is kept there - #705
Merged
ronleizrowice-ant merged 5 commits intoOct 16, 2026
Merged
Conversation
Nothing can be created below a file for as long as it is there. So where a file lies above a write-denied path, in a directory the command may write, the file is bound read-only onto itself. It is not tracked for removal: it is the caller's own. A .git that is a file, as in a worktree or a submodule, is denied itself, in the working directory and, as far as the scan goes, below it.
The commit before this one also denied the .git file of a worktree or a submodule below the working directory. git rewrites and removes that file itself in more commands than it allowed for: a checkout that recurses into a submodule whose commit changes failed and left the tree half switched, and git rm and rm -rf of a submodule stopped half way. It also cost two mounts a file. The working directory's own .git file stays denied.
ig-ant
reviewed
Oct 15, 2026
| // creation and break git init. | ||
| const dotGitPath = path.resolve(cwd, '.git') | ||
| let dotGitIsDirectory = false | ||
| let dotGitIsDirectory: boolean | undefined |
Collaborator
There was a problem hiding this comment.
What's the difference between (true, false, undefined) here?
A boolean that was true, false or undefined stood for a directory, a file and nothing there. No change in behaviour.
DotGitKind takes the place of three strings, and says at each member what is denied there. No change in behaviour.
…y be written is not held The kinds of mount point for an absent deny path were strings, with undefined for "none can be made". They are one enum now, read by one exhaustive switch. The debug line names what is mounted. A file above an absent write deny was bound read-only onto itself even where the command may write that file and not its directory. There nothing can take the file's place, so it is left writable, as it was before this branch.
ronleizrowice-ant
deleted the
fix/linux-a-file-above-a-write-deny-is-held
branch
October 16, 2026 01:30
ronleizrowice-ant
added a commit
that referenced
this pull request
Oct 20, 2026
Joins that needed a decision: - The placeholder planning of #688 and #705 (one decision per destination, asked of the record and not of the host; the kind settled beforehand by whether a deny lies beneath; a file above the path held in place; no mount point where none can be made) replaces this branch's upgrade in place. What this branch adds to it: an empty file a manifest names is covered like an absent path and named, and what the plan makes goes into the wrap's manifest instead of the set in memory. A held file that a manifest names is named by the look at everything the profile binds from. - The count of wraps and the forced clean-ups of #671 are kept in this branch's terms: what the count protects is the manifest and the --args profile of a command that has not started. - #673's cases on a file found at a deny path assert that a leftover stays; here a manifest says which is one. The suite is this branch's own. - #675: one return value for the plan, with the mandatory denies' share. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SEZ8LUnnuZdEgiFocuvVDm Co-authored-by: ronleizrowice <ronleizrowice@anthropic.com>
This was referenced Oct 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Linux. Nothing can be created below a file for as long as the file is there. So where a file lies above a write-denied path, in a directory the command may write, the file is kept there: it is bound read-only onto itself, as a denied path that exists is.
What changes
denyWrite: ["f/x"], andfis a file):fis bound read-only. Several denies below one file lay one bind..gitis a file, and that file is denied itself.bwrap: Can't mkdir parents for …: Not a directory).Who sees a difference
With a worktree or a submodule as the working directory, its
.gitfile can be read, and cannot be changed, moved or removed.git status,loganddiffwork there as before.Not here
A
.gitfile below the working directory. The first commit denied those too, and the second takes it back: git rewrites and removes that file itself. With it denied,git checkout --recurse-submodules(orsubmodule.recurse) to a commit that moves the submodule failed and left the tree half switched;git rmandrm -rfof a submodule stopped half way; and each file cost two mounts, which is felt from some fifty of them on. A test now holds that such a file is left alone.Also not here: a
.gitthat is a link (#693); a directory with no.gitat all, where one can be made, so thatgit initworks. macOS denies these names by path.Testing
git worktree addandgit submodule add, through the library and the CLI, on Node and two builds of Bun. Seven git commands that touch a nested.gitfile, the recursing checkout among them, end as they do on main.allowGitConfig;enableWeakerNestedSandbox; outside every write root; four commands at once in one worktree. The working directory holds afterwards what it held, and the file is byte for byte the same..gitfiles: a command starts as fast as on main, with as many arguments.