Skip to content

Sparse overlay rmdir leaves a ghost VFS node that blocks recreation #4163

Description

@chubes4

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().

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions