Files
libfuse/doc/README.fusermount
Bernd Schubert 8a4834952c Add mount and daemonization README documents
These are useful to

Signed-off-by: Bernd Schubert <bernd@bsbernd.com>
2026-05-02 15:45:40 +02:00

363 lines
14 KiB
Plaintext

Synchronous FUSE_INIT Protocol
================================
Overview
--------
The sync-init feature enables the FUSE library to start worker threads and
perform initialization ioctl calls BEFORE the actual mount() syscall happens.
This is required for the kernel's synchronous FUSE_INIT feature, where the
mount() syscall blocks until the FUSE daemon processes the INIT request.
Without this feature, there would be a deadlock:
- mount() blocks waiting for INIT response
- Worker threads can't start because mount() hasn't returned
- INIT request can't be processed because worker threads aren't running
Protocol Flow
-------------
Traditional mount flow:
1. Library calls fusermount3
2. fusermount3 opens /dev/fuse
3. fusermount3 performs mount() syscall
4. fusermount3 sends fd to library
5. Library starts worker threads
6. Worker threads process FUSE requests
Sync-init mount flow:
1. Library calls fusermount3 with --sync-init flag
2. fusermount3 opens /dev/fuse
3. fusermount3 sends fd to library
4. Library receives fd
5. Library performs FUSE_DEV_IOC_SYNC_INIT ioctl
6. Library starts worker threads
7. Library sends "proceed" signal to fusermount3
8. fusermount3 performs mount() syscall (blocks until INIT completes)
9. Worker threads process INIT request
10. mount() syscall completes
11. fusermount3 exits
Implementation Details
----------------------
Bidirectional Communication:
- Uses the existing unix socket (_FUSE_COMMFD environment variable)
- Simple 1-byte protocol for signaling
- Library signals fusermount3 when ready to proceed with mount
fusermount3 Changes:
- New --sync-init command-line option
- Split mount operation into two phases:
* mount_fuse_prepare(): Opens device, prepares parameters
* mount_fuse_finish_fsmount(): Performs actual mount() syscall
- wait_for_signal(): Waits for library to signal readiness
- struct mount_context: Preserves state between phases
Library Changes:
- fuse_session_mount_new_api(): Uses new protocol when available
- Sends "proceed" signal after worker thread is ready
- Handles both old and new mount protocols for compatibility
Backward Compatibility
----------------------
The implementation maintains full backward compatibility:
- Old library + new fusermount3: Works (uses traditional flow)
- New library + old fusermount3: Falls back to traditional flow
- New library + new fusermount3: Uses sync-init flow when appropriate
Error Handling
--------------
If any step fails during the sync-init flow:
- fusermount3 closes the fd and exits with error
- Library detects failure and cleans up
- No mount is left in inconsistent state
Connection closure:
- If library closes socket before signaling, fusermount3 detects and exits
- If fusermount3 crashes, library detects closed socket
Security Considerations
-----------------------
The sync-init protocol does not introduce new security concerns:
- Uses the same privilege separation as traditional mount
- Socket communication is already established and trusted
- No new privileged operations are added
- File descriptor passing uses existing SCM_RIGHTS mechanism
Performance Impact
------------------
Minimal performance impact:
- One additional recv() call in fusermount3
- One additional send() call in library
- Total overhead: ~2 context switches
- Only affects mount time, not runtime performance
Future Enhancements
-------------------
Potential improvements:
- Extended protocol for more complex initialization sequences
- Support for multiple worker threads coordination
- Enhanced error reporting through the socket
- Timeout mechanisms for detecting hung initialization
ASCII Workflow Diagrams
========================
1. Traditional Mount Flow (without --sync-init, async INIT)
------------------------------------------------------------
Library fusermount3 Kernel
| | |
|--- spawn fusermount3 ---->| |
| | |
| [open /dev/fuse] |
| |------- open -------->|
| |<------ fd ---------- |
| | |
| [mount() syscall] |
| |------ mount -------->|
| |<----- success ------ | [mount returns immediately]
| | | [INIT queued in kernel]
| [send_fd(fd)] |
|<------- fd --------------| |
| | |
| [fusermount3 exits] |
| |
| [start worker thread] |
| [worker reads /dev/fuse] |
|---------------------------------------- read -->|
|<--------------------------------------- INIT ---| [dequeued from kernel]
| |
| OK: INIT was queued, worker reads it later |
| Works fine for async INIT |
1b. Problem: Synchronous INIT without --sync-init
--------------------------------------------------
Library fusermount3 Kernel
| | |
|--- spawn fusermount3 ---->| |
| | |
| [open /dev/fuse] |
| |------- open -------->|
| |<------ fd ---------- |
| | |
| [mount() syscall] |
| |------ mount -------->|
| | | [mount BLOCKS waiting for INIT]
| | (BLOCKED) | [needs worker to process INIT]
| | |
| [waiting for fd...] | |
| | |
| | |
| DEADLOCK: mount() waits for INIT response |
| but worker thread not started yet |
| because we're waiting for fd |
2. Sync-Init Mount Flow (with --sync-init)
-------------------------------------------
Library fusermount3 Kernel
| | |
|--- spawn fusermount3 ---->| |
| with --sync-init | |
| | |
| [open /dev/fuse] |
| |------- open -------->|
| |<------ fd ---------- |
| | |
| [send_fd(fd)] |
|<------- fd --------------| |
| | |
| [wait_for_signal()] |
| | (BLOCKED) |
| | |
| [ioctl SYNC_INIT] | |
|---------------------------------------- ioctl -->|
| |
| [start worker thread] |
| [worker ready] |
| | |
|--- "proceed" signal ----->| |
| [signal received] |
| | |
| [mount() syscall] |
| |------ mount -------->|
| | | [mount blocks]
| | | [sends INIT]
|<------------------------------------------------ |
| | |
| [worker processes INIT] | |
|------------------------------------------------->|
| | | [mount unblocks]
| |<----- success ------ |
| | |
| [fusermount3 exits] |
| |
| SUCCESS: Worker ready before mount() |
| INIT processed synchronously |
3. Error Scenario: Library Crashes Before Signaling
----------------------------------------------------
Library fusermount3 Kernel
| | |
|--- spawn fusermount3 ---->| |
| with --sync-init | |
| | |
| [open /dev/fuse] |
| |------- open -------->|
| |<------ fd ---------- |
| | |
| [send_fd(fd)] |
|<------- fd --------------| |
| | |
| [wait_for_signal()] |
| | (BLOCKED) |
| | |
X [library crashes] | |
| | |
| [recv() returns 0] |
| [socket closed] |
| | |
| [cleanup and exit] |
| X |
| |
| RESULT: Clean failure, no mount performed |
4. Detailed Function Call Flow
-------------------------------
Library (lib/fuse_lowlevel.c):
fuse_session_mount_new_api()
|
+-- fuse_kern_mount_prepare() [lib/mount.c]
| |
| +-- fuse_mount_fusermount() [lib/mount_util.c]
| |
| +-- socketpair() [create comm socket]
| |
| +-- fork()
| |
| +-- [child] execl("fusermount3", "--sync-init", ...)
| |
| +-- [parent] receive_fd() <--- BLOCKS until fd arrives
| |
| +-- recvmsg(SCM_RIGHTS)
| |
| +-- return fd
|
+-- session_start_sync_init() [lib/fuse_lowlevel.c]
| |
| +-- ioctl(fd, FUSE_DEV_IOC_SYNC_INIT)
| |
| +-- pthread_create(worker_thread)
| |
| +-- return
|
+-- fuse_fusermount_proceed_mnt(socket) [lib/mount.c] <--- NEW: Bidirectional handshake
|
+-- send(socket, "proceed", 1) <--- Signal fusermount3 to proceed
|
+-- recv(socket, &status, 1) <--- BLOCKS until mount result arrives
| |
| +-- [fusermount3 performs mount and sends status byte]
|
+-- if (status != 0) return -1 <--- Mount failed
|
+-- return 0 <--- Mount succeeded
Utility (util/fusermount.c):
fusermount3 main() with --sync-init
|
+-- mount_fuse_sync_init() [util/fusermount.c]
|
+-- mount_fuse_prepare() [util/fusermount.c]
| |
| +-- open("/dev/fuse")
| |
| +-- check_perm() [util/fusermount.c]
| |
| +-- return fd
|
+-- send_fd(socket, fd) [util/fusermount.c]
| |
| +-- sendmsg(SCM_RIGHTS)
|
+-- wait_for_signal(socket) [util/fusermount.c] <--- BLOCKS until library signals
| |
| +-- recv(socket, buf, 1)
| |
| +-- return 0
|
+-- mount_fuse_finish_fsmount() [util/fusermount.c]
| |
| +-- fuse_kern_fsmount() [lib/mount_fsmount.c]
| | |
| | +-- fsopen("fuse", FSOPEN_CLOEXEC)
| | | |
| | | +-- [kernel creates filesystem context]
| | |
| | +-- fsconfig(fsfd, SET_STRING, "source", ...)
| | +-- fsconfig(fsfd, SET_STRING, "fd", fd_value, ...)
| | +-- fsconfig(fsfd, ...) [apply mount options]
| | +-- fsconfig(fsfd, CMD_CREATE, ...)
| | |
| | +-- fsmount(fsfd, FSMOUNT_CLOEXEC, mount_attrs)
| | | |
| | | +-- [kernel sends FUSE_INIT here]
| | | |
| | | +-- [worker thread processes INIT]
| | | |
| | | +-- [fsmount returns mntfd]
| | |
| | +-- move_mount(mntfd, "", AT_FDCWD, target, ...)
| | | |
| | | +-- [attach mount to target directory]
| | | |
| | | +-- [no blocking - INIT already processed]
| | |
| | +-- add_mount() [lib/mount_fsmount.c - update /etc/mtab]
| | |
| | +-- return 0 on success, -1 on failure
| |
| +-- if mount failed: return -1
| +-- if mount succeeded: continue
|
+-- send_status_byte(socket) [util/fusermount.c] <--- NEW: Send result to library
| |
| +-- status = (mount_result == 0) ? 0 : 1
| +-- send(socket, &status, 1)
| |
| +-- return
|
+-- return 0
Note: The new mount API (fsopen/fsconfig/fsmount/move_mount) is REQUIRED
for sync-init because fsmount() triggers FUSE_INIT before the mount
is attached. This allows the worker thread to process INIT before
move_mount() completes, preventing deadlock.
And also so we don't expose the directory tree to the mountns until we
know that FUSE_INIT didn't crash the server.