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