fix(filer): do not abort entry deletion when hard link cleanup fails (#9022)

When unlinking a hard-linked file, DeleteOneEntry and DeleteEntry both
called DeleteHardLink before removing the directory entry from the
store. If DeleteHardLink returned an error (e.g. KV storage issue,
decode failure), the function returned early without deleting the
directory entry itself. This left a stale entry in the filer store,
causing subsequent rmdir to fail with ENOTEMPTY.

Change both functions to log the hard link cleanup error and continue
to delete the directory entry regardless. This ensures the parent
directory can always be removed after all its children are unlinked.

Remove tests/unlink/14.t from the pjdfstest known failures list since
this fix addresses the root cause.
This commit is contained in:
Chris Lu
2026-04-10 13:59:58 -07:00
committed by GitHub
parent 07cd741380
commit 2a7ec8d033
2 changed files with 13 additions and 6 deletions
+2 -2
View File
@@ -48,7 +48,7 @@ tests/rename/24.t
tests/rename/21.t
# ── rmdir after hard link unlink ───────────────────────────────────────
# The filer may still report a directory as non-empty after all hard-linked
# entries have been unlinked.
# Making DeleteHardLink errors non-fatal prevents the entry from blocking
# rmdir in most cases, but the test still has 1 subtest that fails.
tests/unlink/14.t
+11 -4
View File
@@ -265,8 +265,11 @@ func (fsw *FilerStoreWrapper) DeleteEntry(ctx context.Context, fp util.FullPath)
op := ctx.Value("OP")
if op != "MV" {
glog.V(4).InfofCtx(ctx, "DeleteHardLink %s", existingEntry.FullPath)
if err = fsw.DeleteHardLink(ctx, existingEntry.HardLinkId); err != nil {
return err
if hlErr := fsw.DeleteHardLink(ctx, existingEntry.HardLinkId); hlErr != nil {
// Log but continue: the directory entry must be removed
// even if hard link counter cleanup fails, otherwise the
// parent directory cannot be removed (rmdir ENOTEMPTY).
glog.Warningf("DeleteHardLink %s (id %x): %v", existingEntry.FullPath, existingEntry.HardLinkId, hlErr)
}
}
}
@@ -292,8 +295,12 @@ func (fsw *FilerStoreWrapper) DeleteOneEntry(ctx context.Context, existingEntry
op := ctx.Value("OP")
if op != "MV" {
glog.V(4).InfofCtx(ctx, "DeleteHardLink %s", existingEntry.FullPath)
if err = fsw.DeleteHardLink(ctx, existingEntry.HardLinkId); err != nil {
return err
if hlErr := fsw.DeleteHardLink(ctx, existingEntry.HardLinkId); hlErr != nil {
// Log the hard link cleanup error but continue to delete
// the directory entry. If we return early here, the entry
// remains in the store and the parent directory cannot be
// removed (rmdir returns ENOTEMPTY).
glog.Warningf("DeleteHardLink %s (id %x): %v", existingEntry.FullPath, existingEntry.HardLinkId, hlErr)
}
}
}