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. Note that means that we can setacl and end up with no ACL xattrs, so we also need to tolerate ENODATA returns from fuse_removexattr. 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..63df349dee1caf 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,17 @@ 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. + */ + if (acl && type == ACL_TYPE_ACCESS && 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 +155,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 +164,22 @@ 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) { + struct iattr attr = { }; + + if (mode != inode->i_mode) { + attr.ia_valid |= ATTR_MODE; + attr.ia_mode = mode; + } + + if (attr.ia_valid) + ret = fuse_do_setattr(idmap, dentry, &attr, NULL); } if (fc->posix_acl) {