xfs: update the unlinked list when repairing link counts
authorDarrick J. Wong <djwong@kernel.org>
Mon, 15 Apr 2024 21:54:49 +0000 (14:54 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Mon, 15 Apr 2024 21:58:55 +0000 (14:58 -0700)
When we're repairing the link counts of a file, we must ensure either
that the file has zero link count and is on the unlinked list; or that
it has nonzero link count and is not on the unlinked list.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
fs/xfs/scrub/nlinks_repair.c

index b87618322f55bd919d6387e3405faa1a50b652e1..58cacb8e94c1b8eb2e405f0c48aa3d73795b150b 100644 (file)
@@ -17,6 +17,7 @@
 #include "xfs_iwalk.h"
 #include "xfs_ialloc.h"
 #include "xfs_sb.h"
+#include "xfs_ag.h"
 #include "scrub/scrub.h"
 #include "scrub/common.h"
 #include "scrub/repair.h"
  * inode is locked.
  */
 
+/* Remove an inode from the unlinked list. */
+STATIC int
+xrep_nlinks_iunlink_remove(
+       struct xfs_scrub        *sc)
+{
+       struct xfs_perag        *pag;
+       int                     error;
+
+       pag = xfs_perag_get(sc->mp, XFS_INO_TO_AGNO(sc->mp, sc->ip->i_ino));
+       error = xfs_iunlink_remove(sc->tp, pag, sc->ip);
+       xfs_perag_put(pag);
+       return error;
+}
+
 /*
  * Correct the link count of the given inode.  Because we have to grab locks
  * and resources in a certain order, it's possible that this will be a no-op.
@@ -99,16 +114,25 @@ xrep_nlinks_repair_inode(
        }
 
        /*
-        * We did not find any links to this inode.  If the inode agrees, we
-        * have nothing further to do.  If not, the inode has a nonzero link
-        * count and we don't have anywhere to graft the child onto.  Dropping
-        * a live inode's link count to zero can cause unexpected shutdowns in
-        * inactivation, so leave it alone.
+        * If this inode is linked from the directory tree and on the unlinked
+        * list, remove it from the unlinked list.
         */
-       if (total_links == 0) {
-               if (actual_nlink != 0)
-                       trace_xrep_nlinks_unfixable_inode(mp, ip, &obs);
-               goto out_trans;
+       if (total_links > 0 && xfs_inode_on_unlinked_list(ip)) {
+               error = xrep_nlinks_iunlink_remove(sc);
+               if (error)
+                       goto out_trans;
+               dirty = true;
+       }
+
+       /*
+        * If this inode is not linked from the directory tree yet not on the
+        * unlinked list, put it on the unlinked list.
+        */
+       if (total_links == 0 && !xfs_inode_on_unlinked_list(ip)) {
+               error = xfs_iunlink(sc->tp, ip);
+               if (error)
+                       goto out_trans;
+               dirty = true;
        }
 
        /* Commit the new link count if it changed. */