macos: defer FD_CLOEXEC fcntl until after fuse_daemonize()#380
Open
Yike-Ye wants to merge 1 commit into
Open
Conversation
On macOS with macFUSE 5.3.3+, fuse_mount() only records the mount
request; the real mount and its XPC channel are created lazily on first
use. fuse_session_fd() is one such first use: since macFUSE commit
867b5cdb ("Defer mount process until first use on macOS") it routes
through fuse_session_mfch(), which triggers the delayed mount and marks
the mount as started. sshfs calls fuse_session_fd() between fuse_mount()
and fuse_daemonize() only to set FD_CLOEXEC on the fuse device, so on
5.3.3 that call now materializes the mount before daemonizing, and
fuse_daemonize() then refuses to fork ("daemonize requested after mount
started; continuing in foreground"). sshfs can no longer background
itself on macOS even though its own code is unchanged.
Defer the FD_CLOEXEC fcntl on macOS until after fuse_daemonize(). The
fuse fd does not exist yet while the first ssh child is spawned in
ssh_connect(), so nothing leaks in that window; the fcntl then runs in
the daemon child, triggering the mount there (where its XPC connection
survives) and still setting FD_CLOEXEC before any ssh child is
re-spawned on reconnect. The upstream mount -> connect -> daemonize
order is otherwise unchanged, and non-macOS platforms keep the fcntl in
its original place (before ssh_connect), where it must stay because the
fuse fd exists from fuse_mount() onward.
Refs: libfuse#338
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
Problem
On macOS with macFUSE 5.3.3, sshfs can no longer daemonize into the background. Mounting without
-fprintsand the process stays attached to the terminal, even though sshfs's own code hasn't changed. @dmik independently hit this in #371 and suggested that "sshfs daemonization should be changed so that it happens before triggering
MFMountuse"; this PR does exactly that.Root cause
macFUSE 5.3.3 mounts lazily:
fuse_session_mount()(called byfuse_mount()) only records the request, and the real mount — including its XPC channel — is created on first use, after the normalfuse_main()daemonization point. Once that mount has started,fuse_daemonize()deliberately refuses to fork, because the XPC connection cannot survive a fork.The catch is that
fuse_session_fd()is one of those "first uses". Since macFUSE commit867b5cdb("Defer mount process until first use on macOS") it went from a plain getter to routing throughfuse_session_mfch(), which triggers the delayed mount:sshfs calls
fuse_session_fd()betweenfuse_mount()andfuse_daemonize()purely to setFD_CLOEXECon the fuse device:On 5.3.3 that call now materializes the mount before daemonizing, so by the time
fuse_daemonize()checksfuse_darwin_mount_started()it is already set, and sshfs stays in the foreground.Fix
Defer the
FD_CLOEXECfcntl on macOS until afterfuse_daemonize(). The upstreammount → connect → daemonizeorder is otherwise unchanged.This is safe:
sshchild is spawned inssh_connect()(the delayed mount hasn't been triggered), so there is nothing to leak in that window.FD_CLOEXECbefore anysshchild is re-spawned on-o reconnect.Non-macOS platforms keep the fcntl in its original place (before
ssh_connect()), where it must stay because the fuse fd exists fromfuse_mount()onward. This makes sshfs on macOS behave likefuse_main(), which never touches the session fd before daemonizing.Testing
macOS 27 + macFUSE 5.3.3, key auth, no
-f: the process now daemonizes to the background (PPID == 1), the mount completes in the daemon child, the SSH connection survives, and directory enumeration works. The foreground fallback no longer triggers.Refs: #338, #371