virtio/fs/macos: keep a fd to unlinked files#513
Merged
slp merged 1 commit intocontainers:mainfrom Feb 6, 2026
Merged
Conversation
5f05a91 to
50417bf
Compare
mtjhrc
reviewed
Jan 16, 2026
Comment on lines
+490
to
+496
| if unlinked_fd >= 0 { | ||
| // Only use `unlinked_fd` when the inode path no longer exists. | ||
| let res = unsafe { libc::access(cstr.as_ptr(), libc::F_OK) }; | ||
| if res != 0 { | ||
| return Ok(InodeHandle::Fd(unlinked_fd as RawFd)); | ||
| } | ||
| } |
Collaborator
There was a problem hiding this comment.
Why do you need to check if the .vol path exists here? If you already have a valid file descriptor can't you always just use that instead?
Collaborator
Author
There was a problem hiding this comment.
In the initial iterations of this commit, many methods didn't work with file descriptors. Right now, only link() needs a path, so I've added a supports_fd boolean to inode_to_handle to deal with this particular method, and return unlinked_fd (if present) for every other one.
9133ba2 to
d256f63
Compare
Collaborator
Author
|
@mtjhrc PTAL |
d256f63 to
284b68c
Compare
On the macOS implementation of passthrough, we avoid keeping an FD open for each dirent entry by accessing the inode using the special directory '/.vol'. But this strategy doesn't work when the inode has been unlinked, and it's completely valid for userspace to keep accessing a file after unlinking it by keeping an FD to it. To fix this without having to keep an FD open as the Linux implementation does (thus risking running out of open handles), here we change do_unlink() to open an FD and store it in InodeData->unlinked_fd. Then we change inode_to_path() to become inode_to_handle(), being able to return either a path accssible through '/.vol' or an FD. Finally, we update every user of inode_to_handle() to be able to operate both with a path and with an FD. The FD stored in InodeData->unlinked_fd is released on forget_one(), which is called when the guest removes the entry from dirent. Instead of wrapping the FD behind an OwnedFd or a File, we operate on it as an integer/RawFd. This allows us to 1) store the value as an atomic operation without a Mutex and 2) save a couple of syscalls everytime we use it. Signed-off-by: Sergio Lopez <slp@redhat.com>
284b68c to
900703e
Compare
This was referenced Feb 9, 2026
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.
On the macOS implementation of passthrough, we avoid keeping an FD open for each dirent entry by accessing the inode using the special directory '/.vol'. But this strategy doesn't work when the inode has been unlinked, and it's completely valid for userspace to keep accessing a file after unlinking it by keeping an FD to it.
To fix this without having to keep an FD open as the Linux implementation does (thus risking running out of open handles), here we change do_unlink() to open an FD and store it in InodeData->unlinked_fd. Then we change inode_to_path() to become inode_to_handle(), being able to return either a path accssible through '/.vol' or an FD. Finally, we update every user of inode_to_handle() to be able to operate both with a path and with an FD.
The FD stored in InodeData->unlinked_fd is released on forget_one(), which is called when the guest removes the entry from dirent.
Instead of wrapping the FD behind an OwnedFd or a File, we operate on it as an integer/RawFd. This allows us to 1) store the value as an atomic operation without a Mutex and 2) save a couple of syscalls everytime we use it.