diff options
Diffstat (limited to 'fs/xfs/xfs_vnodeops.c')
| -rw-r--r-- | fs/xfs/xfs_vnodeops.c | 149 |
1 files changed, 36 insertions, 113 deletions
diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c index 4547608b46c4..f07bf8768c3a 100644 --- a/fs/xfs/xfs_vnodeops.c +++ b/fs/xfs/xfs_vnodeops.c | |||
| @@ -70,7 +70,6 @@ xfs_setattr( | |||
| 70 | gid_t gid=0, igid=0; | 70 | gid_t gid=0, igid=0; |
| 71 | int timeflags = 0; | 71 | int timeflags = 0; |
| 72 | struct xfs_dquot *udqp, *gdqp, *olddquot1, *olddquot2; | 72 | struct xfs_dquot *udqp, *gdqp, *olddquot1, *olddquot2; |
| 73 | int file_owner; | ||
| 74 | int need_iolock = 1; | 73 | int need_iolock = 1; |
| 75 | 74 | ||
| 76 | xfs_itrace_entry(ip); | 75 | xfs_itrace_entry(ip); |
| @@ -81,6 +80,10 @@ xfs_setattr( | |||
| 81 | if (XFS_FORCED_SHUTDOWN(mp)) | 80 | if (XFS_FORCED_SHUTDOWN(mp)) |
| 82 | return XFS_ERROR(EIO); | 81 | return XFS_ERROR(EIO); |
| 83 | 82 | ||
| 83 | code = -inode_change_ok(inode, iattr); | ||
| 84 | if (code) | ||
| 85 | return code; | ||
| 86 | |||
| 84 | olddquot1 = olddquot2 = NULL; | 87 | olddquot1 = olddquot2 = NULL; |
| 85 | udqp = gdqp = NULL; | 88 | udqp = gdqp = NULL; |
| 86 | 89 | ||
| @@ -158,56 +161,6 @@ xfs_setattr( | |||
| 158 | 161 | ||
| 159 | xfs_ilock(ip, lock_flags); | 162 | xfs_ilock(ip, lock_flags); |
| 160 | 163 | ||
| 161 | /* boolean: are we the file owner? */ | ||
| 162 | file_owner = (current_fsuid() == ip->i_d.di_uid); | ||
| 163 | |||
| 164 | /* | ||
| 165 | * Change various properties of a file. | ||
| 166 | * Only the owner or users with CAP_FOWNER | ||
| 167 | * capability may do these things. | ||
| 168 | */ | ||
| 169 | if (mask & (ATTR_MODE|ATTR_UID|ATTR_GID)) { | ||
| 170 | /* | ||
| 171 | * CAP_FOWNER overrides the following restrictions: | ||
| 172 | * | ||
| 173 | * The user ID of the calling process must be equal | ||
| 174 | * to the file owner ID, except in cases where the | ||
| 175 | * CAP_FSETID capability is applicable. | ||
| 176 | */ | ||
| 177 | if (!file_owner && !capable(CAP_FOWNER)) { | ||
| 178 | code = XFS_ERROR(EPERM); | ||
| 179 | goto error_return; | ||
| 180 | } | ||
| 181 | |||
| 182 | /* | ||
| 183 | * CAP_FSETID overrides the following restrictions: | ||
| 184 | * | ||
| 185 | * The effective user ID of the calling process shall match | ||
| 186 | * the file owner when setting the set-user-ID and | ||
| 187 | * set-group-ID bits on that file. | ||
| 188 | * | ||
| 189 | * The effective group ID or one of the supplementary group | ||
| 190 | * IDs of the calling process shall match the group owner of | ||
| 191 | * the file when setting the set-group-ID bit on that file | ||
| 192 | */ | ||
| 193 | if (mask & ATTR_MODE) { | ||
| 194 | mode_t m = 0; | ||
| 195 | |||
| 196 | if ((iattr->ia_mode & S_ISUID) && !file_owner) | ||
| 197 | m |= S_ISUID; | ||
| 198 | if ((iattr->ia_mode & S_ISGID) && | ||
| 199 | !in_group_p((gid_t)ip->i_d.di_gid)) | ||
| 200 | m |= S_ISGID; | ||
| 201 | #if 0 | ||
| 202 | /* Linux allows this, Irix doesn't. */ | ||
| 203 | if ((iattr->ia_mode & S_ISVTX) && !S_ISDIR(ip->i_d.di_mode)) | ||
| 204 | m |= S_ISVTX; | ||
| 205 | #endif | ||
| 206 | if (m && !capable(CAP_FSETID)) | ||
| 207 | iattr->ia_mode &= ~m; | ||
| 208 | } | ||
| 209 | } | ||
| 210 | |||
| 211 | /* | 164 | /* |
| 212 | * Change file ownership. Must be the owner or privileged. | 165 | * Change file ownership. Must be the owner or privileged. |
| 213 | */ | 166 | */ |
| @@ -224,22 +177,6 @@ xfs_setattr( | |||
| 224 | uid = (mask & ATTR_UID) ? iattr->ia_uid : iuid; | 177 | uid = (mask & ATTR_UID) ? iattr->ia_uid : iuid; |
| 225 | 178 | ||
| 226 | /* | 179 | /* |
| 227 | * CAP_CHOWN overrides the following restrictions: | ||
| 228 | * | ||
| 229 | * If _POSIX_CHOWN_RESTRICTED is defined, this capability | ||
| 230 | * shall override the restriction that a process cannot | ||
| 231 | * change the user ID of a file it owns and the restriction | ||
| 232 | * that the group ID supplied to the chown() function | ||
| 233 | * shall be equal to either the group ID or one of the | ||
| 234 | * supplementary group IDs of the calling process. | ||
| 235 | */ | ||
| 236 | if ((iuid != uid || | ||
| 237 | (igid != gid && !in_group_p((gid_t)gid))) && | ||
| 238 | !capable(CAP_CHOWN)) { | ||
| 239 | code = XFS_ERROR(EPERM); | ||
| 240 | goto error_return; | ||
| 241 | } | ||
| 242 | /* | ||
| 243 | * Do a quota reservation only if uid/gid is actually | 180 | * Do a quota reservation only if uid/gid is actually |
| 244 | * going to change. | 181 | * going to change. |
| 245 | */ | 182 | */ |
| @@ -276,36 +213,22 @@ xfs_setattr( | |||
| 276 | code = XFS_ERROR(EINVAL); | 213 | code = XFS_ERROR(EINVAL); |
| 277 | goto error_return; | 214 | goto error_return; |
| 278 | } | 215 | } |
| 216 | |||
| 279 | /* | 217 | /* |
| 280 | * Make sure that the dquots are attached to the inode. | 218 | * Make sure that the dquots are attached to the inode. |
| 281 | */ | 219 | */ |
| 282 | if ((code = XFS_QM_DQATTACH(mp, ip, XFS_QMOPT_ILOCKED))) | 220 | code = XFS_QM_DQATTACH(mp, ip, XFS_QMOPT_ILOCKED); |
| 221 | if (code) | ||
| 283 | goto error_return; | 222 | goto error_return; |
| 284 | } | ||
| 285 | 223 | ||
| 286 | /* | 224 | /* |
| 287 | * Change file access or modified times. | 225 | * Now we can make the changes. Before we join the inode |
| 288 | */ | 226 | * to the transaction, if ATTR_SIZE is set then take care of |
| 289 | if (mask & (ATTR_ATIME|ATTR_MTIME)) { | 227 | * the part of the truncation that must be done without the |
| 290 | if (!file_owner) { | 228 | * inode lock. This needs to be done before joining the inode |
| 291 | if ((mask & (ATTR_MTIME_SET|ATTR_ATIME_SET)) && | 229 | * to the transaction, because the inode cannot be unlocked |
| 292 | !capable(CAP_FOWNER)) { | 230 | * once it is a part of the transaction. |
| 293 | code = XFS_ERROR(EPERM); | 231 | */ |
| 294 | goto error_return; | ||
| 295 | } | ||
| 296 | } | ||
| 297 | } | ||
| 298 | |||
| 299 | /* | ||
| 300 | * Now we can make the changes. Before we join the inode | ||
| 301 | * to the transaction, if ATTR_SIZE is set then take care of | ||
| 302 | * the part of the truncation that must be done without the | ||
| 303 | * inode lock. This needs to be done before joining the inode | ||
| 304 | * to the transaction, because the inode cannot be unlocked | ||
| 305 | * once it is a part of the transaction. | ||
| 306 | */ | ||
| 307 | if (mask & ATTR_SIZE) { | ||
| 308 | code = 0; | ||
| 309 | if (iattr->ia_size > ip->i_size) { | 232 | if (iattr->ia_size > ip->i_size) { |
| 310 | /* | 233 | /* |
| 311 | * Do the first part of growing a file: zero any data | 234 | * Do the first part of growing a file: zero any data |
| @@ -360,17 +283,10 @@ xfs_setattr( | |||
| 360 | } | 283 | } |
| 361 | commit_flags = XFS_TRANS_RELEASE_LOG_RES; | 284 | commit_flags = XFS_TRANS_RELEASE_LOG_RES; |
| 362 | xfs_ilock(ip, XFS_ILOCK_EXCL); | 285 | xfs_ilock(ip, XFS_ILOCK_EXCL); |
| 363 | } | ||
| 364 | 286 | ||
| 365 | if (tp) { | ||
| 366 | xfs_trans_ijoin(tp, ip, lock_flags); | 287 | xfs_trans_ijoin(tp, ip, lock_flags); |
| 367 | xfs_trans_ihold(tp, ip); | 288 | xfs_trans_ihold(tp, ip); |
| 368 | } | ||
| 369 | 289 | ||
| 370 | /* | ||
| 371 | * Truncate file. Must have write permission and not be a directory. | ||
| 372 | */ | ||
| 373 | if (mask & ATTR_SIZE) { | ||
| 374 | /* | 290 | /* |
| 375 | * Only change the c/mtime if we are changing the size | 291 | * Only change the c/mtime if we are changing the size |
| 376 | * or we are explicitly asked to change it. This handles | 292 | * or we are explicitly asked to change it. This handles |
| @@ -410,20 +326,9 @@ xfs_setattr( | |||
| 410 | */ | 326 | */ |
| 411 | xfs_iflags_set(ip, XFS_ITRUNCATED); | 327 | xfs_iflags_set(ip, XFS_ITRUNCATED); |
| 412 | } | 328 | } |
| 413 | } | 329 | } else if (tp) { |
| 414 | 330 | xfs_trans_ijoin(tp, ip, lock_flags); | |
| 415 | /* | 331 | xfs_trans_ihold(tp, ip); |
| 416 | * Change file access modes. | ||
| 417 | */ | ||
| 418 | if (mask & ATTR_MODE) { | ||
| 419 | ip->i_d.di_mode &= S_IFMT; | ||
| 420 | ip->i_d.di_mode |= iattr->ia_mode & ~S_IFMT; | ||
| 421 | |||
| 422 | inode->i_mode &= S_IFMT; | ||
| 423 | inode->i_mode |= iattr->ia_mode & ~S_IFMT; | ||
| 424 | |||
| 425 | xfs_trans_log_inode (tp, ip, XFS_ILOG_CORE); | ||
| 426 | timeflags |= XFS_ICHGTIME_CHG; | ||
| 427 | } | 332 | } |
| 428 | 333 | ||
| 429 | /* | 334 | /* |
| @@ -471,6 +376,24 @@ xfs_setattr( | |||
| 471 | timeflags |= XFS_ICHGTIME_CHG; | 376 | timeflags |= XFS_ICHGTIME_CHG; |
| 472 | } | 377 | } |
| 473 | 378 | ||
| 379 | /* | ||
| 380 | * Change file access modes. | ||
| 381 | */ | ||
| 382 | if (mask & ATTR_MODE) { | ||
| 383 | umode_t mode = iattr->ia_mode; | ||
| 384 | |||
| 385 | if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID)) | ||
| 386 | mode &= ~S_ISGID; | ||
| 387 | |||
| 388 | ip->i_d.di_mode &= S_IFMT; | ||
| 389 | ip->i_d.di_mode |= mode & ~S_IFMT; | ||
| 390 | |||
| 391 | inode->i_mode &= S_IFMT; | ||
| 392 | inode->i_mode |= mode & ~S_IFMT; | ||
| 393 | |||
| 394 | xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); | ||
| 395 | timeflags |= XFS_ICHGTIME_CHG; | ||
| 396 | } | ||
| 474 | 397 | ||
| 475 | /* | 398 | /* |
| 476 | * Change file access or modified times. | 399 | * Change file access or modified times. |
