mount: publish a queued close before unlinking a name another link keeps

With writeback caching, close only queues the flush. Unlink marked the
handle deleted before waiting for that flush, so the flush skipped its
metadata and the surviving link kept the content from before the close.
When another link keeps the file, wait for the queued flush first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Chris LuandClaude Opus 5.5 committed 2026-11-13 01:46:12 -08:00
1 parent 3d49574ba4
commit 641c5081c7
1 file changed
+5 -1
+5 -1
View File
@@ -286,6 +286,7 @@ func (wfs *WFS) Unlink(cancel <-chan struct{}, header *fuse.InHeader, name strin
// before Unlink sets the flag). By waiting here, any in-flight flush
// finishes first; even if it recreated the entry, the filer delete below
// will remove it again.
isHardLink := entry != nil && entry.Attributes != nil && len(entry.HardLinkId) > 0 && entry.HardLinkCounter > 1
var markedInode, deleteEpoch uint64
if inode, found := wfs.inodeToPath.GetInode(entryFullPath); found {
markedInode = inode
@@ -293,6 +294,10 @@ func (wfs *WFS) Unlink(cancel <-chan struct{}, header *fuse.InHeader, name strin
markedInode = entry.Attributes.Inode
}
if markedInode != 0 {
if isHardLink {
// Another link keeps the content, so a queued close publishes it.
wfs.waitForPendingAsyncFlush(context.Background(), markedInode)
}
deleteEpoch = wfs.markHandleDeleted(markedInode)
wfs.waitForPendingAsyncFlush(context.Background(), markedInode)
}
@@ -335,7 +340,6 @@ func (wfs *WFS) Unlink(cancel <-chan struct{}, header *fuse.InHeader, name strin
// to every sibling cache entry and invalidate the kernel attr cache
// so subsequent lstats on other links see the new nlink — pjdfstest
// link/00.t asserts this after `unlink n0` leaves n1/n2 behind.
isHardLink := entry != nil && entry.Attributes != nil && len(entry.HardLinkId) > 0 && entry.HardLinkCounter > 1
var sharedInode uint64
if isHardLink {
sharedInode = entry.Attributes.Inode