diff options
Diffstat (limited to 'fs/xfs/xfs_ioctl.c')
-rw-r--r-- | fs/xfs/xfs_ioctl.c | 121 |
1 files changed, 106 insertions, 15 deletions
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c index 478d04e07f95..bcb6c19ce3ea 100644 --- a/fs/xfs/xfs_ioctl.c +++ b/fs/xfs/xfs_ioctl.c | |||
@@ -114,7 +114,7 @@ xfs_find_handle( | |||
114 | handle.ha_fid.fid_len = sizeof(xfs_fid_t) - | 114 | handle.ha_fid.fid_len = sizeof(xfs_fid_t) - |
115 | sizeof(handle.ha_fid.fid_len); | 115 | sizeof(handle.ha_fid.fid_len); |
116 | handle.ha_fid.fid_pad = 0; | 116 | handle.ha_fid.fid_pad = 0; |
117 | handle.ha_fid.fid_gen = ip->i_d.di_gen; | 117 | handle.ha_fid.fid_gen = inode->i_generation; |
118 | handle.ha_fid.fid_ino = ip->i_ino; | 118 | handle.ha_fid.fid_ino = ip->i_ino; |
119 | 119 | ||
120 | hsize = XFS_HSIZE(handle); | 120 | hsize = XFS_HSIZE(handle); |
@@ -963,7 +963,7 @@ xfs_set_diflags( | |||
963 | di_flags |= XFS_DIFLAG_NODEFRAG; | 963 | di_flags |= XFS_DIFLAG_NODEFRAG; |
964 | if (xflags & FS_XFLAG_FILESTREAM) | 964 | if (xflags & FS_XFLAG_FILESTREAM) |
965 | di_flags |= XFS_DIFLAG_FILESTREAM; | 965 | di_flags |= XFS_DIFLAG_FILESTREAM; |
966 | if (S_ISDIR(ip->i_d.di_mode)) { | 966 | if (S_ISDIR(VFS_I(ip)->i_mode)) { |
967 | if (xflags & FS_XFLAG_RTINHERIT) | 967 | if (xflags & FS_XFLAG_RTINHERIT) |
968 | di_flags |= XFS_DIFLAG_RTINHERIT; | 968 | di_flags |= XFS_DIFLAG_RTINHERIT; |
969 | if (xflags & FS_XFLAG_NOSYMLINKS) | 969 | if (xflags & FS_XFLAG_NOSYMLINKS) |
@@ -972,7 +972,7 @@ xfs_set_diflags( | |||
972 | di_flags |= XFS_DIFLAG_EXTSZINHERIT; | 972 | di_flags |= XFS_DIFLAG_EXTSZINHERIT; |
973 | if (xflags & FS_XFLAG_PROJINHERIT) | 973 | if (xflags & FS_XFLAG_PROJINHERIT) |
974 | di_flags |= XFS_DIFLAG_PROJINHERIT; | 974 | di_flags |= XFS_DIFLAG_PROJINHERIT; |
975 | } else if (S_ISREG(ip->i_d.di_mode)) { | 975 | } else if (S_ISREG(VFS_I(ip)->i_mode)) { |
976 | if (xflags & FS_XFLAG_REALTIME) | 976 | if (xflags & FS_XFLAG_REALTIME) |
977 | di_flags |= XFS_DIFLAG_REALTIME; | 977 | di_flags |= XFS_DIFLAG_REALTIME; |
978 | if (xflags & FS_XFLAG_EXTSIZE) | 978 | if (xflags & FS_XFLAG_EXTSIZE) |
@@ -1060,23 +1060,86 @@ xfs_ioctl_setattr_xflags( | |||
1060 | } | 1060 | } |
1061 | 1061 | ||
1062 | /* | 1062 | /* |
1063 | * If we are changing DAX flags, we have to ensure the file is clean and any | ||
1064 | * cached objects in the address space are invalidated and removed. This | ||
1065 | * requires us to lock out other IO and page faults similar to a truncate | ||
1066 | * operation. The locks need to be held until the transaction has been committed | ||
1067 | * so that the cache invalidation is atomic with respect to the DAX flag | ||
1068 | * manipulation. | ||
1069 | */ | ||
1070 | static int | ||
1071 | xfs_ioctl_setattr_dax_invalidate( | ||
1072 | struct xfs_inode *ip, | ||
1073 | struct fsxattr *fa, | ||
1074 | int *join_flags) | ||
1075 | { | ||
1076 | struct inode *inode = VFS_I(ip); | ||
1077 | int error; | ||
1078 | |||
1079 | *join_flags = 0; | ||
1080 | |||
1081 | /* | ||
1082 | * It is only valid to set the DAX flag on regular files and | ||
1083 | * directories on filesystems where the block size is equal to the page | ||
1084 | * size. On directories it serves as an inherit hint. | ||
1085 | */ | ||
1086 | if (fa->fsx_xflags & FS_XFLAG_DAX) { | ||
1087 | if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) | ||
1088 | return -EINVAL; | ||
1089 | if (ip->i_mount->m_sb.sb_blocksize != PAGE_SIZE) | ||
1090 | return -EINVAL; | ||
1091 | } | ||
1092 | |||
1093 | /* If the DAX state is not changing, we have nothing to do here. */ | ||
1094 | if ((fa->fsx_xflags & FS_XFLAG_DAX) && IS_DAX(inode)) | ||
1095 | return 0; | ||
1096 | if (!(fa->fsx_xflags & FS_XFLAG_DAX) && !IS_DAX(inode)) | ||
1097 | return 0; | ||
1098 | |||
1099 | /* lock, flush and invalidate mapping in preparation for flag change */ | ||
1100 | xfs_ilock(ip, XFS_MMAPLOCK_EXCL | XFS_IOLOCK_EXCL); | ||
1101 | error = filemap_write_and_wait(inode->i_mapping); | ||
1102 | if (error) | ||
1103 | goto out_unlock; | ||
1104 | error = invalidate_inode_pages2(inode->i_mapping); | ||
1105 | if (error) | ||
1106 | goto out_unlock; | ||
1107 | |||
1108 | *join_flags = XFS_MMAPLOCK_EXCL | XFS_IOLOCK_EXCL; | ||
1109 | return 0; | ||
1110 | |||
1111 | out_unlock: | ||
1112 | xfs_iunlock(ip, XFS_MMAPLOCK_EXCL | XFS_IOLOCK_EXCL); | ||
1113 | return error; | ||
1114 | |||
1115 | } | ||
1116 | |||
1117 | /* | ||
1063 | * Set up the transaction structure for the setattr operation, checking that we | 1118 | * Set up the transaction structure for the setattr operation, checking that we |
1064 | * have permission to do so. On success, return a clean transaction and the | 1119 | * have permission to do so. On success, return a clean transaction and the |
1065 | * inode locked exclusively ready for further operation specific checks. On | 1120 | * inode locked exclusively ready for further operation specific checks. On |
1066 | * failure, return an error without modifying or locking the inode. | 1121 | * failure, return an error without modifying or locking the inode. |
1122 | * | ||
1123 | * The inode might already be IO locked on call. If this is the case, it is | ||
1124 | * indicated in @join_flags and we take full responsibility for ensuring they | ||
1125 | * are unlocked from now on. Hence if we have an error here, we still have to | ||
1126 | * unlock them. Otherwise, once they are joined to the transaction, they will | ||
1127 | * be unlocked on commit/cancel. | ||
1067 | */ | 1128 | */ |
1068 | static struct xfs_trans * | 1129 | static struct xfs_trans * |
1069 | xfs_ioctl_setattr_get_trans( | 1130 | xfs_ioctl_setattr_get_trans( |
1070 | struct xfs_inode *ip) | 1131 | struct xfs_inode *ip, |
1132 | int join_flags) | ||
1071 | { | 1133 | { |
1072 | struct xfs_mount *mp = ip->i_mount; | 1134 | struct xfs_mount *mp = ip->i_mount; |
1073 | struct xfs_trans *tp; | 1135 | struct xfs_trans *tp; |
1074 | int error; | 1136 | int error = -EROFS; |
1075 | 1137 | ||
1076 | if (mp->m_flags & XFS_MOUNT_RDONLY) | 1138 | if (mp->m_flags & XFS_MOUNT_RDONLY) |
1077 | return ERR_PTR(-EROFS); | 1139 | goto out_unlock; |
1140 | error = -EIO; | ||
1078 | if (XFS_FORCED_SHUTDOWN(mp)) | 1141 | if (XFS_FORCED_SHUTDOWN(mp)) |
1079 | return ERR_PTR(-EIO); | 1142 | goto out_unlock; |
1080 | 1143 | ||
1081 | tp = xfs_trans_alloc(mp, XFS_TRANS_SETATTR_NOT_SIZE); | 1144 | tp = xfs_trans_alloc(mp, XFS_TRANS_SETATTR_NOT_SIZE); |
1082 | error = xfs_trans_reserve(tp, &M_RES(mp)->tr_ichange, 0, 0); | 1145 | error = xfs_trans_reserve(tp, &M_RES(mp)->tr_ichange, 0, 0); |
@@ -1084,7 +1147,8 @@ xfs_ioctl_setattr_get_trans( | |||
1084 | goto out_cancel; | 1147 | goto out_cancel; |
1085 | 1148 | ||
1086 | xfs_ilock(ip, XFS_ILOCK_EXCL); | 1149 | xfs_ilock(ip, XFS_ILOCK_EXCL); |
1087 | xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); | 1150 | xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL | join_flags); |
1151 | join_flags = 0; | ||
1088 | 1152 | ||
1089 | /* | 1153 | /* |
1090 | * CAP_FOWNER overrides the following restrictions: | 1154 | * CAP_FOWNER overrides the following restrictions: |
@@ -1104,6 +1168,9 @@ xfs_ioctl_setattr_get_trans( | |||
1104 | 1168 | ||
1105 | out_cancel: | 1169 | out_cancel: |
1106 | xfs_trans_cancel(tp); | 1170 | xfs_trans_cancel(tp); |
1171 | out_unlock: | ||
1172 | if (join_flags) | ||
1173 | xfs_iunlock(ip, join_flags); | ||
1107 | return ERR_PTR(error); | 1174 | return ERR_PTR(error); |
1108 | } | 1175 | } |
1109 | 1176 | ||
@@ -1128,14 +1195,14 @@ xfs_ioctl_setattr_check_extsize( | |||
1128 | { | 1195 | { |
1129 | struct xfs_mount *mp = ip->i_mount; | 1196 | struct xfs_mount *mp = ip->i_mount; |
1130 | 1197 | ||
1131 | if ((fa->fsx_xflags & FS_XFLAG_EXTSIZE) && !S_ISREG(ip->i_d.di_mode)) | 1198 | if ((fa->fsx_xflags & FS_XFLAG_EXTSIZE) && !S_ISREG(VFS_I(ip)->i_mode)) |
1132 | return -EINVAL; | 1199 | return -EINVAL; |
1133 | 1200 | ||
1134 | if ((fa->fsx_xflags & FS_XFLAG_EXTSZINHERIT) && | 1201 | if ((fa->fsx_xflags & FS_XFLAG_EXTSZINHERIT) && |
1135 | !S_ISDIR(ip->i_d.di_mode)) | 1202 | !S_ISDIR(VFS_I(ip)->i_mode)) |
1136 | return -EINVAL; | 1203 | return -EINVAL; |
1137 | 1204 | ||
1138 | if (S_ISREG(ip->i_d.di_mode) && ip->i_d.di_nextents && | 1205 | if (S_ISREG(VFS_I(ip)->i_mode) && ip->i_d.di_nextents && |
1139 | ((ip->i_d.di_extsize << mp->m_sb.sb_blocklog) != fa->fsx_extsize)) | 1206 | ((ip->i_d.di_extsize << mp->m_sb.sb_blocklog) != fa->fsx_extsize)) |
1140 | return -EINVAL; | 1207 | return -EINVAL; |
1141 | 1208 | ||
@@ -1202,6 +1269,7 @@ xfs_ioctl_setattr( | |||
1202 | struct xfs_dquot *pdqp = NULL; | 1269 | struct xfs_dquot *pdqp = NULL; |
1203 | struct xfs_dquot *olddquot = NULL; | 1270 | struct xfs_dquot *olddquot = NULL; |
1204 | int code; | 1271 | int code; |
1272 | int join_flags = 0; | ||
1205 | 1273 | ||
1206 | trace_xfs_ioctl_setattr(ip); | 1274 | trace_xfs_ioctl_setattr(ip); |
1207 | 1275 | ||
@@ -1225,7 +1293,18 @@ xfs_ioctl_setattr( | |||
1225 | return code; | 1293 | return code; |
1226 | } | 1294 | } |
1227 | 1295 | ||
1228 | tp = xfs_ioctl_setattr_get_trans(ip); | 1296 | /* |
1297 | * Changing DAX config may require inode locking for mapping | ||
1298 | * invalidation. These need to be held all the way to transaction commit | ||
1299 | * or cancel time, so need to be passed through to | ||
1300 | * xfs_ioctl_setattr_get_trans() so it can apply them to the join call | ||
1301 | * appropriately. | ||
1302 | */ | ||
1303 | code = xfs_ioctl_setattr_dax_invalidate(ip, fa, &join_flags); | ||
1304 | if (code) | ||
1305 | goto error_free_dquots; | ||
1306 | |||
1307 | tp = xfs_ioctl_setattr_get_trans(ip, join_flags); | ||
1229 | if (IS_ERR(tp)) { | 1308 | if (IS_ERR(tp)) { |
1230 | code = PTR_ERR(tp); | 1309 | code = PTR_ERR(tp); |
1231 | goto error_free_dquots; | 1310 | goto error_free_dquots; |
@@ -1256,9 +1335,9 @@ xfs_ioctl_setattr( | |||
1256 | * successful return from chown() | 1335 | * successful return from chown() |
1257 | */ | 1336 | */ |
1258 | 1337 | ||
1259 | if ((ip->i_d.di_mode & (S_ISUID|S_ISGID)) && | 1338 | if ((VFS_I(ip)->i_mode & (S_ISUID|S_ISGID)) && |
1260 | !capable_wrt_inode_uidgid(VFS_I(ip), CAP_FSETID)) | 1339 | !capable_wrt_inode_uidgid(VFS_I(ip), CAP_FSETID)) |
1261 | ip->i_d.di_mode &= ~(S_ISUID|S_ISGID); | 1340 | VFS_I(ip)->i_mode &= ~(S_ISUID|S_ISGID); |
1262 | 1341 | ||
1263 | /* Change the ownerships and register project quota modifications */ | 1342 | /* Change the ownerships and register project quota modifications */ |
1264 | if (xfs_get_projid(ip) != fa->fsx_projid) { | 1343 | if (xfs_get_projid(ip) != fa->fsx_projid) { |
@@ -1341,6 +1420,7 @@ xfs_ioc_setxflags( | |||
1341 | struct xfs_trans *tp; | 1420 | struct xfs_trans *tp; |
1342 | struct fsxattr fa; | 1421 | struct fsxattr fa; |
1343 | unsigned int flags; | 1422 | unsigned int flags; |
1423 | int join_flags = 0; | ||
1344 | int error; | 1424 | int error; |
1345 | 1425 | ||
1346 | if (copy_from_user(&flags, arg, sizeof(flags))) | 1426 | if (copy_from_user(&flags, arg, sizeof(flags))) |
@@ -1357,7 +1437,18 @@ xfs_ioc_setxflags( | |||
1357 | if (error) | 1437 | if (error) |
1358 | return error; | 1438 | return error; |
1359 | 1439 | ||
1360 | tp = xfs_ioctl_setattr_get_trans(ip); | 1440 | /* |
1441 | * Changing DAX config may require inode locking for mapping | ||
1442 | * invalidation. These need to be held all the way to transaction commit | ||
1443 | * or cancel time, so need to be passed through to | ||
1444 | * xfs_ioctl_setattr_get_trans() so it can apply them to the join call | ||
1445 | * appropriately. | ||
1446 | */ | ||
1447 | error = xfs_ioctl_setattr_dax_invalidate(ip, &fa, &join_flags); | ||
1448 | if (error) | ||
1449 | goto out_drop_write; | ||
1450 | |||
1451 | tp = xfs_ioctl_setattr_get_trans(ip, join_flags); | ||
1361 | if (IS_ERR(tp)) { | 1452 | if (IS_ERR(tp)) { |
1362 | error = PTR_ERR(tp); | 1453 | error = PTR_ERR(tp); |
1363 | goto out_drop_write; | 1454 | goto out_drop_write; |