Attempt to fix ENAMETOOLONG error by filtering and deduping open_basedir - #4432
Conversation
📊 Performance Test ResultsComparing dc8b98c vs trunk app-size
site-editor
site-startup
Results are median values from multiple test runs. Legend: 🟢 Improvement (faster) | 🔴 Regression (slower) | ⚪ No change (<50ms diff) |
bcotrim
left a comment
There was a problem hiding this comment.
Logic makes sense, and the testing instructions work as described.
I added 2 issues flagged by Claude that I couldn't quite verify, take it as non-blockers.
| return entryPath | ||
| .split( path.sep ) | ||
| .some( ( segment ) => IGNORED_SCAN_DIRECTORY_NAMES.has( segment ) ); |
There was a problem hiding this comment.
split( path.sep ) never matches on Windows — chokidar normalizes to forward slashes before calling ignored (chokidar/index.js:44-48). Verified on Win11: it receives node_modules and returns false, where trunk's regex returned true. So the watcher descends into the link farms it means to skip.
| return entryPath | |
| .split( path.sep ) | |
| .some( ( segment ) => IGNORED_SCAN_DIRECTORY_NAMES.has( segment ) ); | |
| return entryPath | |
| .split( /[\\/]/ ) | |
| .some( ( segment ) => IGNORED_SCAN_DIRECTORY_NAMES.has( segment ) ); |
| } | ||
|
|
||
| function isCoveredByOpenBasedirAllowlist( target: string ): boolean { | ||
| return getEffectiveOpenBasedirAllowlist().some( ( entry ) => containsPath( entry, target ) ); |
There was a problem hiding this comment.
Runs the full O(N²) dropCoveredPaths() per symlink event, and arePathsEqual does 2 statSync per comparison — 50 entries ≈ 19ms / 22k sync stat calls, on the same loop as the proxy (:460). Trunk was an O(1) Set.has.
The dedup is redundant here: if a dropped entry covers the target, whatever displaced it does too.
| return getEffectiveOpenBasedirAllowlist().some( ( entry ) => containsPath( entry, target ) ); | |
| return [ ...staticOpenBasedirAllowlist, ...symlinkOpenBasedirAllowlist ].some( ( entry ) => | |
| containsPath( entry, target ) | |
| ); |
There was a problem hiding this comment.
Good feedback 👍
Related issues
How AI was used in this PR
I used Claude to iterate in relatively small chunks towards a result I liked. I used Codex to review the result.
Proposed Changes
In #4173, @Tropicalista reported an issue where native PHP sites with "site directory" file access would fail to start and report an
ENAMETOOLONGerror.ENAMETOOLONGerrors indicate that the CLI command was too long for the OS to handle (there is such a limit). We don't know what exactly made the command so long, but our best theory is that it's theopen_basedirdirective, which is passed as a CLI option. It could have happened if there was a pnpm-stylenode_modulesdirectory anywhere in the site directory, for example (since this means there can be hundreds of symlink paths in theopen_basedirdirective).This PR fixes the problem by applying the following changes:
node_modules,.git, and.DS_Storepaths infindSymlinksInDir(). Previously, we only ignored those paths when watching for new symlinks. If a pnpm-stylenode_modulesdir is the source of the problem, then this is the key fix.open_basedirlist contains/lorem/ipsumand/lorem/ipsum/dolor,/lorem/ipsum/doloris now dropped before passing the list to PHP (because it is redundant). This change also ensures that newly added symlinks pointing to an already allowed directory don't trigger a process restart.The next logical step would be to put the
open_basedirdirective in a site-specificphp.inifile. I'll leave that for another PR, though.Testing Instructions
cdintowp-content/pluginsfor that siteln -s PATH_TO_DOWNLOADED_EXTRACTED_PLUGIN_DIRPluginsin the sidebarThis routine ensures that basic symlink watcher functionality and
open_basedircompilation still work as expected. It does not test theENAMETOOLONGerror specifically, as we don't know exactly how to reproduce that.Pre-merge Checklist