fix(install): don't write through hardlinks when copying package files#35735
Conversation
When re-syncing an npm package directory, copy_dir_recursive copied over existing destination files in place. If a lifecycle script had replaced such a file with a hardlink to another path (esbuild's install script hardlinks the platform package's native binary over its own JS shim), the copy wrote through the link and corrupted the file at its other paths, e.g. leaving @esbuild/<platform>/bin/esbuild containing the JS shim, which then spawns itself in an infinite loop (EAGAIN). Remove the destination before copying so the copy always writes a new inode. This also generalizes the previous Linux-only ETXTBSY handling, which relied on the copy failing and so never covered hardlinked files that were not currently executing. Co-Authored-By: Claude Fable 5 <[email protected]>
d1d5937 to
a586415
Compare
Co-Authored-By: Claude Fable 5 <[email protected]>
|
Reviewed — the fix looks correct, and it actually protects more than the reported esbuild case: since A few suggestions:
|
This PR removed its only two call sites; drop the helper and its re-export from deno_npm_cache. Co-Authored-By: Claude Fable 5 <[email protected]>
|
Thanks for the review. Done in 4080c1b — removed |
Fixes #35734
copy_dir_recursivecopied over existing destination files in place. If a lifecycle script had replaced such a file with a hardlink to another path — esbuild's install script hardlinks the platform package's native binary over its own JS shim — the copy wrote through the link and corrupted the file at its other paths, leaving@esbuild/<platform>/bin/esbuildcontaining the JS shim, which then spawns itself in an infinite loop (EAGAIN).fs_copyincopy_dir_recursiveand in thehard_link_filefallback inlocal.rs, so copies always write a new inode instead of writing through a hardlinked destinationcopy_dir_recursive, assert the other end of the link is untouched — fails before this change, passes after🤖 Generated with Claude Code