Unmount lazily on Linux, so a busy filesystem is not left mounted - #714
Merged
Conversation
An unprivileged unmount has always been lazy, going through "fusermount -u -z", but a privileged one called umount(2) with no flags and so failed with EBUSY while the filesystem was still in use. By then the Mount has been consumed, so the caller has nothing left to retry with and the filesystem stays mounted. Detach lazily on Linux instead, which is what libfuse's fuse_kern_unmount() does in both cases. Teardown is already built for it: BackgroundSession::drop waits a bounded time for the connection to end and detaches the session thread if it does not. The privileged path now behaves as the unprivileged one always has, which includes join() and umount_and_join() waiting for open handles to be released rather than reporting EBUSY. The retry of the same unmount syscall in fuse_unmount_pure() is dropped: it is only reached once umount(2) has failed with EPERM, which different flags do not change. Fixes #686 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NjYWzn3mu8Sc2y2eACtJM9
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cedf2411e0
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
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.
Fixes #686.
An unprivileged unmount has always been lazy, going through
fusermount -u -z, but a privileged one calledumount(2)with no flags, so it failed withEBUSYwhile the filesystem was still in use. By then theMounthas been consumed, so the caller has nothing left to retry with and the filesystem stays mounted.This detaches lazily on Linux instead, which is what libfuse's
fuse_kern_unmount()does in both cases.libc_umount()is shared, so this covers all three backends.Behavior, with one open handle on the mount root
SessionUnmounter::unmount()EBUSY, stays mountedumount_and_join()EBUSY, stays mounteddrop(BackgroundSession)UNMOUNT_WAIT, then detachesUNMOUNT_WAIT, then detachesSo this is not purely a fix: privileged
join()/umount_and_join()callers trade an immediateEBUSYfor waiting on open handles. That seemed worth it, since the alternative leaves a filesystem mounted with no way to retry, and it is what the unprivileged path has always done. Teardown was already built for lazy semantics:BackgroundSession::dropwaits a bounded time for the connection to end and detaches the session thread if it does not.The retry of the same unmount syscall in
fuse_unmount_pure()is dropped. It is only reached onceumount(2)has failed withEPERM, which different flags do not change.Testing
umount_succeeds_while_filesystem_is_busyholds an open handle on the mount root and asserts the unmount does not fail. It fails withEBUSYon master when run as root, and passes with this change. Note that CI runs the unit tests unprivileged, where it passes either way — it only guards the regression when the suite is run as root, which is how I verified it.Also run locally, on all three backends both as root and unprivileged: the unit suite,
cargo fmt --check, clippy with and without default features, the musl build,cargo doc,cargo check --target x86_64-apple-darwin --features=macos-no-mount, and both passthrough example tests.Deliberately not included
The issue also asks for
EBUSYhandling on macOS and the BSDs, where there is no lazy unmount. I left both alone: neither is reachable by CI here (mac-ciis build/lint only, andfreebsd-ciruns itscargo teststep unprivileged and skips the mount tests), so a retry loop orMNT_FORCEwould ship unverified.MNT_FORCEin particular is a much stronger semantic than a lazy detach — it revokes open handles, so in-flight I/O fails and unflushed data can be lost. Happy to take either on if you want it here.Two points from the issue's follow-up comment are already resolved on master: unprivileged unmount is no longer gated away for libfuse2 on Linux (
fuse2.rscallsfusermount_unmount()), andDrop for MountImplnow callsfuse_session_destroy()on every path.Generated by Claude Code