]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
xfs: ensure unlinked list state is consistent with nlink during scrub
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:54 +0000 (14:58 -0700)
Now that we have the means to tell if an inode is on an unlinked inode
list or not, we can check that an inode with zero link count is on the
unlinked list; and an inode that has nonzero link count is not on that
list.  Make repair clean things up too.

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

index 6e2fe2d6250b3a8c92adb5f47e81921e862e2f47..d32716fb2fecf7419b1c6b5f3a63a67e8ba7e02b 100644 (file)
@@ -739,6 +739,23 @@ xchk_inode_check_reflink_iflag(
                xchk_ino_set_corrupt(sc, ino);
 }
 
+/*
+ * If this inode has zero link count, it must be on the unlinked list.  If
+ * it has nonzero link count, it must not be on the unlinked list.
+ */
+STATIC void
+xchk_inode_check_unlinked(
+       struct xfs_scrub        *sc)
+{
+       if (VFS_I(sc->ip)->i_nlink == 0) {
+               if (!xfs_inode_on_unlinked_list(sc->ip))
+                       xchk_ino_set_corrupt(sc, sc->ip->i_ino);
+       } else {
+               if (xfs_inode_on_unlinked_list(sc->ip))
+                       xchk_ino_set_corrupt(sc, sc->ip->i_ino);
+       }
+}
+
 /* Scrub an inode. */
 int
 xchk_inode(
@@ -771,6 +788,8 @@ xchk_inode(
        if (S_ISREG(VFS_I(sc->ip)->i_mode))
                xchk_inode_check_reflink_iflag(sc, sc->ip->i_ino);
 
+       xchk_inode_check_unlinked(sc);
+
        xchk_inode_xref(sc, sc->ip->i_ino, &di);
 out:
        return error;
index 097afba3043f8046452425a63a65f3f40e011772..c743772a523e909d4cdf908069b51fda8e9d1d4c 100644 (file)
@@ -1745,6 +1745,46 @@ xrep_inode_problems(
        return xrep_roll_trans(sc);
 }
 
+/*
+ * Make sure this inode's unlinked list pointers are consistent with its
+ * link count.
+ */
+STATIC int
+xrep_inode_unlinked(
+       struct xfs_scrub        *sc)
+{
+       unsigned int            nlink = VFS_I(sc->ip)->i_nlink;
+       int                     error;
+
+       /*
+        * If this inode is linked from the directory tree and on the unlinked
+        * list, remove it from the unlinked list.
+        */
+       if (nlink > 0 && xfs_inode_on_unlinked_list(sc->ip)) {
+               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);
+               if (error)
+                       return error;
+       }
+
+       /*
+        * If this inode is not linked from the directory tree yet not on the
+        * unlinked list, put it on the unlinked list.
+        */
+       if (nlink == 0 && !xfs_inode_on_unlinked_list(sc->ip)) {
+               error = xfs_iunlink(sc->tp, sc->ip);
+               if (error)
+                       return error;
+       }
+
+       return 0;
+}
+
 /* Repair an inode's fields. */
 int
 xrep_inode(
@@ -1794,5 +1834,10 @@ xrep_inode(
                        return error;
        }
 
+       /* Reconnect incore unlinked list */
+       error = xrep_inode_unlinked(sc);
+       if (error)
+               return error;
+
        return xrep_defer_finish(sc);
 }
index ac92c0525d9b6cd24a53f58807d74f793325f109..b24c0e23d37d72e0aed22429698212e4c6f9a258 100644 (file)
@@ -42,9 +42,6 @@
 
 struct kmem_cache *xfs_inode_cache;
 
-STATIC int xfs_iunlink_remove(struct xfs_trans *tp, struct xfs_perag *pag,
-       struct xfs_inode *);
-
 /*
  * helper function to extract extent size hint from inode
  */
@@ -2252,7 +2249,7 @@ xfs_iunlink_remove_inode(
 /*
  * Pull the on-disk inode from the AGI unlinked list.
  */
-STATIC int
+int
 xfs_iunlink_remove(
        struct xfs_trans        *tp,
        struct xfs_perag        *pag,
index 596eec71567539af7818d036b2b31fb81c94d52c..8157ae7f8e59f491819904677a83cfa40702ff39 100644 (file)
@@ -617,6 +617,8 @@ extern struct kmem_cache    *xfs_inode_cache;
 bool xfs_inode_needs_inactive(struct xfs_inode *ip);
 
 int xfs_iunlink(struct xfs_trans *tp, struct xfs_inode *ip);
+int xfs_iunlink_remove(struct xfs_trans *tp, struct xfs_perag *pag,
+               struct xfs_inode *ip);
 
 void xfs_end_io(struct work_struct *work);