diff options
author | Iustin Pop <iustin@k1024.org> | 2015-02-01 18:26:26 -0500 |
---|---|---|
committer | Dave Chinner <david@fromorbit.com> | 2015-02-01 18:26:26 -0500 |
commit | 9b94fcc39822b450af823b3d8cbef6b53ce87ed9 (patch) | |
tree | 1b663a4ca38574d135b903c2242728234db23a49 /fs/xfs | |
parent | 23bd0735cfdf5322170a9ef48c7d47c2e6567ba8 (diff) |
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>
Diffstat (limited to 'fs/xfs')
-rw-r--r-- | fs/xfs/xfs_ioctl.c | 39 |
1 files changed, 25 insertions, 14 deletions
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c index 1f186d2eec06..0f6b6abb7c7a 100644 --- a/fs/xfs/xfs_ioctl.c +++ b/fs/xfs/xfs_ioctl.c | |||
@@ -1098,6 +1098,20 @@ out_cancel: | |||
1098 | return ERR_PTR(error); | 1098 | return ERR_PTR(error); |
1099 | } | 1099 | } |
1100 | 1100 | ||
1101 | /* | ||
1102 | * extent size hint validation is somewhat cumbersome. Rules are: | ||
1103 | * | ||
1104 | * 1. extent size hint is only valid for directories and regular files | ||
1105 | * 2. XFS_XFLAG_EXTSIZE is only valid for regular files | ||
1106 | * 3. XFS_XFLAG_EXTSZINHERIT is only valid for directories. | ||
1107 | * 4. can only be changed on regular files if no extents are allocated | ||
1108 | * 5. can be changed on directories at any time | ||
1109 | * 6. extsize hint of 0 turns off hints, clears inode flags. | ||
1110 | * 7. Extent size must be a multiple of the appropriate block size. | ||
1111 | * 8. for non-realtime files, the extent size hint must be limited | ||
1112 | * to half the AG size to avoid alignment extending the extent beyond the | ||
1113 | * limits of the AG. | ||
1114 | */ | ||
1101 | int | 1115 | int |
1102 | xfs_ioctl_setattr_check_extsize( | 1116 | xfs_ioctl_setattr_check_extsize( |
1103 | struct xfs_inode *ip, | 1117 | struct xfs_inode *ip, |
@@ -1105,20 +1119,17 @@ xfs_ioctl_setattr_check_extsize( | |||
1105 | { | 1119 | { |
1106 | struct xfs_mount *mp = ip->i_mount; | 1120 | struct xfs_mount *mp = ip->i_mount; |
1107 | 1121 | ||
1108 | /* Can't change extent size if any extents are allocated. */ | 1122 | if ((fa->fsx_xflags & XFS_XFLAG_EXTSIZE) && !S_ISREG(ip->i_d.di_mode)) |
1109 | if (ip->i_d.di_nextents && | 1123 | return -EINVAL; |
1124 | |||
1125 | if ((fa->fsx_xflags & XFS_XFLAG_EXTSZINHERIT) && | ||
1126 | !S_ISDIR(ip->i_d.di_mode)) | ||
1127 | return -EINVAL; | ||
1128 | |||
1129 | if (S_ISREG(ip->i_d.di_mode) && ip->i_d.di_nextents && | ||
1110 | ((ip->i_d.di_extsize << mp->m_sb.sb_blocklog) != fa->fsx_extsize)) | 1130 | ((ip->i_d.di_extsize << mp->m_sb.sb_blocklog) != fa->fsx_extsize)) |
1111 | return -EINVAL; | 1131 | return -EINVAL; |
1112 | 1132 | ||
1113 | /* | ||
1114 | * Extent size must be a multiple of the appropriate block size, if set | ||
1115 | * at all. It must also be smaller than the maximum extent size | ||
1116 | * supported by the filesystem. | ||
1117 | * | ||
1118 | * Also, for non-realtime files, limit the extent size hint to half the | ||
1119 | * size of the AGs in the filesystem so alignment doesn't result in | ||
1120 | * extents larger than an AG. | ||
1121 | */ | ||
1122 | if (fa->fsx_extsize != 0) { | 1133 | if (fa->fsx_extsize != 0) { |
1123 | xfs_extlen_t size; | 1134 | xfs_extlen_t size; |
1124 | xfs_fsblock_t extsize_fsb; | 1135 | xfs_fsblock_t extsize_fsb; |
@@ -1138,7 +1149,9 @@ xfs_ioctl_setattr_check_extsize( | |||
1138 | 1149 | ||
1139 | if (fa->fsx_extsize % size) | 1150 | if (fa->fsx_extsize % size) |
1140 | return -EINVAL; | 1151 | return -EINVAL; |
1141 | } | 1152 | } else |
1153 | fa->fsx_xflags &= ~(XFS_XFLAG_EXTSIZE | XFS_XFLAG_EXTSZINHERIT); | ||
1154 | |||
1142 | return 0; | 1155 | return 0; |
1143 | } | 1156 | } |
1144 | 1157 | ||
@@ -1169,8 +1182,6 @@ xfs_ioctl_setattr_check_projid( | |||
1169 | return 0; | 1182 | return 0; |
1170 | } | 1183 | } |
1171 | 1184 | ||
1172 | |||
1173 | |||
1174 | STATIC int | 1185 | STATIC int |
1175 | xfs_ioctl_setattr( | 1186 | xfs_ioctl_setattr( |
1176 | xfs_inode_t *ip, | 1187 | xfs_inode_t *ip, |