aboutsummaryrefslogtreecommitdiffstats
path: root/fs/xfs/xfs_acl.c
diff options
context:
space:
mode:
authorChristoph Hellwig <hch@lst.de>2009-06-10 11:07:47 -0400
committerChristoph Hellwig <hch@brick.lst.de>2009-06-10 11:07:47 -0400
commitef14f0c1578dce4b688726eb2603e50b62d6665a (patch)
tree5a221081850fab8f96455745e90f4a0e2127bce0 /fs/xfs/xfs_acl.c
parent8b5403a6d772d340541cfb30a668fde119c40ac1 (diff)
xfs: use generic Posix ACL code
This patch rips out the XFS ACL handling code and uses the generic fs/posix_acl.c code instead. The ondisk format is of course left unchanged. This also introduces the same ACL caching all other Linux filesystems do by adding pointers to the acl and default acl in struct xfs_inode. Signed-off-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Eric Sandeen <sandeen@sandeen.net>
Diffstat (limited to 'fs/xfs/xfs_acl.c')
-rw-r--r--fs/xfs/xfs_acl.c874
1 files changed, 0 insertions, 874 deletions
diff --git a/fs/xfs/xfs_acl.c b/fs/xfs/xfs_acl.c
deleted file mode 100644
index a8cdd73999a..00000000000
--- a/fs/xfs/xfs_acl.c
+++ /dev/null
@@ -1,874 +0,0 @@
1/*
2 * Copyright (c) 2001-2002,2005 Silicon Graphics, Inc.
3 * All Rights Reserved.
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it would be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write the Free Software Foundation,
16 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18#include "xfs.h"
19#include "xfs_fs.h"
20#include "xfs_types.h"
21#include "xfs_bit.h"
22#include "xfs_inum.h"
23#include "xfs_ag.h"
24#include "xfs_dir2.h"
25#include "xfs_bmap_btree.h"
26#include "xfs_alloc_btree.h"
27#include "xfs_ialloc_btree.h"
28#include "xfs_dir2_sf.h"
29#include "xfs_attr_sf.h"
30#include "xfs_dinode.h"
31#include "xfs_inode.h"
32#include "xfs_btree.h"
33#include "xfs_acl.h"
34#include "xfs_attr.h"
35#include "xfs_vnodeops.h"
36
37#include <linux/capability.h>
38#include <linux/posix_acl_xattr.h>
39
40STATIC int xfs_acl_setmode(struct inode *, xfs_acl_t *, int *);
41STATIC void xfs_acl_filter_mode(mode_t, xfs_acl_t *);
42STATIC void xfs_acl_get_endian(xfs_acl_t *);
43STATIC int xfs_acl_access(uid_t, gid_t, xfs_acl_t *, mode_t, cred_t *);
44STATIC int xfs_acl_invalid(xfs_acl_t *);
45STATIC void xfs_acl_sync_mode(mode_t, xfs_acl_t *);
46STATIC void xfs_acl_get_attr(struct inode *, xfs_acl_t *, int, int, int *);
47STATIC void xfs_acl_set_attr(struct inode *, xfs_acl_t *, int, int *);
48STATIC int xfs_acl_allow_set(struct inode *, int);
49
50kmem_zone_t *xfs_acl_zone;
51
52
53/*
54 * Test for existence of access ACL attribute as efficiently as possible.
55 */
56int
57xfs_acl_vhasacl_access(
58 struct inode *vp)
59{
60 int error;
61
62 xfs_acl_get_attr(vp, NULL, _ACL_TYPE_ACCESS, ATTR_KERNOVAL, &error);
63 return (error == 0);
64}
65
66/*
67 * Test for existence of default ACL attribute as efficiently as possible.
68 */
69int
70xfs_acl_vhasacl_default(
71 struct inode *vp)
72{
73 int error;
74
75 if (!S_ISDIR(vp->i_mode))
76 return 0;
77 xfs_acl_get_attr(vp, NULL, _ACL_TYPE_DEFAULT, ATTR_KERNOVAL, &error);
78 return (error == 0);
79}
80
81/*
82 * Convert from extended attribute representation to in-memory for XFS.
83 */
84STATIC int
85posix_acl_xattr_to_xfs(
86 posix_acl_xattr_header *src,
87 size_t size,
88 xfs_acl_t *dest)
89{
90 posix_acl_xattr_entry *src_entry;
91 xfs_acl_entry_t *dest_entry;
92 int n;
93
94 if (!src || !dest)
95 return EINVAL;
96
97 if (size < sizeof(posix_acl_xattr_header))
98 return EINVAL;
99
100 if (src->a_version != cpu_to_le32(POSIX_ACL_XATTR_VERSION))
101 return EOPNOTSUPP;
102
103 memset(dest, 0, sizeof(xfs_acl_t));
104 dest->acl_cnt = posix_acl_xattr_count(size);
105 if (dest->acl_cnt < 0 || dest->acl_cnt > XFS_ACL_MAX_ENTRIES)
106 return EINVAL;
107
108 /*
109 * acl_set_file(3) may request that we set default ACLs with
110 * zero length -- defend (gracefully) against that here.
111 */
112 if (!dest->acl_cnt)
113 return 0;
114
115 src_entry = (posix_acl_xattr_entry *)((char *)src + sizeof(*src));
116 dest_entry = &dest->acl_entry[0];
117
118 for (n = 0; n < dest->acl_cnt; n++, src_entry++, dest_entry++) {
119 dest_entry->ae_perm = le16_to_cpu(src_entry->e_perm);
120 if (_ACL_PERM_INVALID(dest_entry->ae_perm))
121 return EINVAL;
122 dest_entry->ae_tag = le16_to_cpu(src_entry->e_tag);
123 switch(dest_entry->ae_tag) {
124 case ACL_USER:
125 case ACL_GROUP:
126 dest_entry->ae_id = le32_to_cpu(src_entry->e_id);
127 break;
128 case ACL_USER_OBJ:
129 case ACL_GROUP_OBJ:
130 case ACL_MASK:
131 case ACL_OTHER:
132 dest_entry->ae_id = ACL_UNDEFINED_ID;
133 break;
134 default:
135 return EINVAL;
136 }
137 }
138 if (xfs_acl_invalid(dest))
139 return EINVAL;
140
141 return 0;
142}
143
144/*
145 * Comparison function called from xfs_sort().
146 * Primary key is ae_tag, secondary key is ae_id.
147 */
148STATIC int
149xfs_acl_entry_compare(
150 const void *va,
151 const void *vb)
152{
153 xfs_acl_entry_t *a = (xfs_acl_entry_t *)va,
154 *b = (xfs_acl_entry_t *)vb;
155
156 if (a->ae_tag == b->ae_tag)
157 return (a->ae_id - b->ae_id);
158 return (a->ae_tag - b->ae_tag);
159}
160
161/*
162 * Convert from in-memory XFS to extended attribute representation.
163 */
164STATIC int
165posix_acl_xfs_to_xattr(
166 xfs_acl_t *src,
167 posix_acl_xattr_header *dest,
168 size_t size)
169{
170 int n;
171 size_t new_size = posix_acl_xattr_size(src->acl_cnt);
172 posix_acl_xattr_entry *dest_entry;
173 xfs_acl_entry_t *src_entry;
174
175 if (size < new_size)
176 return -ERANGE;
177
178 /* Need to sort src XFS ACL by <ae_tag,ae_id> */
179 xfs_sort(src->acl_entry, src->acl_cnt, sizeof(src->acl_entry[0]),
180 xfs_acl_entry_compare);
181
182 dest->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION);
183 dest_entry = &dest->a_entries[0];
184 src_entry = &src->acl_entry[0];
185 for (n = 0; n < src->acl_cnt; n++, dest_entry++, src_entry++) {
186 dest_entry->e_perm = cpu_to_le16(src_entry->ae_perm);
187 if (_ACL_PERM_INVALID(src_entry->ae_perm))
188 return -EINVAL;
189 dest_entry->e_tag = cpu_to_le16(src_entry->ae_tag);
190 switch (src_entry->ae_tag) {
191 case ACL_USER:
192 case ACL_GROUP:
193 dest_entry->e_id = cpu_to_le32(src_entry->ae_id);
194 break;
195 case ACL_USER_OBJ:
196 case ACL_GROUP_OBJ:
197 case ACL_MASK:
198 case ACL_OTHER:
199 dest_entry->e_id = cpu_to_le32(ACL_UNDEFINED_ID);
200 break;
201 default:
202 return -EINVAL;
203 }
204 }
205 return new_size;
206}
207
208int
209xfs_acl_vget(
210 struct inode *vp,
211 void *acl,
212 size_t size,
213 int kind)
214{
215 int error;
216 xfs_acl_t *xfs_acl = NULL;
217 posix_acl_xattr_header *ext_acl = acl;
218 int flags = 0;
219
220 if(size) {
221 if (!(_ACL_ALLOC(xfs_acl))) {
222 error = ENOMEM;
223 goto out;
224 }
225 memset(xfs_acl, 0, sizeof(xfs_acl_t));
226 } else
227 flags = ATTR_KERNOVAL;
228
229 xfs_acl_get_attr(vp, xfs_acl, kind, flags, &error);
230 if (error)
231 goto out;
232
233 if (!size) {
234 error = -posix_acl_xattr_size(XFS_ACL_MAX_ENTRIES);
235 } else {
236 if (xfs_acl_invalid(xfs_acl)) {
237 error = EINVAL;
238 goto out;
239 }
240 if (kind == _ACL_TYPE_ACCESS)
241 xfs_acl_sync_mode(XFS_I(vp)->i_d.di_mode, xfs_acl);
242 error = -posix_acl_xfs_to_xattr(xfs_acl, ext_acl, size);
243 }
244out:
245 if(xfs_acl)
246 _ACL_FREE(xfs_acl);
247 return -error;
248}
249
250int
251xfs_acl_vremove(
252 struct inode *vp,
253 int kind)
254{
255 int error;
256
257 error = xfs_acl_allow_set(vp, kind);
258 if (!error) {
259 error = xfs_attr_remove(XFS_I(vp),
260 kind == _ACL_TYPE_DEFAULT?
261 SGI_ACL_DEFAULT: SGI_ACL_FILE,
262 ATTR_ROOT);
263 if (error == ENOATTR)
264 error = 0; /* 'scool */
265 }
266 return -error;
267}
268
269int
270xfs_acl_vset(
271 struct inode *vp,
272 void *acl,
273 size_t size,
274 int kind)
275{
276 posix_acl_xattr_header *ext_acl = acl;
277 xfs_acl_t *xfs_acl;
278 int error;
279 int basicperms = 0; /* more than std unix perms? */
280
281 if (!acl)
282 return -EINVAL;
283
284 if (!(_ACL_ALLOC(xfs_acl)))
285 return -ENOMEM;
286
287 error = posix_acl_xattr_to_xfs(ext_acl, size, xfs_acl);
288 if (error) {
289 _ACL_FREE(xfs_acl);
290 return -error;
291 }
292 if (!xfs_acl->acl_cnt) {
293 _ACL_FREE(xfs_acl);
294 return 0;
295 }
296
297 error = xfs_acl_allow_set(vp, kind);
298
299 /* Incoming ACL exists, set file mode based on its value */
300 if (!error && kind == _ACL_TYPE_ACCESS)
301 error = xfs_acl_setmode(vp, xfs_acl, &basicperms);
302
303 if (error)
304 goto out;
305
306 /*
307 * If we have more than std unix permissions, set up the actual attr.
308 * Otherwise, delete any existing attr. This prevents us from
309 * having actual attrs for permissions that can be stored in the
310 * standard permission bits.
311 */
312 if (!basicperms) {
313 xfs_acl_set_attr(vp, xfs_acl, kind, &error);
314 } else {
315 error = -xfs_acl_vremove(vp, _ACL_TYPE_ACCESS);
316 }
317
318out:
319 _ACL_FREE(xfs_acl);
320 return -error;
321}
322
323int
324xfs_acl_iaccess(
325 xfs_inode_t *ip,
326 mode_t mode,
327 cred_t *cr)
328{
329 xfs_acl_t *acl;
330 int rval;
331 struct xfs_name acl_name = {SGI_ACL_FILE, SGI_ACL_FILE_SIZE};
332
333 if (!(_ACL_ALLOC(acl)))
334 return -1;
335
336 /* If the file has no ACL return -1. */
337 rval = sizeof(xfs_acl_t);
338 if (xfs_attr_fetch(ip, &acl_name, (char *)acl, &rval, ATTR_ROOT)) {
339 _ACL_FREE(acl);
340 return -1;
341 }
342 xfs_acl_get_endian(acl);
343
344 /* If the file has an empty ACL return -1. */
345 if (acl->acl_cnt == XFS_ACL_NOT_PRESENT) {
346 _ACL_FREE(acl);
347 return -1;
348 }
349
350 /* Synchronize ACL with mode bits */
351 xfs_acl_sync_mode(ip->i_d.di_mode, acl);
352
353 rval = xfs_acl_access(ip->i_d.di_uid, ip->i_d.di_gid, acl, mode, cr);
354 _ACL_FREE(acl);
355 return rval;
356}
357
358STATIC int
359xfs_acl_allow_set(
360 struct inode *vp,
361 int kind)
362{
363 if (vp->i_flags & (S_IMMUTABLE|S_APPEND))
364 return EPERM;
365 if (kind == _ACL_TYPE_DEFAULT && !S_ISDIR(vp->i_mode))
366 return ENOTDIR;
367 if (vp->i_sb->s_flags & MS_RDONLY)
368 return EROFS;
369 if (XFS_I(vp)->i_d.di_uid != current_fsuid() && !capable(CAP_FOWNER))
370 return EPERM;
371 return 0;
372}
373
374/*
375 * Note: cr is only used here for the capability check if the ACL test fails.
376 * It is not used to find out the credentials uid or groups etc, as was
377 * done in IRIX. It is assumed that the uid and groups for the current
378 * thread are taken from "current" instead of the cr parameter.
379 */
380STATIC int
381xfs_acl_access(
382 uid_t fuid,
383 gid_t fgid,
384 xfs_acl_t *fap,
385 mode_t md,
386 cred_t *cr)
387{
388 xfs_acl_entry_t matched;
389 int i, allows;
390 int maskallows = -1; /* true, but not 1, either */
391 int seen_userobj = 0;
392
393 matched.ae_tag = 0; /* Invalid type */
394 matched.ae_perm = 0;
395
396 for (i = 0; i < fap->acl_cnt; i++) {
397 /*
398 * Break out if we've got a user_obj entry or
399 * a user entry and the mask (and have processed USER_OBJ)
400 */
401 if (matched.ae_tag == ACL_USER_OBJ)
402 break;
403 if (matched.ae_tag == ACL_USER) {
404 if (maskallows != -1 && seen_userobj)
405 break;
406 if (fap->acl_entry[i].ae_tag != ACL_MASK &&
407 fap->acl_entry[i].ae_tag != ACL_USER_OBJ)
408 continue;
409 }
410 /* True if this entry allows the requested access */
411 allows = ((fap->acl_entry[i].ae_perm & md) == md);
412
413 switch (fap->acl_entry[i].ae_tag) {
414 case ACL_USER_OBJ:
415 seen_userobj = 1;
416 if (fuid != current_fsuid())
417 continue;
418 matched.ae_tag = ACL_USER_OBJ;
419 matched.ae_perm = allows;
420 break;
421 case ACL_USER:
422 if (fap->acl_entry[i].ae_id != current_fsuid())
423 continue;
424 matched.ae_tag = ACL_USER;
425 matched.ae_perm = allows;
426 break;
427 case ACL_GROUP_OBJ:
428 if ((matched.ae_tag == ACL_GROUP_OBJ ||
429 matched.ae_tag == ACL_GROUP) && !allows)
430 continue;
431 if (!in_group_p(fgid))
432 continue;
433 matched.ae_tag = ACL_GROUP_OBJ;
434 matched.ae_perm = allows;
435 break;
436 case ACL_GROUP:
437 if ((matched.ae_tag == ACL_GROUP_OBJ ||
438 matched.ae_tag == ACL_GROUP) && !allows)
439 continue;
440 if (!in_group_p(fap->acl_entry[i].ae_id))
441 continue;
442 matched.ae_tag = ACL_GROUP;
443 matched.ae_perm = allows;
444 break;
445 case ACL_MASK:
446 maskallows = allows;
447 break;
448 case ACL_OTHER:
449 if (matched.ae_tag != 0)
450 continue;
451 matched.ae_tag = ACL_OTHER;
452 matched.ae_perm = allows;
453 break;
454 }
455 }
456 /*
457 * First possibility is that no matched entry allows access.
458 * The capability to override DAC may exist, so check for it.
459 */
460 switch (matched.ae_tag) {
461 case ACL_OTHER:
462 case ACL_USER_OBJ:
463 if (matched.ae_perm)
464 return 0;
465 break;
466 case ACL_USER:
467 case ACL_GROUP_OBJ:
468 case ACL_GROUP:
469 if (maskallows && matched.ae_perm)
470 return 0;
471 break;
472 case 0:
473 break;
474 }
475
476 /* EACCES tells generic_permission to check for capability overrides */
477 return EACCES;
478}
479
480/*
481 * ACL validity checker.
482 * This acl validation routine checks each ACL entry read in makes sense.
483 */
484STATIC int
485xfs_acl_invalid(
486 xfs_acl_t *aclp)
487{
488 xfs_acl_entry_t *entry, *e;
489 int user = 0, group = 0, other = 0, mask = 0;
490 int mask_required = 0;
491 int i, j;
492
493 if (!aclp)
494 goto acl_invalid;
495
496 if (aclp->acl_cnt > XFS_ACL_MAX_ENTRIES)
497 goto acl_invalid;
498
499 for (i = 0; i < aclp->acl_cnt; i++) {
500 entry = &aclp->acl_entry[i];
501 switch (entry->ae_tag) {
502 case ACL_USER_OBJ:
503 if (user++)
504 goto acl_invalid;
505 break;
506 case ACL_GROUP_OBJ:
507 if (group++)
508 goto acl_invalid;
509 break;
510 case ACL_OTHER:
511 if (other++)
512 goto acl_invalid;
513 break;
514 case ACL_USER:
515 case ACL_GROUP:
516 for (j = i + 1; j < aclp->acl_cnt; j++) {
517 e = &aclp->acl_entry[j];
518 if (e->ae_id == entry->ae_id &&
519 e->ae_tag == entry->ae_tag)
520 goto acl_invalid;
521 }
522 mask_required++;
523 break;
524 case ACL_MASK:
525 if (mask++)
526 goto acl_invalid;
527 break;
528 default:
529 goto acl_invalid;
530 }
531 }
532 if (!user || !group || !other || (mask_required && !mask))
533 goto acl_invalid;
534 else
535 return 0;
536acl_invalid:
537 return EINVAL;
538}
539
540/*
541 * Do ACL endian conversion.
542 */
543STATIC void
544xfs_acl_get_endian(
545 xfs_acl_t *aclp)
546{
547 xfs_acl_entry_t *ace, *end;
548
549 INT_SET(aclp->acl_cnt, ARCH_CONVERT, aclp->acl_cnt);
550 end = &aclp->acl_entry[0]+aclp->acl_cnt;
551 for (ace = &aclp->acl_entry[0]; ace < end; ace++) {
552 INT_SET(ace->ae_tag, ARCH_CONVERT, ace->ae_tag);
553 INT_SET(ace->ae_id, ARCH_CONVERT, ace->ae_id);
554 INT_SET(ace->ae_perm, ARCH_CONVERT, ace->ae_perm);
555 }
556}
557
558/*
559 * Get the ACL from the EA and do endian conversion.
560 */
561STATIC void
562xfs_acl_get_attr(
563 struct inode *vp,
564 xfs_acl_t *aclp,
565 int kind,
566 int flags,
567 int *error)
568{
569 int len = sizeof(xfs_acl_t);
570
571 ASSERT((flags & ATTR_KERNOVAL) ? (aclp == NULL) : 1);
572 flags |= ATTR_ROOT;
573 *error = xfs_attr_get(XFS_I(vp),
574 kind == _ACL_TYPE_ACCESS ?
575 SGI_ACL_FILE : SGI_ACL_DEFAULT,
576 (char *)aclp, &len, flags);
577 if (*error || (flags & ATTR_KERNOVAL))
578 return;
579 xfs_acl_get_endian(aclp);
580}
581
582/*
583 * Set the EA with the ACL and do endian conversion.
584 */
585STATIC void
586xfs_acl_set_attr(
587 struct inode *vp,
588 xfs_acl_t *aclp,
589 int kind,
590 int *error)
591{
592 xfs_acl_entry_t *ace, *newace, *end;
593 xfs_acl_t *newacl;
594 int len;
595
596 if (!(_ACL_ALLOC(newacl))) {
597 *error = ENOMEM;
598 return;
599 }
600
601 len = sizeof(xfs_acl_t) -
602 (sizeof(xfs_acl_entry_t) * (XFS_ACL_MAX_ENTRIES - aclp->acl_cnt));
603 end = &aclp->acl_entry[0]+aclp->acl_cnt;
604 for (ace = &aclp->acl_entry[0], newace = &newacl->acl_entry[0];
605 ace < end;
606 ace++, newace++) {
607 INT_SET(newace->ae_tag, ARCH_CONVERT, ace->ae_tag);
608 INT_SET(newace->ae_id, ARCH_CONVERT, ace->ae_id);
609 INT_SET(newace->ae_perm, ARCH_CONVERT, ace->ae_perm);
610 }
611 INT_SET(newacl->acl_cnt, ARCH_CONVERT, aclp->acl_cnt);
612 *error = xfs_attr_set(XFS_I(vp),
613 kind == _ACL_TYPE_ACCESS ?
614 SGI_ACL_FILE: SGI_ACL_DEFAULT,
615 (char *)newacl, len, ATTR_ROOT);
616 _ACL_FREE(newacl);
617}
618
619int
620xfs_acl_vtoacl(
621 struct inode *vp,
622 xfs_acl_t *access_acl,
623 xfs_acl_t *default_acl)
624{
625 int error = 0;
626
627 if (access_acl) {
628 /*
629 * Get the Access ACL and the mode. If either cannot
630 * be obtained for some reason, invalidate the access ACL.
631 */
632 xfs_acl_get_attr(vp, access_acl, _ACL_TYPE_ACCESS, 0, &error);
633 if (error)
634 access_acl->acl_cnt = XFS_ACL_NOT_PRESENT;
635 else /* We have a good ACL and the file mode, synchronize. */
636 xfs_acl_sync_mode(XFS_I(vp)->i_d.di_mode, access_acl);
637 }
638
639 if (default_acl) {
640 xfs_acl_get_attr(vp, default_acl, _ACL_TYPE_DEFAULT, 0, &error);
641 if (error)
642 default_acl->acl_cnt = XFS_ACL_NOT_PRESENT;
643 }
644 return error;
645}
646
647/*
648 * This function retrieves the parent directory's acl, processes it
649 * and lets the child inherit the acl(s) that it should.
650 */
651int
652xfs_acl_inherit(
653 struct inode *vp,
654 mode_t mode,
655 xfs_acl_t *pdaclp)
656{
657 xfs_acl_t *cacl;
658 int error = 0;
659 int basicperms = 0;
660
661 /*
662 * If the parent does not have a default ACL, or it's an
663 * invalid ACL, we're done.
664 */
665 if (!vp)
666 return 0;
667 if (!pdaclp || xfs_acl_invalid(pdaclp))
668 return 0;
669
670 /*
671 * Copy the default ACL of the containing directory to
672 * the access ACL of the new file and use the mode that
673 * was passed in to set up the correct initial values for
674 * the u::,g::[m::], and o:: entries. This is what makes
675 * umask() "work" with ACL's.
676 */
677
678 if (!(_ACL_ALLOC(cacl)))
679 return ENOMEM;
680
681 memcpy(cacl, pdaclp, sizeof(xfs_acl_t));
682 xfs_acl_filter_mode(mode, cacl);
683 error = xfs_acl_setmode(vp, cacl, &basicperms);
684 if (error)
685 goto out_error;
686
687 /*
688 * Set the Default and Access ACL on the file. The mode is already
689 * set on the file, so we don't need to worry about that.
690 *
691 * If the new file is a directory, its default ACL is a copy of
692 * the containing directory's default ACL.
693 */
694 if (S_ISDIR(vp->i_mode))
695 xfs_acl_set_attr(vp, pdaclp, _ACL_TYPE_DEFAULT, &error);
696 if (!error && !basicperms)
697 xfs_acl_set_attr(vp, cacl, _ACL_TYPE_ACCESS, &error);
698out_error:
699 _ACL_FREE(cacl);
700 return error;
701}
702
703/*
704 * Set up the correct mode on the file based on the supplied ACL. This
705 * makes sure that the mode on the file reflects the state of the
706 * u::,g::[m::], and o:: entries in the ACL. Since the mode is where
707 * the ACL is going to get the permissions for these entries, we must
708 * synchronize the mode whenever we set the ACL on a file.
709 */
710STATIC int
711xfs_acl_setmode(
712 struct inode *vp,
713 xfs_acl_t *acl,
714 int *basicperms)
715{
716 struct iattr iattr;
717 xfs_acl_entry_t *ap;
718 xfs_acl_entry_t *gap = NULL;
719 int i, nomask = 1;
720
721 *basicperms = 1;
722
723 if (acl->acl_cnt == XFS_ACL_NOT_PRESENT)
724 return 0;
725
726 /*
727 * Copy the u::, g::, o::, and m:: bits from the ACL into the
728 * mode. The m:: bits take precedence over the g:: bits.
729 */
730 iattr.ia_valid = ATTR_MODE;
731 iattr.ia_mode = XFS_I(vp)->i_d.di_mode;
732 iattr.ia_mode &= ~(S_IRWXU|S_IRWXG|S_IRWXO);
733 ap = acl->acl_entry;
734 for (i = 0; i < acl->acl_cnt; ++i) {
735 switch (ap->ae_tag) {
736 case ACL_USER_OBJ:
737 iattr.ia_mode |= ap->ae_perm << 6;
738 break;
739 case ACL_GROUP_OBJ:
740 gap = ap;
741 break;
742 case ACL_MASK: /* more than just standard modes */
743 nomask = 0;
744 iattr.ia_mode |= ap->ae_perm << 3;
745 *basicperms = 0;
746 break;
747 case ACL_OTHER:
748 iattr.ia_mode |= ap->ae_perm;
749 break;
750 default: /* more than just standard modes */
751 *basicperms = 0;
752 break;
753 }
754 ap++;
755 }
756
757 /* Set the group bits from ACL_GROUP_OBJ if there's no ACL_MASK */
758 if (gap && nomask)
759 iattr.ia_mode |= gap->ae_perm << 3;
760
761 return xfs_setattr(XFS_I(vp), &iattr, 0);
762}
763
764/*
765 * The permissions for the special ACL entries (u::, g::[m::], o::) are
766 * actually stored in the file mode (if there is both a group and a mask,
767 * the group is stored in the ACL entry and the mask is stored on the file).
768 * This allows the mode to remain automatically in sync with the ACL without
769 * the need for a call-back to the ACL system at every point where the mode
770 * could change. This function takes the permissions from the specified mode
771 * and places it in the supplied ACL.
772 *
773 * This implementation draws its validity from the fact that, when the ACL
774 * was assigned, the mode was copied from the ACL.
775 * If the mode did not change, therefore, the mode remains exactly what was
776 * taken from the special ACL entries at assignment.
777 * If a subsequent chmod() was done, the POSIX spec says that the change in
778 * mode must cause an update to the ACL seen at user level and used for
779 * access checks. Before and after a mode change, therefore, the file mode
780 * most accurately reflects what the special ACL entries should permit/deny.
781 *
782 * CAVEAT: If someone sets the SGI_ACL_FILE attribute directly,
783 * the existing mode bits will override whatever is in the
784 * ACL. Similarly, if there is a pre-existing ACL that was
785 * never in sync with its mode (owing to a bug in 6.5 and
786 * before), it will now magically (or mystically) be
787 * synchronized. This could cause slight astonishment, but
788 * it is better than inconsistent permissions.
789 *
790 * The supplied ACL is a template that may contain any combination
791 * of special entries. These are treated as place holders when we fill
792 * out the ACL. This routine does not add or remove special entries, it
793 * simply unites each special entry with its associated set of permissions.
794 */
795STATIC void
796xfs_acl_sync_mode(
797 mode_t mode,
798 xfs_acl_t *acl)
799{
800 int i, nomask = 1;
801 xfs_acl_entry_t *ap;
802 xfs_acl_entry_t *gap = NULL;
803
804 /*
805 * Set ACL entries. POSIX1003.1eD16 requires that the MASK
806 * be set instead of the GROUP entry, if there is a MASK.
807 */
808 for (ap = acl->acl_entry, i = 0; i < acl->acl_cnt; ap++, i++) {
809 switch (ap->ae_tag) {
810 case ACL_USER_OBJ:
811 ap->ae_perm = (mode >> 6) & 0x7;
812 break;
813 case ACL_GROUP_OBJ:
814 gap = ap;
815 break;
816 case ACL_MASK:
817 nomask = 0;
818 ap->ae_perm = (mode >> 3) & 0x7;
819 break;
820 case ACL_OTHER:
821 ap->ae_perm = mode & 0x7;
822 break;
823 default:
824 break;
825 }
826 }
827 /* Set the ACL_GROUP_OBJ if there's no ACL_MASK */
828 if (gap && nomask)
829 gap->ae_perm = (mode >> 3) & 0x7;
830}
831
832/*
833 * When inheriting an Access ACL from a directory Default ACL,
834 * the ACL bits are set to the intersection of the ACL default
835 * permission bits and the file permission bits in mode. If there
836 * are no permission bits on the file then we must not give them
837 * the ACL. This is what what makes umask() work with ACLs.
838 */
839STATIC void
840xfs_acl_filter_mode(
841 mode_t mode,
842 xfs_acl_t *acl)
843{
844 int i, nomask = 1;
845 xfs_acl_entry_t *ap;
846 xfs_acl_entry_t *gap = NULL;
847
848 /*
849 * Set ACL entries. POSIX1003.1eD16 requires that the MASK
850 * be merged with GROUP entry, if there is a MASK.
851 */
852 for (ap = acl->acl_entry, i = 0; i < acl->acl_cnt; ap++, i++) {
853 switch (ap->ae_tag) {
854 case ACL_USER_OBJ:
855 ap->ae_perm &= (mode >> 6) & 0x7;
856 break;
857 case ACL_GROUP_OBJ:
858 gap = ap;
859 break;
860 case ACL_MASK:
861 nomask = 0;
862 ap->ae_perm &= (mode >> 3) & 0x7;
863 break;
864 case ACL_OTHER:
865 ap->ae_perm &= mode & 0x7;
866 break;
867 default:
868 break;
869 }
870 }
871 /* Set the ACL_GROUP_OBJ if there's no ACL_MASK */
872 if (gap && nomask)
873 gap->ae_perm &= (mode >> 3) & 0x7;
874}