Problem
The sparse NODEFS overlay introduced by fa57590e1 cannot recreate a directory after removing it in one fresh PHP process. After the first successful mkdir -> write -> unlink -> rmdir cycle, the same path reports contradictory state:
file_exists=true
is_dir=false
lstat mode/inode=0
mkdir(): File exists
file_put_contents(): No such file or directory
The same PHP code passes through the primary runtime and fails only through the fresh-process sparse overlay.
Root cause
createOverlayNodeFsMountHandler().node_ops.rmdir() checks merged entries with:
overlay.node_ops.readdir(createNode(parent, name, 0o40777))
createNode() registers that synthetic node in the Emscripten parent. The outer FS removal destroys the original node, leaving the synthetic replacement behind as a zero-mode ghost. Host-side removal and whiteout state are correct, but the polluted VFS parent blocks recreation.
Expected behavior
Inspect the existing VFS node without creating a replacement. Add regression coverage that removes and recreates the same empty overlay directory and verifies normal stat/write behavior afterward.
Reproduction
Run this inside a fresh-process sandbox backed by the sparse overlay:
$path = "/tmp/overlay-recreate";
for ( $i = 0; $i < 3; $i++ ) {
mkdir( $path );
file_put_contents( "$path/file", "x" );
unlink( "$path/file" );
rmdir( $path );
}
Observed: cycle 1 passes; cycles 2 and 3 fail with the ghost state above.
Downstream evidence: Automattic/wp-codebox bounded runtime execution, where processIdentity routes commands through runInFreshProcess().
Problem
The sparse NODEFS overlay introduced by
fa57590e1cannot recreate a directory after removing it in one fresh PHP process. After the first successfulmkdir -> write -> unlink -> rmdircycle, the same path reports contradictory state:The same PHP code passes through the primary runtime and fails only through the fresh-process sparse overlay.
Root cause
createOverlayNodeFsMountHandler().node_ops.rmdir()checks merged entries with:createNode()registers that synthetic node in the Emscripten parent. The outer FS removal destroys the original node, leaving the synthetic replacement behind as a zero-mode ghost. Host-side removal and whiteout state are correct, but the polluted VFS parent blocks recreation.Expected behavior
Inspect the existing VFS node without creating a replacement. Add regression coverage that removes and recreates the same empty overlay directory and verifies normal stat/write behavior afterward.
Reproduction
Run this inside a fresh-process sandbox backed by the sparse overlay:
Observed: cycle 1 passes; cycles 2 and 3 fail with the ghost state above.
Downstream evidence: Automattic/wp-codebox bounded runtime execution, where
processIdentityroutes commands throughrunInFreshProcess().