From 641c5081c7b98c05a9c99478ea9b2aa13c2af516 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Fri, 13 Nov 2026 01:46:12 -0800 Subject: [PATCH] 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 --- weed/mount/weedfs_file_mkrm.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/weed/mount/weedfs_file_mkrm.go b/weed/mount/weedfs_file_mkrm.go index 548eefcd3d..f55c0473ff 100644 --- a/weed/mount/weedfs_file_mkrm.go +++ b/weed/mount/weedfs_file_mkrm.go @@ -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