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) 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 (fuse_is_bad(inode)) 23 return ERR_PTR(-EIO); 24 25 if (!fc->posix_acl || fc->no_getxattr) 26 return NULL; 27 28 if (type == ACL_TYPE_ACCESS) 29 name = XATTR_NAME_POSIX_ACL_ACCESS; 30 else if (type == ACL_TYPE_DEFAULT) 31 name = XATTR_NAME_POSIX_ACL_DEFAULT; 32 else 33 return ERR_PTR(-EOPNOTSUPP); 34 35 value = kmalloc(PAGE_SIZE, GFP_KERNEL); 36 if (!value) 37 return ERR_PTR(-ENOMEM); 38 size = fuse_getxattr(inode, name, value, PAGE_SIZE); 39 if (size > 0) 40 acl = posix_acl_from_xattr(fc->user_ns, value, size); 41 else if ((size == 0) || (size == -ENODATA) || 42 (size == -EOPNOTSUPP && fc->no_getxattr)) 43 acl = NULL; 44 else if (size == -ERANGE) 45 acl = ERR_PTR(-E2BIG); 46 else 47 acl = ERR_PTR(size); 48 49 kfree(value); 50 return acl; 51 } 52 53 int fuse_set_acl(struct user_namespace *mnt_userns, struct inode *inode, 54 struct posix_acl *acl, int type) 55 { 56 struct fuse_conn *fc = get_fuse_conn(inode); 57 const char *name; 58 int ret; 59 60 if (fuse_is_bad(inode)) 61 return -EIO; 62 63 if (!fc->posix_acl || fc->no_setxattr) 64 return -EOPNOTSUPP; 65 66 if (type == ACL_TYPE_ACCESS) 67 name = XATTR_NAME_POSIX_ACL_ACCESS; 68 else if (type == ACL_TYPE_DEFAULT) 69 name = XATTR_NAME_POSIX_ACL_DEFAULT; 70 else 71 return -EINVAL; 72 73 if (acl) { 74 unsigned int extra_flags = 0; 75 /* 76 * Fuse userspace is responsible for updating access 77 * permissions in the inode, if needed. fuse_setxattr 78 * invalidates the inode attributes, which will force 79 * them to be refreshed the next time they are used, 80 * and it also updates i_ctime. 81 */ 82 size_t size = posix_acl_xattr_size(acl->a_count); 83 void *value; 84 85 if (size > PAGE_SIZE) 86 return -E2BIG; 87 88 value = kmalloc(size, GFP_KERNEL); 89 if (!value) 90 return -ENOMEM; 91 92 ret = posix_acl_to_xattr(fc->user_ns, acl, value, size); 93 if (ret < 0) { 94 kfree(value); 95 return ret; 96 } 97 98 if (!in_group_p(i_gid_into_mnt(&init_user_ns, inode)) && 99 !capable_wrt_inode_uidgid(&init_user_ns, inode, CAP_FSETID)) 100 extra_flags |= FUSE_SETXATTR_ACL_KILL_SGID; 101 102 ret = fuse_setxattr(inode, name, value, size, 0, extra_flags); 103 kfree(value); 104 } else { 105 ret = fuse_removexattr(inode, name); 106 } 107 forget_all_cached_acls(inode); 108 fuse_invalidate_attr(inode); 109 110 return ret; 111 } 112