1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2 * vim:expandtab:shiftwidth=8:tabstop=8:
4 * lustre/obdclass/acl.c
5 * Lustre Access Control List.
6 * Author: Fan Yong <fanyong@clusterfs.com>
8 * Copyright (c) 2004-2007 Cluster File Systems, Inc.
10 * This file is part of Lustre, http://www.lustre.org.
12 * Lustre is free software; you can redistribute it and/or
13 * modify it under the terms of version 2 of the GNU General Public
14 * License as published by the Free Software Foundation.
16 * Lustre is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with Lustre; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 # define EXPORT_SYMTAB
29 #define DEBUG_SUBSYSTEM S_SEC
31 #include <linux/lustre_acl.h>
32 #include <lustre_eacl.h>
33 #include <obd_support.h>
35 #ifdef CONFIG_FS_POSIX_ACL
37 #define CFS_ACL_XATTR_VERSION POSIX_ACL_XATTR_VERSION
40 ES_UNK = 0, /* unknown stat */
41 ES_UNC = 1, /* ACL entry is not changed */
42 ES_MOD = 2, /* ACL entry is modified */
43 ES_ADD = 3, /* ACL entry is added */
44 ES_DEL = 4 /* ACL entry is deleted */
47 static inline void lustre_ext_acl_le_to_cpu(ext_acl_xattr_entry *d,
48 ext_acl_xattr_entry *s)
50 d->e_tag = le16_to_cpu(s->e_tag);
51 d->e_perm = le16_to_cpu(s->e_perm);
52 d->e_id = le32_to_cpu(s->e_id);
53 d->e_stat = le32_to_cpu(s->e_stat);
56 static inline void lustre_ext_acl_cpu_to_le(ext_acl_xattr_entry *d,
57 ext_acl_xattr_entry *s)
59 d->e_tag = cpu_to_le16(s->e_tag);
60 d->e_perm = cpu_to_le16(s->e_perm);
61 d->e_id = cpu_to_le32(s->e_id);
62 d->e_stat = cpu_to_le32(s->e_stat);
65 static inline void lustre_posix_acl_le_to_cpu(posix_acl_xattr_entry *d,
66 posix_acl_xattr_entry *s)
68 d->e_tag = le16_to_cpu(s->e_tag);
69 d->e_perm = le16_to_cpu(s->e_perm);
70 d->e_id = le32_to_cpu(s->e_id);
73 static inline void lustre_posix_acl_cpu_to_le(posix_acl_xattr_entry *d,
74 posix_acl_xattr_entry *s)
76 d->e_tag = cpu_to_le16(s->e_tag);
77 d->e_perm = cpu_to_le16(s->e_perm);
78 d->e_id = cpu_to_le32(s->e_id);
82 * Check permission based on POSIX ACL.
84 int lustre_posix_acl_permission(struct md_ucred *mu, struct lu_attr *la,
85 int want, posix_acl_xattr_entry *entry,
88 posix_acl_xattr_entry *pa, *pe, *mask_obj;
89 posix_acl_xattr_entry ae, me;
95 for (pa = &entry[0], pe = &entry[count - 1]; pa <= pe; pa++) {
96 lustre_posix_acl_le_to_cpu(&ae, pa);
99 /* (May have been checked already) */
100 if (la->la_uid == mu->mu_fsuid)
104 if (ae.e_id == mu->mu_fsuid)
108 if (lustre_in_group_p(mu, la->la_gid)) {
110 if ((ae.e_perm & want) == want)
115 if (lustre_in_group_p(mu, ae.e_id)) {
117 if ((ae.e_perm & want) == want)
135 for (mask_obj = pa + 1; mask_obj <= pe; mask_obj++) {
136 lustre_posix_acl_le_to_cpu(&me, mask_obj);
137 if (me.e_tag == ACL_MASK) {
138 if ((ae.e_perm & me.e_perm & want) == want)
146 if ((ae.e_perm & want) == want)
151 EXPORT_SYMBOL(lustre_posix_acl_permission);
154 * Modify the ACL for the chmod.
156 int lustre_posix_acl_chmod_masq(posix_acl_xattr_entry *entry, __u32 mode,
159 posix_acl_xattr_entry *group_obj = NULL, *mask_obj = NULL, *pa, *pe;
161 for (pa = &entry[0], pe = &entry[count - 1]; pa <= pe; pa++) {
162 switch (le16_to_cpu(pa->e_tag)) {
164 pa->e_perm = cpu_to_le16((mode & S_IRWXU) >> 6);
176 pa->e_perm = cpu_to_le16(mode & S_IRWXO);
184 mask_obj->e_perm = cpu_to_le16((mode & S_IRWXG) >> 3);
188 group_obj->e_perm = cpu_to_le16((mode & S_IRWXG) >> 3);
193 EXPORT_SYMBOL(lustre_posix_acl_chmod_masq);
196 * Modify acl when creating a new object.
198 int lustre_posix_acl_create_masq(posix_acl_xattr_entry *entry, __u32 *pmode,
201 posix_acl_xattr_entry *group_obj = NULL, *mask_obj = NULL, *pa, *pe;
202 posix_acl_xattr_entry ae;
206 for (pa = &entry[0], pe = &entry[count - 1]; pa <= pe; pa++) {
207 lustre_posix_acl_le_to_cpu(&ae, pa);
210 ae.e_perm &= (mode >> 6) | ~S_IRWXO;
211 pa->e_perm = cpu_to_le16(ae.e_perm);
212 mode &= (ae.e_perm << 6) | ~S_IRWXU;
222 ae.e_perm &= mode | ~S_IRWXO;
223 pa->e_perm = cpu_to_le16(ae.e_perm);
224 mode &= ae.e_perm | ~S_IRWXO;
236 ae.e_perm = le16_to_cpu(mask_obj->e_perm) &
237 ((mode >> 3) | ~S_IRWXO);
238 mode &= (ae.e_perm << 3) | ~S_IRWXG;
239 mask_obj->e_perm = cpu_to_le16(ae.e_perm);
243 ae.e_perm = le16_to_cpu(group_obj->e_perm) &
244 ((mode >> 3) | ~S_IRWXO);
245 mode &= (ae.e_perm << 3) | ~S_IRWXG;
246 group_obj->e_perm = cpu_to_le16(ae.e_perm);
249 *pmode = (*pmode & ~S_IRWXUGO) | mode;
252 EXPORT_SYMBOL(lustre_posix_acl_create_masq);
254 /* if "new_count == 0", then "new = {a_version, NULL}", NOT NULL. */
255 static int lustre_posix_acl_xattr_reduce_space(posix_acl_xattr_header **header,
256 int old_count, int new_count)
258 int old_size = CFS_ACL_XATTR_SIZE(old_count, posix_acl_xattr);
259 int new_size = CFS_ACL_XATTR_SIZE(new_count, posix_acl_xattr);
260 posix_acl_xattr_header *new;
262 if (unlikely(old_count <= new_count))
265 OBD_ALLOC(new, new_size);
266 if (unlikely(new == NULL))
269 memcpy(new, *header, new_size);
270 OBD_FREE(*header, old_size);
275 /* if "new_count == 0", then "new = {0, NULL}", NOT NULL. */
276 static int lustre_ext_acl_xattr_reduce_space(ext_acl_xattr_header **header,
279 int ext_count = le32_to_cpu((*header)->a_count);
280 int ext_size = CFS_ACL_XATTR_SIZE(ext_count, ext_acl_xattr);
281 int old_size = CFS_ACL_XATTR_SIZE(old_count, ext_acl_xattr);
282 ext_acl_xattr_header *new;
284 if (unlikely(old_count <= ext_count))
287 OBD_ALLOC(new, ext_size);
288 if (unlikely(new == NULL))
291 memcpy(new, *header, ext_size);
292 OBD_FREE(*header, old_size);
298 * Generate new extended ACL based on the posix ACL.
300 ext_acl_xattr_header *
301 lustre_posix_acl_xattr_2ext(posix_acl_xattr_header *header, int size)
304 ext_acl_xattr_header *new;
307 if (unlikely(size < 0))
308 RETURN(ERR_PTR(-EINVAL));
312 count = CFS_ACL_XATTR_COUNT(size, posix_acl_xattr);
313 esize = CFS_ACL_XATTR_SIZE(count, ext_acl_xattr);
314 OBD_ALLOC(new, esize);
315 if (unlikely(new == NULL))
316 RETURN(ERR_PTR(-ENOMEM));
318 new->a_count = cpu_to_le32(count);
319 for (i = 0; i < count; i++) {
320 new->a_entries[i].e_tag = header->a_entries[i].e_tag;
321 new->a_entries[i].e_perm = header->a_entries[i].e_perm;
322 new->a_entries[i].e_id = header->a_entries[i].e_id;
323 new->a_entries[i].e_stat = cpu_to_le32(ES_UNK);
328 EXPORT_SYMBOL(lustre_posix_acl_xattr_2ext);
331 * Filter out the "nobody" entries in the posix ACL.
333 int lustre_posix_acl_xattr_filter(posix_acl_xattr_header *header, int size,
334 posix_acl_xattr_header **out)
336 int count, i, j, rc = 0;
338 posix_acl_xattr_header *new;
341 if (unlikely(size < 0))
346 OBD_ALLOC(new, size);
347 if (unlikely(new == NULL))
350 new->a_version = cpu_to_le32(CFS_ACL_XATTR_VERSION);
351 count = CFS_ACL_XATTR_COUNT(size, posix_acl_xattr);
352 for (i = 0, j = 0; i < count; i++) {
353 id = le32_to_cpu(header->a_entries[i].e_id);
354 switch (le16_to_cpu(header->a_entries[i].e_tag)) {
359 if (id != ACL_UNDEFINED_ID)
360 GOTO(_out, rc = -EIO);
362 memcpy(&new->a_entries[j++], &header->a_entries[i],
363 sizeof(posix_acl_xattr_entry));
366 if (id != NOBODY_UID)
367 memcpy(&new->a_entries[j++],
368 &header->a_entries[i],
369 sizeof(posix_acl_xattr_entry));
372 if (id != NOBODY_GID)
373 memcpy(&new->a_entries[j++],
374 &header->a_entries[i],
375 sizeof(posix_acl_xattr_entry));
378 GOTO(_out, rc = -EIO);
382 /* free unused space. */
383 rc = lustre_posix_acl_xattr_reduce_space(&new, count, j);
398 EXPORT_SYMBOL(lustre_posix_acl_xattr_filter);
401 * Convert server-side uid/gid in the posix ACL items to the client-side ones.
404 * nothing to be converted.
406 * mapped ids are converted to client-side ones,
407 * unmapped ones are converted to "nobody".
409 * only mapped ids are converted to "nobody".
411 * only unmapped ids are converted to "nobody".
413 int lustre_posix_acl_xattr_id2client(struct md_ucred *mu,
414 struct lustre_idmap_table *t,
415 posix_acl_xattr_header *header,
422 if (unlikely(size < 0))
427 if (unlikely(flags == CFS_IC_NOTHING))
430 count = CFS_ACL_XATTR_COUNT(size, posix_acl_xattr);
431 for (i = 0; i < count; i++) {
432 id = le32_to_cpu(header->a_entries[i].e_id);
433 switch (le16_to_cpu(header->a_entries[i].e_tag)) {
438 if (id != ACL_UNDEFINED_ID)
442 id = lustre_idmap_lookup_uid(mu, t, 1, id);
443 if (flags == CFS_IC_ALL) {
444 if (id == CFS_IDMAP_NOTFOUND)
446 header->a_entries[i].e_id = cpu_to_le32(id);
447 } else if (flags == CFS_IC_MAPPED) {
448 if (id != CFS_IDMAP_NOTFOUND)
449 header->a_entries[i].e_id =
450 cpu_to_le32(NOBODY_UID);
451 } else if (flags == CFS_IC_UNMAPPED) {
452 if (id == CFS_IDMAP_NOTFOUND)
453 header->a_entries[i].e_id =
454 cpu_to_le32(NOBODY_UID);
458 id = lustre_idmap_lookup_gid(mu, t, 1, id);
459 if (flags == CFS_IC_ALL) {
460 if (id == CFS_IDMAP_NOTFOUND)
462 header->a_entries[i].e_id = cpu_to_le32(id);
463 } else if (flags == CFS_IC_MAPPED) {
464 if (id != CFS_IDMAP_NOTFOUND)
465 header->a_entries[i].e_id =
466 cpu_to_le32(NOBODY_GID);
467 } else if (flags == CFS_IC_UNMAPPED) {
468 if (id == CFS_IDMAP_NOTFOUND)
469 header->a_entries[i].e_id =
470 cpu_to_le32(NOBODY_GID);
479 EXPORT_SYMBOL(lustre_posix_acl_xattr_id2client);
482 * Release the posix ACL space.
484 void lustre_posix_acl_xattr_free(posix_acl_xattr_header *header, int size)
486 OBD_FREE(header, size);
488 EXPORT_SYMBOL(lustre_posix_acl_xattr_free);
491 * Converts client-side uid/gid in the extended ACL items to server-side ones.
493 * mapped ids are converted to server-side ones,
494 * unmapped ones cause "EPERM" error.
496 int lustre_ext_acl_xattr_id2server(struct md_ucred *mu,
497 struct lustre_idmap_table *t,
498 ext_acl_xattr_header *header)
501 int i, count = le32_to_cpu(header->a_count);
505 for (i = 0; i < count; i++) {
506 id = le32_to_cpu(header->a_entries[i].e_id);
507 switch (le16_to_cpu(header->a_entries[i].e_tag)) {
512 if (id != ACL_UNDEFINED_ID)
516 id = lustre_idmap_lookup_uid(mu, t, 0, id);
517 if (id == CFS_IDMAP_NOTFOUND)
520 header->a_entries[i].e_id = cpu_to_le32(id);
523 id = lustre_idmap_lookup_gid(mu, t, 0, id);
524 if (id == CFS_IDMAP_NOTFOUND)
527 header->a_entries[i].e_id = cpu_to_le32(id);
535 EXPORT_SYMBOL(lustre_ext_acl_xattr_id2server);
538 * Release the extended ACL space.
540 void lustre_ext_acl_xattr_free(ext_acl_xattr_header *header)
542 OBD_FREE(header, CFS_ACL_XATTR_SIZE(le32_to_cpu(header->a_count), \
545 EXPORT_SYMBOL(lustre_ext_acl_xattr_free);
547 static ext_acl_xattr_entry *
548 lustre_ext_acl_xattr_search(ext_acl_xattr_header *header,
549 posix_acl_xattr_entry *entry, int *pos)
551 int once, start, end, i, j, count = le32_to_cpu(header->a_count);
558 for (i = start; i < end; i++) {
559 if (header->a_entries[i].e_tag == entry->e_tag &&
560 header->a_entries[i].e_id == entry->e_id) {
565 return &header->a_entries[j];
580 * Merge the posix ACL and the extended ACL into new posix ACL.
582 int lustre_acl_xattr_merge2posix(posix_acl_xattr_header *posix_header, int size,
583 ext_acl_xattr_header *ext_header,
584 posix_acl_xattr_header **out)
586 int posix_count, posix_size, i, j;
587 int ext_count = le32_to_cpu(ext_header->a_count), pos = 0, rc = 0;
588 posix_acl_xattr_entry pe = {ACL_MASK, 0, ACL_UNDEFINED_ID};
589 posix_acl_xattr_header *new;
590 ext_acl_xattr_entry *ee, ae;
593 lustre_posix_acl_cpu_to_le(&pe, &pe);
594 ee = lustre_ext_acl_xattr_search(ext_header, &pe, &pos);
595 if (ee == NULL || le32_to_cpu(ee->e_stat) == ES_DEL) {
596 /* there are only base ACL entries at most. */
598 posix_size = CFS_ACL_XATTR_SIZE(posix_count, posix_acl_xattr);
599 OBD_ALLOC(new, posix_size);
600 if (unlikely(new == NULL))
603 new->a_version = cpu_to_le32(CFS_ACL_XATTR_VERSION);
604 for (i = 0, j = 0; i < ext_count; i++) {
605 lustre_ext_acl_le_to_cpu(&ae,
606 &ext_header->a_entries[i]);
611 if (ae.e_id != ACL_UNDEFINED_ID)
612 GOTO(_out, rc = -EIO);
614 if (ae.e_stat != ES_DEL) {
615 new->a_entries[j].e_tag =
616 ext_header->a_entries[i].e_tag;
617 new->a_entries[j].e_perm =
618 ext_header->a_entries[i].e_perm;
619 new->a_entries[j++].e_id =
620 ext_header->a_entries[i].e_id;
626 if (ae.e_stat == ES_DEL)
629 GOTO(_out, rc = -EIO);
633 /* maybe there are valid ACL_USER or ACL_GROUP entries in the
634 * original server-side ACL, they are regarded as ES_UNC stat.*/
637 if (unlikely(size < 0))
643 CFS_ACL_XATTR_COUNT(size, posix_acl_xattr);
644 posix_count = ori_posix_count + ext_count;
646 CFS_ACL_XATTR_SIZE(posix_count, posix_acl_xattr);
647 OBD_ALLOC(new, posix_size);
648 if (unlikely(new == NULL))
651 new->a_version = cpu_to_le32(CFS_ACL_XATTR_VERSION);
652 /* 1. process the unchanged ACL entries
653 * in the original server-side ACL. */
655 for (i = 0, j = 0; i < ori_posix_count; i++) {
656 ee = lustre_ext_acl_xattr_search(ext_header,
657 &posix_header->a_entries[i], &pos);
659 memcpy(&new->a_entries[j++],
660 &posix_header->a_entries[i],
661 sizeof(posix_acl_xattr_entry));
664 /* 2. process the non-deleted entries
665 * from client-side extended ACL. */
666 for (i = 0; i < ext_count; i++) {
667 if (le16_to_cpu(ext_header->a_entries[i].e_stat) !=
669 new->a_entries[j].e_tag =
670 ext_header->a_entries[i].e_tag;
671 new->a_entries[j].e_perm =
672 ext_header->a_entries[i].e_perm;
673 new->a_entries[j++].e_id =
674 ext_header->a_entries[i].e_id;
679 /* free unused space. */
680 rc = lustre_posix_acl_xattr_reduce_space(&new, posix_count, j);
690 OBD_FREE(new, posix_size);
695 EXPORT_SYMBOL(lustre_acl_xattr_merge2posix);
698 * Merge the posix ACL and the extended ACL into new extended ACL.
700 ext_acl_xattr_header *
701 lustre_acl_xattr_merge2ext(posix_acl_xattr_header *posix_header, int size,
702 ext_acl_xattr_header *ext_header)
704 int ori_ext_count, posix_count, ext_count, ext_size;
705 int i, j, pos = 0, rc = 0;
706 posix_acl_xattr_entry pae;
707 ext_acl_xattr_header *new;
708 ext_acl_xattr_entry *ee, eae;
711 if (unlikely(size < 0))
712 RETURN(ERR_PTR(-EINVAL));
716 posix_count = CFS_ACL_XATTR_COUNT(size, posix_acl_xattr);
717 ori_ext_count = le32_to_cpu(ext_header->a_count);
718 ext_count = posix_count + ori_ext_count;
719 ext_size = CFS_ACL_XATTR_SIZE(ext_count, ext_acl_xattr);
721 OBD_ALLOC(new, ext_size);
722 if (unlikely(new == NULL))
723 RETURN(ERR_PTR(-ENOMEM));
725 for (i = 0, j = 0; i < posix_count; i++) {
726 lustre_posix_acl_le_to_cpu(&pae, &posix_header->a_entries[i]);
732 if (pae.e_id != ACL_UNDEFINED_ID)
733 GOTO(out, rc = -EIO);
735 /* ignore "nobody" entry. */
736 if (pae.e_id == NOBODY_UID)
739 new->a_entries[j].e_tag =
740 posix_header->a_entries[i].e_tag;
741 new->a_entries[j].e_perm =
742 posix_header->a_entries[i].e_perm;
743 new->a_entries[j].e_id =
744 posix_header->a_entries[i].e_id;
745 ee = lustre_ext_acl_xattr_search(ext_header,
746 &posix_header->a_entries[i], &pos);
748 if (posix_header->a_entries[i].e_perm !=
750 /* entry modified. */
752 new->a_entries[j++].e_stat =
755 /* entry unchanged. */
757 new->a_entries[j++].e_stat =
761 new->a_entries[j++].e_stat =
766 /* ignore "nobody" entry. */
767 if (pae.e_id == NOBODY_GID)
769 new->a_entries[j].e_tag =
770 posix_header->a_entries[i].e_tag;
771 new->a_entries[j].e_perm =
772 posix_header->a_entries[i].e_perm;
773 new->a_entries[j].e_id =
774 posix_header->a_entries[i].e_id;
775 ee = lustre_ext_acl_xattr_search(ext_header,
776 &posix_header->a_entries[i], &pos);
778 if (posix_header->a_entries[i].e_perm !=
780 /* entry modified. */
782 new->a_entries[j++].e_stat =
785 /* entry unchanged. */
787 new->a_entries[j++].e_stat =
791 new->a_entries[j++].e_stat =
796 GOTO(out, rc = -EIO);
800 /* process deleted entries. */
801 for (i = 0; i < ori_ext_count; i++) {
802 lustre_ext_acl_le_to_cpu(&eae, &ext_header->a_entries[i]);
803 if (eae.e_stat == ES_UNK) {
804 /* ignore "nobody" entry. */
805 if ((eae.e_tag == ACL_USER && eae.e_id == NOBODY_UID) ||
806 (eae.e_tag == ACL_GROUP && eae.e_id == NOBODY_GID))
809 new->a_entries[j].e_tag =
810 ext_header->a_entries[i].e_tag;
811 new->a_entries[j].e_perm =
812 ext_header->a_entries[i].e_perm;
813 new->a_entries[j].e_id = ext_header->a_entries[i].e_id;
814 new->a_entries[j++].e_stat = cpu_to_le32(ES_DEL);
818 new->a_count = cpu_to_le32(j);
819 /* free unused space. */
820 rc = lustre_ext_acl_xattr_reduce_space(&new, ext_count);
825 OBD_FREE(new, ext_size);
830 EXPORT_SYMBOL(lustre_acl_xattr_merge2ext);