]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
xfs: fix di_onlink checking for V1/V2 inodes
authorDarrick J. Wong <djwong@kernel.org>
Thu, 22 Aug 2024 23:59:01 +0000 (16:59 -0700)
committerChandan Babu R <chandanbabu@kernel.org>
Mon, 26 Aug 2024 04:20:41 +0000 (09:50 +0530)
"KjellR" complained on IRC that an old V4 filesystem suddenly stopped
mounting after upgrading from 6.9.11 to 6.10.3, with the following splat
when trying to read the rt bitmap inode:

00000000: 49 4e 80 00 01 02 00 01 00 00 00 00 00 00 00 00  IN..............
00000010: 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00  ................
00000020: 00 00 00 00 00 00 00 00 43 d2 a9 da 21 0f d6 30  ........C...!..0
00000030: 43 d2 a9 da 21 0f d6 30 00 00 00 00 00 00 00 00  C...!..0........
00000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00000050: 00 00 00 02 00 00 00 00 00 00 00 04 00 00 00 00  ................
00000060: ff ff ff ff 00 00 00 00 00 00 00 00 00 00 00 00  ................
00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

As Dave Chinner points out, this is a V1 inode with both di_onlink and
di_nlink set to 1 and di_flushiter == 0.  In other words, this inode was
formatted this way by mkfs and hasn't been touched since then.

Back in the old days of xfsprogs 3.2.3, I observed that libxfs_ialloc
would set di_nlink, but if the filesystem didn't have NLINK, it would
then set di_version = 1.  libxfs_iflush_int later sees the V1 inode and
copies the value of di_nlink to di_onlink without zeroing di_onlink.

Eventually this filesystem must have been upgraded to support NLINK
because 6.10 doesn't support !NLINK filesystems, which is how we tripped
over this old behavior.  The filesystem doesn't have a realtime section,
so that's why the rtbitmap inode has never been touched.

Fix this by removing the di_onlink/di_nlink checking for all V1/V2
inodes because this is a muddy mess.  The V3 inode handling code has
always supported NLINK and written di_onlink==0 so keep that check.
The removal of the V1 inode handling code when we dropped support for
!NLINK obscured this old behavior.

Reported-by: kjell.m.randa@gmail.com
Fixes: 40cb8613d612 ("xfs: check unused nlink fields in the ondisk inode")
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
fs/xfs/libxfs/xfs_inode_buf.c

index 513b50da6215f7d67b11448bb4722a31e7923c95..79babeac9d7546c1f232629421dcb1dbcb618f67 100644 (file)
@@ -514,12 +514,18 @@ xfs_dinode_verify(
                        return __this_address;
        }
 
-       if (dip->di_version > 1) {
+       /*
+        * Historical note: xfsprogs in the 3.2 era set up its incore inodes to
+        * have di_nlink track the link count, even if the actual filesystem
+        * only supported V1 inodes (i.e. di_onlink).  When writing out the
+        * ondisk inode, it would set both the ondisk di_nlink and di_onlink to
+        * the the incore di_nlink value, which is why we cannot check for
+        * di_nlink==0 on a V1 inode.  V2/3 inodes would get written out with
+        * di_onlink==0, so we can check that.
+        */
+       if (dip->di_version >= 2) {
                if (dip->di_onlink)
                        return __this_address;
-       } else {
-               if (dip->di_nlink)
-                       return __this_address;
        }
 
        /* don't allow invalid i_size */