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 inode *inode, 57 struct posix_acl *acl, int type) 58 { 59 struct fuse_conn *fc = get_fuse_conn(inode); 60 const char *name; 61 int ret; 62 63 if (fuse_is_bad(inode)) 64 return -EIO; 65 66 if (!fc->posix_acl || fc->no_setxattr) 67 return -EOPNOTSUPP; 68 69 if (type == ACL_TYPE_ACCESS) 70 name = XATTR_NAME_POSIX_ACL_ACCESS; 71 else if (type == ACL_TYPE_DEFAULT) 72 name = XATTR_NAME_POSIX_ACL_DEFAULT; 73 else 74 return -EINVAL; 75 76 if (acl) { 77 unsigned int extra_flags = 0; 78 /* 79 * Fuse userspace is responsible for updating access 80 * permissions in the inode, if needed. fuse_setxattr 81 * invalidates the inode attributes, which will force 82 * them to be refreshed the next time they are used, 83 * and it also updates i_ctime. 84 */ 85 size_t size = posix_acl_xattr_size(acl->a_count); 86 void *value; 87 88 if (size > PAGE_SIZE) 89 return -E2BIG; 90 91 value = kmalloc(size, GFP_KERNEL); 92 if (!value) 93 return -ENOMEM; 94 95 ret = posix_acl_to_xattr(fc->user_ns, acl, value, size); 96 if (ret < 0) { 97 kfree(value); 98 return ret; 99 } 100 101 if (!in_group_p(i_gid_into_mnt(&init_user_ns, inode)) && 102 !capable_wrt_inode_uidgid(&init_user_ns, inode, CAP_FSETID)) 103 extra_flags |= FUSE_SETXATTR_ACL_KILL_SGID; 104 105 ret = fuse_setxattr(inode, name, value, size, 0, extra_flags); 106 kfree(value); 107 } else { 108 ret = fuse_removexattr(inode, name); 109 } 110 forget_all_cached_acls(inode); 111 fuse_invalidate_attr(inode); 112 113 return ret; 114 } 115