xref: /kernel/linux/linux-5.10/fs/fuse/acl.c (revision 8c2ecf20)
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
14struct 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
53int 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