initial code repo
[stor4nfv.git] / src / ceph / src / client / posix_acl.cc
diff --git a/src/ceph/src/client/posix_acl.cc b/src/ceph/src/client/posix_acl.cc
new file mode 100644 (file)
index 0000000..e6331ed
--- /dev/null
@@ -0,0 +1,290 @@
+#include "include/types.h"
+#include <sys/stat.h>
+#include "posix_acl.h"
+#include "UserPerm.h"
+
+#ifndef ACCESSPERMS
+#define ACCESSPERMS (S_IRWXU|S_IRWXG|S_IRWXO)
+#endif
+
+int posix_acl_check(const void *xattr, size_t size)
+{
+  const acl_ea_header *header;
+  if (size < sizeof(*header))
+    return -1;
+  header = reinterpret_cast<const acl_ea_header*>(xattr);
+  ceph_le32 expected_version;
+  expected_version = ACL_EA_VERSION;
+  if (header->a_version != expected_version)
+    return -1;
+
+  const acl_ea_entry *entry = header->a_entries;
+  size -= sizeof(*header);
+  if (size % sizeof(*entry))
+    return -1;
+
+  int count = size / sizeof(*entry);
+  if (count == 0)
+    return 0;
+
+  int state = ACL_USER_OBJ;
+  int needs_mask = 0;
+  for (int i = 0; i < count; ++i) {
+    __u16 tag = entry->e_tag;
+    switch(tag) {
+    case ACL_USER_OBJ:
+      if (state == ACL_USER_OBJ) {
+        state = ACL_USER;
+        break;
+      }
+      return -1;
+    case ACL_USER:
+      if (state != ACL_USER)
+        return -1;
+      needs_mask = 1;
+      break;
+    case ACL_GROUP_OBJ:
+      if (state == ACL_USER) {
+        state = ACL_GROUP;
+        break;
+      }
+      return -1;
+    case ACL_GROUP:
+      if (state != ACL_GROUP)
+        return -1;
+      needs_mask = 1;
+      break;
+    case ACL_MASK:
+      if (state != ACL_GROUP)
+        return -1;
+      state = ACL_OTHER;
+      break;
+    case ACL_OTHER:
+      if (state == ACL_OTHER ||
+          (state == ACL_GROUP && !needs_mask)) {
+        state = 0;
+        break;
+      }
+      // fall-thru
+    default:
+      return -1;
+    }
+    ++entry;
+  }
+
+  return state == 0 ? count : -1;
+}
+
+int posix_acl_equiv_mode(const void *xattr, size_t size, mode_t *mode_p)
+{
+  if (posix_acl_check(xattr, size) < 0)
+    return -EINVAL;
+
+  int not_equiv = 0;
+  mode_t mode = 0;
+
+  const acl_ea_header *header = reinterpret_cast<const acl_ea_header*>(xattr);
+  const acl_ea_entry *entry = header->a_entries;
+  int count = (size - sizeof(*header)) / sizeof(*entry);
+  for (int i = 0; i < count; ++i) {
+    __u16 tag = entry->e_tag;
+    __u16 perm = entry->e_perm;
+    switch(tag) {
+      case ACL_USER_OBJ:
+       mode |= (perm & S_IRWXO) << 6;
+       break;
+      case ACL_GROUP_OBJ:
+       mode |= (perm & S_IRWXO) << 3;
+       break;
+      case ACL_OTHER:
+       mode |= perm & S_IRWXO;
+       break;
+      case ACL_MASK:
+       mode = (mode & ~S_IRWXG) | ((perm & S_IRWXO) << 3);
+       /* fall through */
+      case ACL_USER:
+      case ACL_GROUP:
+       not_equiv = 1;
+       break;
+      default:
+       return -EINVAL;
+    }
+    ++entry;
+  }
+  if (mode_p)
+    *mode_p = (*mode_p & ~ACCESSPERMS) | mode;
+  return not_equiv;
+}
+
+int posix_acl_inherit_mode(bufferptr& acl, mode_t *mode_p)
+{
+  if (posix_acl_check(acl.c_str(), acl.length()) <= 0)
+    return -EIO;
+
+  acl_ea_entry *group_entry = NULL, *mask_entry = NULL;
+  mode_t mode = *mode_p;
+  int not_equiv = 0;
+
+  acl_ea_header *header = reinterpret_cast<acl_ea_header*>(acl.c_str());
+  acl_ea_entry *entry = header->a_entries;
+  int count = (acl.length() - sizeof(*header)) / sizeof(*entry);
+  for (int i = 0; i < count; ++i) {
+    __u16 tag = entry->e_tag;
+    __u16 perm = entry->e_perm;
+    switch(tag) {
+      case ACL_USER_OBJ:
+       perm &= (mode >> 6) | ~S_IRWXO;
+       mode &= (perm << 6) | ~S_IRWXU;
+       entry->e_perm = perm;
+       break;
+      case ACL_USER:
+      case ACL_GROUP:
+       not_equiv = 1;
+       break;
+      case ACL_GROUP_OBJ:
+       group_entry = entry;
+       break;
+      case ACL_OTHER:
+       perm &= mode | ~S_IRWXO;
+       mode &= perm | ~S_IRWXO;
+       entry->e_perm = perm;
+       break;
+      case ACL_MASK:
+       mask_entry = entry;
+       not_equiv = 1;
+       break;
+      default:
+       return -EIO;
+
+    }
+    ++entry;
+  }
+
+  if (mask_entry) {
+    __u16 perm = mask_entry->e_perm;
+    perm &= (mode >> 3) | ~S_IRWXO;
+    mode &= (perm << 3) | ~S_IRWXG;
+    mask_entry->e_perm = perm;
+  } else {
+    if (!group_entry)
+      return -EIO;
+    __u16 perm = group_entry->e_perm;
+    perm &= (mode >> 3) | ~S_IRWXO;
+    mode &= (perm << 3) | ~S_IRWXG;
+    group_entry->e_perm = perm;
+  }
+
+  *mode_p = (*mode_p & ~ACCESSPERMS) | mode;
+  return not_equiv;
+}
+
+int posix_acl_access_chmod(bufferptr& acl, mode_t mode)
+{
+  if (posix_acl_check(acl.c_str(), acl.length()) <= 0)
+    return -EIO;
+
+  acl_ea_entry *group_entry = NULL, *mask_entry = NULL;
+
+  acl_ea_header *header = reinterpret_cast<acl_ea_header*>(acl.c_str());
+  acl_ea_entry *entry = header->a_entries;
+  int count = (acl.length() - sizeof(*header)) / sizeof(*entry);
+  for (int i = 0; i < count; ++i) {
+    __u16 tag = entry->e_tag;
+    switch(tag) {
+      case ACL_USER_OBJ:
+       entry->e_perm = (mode & S_IRWXU) >> 6;
+       break;
+      case ACL_GROUP_OBJ:
+       group_entry = entry;
+       break;
+      case ACL_MASK:
+       mask_entry = entry;
+       break;
+      case ACL_OTHER:
+       entry->e_perm = mode & S_IRWXO;
+       break;
+      default:
+       break;
+    }
+    ++entry;
+  }
+
+  if (mask_entry) {
+    mask_entry->e_perm = (mode & S_IRWXG) >> 3;
+  } else {
+    if (!group_entry)
+      return -EIO;
+    group_entry->e_perm = (mode & S_IRWXG) >> 3;
+  }
+  return 0;
+}
+
+int posix_acl_permits(const bufferptr& acl, uid_t i_uid, gid_t i_gid,
+                        const UserPerm& perms, unsigned want)
+{
+  if (posix_acl_check(acl.c_str(), acl.length()) < 0)
+    return -EIO;
+
+  const acl_ea_header *header = reinterpret_cast<const acl_ea_header*>(acl.c_str());
+  const acl_ea_entry *entry = header->a_entries;
+  const acl_ea_entry *next_entry;
+  __u16 perm, tag;
+  __u32 id;
+  int group_found = 0;
+  int idx;
+  int count = (acl.length() - sizeof(*header)) / sizeof(*entry);
+  for (idx = 0; idx < count; ++idx) {
+    tag = entry->e_tag;
+    perm = entry->e_perm;
+    switch(tag) {
+      case ACL_USER_OBJ:
+       if (i_uid == perms.uid())
+         goto check_perm;
+       break;
+      case ACL_USER:
+       id = entry->e_id;
+       if (id == perms.uid())
+         goto check_mask;
+       break;
+      case ACL_GROUP_OBJ:
+       /* fall through */
+      case ACL_GROUP:
+       id = (tag == ACL_GROUP_OBJ) ? i_gid : entry->e_id;
+       if (perms.gid_in_groups(id)) {
+         group_found = 1;
+         if ((perm & want) == want)
+           goto check_mask;
+       }
+       break;
+      case ACL_MASK:
+       break;
+      case ACL_OTHER:
+       if (group_found)
+         return -EACCES;
+       else
+         goto check_perm;
+       break;
+      default:
+       return -EIO;
+    }
+    ++entry;
+  }
+  return -EIO;
+
+check_mask:
+  next_entry = entry + 1;
+  for (++idx; idx < count; ++idx) {
+    tag = next_entry->e_tag;
+    if (tag == ACL_MASK) {
+      __u16 mask = next_entry->e_perm;
+      if ((perm & mask & want) == want)
+       return 0;
+      return -EACCES;
+    }
+    ++next_entry;
+  }
+check_perm:
+  if ((perm & want) == want)
+    return 0;
+  return -EACCES;
+}