projects/linux·blob master · refs

161 lines · 3.9 KiB

1// SPDX-License-Identifier: GPL-2.0
2/*
3 * FUSE: Filesystem in Userspace
4 * Copyright (C) 2016 Canonical Ltd. <[email protected]>
5 */
6
7#include "fuse_i.h"
8
9#include <linux/posix_acl.h>
10#include <linux/posix_acl_xattr.h>
11
12static struct posix_acl *__fuse_get_acl(struct fuse_conn *fc,
13 struct inode *inode, int type, bool rcu)
14{
15 int size;
16 const char *name;
17 void *value = NULL;
18 struct posix_acl *acl;
19
20 if (rcu)
21 return ERR_PTR(-ECHILD);
22
23 if (fuse_is_bad(inode))
24 return ERR_PTR(-EIO);
25
26 if (fc->no_getxattr)
27 return NULL;
28
29 if (type == ACL_TYPE_ACCESS)
30 name = XATTR_NAME_POSIX_ACL_ACCESS;
31 else if (type == ACL_TYPE_DEFAULT)
32 name = XATTR_NAME_POSIX_ACL_DEFAULT;
33 else
34 return ERR_PTR(-EOPNOTSUPP);
35
36 value = kmalloc(PAGE_SIZE, GFP_KERNEL);
37 if (!value)
38 return ERR_PTR(-ENOMEM);
39 size = fuse_getxattr(inode, name, value, PAGE_SIZE);
40 if (size > 0)
41 acl = posix_acl_from_xattr(fc->user_ns, value, size);
42 else if ((size == 0) || (size == -ENODATA) ||
43 (size == -EOPNOTSUPP && fc->no_getxattr))
44 acl = NULL;
45 else if (size == -ERANGE)
46 acl = ERR_PTR(-E2BIG);
47 else
48 acl = ERR_PTR(size);
49
50 kfree(value);
51 return acl;
52}
53
54static inline bool fuse_no_acl(const struct fuse_conn *fc,
55 const struct inode *inode)
56{
57 /*
58 * Refuse interacting with POSIX ACLs for daemons that
59 * don't support FUSE_POSIX_ACL and are not mounted on
60 * the host to retain backwards compatibility.
61 */
62 return !fc->posix_acl && (i_user_ns(inode) != &init_user_ns);
63}
64
65struct posix_acl *fuse_get_acl(const struct mnt_idmap *idmap,
66 struct dentry *dentry, int type)
67{
68 struct inode *inode = d_inode(dentry);
69 struct fuse_conn *fc = get_fuse_conn(inode);
70
71 if (fuse_no_acl(fc, inode))
72 return ERR_PTR(-EOPNOTSUPP);
73
74 return __fuse_get_acl(fc, inode, type, false);
75}
76
77struct posix_acl *fuse_get_inode_acl(struct inode *inode, int type, bool rcu)
78{
79 struct fuse_conn *fc = get_fuse_conn(inode);
80
81 /*
82 * FUSE daemons before FUSE_POSIX_ACL was introduced could get and set
83 * POSIX ACLs without them being used for permission checking by the
84 * vfs. Retain that behavior for backwards compatibility as there are
85 * filesystems that do all permission checking for acls in the daemon
86 * and not in the kernel.
87 */
88 if (!fc->posix_acl)
89 return NULL;
90 return __fuse_get_acl(fc, inode, type, rcu);
91}
92
93int fuse_set_acl(const struct mnt_idmap *idmap, struct dentry *dentry,
94 struct posix_acl *acl, int type)
95{
96 struct inode *inode = d_inode(dentry);
97 struct fuse_conn *fc = get_fuse_conn(inode);
98 const char *name;
99 int ret;
100
101 if (fuse_is_bad(inode))
102 return -EIO;
103
104 if (fc->no_setxattr || fuse_no_acl(fc, inode))
105 return -EOPNOTSUPP;
106
107 if (type == ACL_TYPE_ACCESS)
108 name = XATTR_NAME_POSIX_ACL_ACCESS;
109 else if (type == ACL_TYPE_DEFAULT)
110 name = XATTR_NAME_POSIX_ACL_DEFAULT;
111 else
112 return -EINVAL;
113
114 if (acl) {
115 unsigned int extra_flags = 0;
116 /*
117 * Fuse userspace is responsible for updating access
118 * permissions in the inode, if needed. fuse_setxattr
119 * invalidates the inode attributes, which will force
120 * them to be refreshed the next time they are used,
121 * and it also updates i_ctime.
122 */
123 size_t size;
124 void *value;
125
126 value = posix_acl_to_xattr(fc->user_ns, acl, &size, GFP_KERNEL);
127 if (!value)
128 return -ENOMEM;
129
130 if (size > PAGE_SIZE) {
131 kfree(value);
132 return -E2BIG;
133 }
134
135 /*
136 * Fuse daemons without FUSE_POSIX_ACL never changed the passed
137 * through POSIX ACLs. Such daemons don't expect setgid bits to
138 * be stripped.
139 */
140 if (fc->posix_acl &&
141 !in_group_or_capable(idmap, inode,
142 i_gid_into_vfsgid(idmap, inode)))
143 extra_flags |= FUSE_SETXATTR_ACL_KILL_SGID;
144
145 ret = fuse_setxattr(inode, name, value, size, 0, extra_flags);
146 kfree(value);
147 } else {
148 ret = fuse_removexattr(inode, name);
149 }
150
151 if (fc->posix_acl) {
152 /*
153 * Fuse daemons without FUSE_POSIX_ACL never cached POSIX ACLs
154 * and didn't invalidate attributes. Retain that behavior.
155 */
156 forget_all_cached_acls(inode);
157 fuse_invalidate_attr(inode);
158 }
159
160 return ret;
161}