Skip to content

fuse: add no_create fallback and offer ATOMIC_O_TRUNC/BIG_WRITES in INIT - #14582

Open
copybara-service[bot] wants to merge 1 commit into
masterfrom
test/cl975530810
Open

fuse: add no_create fallback and offer ATOMIC_O_TRUNC/BIG_WRITES in INIT#14582
copybara-service[bot] wants to merge 1 commit into
masterfrom
test/cl975530810

Conversation

@copybara-service

Copy link
Copy Markdown

fuse: add no_create fallback and offer ATOMIC_O_TRUNC/BIG_WRITES in INIT

Three independent fixes to the FUSE client, found while getting mountpoint-s3 to work inside a sandbox. Each applies to any FUSE server.

1. Fall back to FUSE_MKNOD when the server does not implement FUSE_CREATE.
FUSE_CREATE is optional; a server may answer it with ENOSYS. Linux then latches fc->no_create and creates the file with FUSE_MKNOD followed by FUSE_OPEN (fs/fuse/dir.c:fuse_atomic_open()). The sentry returns the ENOSYS to the application, so open(O_CREAT) fails on any such server while mknod(2) then open(2) succeeds. mountpoint-s3 is one such server: it implements mknod and open but not create, so nothing that creates files the ordinary way — open(path, "w"), shell >, cp — works on it under gVisor. This adds conn.noCreate, mirroring the existing conn.noOpen.

2. Offer FUSE_ATOMIC_O_TRUNC in FUSE_INIT.
The sentry already implements the flag on the reply side but never offers it, so no server can enable it and O_TRUNC is always emulated with a separate SETATTR. Servers that require atomic truncate refuse to start: mountpoint-s3 with --allow-overwrite panics at INIT, which leaves the mount unusable. Two latent bugs become reachable once the flag is offered and are fixed first: a server that has ENOSYSed FUSE_OPEN and negotiated the flag had O_TRUNC silently dropped, and the first FUSE_OPEN carrying a delegated O_TRUNC could itself return ENOSYS and leave the file untruncated.

3. Offer FUSE_BIG_WRITES in FUSE_INIT.
Also already implemented on the reply side but never offered, so every FUSE_WRITE is clamped to one page. With the flag, writes go out at up to min(max_pages * 4096, max_write). Two prerequisites are fixed first: the write loop bounded a byte count by an absolute file offset (one spurious zero-length FUSE_WRITE per write(), and over-sized allocations once the clamp is gone), and the daemon read-buffer minimum used the reply header instead of FUSEWriteIn, 24 bytes short of what Linux requires. Note that each in-flight write is held twice in sentry memory until the daemon reads it, so the worst-case memory pinned by writes to a stalled daemon grows with the larger request size (up to maxActiveRequests × 2 MiB).

Linux has offered both flags since FUSE 7.9.

Measured with mountpoint-s3 1.24.0 inside a sandbox, before → after: open(O_CREAT) ENOSYS → succeeds; --allow-overwrite panics at INIT → mounts and overwrites; INIT flags offered 0x4000000x400028 (mount-s3 echoes 0x400028 with --allow-overwrite, 0x400020 without); dd bs=1M write throughput 138 → 854 MB/s (median of 3; this measures FUSE_WRITE round trips, the S3 upload happens on close). bazel test //pkg/sentry/fsimpl/fuse:fuse_test passes.

Updates #3199

Assisted-by: Claude Code
FUTURE_COPYBARA_INTEGRATE_REVIEW=#14447 from nicolaslara:upstream/fuse-init-flags-and-no-create 392210b

Three independent fixes to the FUSE client, found while getting mountpoint-s3 to work inside a sandbox. Each applies to any FUSE server.

**1. Fall back to `FUSE_MKNOD` when the server does not implement `FUSE_CREATE`.**
`FUSE_CREATE` is optional; a server may answer it with ENOSYS. Linux then latches `fc->no_create` and creates the file with `FUSE_MKNOD` followed by `FUSE_OPEN` (`fs/fuse/dir.c:fuse_atomic_open()`). The sentry returns the ENOSYS to the application, so `open(O_CREAT)` fails on any such server while `mknod(2)` then `open(2)` succeeds. mountpoint-s3 is one such server: it implements `mknod` and `open` but not `create`, so nothing that creates files the ordinary way — `open(path, "w")`, shell `>`, `cp` — works on it under gVisor. This adds `conn.noCreate`, mirroring the existing `conn.noOpen`.

**2. Offer `FUSE_ATOMIC_O_TRUNC` in `FUSE_INIT`.**
The sentry already implements the flag on the reply side but never offers it, so no server can enable it and `O_TRUNC` is always emulated with a separate `SETATTR`. Servers that require atomic truncate refuse to start: mountpoint-s3 with `--allow-overwrite` panics at INIT, which leaves the mount unusable. Two latent bugs become reachable once the flag is offered and are fixed first: a server that has ENOSYSed `FUSE_OPEN` and negotiated the flag had `O_TRUNC` silently dropped, and the first `FUSE_OPEN` carrying a delegated `O_TRUNC` could itself return ENOSYS and leave the file untruncated.

**3. Offer `FUSE_BIG_WRITES` in `FUSE_INIT`.**
Also already implemented on the reply side but never offered, so every `FUSE_WRITE` is clamped to one page. With the flag, writes go out at up to `min(max_pages * 4096, max_write)`. Two prerequisites are fixed first: the write loop bounded a byte count by an absolute file offset (one spurious zero-length `FUSE_WRITE` per `write()`, and over-sized allocations once the clamp is gone), and the daemon read-buffer minimum used the reply header instead of `FUSEWriteIn`, 24 bytes short of what Linux requires. Note that each in-flight write is held twice in sentry memory until the daemon reads it, so the worst-case memory pinned by writes to a stalled daemon grows with the larger request size (up to `maxActiveRequests` × 2 MiB).

Linux has offered both flags since FUSE 7.9.

Measured with mountpoint-s3 1.24.0 inside a sandbox, before → after: `open(O_CREAT)` ENOSYS → succeeds; `--allow-overwrite` panics at INIT → mounts and overwrites; INIT flags offered `0x400000` → `0x400028` (mount-s3 echoes `0x400028` with `--allow-overwrite`, `0x400020` without); `dd bs=1M` write throughput 138 → 854 MB/s (median of 3; this measures `FUSE_WRITE` round trips, the S3 upload happens on close). `bazel test //pkg/sentry/fsimpl/fuse:fuse_test` passes.

Updates #3199

Assisted-by: Claude Code
FUTURE_COPYBARA_INTEGRATE_REVIEW=#14447 from nicolaslara:upstream/fuse-init-flags-and-no-create 392210b
PiperOrigin-RevId: 975530810
@copybara-service copybara-service Bot added the exported Issue was exported automatically label Sep 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

exported Issue was exported automatically

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant