]>
Commit | Line | Data |
---|---|---|
024b7d6a | 1 | // SPDX-License-Identifier: LGPL-2.1 |
85ff872d AK |
2 | /* |
3 | * Copyright IBM Corporation, 2010 | |
4 | * Author Aneesh Kumar K.V <[email protected]> | |
85ff872d AK |
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> | |
22d8dcdf | 12 | #include <linux/sched.h> |
85ff872d AK |
13 | #include <linux/posix_acl_xattr.h> |
14 | #include "xattr.h" | |
15 | #include "acl.h" | |
76381a42 | 16 | #include "v9fs.h" |
5ffc0cb3 | 17 | #include "v9fs_vfs.h" |
0f235cae | 18 | #include "fid.h" |
85ff872d | 19 | |
6cd4d4e8 | 20 | static struct posix_acl *v9fs_fid_get_acl(struct p9_fid *fid, const char *name) |
85ff872d AK |
21 | { |
22 | ssize_t size; | |
23 | void *value = NULL; | |
009ca389 | 24 | struct posix_acl *acl = NULL; |
85ff872d AK |
25 | |
26 | size = v9fs_fid_xattr_get(fid, name, NULL, 0); | |
6cd4d4e8 CB |
27 | if (size < 0) |
28 | return ERR_PTR(size); | |
29 | if (size == 0) | |
30 | return ERR_PTR(-ENODATA); | |
31 | ||
32 | value = kzalloc(size, GFP_NOFS); | |
33 | if (!value) | |
34 | return ERR_PTR(-ENOMEM); | |
35 | ||
36 | size = v9fs_fid_xattr_get(fid, name, value, size); | |
37 | if (size < 0) | |
38 | acl = ERR_PTR(size); | |
39 | else if (size == 0) | |
40 | acl = ERR_PTR(-ENODATA); | |
41 | else | |
42 | acl = posix_acl_from_xattr(&init_user_ns, value, size); | |
85ff872d AK |
43 | kfree(value); |
44 | return acl; | |
45 | } | |
46 | ||
6cd4d4e8 CB |
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(fid)) | |
54 | return ERR_CAST(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(acl)) | |
68 | return acl; | |
69 | ||
70 | retval = PTR_ERR(acl); | |
71 | if (retval == -ENODATA || retval == -ENOSYS || retval == -EOPNOTSUPP) | |
72 | return NULL; | |
73 | ||
74 | /* map everything else to -EIO */ | |
75 | return ERR_PTR(-EIO); | |
76 | } | |
77 | ||
85ff872d AK |
78 | int v9fs_get_acl(struct inode *inode, struct p9_fid *fid) |
79 | { | |
80 | int retval = 0; | |
81 | struct posix_acl *pacl, *dacl; | |
76381a42 | 82 | struct v9fs_session_info *v9ses; |
85ff872d | 83 | |
76381a42 | 84 | v9ses = v9fs_inode2v9ses(inode); |
e782ef71 VJJ |
85 | if (((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) || |
86 | ((v9ses->flags & V9FS_ACL_MASK) != V9FS_POSIX_ACL)) { | |
76381a42 AK |
87 | set_cached_acl(inode, ACL_TYPE_DEFAULT, NULL); |
88 | set_cached_acl(inode, ACL_TYPE_ACCESS, NULL); | |
89 | return 0; | |
90 | } | |
85ff872d | 91 | /* get the default/access acl values and cache them */ |
97d79299 AG |
92 | dacl = __v9fs_get_acl(fid, XATTR_NAME_POSIX_ACL_DEFAULT); |
93 | pacl = __v9fs_get_acl(fid, XATTR_NAME_POSIX_ACL_ACCESS); | |
85ff872d AK |
94 | |
95 | if (!IS_ERR(dacl) && !IS_ERR(pacl)) { | |
96 | set_cached_acl(inode, ACL_TYPE_DEFAULT, dacl); | |
97 | set_cached_acl(inode, ACL_TYPE_ACCESS, pacl); | |
85ff872d AK |
98 | } else |
99 | retval = -EIO; | |
100 | ||
c61fa0d6 VJJ |
101 | if (!IS_ERR(dacl)) |
102 | posix_acl_release(dacl); | |
103 | ||
104 | if (!IS_ERR(pacl)) | |
105 | posix_acl_release(pacl); | |
106 | ||
85ff872d AK |
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); | |
b8a7a3a6 | 118 | BUG_ON(is_uncached_acl(acl)); |
85ff872d AK |
119 | return acl; |
120 | } | |
121 | ||
6cd4d4e8 | 122 | struct posix_acl *v9fs_iop_get_inode_acl(struct inode *inode, int type, bool rcu) |
85ff872d | 123 | { |
76381a42 AK |
124 | struct v9fs_session_info *v9ses; |
125 | ||
0cad6246 MS |
126 | if (rcu) |
127 | return ERR_PTR(-ECHILD); | |
128 | ||
76381a42 | 129 | v9ses = v9fs_inode2v9ses(inode); |
e782ef71 VJJ |
130 | if (((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) || |
131 | ((v9ses->flags & V9FS_ACL_MASK) != V9FS_POSIX_ACL)) { | |
76381a42 | 132 | /* |
e782ef71 | 133 | * On access = client and acl = on mode get the acl |
76381a42 AK |
134 | * values from the server |
135 | */ | |
4e34e719 | 136 | return NULL; |
76381a42 | 137 | } |
4e34e719 CH |
138 | return v9fs_get_cached_acl(inode, type); |
139 | ||
85ff872d | 140 | } |
7a4566b0 | 141 | |
77435322 | 142 | struct posix_acl *v9fs_iop_get_acl(struct mnt_idmap *idmap, |
6cd4d4e8 CB |
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, posix_acl_xattr_name(type)); | |
151 | return v9fs_get_cached_acl(d_inode(dentry), type); | |
152 | } | |
153 | ||
13e83a49 | 154 | int v9fs_iop_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, |
079da629 CB |
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(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(&init_user_ns, acl, 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, acl_name, value, size, 0); | |
190 | goto err_out; | |
191 | } | |
192 | ||
193 | if (S_ISLNK(inode->i_mode)) { | |
194 | retval = -EOPNOTSUPP; | |
195 | goto err_out; | |
196 | } | |
197 | ||
01beba79 | 198 | if (!inode_owner_or_capable(&nop_mnt_idmap, inode)) { |
079da629 CB |
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 | ||
700b7940 | 209 | retval = posix_acl_update_mode(&nop_mnt_idmap, inode, |
079da629 CB |
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(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 | */ | |
c1632a0f | 228 | v9fs_vfs_setattr_dotl(&nop_mnt_idmap, dentry, &iattr); |
079da629 CB |
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, acl_name, value, size, 0); | |
240 | if (!retval) | |
241 | set_cached_acl(inode, type, acl); | |
242 | ||
243 | err_out: | |
244 | kfree(value); | |
245 | return retval; | |
246 | } | |
247 | ||
0f235cae | 248 | static int v9fs_set_acl(struct p9_fid *fid, int type, struct posix_acl *acl) |
6e8dc555 AK |
249 | { |
250 | int retval; | |
251 | char *name; | |
252 | size_t size; | |
253 | void *buffer; | |
6e195b0f | 254 | |
d344b0fb VJJ |
255 | if (!acl) |
256 | return 0; | |
257 | ||
6e8dc555 AK |
258 | /* Set a setxattr request to server */ |
259 | size = posix_acl_xattr_size(acl->a_count); | |
260 | buffer = kmalloc(size, GFP_KERNEL); | |
261 | if (!buffer) | |
262 | return -ENOMEM; | |
5f3a4a28 | 263 | retval = posix_acl_to_xattr(&init_user_ns, acl, buffer, size); |
6e8dc555 AK |
264 | if (retval < 0) |
265 | goto err_free_out; | |
266 | switch (type) { | |
267 | case ACL_TYPE_ACCESS: | |
97d79299 | 268 | name = XATTR_NAME_POSIX_ACL_ACCESS; |
6e8dc555 AK |
269 | break; |
270 | case ACL_TYPE_DEFAULT: | |
97d79299 | 271 | name = XATTR_NAME_POSIX_ACL_DEFAULT; |
6e8dc555 AK |
272 | break; |
273 | default: | |
274 | BUG(); | |
275 | } | |
0f235cae | 276 | retval = v9fs_fid_xattr_set(fid, name, buffer, size, 0); |
6e8dc555 AK |
277 | err_free_out: |
278 | kfree(buffer); | |
279 | return retval; | |
280 | } | |
281 | ||
be308f07 | 282 | int v9fs_acl_chmod(struct inode *inode, struct p9_fid *fid) |
6e8dc555 AK |
283 | { |
284 | int retval = 0; | |
bc26ab5f | 285 | struct posix_acl *acl; |
6e8dc555 AK |
286 | |
287 | if (S_ISLNK(inode->i_mode)) | |
288 | return -EOPNOTSUPP; | |
289 | acl = v9fs_get_cached_acl(inode, ACL_TYPE_ACCESS); | |
290 | if (acl) { | |
5bf3258f | 291 | retval = __posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode); |
bc26ab5f AV |
292 | if (retval) |
293 | return retval; | |
7f165aaa | 294 | set_cached_acl(inode, ACL_TYPE_ACCESS, acl); |
0f235cae | 295 | retval = v9fs_set_acl(fid, ACL_TYPE_ACCESS, acl); |
6e8dc555 | 296 | posix_acl_release(acl); |
6e8dc555 AK |
297 | } |
298 | return retval; | |
299 | } | |
300 | ||
3592ac44 | 301 | int v9fs_set_create_acl(struct inode *inode, struct p9_fid *fid, |
5fa6300a | 302 | struct posix_acl *dacl, struct posix_acl *acl) |
ad77dbce | 303 | { |
3592ac44 AV |
304 | set_cached_acl(inode, ACL_TYPE_DEFAULT, dacl); |
305 | set_cached_acl(inode, ACL_TYPE_ACCESS, acl); | |
306 | v9fs_set_acl(fid, ACL_TYPE_DEFAULT, dacl); | |
307 | v9fs_set_acl(fid, ACL_TYPE_ACCESS, acl); | |
ad77dbce AK |
308 | return 0; |
309 | } | |
310 | ||
5fa6300a AV |
311 | void v9fs_put_acl(struct posix_acl *dacl, |
312 | struct posix_acl *acl) | |
313 | { | |
314 | posix_acl_release(dacl); | |
315 | posix_acl_release(acl); | |
316 | } | |
317 | ||
d3fb6120 | 318 | int v9fs_acl_mode(struct inode *dir, umode_t *modep, |
ad77dbce AK |
319 | struct posix_acl **dpacl, struct posix_acl **pacl) |
320 | { | |
321 | int retval = 0; | |
d3fb6120 | 322 | umode_t mode = *modep; |
ad77dbce AK |
323 | struct posix_acl *acl = NULL; |
324 | ||
325 | if (!S_ISLNK(mode)) { | |
326 | acl = v9fs_get_cached_acl(dir, ACL_TYPE_DEFAULT); | |
327 | if (IS_ERR(acl)) | |
328 | return PTR_ERR(acl); | |
329 | if (!acl) | |
330 | mode &= ~current_umask(); | |
331 | } | |
332 | if (acl) { | |
ad77dbce | 333 | if (S_ISDIR(mode)) |
1ec95bf3 | 334 | *dpacl = posix_acl_dup(acl); |
37bc1539 | 335 | retval = __posix_acl_create(&acl, GFP_NOFS, &mode); |
826cae2f AV |
336 | if (retval < 0) |
337 | return retval; | |
ad77dbce | 338 | if (retval > 0) |
826cae2f | 339 | *pacl = acl; |
1ec95bf3 | 340 | else |
826cae2f | 341 | posix_acl_release(acl); |
ad77dbce AK |
342 | } |
343 | *modep = mode; | |
344 | return 0; | |
ad77dbce | 345 | } |