Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / staging / lustre / lustre / obdclass / acl.c
diff --git a/kernel/drivers/staging/lustre/lustre/obdclass/acl.c b/kernel/drivers/staging/lustre/lustre/obdclass/acl.c
new file mode 100644 (file)
index 0000000..9a69f6b
--- /dev/null
@@ -0,0 +1,548 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/obdclass/acl.c
+ *
+ * Lustre Access Control List.
+ *
+ * Author: Fan Yong <fanyong@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_SEC
+#include "../include/lu_object.h"
+#include "../include/lustre_acl.h"
+#include "../include/lustre_eacl.h"
+#include "../include/obd_support.h"
+
+#ifdef CONFIG_FS_POSIX_ACL
+
+#define CFS_ACL_XATTR_VERSION POSIX_ACL_XATTR_VERSION
+
+enum {
+       ES_UNK  = 0,    /* unknown stat */
+       ES_UNC  = 1,    /* ACL entry is not changed */
+       ES_MOD  = 2,    /* ACL entry is modified */
+       ES_ADD  = 3,    /* ACL entry is added */
+       ES_DEL  = 4     /* ACL entry is deleted */
+};
+
+static inline void lustre_ext_acl_le_to_cpu(ext_acl_xattr_entry *d,
+                                           ext_acl_xattr_entry *s)
+{
+       d->e_tag        = le16_to_cpu(s->e_tag);
+       d->e_perm       = le16_to_cpu(s->e_perm);
+       d->e_id  = le32_to_cpu(s->e_id);
+       d->e_stat       = le32_to_cpu(s->e_stat);
+}
+
+static inline void lustre_ext_acl_cpu_to_le(ext_acl_xattr_entry *d,
+                                           ext_acl_xattr_entry *s)
+{
+       d->e_tag        = cpu_to_le16(s->e_tag);
+       d->e_perm       = cpu_to_le16(s->e_perm);
+       d->e_id  = cpu_to_le32(s->e_id);
+       d->e_stat       = cpu_to_le32(s->e_stat);
+}
+
+static inline void lustre_posix_acl_le_to_cpu(posix_acl_xattr_entry *d,
+                                             posix_acl_xattr_entry *s)
+{
+       d->e_tag        = le16_to_cpu(s->e_tag);
+       d->e_perm       = le16_to_cpu(s->e_perm);
+       d->e_id  = le32_to_cpu(s->e_id);
+}
+
+static inline void lustre_posix_acl_cpu_to_le(posix_acl_xattr_entry *d,
+                                             posix_acl_xattr_entry *s)
+{
+       d->e_tag        = cpu_to_le16(s->e_tag);
+       d->e_perm       = cpu_to_le16(s->e_perm);
+       d->e_id  = cpu_to_le32(s->e_id);
+}
+
+
+/* if "new_count == 0", then "new = {a_version, NULL}", NOT NULL. */
+static int lustre_posix_acl_xattr_reduce_space(posix_acl_xattr_header **header,
+                                              int old_count, int new_count)
+{
+       int old_size = CFS_ACL_XATTR_SIZE(old_count, posix_acl_xattr);
+       int new_size = CFS_ACL_XATTR_SIZE(new_count, posix_acl_xattr);
+       posix_acl_xattr_header *new;
+
+       if (unlikely(old_count <= new_count))
+               return old_size;
+
+       OBD_ALLOC(new, new_size);
+       if (unlikely(new == NULL))
+               return -ENOMEM;
+
+       memcpy(new, *header, new_size);
+       OBD_FREE(*header, old_size);
+       *header = new;
+       return new_size;
+}
+
+/* if "new_count == 0", then "new = {0, NULL}", NOT NULL. */
+static int lustre_ext_acl_xattr_reduce_space(ext_acl_xattr_header **header,
+                                            int old_count)
+{
+       int ext_count = le32_to_cpu((*header)->a_count);
+       int ext_size = CFS_ACL_XATTR_SIZE(ext_count, ext_acl_xattr);
+       int old_size = CFS_ACL_XATTR_SIZE(old_count, ext_acl_xattr);
+       ext_acl_xattr_header *new;
+
+       if (unlikely(old_count <= ext_count))
+               return 0;
+
+       OBD_ALLOC(new, ext_size);
+       if (unlikely(new == NULL))
+               return -ENOMEM;
+
+       memcpy(new, *header, ext_size);
+       OBD_FREE(*header, old_size);
+       *header = new;
+       return 0;
+}
+
+/*
+ * Generate new extended ACL based on the posix ACL.
+ */
+ext_acl_xattr_header *
+lustre_posix_acl_xattr_2ext(posix_acl_xattr_header *header, int size)
+{
+       int count, i, esize;
+       ext_acl_xattr_header *new;
+
+       if (unlikely(size < 0))
+               return ERR_PTR(-EINVAL);
+       else if (!size)
+               count = 0;
+       else
+               count = CFS_ACL_XATTR_COUNT(size, posix_acl_xattr);
+       esize = CFS_ACL_XATTR_SIZE(count, ext_acl_xattr);
+       OBD_ALLOC(new, esize);
+       if (unlikely(new == NULL))
+               return ERR_PTR(-ENOMEM);
+
+       new->a_count = cpu_to_le32(count);
+       for (i = 0; i < count; i++) {
+               new->a_entries[i].e_tag  = header->a_entries[i].e_tag;
+               new->a_entries[i].e_perm = header->a_entries[i].e_perm;
+               new->a_entries[i].e_id   = header->a_entries[i].e_id;
+               new->a_entries[i].e_stat = cpu_to_le32(ES_UNK);
+       }
+
+       return new;
+}
+EXPORT_SYMBOL(lustre_posix_acl_xattr_2ext);
+
+/*
+ * Filter out the "nobody" entries in the posix ACL.
+ */
+int lustre_posix_acl_xattr_filter(posix_acl_xattr_header *header, size_t size,
+                                 posix_acl_xattr_header **out)
+{
+       int count, i, j, rc = 0;
+       __u32 id;
+       posix_acl_xattr_header *new;
+
+       if (!size)
+               return 0;
+       if (size < sizeof(*new))
+               return -EINVAL;
+
+       OBD_ALLOC(new, size);
+       if (unlikely(new == NULL))
+               return -ENOMEM;
+
+       new->a_version = cpu_to_le32(CFS_ACL_XATTR_VERSION);
+       count = CFS_ACL_XATTR_COUNT(size, posix_acl_xattr);
+       for (i = 0, j = 0; i < count; i++) {
+               id = le32_to_cpu(header->a_entries[i].e_id);
+               switch (le16_to_cpu(header->a_entries[i].e_tag)) {
+               case ACL_USER_OBJ:
+               case ACL_GROUP_OBJ:
+               case ACL_MASK:
+               case ACL_OTHER:
+                       if (id != ACL_UNDEFINED_ID) {
+                               rc = -EIO;
+                               goto _out;
+                       }
+
+                       memcpy(&new->a_entries[j++], &header->a_entries[i],
+                              sizeof(posix_acl_xattr_entry));
+                       break;
+               case ACL_USER:
+                       if (id != NOBODY_UID)
+                               memcpy(&new->a_entries[j++],
+                                      &header->a_entries[i],
+                                      sizeof(posix_acl_xattr_entry));
+                       break;
+               case ACL_GROUP:
+                       if (id != NOBODY_GID)
+                               memcpy(&new->a_entries[j++],
+                                      &header->a_entries[i],
+                                      sizeof(posix_acl_xattr_entry));
+                       break;
+               default:
+                       rc = -EIO;
+                       goto _out;
+               }
+       }
+
+       /* free unused space. */
+       rc = lustre_posix_acl_xattr_reduce_space(&new, count, j);
+       if (rc >= 0) {
+               size = rc;
+               *out = new;
+               rc = 0;
+       }
+
+_out:
+       if (rc) {
+               OBD_FREE(new, size);
+               size = rc;
+       }
+       return size;
+}
+EXPORT_SYMBOL(lustre_posix_acl_xattr_filter);
+
+/*
+ * Release the posix ACL space.
+ */
+void lustre_posix_acl_xattr_free(posix_acl_xattr_header *header, int size)
+{
+       OBD_FREE(header, size);
+}
+EXPORT_SYMBOL(lustre_posix_acl_xattr_free);
+
+/*
+ * Release the extended ACL space.
+ */
+void lustre_ext_acl_xattr_free(ext_acl_xattr_header *header)
+{
+       OBD_FREE(header, CFS_ACL_XATTR_SIZE(le32_to_cpu(header->a_count), \
+                                           ext_acl_xattr));
+}
+EXPORT_SYMBOL(lustre_ext_acl_xattr_free);
+
+static ext_acl_xattr_entry *
+lustre_ext_acl_xattr_search(ext_acl_xattr_header *header,
+                           posix_acl_xattr_entry *entry, int *pos)
+{
+       int once, start, end, i, j, count = le32_to_cpu(header->a_count);
+
+       once = 0;
+       start = *pos;
+       end = count;
+
+again:
+       for (i = start; i < end; i++) {
+               if (header->a_entries[i].e_tag == entry->e_tag &&
+                   header->a_entries[i].e_id == entry->e_id) {
+                       j = i;
+                       if (++i >= count)
+                               i = 0;
+                       *pos = i;
+                       return &header->a_entries[j];
+               }
+       }
+
+       if (!once) {
+               once = 1;
+               start = 0;
+               end = *pos;
+               goto again;
+       }
+
+       return NULL;
+}
+
+/*
+ * Merge the posix ACL and the extended ACL into new posix ACL.
+ */
+int lustre_acl_xattr_merge2posix(posix_acl_xattr_header *posix_header, int size,
+                                ext_acl_xattr_header *ext_header,
+                                posix_acl_xattr_header **out)
+{
+       int posix_count, posix_size, i, j;
+       int ext_count = le32_to_cpu(ext_header->a_count), pos = 0, rc = 0;
+       posix_acl_xattr_entry pe = {ACL_MASK, 0, ACL_UNDEFINED_ID};
+       posix_acl_xattr_header *new;
+       ext_acl_xattr_entry *ee, ae;
+
+       lustre_posix_acl_cpu_to_le(&pe, &pe);
+       ee = lustre_ext_acl_xattr_search(ext_header, &pe, &pos);
+       if (ee == NULL || le32_to_cpu(ee->e_stat) == ES_DEL) {
+               /* there are only base ACL entries at most. */
+               posix_count = 3;
+               posix_size = CFS_ACL_XATTR_SIZE(posix_count, posix_acl_xattr);
+               OBD_ALLOC(new, posix_size);
+               if (unlikely(new == NULL))
+                       return -ENOMEM;
+
+               new->a_version = cpu_to_le32(CFS_ACL_XATTR_VERSION);
+               for (i = 0, j = 0; i < ext_count; i++) {
+                       lustre_ext_acl_le_to_cpu(&ae,
+                                                &ext_header->a_entries[i]);
+                       switch (ae.e_tag) {
+                       case ACL_USER_OBJ:
+                       case ACL_GROUP_OBJ:
+                       case ACL_OTHER:
+                               if (ae.e_id != ACL_UNDEFINED_ID) {
+                                       rc = -EIO;
+                                       goto _out;
+                               }
+
+                               if (ae.e_stat != ES_DEL) {
+                                       new->a_entries[j].e_tag =
+                                               ext_header->a_entries[i].e_tag;
+                                       new->a_entries[j].e_perm =
+                                               ext_header->a_entries[i].e_perm;
+                                       new->a_entries[j++].e_id =
+                                               ext_header->a_entries[i].e_id;
+                               }
+                               break;
+                       case ACL_MASK:
+                       case ACL_USER:
+                       case ACL_GROUP:
+                               if (ae.e_stat == ES_DEL)
+                                       break;
+                       default:
+                               rc = -EIO;
+                               goto _out;
+                       }
+               }
+       } else {
+               /* maybe there are valid ACL_USER or ACL_GROUP entries in the
+                * original server-side ACL, they are regarded as ES_UNC stat.*/
+               int ori_posix_count;
+
+               if (unlikely(size < 0))
+                       return -EINVAL;
+               else if (!size)
+                       ori_posix_count = 0;
+               else
+                       ori_posix_count =
+                               CFS_ACL_XATTR_COUNT(size, posix_acl_xattr);
+               posix_count = ori_posix_count + ext_count;
+               posix_size =
+                       CFS_ACL_XATTR_SIZE(posix_count, posix_acl_xattr);
+               OBD_ALLOC(new, posix_size);
+               if (unlikely(new == NULL))
+                       return -ENOMEM;
+
+               new->a_version = cpu_to_le32(CFS_ACL_XATTR_VERSION);
+               /* 1. process the unchanged ACL entries
+                *    in the original server-side ACL. */
+               pos = 0;
+               for (i = 0, j = 0; i < ori_posix_count; i++) {
+                       ee = lustre_ext_acl_xattr_search(ext_header,
+                                       &posix_header->a_entries[i], &pos);
+                       if (ee == NULL)
+                               memcpy(&new->a_entries[j++],
+                                      &posix_header->a_entries[i],
+                                      sizeof(posix_acl_xattr_entry));
+               }
+
+               /* 2. process the non-deleted entries
+                *    from client-side extended ACL. */
+               for (i = 0; i < ext_count; i++) {
+                       if (le16_to_cpu(ext_header->a_entries[i].e_stat) !=
+                           ES_DEL) {
+                               new->a_entries[j].e_tag =
+                                               ext_header->a_entries[i].e_tag;
+                               new->a_entries[j].e_perm =
+                                               ext_header->a_entries[i].e_perm;
+                               new->a_entries[j++].e_id =
+                                               ext_header->a_entries[i].e_id;
+                       }
+               }
+       }
+
+       /* free unused space. */
+       rc = lustre_posix_acl_xattr_reduce_space(&new, posix_count, j);
+       if (rc >= 0) {
+               posix_size = rc;
+               *out = new;
+               rc = 0;
+       }
+
+_out:
+       if (rc) {
+               OBD_FREE(new, posix_size);
+               posix_size = rc;
+       }
+       return posix_size;
+}
+EXPORT_SYMBOL(lustre_acl_xattr_merge2posix);
+
+/*
+ * Merge the posix ACL and the extended ACL into new extended ACL.
+ */
+ext_acl_xattr_header *
+lustre_acl_xattr_merge2ext(posix_acl_xattr_header *posix_header, int size,
+                          ext_acl_xattr_header *ext_header)
+{
+       int ori_ext_count, posix_count, ext_count, ext_size;
+       int i, j, pos = 0, rc = 0;
+       posix_acl_xattr_entry pae;
+       ext_acl_xattr_header *new;
+       ext_acl_xattr_entry *ee, eae;
+
+       if (unlikely(size < 0))
+               return ERR_PTR(-EINVAL);
+       else if (!size)
+               posix_count = 0;
+       else
+               posix_count = CFS_ACL_XATTR_COUNT(size, posix_acl_xattr);
+       ori_ext_count = le32_to_cpu(ext_header->a_count);
+       ext_count = posix_count + ori_ext_count;
+       ext_size = CFS_ACL_XATTR_SIZE(ext_count, ext_acl_xattr);
+
+       OBD_ALLOC(new, ext_size);
+       if (unlikely(new == NULL))
+               return ERR_PTR(-ENOMEM);
+
+       for (i = 0, j = 0; i < posix_count; i++) {
+               lustre_posix_acl_le_to_cpu(&pae, &posix_header->a_entries[i]);
+               switch (pae.e_tag) {
+               case ACL_USER_OBJ:
+               case ACL_GROUP_OBJ:
+               case ACL_MASK:
+               case ACL_OTHER:
+                       if (pae.e_id != ACL_UNDEFINED_ID) {
+                               rc = -EIO;
+                               goto out;
+               }
+               case ACL_USER:
+                       /* ignore "nobody" entry. */
+                       if (pae.e_id == NOBODY_UID)
+                               break;
+
+                       new->a_entries[j].e_tag =
+                                       posix_header->a_entries[i].e_tag;
+                       new->a_entries[j].e_perm =
+                                       posix_header->a_entries[i].e_perm;
+                       new->a_entries[j].e_id =
+                                       posix_header->a_entries[i].e_id;
+                       ee = lustre_ext_acl_xattr_search(ext_header,
+                                       &posix_header->a_entries[i], &pos);
+                       if (ee) {
+                               if (posix_header->a_entries[i].e_perm !=
+                                                               ee->e_perm)
+                                       /* entry modified. */
+                                       ee->e_stat =
+                                       new->a_entries[j++].e_stat =
+                                                       cpu_to_le32(ES_MOD);
+                               else
+                                       /* entry unchanged. */
+                                       ee->e_stat =
+                                       new->a_entries[j++].e_stat =
+                                                       cpu_to_le32(ES_UNC);
+                       } else {
+                               /* new entry. */
+                               new->a_entries[j++].e_stat =
+                                                       cpu_to_le32(ES_ADD);
+                       }
+                       break;
+               case ACL_GROUP:
+                       /* ignore "nobody" entry. */
+                       if (pae.e_id == NOBODY_GID)
+                               break;
+                       new->a_entries[j].e_tag =
+                                       posix_header->a_entries[i].e_tag;
+                       new->a_entries[j].e_perm =
+                                       posix_header->a_entries[i].e_perm;
+                       new->a_entries[j].e_id =
+                                       posix_header->a_entries[i].e_id;
+                       ee = lustre_ext_acl_xattr_search(ext_header,
+                                       &posix_header->a_entries[i], &pos);
+                       if (ee) {
+                               if (posix_header->a_entries[i].e_perm !=
+                                                               ee->e_perm)
+                                       /* entry modified. */
+                                       ee->e_stat =
+                                       new->a_entries[j++].e_stat =
+                                                       cpu_to_le32(ES_MOD);
+                               else
+                                       /* entry unchanged. */
+                                       ee->e_stat =
+                                       new->a_entries[j++].e_stat =
+                                                       cpu_to_le32(ES_UNC);
+                       } else {
+                               /* new entry. */
+                               new->a_entries[j++].e_stat =
+                                                       cpu_to_le32(ES_ADD);
+                       }
+                       break;
+               default:
+                       rc = -EIO;
+                       goto out;
+               }
+       }
+
+       /* process deleted entries. */
+       for (i = 0; i < ori_ext_count; i++) {
+               lustre_ext_acl_le_to_cpu(&eae, &ext_header->a_entries[i]);
+               if (eae.e_stat == ES_UNK) {
+                       /* ignore "nobody" entry. */
+                       if ((eae.e_tag == ACL_USER && eae.e_id == NOBODY_UID) ||
+                           (eae.e_tag == ACL_GROUP && eae.e_id == NOBODY_GID))
+                               continue;
+
+                       new->a_entries[j].e_tag =
+                                               ext_header->a_entries[i].e_tag;
+                       new->a_entries[j].e_perm =
+                                               ext_header->a_entries[i].e_perm;
+                       new->a_entries[j].e_id = ext_header->a_entries[i].e_id;
+                       new->a_entries[j++].e_stat = cpu_to_le32(ES_DEL);
+               }
+       }
+
+       new->a_count = cpu_to_le32(j);
+       /* free unused space. */
+       rc = lustre_ext_acl_xattr_reduce_space(&new, ext_count);
+
+out:
+       if (rc) {
+               OBD_FREE(new, ext_size);
+               new = ERR_PTR(rc);
+       }
+       return new;
+}
+EXPORT_SYMBOL(lustre_acl_xattr_merge2ext);
+
+#endif