1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * FUSE: Filesystem in Userspace 4 * Copyright (C) 2016 Canonical Ltd. <seth.forshee@canonical.com> 5 */ 6 7 #include "fuse_i.h" 8 9 #include <linux/posix_acl.h> 10 #include <linux/posix_acl_xattr.h> 11 12 static struct posix_acl *__fuse_get_acl(struct fuse_conn *fc, 13 struct inode *inode, int type, bool rcu) 14 { 15 int size; 16 const char *name; 17 void *value = NULL; 18 struct posix_acl *acl; 19 20 if (rcu) 21 return ERR_PTR(-ECHILD); 22 23 if (fuse_is_bad(inode)) 24 return ERR_PTR(-EIO); 25 26 if (fc->no_getxattr) 27 return NULL; 28 29 if (type == ACL_TYPE_ACCESS) 30 name = XATTR_NAME_POSIX_ACL_ACCESS; 31 else if (type == ACL_TYPE_DEFAULT) 32 name = XATTR_NAME_POSIX_ACL_DEFAULT; 33 else 34 return ERR_PTR(-EOPNOTSUPP); 35 36 value = kmalloc(PAGE_SIZE, GFP_KERNEL); 37 if (!value) 38 return ERR_PTR(-ENOMEM); 39 size = fuse_getxattr(inode, name, value, PAGE_SIZE); 40 if (size > 0) 41 acl = posix_acl_from_xattr(fc->user_ns, value, size); 42 else if ((size == 0) || (size == -ENODATA) || 43 (size == -EOPNOTSUPP && fc->no_getxattr)) 44 acl = NULL; 45 else if (size == -ERANGE) 46 acl = ERR_PTR(-E2BIG); 47 else 48 acl = ERR_PTR(size); 49 50 kfree(value); 51 return acl; 52 } 53 54 static inline bool fuse_no_acl(const struct fuse_conn *fc, 55 const struct inode *inode) 56 { 57 /* 58 * Refuse interacting with POSIX ACLs for daemons that 59 * don't support FUSE_POSIX_ACL and are not mounted on 60 * the host to retain backwards compatibility. 61 */ 62 return !fc->posix_acl && (i_user_ns(inode) != &init_user_ns); 63 } 64 65 struct posix_acl *fuse_get_acl(struct mnt_idmap *idmap, 66 struct dentry *dentry, int type) 67 { 68 struct inode *inode = d_inode(dentry); 69 struct fuse_conn *fc = get_fuse_conn(inode); 70 71 if (fuse_no_acl(fc, inode)) 72 return ERR_PTR(-EOPNOTSUPP); 73 74 return __fuse_get_acl(fc, inode, type, false); 75 } 76 77 struct posix_acl *fuse_get_inode_acl(struct inode *inode, int type, bool rcu) 78 { 79 struct fuse_conn *fc = get_fuse_conn(inode); 80 81 /* 82 * FUSE daemons before FUSE_POSIX_ACL was introduced could get and set 83 * POSIX ACLs without them being used for permission checking by the 84 * vfs. Retain that behavior for backwards compatibility as there are 85 * filesystems that do all permission checking for acls in the daemon 86 * and not in the kernel. 87 */ 88 if (!fc->posix_acl) 89 return NULL; 90 return __fuse_get_acl(fc, inode, type, rcu); 91 } 92 93 int fuse_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, 94 struct posix_acl *acl, int type) 95 { 96 struct inode *inode = d_inode(dentry); 97 struct fuse_conn *fc = get_fuse_conn(inode); 98 const char *name; 99 int ret; 100 101 if (fuse_is_bad(inode)) 102 return -EIO; 103 104 if (fc->no_setxattr || fuse_no_acl(fc, inode)) 105 return -EOPNOTSUPP; 106 107 if (type == ACL_TYPE_ACCESS) 108 name = XATTR_NAME_POSIX_ACL_ACCESS; 109 else if (type == ACL_TYPE_DEFAULT) 110 name = XATTR_NAME_POSIX_ACL_DEFAULT; 111 else 112 return -EINVAL; 113 114 if (acl) { 115 unsigned int extra_flags = 0; 116 /* 117 * Fuse userspace is responsible for updating access 118 * permissions in the inode, if needed. fuse_setxattr 119 * invalidates the inode attributes, which will force 120 * them to be refreshed the next time they are used, 121 * and it also updates i_ctime. 122 */ 123 size_t size; 124 void *value; 125 126 value = posix_acl_to_xattr(fc->user_ns, acl, &size, GFP_KERNEL); 127 if (!value) 128 return -ENOMEM; 129 130 if (size > PAGE_SIZE) { 131 kfree(value); 132 return -E2BIG; 133 } 134 135 /* 136 * Fuse daemons without FUSE_POSIX_ACL never changed the passed 137 * through POSIX ACLs. Such daemons don't expect setgid bits to 138 * be stripped. 139 */ 140 if (fc->posix_acl && 141 !in_group_or_capable(idmap, inode, 142 i_gid_into_vfsgid(idmap, inode))) 143 extra_flags |= FUSE_SETXATTR_ACL_KILL_SGID; 144 145 ret = fuse_setxattr(inode, name, value, size, 0, extra_flags); 146 kfree(value); 147 } else { 148 ret = fuse_removexattr(inode, name); 149 } 150 151 if (fc->posix_acl) { 152 /* 153 * Fuse daemons without FUSE_POSIX_ACL never cached POSIX ACLs 154 * and didn't invalidate attributes. Retain that behavior. 155 */ 156 forget_all_cached_acls(inode); 157 fuse_invalidate_attr(inode); 158 } 159 160 return ret; 161 } 162