Skip to content

Conversation

@slp
Copy link
Collaborator

@slp slp commented Jan 13, 2026

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.

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 <[email protected]>
@slp slp force-pushed the fs-macos-fix-getattr branch from 5f05a91 to 50417bf Compare January 13, 2026 13:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant