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: Iustin Pop <iustin@k1024.org> Signed-off-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Brian Foster <bfoster@redhat.com> Signed-off-by: Dave Chinner <david@fromorbit.com>
This commit is contained in:
parent
23bd0735cf
commit
9b94fcc398
|
@ -1098,6 +1098,20 @@ xfs_ioctl_setattr_get_trans(
|
|||
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
|
||||
xfs_ioctl_setattr_check_extsize(
|
||||
struct xfs_inode *ip,
|
||||
|
@ -1105,20 +1119,17 @@ xfs_ioctl_setattr_check_extsize(
|
|||
{
|
||||
struct xfs_mount *mp = ip->i_mount;
|
||||
|
||||
/* Can't change extent size if any extents are allocated. */
|
||||
if (ip->i_d.di_nextents &&
|
||||
if ((fa->fsx_xflags & XFS_XFLAG_EXTSIZE) && !S_ISREG(ip->i_d.di_mode))
|
||||
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))
|
||||
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) {
|
||||
xfs_extlen_t size;
|
||||
xfs_fsblock_t extsize_fsb;
|
||||
|
@ -1138,7 +1149,9 @@ xfs_ioctl_setattr_check_extsize(
|
|||
|
||||
if (fa->fsx_extsize % size)
|
||||
return -EINVAL;
|
||||
}
|
||||
} else
|
||||
fa->fsx_xflags &= ~(XFS_XFLAG_EXTSIZE | XFS_XFLAG_EXTSZINHERIT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1169,8 +1182,6 @@ xfs_ioctl_setattr_check_projid(
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC int
|
||||
xfs_ioctl_setattr(
|
||||
xfs_inode_t *ip,
|
||||
|
|
Loading…
Reference in New Issue