xfs
[Top] [All Lists]

[PATCH] xfs: fix behaviour of XFS_IOC_FSSETXATTR on directories

To: david@xxxxxxxxxxxxx
Subject: [PATCH] xfs: fix behaviour of XFS_IOC_FSSETXATTR on directories
From: Iustin Pop <iustin@xxxxxxxxx>
Date: Thu, 4 Dec 2014 05:14:26 +0100
Cc: xfs@xxxxxxxxxxx, Iustin Pop <iustin@xxxxxxxxx>
Delivered-to: xfs@xxxxxxxxxxx
In-reply-to: <20140829004607.GX20518@dastard>
References: <20140829004607.GX20518@dastard>
Currently, the ioctl handling code for XFS_IOC_FSSETXATTR treats all
targets as regular files: it refuses to change the extent size if
extents are allocated. This is wrong for directories, as there the
extent size is only used as a default for children.

The patch fixes this issue and improves validation of flag
combinations:

- only disallow extent size changes after extents have been allocated
  for regular files
- only allow XFS_XFLAG_EXTSIZE for regular files
- only allow XFS_XFLAG_EXTSZINHERIT for directories
- automatically clear the flags if the extent size is zero

Thanks to Dave Chinner for guidance on the proper fix for this issue.

Signed-off-by: Iustin Pop <iustin@xxxxxxxxx>
---
 Trying to revive this fix. Note that this patch is on top of
 git://git.kernel.org/pub/scm/linux/kernel/git/dgc/linux-xfs.git master, which
 seems to have no commits since Oct 26; let me know if I should rebase it on
 top of something else.

 fs/xfs/xfs_ioctl.c | 59 ++++++++++++++++++++++++++++++++----------------------
 1 file changed, 35 insertions(+), 24 deletions(-)

diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
index 24c926b..67e3ab1 100644
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -1113,34 +1113,49 @@ xfs_ioctl_setattr(
        }
 
        if (mask & FSX_EXTSIZE) {
-               /*
-                * Can't change extent size if any extents are allocated.
+               code = -EINVAL;
+
+               /* Validate the flags are set appropriately per the
+                * inode type.
                 */
-               if (ip->i_d.di_nextents &&
-                   ((ip->i_d.di_extsize << mp->m_sb.sb_blocklog) !=
-                    fa->fsx_extsize)) {
-                       code = -EINVAL; /* EFBIG? */
+               if ((fa->fsx_xflags & XFS_XFLAG_EXTSIZE) &&
+                   !S_ISREG(ip->i_d.di_mode))
+                       goto error_return;
+               if ((fa->fsx_xflags & XFS_XFLAG_EXTSZINHERIT) &&
+                   !S_ISDIR(ip->i_d.di_mode))
                        goto error_return;
-               }
 
                /*
-                * Extent size must be a multiple of the appropriate block
-                * size, if set at all. It must also be smaller than the
-                * maximum extent size supported by the filesystem.
-                *
-                * Also, for non-realtime files, limit the extent size hint to
-                * half the size of the AGs in the filesystem so alignment
-                * doesn't result in extents larger than an AG.
+                * Dissalow changing extent size on regular files with
+                * allocated extents.
                 */
-               if (fa->fsx_extsize != 0) {
+               if (S_ISREG(ip->i_d.di_mode) && ip->i_d.di_nextents &&
+                   ((ip->i_d.di_extsize << mp->m_sb.sb_blocklog) !=
+                    fa->fsx_extsize))
+                       goto error_return;
+
+               /* If the extent size is zero, clear the inode flags. */
+               if (fa->fsx_extsize == 0) {
+                       fa->fsx_xflags &= ~(XFS_XFLAG_EXTSIZE |
+                                           XFS_XFLAG_EXTSZINHERIT);
+               } else {
+                       /*
+                        * Extent size must be a multiple of the
+                        * appropriate block size, if set at all. It
+                        * must also be smaller than the maximum
+                        * extent size supported by the filesystem.
+                        *
+                        * Also, for non-realtime files, limit the
+                        * extent size hint to half the size of the
+                        * AGs in the filesystem so alignment doesn't
+                        * result in extents larger than an AG.
+                        */
                        xfs_extlen_t    size;
                        xfs_fsblock_t   extsize_fsb;
 
                        extsize_fsb = XFS_B_TO_FSB(mp, fa->fsx_extsize);
-                       if (extsize_fsb > MAXEXTLEN) {
-                               code = -EINVAL;
+                       if (extsize_fsb > MAXEXTLEN)
                                goto error_return;
-                       }
 
                        if (XFS_IS_REALTIME_INODE(ip) ||
                            ((mask & FSX_XFLAGS) &&
@@ -1149,16 +1164,12 @@ xfs_ioctl_setattr(
                                       mp->m_sb.sb_blocklog;
                        } else {
                                size = mp->m_sb.sb_blocksize;
-                               if (extsize_fsb > mp->m_sb.sb_agblocks / 2) {
-                                       code = -EINVAL;
+                               if (extsize_fsb > mp->m_sb.sb_agblocks / 2)
                                        goto error_return;
-                               }
                        }
 
-                       if (fa->fsx_extsize % size) {
-                               code = -EINVAL;
+                       if (fa->fsx_extsize % size)
                                goto error_return;
-                       }
                }
        }
 
-- 
2.1.3

<Prev in Thread] Current Thread [Next in Thread>