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
Open
fuse: add no_create fallback and offer ATOMIC_O_TRUNC/BIG_WRITES in INIT#14582copybara-service[bot] wants to merge 1 commit into
copybara-service[bot] wants to merge 1 commit into
Conversation
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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_MKNODwhen the server does not implementFUSE_CREATE.FUSE_CREATEis optional; a server may answer it with ENOSYS. Linux then latchesfc->no_createand creates the file withFUSE_MKNODfollowed byFUSE_OPEN(fs/fuse/dir.c:fuse_atomic_open()). The sentry returns the ENOSYS to the application, soopen(O_CREAT)fails on any such server whilemknod(2)thenopen(2)succeeds. mountpoint-s3 is one such server: it implementsmknodandopenbut notcreate, so nothing that creates files the ordinary way —open(path, "w"), shell>,cp— works on it under gVisor. This addsconn.noCreate, mirroring the existingconn.noOpen.2. Offer
FUSE_ATOMIC_O_TRUNCinFUSE_INIT.The sentry already implements the flag on the reply side but never offers it, so no server can enable it and
O_TRUNCis always emulated with a separateSETATTR. Servers that require atomic truncate refuse to start: mountpoint-s3 with--allow-overwritepanics 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 ENOSYSedFUSE_OPENand negotiated the flag hadO_TRUNCsilently dropped, and the firstFUSE_OPENcarrying a delegatedO_TRUNCcould itself return ENOSYS and leave the file untruncated.3. Offer
FUSE_BIG_WRITESinFUSE_INIT.Also already implemented on the reply side but never offered, so every
FUSE_WRITEis clamped to one page. With the flag, writes go out at up tomin(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-lengthFUSE_WRITEperwrite(), and over-sized allocations once the clamp is gone), and the daemon read-buffer minimum used the reply header instead ofFUSEWriteIn, 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 tomaxActiveRequests× 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-overwritepanics at INIT → mounts and overwrites; INIT flags offered0x400000→0x400028(mount-s3 echoes0x400028with--allow-overwrite,0x400020without);dd bs=1Mwrite throughput 138 → 854 MB/s (median of 3; this measuresFUSE_WRITEround trips, the S3 upload happens on close).bazel test //pkg/sentry/fsimpl/fuse:fuse_testpasses.Updates #3199
Assisted-by: Claude Code
FUTURE_COPYBARA_INTEGRATE_REVIEW=#14447 from nicolaslara:upstream/fuse-init-flags-and-no-create 392210b