xfs: don't set v3 xflags for v2 inodes
authorChristoph Hellwig <hch@lst.de>
Sat, 2 Sep 2017 15:21:20 +0000 (08:21 -0700)
committerDarrick J. Wong <darrick.wong@oracle.com>
Sat, 2 Sep 2017 15:22:19 +0000 (08:22 -0700)
Reject attempts to set XFLAGS that correspond to di_flags2 inode flags
if the inode isn't a v3 inode, because di_flags2 only exists on v3.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
fs/xfs/xfs_ioctl.c

index 06ca244c323ae3af6940478f08b834b7707101d6..5049e8ab6e302e05b4c0d66fcf3eaa1ba9e2721b 100644 (file)
@@ -931,16 +931,15 @@ xfs_ioc_fsgetxattr(
        return 0;
 }
 
-STATIC void
-xfs_set_diflags(
+STATIC uint16_t
+xfs_flags2diflags(
        struct xfs_inode        *ip,
        unsigned int            xflags)
 {
-       unsigned int            di_flags;
-       uint64_t                di_flags2;
-
        /* can't set PREALLOC this way, just preserve it */
-       di_flags = (ip->i_d.di_flags & XFS_DIFLAG_PREALLOC);
+       uint16_t                di_flags =
+               (ip->i_d.di_flags & XFS_DIFLAG_PREALLOC);
+
        if (xflags & FS_XFLAG_IMMUTABLE)
                di_flags |= XFS_DIFLAG_IMMUTABLE;
        if (xflags & FS_XFLAG_APPEND)
@@ -970,19 +969,24 @@ xfs_set_diflags(
                if (xflags & FS_XFLAG_EXTSIZE)
                        di_flags |= XFS_DIFLAG_EXTSIZE;
        }
-       ip->i_d.di_flags = di_flags;
 
-       /* diflags2 only valid for v3 inodes. */
-       if (ip->i_d.di_version < 3)
-               return;
+       return di_flags;
+}
+
+STATIC uint64_t
+xfs_flags2diflags2(
+       struct xfs_inode        *ip,
+       unsigned int            xflags)
+{
+       uint64_t                di_flags2 =
+               (ip->i_d.di_flags2 & XFS_DIFLAG2_REFLINK);
 
-       di_flags2 = (ip->i_d.di_flags2 & XFS_DIFLAG2_REFLINK);
        if (xflags & FS_XFLAG_DAX)
                di_flags2 |= XFS_DIFLAG2_DAX;
        if (xflags & FS_XFLAG_COWEXTSIZE)
                di_flags2 |= XFS_DIFLAG2_COWEXTSIZE;
 
-       ip->i_d.di_flags2 = di_flags2;
+       return di_flags2;
 }
 
 STATIC void
@@ -1023,6 +1027,7 @@ xfs_ioctl_setattr_xflags(
        struct fsxattr          *fa)
 {
        struct xfs_mount        *mp = ip->i_mount;
+       uint64_t                di_flags2;
 
        /* Can't change realtime flag if any extents are allocated. */
        if ((ip->i_d.di_nextents || ip->i_delayed_blks) &&
@@ -1053,7 +1058,14 @@ xfs_ioctl_setattr_xflags(
            !capable(CAP_LINUX_IMMUTABLE))
                return -EPERM;
 
-       xfs_set_diflags(ip, fa->fsx_xflags);
+       /* diflags2 only valid for v3 inodes. */
+       di_flags2 = xfs_flags2diflags2(ip, fa->fsx_xflags);
+       if (di_flags2 && ip->i_d.di_version < 3)
+               return -EINVAL;
+
+       ip->i_d.di_flags = xfs_flags2diflags(ip, fa->fsx_xflags);
+       ip->i_d.di_flags2 = di_flags2;
+
        xfs_diflags_to_linux(ip);
        xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG);
        xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);