mirror of
https://github.com/libfuse/libfuse.git
synced 2026-07-07 14:47:41 +08:00
8a4834952c
These are useful to Signed-off-by: Bernd Schubert <bernd@bsbernd.com>
363 lines
14 KiB
Plaintext
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.
|
|
|