summaryrefslogtreecommitdiff
path: root/fs/posix_acl.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/posix_acl.c')
-rw-r--r--fs/posix_acl.c31
1 files changed, 31 insertions, 0 deletions
diff --git a/fs/posix_acl.c b/fs/posix_acl.c
index 3542f1f814e2..1da000aabb08 100644
--- a/fs/posix_acl.c
+++ b/fs/posix_acl.c
@@ -407,6 +407,37 @@ posix_acl_create(struct posix_acl **acl, gfp_t gfp, umode_t *mode_p)
}
EXPORT_SYMBOL(posix_acl_create);
+/**
+ * posix_acl_update_mode - update mode in set_acl
+ *
+ * Update the file mode when setting an ACL: compute the new file permission
+ * bits based on the ACL. In addition, if the ACL is equivalent to the new
+ * file mode, set *acl to NULL to indicate that no ACL should be set.
+ *
+ * As with chmod, clear the setgit bit if the caller is not in the owning group
+ * or capable of CAP_FSETID (see inode_change_ok).
+ *
+ * Called from set_acl inode operations.
+ */
+int posix_acl_update_mode(struct inode *inode, umode_t *mode_p,
+ struct posix_acl **acl)
+{
+ umode_t mode = inode->i_mode;
+ int error;
+
+ error = posix_acl_equiv_mode(*acl, &mode);
+ if (error < 0)
+ return error;
+ if (error == 0)
+ *acl = NULL;
+ if (!in_group_p(inode->i_gid) &&
+ !capable_wrt_inode_uidgid(inode, CAP_FSETID))
+ mode &= ~S_ISGID;
+ *mode_p = mode;
+ return 0;
+}
+EXPORT_SYMBOL(posix_acl_update_mode);
+
int
posix_acl_chmod(struct posix_acl **acl, gfp_t gfp, umode_t mode)
{