1 | // SPDX-License-Identifier: LGPL-2.1 |
2 | /* |
3 | * Copyright IBM Corporation, 2010 |
4 | * Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> |
5 | */ |
6 | |
7 | #include <linux/module.h> |
8 | #include <linux/fs.h> |
9 | #include <net/9p/9p.h> |
10 | #include <net/9p/client.h> |
11 | #include <linux/slab.h> |
12 | #include <linux/sched.h> |
13 | #include <linux/posix_acl_xattr.h> |
14 | #include "xattr.h" |
15 | #include "acl.h" |
16 | #include "v9fs.h" |
17 | #include "v9fs_vfs.h" |
18 | #include "fid.h" |
19 | |
20 | static struct posix_acl *v9fs_fid_get_acl(struct p9_fid *fid, const char *name) |
21 | { |
22 | ssize_t size; |
23 | void *value = NULL; |
24 | struct posix_acl *acl = NULL; |
25 | |
26 | size = v9fs_fid_xattr_get(fid, name, NULL, buffer_size: 0); |
27 | if (size < 0) |
28 | return ERR_PTR(error: size); |
29 | if (size == 0) |
30 | return ERR_PTR(error: -ENODATA); |
31 | |
32 | value = kzalloc(size, GFP_NOFS); |
33 | if (!value) |
34 | return ERR_PTR(error: -ENOMEM); |
35 | |
36 | size = v9fs_fid_xattr_get(fid, name, buffer: value, buffer_size: size); |
37 | if (size < 0) |
38 | acl = ERR_PTR(error: size); |
39 | else if (size == 0) |
40 | acl = ERR_PTR(error: -ENODATA); |
41 | else |
42 | acl = posix_acl_from_xattr(user_ns: &init_user_ns, value, size); |
43 | kfree(objp: value); |
44 | return acl; |
45 | } |
46 | |
47 | static struct posix_acl *v9fs_acl_get(struct dentry *dentry, const char *name) |
48 | { |
49 | struct p9_fid *fid; |
50 | struct posix_acl *acl = NULL; |
51 | |
52 | fid = v9fs_fid_lookup(dentry); |
53 | if (IS_ERR(ptr: fid)) |
54 | return ERR_CAST(ptr: fid); |
55 | |
56 | acl = v9fs_fid_get_acl(fid, name); |
57 | p9_fid_put(fid); |
58 | return acl; |
59 | } |
60 | |
61 | static struct posix_acl *__v9fs_get_acl(struct p9_fid *fid, const char *name) |
62 | { |
63 | int retval; |
64 | struct posix_acl *acl = NULL; |
65 | |
66 | acl = v9fs_fid_get_acl(fid, name); |
67 | if (!IS_ERR(ptr: acl)) |
68 | return acl; |
69 | |
70 | retval = PTR_ERR(ptr: acl); |
71 | if (retval == -ENODATA || retval == -ENOSYS || retval == -EOPNOTSUPP) |
72 | return NULL; |
73 | |
74 | /* map everything else to -EIO */ |
75 | return ERR_PTR(error: -EIO); |
76 | } |
77 | |
78 | int v9fs_get_acl(struct inode *inode, struct p9_fid *fid) |
79 | { |
80 | int retval = 0; |
81 | struct posix_acl *pacl, *dacl; |
82 | struct v9fs_session_info *v9ses; |
83 | |
84 | v9ses = v9fs_inode2v9ses(inode); |
85 | if (((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) || |
86 | ((v9ses->flags & V9FS_ACL_MASK) != V9FS_POSIX_ACL)) { |
87 | set_cached_acl(inode, ACL_TYPE_DEFAULT, NULL); |
88 | set_cached_acl(inode, ACL_TYPE_ACCESS, NULL); |
89 | return 0; |
90 | } |
91 | /* get the default/access acl values and cache them */ |
92 | dacl = __v9fs_get_acl(fid, XATTR_NAME_POSIX_ACL_DEFAULT); |
93 | pacl = __v9fs_get_acl(fid, XATTR_NAME_POSIX_ACL_ACCESS); |
94 | |
95 | if (!IS_ERR(ptr: dacl) && !IS_ERR(ptr: pacl)) { |
96 | set_cached_acl(inode, ACL_TYPE_DEFAULT, acl: dacl); |
97 | set_cached_acl(inode, ACL_TYPE_ACCESS, acl: pacl); |
98 | } else |
99 | retval = -EIO; |
100 | |
101 | if (!IS_ERR(ptr: dacl)) |
102 | posix_acl_release(acl: dacl); |
103 | |
104 | if (!IS_ERR(ptr: pacl)) |
105 | posix_acl_release(acl: pacl); |
106 | |
107 | return retval; |
108 | } |
109 | |
110 | static struct posix_acl *v9fs_get_cached_acl(struct inode *inode, int type) |
111 | { |
112 | struct posix_acl *acl; |
113 | /* |
114 | * 9p Always cache the acl value when |
115 | * instantiating the inode (v9fs_inode_from_fid) |
116 | */ |
117 | acl = get_cached_acl(inode, type); |
118 | BUG_ON(is_uncached_acl(acl)); |
119 | return acl; |
120 | } |
121 | |
122 | struct posix_acl *v9fs_iop_get_inode_acl(struct inode *inode, int type, bool rcu) |
123 | { |
124 | struct v9fs_session_info *v9ses; |
125 | |
126 | if (rcu) |
127 | return ERR_PTR(error: -ECHILD); |
128 | |
129 | v9ses = v9fs_inode2v9ses(inode); |
130 | if (((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) || |
131 | ((v9ses->flags & V9FS_ACL_MASK) != V9FS_POSIX_ACL)) { |
132 | /* |
133 | * On access = client and acl = on mode get the acl |
134 | * values from the server |
135 | */ |
136 | return NULL; |
137 | } |
138 | return v9fs_get_cached_acl(inode, type); |
139 | |
140 | } |
141 | |
142 | struct posix_acl *v9fs_iop_get_acl(struct mnt_idmap *idmap, |
143 | struct dentry *dentry, int type) |
144 | { |
145 | struct v9fs_session_info *v9ses; |
146 | |
147 | v9ses = v9fs_dentry2v9ses(dentry); |
148 | /* We allow set/get/list of acl when access=client is not specified. */ |
149 | if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) |
150 | return v9fs_acl_get(dentry, name: posix_acl_xattr_name(type)); |
151 | return v9fs_get_cached_acl(inode: d_inode(dentry), type); |
152 | } |
153 | |
154 | int v9fs_iop_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, |
155 | struct posix_acl *acl, int type) |
156 | { |
157 | int retval; |
158 | size_t size = 0; |
159 | void *value = NULL; |
160 | const char *acl_name; |
161 | struct v9fs_session_info *v9ses; |
162 | struct inode *inode = d_inode(dentry); |
163 | |
164 | if (acl) { |
165 | retval = posix_acl_valid(inode->i_sb->s_user_ns, acl); |
166 | if (retval) |
167 | goto err_out; |
168 | |
169 | size = posix_acl_xattr_size(count: acl->a_count); |
170 | |
171 | value = kzalloc(size, GFP_NOFS); |
172 | if (!value) { |
173 | retval = -ENOMEM; |
174 | goto err_out; |
175 | } |
176 | |
177 | retval = posix_acl_to_xattr(user_ns: &init_user_ns, acl, buffer: value, size); |
178 | if (retval < 0) |
179 | goto err_out; |
180 | } |
181 | |
182 | /* |
183 | * set the attribute on the remote. Without even looking at the |
184 | * xattr value. We leave it to the server to validate |
185 | */ |
186 | acl_name = posix_acl_xattr_name(type); |
187 | v9ses = v9fs_dentry2v9ses(dentry); |
188 | if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) { |
189 | retval = v9fs_xattr_set(dentry, name: acl_name, value, value_len: size, flags: 0); |
190 | goto err_out; |
191 | } |
192 | |
193 | if (S_ISLNK(inode->i_mode)) { |
194 | retval = -EOPNOTSUPP; |
195 | goto err_out; |
196 | } |
197 | |
198 | if (!inode_owner_or_capable(idmap: &nop_mnt_idmap, inode)) { |
199 | retval = -EPERM; |
200 | goto err_out; |
201 | } |
202 | |
203 | switch (type) { |
204 | case ACL_TYPE_ACCESS: |
205 | if (acl) { |
206 | struct iattr iattr = {}; |
207 | struct posix_acl *acl_mode = acl; |
208 | |
209 | retval = posix_acl_update_mode(&nop_mnt_idmap, inode, |
210 | &iattr.ia_mode, |
211 | &acl_mode); |
212 | if (retval) |
213 | goto err_out; |
214 | if (!acl_mode) { |
215 | /* |
216 | * ACL can be represented by the mode bits. |
217 | * So don't update ACL below. |
218 | */ |
219 | kfree(objp: value); |
220 | value = NULL; |
221 | size = 0; |
222 | } |
223 | iattr.ia_valid = ATTR_MODE; |
224 | /* |
225 | * FIXME should we update ctime ? |
226 | * What is the following setxattr update the mode ? |
227 | */ |
228 | v9fs_vfs_setattr_dotl(idmap: &nop_mnt_idmap, dentry, iattr: &iattr); |
229 | } |
230 | break; |
231 | case ACL_TYPE_DEFAULT: |
232 | if (!S_ISDIR(inode->i_mode)) { |
233 | retval = acl ? -EINVAL : 0; |
234 | goto err_out; |
235 | } |
236 | break; |
237 | } |
238 | |
239 | retval = v9fs_xattr_set(dentry, name: acl_name, value, value_len: size, flags: 0); |
240 | if (!retval) |
241 | set_cached_acl(inode, type, acl); |
242 | |
243 | err_out: |
244 | kfree(objp: value); |
245 | return retval; |
246 | } |
247 | |
248 | static int v9fs_set_acl(struct p9_fid *fid, int type, struct posix_acl *acl) |
249 | { |
250 | int retval; |
251 | char *name; |
252 | size_t size; |
253 | void *buffer; |
254 | |
255 | if (!acl) |
256 | return 0; |
257 | |
258 | /* Set a setxattr request to server */ |
259 | size = posix_acl_xattr_size(count: acl->a_count); |
260 | buffer = kmalloc(size, GFP_KERNEL); |
261 | if (!buffer) |
262 | return -ENOMEM; |
263 | retval = posix_acl_to_xattr(user_ns: &init_user_ns, acl, buffer, size); |
264 | if (retval < 0) |
265 | goto err_free_out; |
266 | switch (type) { |
267 | case ACL_TYPE_ACCESS: |
268 | name = XATTR_NAME_POSIX_ACL_ACCESS; |
269 | break; |
270 | case ACL_TYPE_DEFAULT: |
271 | name = XATTR_NAME_POSIX_ACL_DEFAULT; |
272 | break; |
273 | default: |
274 | BUG(); |
275 | } |
276 | retval = v9fs_fid_xattr_set(fid, name, value: buffer, value_len: size, flags: 0); |
277 | err_free_out: |
278 | kfree(objp: buffer); |
279 | return retval; |
280 | } |
281 | |
282 | int v9fs_acl_chmod(struct inode *inode, struct p9_fid *fid) |
283 | { |
284 | int retval = 0; |
285 | struct posix_acl *acl; |
286 | |
287 | if (S_ISLNK(inode->i_mode)) |
288 | return -EOPNOTSUPP; |
289 | acl = v9fs_get_cached_acl(inode, ACL_TYPE_ACCESS); |
290 | if (acl) { |
291 | retval = __posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode); |
292 | if (retval) |
293 | return retval; |
294 | set_cached_acl(inode, ACL_TYPE_ACCESS, acl); |
295 | retval = v9fs_set_acl(fid, ACL_TYPE_ACCESS, acl); |
296 | posix_acl_release(acl); |
297 | } |
298 | return retval; |
299 | } |
300 | |
301 | int v9fs_set_create_acl(struct inode *inode, struct p9_fid *fid, |
302 | struct posix_acl *dacl, struct posix_acl *acl) |
303 | { |
304 | set_cached_acl(inode, ACL_TYPE_DEFAULT, acl: dacl); |
305 | set_cached_acl(inode, ACL_TYPE_ACCESS, acl); |
306 | v9fs_set_acl(fid, ACL_TYPE_DEFAULT, acl: dacl); |
307 | v9fs_set_acl(fid, ACL_TYPE_ACCESS, acl); |
308 | return 0; |
309 | } |
310 | |
311 | void v9fs_put_acl(struct posix_acl *dacl, |
312 | struct posix_acl *acl) |
313 | { |
314 | posix_acl_release(acl: dacl); |
315 | posix_acl_release(acl); |
316 | } |
317 | |
318 | int v9fs_acl_mode(struct inode *dir, umode_t *modep, |
319 | struct posix_acl **dpacl, struct posix_acl **pacl) |
320 | { |
321 | int retval = 0; |
322 | umode_t mode = *modep; |
323 | struct posix_acl *acl = NULL; |
324 | |
325 | if (!S_ISLNK(mode)) { |
326 | acl = v9fs_get_cached_acl(inode: dir, ACL_TYPE_DEFAULT); |
327 | if (IS_ERR(ptr: acl)) |
328 | return PTR_ERR(ptr: acl); |
329 | if (!acl) |
330 | mode &= ~current_umask(); |
331 | } |
332 | if (acl) { |
333 | if (S_ISDIR(mode)) |
334 | *dpacl = posix_acl_dup(acl); |
335 | retval = __posix_acl_create(&acl, GFP_NOFS, &mode); |
336 | if (retval < 0) |
337 | return retval; |
338 | if (retval > 0) |
339 | *pacl = acl; |
340 | else |
341 | posix_acl_release(acl); |
342 | } |
343 | *modep = mode; |
344 | return 0; |
345 | } |
346 | |