aboutsummaryrefslogtreecommitdiffstats
path: root/fs/xfs
diff options
context:
space:
mode:
authorDave Chinner <david@fromorbit.com>2015-02-01 18:57:30 -0500
committerDave Chinner <david@fromorbit.com>2015-02-01 18:57:30 -0500
commit179073620d8090e9c52bf203e2033fc9cfe088b3 (patch)
treeeda577c597f9fc56f6df337b46058ce3cd4f5aa4 /fs/xfs
parent3fd1b0d158b6b98d9561e2f7f9c6970d95cf71d6 (diff)
parent9b94fcc39822b450af823b3d8cbef6b53ce87ed9 (diff)
Merge branch 'xfs-ioctl-setattr-cleanup' into for-next
Diffstat (limited to 'fs/xfs')
-rw-r--r--fs/xfs/xfs_ioctl.c451
1 files changed, 231 insertions, 220 deletions
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
index d58bcd28f5f8..b88ab927debe 100644
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -987,20 +987,182 @@ xfs_diflags_to_linux(
987 inode->i_flags &= ~S_NOATIME; 987 inode->i_flags &= ~S_NOATIME;
988} 988}
989 989
990#define FSX_PROJID 1 990static int
991#define FSX_EXTSIZE 2 991xfs_ioctl_setattr_xflags(
992#define FSX_XFLAGS 4 992 struct xfs_trans *tp,
993#define FSX_NONBLOCK 8 993 struct xfs_inode *ip,
994 struct fsxattr *fa)
995{
996 struct xfs_mount *mp = ip->i_mount;
997
998 /* Can't change realtime flag if any extents are allocated. */
999 if ((ip->i_d.di_nextents || ip->i_delayed_blks) &&
1000 XFS_IS_REALTIME_INODE(ip) != (fa->fsx_xflags & XFS_XFLAG_REALTIME))
1001 return -EINVAL;
1002
1003 /* If realtime flag is set then must have realtime device */
1004 if (fa->fsx_xflags & XFS_XFLAG_REALTIME) {
1005 if (mp->m_sb.sb_rblocks == 0 || mp->m_sb.sb_rextsize == 0 ||
1006 (ip->i_d.di_extsize % mp->m_sb.sb_rextsize))
1007 return -EINVAL;
1008 }
1009
1010 /*
1011 * Can't modify an immutable/append-only file unless
1012 * we have appropriate permission.
1013 */
1014 if (((ip->i_d.di_flags & (XFS_DIFLAG_IMMUTABLE | XFS_DIFLAG_APPEND)) ||
1015 (fa->fsx_xflags & (XFS_XFLAG_IMMUTABLE | XFS_XFLAG_APPEND))) &&
1016 !capable(CAP_LINUX_IMMUTABLE))
1017 return -EPERM;
1018
1019 xfs_set_diflags(ip, fa->fsx_xflags);
1020 xfs_diflags_to_linux(ip);
1021 xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG);
1022 xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
1023 XFS_STATS_INC(xs_ig_attrchg);
1024 return 0;
1025}
1026
1027/*
1028 * Set up the transaction structure for the setattr operation, checking that we
1029 * have permission to do so. On success, return a clean transaction and the
1030 * inode locked exclusively ready for further operation specific checks. On
1031 * failure, return an error without modifying or locking the inode.
1032 */
1033static struct xfs_trans *
1034xfs_ioctl_setattr_get_trans(
1035 struct xfs_inode *ip)
1036{
1037 struct xfs_mount *mp = ip->i_mount;
1038 struct xfs_trans *tp;
1039 int error;
1040
1041 if (mp->m_flags & XFS_MOUNT_RDONLY)
1042 return ERR_PTR(-EROFS);
1043 if (XFS_FORCED_SHUTDOWN(mp))
1044 return ERR_PTR(-EIO);
1045
1046 tp = xfs_trans_alloc(mp, XFS_TRANS_SETATTR_NOT_SIZE);
1047 error = xfs_trans_reserve(tp, &M_RES(mp)->tr_ichange, 0, 0);
1048 if (error)
1049 goto out_cancel;
1050
1051 xfs_ilock(ip, XFS_ILOCK_EXCL);
1052 xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
1053
1054 /*
1055 * CAP_FOWNER overrides the following restrictions:
1056 *
1057 * The user ID of the calling process must be equal to the file owner
1058 * ID, except in cases where the CAP_FSETID capability is applicable.
1059 */
1060 if (!inode_owner_or_capable(VFS_I(ip))) {
1061 error = -EPERM;
1062 goto out_cancel;
1063 }
1064
1065 if (mp->m_flags & XFS_MOUNT_WSYNC)
1066 xfs_trans_set_sync(tp);
1067
1068 return tp;
1069
1070out_cancel:
1071 xfs_trans_cancel(tp, 0);
1072 return ERR_PTR(error);
1073}
1074
1075/*
1076 * extent size hint validation is somewhat cumbersome. Rules are:
1077 *
1078 * 1. extent size hint is only valid for directories and regular files
1079 * 2. XFS_XFLAG_EXTSIZE is only valid for regular files
1080 * 3. XFS_XFLAG_EXTSZINHERIT is only valid for directories.
1081 * 4. can only be changed on regular files if no extents are allocated
1082 * 5. can be changed on directories at any time
1083 * 6. extsize hint of 0 turns off hints, clears inode flags.
1084 * 7. Extent size must be a multiple of the appropriate block size.
1085 * 8. for non-realtime files, the extent size hint must be limited
1086 * to half the AG size to avoid alignment extending the extent beyond the
1087 * limits of the AG.
1088 */
1089int
1090xfs_ioctl_setattr_check_extsize(
1091 struct xfs_inode *ip,
1092 struct fsxattr *fa)
1093{
1094 struct xfs_mount *mp = ip->i_mount;
1095
1096 if ((fa->fsx_xflags & XFS_XFLAG_EXTSIZE) && !S_ISREG(ip->i_d.di_mode))
1097 return -EINVAL;
1098
1099 if ((fa->fsx_xflags & XFS_XFLAG_EXTSZINHERIT) &&
1100 !S_ISDIR(ip->i_d.di_mode))
1101 return -EINVAL;
1102
1103 if (S_ISREG(ip->i_d.di_mode) && ip->i_d.di_nextents &&
1104 ((ip->i_d.di_extsize << mp->m_sb.sb_blocklog) != fa->fsx_extsize))
1105 return -EINVAL;
1106
1107 if (fa->fsx_extsize != 0) {
1108 xfs_extlen_t size;
1109 xfs_fsblock_t extsize_fsb;
1110
1111 extsize_fsb = XFS_B_TO_FSB(mp, fa->fsx_extsize);
1112 if (extsize_fsb > MAXEXTLEN)
1113 return -EINVAL;
1114
1115 if (XFS_IS_REALTIME_INODE(ip) ||
1116 (fa->fsx_xflags & XFS_XFLAG_REALTIME)) {
1117 size = mp->m_sb.sb_rextsize << mp->m_sb.sb_blocklog;
1118 } else {
1119 size = mp->m_sb.sb_blocksize;
1120 if (extsize_fsb > mp->m_sb.sb_agblocks / 2)
1121 return -EINVAL;
1122 }
1123
1124 if (fa->fsx_extsize % size)
1125 return -EINVAL;
1126 } else
1127 fa->fsx_xflags &= ~(XFS_XFLAG_EXTSIZE | XFS_XFLAG_EXTSZINHERIT);
1128
1129 return 0;
1130}
1131
1132int
1133xfs_ioctl_setattr_check_projid(
1134 struct xfs_inode *ip,
1135 struct fsxattr *fa)
1136{
1137 /* Disallow 32bit project ids if projid32bit feature is not enabled. */
1138 if (fa->fsx_projid > (__uint16_t)-1 &&
1139 !xfs_sb_version_hasprojid32bit(&ip->i_mount->m_sb))
1140 return -EINVAL;
1141
1142 /*
1143 * Project Quota ID state is only allowed to change from within the init
1144 * namespace. Enforce that restriction only if we are trying to change
1145 * the quota ID state. Everything else is allowed in user namespaces.
1146 */
1147 if (current_user_ns() == &init_user_ns)
1148 return 0;
1149
1150 if (xfs_get_projid(ip) != fa->fsx_projid)
1151 return -EINVAL;
1152 if ((fa->fsx_xflags & XFS_XFLAG_PROJINHERIT) !=
1153 (ip->i_d.di_flags & XFS_DIFLAG_PROJINHERIT))
1154 return -EINVAL;
1155
1156 return 0;
1157}
994 1158
995STATIC int 1159STATIC int
996xfs_ioctl_setattr( 1160xfs_ioctl_setattr(
997 xfs_inode_t *ip, 1161 xfs_inode_t *ip,
998 struct fsxattr *fa, 1162 struct fsxattr *fa)
999 int mask)
1000{ 1163{
1001 struct xfs_mount *mp = ip->i_mount; 1164 struct xfs_mount *mp = ip->i_mount;
1002 struct xfs_trans *tp; 1165 struct xfs_trans *tp;
1003 unsigned int lock_flags = 0;
1004 struct xfs_dquot *udqp = NULL; 1166 struct xfs_dquot *udqp = NULL;
1005 struct xfs_dquot *pdqp = NULL; 1167 struct xfs_dquot *pdqp = NULL;
1006 struct xfs_dquot *olddquot = NULL; 1168 struct xfs_dquot *olddquot = NULL;
@@ -1008,17 +1170,9 @@ xfs_ioctl_setattr(
1008 1170
1009 trace_xfs_ioctl_setattr(ip); 1171 trace_xfs_ioctl_setattr(ip);
1010 1172
1011 if (mp->m_flags & XFS_MOUNT_RDONLY) 1173 code = xfs_ioctl_setattr_check_projid(ip, fa);
1012 return -EROFS; 1174 if (code)
1013 if (XFS_FORCED_SHUTDOWN(mp)) 1175 return code;
1014 return -EIO;
1015
1016 /*
1017 * Disallow 32bit project ids when projid32bit feature is not enabled.
1018 */
1019 if ((mask & FSX_PROJID) && (fa->fsx_projid > (__uint16_t)-1) &&
1020 !xfs_sb_version_hasprojid32bit(&ip->i_mount->m_sb))
1021 return -EINVAL;
1022 1176
1023 /* 1177 /*
1024 * If disk quotas is on, we make sure that the dquots do exist on disk, 1178 * If disk quotas is on, we make sure that the dquots do exist on disk,
@@ -1028,7 +1182,7 @@ xfs_ioctl_setattr(
1028 * If the IDs do change before we take the ilock, we're covered 1182 * If the IDs do change before we take the ilock, we're covered
1029 * because the i_*dquot fields will get updated anyway. 1183 * because the i_*dquot fields will get updated anyway.
1030 */ 1184 */
1031 if (XFS_IS_QUOTA_ON(mp) && (mask & FSX_PROJID)) { 1185 if (XFS_IS_QUOTA_ON(mp)) {
1032 code = xfs_qm_vop_dqalloc(ip, ip->i_d.di_uid, 1186 code = xfs_qm_vop_dqalloc(ip, ip->i_d.di_uid,
1033 ip->i_d.di_gid, fa->fsx_projid, 1187 ip->i_d.di_gid, fa->fsx_projid,
1034 XFS_QMOPT_PQUOTA, &udqp, NULL, &pdqp); 1188 XFS_QMOPT_PQUOTA, &udqp, NULL, &pdqp);
@@ -1036,175 +1190,49 @@ xfs_ioctl_setattr(
1036 return code; 1190 return code;
1037 } 1191 }
1038 1192
1039 /* 1193 tp = xfs_ioctl_setattr_get_trans(ip);
1040 * For the other attributes, we acquire the inode lock and 1194 if (IS_ERR(tp)) {
1041 * first do an error checking pass. 1195 code = PTR_ERR(tp);
1042 */ 1196 goto error_free_dquots;
1043 tp = xfs_trans_alloc(mp, XFS_TRANS_SETATTR_NOT_SIZE);
1044 code = xfs_trans_reserve(tp, &M_RES(mp)->tr_ichange, 0, 0);
1045 if (code)
1046 goto error_return;
1047
1048 lock_flags = XFS_ILOCK_EXCL;
1049 xfs_ilock(ip, lock_flags);
1050
1051 /*
1052 * CAP_FOWNER overrides the following restrictions:
1053 *
1054 * The user ID of the calling process must be equal
1055 * to the file owner ID, except in cases where the
1056 * CAP_FSETID capability is applicable.
1057 */
1058 if (!inode_owner_or_capable(VFS_I(ip))) {
1059 code = -EPERM;
1060 goto error_return;
1061 } 1197 }
1062 1198
1063 /*
1064 * Do a quota reservation only if projid is actually going to change.
1065 * Only allow changing of projid from init_user_ns since it is a
1066 * non user namespace aware identifier.
1067 */
1068 if (mask & FSX_PROJID) {
1069 if (current_user_ns() != &init_user_ns) {
1070 code = -EINVAL;
1071 goto error_return;
1072 }
1073
1074 if (XFS_IS_QUOTA_RUNNING(mp) &&
1075 XFS_IS_PQUOTA_ON(mp) &&
1076 xfs_get_projid(ip) != fa->fsx_projid) {
1077 ASSERT(tp);
1078 code = xfs_qm_vop_chown_reserve(tp, ip, udqp, NULL,
1079 pdqp, capable(CAP_FOWNER) ?
1080 XFS_QMOPT_FORCE_RES : 0);
1081 if (code) /* out of quota */
1082 goto error_return;
1083 }
1084 }
1085 1199
1086 if (mask & FSX_EXTSIZE) { 1200 if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp) &&
1087 /* 1201 xfs_get_projid(ip) != fa->fsx_projid) {
1088 * Can't change extent size if any extents are allocated. 1202 code = xfs_qm_vop_chown_reserve(tp, ip, udqp, NULL, pdqp,
1089 */ 1203 capable(CAP_FOWNER) ? XFS_QMOPT_FORCE_RES : 0);
1090 if (ip->i_d.di_nextents && 1204 if (code) /* out of quota */
1091 ((ip->i_d.di_extsize << mp->m_sb.sb_blocklog) != 1205 goto error_trans_cancel;
1092 fa->fsx_extsize)) {
1093 code = -EINVAL; /* EFBIG? */
1094 goto error_return;
1095 }
1096
1097 /*
1098 * Extent size must be a multiple of the appropriate block
1099 * size, if set at all. It must also be smaller than the
1100 * maximum extent size supported by the filesystem.
1101 *
1102 * Also, for non-realtime files, limit the extent size hint to
1103 * half the size of the AGs in the filesystem so alignment
1104 * doesn't result in extents larger than an AG.
1105 */
1106 if (fa->fsx_extsize != 0) {
1107 xfs_extlen_t size;
1108 xfs_fsblock_t extsize_fsb;
1109
1110 extsize_fsb = XFS_B_TO_FSB(mp, fa->fsx_extsize);
1111 if (extsize_fsb > MAXEXTLEN) {
1112 code = -EINVAL;
1113 goto error_return;
1114 }
1115
1116 if (XFS_IS_REALTIME_INODE(ip) ||
1117 ((mask & FSX_XFLAGS) &&
1118 (fa->fsx_xflags & XFS_XFLAG_REALTIME))) {
1119 size = mp->m_sb.sb_rextsize <<
1120 mp->m_sb.sb_blocklog;
1121 } else {
1122 size = mp->m_sb.sb_blocksize;
1123 if (extsize_fsb > mp->m_sb.sb_agblocks / 2) {
1124 code = -EINVAL;
1125 goto error_return;
1126 }
1127 }
1128
1129 if (fa->fsx_extsize % size) {
1130 code = -EINVAL;
1131 goto error_return;
1132 }
1133 }
1134 } 1206 }
1135 1207
1208 code = xfs_ioctl_setattr_check_extsize(ip, fa);
1209 if (code)
1210 goto error_trans_cancel;
1136 1211
1137 if (mask & FSX_XFLAGS) { 1212 code = xfs_ioctl_setattr_xflags(tp, ip, fa);
1138 /* 1213 if (code)
1139 * Can't change realtime flag if any extents are allocated. 1214 goto error_trans_cancel;
1140 */
1141 if ((ip->i_d.di_nextents || ip->i_delayed_blks) &&
1142 (XFS_IS_REALTIME_INODE(ip)) !=
1143 (fa->fsx_xflags & XFS_XFLAG_REALTIME)) {
1144 code = -EINVAL; /* EFBIG? */
1145 goto error_return;
1146 }
1147
1148 /*
1149 * If realtime flag is set then must have realtime data.
1150 */
1151 if ((fa->fsx_xflags & XFS_XFLAG_REALTIME)) {
1152 if ((mp->m_sb.sb_rblocks == 0) ||
1153 (mp->m_sb.sb_rextsize == 0) ||
1154 (ip->i_d.di_extsize % mp->m_sb.sb_rextsize)) {
1155 code = -EINVAL;
1156 goto error_return;
1157 }
1158 }
1159
1160 /*
1161 * Can't modify an immutable/append-only file unless
1162 * we have appropriate permission.
1163 */
1164 if ((ip->i_d.di_flags &
1165 (XFS_DIFLAG_IMMUTABLE|XFS_DIFLAG_APPEND) ||
1166 (fa->fsx_xflags &
1167 (XFS_XFLAG_IMMUTABLE | XFS_XFLAG_APPEND))) &&
1168 !capable(CAP_LINUX_IMMUTABLE)) {
1169 code = -EPERM;
1170 goto error_return;
1171 }
1172 }
1173
1174 xfs_trans_ijoin(tp, ip, 0);
1175 1215
1176 /* 1216 /*
1177 * Change file ownership. Must be the owner or privileged. 1217 * Change file ownership. Must be the owner or privileged. CAP_FSETID
1218 * overrides the following restrictions:
1219 *
1220 * The set-user-ID and set-group-ID bits of a file will be cleared upon
1221 * successful return from chown()
1178 */ 1222 */
1179 if (mask & FSX_PROJID) {
1180 /*
1181 * CAP_FSETID overrides the following restrictions:
1182 *
1183 * The set-user-ID and set-group-ID bits of a file will be
1184 * cleared upon successful return from chown()
1185 */
1186 if ((ip->i_d.di_mode & (S_ISUID|S_ISGID)) &&
1187 !capable_wrt_inode_uidgid(VFS_I(ip), CAP_FSETID))
1188 ip->i_d.di_mode &= ~(S_ISUID|S_ISGID);
1189
1190 /*
1191 * Change the ownerships and register quota modifications
1192 * in the transaction.
1193 */
1194 if (xfs_get_projid(ip) != fa->fsx_projid) {
1195 if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp)) {
1196 olddquot = xfs_qm_vop_chown(tp, ip,
1197 &ip->i_pdquot, pdqp);
1198 }
1199 ASSERT(ip->i_d.di_version > 1);
1200 xfs_set_projid(ip, fa->fsx_projid);
1201 }
1202 1223
1203 } 1224 if ((ip->i_d.di_mode & (S_ISUID|S_ISGID)) &&
1225 !capable_wrt_inode_uidgid(VFS_I(ip), CAP_FSETID))
1226 ip->i_d.di_mode &= ~(S_ISUID|S_ISGID);
1204 1227
1205 if (mask & FSX_XFLAGS) { 1228 /* Change the ownerships and register project quota modifications */
1206 xfs_set_diflags(ip, fa->fsx_xflags); 1229 if (xfs_get_projid(ip) != fa->fsx_projid) {
1207 xfs_diflags_to_linux(ip); 1230 if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp)) {
1231 olddquot = xfs_qm_vop_chown(tp, ip,
1232 &ip->i_pdquot, pdqp);
1233 }
1234 ASSERT(ip->i_d.di_version > 1);
1235 xfs_set_projid(ip, fa->fsx_projid);
1208 } 1236 }
1209 1237
1210 /* 1238 /*
@@ -1212,34 +1240,12 @@ xfs_ioctl_setattr(
1212 * extent size hint should be set on the inode. If no extent size flags 1240 * extent size hint should be set on the inode. If no extent size flags
1213 * are set on the inode then unconditionally clear the extent size hint. 1241 * are set on the inode then unconditionally clear the extent size hint.
1214 */ 1242 */
1215 if (mask & FSX_EXTSIZE) { 1243 if (ip->i_d.di_flags & (XFS_DIFLAG_EXTSIZE | XFS_DIFLAG_EXTSZINHERIT))
1216 int extsize = 0; 1244 ip->i_d.di_extsize = fa->fsx_extsize >> mp->m_sb.sb_blocklog;
1217 1245 else
1218 if (ip->i_d.di_flags & 1246 ip->i_d.di_extsize = 0;
1219 (XFS_DIFLAG_EXTSIZE | XFS_DIFLAG_EXTSZINHERIT))
1220 extsize = fa->fsx_extsize >> mp->m_sb.sb_blocklog;
1221 ip->i_d.di_extsize = extsize;
1222 }
1223
1224 xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG);
1225 xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
1226
1227 XFS_STATS_INC(xs_ig_attrchg);
1228 1247
1229 /*
1230 * If this is a synchronous mount, make sure that the
1231 * transaction goes to disk before returning to the user.
1232 * This is slightly sub-optimal in that truncates require
1233 * two sync transactions instead of one for wsync filesystems.
1234 * One for the truncate and one for the timestamps since we
1235 * don't want to change the timestamps unless we're sure the
1236 * truncate worked. Truncates are less than 1% of the laddis
1237 * mix so this probably isn't worth the trouble to optimize.
1238 */
1239 if (mp->m_flags & XFS_MOUNT_WSYNC)
1240 xfs_trans_set_sync(tp);
1241 code = xfs_trans_commit(tp, 0); 1248 code = xfs_trans_commit(tp, 0);
1242 xfs_iunlock(ip, lock_flags);
1243 1249
1244 /* 1250 /*
1245 * Release any dquot(s) the inode had kept before chown. 1251 * Release any dquot(s) the inode had kept before chown.
@@ -1250,12 +1256,11 @@ xfs_ioctl_setattr(
1250 1256
1251 return code; 1257 return code;
1252 1258
1253 error_return: 1259error_trans_cancel:
1260 xfs_trans_cancel(tp, 0);
1261error_free_dquots:
1254 xfs_qm_dqrele(udqp); 1262 xfs_qm_dqrele(udqp);
1255 xfs_qm_dqrele(pdqp); 1263 xfs_qm_dqrele(pdqp);
1256 xfs_trans_cancel(tp, 0);
1257 if (lock_flags)
1258 xfs_iunlock(ip, lock_flags);
1259 return code; 1264 return code;
1260} 1265}
1261 1266
@@ -1266,20 +1271,15 @@ xfs_ioc_fssetxattr(
1266 void __user *arg) 1271 void __user *arg)
1267{ 1272{
1268 struct fsxattr fa; 1273 struct fsxattr fa;
1269 unsigned int mask;
1270 int error; 1274 int error;
1271 1275
1272 if (copy_from_user(&fa, arg, sizeof(fa))) 1276 if (copy_from_user(&fa, arg, sizeof(fa)))
1273 return -EFAULT; 1277 return -EFAULT;
1274 1278
1275 mask = FSX_XFLAGS | FSX_EXTSIZE | FSX_PROJID;
1276 if (filp->f_flags & (O_NDELAY|O_NONBLOCK))
1277 mask |= FSX_NONBLOCK;
1278
1279 error = mnt_want_write_file(filp); 1279 error = mnt_want_write_file(filp);
1280 if (error) 1280 if (error)
1281 return error; 1281 return error;
1282 error = xfs_ioctl_setattr(ip, &fa, mask); 1282 error = xfs_ioctl_setattr(ip, &fa);
1283 mnt_drop_write_file(filp); 1283 mnt_drop_write_file(filp);
1284 return error; 1284 return error;
1285} 1285}
@@ -1299,14 +1299,14 @@ xfs_ioc_getxflags(
1299 1299
1300STATIC int 1300STATIC int
1301xfs_ioc_setxflags( 1301xfs_ioc_setxflags(
1302 xfs_inode_t *ip, 1302 struct xfs_inode *ip,
1303 struct file *filp, 1303 struct file *filp,
1304 void __user *arg) 1304 void __user *arg)
1305{ 1305{
1306 struct xfs_trans *tp;
1306 struct fsxattr fa; 1307 struct fsxattr fa;
1307 unsigned int flags; 1308 unsigned int flags;
1308 unsigned int mask; 1309 int error;
1309 int error;
1310 1310
1311 if (copy_from_user(&flags, arg, sizeof(flags))) 1311 if (copy_from_user(&flags, arg, sizeof(flags)))
1312 return -EFAULT; 1312 return -EFAULT;
@@ -1316,15 +1316,26 @@ xfs_ioc_setxflags(
1316 FS_SYNC_FL)) 1316 FS_SYNC_FL))
1317 return -EOPNOTSUPP; 1317 return -EOPNOTSUPP;
1318 1318
1319 mask = FSX_XFLAGS;
1320 if (filp->f_flags & (O_NDELAY|O_NONBLOCK))
1321 mask |= FSX_NONBLOCK;
1322 fa.fsx_xflags = xfs_merge_ioc_xflags(flags, xfs_ip2xflags(ip)); 1319 fa.fsx_xflags = xfs_merge_ioc_xflags(flags, xfs_ip2xflags(ip));
1323 1320
1324 error = mnt_want_write_file(filp); 1321 error = mnt_want_write_file(filp);
1325 if (error) 1322 if (error)
1326 return error; 1323 return error;
1327 error = xfs_ioctl_setattr(ip, &fa, mask); 1324
1325 tp = xfs_ioctl_setattr_get_trans(ip);
1326 if (IS_ERR(tp)) {
1327 error = PTR_ERR(tp);
1328 goto out_drop_write;
1329 }
1330
1331 error = xfs_ioctl_setattr_xflags(tp, ip, &fa);
1332 if (error) {
1333 xfs_trans_cancel(tp, 0);
1334 goto out_drop_write;
1335 }
1336
1337 error = xfs_trans_commit(tp, 0);
1338out_drop_write:
1328 mnt_drop_write_file(filp); 1339 mnt_drop_write_file(filp);
1329 return error; 1340 return error;
1330} 1341}