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