1 /* 2 * FUSE: Filesystem in Userspace 3 * Copyright (C) 2016 Canonical Ltd. <seth.forshee@canonical.com> 4 * 5 * This program can be distributed under the terms of the GNU GPL. 6 * See the file COPYING. 7 */ 8 9 #include "fuse_i.h" 10 11 #include <linux/posix_acl.h> 12 #include <linux/posix_acl_xattr.h> 13 14 struct posix_acl *fuse_get_acl(struct inode *inode, int type, bool rcu) 15 { 16 struct fuse_conn *fc = get_fuse_conn(inode); 17 int size; 18 const char *name; 19 void *value = NULL; 20 struct posix_acl *acl; 21 22 if (rcu) 23 return ERR_PTR(-ECHILD); 24 25 if (fuse_is_bad(inode)) 26 return ERR_PTR(-EIO); 27 28 if (!fc->posix_acl || fc->no_getxattr) 29 return NULL; 30 31 if (type == ACL_TYPE_ACCESS) 32 name = XATTR_NAME_POSIX_ACL_ACCESS; 33 else if (type == ACL_TYPE_DEFAULT) 34 name = XATTR_NAME_POSIX_ACL_DEFAULT; 35 else 36 return ERR_PTR(-EOPNOTSUPP); 37 38 value = kmalloc(PAGE_SIZE, GFP_KERNEL); 39 if (!value) 40 return ERR_PTR(-ENOMEM); 41 size = fuse_getxattr(inode, name, value, PAGE_SIZE); 42 if (size > 0) 43 acl = posix_acl_from_xattr(fc->user_ns, value, size); 44 else if ((size == 0) || (size == -ENODATA) || 45 (size == -EOPNOTSUPP && fc->no_getxattr)) 46 acl = NULL; 47 else if (size == -ERANGE) 48 acl = ERR_PTR(-E2BIG); 49 else 50 acl = ERR_PTR(size); 51 52 kfree(value); 53 return acl; 54 } 55 56 int fuse_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry, 57 struct posix_acl *acl, int type) 58 { 59 struct inode *inode = d_inode(dentry); 60 struct fuse_conn *fc = get_fuse_conn(inode); 61 const char *name; 62 int ret; 63 64 if (fuse_is_bad(inode)) 65 return -EIO; 66 67 if (!fc->posix_acl || fc->no_setxattr) 68 return -EOPNOTSUPP; 69 70 if (type == ACL_TYPE_ACCESS) 71 name = XATTR_NAME_POSIX_ACL_ACCESS; 72 else if (type == ACL_TYPE_DEFAULT) 73 name = XATTR_NAME_POSIX_ACL_DEFAULT; 74 else 75 return -EINVAL; 76 77 if (acl) { 78 unsigned int extra_flags = 0; 79 /* 80 * Fuse userspace is responsible for updating access 81 * permissions in the inode, if needed. fuse_setxattr 82 * invalidates the inode attributes, which will force 83 * them to be refreshed the next time they are used, 84 * and it also updates i_ctime. 85 */ 86 size_t size = posix_acl_xattr_size(acl->a_count); 87 void *value; 88 89 if (size > PAGE_SIZE) 90 return -E2BIG; 91 92 value = kmalloc(size, GFP_KERNEL); 93 if (!value) 94 return -ENOMEM; 95 96 ret = posix_acl_to_xattr(fc->user_ns, acl, value, size); 97 if (ret < 0) { 98 kfree(value); 99 return ret; 100 } 101 102 if (!vfsgid_in_group_p(i_gid_into_vfsgid(&init_user_ns, inode)) && 103 !capable_wrt_inode_uidgid(&init_user_ns, inode, CAP_FSETID)) 104 extra_flags |= FUSE_SETXATTR_ACL_KILL_SGID; 105 106 ret = fuse_setxattr(inode, name, value, size, 0, extra_flags); 107 kfree(value); 108 } else { 109 ret = fuse_removexattr(inode, name); 110 } 111 forget_all_cached_acls(inode); 112 fuse_invalidate_attr(inode); 113 114 return ret; 115 } 116