Commit 9b94fcc3 authored by Iustin Pop's avatar Iustin Pop Committed by Dave Chinner

xfs: fix behaviour of XFS_IOC_FSSETXATTR on directories

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.

[dchinner: ported changes onto cleanup series. Makes changes clear
	   and obvious.]
[dchinner: added comments documenting validity checking rules.]
Signed-off-by: default avatarIustin Pop <iustin@k1024.org>
Signed-off-by: default avatarDave Chinner <dchinner@redhat.com>
Reviewed-by: default avatarBrian Foster <bfoster@redhat.com>
Signed-off-by: default avatarDave Chinner <david@fromorbit.com>
parent 23bd0735
...@@ -1098,6 +1098,20 @@ xfs_ioctl_setattr_get_trans( ...@@ -1098,6 +1098,20 @@ xfs_ioctl_setattr_get_trans(
return ERR_PTR(error); return ERR_PTR(error);
} }
/*
* extent size hint validation is somewhat cumbersome. Rules are:
*
* 1. extent size hint is only valid for directories and regular files
* 2. XFS_XFLAG_EXTSIZE is only valid for regular files
* 3. XFS_XFLAG_EXTSZINHERIT is only valid for directories.
* 4. can only be changed on regular files if no extents are allocated
* 5. can be changed on directories at any time
* 6. extsize hint of 0 turns off hints, clears inode flags.
* 7. Extent size must be a multiple of the appropriate block size.
* 8. for non-realtime files, the extent size hint must be limited
* to half the AG size to avoid alignment extending the extent beyond the
* limits of the AG.
*/
int int
xfs_ioctl_setattr_check_extsize( xfs_ioctl_setattr_check_extsize(
struct xfs_inode *ip, struct xfs_inode *ip,
...@@ -1105,20 +1119,17 @@ xfs_ioctl_setattr_check_extsize( ...@@ -1105,20 +1119,17 @@ xfs_ioctl_setattr_check_extsize(
{ {
struct xfs_mount *mp = ip->i_mount; struct xfs_mount *mp = ip->i_mount;
/* Can't change extent size if any extents are allocated. */ if ((fa->fsx_xflags & XFS_XFLAG_EXTSIZE) && !S_ISREG(ip->i_d.di_mode))
if (ip->i_d.di_nextents && return -EINVAL;
if ((fa->fsx_xflags & XFS_XFLAG_EXTSZINHERIT) &&
!S_ISDIR(ip->i_d.di_mode))
return -EINVAL;
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)) ((ip->i_d.di_extsize << mp->m_sb.sb_blocklog) != fa->fsx_extsize))
return -EINVAL; return -EINVAL;
/*
* 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.
*/
if (fa->fsx_extsize != 0) { if (fa->fsx_extsize != 0) {
xfs_extlen_t size; xfs_extlen_t size;
xfs_fsblock_t extsize_fsb; xfs_fsblock_t extsize_fsb;
...@@ -1138,7 +1149,9 @@ xfs_ioctl_setattr_check_extsize( ...@@ -1138,7 +1149,9 @@ xfs_ioctl_setattr_check_extsize(
if (fa->fsx_extsize % size) if (fa->fsx_extsize % size)
return -EINVAL; return -EINVAL;
} } else
fa->fsx_xflags &= ~(XFS_XFLAG_EXTSIZE | XFS_XFLAG_EXTSZINHERIT);
return 0; return 0;
} }
...@@ -1169,8 +1182,6 @@ xfs_ioctl_setattr_check_projid( ...@@ -1169,8 +1182,6 @@ xfs_ioctl_setattr_check_projid(
return 0; return 0;
} }
STATIC int STATIC int
xfs_ioctl_setattr( xfs_ioctl_setattr(
xfs_inode_t *ip, xfs_inode_t *ip,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment