]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * fs/nfs4acl/acl.c | |
3 | * | |
4 | * Common NFSv4 ACL handling code. | |
5 | * | |
6 | * Copyright (c) 2002, 2003 The Regents of the University of Michigan. | |
7 | * All rights reserved. | |
8 | * | |
9 | * Marius Aamodt Eriksen <marius@umich.edu> | |
10 | * Jeff Sedlak <jsedlak@umich.edu> | |
11 | * J. Bruce Fields <bfields@umich.edu> | |
12 | * | |
13 | * Redistribution and use in source and binary forms, with or without | |
14 | * modification, are permitted provided that the following conditions | |
15 | * are met: | |
16 | * | |
17 | * 1. Redistributions of source code must retain the above copyright | |
18 | * notice, this list of conditions and the following disclaimer. | |
19 | * 2. Redistributions in binary form must reproduce the above copyright | |
20 | * notice, this list of conditions and the following disclaimer in the | |
21 | * documentation and/or other materials provided with the distribution. | |
22 | * 3. Neither the name of the University nor the names of its | |
23 | * contributors may be used to endorse or promote products derived | |
24 | * from this software without specific prior written permission. | |
25 | * | |
26 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | |
27 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
28 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
29 | * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
30 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
31 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
32 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR | |
33 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
34 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
35 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
36 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
37 | */ | |
38 | ||
39 | #include <linux/string.h> | |
40 | #include <linux/slab.h> | |
41 | #include <linux/list.h> | |
42 | #include <linux/types.h> | |
43 | #include <linux/fs.h> | |
44 | #include <linux/module.h> | |
45 | #include <linux/nfs_fs.h> | |
46 | #include <linux/posix_acl.h> | |
47 | #include <linux/nfs4.h> | |
48 | #include <linux/nfs4_acl.h> | |
49 | ||
50 | ||
51 | /* mode bit translations: */ | |
52 | #define NFS4_READ_MODE (NFS4_ACE_READ_DATA) | |
53 | #define NFS4_WRITE_MODE (NFS4_ACE_WRITE_DATA | NFS4_ACE_APPEND_DATA) | |
54 | #define NFS4_EXECUTE_MODE NFS4_ACE_EXECUTE | |
55 | #define NFS4_ANYONE_MODE (NFS4_ACE_READ_ATTRIBUTES | NFS4_ACE_READ_ACL | NFS4_ACE_SYNCHRONIZE) | |
56 | #define NFS4_OWNER_MODE (NFS4_ACE_WRITE_ATTRIBUTES | NFS4_ACE_WRITE_ACL) | |
57 | ||
58 | /* We don't support these bits; insist they be neither allowed nor denied */ | |
59 | #define NFS4_MASK_UNSUPP (NFS4_ACE_DELETE | NFS4_ACE_WRITE_OWNER \ | |
60 | | NFS4_ACE_READ_NAMED_ATTRS | NFS4_ACE_WRITE_NAMED_ATTRS) | |
61 | ||
62 | /* flags used to simulate posix default ACLs */ | |
63 | #define NFS4_INHERITANCE_FLAGS (NFS4_ACE_FILE_INHERIT_ACE \ | |
64 | | NFS4_ACE_DIRECTORY_INHERIT_ACE | NFS4_ACE_INHERIT_ONLY_ACE) | |
65 | ||
66 | #define MASK_EQUAL(mask1, mask2) \ | |
67 | ( ((mask1) & NFS4_ACE_MASK_ALL) == ((mask2) & NFS4_ACE_MASK_ALL) ) | |
68 | ||
69 | static u32 | |
70 | mask_from_posix(unsigned short perm, unsigned int flags) | |
71 | { | |
72 | int mask = NFS4_ANYONE_MODE; | |
73 | ||
74 | if (flags & NFS4_ACL_OWNER) | |
75 | mask |= NFS4_OWNER_MODE; | |
76 | if (perm & ACL_READ) | |
77 | mask |= NFS4_READ_MODE; | |
78 | if (perm & ACL_WRITE) | |
79 | mask |= NFS4_WRITE_MODE; | |
80 | if ((perm & ACL_WRITE) && (flags & NFS4_ACL_DIR)) | |
81 | mask |= NFS4_ACE_DELETE_CHILD; | |
82 | if (perm & ACL_EXECUTE) | |
83 | mask |= NFS4_EXECUTE_MODE; | |
84 | return mask; | |
85 | } | |
86 | ||
87 | static u32 | |
88 | deny_mask(u32 allow_mask, unsigned int flags) | |
89 | { | |
90 | u32 ret = ~allow_mask & ~NFS4_MASK_UNSUPP; | |
91 | if (!(flags & NFS4_ACL_DIR)) | |
92 | ret &= ~NFS4_ACE_DELETE_CHILD; | |
93 | return ret; | |
94 | } | |
95 | ||
96 | /* XXX: modify functions to return NFS errors; they're only ever | |
97 | * used by nfs code, after all.... */ | |
98 | ||
99 | static int | |
100 | mode_from_nfs4(u32 perm, unsigned short *mode, unsigned int flags) | |
101 | { | |
102 | u32 ignore = 0; | |
103 | ||
104 | if (!(flags & NFS4_ACL_DIR)) | |
105 | ignore |= NFS4_ACE_DELETE_CHILD; /* ignore it */ | |
106 | perm |= ignore; | |
107 | *mode = 0; | |
108 | if ((perm & NFS4_READ_MODE) == NFS4_READ_MODE) | |
109 | *mode |= ACL_READ; | |
110 | if ((perm & NFS4_WRITE_MODE) == NFS4_WRITE_MODE) | |
111 | *mode |= ACL_WRITE; | |
112 | if ((perm & NFS4_EXECUTE_MODE) == NFS4_EXECUTE_MODE) | |
113 | *mode |= ACL_EXECUTE; | |
114 | if (!MASK_EQUAL(perm, ignore|mask_from_posix(*mode, flags))) | |
115 | return -EINVAL; | |
116 | return 0; | |
117 | } | |
118 | ||
119 | struct ace_container { | |
120 | struct nfs4_ace *ace; | |
121 | struct list_head ace_l; | |
122 | }; | |
123 | ||
124 | static short ace2type(struct nfs4_ace *); | |
125 | static int _posix_to_nfsv4_one(struct posix_acl *, struct nfs4_acl *, unsigned int); | |
126 | static struct posix_acl *_nfsv4_to_posix_one(struct nfs4_acl *, unsigned int); | |
127 | int nfs4_acl_add_ace(struct nfs4_acl *, u32, u32, u32, int, uid_t); | |
fd39ca9a | 128 | static int nfs4_acl_split(struct nfs4_acl *, struct nfs4_acl *); |
1da177e4 LT |
129 | |
130 | struct nfs4_acl * | |
131 | nfs4_acl_posix_to_nfsv4(struct posix_acl *pacl, struct posix_acl *dpacl, | |
132 | unsigned int flags) | |
133 | { | |
134 | struct nfs4_acl *acl; | |
135 | int error = -EINVAL; | |
136 | ||
137 | if ((pacl != NULL && | |
138 | (posix_acl_valid(pacl) < 0 || pacl->a_count == 0)) || | |
139 | (dpacl != NULL && | |
140 | (posix_acl_valid(dpacl) < 0 || dpacl->a_count == 0))) | |
141 | goto out_err; | |
142 | ||
143 | acl = nfs4_acl_new(); | |
144 | if (acl == NULL) { | |
145 | error = -ENOMEM; | |
146 | goto out_err; | |
147 | } | |
148 | ||
149 | if (pacl != NULL) { | |
150 | error = _posix_to_nfsv4_one(pacl, acl, | |
151 | flags & ~NFS4_ACL_TYPE_DEFAULT); | |
152 | if (error < 0) | |
153 | goto out_acl; | |
154 | } | |
155 | ||
156 | if (dpacl != NULL) { | |
157 | error = _posix_to_nfsv4_one(dpacl, acl, | |
158 | flags | NFS4_ACL_TYPE_DEFAULT); | |
159 | if (error < 0) | |
160 | goto out_acl; | |
161 | } | |
162 | ||
163 | return acl; | |
164 | ||
165 | out_acl: | |
166 | nfs4_acl_free(acl); | |
167 | out_err: | |
168 | acl = ERR_PTR(error); | |
169 | ||
170 | return acl; | |
171 | } | |
172 | ||
173 | static int | |
174 | nfs4_acl_add_pair(struct nfs4_acl *acl, int eflag, u32 mask, int whotype, | |
175 | uid_t owner, unsigned int flags) | |
176 | { | |
177 | int error; | |
178 | ||
179 | error = nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE, | |
180 | eflag, mask, whotype, owner); | |
181 | if (error < 0) | |
182 | return error; | |
183 | error = nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_DENIED_ACE_TYPE, | |
184 | eflag, deny_mask(mask, flags), whotype, owner); | |
185 | return error; | |
186 | } | |
187 | ||
188 | /* We assume the acl has been verified with posix_acl_valid. */ | |
189 | static int | |
190 | _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl, | |
191 | unsigned int flags) | |
192 | { | |
193 | struct posix_acl_entry *pa, *pe, *group_owner_entry; | |
194 | int error = -EINVAL; | |
195 | u32 mask, mask_mask; | |
196 | int eflag = ((flags & NFS4_ACL_TYPE_DEFAULT) ? | |
197 | NFS4_INHERITANCE_FLAGS : 0); | |
198 | ||
199 | BUG_ON(pacl->a_count < 3); | |
200 | pe = pacl->a_entries + pacl->a_count; | |
201 | pa = pe - 2; /* if mask entry exists, it's second from the last. */ | |
202 | if (pa->e_tag == ACL_MASK) | |
203 | mask_mask = deny_mask(mask_from_posix(pa->e_perm, flags), flags); | |
204 | else | |
205 | mask_mask = 0; | |
206 | ||
207 | pa = pacl->a_entries; | |
208 | BUG_ON(pa->e_tag != ACL_USER_OBJ); | |
209 | mask = mask_from_posix(pa->e_perm, flags | NFS4_ACL_OWNER); | |
210 | error = nfs4_acl_add_pair(acl, eflag, mask, NFS4_ACL_WHO_OWNER, 0, flags); | |
211 | if (error < 0) | |
212 | goto out; | |
213 | pa++; | |
214 | ||
215 | while (pa->e_tag == ACL_USER) { | |
216 | mask = mask_from_posix(pa->e_perm, flags); | |
217 | error = nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_DENIED_ACE_TYPE, | |
218 | eflag, mask_mask, NFS4_ACL_WHO_NAMED, pa->e_id); | |
219 | if (error < 0) | |
220 | goto out; | |
221 | ||
222 | ||
223 | error = nfs4_acl_add_pair(acl, eflag, mask, | |
224 | NFS4_ACL_WHO_NAMED, pa->e_id, flags); | |
225 | if (error < 0) | |
226 | goto out; | |
227 | pa++; | |
228 | } | |
229 | ||
230 | /* In the case of groups, we apply allow ACEs first, then deny ACEs, | |
231 | * since a user can be in more than one group. */ | |
232 | ||
233 | /* allow ACEs */ | |
234 | ||
235 | if (pacl->a_count > 3) { | |
236 | BUG_ON(pa->e_tag != ACL_GROUP_OBJ); | |
237 | error = nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_DENIED_ACE_TYPE, | |
238 | NFS4_ACE_IDENTIFIER_GROUP | eflag, mask_mask, | |
239 | NFS4_ACL_WHO_GROUP, 0); | |
240 | if (error < 0) | |
241 | goto out; | |
242 | } | |
243 | group_owner_entry = pa; | |
244 | mask = mask_from_posix(pa->e_perm, flags); | |
245 | error = nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE, | |
246 | NFS4_ACE_IDENTIFIER_GROUP | eflag, mask, | |
247 | NFS4_ACL_WHO_GROUP, 0); | |
248 | if (error < 0) | |
249 | goto out; | |
250 | pa++; | |
251 | ||
252 | while (pa->e_tag == ACL_GROUP) { | |
253 | mask = mask_from_posix(pa->e_perm, flags); | |
254 | error = nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_DENIED_ACE_TYPE, | |
255 | NFS4_ACE_IDENTIFIER_GROUP | eflag, mask_mask, | |
256 | NFS4_ACL_WHO_NAMED, pa->e_id); | |
257 | if (error < 0) | |
258 | goto out; | |
259 | ||
260 | error = nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE, | |
261 | NFS4_ACE_IDENTIFIER_GROUP | eflag, mask, | |
262 | NFS4_ACL_WHO_NAMED, pa->e_id); | |
263 | if (error < 0) | |
264 | goto out; | |
265 | pa++; | |
266 | } | |
267 | ||
268 | /* deny ACEs */ | |
269 | ||
270 | pa = group_owner_entry; | |
271 | mask = mask_from_posix(pa->e_perm, flags); | |
272 | error = nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_DENIED_ACE_TYPE, | |
273 | NFS4_ACE_IDENTIFIER_GROUP | eflag, | |
274 | deny_mask(mask, flags), NFS4_ACL_WHO_GROUP, 0); | |
275 | if (error < 0) | |
276 | goto out; | |
277 | pa++; | |
278 | while (pa->e_tag == ACL_GROUP) { | |
279 | mask = mask_from_posix(pa->e_perm, flags); | |
280 | error = nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_DENIED_ACE_TYPE, | |
281 | NFS4_ACE_IDENTIFIER_GROUP | eflag, | |
282 | deny_mask(mask, flags), NFS4_ACL_WHO_NAMED, pa->e_id); | |
283 | if (error < 0) | |
284 | goto out; | |
285 | pa++; | |
286 | } | |
287 | ||
288 | if (pa->e_tag == ACL_MASK) | |
289 | pa++; | |
290 | BUG_ON(pa->e_tag != ACL_OTHER); | |
291 | mask = mask_from_posix(pa->e_perm, flags); | |
292 | error = nfs4_acl_add_pair(acl, eflag, mask, NFS4_ACL_WHO_EVERYONE, 0, flags); | |
293 | ||
294 | out: | |
295 | return error; | |
296 | } | |
297 | ||
298 | static void | |
299 | sort_pacl_range(struct posix_acl *pacl, int start, int end) { | |
300 | int sorted = 0, i; | |
301 | struct posix_acl_entry tmp; | |
302 | ||
303 | /* We just do a bubble sort; easy to do in place, and we're not | |
304 | * expecting acl's to be long enough to justify anything more. */ | |
305 | while (!sorted) { | |
306 | sorted = 1; | |
307 | for (i = start; i < end; i++) { | |
308 | if (pacl->a_entries[i].e_id | |
309 | > pacl->a_entries[i+1].e_id) { | |
310 | sorted = 0; | |
311 | tmp = pacl->a_entries[i]; | |
312 | pacl->a_entries[i] = pacl->a_entries[i+1]; | |
313 | pacl->a_entries[i+1] = tmp; | |
314 | } | |
315 | } | |
316 | } | |
317 | } | |
318 | ||
319 | static void | |
320 | sort_pacl(struct posix_acl *pacl) | |
321 | { | |
322 | /* posix_acl_valid requires that users and groups be in order | |
323 | * by uid/gid. */ | |
324 | int i, j; | |
325 | ||
326 | if (pacl->a_count <= 4) | |
327 | return; /* no users or groups */ | |
328 | i = 1; | |
329 | while (pacl->a_entries[i].e_tag == ACL_USER) | |
330 | i++; | |
331 | sort_pacl_range(pacl, 1, i-1); | |
332 | ||
333 | BUG_ON(pacl->a_entries[i].e_tag != ACL_GROUP_OBJ); | |
334 | j = i++; | |
335 | while (pacl->a_entries[j].e_tag == ACL_GROUP) | |
336 | j++; | |
337 | sort_pacl_range(pacl, i, j-1); | |
338 | return; | |
339 | } | |
340 | ||
341 | static int | |
342 | write_pace(struct nfs4_ace *ace, struct posix_acl *pacl, | |
343 | struct posix_acl_entry **pace, short tag, unsigned int flags) | |
344 | { | |
345 | struct posix_acl_entry *this = *pace; | |
346 | ||
347 | if (*pace == pacl->a_entries + pacl->a_count) | |
348 | return -EINVAL; /* fell off the end */ | |
349 | (*pace)++; | |
350 | this->e_tag = tag; | |
351 | if (tag == ACL_USER_OBJ) | |
352 | flags |= NFS4_ACL_OWNER; | |
353 | if (mode_from_nfs4(ace->access_mask, &this->e_perm, flags)) | |
354 | return -EINVAL; | |
355 | this->e_id = (tag == ACL_USER || tag == ACL_GROUP ? | |
356 | ace->who : ACL_UNDEFINED_ID); | |
357 | return 0; | |
358 | } | |
359 | ||
360 | static struct nfs4_ace * | |
361 | get_next_v4_ace(struct list_head **p, struct list_head *head) | |
362 | { | |
363 | struct nfs4_ace *ace; | |
364 | ||
365 | *p = (*p)->next; | |
366 | if (*p == head) | |
367 | return NULL; | |
368 | ace = list_entry(*p, struct nfs4_ace, l_ace); | |
369 | ||
370 | return ace; | |
371 | } | |
372 | ||
373 | int | |
374 | nfs4_acl_nfsv4_to_posix(struct nfs4_acl *acl, struct posix_acl **pacl, | |
375 | struct posix_acl **dpacl, unsigned int flags) | |
376 | { | |
377 | struct nfs4_acl *dacl; | |
378 | int error = -ENOMEM; | |
379 | ||
380 | *pacl = NULL; | |
381 | *dpacl = NULL; | |
382 | ||
383 | dacl = nfs4_acl_new(); | |
384 | if (dacl == NULL) | |
385 | goto out; | |
386 | ||
387 | error = nfs4_acl_split(acl, dacl); | |
388 | if (error < 0) | |
389 | goto out_acl; | |
390 | ||
391 | if (pacl != NULL) { | |
392 | if (acl->naces == 0) { | |
393 | error = -ENODATA; | |
394 | goto try_dpacl; | |
395 | } | |
396 | ||
397 | *pacl = _nfsv4_to_posix_one(acl, flags); | |
398 | if (IS_ERR(*pacl)) { | |
399 | error = PTR_ERR(*pacl); | |
400 | *pacl = NULL; | |
401 | goto out_acl; | |
402 | } | |
403 | } | |
404 | ||
405 | try_dpacl: | |
406 | if (dpacl != NULL) { | |
407 | if (dacl->naces == 0) { | |
408 | if (pacl == NULL || *pacl == NULL) | |
409 | error = -ENODATA; | |
410 | goto out_acl; | |
411 | } | |
412 | ||
413 | error = 0; | |
414 | *dpacl = _nfsv4_to_posix_one(dacl, flags); | |
415 | if (IS_ERR(*dpacl)) { | |
416 | error = PTR_ERR(*dpacl); | |
417 | *dpacl = NULL; | |
418 | goto out_acl; | |
419 | } | |
420 | } | |
421 | ||
422 | out_acl: | |
423 | if (error && pacl) { | |
424 | posix_acl_release(*pacl); | |
425 | *pacl = NULL; | |
426 | } | |
427 | nfs4_acl_free(dacl); | |
428 | out: | |
429 | return error; | |
430 | } | |
431 | ||
432 | static int | |
433 | same_who(struct nfs4_ace *a, struct nfs4_ace *b) | |
434 | { | |
435 | return a->whotype == b->whotype && | |
436 | (a->whotype != NFS4_ACL_WHO_NAMED || a->who == b->who); | |
437 | } | |
438 | ||
439 | static int | |
440 | complementary_ace_pair(struct nfs4_ace *allow, struct nfs4_ace *deny, | |
441 | unsigned int flags) | |
442 | { | |
443 | int ignore = 0; | |
444 | if (!(flags & NFS4_ACL_DIR)) | |
445 | ignore |= NFS4_ACE_DELETE_CHILD; | |
446 | return MASK_EQUAL(ignore|deny_mask(allow->access_mask, flags), | |
447 | ignore|deny->access_mask) && | |
448 | allow->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE && | |
449 | deny->type == NFS4_ACE_ACCESS_DENIED_ACE_TYPE && | |
450 | allow->flag == deny->flag && | |
451 | same_who(allow, deny); | |
452 | } | |
453 | ||
454 | static inline int | |
455 | user_obj_from_v4(struct nfs4_acl *n4acl, struct list_head **p, | |
456 | struct posix_acl *pacl, struct posix_acl_entry **pace, | |
457 | unsigned int flags) | |
458 | { | |
459 | int error = -EINVAL; | |
460 | struct nfs4_ace *ace, *ace2; | |
461 | ||
462 | ace = get_next_v4_ace(p, &n4acl->ace_head); | |
463 | if (ace == NULL) | |
464 | goto out; | |
465 | if (ace2type(ace) != ACL_USER_OBJ) | |
466 | goto out; | |
467 | error = write_pace(ace, pacl, pace, ACL_USER_OBJ, flags); | |
468 | if (error < 0) | |
469 | goto out; | |
470 | error = -EINVAL; | |
471 | ace2 = get_next_v4_ace(p, &n4acl->ace_head); | |
472 | if (ace2 == NULL) | |
473 | goto out; | |
474 | if (!complementary_ace_pair(ace, ace2, flags)) | |
475 | goto out; | |
476 | error = 0; | |
477 | out: | |
478 | return error; | |
479 | } | |
480 | ||
481 | static inline int | |
482 | users_from_v4(struct nfs4_acl *n4acl, struct list_head **p, | |
483 | struct nfs4_ace **mask_ace, | |
484 | struct posix_acl *pacl, struct posix_acl_entry **pace, | |
485 | unsigned int flags) | |
486 | { | |
487 | int error = -EINVAL; | |
488 | struct nfs4_ace *ace, *ace2; | |
489 | ||
490 | ace = get_next_v4_ace(p, &n4acl->ace_head); | |
491 | if (ace == NULL) | |
492 | goto out; | |
493 | while (ace2type(ace) == ACL_USER) { | |
494 | if (ace->type != NFS4_ACE_ACCESS_DENIED_ACE_TYPE) | |
495 | goto out; | |
496 | if (*mask_ace && | |
497 | !MASK_EQUAL(ace->access_mask, (*mask_ace)->access_mask)) | |
498 | goto out; | |
499 | *mask_ace = ace; | |
500 | ace = get_next_v4_ace(p, &n4acl->ace_head); | |
501 | if (ace == NULL) | |
502 | goto out; | |
503 | if (ace->type != NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) | |
504 | goto out; | |
505 | error = write_pace(ace, pacl, pace, ACL_USER, flags); | |
506 | if (error < 0) | |
507 | goto out; | |
508 | error = -EINVAL; | |
509 | ace2 = get_next_v4_ace(p, &n4acl->ace_head); | |
510 | if (ace2 == NULL) | |
511 | goto out; | |
512 | if (!complementary_ace_pair(ace, ace2, flags)) | |
513 | goto out; | |
514 | if ((*mask_ace)->flag != ace2->flag || | |
515 | !same_who(*mask_ace, ace2)) | |
516 | goto out; | |
517 | ace = get_next_v4_ace(p, &n4acl->ace_head); | |
518 | if (ace == NULL) | |
519 | goto out; | |
520 | } | |
521 | error = 0; | |
522 | out: | |
523 | return error; | |
524 | } | |
525 | ||
526 | static inline int | |
527 | group_obj_and_groups_from_v4(struct nfs4_acl *n4acl, struct list_head **p, | |
528 | struct nfs4_ace **mask_ace, | |
529 | struct posix_acl *pacl, struct posix_acl_entry **pace, | |
530 | unsigned int flags) | |
531 | { | |
532 | int error = -EINVAL; | |
533 | struct nfs4_ace *ace, *ace2; | |
534 | struct ace_container *ac; | |
535 | struct list_head group_l; | |
536 | ||
537 | INIT_LIST_HEAD(&group_l); | |
538 | ace = list_entry(*p, struct nfs4_ace, l_ace); | |
539 | ||
540 | /* group owner (mask and allow aces) */ | |
541 | ||
542 | if (pacl->a_count != 3) { | |
543 | /* then the group owner should be preceded by mask */ | |
544 | if (ace->type != NFS4_ACE_ACCESS_DENIED_ACE_TYPE) | |
545 | goto out; | |
546 | if (*mask_ace && | |
547 | !MASK_EQUAL(ace->access_mask, (*mask_ace)->access_mask)) | |
548 | goto out; | |
549 | *mask_ace = ace; | |
550 | ace = get_next_v4_ace(p, &n4acl->ace_head); | |
551 | if (ace == NULL) | |
552 | goto out; | |
553 | ||
554 | if ((*mask_ace)->flag != ace->flag || !same_who(*mask_ace, ace)) | |
555 | goto out; | |
556 | } | |
557 | ||
558 | if (ace2type(ace) != ACL_GROUP_OBJ) | |
559 | goto out; | |
560 | ||
561 | ac = kmalloc(sizeof(*ac), GFP_KERNEL); | |
562 | error = -ENOMEM; | |
563 | if (ac == NULL) | |
564 | goto out; | |
565 | ac->ace = ace; | |
566 | list_add_tail(&ac->ace_l, &group_l); | |
567 | ||
568 | error = -EINVAL; | |
569 | if (ace->type != NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) | |
570 | goto out; | |
571 | ||
572 | error = write_pace(ace, pacl, pace, ACL_GROUP_OBJ, flags); | |
573 | if (error < 0) | |
574 | goto out; | |
575 | ||
576 | error = -EINVAL; | |
577 | ace = get_next_v4_ace(p, &n4acl->ace_head); | |
578 | if (ace == NULL) | |
579 | goto out; | |
580 | ||
581 | /* groups (mask and allow aces) */ | |
582 | ||
583 | while (ace2type(ace) == ACL_GROUP) { | |
584 | if (*mask_ace == NULL) | |
585 | goto out; | |
586 | ||
587 | if (ace->type != NFS4_ACE_ACCESS_DENIED_ACE_TYPE || | |
588 | !MASK_EQUAL(ace->access_mask, (*mask_ace)->access_mask)) | |
589 | goto out; | |
590 | *mask_ace = ace; | |
591 | ||
592 | ace = get_next_v4_ace(p, &n4acl->ace_head); | |
593 | if (ace == NULL) | |
594 | goto out; | |
595 | ac = kmalloc(sizeof(*ac), GFP_KERNEL); | |
596 | error = -ENOMEM; | |
597 | if (ac == NULL) | |
598 | goto out; | |
599 | error = -EINVAL; | |
600 | if (ace->type != NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE || | |
601 | !same_who(ace, *mask_ace)) | |
602 | goto out; | |
603 | ||
604 | ac->ace = ace; | |
605 | list_add_tail(&ac->ace_l, &group_l); | |
606 | ||
607 | error = write_pace(ace, pacl, pace, ACL_GROUP, flags); | |
608 | if (error < 0) | |
609 | goto out; | |
610 | error = -EINVAL; | |
611 | ace = get_next_v4_ace(p, &n4acl->ace_head); | |
612 | if (ace == NULL) | |
613 | goto out; | |
614 | } | |
615 | ||
616 | /* group owner (deny ace) */ | |
617 | ||
618 | if (ace2type(ace) != ACL_GROUP_OBJ) | |
619 | goto out; | |
620 | ac = list_entry(group_l.next, struct ace_container, ace_l); | |
621 | ace2 = ac->ace; | |
622 | if (!complementary_ace_pair(ace2, ace, flags)) | |
623 | goto out; | |
624 | list_del(group_l.next); | |
625 | kfree(ac); | |
626 | ||
627 | /* groups (deny aces) */ | |
628 | ||
629 | while (!list_empty(&group_l)) { | |
630 | ace = get_next_v4_ace(p, &n4acl->ace_head); | |
631 | if (ace == NULL) | |
632 | goto out; | |
633 | if (ace2type(ace) != ACL_GROUP) | |
634 | goto out; | |
635 | ac = list_entry(group_l.next, struct ace_container, ace_l); | |
636 | ace2 = ac->ace; | |
637 | if (!complementary_ace_pair(ace2, ace, flags)) | |
638 | goto out; | |
639 | list_del(group_l.next); | |
640 | kfree(ac); | |
641 | } | |
642 | ||
643 | ace = get_next_v4_ace(p, &n4acl->ace_head); | |
644 | if (ace == NULL) | |
645 | goto out; | |
646 | if (ace2type(ace) != ACL_OTHER) | |
647 | goto out; | |
648 | error = 0; | |
649 | out: | |
650 | while (!list_empty(&group_l)) { | |
651 | ac = list_entry(group_l.next, struct ace_container, ace_l); | |
652 | list_del(group_l.next); | |
653 | kfree(ac); | |
654 | } | |
655 | return error; | |
656 | } | |
657 | ||
658 | static inline int | |
659 | mask_from_v4(struct nfs4_acl *n4acl, struct list_head **p, | |
660 | struct nfs4_ace **mask_ace, | |
661 | struct posix_acl *pacl, struct posix_acl_entry **pace, | |
662 | unsigned int flags) | |
663 | { | |
664 | int error = -EINVAL; | |
665 | struct nfs4_ace *ace; | |
666 | ||
667 | ace = list_entry(*p, struct nfs4_ace, l_ace); | |
668 | if (pacl->a_count != 3) { | |
669 | if (*mask_ace == NULL) | |
670 | goto out; | |
671 | (*mask_ace)->access_mask = deny_mask((*mask_ace)->access_mask, flags); | |
672 | write_pace(*mask_ace, pacl, pace, ACL_MASK, flags); | |
673 | } | |
674 | error = 0; | |
675 | out: | |
676 | return error; | |
677 | } | |
678 | ||
679 | static inline int | |
680 | other_from_v4(struct nfs4_acl *n4acl, struct list_head **p, | |
681 | struct posix_acl *pacl, struct posix_acl_entry **pace, | |
682 | unsigned int flags) | |
683 | { | |
684 | int error = -EINVAL; | |
685 | struct nfs4_ace *ace, *ace2; | |
686 | ||
687 | ace = list_entry(*p, struct nfs4_ace, l_ace); | |
688 | if (ace->type != NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) | |
689 | goto out; | |
690 | error = write_pace(ace, pacl, pace, ACL_OTHER, flags); | |
691 | if (error < 0) | |
692 | goto out; | |
693 | error = -EINVAL; | |
694 | ace2 = get_next_v4_ace(p, &n4acl->ace_head); | |
695 | if (ace2 == NULL) | |
696 | goto out; | |
697 | if (!complementary_ace_pair(ace, ace2, flags)) | |
698 | goto out; | |
699 | error = 0; | |
700 | out: | |
701 | return error; | |
702 | } | |
703 | ||
704 | static int | |
705 | calculate_posix_ace_count(struct nfs4_acl *n4acl) | |
706 | { | |
707 | if (n4acl->naces == 6) /* owner, owner group, and other only */ | |
708 | return 3; | |
709 | else { /* Otherwise there must be a mask entry. */ | |
710 | /* Also, the remaining entries are for named users and | |
711 | * groups, and come in threes (mask, allow, deny): */ | |
712 | if (n4acl->naces < 7) | |
713 | return -1; | |
714 | if ((n4acl->naces - 7) % 3) | |
715 | return -1; | |
716 | return 4 + (n4acl->naces - 7)/3; | |
717 | } | |
718 | } | |
719 | ||
720 | ||
721 | static struct posix_acl * | |
722 | _nfsv4_to_posix_one(struct nfs4_acl *n4acl, unsigned int flags) | |
723 | { | |
724 | struct posix_acl *pacl; | |
725 | int error = -EINVAL, nace = 0; | |
726 | struct list_head *p; | |
727 | struct nfs4_ace *mask_ace = NULL; | |
728 | struct posix_acl_entry *pace; | |
729 | ||
730 | nace = calculate_posix_ace_count(n4acl); | |
731 | if (nace < 0) | |
732 | goto out_err; | |
733 | ||
734 | pacl = posix_acl_alloc(nace, GFP_KERNEL); | |
735 | error = -ENOMEM; | |
736 | if (pacl == NULL) | |
737 | goto out_err; | |
738 | ||
739 | pace = &pacl->a_entries[0]; | |
740 | p = &n4acl->ace_head; | |
741 | ||
742 | error = user_obj_from_v4(n4acl, &p, pacl, &pace, flags); | |
743 | if (error) | |
744 | goto out_acl; | |
745 | ||
746 | error = users_from_v4(n4acl, &p, &mask_ace, pacl, &pace, flags); | |
747 | if (error) | |
748 | goto out_acl; | |
749 | ||
750 | error = group_obj_and_groups_from_v4(n4acl, &p, &mask_ace, pacl, &pace, | |
751 | flags); | |
752 | if (error) | |
753 | goto out_acl; | |
754 | ||
755 | error = mask_from_v4(n4acl, &p, &mask_ace, pacl, &pace, flags); | |
756 | if (error) | |
757 | goto out_acl; | |
758 | error = other_from_v4(n4acl, &p, pacl, &pace, flags); | |
759 | if (error) | |
760 | goto out_acl; | |
761 | ||
762 | error = -EINVAL; | |
763 | if (p->next != &n4acl->ace_head) | |
764 | goto out_acl; | |
765 | if (pace != pacl->a_entries + pacl->a_count) | |
766 | goto out_acl; | |
767 | ||
768 | sort_pacl(pacl); | |
769 | ||
770 | return pacl; | |
771 | out_acl: | |
772 | posix_acl_release(pacl); | |
773 | out_err: | |
774 | pacl = ERR_PTR(error); | |
775 | return pacl; | |
776 | } | |
777 | ||
fd39ca9a | 778 | static int |
1da177e4 LT |
779 | nfs4_acl_split(struct nfs4_acl *acl, struct nfs4_acl *dacl) |
780 | { | |
781 | struct list_head *h, *n; | |
782 | struct nfs4_ace *ace; | |
783 | int error = 0; | |
784 | ||
785 | list_for_each_safe(h, n, &acl->ace_head) { | |
786 | ace = list_entry(h, struct nfs4_ace, l_ace); | |
787 | ||
788 | if ((ace->flag & NFS4_INHERITANCE_FLAGS) | |
789 | != NFS4_INHERITANCE_FLAGS) | |
790 | continue; | |
791 | ||
792 | error = nfs4_acl_add_ace(dacl, ace->type, ace->flag, | |
793 | ace->access_mask, ace->whotype, ace->who) == -1; | |
794 | if (error < 0) | |
795 | goto out; | |
796 | ||
797 | list_del(h); | |
798 | kfree(ace); | |
799 | acl->naces--; | |
800 | } | |
801 | ||
802 | out: | |
803 | return error; | |
804 | } | |
805 | ||
806 | static short | |
807 | ace2type(struct nfs4_ace *ace) | |
808 | { | |
809 | switch (ace->whotype) { | |
810 | case NFS4_ACL_WHO_NAMED: | |
811 | return (ace->flag & NFS4_ACE_IDENTIFIER_GROUP ? | |
812 | ACL_GROUP : ACL_USER); | |
813 | case NFS4_ACL_WHO_OWNER: | |
814 | return ACL_USER_OBJ; | |
815 | case NFS4_ACL_WHO_GROUP: | |
816 | return ACL_GROUP_OBJ; | |
817 | case NFS4_ACL_WHO_EVERYONE: | |
818 | return ACL_OTHER; | |
819 | } | |
820 | BUG(); | |
821 | return -1; | |
822 | } | |
823 | ||
824 | EXPORT_SYMBOL(nfs4_acl_posix_to_nfsv4); | |
825 | EXPORT_SYMBOL(nfs4_acl_nfsv4_to_posix); | |
826 | ||
827 | struct nfs4_acl * | |
828 | nfs4_acl_new(void) | |
829 | { | |
830 | struct nfs4_acl *acl; | |
831 | ||
832 | if ((acl = kmalloc(sizeof(*acl), GFP_KERNEL)) == NULL) | |
833 | return NULL; | |
834 | ||
835 | acl->naces = 0; | |
836 | INIT_LIST_HEAD(&acl->ace_head); | |
837 | ||
838 | return acl; | |
839 | } | |
840 | ||
841 | void | |
842 | nfs4_acl_free(struct nfs4_acl *acl) | |
843 | { | |
844 | struct list_head *h; | |
845 | struct nfs4_ace *ace; | |
846 | ||
847 | if (!acl) | |
848 | return; | |
849 | ||
850 | while (!list_empty(&acl->ace_head)) { | |
851 | h = acl->ace_head.next; | |
852 | list_del(h); | |
853 | ace = list_entry(h, struct nfs4_ace, l_ace); | |
854 | kfree(ace); | |
855 | } | |
856 | ||
857 | kfree(acl); | |
858 | ||
859 | return; | |
860 | } | |
861 | ||
862 | int | |
863 | nfs4_acl_add_ace(struct nfs4_acl *acl, u32 type, u32 flag, u32 access_mask, | |
864 | int whotype, uid_t who) | |
865 | { | |
866 | struct nfs4_ace *ace; | |
867 | ||
868 | if ((ace = kmalloc(sizeof(*ace), GFP_KERNEL)) == NULL) | |
869 | return -1; | |
870 | ||
871 | ace->type = type; | |
872 | ace->flag = flag; | |
873 | ace->access_mask = access_mask; | |
874 | ace->whotype = whotype; | |
875 | ace->who = who; | |
876 | ||
877 | list_add_tail(&ace->l_ace, &acl->ace_head); | |
878 | acl->naces++; | |
879 | ||
880 | return 0; | |
881 | } | |
882 | ||
883 | static struct { | |
884 | char *string; | |
885 | int stringlen; | |
886 | int type; | |
887 | } s2t_map[] = { | |
888 | { | |
889 | .string = "OWNER@", | |
890 | .stringlen = sizeof("OWNER@") - 1, | |
891 | .type = NFS4_ACL_WHO_OWNER, | |
892 | }, | |
893 | { | |
894 | .string = "GROUP@", | |
895 | .stringlen = sizeof("GROUP@") - 1, | |
896 | .type = NFS4_ACL_WHO_GROUP, | |
897 | }, | |
898 | { | |
899 | .string = "EVERYONE@", | |
900 | .stringlen = sizeof("EVERYONE@") - 1, | |
901 | .type = NFS4_ACL_WHO_EVERYONE, | |
902 | }, | |
903 | }; | |
904 | ||
905 | int | |
906 | nfs4_acl_get_whotype(char *p, u32 len) | |
907 | { | |
908 | int i; | |
909 | ||
e8c96f8c | 910 | for (i = 0; i < ARRAY_SIZE(s2t_map); i++) { |
1da177e4 LT |
911 | if (s2t_map[i].stringlen == len && |
912 | 0 == memcmp(s2t_map[i].string, p, len)) | |
913 | return s2t_map[i].type; | |
914 | } | |
915 | return NFS4_ACL_WHO_NAMED; | |
916 | } | |
917 | ||
918 | int | |
919 | nfs4_acl_write_who(int who, char *p) | |
920 | { | |
921 | int i; | |
922 | ||
e8c96f8c | 923 | for (i = 0; i < ARRAY_SIZE(s2t_map); i++) { |
1da177e4 LT |
924 | if (s2t_map[i].type == who) { |
925 | memcpy(p, s2t_map[i].string, s2t_map[i].stringlen); | |
926 | return s2t_map[i].stringlen; | |
927 | } | |
928 | } | |
929 | BUG(); | |
930 | return -1; | |
931 | } | |
932 | ||
933 | static inline int | |
934 | match_who(struct nfs4_ace *ace, uid_t owner, gid_t group, uid_t who) | |
935 | { | |
936 | switch (ace->whotype) { | |
937 | case NFS4_ACL_WHO_NAMED: | |
938 | return who == ace->who; | |
939 | case NFS4_ACL_WHO_OWNER: | |
940 | return who == owner; | |
941 | case NFS4_ACL_WHO_GROUP: | |
942 | return who == group; | |
943 | case NFS4_ACL_WHO_EVERYONE: | |
944 | return 1; | |
945 | default: | |
946 | return 0; | |
947 | } | |
948 | } | |
949 | ||
950 | EXPORT_SYMBOL(nfs4_acl_new); | |
951 | EXPORT_SYMBOL(nfs4_acl_free); | |
952 | EXPORT_SYMBOL(nfs4_acl_add_ace); | |
953 | EXPORT_SYMBOL(nfs4_acl_get_whotype); | |
954 | EXPORT_SYMBOL(nfs4_acl_write_who); |