summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/xfs/xfs_vnodeops.c149
1 files changed, 36 insertions, 113 deletions
diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c
index 4547608b46c..f07bf8768c3 100644
--- a/fs/xfs/xfs_vnodeops.c
+++ b/fs/xfs/xfs_vnodeops.c
@@ -70,7 +70,6 @@ xfs_setattr(
gid_t gid=0, igid=0;
int timeflags = 0;
struct xfs_dquot *udqp, *gdqp, *olddquot1, *olddquot2;
- int file_owner;
int need_iolock = 1;
xfs_itrace_entry(ip);
@@ -81,6 +80,10 @@ xfs_setattr(
if (XFS_FORCED_SHUTDOWN(mp))
return XFS_ERROR(EIO);
+ code = -inode_change_ok(inode, iattr);
+ if (code)
+ return code;
+
olddquot1 = olddquot2 = NULL;
udqp = gdqp = NULL;
@@ -158,56 +161,6 @@ xfs_setattr(
xfs_ilock(ip, lock_flags);
- /* boolean: are we the file owner? */
- file_owner = (current_fsuid() == ip->i_d.di_uid);
-
- /*
- * Change various properties of a file.
- * Only the owner or users with CAP_FOWNER
- * capability may do these things.
- */
- if (mask & (ATTR_MODE|ATTR_UID|ATTR_GID)) {
- /*
- * CAP_FOWNER overrides the following restrictions:
- *
- * The user ID of the calling process must be equal
- * to the file owner ID, except in cases where the
- * CAP_FSETID capability is applicable.
- */
- if (!file_owner && !capable(CAP_FOWNER)) {
- code = XFS_ERROR(EPERM);
- goto error_return;
- }
-
- /*
- * CAP_FSETID overrides the following restrictions:
- *
- * The effective user ID of the calling process shall match
- * the file owner when setting the set-user-ID and
- * set-group-ID bits on that file.
- *
- * The effective group ID or one of the supplementary group
- * IDs of the calling process shall match the group owner of
- * the file when setting the set-group-ID bit on that file
- */
- if (mask & ATTR_MODE) {
- mode_t m = 0;
-
- if ((iattr->ia_mode & S_ISUID) && !file_owner)
- m |= S_ISUID;
- if ((iattr->ia_mode & S_ISGID) &&
- !in_group_p((gid_t)ip->i_d.di_gid))
- m |= S_ISGID;
-#if 0
- /* Linux allows this, Irix doesn't. */
- if ((iattr->ia_mode & S_ISVTX) && !S_ISDIR(ip->i_d.di_mode))
- m |= S_ISVTX;
-#endif
- if (m && !capable(CAP_FSETID))
- iattr->ia_mode &= ~m;
- }
- }
-
/*
* Change file ownership. Must be the owner or privileged.
*/
@@ -224,22 +177,6 @@ xfs_setattr(
uid = (mask & ATTR_UID) ? iattr->ia_uid : iuid;
/*
- * CAP_CHOWN overrides the following restrictions:
- *
- * If _POSIX_CHOWN_RESTRICTED is defined, this capability
- * shall override the restriction that a process cannot
- * change the user ID of a file it owns and the restriction
- * that the group ID supplied to the chown() function
- * shall be equal to either the group ID or one of the
- * supplementary group IDs of the calling process.
- */
- if ((iuid != uid ||
- (igid != gid && !in_group_p((gid_t)gid))) &&
- !capable(CAP_CHOWN)) {
- code = XFS_ERROR(EPERM);
- goto error_return;
- }
- /*
* Do a quota reservation only if uid/gid is actually
* going to change.
*/
@@ -276,36 +213,22 @@ xfs_setattr(
code = XFS_ERROR(EINVAL);
goto error_return;
}
+
/*
* Make sure that the dquots are attached to the inode.
*/
- if ((code = XFS_QM_DQATTACH(mp, ip, XFS_QMOPT_ILOCKED)))
+ code = XFS_QM_DQATTACH(mp, ip, XFS_QMOPT_ILOCKED);
+ if (code)
goto error_return;
- }
- /*
- * Change file access or modified times.
- */
- if (mask & (ATTR_ATIME|ATTR_MTIME)) {
- if (!file_owner) {
- if ((mask & (ATTR_MTIME_SET|ATTR_ATIME_SET)) &&
- !capable(CAP_FOWNER)) {
- code = XFS_ERROR(EPERM);
- goto error_return;
- }
- }
- }
-
- /*
- * Now we can make the changes. Before we join the inode
- * to the transaction, if ATTR_SIZE is set then take care of
- * the part of the truncation that must be done without the
- * inode lock. This needs to be done before joining the inode
- * to the transaction, because the inode cannot be unlocked
- * once it is a part of the transaction.
- */
- if (mask & ATTR_SIZE) {
- code = 0;
+ /*
+ * Now we can make the changes. Before we join the inode
+ * to the transaction, if ATTR_SIZE is set then take care of
+ * the part of the truncation that must be done without the
+ * inode lock. This needs to be done before joining the inode
+ * to the transaction, because the inode cannot be unlocked
+ * once it is a part of the transaction.
+ */
if (iattr->ia_size > ip->i_size) {
/*
* Do the first part of growing a file: zero any data
@@ -360,17 +283,10 @@ xfs_setattr(
}
commit_flags = XFS_TRANS_RELEASE_LOG_RES;
xfs_ilock(ip, XFS_ILOCK_EXCL);
- }
- if (tp) {
xfs_trans_ijoin(tp, ip, lock_flags);
xfs_trans_ihold(tp, ip);
- }
- /*
- * Truncate file. Must have write permission and not be a directory.
- */
- if (mask & ATTR_SIZE) {
/*
* Only change the c/mtime if we are changing the size
* or we are explicitly asked to change it. This handles
@@ -410,20 +326,9 @@ xfs_setattr(
*/
xfs_iflags_set(ip, XFS_ITRUNCATED);
}
- }
-
- /*
- * Change file access modes.
- */
- if (mask & ATTR_MODE) {
- ip->i_d.di_mode &= S_IFMT;
- ip->i_d.di_mode |= iattr->ia_mode & ~S_IFMT;
-
- inode->i_mode &= S_IFMT;
- inode->i_mode |= iattr->ia_mode & ~S_IFMT;
-
- xfs_trans_log_inode (tp, ip, XFS_ILOG_CORE);
- timeflags |= XFS_ICHGTIME_CHG;
+ } else if (tp) {
+ xfs_trans_ijoin(tp, ip, lock_flags);
+ xfs_trans_ihold(tp, ip);
}
/*
@@ -471,6 +376,24 @@ xfs_setattr(
timeflags |= XFS_ICHGTIME_CHG;
}
+ /*
+ * Change file access modes.
+ */
+ if (mask & ATTR_MODE) {
+ umode_t mode = iattr->ia_mode;
+
+ if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID))
+ mode &= ~S_ISGID;
+
+ ip->i_d.di_mode &= S_IFMT;
+ ip->i_d.di_mode |= mode & ~S_IFMT;
+
+ inode->i_mode &= S_IFMT;
+ inode->i_mode |= mode & ~S_IFMT;
+
+ xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+ timeflags |= XFS_ICHGTIME_CHG;
+ }
/*
* Change file access or modified times.