Repository navigation
linux: a wrap that starts over keeps what it has walked, and ends - #638
Conversation
A wrap starts over when updateConfig() has replaced the configuration during its walk. It started over from nothing, so each replacement cost a whole walk more, the same content included, and nothing bounded it. The wrap now keeps the directories it listed and each pattern's finished walk, so only a pattern new to it is walked. After two restarts it walks without giving the thread back.
antdres
left a comment
There was a problem hiding this comment.
One blocker, found after merge. It is on main (c7adb1e) and in no release yet.
A deny rule added while a command is being wrapped can miss a file that already existed when the rule was added.
Steps, on Linux with real bubblewrap:
- The configuration denies
proj/**/.env. A wrap ofcat proj/key.pemstarts on a large tree (66,000 entries, so the walk takes a few turns). - 30 ms in, the host writes
proj/key.pem, then callsupdateConfig()with a configuration that also deniesproj/**/*.pem. - The wrap starts over, as intended, and returns.
result of cat proj/key.pem |
|
|---|---|
| before this PR (60e5e9f) | Permission denied |
| this PR (c7adb1e) | prints the file's contents |
| this PR, same configuration, wrap not disturbed | Permission denied |
Same result on every run (3 of 3 each side).
Why: when the wrap starts over, the new *.pem pattern is walked from the directory listings the first attempt kept. proj/ was listed before key.pem was written, so the new rule never sees the file. Before this PR the second attempt listed the disk again.
This is not the "as old as one long walk" case from the description. The rule was added after the file existed, and the wrap that returns claims to apply that rule.
The same happens when the configuration is replaced with identical content: a file written between the first walk and the replacement is readable here and denied before.
A possible fix: keep the part that makes the wrap end (walk without turns after two restarts), and have an attempt that starts over read the disk again instead of reusing the first attempt's listings and walks.
Generated by Claude Code
…bundle The kept walks go into the one options object, beside the anchor, the budget and the listings. Three things git did not mark: the anchor is part of a kept walk's key; a kept walk is charged to the budget the entries it looked at; and the allow side passes its anchor on as well as the walks. The budget is still made anew by a wrap that starts over.
It undoes the join of #638: the kept walks, the anchor in their key and their charge to the budget go with the walks themselves. The budget and the listings are again made by each attempt. Of the three join cases one stays, under another name: a budget that arrives during the denyRead walk is the one the wrap ends by.
Since anthropics#638 a wrap that starts over kept the directories it had listed and each pattern's finished walk. A host that wrote a file and then installed a rule denying it, while a command was being wrapped, got a command that could read the file: the rule was walked over a listing made before the file was there. A wrap that starts over now keeps nothing, so what it hands out was read after its configuration was installed. It still ends: after two restarts it walks without giving the thread back. And an attempt is left as soon as its configuration is seen to be replaced, not walked to its end.
Follows #628, which is on
mainand in no release. Since #628 a wrap walks its read patterns in turns, and starts over whenupdateConfig()replaced the configuration meanwhile. It started over from nothing: every replacement during a wrap cost a whole walk more, identical content included, and nothing bounded it. A host that refreshes its configuration often could keep a command from starting.What changes
Who sees a difference
Only a caller whose configuration is replaced during a wrap. What the wrap keeps is as old as its first walk, so a path made since is not seen, as with one long walk. A third attempt holds the thread while it walks a new pattern (about 0.1 s here).
Measured
Six
**patterns over 400,000 files, through the package's export. Wrap time as a multiple of an undisturbed wrap (about 1 s), on Node 22 / Bun 1.4.2:updateConfig()during the wrapmainTesting
Five new tests. Each fails with its part of the change taken out (nine mutations), and all five fail on
main's source. Linux full suite: nothing that passes onmainfails here.