From b892d619b9eaa9c6ac6ae1751cbe06ef5609cd64 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Fri, 13 Nov 2026 01:09:29 -0800 Subject: [PATCH] mount: keep flushing an open file whose other hard link survives an unlink Unlink marks the inode's open handle deleted so no flush recreates the removed name. When the entry was one of several hard links the inode lives on under another name, yet the mark stayed and every later fsync and close returned OK without publishing metadata. Lift the unlink's own mark once the name is gone and the inode still has a path. Co-Authored-By: Claude Opus 5.5 --- weed/mount/weedfs_file_mkrm.go | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/weed/mount/weedfs_file_mkrm.go b/weed/mount/weedfs_file_mkrm.go index 78ad8d819d..06ec73d1ef 100644 --- a/weed/mount/weedfs_file_mkrm.go +++ b/weed/mount/weedfs_file_mkrm.go @@ -286,13 +286,15 @@ 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. + var markedInode, deleteEpoch uint64 if inode, found := wfs.inodeToPath.GetInode(entryFullPath); found { - wfs.markHandleDeleted(inode) - wfs.waitForPendingAsyncFlush(context.Background(), inode) + markedInode = inode } else if entry != nil && entry.Attributes != nil && entry.Attributes.Inode != 0 { - inodeFromEntry := entry.Attributes.Inode - wfs.markHandleDeleted(inodeFromEntry) - wfs.waitForPendingAsyncFlush(context.Background(), inodeFromEntry) + markedInode = entry.Attributes.Inode + } + if markedInode != 0 { + deleteEpoch = wfs.markHandleDeleted(markedInode) + wfs.waitForPendingAsyncFlush(context.Background(), markedInode) } // first, ensure the filer store can correctly delete @@ -355,6 +357,14 @@ func (wfs *WFS) Unlink(cancel <-chan struct{}, header *fuse.InHeader, name strin wfs.syncHardLinkSiblings(ctx, sharedInode, decremented, event.GetTsNs(), entryFullPath) } + // Another link still names the inode, so its open handle keeps flushing + // through that name. + if isHardLink && markedInode != 0 { + if _, status := wfs.inodeToPath.GetPath(markedInode); status == fuse.OK { + wfs.clearHandleDeleted(markedInode, deleteEpoch) + } + } + return fuse.OK }