Skip to content

feat: add FUSE passthrough - #16

Open
aron-cf wants to merge 3 commits into
mmdevries:mainfrom
aron-cf:fuse-passthrough
Open

aron-cf wants to merge 3 commits into
mmdevries:mainfrom
aron-cf:fuse-passthrough

Conversation

@aron-cf

@aron-cf aron-cf commented Oct 12, 2026

Copy link
Copy Markdown

Summary

Hi, thanks for this project. I'm currently working on adding support for FUSE passthrough to @cloudflare/computer (cloudflare/computer#197). As part of that change I'm looking at porting from fuse-native to this more modern library.

This PR implements the passthrough interface, hopefully you'd be open to considering adopting support.

References: kernel FUSE passthrough docs, Linux 6.9, libfuse 3.17.1 release, passthrough_hp.cc.

Compatibility

  • Linux 6.9+ with libfuse 3.17+. backingOpen needs CAP_SYS_ADMIN, which the kernel requires.
  • Builds against older libfuse still work: backingOpen throws ENOTSUP and a non-zero backingId is refused. macOS behaves the same way.
  • Existing open/create/opendir results and initWithConfig are unchanged. opendir refuses backingId.
  • Linux prebuilds only get passthrough if they're compiled against libfuse 3.17+ headers.

Verification

Linux 7.0 (OrbStack), Node 22:

  • Debian trixie (libfuse 3.17.2): npm run check, test:types, test:unit, test:fuzz and npm test pass. The real-mount test/passthrough.js confirms the read/write handlers are never called and that writes land in the backing file.
  • Debian bookworm (libfuse 3.14): builds with no warnings. test/passthrough.js skips and test/hardening.js passes.
  • I've not tested on macOS.

Release scope

  • This pull request does not change package versions, create release tags,
    add generated prebuilds, or publish artifacts.
  • Public behavior and TypeScript changes include matching tests and
    documentation.
  • Security-sensitive changes have been disclosed privately when required.

Let open and create hand a file to the kernel, which then serves that
handle's reads, writes and mmap from a local backing file without calling
read or write (Linux 6.9+, libfuse 3.17+).

- backingOpen(fd) and backingClose(id) wrap FUSE_DEV_IOC_BACKING_OPEN and
  FUSE_DEV_IOC_BACKING_CLOSE, the ioctls libfuse only exposes through its
  low-level API. They run under the invalidation mutex, which teardown
  holds while clearing the session, and throw an Error carrying the errno.
- open and create results accept backingId, written to
  fuse_file_info.backing_id; libfuse then replies with FOPEN_PASSTHROUGH.
  opendir keeps its two-value result and refuses a backingId.
- A backingId combined with keepCache or directIO, or with the
  kernelCache, autoCache or directIo mount options, is refused before it
  reaches the kernel, which would otherwise fail the open with an opaque
  EIO (fs/fuse/iomode.c).
- initWithConfig accepts maxBackingStackDepth (0 to 2), for backing files
  on a stacked filesystem such as overlayfs.
- Fuse.CAP_PASSTHROUGH names the capability bit to request in want.

Building against libfuse older than 3.17 still works: backingOpen then
reports ENOTSUP and a non-zero backingId is refused.
Matthijs de Vries added 2 commits October 12, 2026 22:12
Use direct I/O where mounted timeout tests require the exact ETIMEDOUT
errno. Add separate Linux cached-read coverage that allows kernel-level
EIO while checking that every native read reply is still ETIMEDOUT.

Cover late read callbacks before and after unmount, add a native-boundary
unit regression, and document deadline and direct-I/O semantics.

This branch has not been deployed

No deployments
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