diff options
| -rw-r--r-- | fs/xfs/xfs_ioctl.c | 90 |
1 files changed, 51 insertions, 39 deletions
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c index b65817cbc318..9f808539fc61 100644 --- a/fs/xfs/xfs_ioctl.c +++ b/fs/xfs/xfs_ioctl.c | |||
| @@ -1098,6 +1098,51 @@ out_cancel: | |||
| 1098 | return ERR_PTR(error); | 1098 | return ERR_PTR(error); |
| 1099 | } | 1099 | } |
| 1100 | 1100 | ||
| 1101 | int | ||
| 1102 | xfs_ioctl_setattr_check_extsize( | ||
| 1103 | struct xfs_inode *ip, | ||
| 1104 | struct fsxattr *fa) | ||
| 1105 | { | ||
| 1106 | struct xfs_mount *mp = ip->i_mount; | ||
| 1107 | |||
| 1108 | /* Can't change extent size if any extents are allocated. */ | ||
| 1109 | if (ip->i_d.di_nextents && | ||
| 1110 | ((ip->i_d.di_extsize << mp->m_sb.sb_blocklog) != fa->fsx_extsize)) | ||
| 1111 | return -EINVAL; | ||
| 1112 | |||
| 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) { | ||
| 1123 | xfs_extlen_t size; | ||
| 1124 | xfs_fsblock_t extsize_fsb; | ||
| 1125 | |||
| 1126 | extsize_fsb = XFS_B_TO_FSB(mp, fa->fsx_extsize); | ||
| 1127 | if (extsize_fsb > MAXEXTLEN) | ||
| 1128 | return -EINVAL; | ||
| 1129 | |||
| 1130 | if (XFS_IS_REALTIME_INODE(ip) || | ||
| 1131 | (fa->fsx_xflags & XFS_XFLAG_REALTIME)) { | ||
| 1132 | size = mp->m_sb.sb_rextsize << mp->m_sb.sb_blocklog; | ||
| 1133 | } else { | ||
| 1134 | size = mp->m_sb.sb_blocksize; | ||
| 1135 | if (extsize_fsb > mp->m_sb.sb_agblocks / 2) | ||
| 1136 | return -EINVAL; | ||
| 1137 | } | ||
| 1138 | |||
| 1139 | if (fa->fsx_extsize % size) | ||
| 1140 | return -EINVAL; | ||
| 1141 | } | ||
| 1142 | return 0; | ||
| 1143 | } | ||
| 1144 | |||
| 1145 | |||
| 1101 | STATIC int | 1146 | STATIC int |
| 1102 | xfs_ioctl_setattr( | 1147 | xfs_ioctl_setattr( |
| 1103 | xfs_inode_t *ip, | 1148 | xfs_inode_t *ip, |
| @@ -1160,49 +1205,16 @@ xfs_ioctl_setattr( | |||
| 1160 | code = xfs_qm_vop_chown_reserve(tp, ip, udqp, NULL, pdqp, | 1205 | code = xfs_qm_vop_chown_reserve(tp, ip, udqp, NULL, pdqp, |
| 1161 | capable(CAP_FOWNER) ? XFS_QMOPT_FORCE_RES : 0); | 1206 | capable(CAP_FOWNER) ? XFS_QMOPT_FORCE_RES : 0); |
| 1162 | if (code) /* out of quota */ | 1207 | if (code) /* out of quota */ |
| 1163 | goto error_return; | 1208 | goto error_trans_cancel; |
| 1164 | } | ||
| 1165 | |||
| 1166 | /* Can't change extent size if any extents are allocated. */ | ||
| 1167 | code = -EINVAL; | ||
| 1168 | if (ip->i_d.di_nextents && | ||
| 1169 | ((ip->i_d.di_extsize << mp->m_sb.sb_blocklog) != fa->fsx_extsize)) | ||
| 1170 | goto error_return; | ||
| 1171 | |||
| 1172 | /* | ||
| 1173 | * Extent size must be a multiple of the appropriate block size, if set | ||
| 1174 | * at all. It must also be smaller than the maximum extent size | ||
| 1175 | * supported by the filesystem. | ||
| 1176 | * | ||
| 1177 | * Also, for non-realtime files, limit the extent size hint to half the | ||
| 1178 | * size of the AGs in the filesystem so alignment doesn't result in | ||
| 1179 | * extents larger than an AG. | ||
| 1180 | */ | ||
| 1181 | if (fa->fsx_extsize != 0) { | ||
| 1182 | xfs_extlen_t size; | ||
| 1183 | xfs_fsblock_t extsize_fsb; | ||
| 1184 | |||
| 1185 | extsize_fsb = XFS_B_TO_FSB(mp, fa->fsx_extsize); | ||
| 1186 | if (extsize_fsb > MAXEXTLEN) | ||
| 1187 | goto error_return; | ||
| 1188 | |||
| 1189 | if (XFS_IS_REALTIME_INODE(ip) || | ||
| 1190 | (fa->fsx_xflags & XFS_XFLAG_REALTIME)) { | ||
| 1191 | size = mp->m_sb.sb_rextsize << mp->m_sb.sb_blocklog; | ||
| 1192 | } else { | ||
| 1193 | size = mp->m_sb.sb_blocksize; | ||
| 1194 | if (extsize_fsb > mp->m_sb.sb_agblocks / 2) | ||
| 1195 | goto error_return; | ||
| 1196 | } | ||
| 1197 | |||
| 1198 | if (fa->fsx_extsize % size) | ||
| 1199 | goto error_return; | ||
| 1200 | } | 1209 | } |
| 1201 | 1210 | ||
| 1211 | code = xfs_ioctl_setattr_check_extsize(ip, fa); | ||
| 1212 | if (code) | ||
| 1213 | goto error_trans_cancel; | ||
| 1202 | 1214 | ||
| 1203 | code = xfs_ioctl_setattr_xflags(tp, ip, fa); | 1215 | code = xfs_ioctl_setattr_xflags(tp, ip, fa); |
| 1204 | if (code) | 1216 | if (code) |
| 1205 | goto error_return; | 1217 | goto error_trans_cancel; |
| 1206 | 1218 | ||
| 1207 | /* | 1219 | /* |
| 1208 | * Change file ownership. Must be the owner or privileged. CAP_FSETID | 1220 | * Change file ownership. Must be the owner or privileged. CAP_FSETID |
| @@ -1247,7 +1259,7 @@ xfs_ioctl_setattr( | |||
| 1247 | 1259 | ||
| 1248 | return code; | 1260 | return code; |
| 1249 | 1261 | ||
| 1250 | error_return: | 1262 | error_trans_cancel: |
| 1251 | xfs_trans_cancel(tp, 0); | 1263 | xfs_trans_cancel(tp, 0); |
| 1252 | error_free_dquots: | 1264 | error_free_dquots: |
| 1253 | xfs_qm_dqrele(udqp); | 1265 | xfs_qm_dqrele(udqp); |
