Skip to content

host vfs: synthesize POSIX permission bits for host mounts on Windows#965

Open
mho22 wants to merge 2 commits into
mainfrom
fix/windows-host-mount-perms
Open

host vfs: synthesize POSIX permission bits for host mounts on Windows#965
mho22 wants to merge 2 commits into
mainfrom
fix/windows-host-mount-perms

Conversation

@mho22

@mho22 mho22 commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Why

On Windows there is no POSIX permission model. Node's fs.statSync reports
every host filesystem entry as either 0o666 (writable) or 0o444
(read-only): there is no owner/group/other split, no execute/"search" bit on
directories, and chmod cannot express POSIX bits at all.

That breaks host-backed mounts for any guest process that drops privileges —
for example a php-fpm worker running as a non-root user id — in two ways:

  • Directory traversal fails. POSIX directory lookup enforces the execute
    (search, X_OK) bit on every path component. Because Windows never
    reports a search bit, the privilege-dropped worker cannot traverse any
    host-backed directory: the entire mounted tree is untraversable and every
    request fails.
  • Writes into the mount fail. WordPress writes its SQLite database,
    uploads, and cache under the mounted tree, so the worker must be able to
    write there. On POSIX, hosts grant this by chmod-ing those directories
    world-writable. On Windows that chmod is a no-op and never reaches this
    overlay, so the intent is invisible here.

This is a host-platform boundary, not a gap in the kernel's POSIX
enforcement: Windows ACLs don't map onto POSIX permission bits, so the kernel
cannot enforce them anyway. The fix belongs at the point where native Windows
metadata is translated into the POSIX model the guest sees.

What changed

When the native mode carries no meaningful permission bits (Windows only),
NativeMetadataOverlay now synthesizes host-backed entries as
world-accessible — 0o777 for directories, 0o666 for files — so a
privilege-dropped guest can both traverse and write the sandbox it was given.
The one attribute Windows does expose is still honored: read-only entries map
to 0o555/0o444.

Guarantees preserved:

  • Type bits still come from the native mode.
  • A genuinely read-only host file still fails its write at the native fs
    layer.
  • Guest chmod/chown continue to override through the overlay.
  • POSIX hosts are unaffected — the synthesis is gated on
    process.platform === "win32".

Both HostFileSystem and NodePlatformIO funnel native stats through the
overlay, so the change covers every host-backed stat/fstat/lstat path.

Validation

Adds host/test/platform/native-metadata.test.ts covering the synthesized
bits, the read-only mapping, and the POSIX-host pass-through.


Split out from fix/windows-host-dir-search-bits (one fix per PR). The
companion PR covers the unrelated spawn-pid allocator fix.

🤖 Generated with Claude Code

mho22 and others added 2 commits July 16, 2026 12:51
Windows has no POSIX permission model. Node's `fs.statSync` reports every
host entry as `0o666` (writable) or `0o444` (read-only), with no
owner/group/other split, no execute/search bit on directories, and `chmod`
cannot express POSIX bits. Two things then break for a guest process that
drops privileges (e.g. a php-fpm worker running as a non-root uid):

  - Directory lookup enforces `X_OK` on every path component, so with no
    search bit the worker cannot traverse any host-backed directory — the
    whole mounted tree is untraversable and every request fails.
  - The worker must also write into the mount (WordPress writes its SQLite
    database, uploads, and cache under the mounted tree). Hosts grant this
    by `chmod`-ing those directories world-writable, a no-op on Windows
    that also never reaches this overlay, so the intent is invisible here.

This is a host-platform boundary, not a POSIX gap in the kernel: Windows
ACLs don't map to POSIX bits, so the kernel can't enforce them anyway. When
the native mode carries no meaningful permission bits (Windows only), the
NativeMetadataOverlay now represents host-backed entries as world-accessible
— `0o777` directories, `0o666` files — so a privilege-dropped guest can both
traverse and write the sandbox it was given, while still honoring the one
attribute Windows exposes by mapping read-only entries to `0o555`/`0o444`.
Type bits come from the native mode, a genuine host-level read-only file
still fails its write at the native fs layer, and guest `chmod`/`chown`
continue to override through the overlay. POSIX hosts are unaffected: the
synthesis is gated on `process.platform === "win32"`.

Both HostFileSystem and NodePlatformIO funnel native stats through the
overlay, so the fix covers every host-backed stat/fstat/lstat path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…t-perms

# Conflicts:
#	host/src/platform/native-metadata.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant