xfs: don't expose misaligned extszinherit hints to userspace
authorDarrick J. Wong <djwong@kernel.org>
Mon, 12 Jul 2021 19:58:51 +0000 (12:58 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Thu, 15 Jul 2021 16:58:42 +0000 (09:58 -0700)
Commit 603f000b15f2 changed xfs_ioctl_setattr_check_extsize to reject an
attempt to set an EXTSZINHERIT extent size hint on a directory with
RTINHERIT set if the hint isn't a multiple of the realtime extent size.
However, I have recently discovered that it is possible to change the
realtime extent size when adding a rt device to a filesystem, which
means that the existence of directories with misaligned inherited hints
is not an accident.

As a result, it's possible that someone could have set a valid hint and
added an rt volume with a different rt extent size, which invalidates
the ondisk hints.  After such a sequence, FSGETXATTR will report a
misaligned hint, which FSSETXATTR will trip over, causing confusion if
the user was doing the usual GET/SET sequence to change some other
attribute.  Change xfs_fill_fsxattr to omit the hint if it isn't aligned
properly.

Fixes: 603f000b15f2 ("xfs: validate extsz hints against rt extent size when rtinherit is set")
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
fs/xfs/xfs_ioctl.c

index 275d1c3..16039ea 100644 (file)
@@ -1065,7 +1065,24 @@ xfs_fill_fsxattr(
 
        fileattr_fill_xflags(fa, xfs_ip2xflags(ip));
 
-       fa->fsx_extsize = XFS_FSB_TO_B(mp, ip->i_extsize);
+       if (ip->i_diflags & XFS_DIFLAG_EXTSIZE) {
+               fa->fsx_extsize = XFS_FSB_TO_B(mp, ip->i_extsize);
+       } else if (ip->i_diflags & XFS_DIFLAG_EXTSZINHERIT) {
+               /*
+                * Don't let a misaligned extent size hint on a directory
+                * escape to userspace if it won't pass the setattr checks
+                * later.
+                */
+               if ((ip->i_diflags & XFS_DIFLAG_RTINHERIT) &&
+                   ip->i_extsize % mp->m_sb.sb_rextsize > 0) {
+                       fa->fsx_xflags &= ~(FS_XFLAG_EXTSIZE |
+                                           FS_XFLAG_EXTSZINHERIT);
+                       fa->fsx_extsize = 0;
+               } else {
+                       fa->fsx_extsize = XFS_FSB_TO_B(mp, ip->i_extsize);
+               }
+       }
+
        if (ip->i_diflags2 & XFS_DIFLAG2_COWEXTSIZE)
                fa->fsx_cowextsize = XFS_FSB_TO_B(mp, ip->i_cowextsize);
        fa->fsx_projid = ip->i_projid;