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 /* 75 * Fuse userspace is responsible for updating access 76 * permissions in the inode, if needed. fuse_setxattr 77 * invalidates the inode attributes, which will force 78 * them to be refreshed the next time they are used, 79 * and it also updates i_ctime. 80 */ 81 size_t size = posix_acl_xattr_size(acl->a_count); 82 void *value; 83 84 if (size > PAGE_SIZE) 85 return -E2BIG; 86 87 value = kmalloc(size, GFP_KERNEL); 88 if (!value) 89 return -ENOMEM; 90 91 ret = posix_acl_to_xattr(fc->user_ns, acl, value, size); 92 if (ret < 0) { 93 kfree(value); 94 return ret; 95 } 96 97 ret = fuse_setxattr(inode, name, value, size, 0); 98 kfree(value); 99 } else { 100 ret = fuse_removexattr(inode, name); 101 } 102 forget_all_cached_acls(inode); 103 fuse_invalidate_attr(inode); 104 105 return ret; 106 } 107