From: Darrick J. Wong <djwong@xxxxxxxxxx> If someone sets ACLs on a file that can be expressed fully as Unix DAC mode bits, most filesystems will then update the mode bits and drop the ACL xattr to reduce inefficiency in the file access paths. Let's do that too. Signed-off-by: "Darrick J. Wong" <djwong@xxxxxxxxxx> --- fs/fuse/acl.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/fs/fuse/acl.c b/fs/fuse/acl.c index 8f484b105f13ab..b892976d9e284c 100644 --- a/fs/fuse/acl.c +++ b/fs/fuse/acl.c @@ -98,6 +98,7 @@ int fuse_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, struct inode *inode = d_inode(dentry); struct fuse_conn *fc = get_fuse_conn(inode); const char *name; + umode_t mode = inode->i_mode; int ret; if (fuse_is_bad(inode)) @@ -113,6 +114,20 @@ int fuse_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, else return -EINVAL; + /* + * If the ACL can be represented entirely with changes to the mode + * bits, then most filesystems will update the mode bits and delete + * the ACL xattr. Note that we only started doing this after the main + * ACL implementation was merged, so that's why it's gated on regular + * iomap. XXX: This should be some sort of separate flag? + */ + if (acl && type == ACL_TYPE_ACCESS && + fuse_has_iomap(inode) && fc->posix_acl) { + ret = posix_acl_update_mode(idmap, inode, &mode, &acl); + if (ret) + return ret; + } + if (acl) { unsigned int extra_flags = 0; /* @@ -143,7 +158,7 @@ int fuse_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, * through POSIX ACLs. Such daemons don't expect setgid bits to * be stripped. */ - if (fc->posix_acl && + if (fc->posix_acl && mode == inode->i_mode && !in_group_or_capable(idmap, inode, i_gid_into_vfsgid(idmap, inode))) extra_flags |= FUSE_SETXATTR_ACL_KILL_SGID; @@ -152,6 +167,19 @@ int fuse_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, kfree(value); } else { ret = fuse_removexattr(inode, name); + /* If the acl didn't exist to start with that's fine. */ + if (ret == -ENODATA) + ret = 0; + } + + /* If we scheduled a mode update above, push that to userspace now. */ + if (!ret && mode != inode->i_mode) { + struct iattr attr = { + .ia_valid = ATTR_MODE, + .ia_mode = mode, + }; + + ret = fuse_do_setattr(idmap, dentry, &attr, NULL); } if (fc->posix_acl) {