summaryrefslogtreecommitdiffstats
path: root/xlators/system
diff options
context:
space:
mode:
authorAnand Avati <avati@gluster.com>2011-07-01 17:18:10 +0000
committerAnand Avati <avati@gluster.com>2011-07-08 02:26:11 -0700
commit04ed499a8eef1c77c491227941d187778fb5c2e4 (patch)
tree7ba88ae7d30a01c22bba8e8c70abc6de301ce125 /xlators/system
parent6433214452c5d8d3ae63c5f4f578862be348ca63 (diff)
posix-acl: implementation of POSIX ACL as a translator
Signed-off-by: Anand Avati <avati@gluster.com> BUG: 2815 (Server-enforced ACLs) URL: http://bugs.gluster.com/cgi-bin/bugzilla3/show_bug.cgi?id=2815
Diffstat (limited to 'xlators/system')
-rw-r--r--xlators/system/Makefile.am1
-rw-r--r--xlators/system/posix-acl/Makefile.am1
-rw-r--r--xlators/system/posix-acl/src/Makefile.am13
-rw-r--r--xlators/system/posix-acl/src/posix-acl-xattr.c190
-rw-r--r--xlators/system/posix-acl/src/posix-acl-xattr.h52
-rw-r--r--xlators/system/posix-acl/src/posix-acl.c1792
-rw-r--r--xlators/system/posix-acl/src/posix-acl.h86
7 files changed, 2135 insertions, 0 deletions
diff --git a/xlators/system/Makefile.am b/xlators/system/Makefile.am
new file mode 100644
index 0000000..65aeea5
--- /dev/null
+++ b/xlators/system/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = posix-acl \ No newline at end of file
diff --git a/xlators/system/posix-acl/Makefile.am b/xlators/system/posix-acl/Makefile.am
new file mode 100644
index 0000000..af437a6
--- /dev/null
+++ b/xlators/system/posix-acl/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = src
diff --git a/xlators/system/posix-acl/src/Makefile.am b/xlators/system/posix-acl/src/Makefile.am
new file mode 100644
index 0000000..57ff27e
--- /dev/null
+++ b/xlators/system/posix-acl/src/Makefile.am
@@ -0,0 +1,13 @@
+xlator_LTLIBRARIES = posix-acl.la
+xlatordir = $(libdir)/glusterfs/$(PACKAGE_VERSION)/xlator/system
+posix_acl_la_LDFLAGS = -module -avoidversion
+posix_acl_la_SOURCES = posix-acl.c posix-acl-xattr.c
+posix_acl_la_LIBADD = $(top_builddir)/libglusterfs/src/libglusterfs.la
+
+noinst_HEADERS = posix-acl.h posix-acl-xattr.h
+
+AM_CFLAGS = -fPIC -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -Wall -D$(GF_HOST_OS)\
+ -I$(top_srcdir)/libglusterfs/src -shared -nostartfiles $(GF_CFLAGS)\
+ -L$(xlatordir)/
+
+CLEANFILES =
diff --git a/xlators/system/posix-acl/src/posix-acl-xattr.c b/xlators/system/posix-acl/src/posix-acl-xattr.c
new file mode 100644
index 0000000..a473c36
--- /dev/null
+++ b/xlators/system/posix-acl/src/posix-acl-xattr.c
@@ -0,0 +1,190 @@
+/*
+ Copyright (c) 2011 Gluster, Inc. <http://www.gluster.com>
+ This file is part of GlusterFS.
+
+ GlusterFS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation; either version 3 of the License,
+ or (at your option) any later version.
+
+ GlusterFS 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
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "posix-acl.h"
+#include "posix-acl-xattr.h"
+
+
+int
+posix_ace_cmp (const void *val1, const void *val2)
+{
+ const struct posix_ace *ace1 = NULL;
+ const struct posix_ace *ace2 = NULL;
+ int ret = 0;
+
+ ace1 = val1;
+ ace2 = val2;
+
+ ret = (ace1->tag - ace2->tag);
+ if (!ret)
+ ret = (ace1->id - ace2->id);
+
+ return ret;
+}
+
+
+void
+posix_acl_normalize (xlator_t *this, struct posix_acl *acl)
+{
+ qsort (acl->entries, acl->count, sizeof (struct posix_ace *),
+ posix_ace_cmp);
+}
+
+
+struct posix_acl *
+posix_acl_from_xattr (xlator_t *this, const char *xattr_buf, int xattr_size)
+{
+ struct posix_acl_xattr_header *header = NULL;
+ struct posix_acl_xattr_entry *entry = NULL;
+ struct posix_acl *acl = NULL;
+ struct posix_ace *ace = NULL;
+ int size = 0;
+ int count = 0;
+ int i = 0;
+
+ size = xattr_size;
+
+ if (size < sizeof (*header))
+ return NULL;
+
+ size -= sizeof (*header);
+
+ if (size % sizeof (*entry))
+ return NULL;
+
+ count = size / sizeof (*entry);
+
+ header = (struct posix_acl_xattr_header *) (xattr_buf);
+ entry = (struct posix_acl_xattr_entry *) (header + 1);
+
+ if (header->version != htole32 (POSIX_ACL_VERSION))
+ return NULL;
+
+ acl = posix_acl_new (this, count);
+ if (!acl)
+ return NULL;
+
+ ace = acl->entries;
+
+ for (i = 0; i < count; i++) {
+ ace->tag = letoh16 (entry->tag);
+ ace->perm = letoh16 (entry->perm);
+
+ switch (ace->tag) {
+ case POSIX_ACL_USER_OBJ:
+ case POSIX_ACL_GROUP_OBJ:
+ case POSIX_ACL_MASK:
+ case POSIX_ACL_OTHER:
+ ace->id = POSIX_ACL_UNDEFINED_ID;
+ break;
+
+ case POSIX_ACL_GROUP:
+ case POSIX_ACL_USER:
+ ace->id = letoh32 (entry->id);
+ break;
+
+ default:
+ goto err;
+ }
+
+ ace++;
+ entry++;
+ }
+
+ posix_acl_normalize (this, acl);
+
+ return acl;
+err:
+ posix_acl_destroy (this, acl);
+ return NULL;
+}
+
+
+int
+posix_acl_to_xattr (xlator_t *this, struct posix_acl *acl, char *xattr_buf,
+ int xattr_size)
+{
+ int size = 0;
+ struct posix_acl_xattr_header *header = NULL;
+ struct posix_acl_xattr_entry *entry = NULL;
+ struct posix_ace *ace = NULL;
+ int i = 0;
+
+ size = sizeof (*header) + (acl->count * sizeof (*entry));
+
+ if (xattr_size < size)
+ return size;
+
+ header = (struct posix_acl_xattr_header *) (xattr_buf);
+ entry = (struct posix_acl_xattr_entry *) (header + 1);
+ ace = acl->entries;
+
+ header->version = htole32 (POSIX_ACL_VERSION);
+
+ for (i = 0; i < acl->count; i++) {
+ entry->tag = htole16 (ace->tag);
+ entry->perm = htole16 (ace->perm);
+
+ switch (ace->tag) {
+ case POSIX_ACL_USER:
+ case POSIX_ACL_GROUP:
+ entry->id = htole32 (ace->id);
+ break;
+ default:
+ entry->id = POSIX_ACL_UNDEFINED_ID;
+ break;
+ }
+
+ ace++;
+ entry++;
+ }
+
+ return 0;
+}
+
+
+int
+posix_acl_matches_xattr (xlator_t *this, struct posix_acl *acl, const char *buf,
+ int size)
+{
+ struct posix_acl *acl2 = NULL;
+ int ret = 1;
+
+ acl2 = posix_acl_from_xattr (this, buf, size);
+ if (!acl2)
+ return 0;
+
+ if (acl->count != acl2->count) {
+ ret = 0;
+ goto out;
+ }
+
+ if (memcmp (acl->entries, acl2->entries,
+ (acl->count * sizeof (struct posix_ace))))
+ ret = 0;
+out:
+ posix_acl_destroy (this, acl2);
+
+ return ret;
+}
+
diff --git a/xlators/system/posix-acl/src/posix-acl-xattr.h b/xlators/system/posix-acl/src/posix-acl-xattr.h
new file mode 100644
index 0000000..008a55d
--- /dev/null
+++ b/xlators/system/posix-acl/src/posix-acl-xattr.h
@@ -0,0 +1,52 @@
+/*
+ Copyright (c) 2011 Gluster, Inc. <http://www.gluster.com>
+ This file is part of GlusterFS.
+
+ GlusterFS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation; either version 3 of the License,
+ or (at your option) any later version.
+
+ GlusterFS 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
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+
+#ifndef _POSIX_ACL_XATTR_H
+#define _POSIX_ACL_XATTR_H
+
+#include <stdint.h>
+
+#include "common-utils.h"
+#include "posix-acl.h"
+
+#define POSIX_ACL_ACCESS_XATTR "system.posix_acl_access"
+#define POSIX_ACL_DEFAULT_XATTR "system.posix_acl_default"
+
+#define POSIX_ACL_VERSION 2
+
+struct posix_acl_xattr_entry {
+ uint16_t tag;
+ uint16_t perm;
+ uint32_t id;
+};
+
+struct posix_acl_xattr_header {
+ uint32_t version;
+ struct posix_acl_xattr_entry entries[0];
+};
+
+struct posix_acl *posix_acl_from_xattr (xlator_t *this, const char *buf, int size);
+
+int posix_acl_to_xattr (xlator_t *this, struct posix_acl *acl, char *buf, int size);
+
+int posix_acl_matches_xattr (xlator_t *this, struct posix_acl *acl, const char *buf, int size);
+
+
+#endif /* !_POSIX_ACL_XATTR_H */
diff --git a/xlators/system/posix-acl/src/posix-acl.c b/xlators/system/posix-acl/src/posix-acl.c
new file mode 100644
index 0000000..f05ed3f
--- /dev/null
+++ b/xlators/system/posix-acl/src/posix-acl.c
@@ -0,0 +1,1792 @@
+/*
+ Copyright (c) 2011 Gluster, Inc. <http://www.gluster.com>
+ This file is part of GlusterFS.
+
+ GlusterFS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation; either version 3 of the License,
+ or (at your option) any later version.
+
+ GlusterFS 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
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+
+#include <errno.h>
+
+#include "xlator.h"
+#include "glusterfs.h"
+
+#include "posix-acl.h"
+#include "posix-acl-xattr.h"
+
+
+#define UINT64(ptr) ((uint64_t)((long)(ptr)))
+#define PTR(num) ((void *)((long)(num)))
+
+
+int
+whitelisted_xattr (const char *key)
+{
+ if (!key)
+ return 0;
+
+ if (strcmp (POSIX_ACL_ACCESS_XATTR, key) == 0)
+ return 1;
+ if (strcmp (POSIX_ACL_DEFAULT_XATTR, key) == 0)
+ return 1;
+ return 0;
+}
+
+
+int
+frame_is_user (call_frame_t *frame, uid_t uid)
+{
+ return (frame->root->uid == uid);
+}
+
+
+int
+frame_in_group (call_frame_t *frame, gid_t gid)
+{
+ int i = 0;
+
+ if (frame->root->gid == gid)
+ return 1;
+
+ for (i = 0; i < frame->root->ngrps; i++)
+ if (frame->root->groups[i] == gid)
+ return 1;
+ return 0;
+}
+
+
+mode_t
+posix_acl_access_set_mode (struct posix_acl *acl, struct posix_acl_ctx *ctx)
+{
+ struct posix_ace *ace = NULL;
+ struct posix_ace *group_ce = NULL;
+ struct posix_ace *mask_ce = NULL;
+ int count = 0;
+ int i = 0;
+ mode_t mode = 0;
+ int mask = 0;
+
+ count = acl->count;
+
+ ace = acl->entries;
+ for (i = 0; i < count; i++) {
+ switch (ace->tag) {
+ case POSIX_ACL_USER_OBJ:
+ mask |= S_IRWXU;
+ mode |= (ace->perm << 6);
+ break;
+ case POSIX_ACL_GROUP_OBJ:
+ group_ce = ace;
+ break;
+ case POSIX_ACL_MASK:
+ mask_ce = ace;
+ break;
+ case POSIX_ACL_OTHER:
+ mask |= S_IRWXO;
+ mode |= (ace->perm);
+ break;
+ }
+ ace++;
+ }
+
+ if (mask_ce) {
+ mask |= S_IRWXG;
+ mode |= (mask_ce->perm << 3);
+ } else {
+ if (!group_ce)
+ goto out;
+ mask |= S_IRWXG;
+ mode |= (group_ce->perm << 3);
+ }
+
+out:
+ ctx->perm = (ctx->perm & ~mask) | mode;
+
+ return mode;
+}
+
+
+static int
+sticky_permits (call_frame_t *frame, inode_t *parent, inode_t *inode)
+{
+ struct posix_acl_ctx *par = NULL;
+ struct posix_acl_ctx *ctx = NULL;
+
+ par = posix_acl_ctx_get (parent, frame->this);
+ ctx = posix_acl_ctx_get (inode, frame->this);
+
+ if (frame_is_user (frame, 0))
+ return 1;
+
+ if (!(par->perm & S_ISVTX))
+ return 1;
+
+ if (frame_is_user (frame, par->uid))
+ return 1;
+
+ if (frame_is_user (frame, ctx->uid))
+ return 1;
+
+ return 0;
+}
+
+
+static int
+acl_permits (call_frame_t *frame, inode_t *inode, int want)
+{
+ int verdict = 0;
+ int ret = 0;
+ struct posix_acl *acl = NULL;
+ struct posix_ace *ace = NULL;
+ struct posix_acl_ctx *ctx = NULL;
+ struct posix_acl_conf *conf = NULL;
+ int i = 0;
+ int perm = 0;
+ int found = 0;
+
+ conf = frame->this->private;
+
+ ctx = posix_acl_ctx_get (inode, frame->this);
+ if (!ctx)
+ goto red;
+
+ if (frame->root->uid == 0)
+ goto green;
+
+ ret = posix_acl_get (inode, frame->this, &acl, NULL);
+
+ if (!acl) {
+ acl = posix_acl_ref (frame->this, conf->minimal_acl);
+ }
+
+ ace = acl->entries;
+
+ for (i = 0; i < acl->count; i++) {
+ switch (ace->tag) {
+ case POSIX_ACL_USER_OBJ:
+ perm = ((ctx->perm & S_IRWXU) >> 6);
+ if (frame_is_user (frame, ctx->uid))
+ goto perm_check;
+ break;
+ case POSIX_ACL_USER:
+ perm = ace->perm;
+ if (frame_is_user (frame, ace->id))
+ goto mask_check;
+ break;
+ case POSIX_ACL_GROUP_OBJ:
+ perm = ((ctx->perm & S_IRWXG) >> 3);
+ if (frame_in_group (frame, ctx->gid)) {
+ found = 1;
+ if ((perm & want) == want)
+ goto mask_check;
+ }
+ break;
+ case POSIX_ACL_GROUP:
+ perm = ace->perm;
+ if (frame_in_group (frame, ace->id)) {
+ found = 1;
+ if ((perm & want) == want)
+ goto mask_check;
+ }
+ break;
+ case POSIX_ACL_MASK:
+ break;
+ case POSIX_ACL_OTHER:
+ perm = (ctx->perm & S_IRWXO);
+ if (!found)
+ goto perm_check;
+ /* fall through */
+ default:
+ goto red;
+ }
+
+ ace++;
+ }
+
+mask_check:
+ ace = acl->entries;
+
+ for (i = 0; i < acl->count; i++, ace++) {
+ if (ace->tag != POSIX_ACL_MASK)
+ continue;
+ if ((ace->perm & perm & want) == want)
+ goto green;
+ goto red;
+ }
+
+perm_check:
+ if ((perm & want) == want)
+ goto green;
+ else
+ goto red;
+
+green:
+ verdict = 1;
+ goto out;
+red:
+ verdict = 0;
+out:
+ if (acl)
+ posix_acl_unref (frame->this, acl);
+
+ return verdict;
+}
+
+
+struct posix_acl_ctx *
+posix_acl_ctx_get (inode_t *inode, xlator_t *this)
+{
+ struct posix_acl_ctx *ctx = NULL;
+ uint64_t int_ctx = 0;
+ int ret = 0;
+
+ ret = inode_ctx_get (inode, this, &int_ctx);
+ if ((ret == 0) && (int_ctx))
+ return PTR(int_ctx);
+
+ ctx = CALLOC (1, sizeof (*ctx));
+ if (!ctx)
+ return NULL;
+
+ ret = inode_ctx_put (inode, this, UINT64 (ctx));
+
+ return ctx;
+}
+
+
+int
+__posix_acl_set (inode_t *inode, xlator_t *this, struct posix_acl *acl_access,
+ struct posix_acl *acl_default)
+{
+ int ret = 0;
+ struct posix_acl_ctx *ctx = NULL;
+
+ ctx = posix_acl_ctx_get (inode, this);
+ if (!ctx)
+ goto out;
+
+ ctx->acl_access = acl_access;
+ ctx->acl_default = acl_default;
+
+out:
+ return ret;
+}
+
+
+int
+__posix_acl_get (inode_t *inode, xlator_t *this, struct posix_acl **acl_access_p,
+ struct posix_acl **acl_default_p)
+{
+ int ret = 0;
+ struct posix_acl_ctx *ctx = NULL;
+
+ ctx = posix_acl_ctx_get (inode, this);
+ if (!ctx)
+ goto out;
+
+ if (acl_access_p)
+ *acl_access_p = ctx->acl_access;
+ if (acl_default_p)
+ *acl_default_p = ctx->acl_default;
+
+out:
+ return ret;
+}
+
+
+struct posix_acl *
+posix_acl_new (xlator_t *this, int entrycnt)
+{
+ struct posix_acl *acl = NULL;
+ struct posix_ace *ace = NULL;
+
+ acl = CALLOC (1, sizeof (*acl) + (entrycnt * sizeof (*ace)));
+ if (!acl)
+ return NULL;
+
+ acl->count = entrycnt;
+
+ posix_acl_ref (this, acl);
+
+ return acl;
+}
+
+
+void
+posix_acl_destroy (xlator_t *this, struct posix_acl *acl)
+{
+ FREE (acl);
+
+ return;
+}
+
+
+struct posix_acl *
+posix_acl_ref (xlator_t *this, struct posix_acl *acl)
+{
+ struct posix_acl_conf *conf = NULL;
+
+ conf = this->private;
+
+ LOCK(&conf->acl_lock);
+ {
+ acl->refcnt++;
+ }
+ UNLOCK(&conf->acl_lock);
+
+ return acl;
+}
+
+
+struct posix_acl *
+posix_acl_dup (xlator_t *this, struct posix_acl *acl)
+{
+ struct posix_acl_conf *conf = NULL;
+ struct posix_acl *dup = NULL;
+
+ conf = this->private;
+
+ dup = posix_acl_new (this, acl->count);
+ if (!dup)
+ return NULL;
+
+ memcpy (dup->entries, acl->entries,
+ sizeof (struct posix_ace) * acl->count);
+
+ return dup;
+}
+
+
+void
+posix_acl_unref (xlator_t *this, struct posix_acl *acl)
+{
+ struct posix_acl_conf *conf = NULL;
+ int refcnt = 0;
+
+ conf = this->private;
+
+ LOCK(&conf->acl_lock);
+ {
+ refcnt = --acl->refcnt;
+ }
+ UNLOCK(&conf->acl_lock);
+
+ if (!refcnt)
+ posix_acl_destroy (this, acl);
+}
+
+
+int
+posix_acl_set (inode_t *inode, xlator_t *this, struct posix_acl *acl_access,
+ struct posix_acl *acl_default)
+{
+ int ret = 0;
+ int oldret = 0;
+ struct posix_acl *old_access = NULL;
+ struct posix_acl *old_default = NULL;
+ struct posix_acl_conf *conf = NULL;
+
+ conf = this->private;
+
+ LOCK(&conf->acl_lock);
+ {
+ oldret = __posix_acl_get (inode, this, &old_access,
+ &old_default);
+ if (acl_access)
+ acl_access->refcnt++;
+ if (acl_default)
+ acl_default->refcnt++;
+
+ ret = __posix_acl_set (inode, this, acl_access, acl_default);
+ }
+ UNLOCK(&conf->acl_lock);
+
+ if (oldret == 0) {
+ if (old_access)
+ posix_acl_unref (this, old_access);
+ if (old_default)
+ posix_acl_unref (this, old_default);
+ }
+
+ return ret;
+}
+
+
+int
+posix_acl_get (inode_t *inode, xlator_t *this, struct posix_acl **acl_access_p,
+ struct posix_acl **acl_default_p)
+{
+ struct posix_acl_conf *conf = NULL;
+ struct posix_acl *acl_access = NULL;
+ struct posix_acl *acl_default = NULL;
+ int ret = 0;
+
+ conf = this->private;
+
+ LOCK(&conf->acl_lock);
+ {
+ ret = __posix_acl_get (inode, this, &acl_access, &acl_default);
+
+ if (ret != 0)
+ goto unlock;
+
+ if (acl_access && acl_access_p)
+ acl_access->refcnt++;
+ if (acl_default && acl_default_p)
+ acl_default->refcnt++;
+ }
+unlock:
+ UNLOCK(&conf->acl_lock);
+
+ if (acl_access_p)
+ *acl_access_p = acl_access;
+ if (acl_default_p)
+ *acl_default_p = acl_default;
+
+ return ret;
+}
+
+
+mode_t
+posix_acl_inherit_mode (struct posix_acl *acl, mode_t modein)
+{
+ struct posix_ace *ace = NULL;
+ int count = 0;
+ int i = 0;
+ mode_t newmode = 0;
+ mode_t mode = 0;
+ struct posix_ace *mask_ce = NULL;
+ struct posix_ace *group_ce = NULL;
+
+ newmode = mode = modein;
+
+ count = acl->count;
+
+ ace = acl->entries;
+ for (i = 0; i < count; i++) {
+ switch (ace->tag) {
+ case POSIX_ACL_USER_OBJ:
+ ace->perm &= (mode >> 6) | ~S_IRWXO;
+ mode &= (ace->perm << 6) | ~S_IRWXU;
+ break;
+ case POSIX_ACL_GROUP_OBJ:
+ group_ce = ace;
+ break;
+ case POSIX_ACL_MASK:
+ mask_ce = ace;
+ break;
+ case POSIX_ACL_OTHER:
+ ace->perm &= (mode) | ~S_IRWXO;
+ mode &= (ace->perm) | ~S_IRWXO;
+ break;
+ }
+ ace++;
+ }
+
+ if (mask_ce) {
+ mask_ce->perm &= (mode >> 3) | ~S_IRWXO;
+ mode &= (mask_ce->perm << 3) | ~S_IRWXG;
+ } else {
+ group_ce->perm &= (mode >> 3) | ~S_IRWXO;
+ mode &= (group_ce->perm << 3) | ~S_IRWXG;
+ }
+
+ newmode = ((modein & S_IFMT) | (mode & (S_IRWXU|S_IRWXG|S_IRWXO)));
+
+ return newmode;
+}
+
+
+mode_t
+posix_acl_inherit (xlator_t *this, loc_t *loc, dict_t *params, mode_t mode,
+ int is_dir)
+{
+ int ret = 0;
+ struct posix_acl *par_default = NULL;
+ struct posix_acl *acl_default = NULL;
+ struct posix_acl *acl_access = NULL;
+ struct posix_acl_ctx *ctx = NULL;
+ char *xattr_default = NULL;
+ char *xattr_access = NULL;
+ int size_default = 0;
+ int size_access = 0;
+ mode_t retmode = 0;
+
+ retmode = mode;
+
+ ret = posix_acl_get (loc->parent, this, NULL, &par_default);
+
+ if (!par_default)
+ goto out;
+
+ ctx = posix_acl_ctx_get (loc->inode, this);
+
+ acl_access = posix_acl_dup (this, par_default);
+ if (!acl_access)
+ goto out;
+
+ retmode = posix_acl_inherit_mode (acl_access, mode);
+ ctx->perm = retmode;
+
+ size_access = posix_acl_to_xattr (this, acl_access, NULL, 0);
+ xattr_access = CALLOC (1, size_access);
+ if (!xattr_access) {
+ gf_log (this->name, GF_LOG_ERROR, "out of memory");
+ ret = -1;
+ goto out;
+ }
+ posix_acl_to_xattr (this, acl_access, xattr_access, size_access);
+
+ ret = dict_set_bin (params, POSIX_ACL_ACCESS_XATTR, xattr_access,
+ size_access);
+ if (ret) {
+ gf_log (this->name, GF_LOG_ERROR, "out of memory");
+ ret = -1;
+ goto out;
+ }
+
+ if (!is_dir)
+ goto set;
+
+
+ acl_default = posix_acl_ref (this, par_default);
+
+ size_default = posix_acl_to_xattr (this, acl_default, NULL, 0);
+ xattr_default = CALLOC (1, size_default);
+ if (!xattr_default) {
+ gf_log (this->name, GF_LOG_ERROR, "out of memory");
+ ret = -1;
+ goto out;
+ }
+ posix_acl_to_xattr (this, acl_default, xattr_default, size_default);
+
+ ret = dict_set_bin (params, POSIX_ACL_DEFAULT_XATTR, xattr_default,
+ size_default);
+ if (ret) {
+ gf_log (this->name, GF_LOG_ERROR, "out of memory");
+ ret = -1;
+ goto out;
+ }
+
+set:
+ ret = posix_acl_set (loc->inode, this, acl_access, acl_default);
+ if (ret != 0)
+ goto out;
+
+out:
+ if (par_default)
+ posix_acl_unref (this, par_default);
+ if (acl_access)
+ posix_acl_unref (this, acl_access);
+ if (acl_default)
+ posix_acl_unref (this, acl_default);
+
+ return retmode;
+}
+
+
+mode_t
+posix_acl_inherit_dir (xlator_t *this, loc_t *loc, dict_t *params, mode_t mode)
+{
+ mode_t retmode = 0;
+
+ retmode = posix_acl_inherit (this, loc, params, mode, 1);
+
+ return retmode;
+}
+
+
+mode_t
+posix_acl_inherit_file (xlator_t *this, loc_t *loc, dict_t *params, mode_t mode)
+{
+ mode_t retmode = 0;
+
+ retmode = posix_acl_inherit (this, loc, params, mode, 0);
+
+ return retmode;
+}
+
+
+int
+posix_acl_ctx_update (inode_t *inode, xlator_t *this, struct iatt *buf)
+{
+ struct posix_acl_ctx *ctx = NULL;
+ int ret = 0;
+
+ ctx = posix_acl_ctx_get (inode, this);
+ if (!ctx) {
+ ret = -1;
+ goto out;
+ }
+
+ LOCK(&inode->lock);
+ {
+ ctx->uid = buf->ia_uid;
+ ctx->gid = buf->ia_gid;
+ ctx->perm = st_mode_from_ia (buf->ia_prot, buf->ia_type);
+ }
+ UNLOCK(&inode->lock);
+out:
+ return ret;
+}
+
+
+int
+posix_acl_lookup_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno, inode_t *inode,
+ struct iatt *buf, dict_t *xattr, struct iatt *postparent)
+{
+ struct posix_acl *acl_access = NULL;
+ struct posix_acl *acl_default = NULL;
+ struct posix_acl *old_access = NULL;
+ struct posix_acl *old_default = NULL;
+ data_t *data = NULL;
+ int ret = 0;
+ dict_t *my_xattr = NULL;
+
+ if (op_ret != 0)
+ goto unwind;
+
+ ret = posix_acl_get (inode, this, &old_access, &old_default);
+
+ data = dict_get (xattr, POSIX_ACL_ACCESS_XATTR);
+ if (!data)
+ goto acl_default;
+
+ if (old_access &&
+ posix_acl_matches_xattr (this, old_access, data->data,
+ data->len)) {
+ acl_access = posix_acl_ref (this, old_access);
+ } else {
+ acl_access = posix_acl_from_xattr (this, data->data,
+ data->len);
+ }
+
+acl_default:
+ data = dict_get (xattr, POSIX_ACL_DEFAULT_XATTR);
+ if (!data)
+ goto acl_set;
+
+ if (old_default &&
+ posix_acl_matches_xattr (this, old_default, data->data,
+ data->len)) {
+ acl_default = posix_acl_ref (this, old_default);
+ } else {
+ acl_default = posix_acl_from_xattr (this, data->data,
+ data->len);
+ }
+
+acl_set:
+ posix_acl_ctx_update (inode, this, buf);
+
+ ret = posix_acl_set (inode, this, acl_access, acl_default);
+
+unwind:
+ my_xattr = frame->local;
+ frame->local = NULL;
+ STACK_UNWIND_STRICT (lookup, frame, op_ret, op_errno, inode, buf, xattr,
+ postparent);
+
+ if (acl_access)
+ posix_acl_unref (this, acl_access);
+ if (acl_default)
+ posix_acl_unref (this, acl_default);
+ if (old_access)
+ posix_acl_unref (this, old_access);
+ if (old_default)
+ posix_acl_unref (this, old_default);
+ if (my_xattr)
+ dict_unref (my_xattr);
+
+ return 0;
+}
+
+
+int
+posix_acl_lookup (call_frame_t *frame, xlator_t *this, loc_t *loc,
+ dict_t *xattr)
+{
+ int ret = 0;
+ dict_t *my_xattr = NULL;
+
+ if (!loc->parent)
+ /* lookup of / is always permitted */
+ goto green;
+
+ if (acl_permits (frame, loc->parent, POSIX_ACL_EXECUTE))
+ goto green;
+ else
+ goto red;
+
+green:
+ if (xattr) {
+ my_xattr = dict_ref (xattr);
+ } else {
+ my_xattr = dict_new ();
+ }
+
+ ret = dict_set_int8 (my_xattr, POSIX_ACL_ACCESS_XATTR, 0);
+ ret = dict_set_int8 (my_xattr, POSIX_ACL_DEFAULT_XATTR, 0);
+
+ frame->local = my_xattr;
+ STACK_WIND (frame, posix_acl_lookup_cbk,
+ FIRST_CHILD (this), FIRST_CHILD (this)->fops->lookup,
+ loc, my_xattr);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (lookup, frame, -1, EACCES, NULL, NULL, NULL,
+ NULL);
+
+ return 0;
+}
+
+
+int
+posix_acl_access (call_frame_t *frame, xlator_t *this, loc_t *loc, int mask)
+{
+ int op_ret = 0;
+ int op_errno = 0;
+ int perm = 0;
+
+
+ if (mask & R_OK)
+ perm |= POSIX_ACL_READ;
+ if (mask & W_OK)
+ perm |= POSIX_ACL_WRITE;
+ if (mask & X_OK)
+ perm |= POSIX_ACL_EXECUTE;
+ if (!perm) {
+ op_ret = -1;
+ op_errno = EINVAL;
+ goto unwind;
+ }
+
+ if (acl_permits (frame, loc->inode, perm)) {
+ op_ret = 0;
+ op_errno = 0;
+ } else {
+ op_ret = -1;
+ op_errno = EACCES;
+ }
+
+unwind:
+ STACK_UNWIND_STRICT (access, frame, op_ret, op_errno);
+
+ return 0;
+}
+
+
+int
+posix_acl_truncate_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno, struct iatt *prebuf,
+ struct iatt *postbuf)
+{
+ STACK_UNWIND_STRICT (truncate, frame, op_ret, op_errno, prebuf, postbuf);
+
+ return 0;
+}
+
+
+int
+posix_acl_truncate (call_frame_t *frame, xlator_t *this, loc_t *loc, off_t off)
+{
+ if (acl_permits (frame, loc->inode, POSIX_ACL_WRITE))
+ goto green;
+ else
+ goto red;
+green:
+ STACK_WIND (frame, posix_acl_truncate_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->truncate,
+ loc, off);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (truncate, frame, -1, EACCES, NULL, NULL);
+ return 0;
+}
+
+
+int
+posix_acl_open_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno, fd_t *fd)
+{
+ STACK_UNWIND_STRICT (open, frame, op_ret, op_errno, fd);
+
+ return 0;
+}
+
+
+int
+posix_acl_open (call_frame_t *frame, xlator_t *this, loc_t *loc, int flags,
+ fd_t *fd, int wbflags)
+{
+ int perm = 0;
+
+ switch (flags & O_ACCMODE) {
+ case O_RDONLY:
+ perm = POSIX_ACL_READ;
+ break;
+ case O_WRONLY:
+ perm = POSIX_ACL_WRITE;
+ break;
+ case O_RDWR:
+ perm = POSIX_ACL_READ|POSIX_ACL_WRITE;
+ break;
+ }
+
+ if (acl_permits (frame, loc->inode, perm))
+ goto green;
+ else
+ goto red;
+green:
+ STACK_WIND (frame, posix_acl_open_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->open,
+ loc, flags, fd, wbflags);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (open, frame, -1, EACCES, NULL);
+ return 0;
+}
+
+
+
+int
+posix_acl_opendir_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno, fd_t *fd)
+{
+ STACK_UNWIND_STRICT (opendir, frame, op_ret, op_errno, fd);
+
+ return 0;
+}
+
+
+int
+posix_acl_opendir (call_frame_t *frame, xlator_t *this, loc_t *loc, fd_t *fd)
+{
+ if (acl_permits (frame, loc->inode, POSIX_ACL_READ))
+ goto green;
+ else
+ goto red;
+green:
+ STACK_WIND (frame, posix_acl_opendir_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->opendir,
+ loc, fd);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (opendir, frame, -1, EACCES, NULL);
+ return 0;
+}
+
+
+int
+posix_acl_mkdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno, inode_t *inode, struct iatt *buf,
+ struct iatt *preparent, struct iatt *postparent)
+{
+ if (op_ret != 0)
+ goto unwind;
+
+ posix_acl_ctx_update (inode, this, buf);
+
+unwind:
+ STACK_UNWIND_STRICT (mkdir, frame, op_ret, op_errno, inode, buf,
+ preparent, postparent);
+ return 0;
+}
+
+
+int
+posix_acl_mkdir (call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode,
+ dict_t *params)
+{
+ mode_t newmode = 0;
+
+ newmode = mode;
+ if (acl_permits (frame, loc->parent, POSIX_ACL_WRITE|POSIX_ACL_EXECUTE))
+ goto green;
+ else
+ goto red;
+green:
+ newmode = posix_acl_inherit_dir (this, loc, params, mode);
+
+ STACK_WIND (frame, posix_acl_mkdir_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->mkdir,
+ loc, newmode, params);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (mkdir, frame, -1, EACCES, NULL, NULL, NULL, NULL);
+ return 0;
+}
+
+
+int
+posix_acl_mknod_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno, inode_t *inode, struct iatt *buf,
+ struct iatt *preparent, struct iatt *postparent)
+{
+ if (op_ret != 0)
+ goto unwind;
+
+ posix_acl_ctx_update (inode, this, buf);
+
+unwind:
+ STACK_UNWIND_STRICT (mknod, frame, op_ret, op_errno, inode, buf,
+ preparent, postparent);
+ return 0;
+}
+
+
+int
+posix_acl_mknod (call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode,
+ dev_t rdev, dict_t *params)
+{
+ mode_t newmode = 0;
+
+ newmode = mode;
+ if (acl_permits (frame, loc->parent, POSIX_ACL_WRITE|POSIX_ACL_EXECUTE))
+ goto green;
+ else
+ goto red;
+green:
+ newmode = posix_acl_inherit_file (this, loc, params, mode);
+
+ STACK_WIND (frame, posix_acl_mknod_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->mknod,
+ loc, newmode, rdev, params);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (mknod, frame, -1, EACCES, NULL, NULL, NULL, NULL);
+ return 0;
+}
+
+
+int
+posix_acl_create_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno, fd_t *fd, inode_t *inode,
+ struct iatt *buf, struct iatt *preparent,
+ struct iatt *postparent)
+{
+ if (op_ret != 0)
+ goto unwind;
+
+ posix_acl_ctx_update (inode, this, buf);
+
+unwind:
+ STACK_UNWIND_STRICT (create, frame, op_ret, op_errno, fd, inode, buf,
+ preparent, postparent);
+ return 0;
+}
+
+
+int
+posix_acl_create (call_frame_t *frame, xlator_t *this, loc_t *loc, int flags,
+ mode_t mode, fd_t *fd, dict_t *params)
+{
+ mode_t newmode = 0;
+
+ newmode = mode;
+ if (acl_permits (frame, loc->parent, POSIX_ACL_WRITE|POSIX_ACL_EXECUTE))
+ goto green;
+ else
+ goto red;
+green:
+ newmode = posix_acl_inherit_file (this, loc, params, mode);
+
+ STACK_WIND (frame, posix_acl_create_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->create,
+ loc, flags, newmode, fd, params);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (create, frame, -1, EACCES, NULL, NULL, NULL, NULL, NULL);
+ return 0;
+}
+
+
+int
+posix_acl_symlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno, inode_t *inode,
+ struct iatt *buf, struct iatt *preparent,
+ struct iatt *postparent)
+{
+ if (op_ret != 0)
+ goto unwind;
+
+ posix_acl_ctx_update (inode, this, buf);
+
+unwind:
+ STACK_UNWIND_STRICT (symlink, frame, op_ret, op_errno, inode, buf,
+ preparent, postparent);
+ return 0;
+}
+
+
+int
+posix_acl_symlink (call_frame_t *frame, xlator_t *this, const char *linkname,
+ loc_t *loc, dict_t *params)
+{
+ if (acl_permits (frame, loc->parent, POSIX_ACL_WRITE|POSIX_ACL_EXECUTE))
+ goto green;
+ else
+ goto red;
+green:
+ STACK_WIND (frame, posix_acl_symlink_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->symlink,
+ linkname, loc, params);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (symlink, frame, -1, EACCES, NULL, NULL, NULL, NULL);
+ return 0;
+}
+
+
+int
+posix_acl_unlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno,
+ struct iatt *preparent, struct iatt *postparent)
+{
+ if (op_ret != 0)
+ goto unwind;
+unwind:
+ STACK_UNWIND_STRICT (unlink, frame, op_ret, op_errno,
+ preparent, postparent);
+ return 0;
+}
+
+
+int
+posix_acl_unlink (call_frame_t *frame, xlator_t *this, loc_t *loc)
+{
+ if (!sticky_permits (frame, loc->parent, loc->inode))
+ goto red;
+
+ if (acl_permits (frame, loc->parent, POSIX_ACL_WRITE|POSIX_ACL_EXECUTE))
+ goto green;
+ else
+ goto red;
+green:
+ STACK_WIND (frame, posix_acl_unlink_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->unlink,
+ loc);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (unlink, frame, -1, EACCES, NULL, NULL);
+ return 0;
+}
+
+
+int
+posix_acl_rmdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno,
+ struct iatt *preparent, struct iatt *postparent)
+{
+ if (op_ret != 0)
+ goto unwind;
+unwind:
+ STACK_UNWIND_STRICT (rmdir, frame, op_ret, op_errno,
+ preparent, postparent);
+ return 0;
+}
+
+
+int
+posix_acl_rmdir (call_frame_t *frame, xlator_t *this, loc_t *loc, int flags)
+{
+ if (!sticky_permits (frame, loc->parent, loc->inode))
+ goto red;
+
+ if (acl_permits (frame, loc->parent, POSIX_ACL_WRITE|POSIX_ACL_EXECUTE))
+ goto green;
+ else
+ goto red;
+green:
+ STACK_WIND (frame, posix_acl_rmdir_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->rmdir,
+ loc, flags);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (rmdir, frame, -1, EACCES, NULL, NULL);
+ return 0;
+}
+
+
+int
+posix_acl_rename_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno, struct iatt *buf,
+ struct iatt *preoldparent, struct iatt *postoldparent,
+ struct iatt *prenewparent, struct iatt *postnewparent)
+{
+ if (op_ret != 0)
+ goto unwind;
+unwind:
+ STACK_UNWIND_STRICT (rename, frame, op_ret, op_errno, buf,
+ preoldparent, postoldparent,
+ prenewparent, postnewparent);
+ return 0;
+}
+
+
+int
+posix_acl_rename (call_frame_t *frame, xlator_t *this, loc_t *old, loc_t *new)
+{
+ if (!acl_permits (frame, old->parent, POSIX_ACL_WRITE))
+ goto red;
+
+ if (!acl_permits (frame, new->parent, POSIX_ACL_WRITE))
+ goto red;
+
+ if (!sticky_permits (frame, old->parent, old->inode))
+ goto red;
+
+ if (new->inode) {
+ if (!sticky_permits (frame, new->parent, new->inode))
+ goto red;
+ }
+
+ STACK_WIND (frame, posix_acl_rename_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->rename,
+ old, new);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (rename, frame, -1, EACCES, NULL, NULL, NULL, NULL,
+ NULL);
+ return 0;
+}
+
+
+int
+posix_acl_link_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno, inode_t *inode, struct iatt *buf,
+ struct iatt *preparent, struct iatt *postparent)
+{
+ if (op_ret != 0)
+ goto unwind;
+unwind:
+ STACK_UNWIND_STRICT (link, frame, op_ret, op_errno, inode, buf,
+ preparent, postparent);
+ return 0;
+}
+
+
+int
+posix_acl_link (call_frame_t *frame, xlator_t *this, loc_t *old, loc_t *new)
+{
+ struct posix_acl_ctx *ctx = NULL;
+ int op_errno = 0;
+
+ ctx = posix_acl_ctx_get (old->inode, this);
+ if (!ctx) {
+ op_errno = EIO;
+ goto red;
+ }
+
+ if (!acl_permits (frame, new->parent, POSIX_ACL_WRITE)) {
+ op_errno = EACCES;
+ goto red;
+ }
+
+ STACK_WIND (frame, posix_acl_link_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->link,
+ old, new);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (link, frame, -1, op_errno, NULL, NULL, NULL, NULL);
+
+ return 0;
+}
+
+
+int
+posix_acl_readdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno, gf_dirent_t *entries)
+{
+ if (op_ret != 0)
+ goto unwind;
+unwind:
+ STACK_UNWIND_STRICT (readdir, frame, op_ret, op_errno, entries);
+ return 0;
+}
+
+
+int
+posix_acl_readdir (call_frame_t *frame, xlator_t *this, fd_t *fd, size_t size,
+ off_t offset)
+{
+ if (acl_permits (frame, fd->inode, POSIX_ACL_READ))
+ goto green;
+ else
+ goto red;
+green:
+ STACK_WIND (frame, posix_acl_readdir_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->readdir,
+ fd, size, offset);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (readdir, frame, -1, EACCES, NULL);
+
+ return 0;
+}
+
+
+int
+posix_acl_readdirp_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno, gf_dirent_t *entries)
+{
+ if (op_ret != 0)
+ goto unwind;
+unwind:
+ STACK_UNWIND_STRICT (readdirp, frame, op_ret, op_errno, entries);
+ return 0;
+}
+
+
+int
+posix_acl_readdirp (call_frame_t *frame, xlator_t *this, fd_t *fd, size_t size,
+ off_t offset)
+{
+ if (acl_permits (frame, fd->inode, POSIX_ACL_READ))
+ goto green;
+ else
+ goto red;
+green:
+ STACK_WIND (frame, posix_acl_readdirp_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->readdirp,
+ fd, size, offset);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (readdirp, frame, -1, EACCES, NULL);
+
+ return 0;
+}
+
+
+int
+setattr_scrutiny (call_frame_t *frame, inode_t *inode, struct iatt *buf,
+ int valid)
+{
+ struct posix_acl_ctx *ctx = NULL;
+
+ if (frame->root->uid == 0)
+ return 0;
+
+ ctx = posix_acl_ctx_get (inode, frame->this);
+ if (!ctx)
+ return EIO;
+
+ if (valid & GF_SET_ATTR_MODE) {
+/*
+ The effective UID of the calling process must match the owner of the
+ file, or the process must be privileged
+*/
+ if (!frame_is_user (frame, ctx->uid))
+ return EPERM;
+/*
+ If the calling process is not privileged (Linux: does not have the
+ CAP_FSETID capability), and the group of the file does not match the
+ effective group ID of the process or one of its supplementary group
+ IDs, the S_ISGID bit will be turned off, but this will not cause an
+ error to be returned.
+
+*/
+ if (!frame_in_group (frame, ctx->gid))
+ buf->ia_prot.sgid = 0;
+ }
+
+ if (valid & (GF_SET_ATTR_ATIME | GF_SET_ATTR_MTIME)) {
+/*
+ Changing timestamps is permitted when: either the process has appropri?
+ ate privileges, or the effective user ID equals the user ID of the
+ file, or times is NULL and the process has write permission for the
+ file.
+*/
+ if (!frame_is_user (frame, ctx->uid) &&
+ !acl_permits (frame, inode, POSIX_ACL_WRITE))
+ return EACCES;
+ }
+
+ if (valid & GF_SET_ATTR_UID) {
+ if ((frame->root->uid != 0) &&
+ (buf->ia_uid != ctx->uid))
+ return EPERM;
+ }
+
+ if (valid & GF_SET_ATTR_GID) {
+ if (!frame_is_user (frame, ctx->uid))
+ return EPERM;
+ if (!frame_in_group (frame, buf->ia_gid))
+ return EPERM;
+ }
+
+ return 0;
+}
+
+
+int
+posix_acl_setattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno,
+ struct iatt *prebuf, struct iatt *postbuf)
+{
+ inode_t *inode = NULL;
+
+ inode = frame->local;
+ frame->local = NULL;
+
+ if (op_ret != 0)
+ goto unwind;
+
+ posix_acl_ctx_update (inode, this, postbuf);
+
+unwind:
+ STACK_UNWIND_STRICT (setattr, frame, op_ret, op_errno, prebuf, postbuf);
+ return 0;
+}
+
+
+int
+posix_acl_setattr (call_frame_t *frame, xlator_t *this, loc_t *loc,
+ struct iatt *buf, int valid)
+{
+ int op_errno = 0;
+
+ op_errno = setattr_scrutiny (frame, loc->inode, buf, valid);
+
+ if (op_errno)
+ goto red;
+
+ frame->local = loc->inode;
+
+ STACK_WIND (frame, posix_acl_setattr_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->setattr,
+ loc, buf, valid);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (setattr, frame, -1, op_errno, NULL, NULL);
+
+ return 0;
+}
+
+
+int
+posix_acl_fsetattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno,
+ struct iatt *prebuf, struct iatt *postbuf)
+{
+ inode_t *inode = NULL;
+
+ inode = frame->local;
+ frame->local = NULL;
+
+ if (op_ret != 0)
+ goto unwind;
+
+ posix_acl_ctx_update (inode, this, postbuf);
+
+unwind:
+ STACK_UNWIND_STRICT (fsetattr, frame, op_ret, op_errno, prebuf, postbuf);
+ return 0;
+}
+
+
+int
+posix_acl_fsetattr (call_frame_t *frame, xlator_t *this, fd_t *fd,
+ struct iatt *buf, int valid)
+{
+ int op_errno = 0;
+
+ op_errno = setattr_scrutiny (frame, fd->inode, buf, valid);
+
+ if (op_errno)
+ goto red;
+
+ frame->local = fd->inode;
+
+ STACK_WIND (frame, posix_acl_fsetattr_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->fsetattr,
+ fd, buf, valid);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (fsetattr, frame, -1, EACCES, NULL, NULL);
+
+ return 0;
+}
+
+
+int
+setxattr_scrutiny (call_frame_t *frame, inode_t *inode, dict_t *xattr)
+{
+ struct posix_acl_ctx *ctx = NULL;
+ int found = 0;
+
+ if (frame->root->uid == 0)
+ return 0;
+
+ ctx = posix_acl_ctx_get (inode, frame->this);
+ if (!ctx)
+ return EIO;
+
+ if (dict_get (xattr, POSIX_ACL_ACCESS_XATTR)) {
+ found = 1;
+ if (!frame_is_user (frame, ctx->uid))
+ return EPERM;
+ }
+
+ if (dict_get (xattr, POSIX_ACL_DEFAULT_XATTR)) {
+ found = 1;
+ if (!frame_is_user (frame, ctx->uid))
+ return EPERM;
+ }
+
+ if (!found && !acl_permits (frame, inode, POSIX_ACL_WRITE))
+ return EACCES;
+
+ return 0;
+}
+
+
+struct posix_acl *
+posix_acl_xattr_update (xlator_t *this, inode_t *inode, dict_t *xattr,
+ char *name, struct posix_acl *old)
+{
+ struct posix_acl *acl = NULL;
+ data_t *data = NULL;
+
+ data = dict_get (xattr, name);
+ if (data) {
+ acl = posix_acl_from_xattr (this, data->data,
+ data->len);
+ }
+
+ if (!acl && old)
+ acl = posix_acl_ref (this, old);
+
+ return acl;
+}
+
+
+int
+posix_acl_setxattr_update (xlator_t *this, inode_t *inode, dict_t *xattr)
+{
+ struct posix_acl *acl_access = NULL;
+ struct posix_acl *acl_default = NULL;
+ struct posix_acl *old_access = NULL;
+ struct posix_acl *old_default = NULL;
+ struct posix_acl_ctx *ctx = NULL;
+ int ret = 0;
+ mode_t mode = 0;
+
+ ctx = posix_acl_ctx_get (inode, this);
+ if (!ctx)
+ return -1;
+
+ ret = posix_acl_get (inode, this, &old_access, &old_default);
+
+ acl_access = posix_acl_xattr_update (this, inode, xattr,
+ POSIX_ACL_ACCESS_XATTR,
+ old_access);
+
+ acl_default = posix_acl_xattr_update (this, inode, xattr,
+ POSIX_ACL_DEFAULT_XATTR,
+ old_default);
+
+ ret = posix_acl_set (inode, this, acl_access, acl_default);
+
+ if (acl_access && acl_access != old_access) {
+ mode = posix_acl_access_set_mode (acl_access, ctx);
+ }
+
+ if (acl_access)
+ posix_acl_unref (this, acl_access);
+ if (acl_default)
+ posix_acl_unref (this, acl_default);
+ if (old_access)
+ posix_acl_unref (this, old_access);
+ if (old_default)
+ posix_acl_unref (this, old_default);
+
+ return 0;
+}
+
+
+int
+posix_acl_setxattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno)
+{
+ STACK_UNWIND_STRICT (setxattr, frame, op_ret, op_errno);
+
+ return 0;
+}
+
+
+int
+posix_acl_setxattr (call_frame_t *frame, xlator_t *this, loc_t *loc,
+ dict_t *xattr, int flags)
+{
+ int op_errno = 0;
+
+ op_errno = setxattr_scrutiny (frame, loc->inode, xattr);
+
+ if (op_errno != 0)
+ goto red;
+
+ posix_acl_setxattr_update (this, loc->inode, xattr);
+
+ STACK_WIND (frame, posix_acl_setxattr_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->setxattr,
+ loc, xattr, flags);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (setxattr, frame, -1, op_errno);
+
+ return 0;
+}
+
+
+int
+posix_acl_fsetxattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno)
+{
+ STACK_UNWIND_STRICT (fsetxattr, frame, op_ret, op_errno);
+
+ return 0;
+}
+
+
+int
+posix_acl_fsetxattr (call_frame_t *frame, xlator_t *this, fd_t *fd,
+ dict_t *xattr, int flags)
+{
+ int op_errno = 0;
+
+ op_errno = setxattr_scrutiny (frame, fd->inode, xattr);
+
+ if (op_errno != 0)
+ goto red;
+
+ posix_acl_setxattr_update (this, fd->inode, xattr);
+
+ STACK_WIND (frame, posix_acl_fsetxattr_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->fsetxattr,
+ fd, xattr, flags);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (fsetxattr, frame, -1, op_errno);
+
+ return 0;
+}
+
+
+int
+posix_acl_getxattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno, dict_t *xattr)
+{
+ STACK_UNWIND_STRICT (getxattr, frame, op_ret, op_errno, xattr);
+
+ return 0;
+}
+
+
+int
+posix_acl_getxattr (call_frame_t *frame, xlator_t *this, loc_t *loc,
+ const char *name)
+{
+ if (whitelisted_xattr (name))
+ goto green;
+
+ if (acl_permits (frame, loc->inode, POSIX_ACL_READ))
+ goto green;
+ else
+ goto red;
+green:
+ STACK_WIND (frame, posix_acl_getxattr_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->getxattr,
+ loc, name);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (getxattr, frame, -1, EACCES, NULL);
+
+ return 0;
+}
+
+
+int
+posix_acl_fgetxattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno, dict_t *xattr)
+{
+ STACK_UNWIND_STRICT (fgetxattr, frame, op_ret, op_errno, xattr);
+
+ return 0;
+}
+
+
+int
+posix_acl_fgetxattr (call_frame_t *frame, xlator_t *this, fd_t *fd,
+ const char *name)
+{
+ if (whitelisted_xattr (name))
+ goto green;
+
+ if (acl_permits (frame, fd->inode, POSIX_ACL_READ))
+ goto green;
+ else
+ goto red;
+green:
+ STACK_WIND (frame, posix_acl_fgetxattr_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->fgetxattr,
+ fd, name);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (fgetxattr, frame, -1, EACCES, NULL);
+
+ return 0;
+}
+
+
+int
+posix_acl_removexattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno)
+{
+ STACK_UNWIND_STRICT (removexattr, frame, op_ret, op_errno);
+
+ return 0;
+}
+
+
+int
+posix_acl_removexattr (call_frame_t *frame, xlator_t *this, loc_t *loc,
+ const char *name)
+{
+ struct posix_acl_ctx *ctx = NULL;
+ int op_errno = EACCES;
+
+ if (frame_is_user (frame, 0))
+ goto green;
+
+ ctx = posix_acl_ctx_get (loc->inode, this);
+ if (!ctx) {
+ op_errno = EIO;
+ goto red;
+ }
+
+ if (whitelisted_xattr (name)) {
+ if (!frame_is_user (frame, ctx->uid)) {
+ op_errno = EPERM;
+ goto red;
+ }
+ }
+
+ if (acl_permits (frame, loc->inode, POSIX_ACL_WRITE))
+ goto green;
+ else
+ goto red;
+green:
+ STACK_WIND (frame, posix_acl_removexattr_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->removexattr,
+ loc, name);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (removexattr, frame, -1, op_errno);
+
+ return 0;
+}
+
+
+int
+posix_acl_forget (xlator_t *this, inode_t *inode)
+{
+ struct posix_acl_ctx *ctx = NULL;
+
+ ctx = posix_acl_ctx_get (inode, this);
+ if (!ctx)
+ goto out;
+
+ if (ctx->acl_access)
+ posix_acl_unref (this, ctx->acl_access);
+
+ if (ctx->acl_default)
+ posix_acl_unref (this, ctx->acl_default);
+
+ FREE (ctx);
+out:
+ return 0;
+}
+
+
+int
+init (xlator_t *this)
+{
+ struct posix_acl_conf *conf = NULL;
+ struct posix_acl *minacl = NULL;
+ struct posix_ace *minace = NULL;
+
+ conf = CALLOC (1, sizeof (*conf));
+ if (!conf) {
+ gf_log (this->name, GF_LOG_ERROR,
+ "out of memory");
+ return -1;
+ }
+
+ LOCK_INIT (&conf->acl_lock);
+
+ this->private = conf;
+
+ minacl = posix_acl_new (this, 3);
+ if (!minacl)
+ return -1;
+
+ minace = minacl->entries;
+ minace[0].tag = POSIX_ACL_USER_OBJ;
+ minace[1].tag = POSIX_ACL_GROUP_OBJ;
+ minace[2].tag = POSIX_ACL_OTHER;
+
+ conf->minimal_acl = minacl;
+
+ return 0;
+}
+
+
+int
+fini (xlator_t *this)
+{
+ return 0;
+}
+
+
+struct xlator_fops fops = {
+ .lookup = posix_acl_lookup,
+ .open = posix_acl_open,
+ .access = posix_acl_access,
+ .truncate = posix_acl_truncate,
+ .mkdir = posix_acl_mkdir,
+ .mknod = posix_acl_mknod,
+ .create = posix_acl_create,
+ .symlink = posix_acl_symlink,
+ .unlink = posix_acl_unlink,
+ .rmdir = posix_acl_rmdir,
+ .rename = posix_acl_rename,
+ .link = posix_acl_link,
+ .opendir = posix_acl_opendir,
+ .readdir = posix_acl_readdir,
+ .readdirp = posix_acl_readdirp,
+ .setattr = posix_acl_setattr,
+ .fsetattr = posix_acl_fsetattr,
+ .setxattr = posix_acl_setxattr,
+ .fsetxattr = posix_acl_fsetxattr,
+ .getxattr = posix_acl_getxattr,
+ .fgetxattr = posix_acl_fgetxattr,
+ .removexattr = posix_acl_removexattr,
+};
+
+
+struct xlator_cbks cbks = {
+ .forget = posix_acl_forget
+};
diff --git a/xlators/system/posix-acl/src/posix-acl.h b/xlators/system/posix-acl/src/posix-acl.h
new file mode 100644
index 0000000..46b56d6
--- /dev/null
+++ b/xlators/system/posix-acl/src/posix-acl.h
@@ -0,0 +1,86 @@
+/*
+ Copyright (c) 2011 Gluster, Inc. <http://www.gluster.com>
+ This file is part of GlusterFS.
+
+ GlusterFS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation; either version 3 of the License,
+ or (at your option) any later version.
+
+ GlusterFS 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
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+
+#ifndef _POSIX_ACL_H
+#define _POSIX_ACL_H
+
+#include <stdint.h>
+
+#include "xlator.h"
+#include "common-utils.h"
+#include "byte-order.h"
+
+
+#define POSIX_ACL_READ (0x04)
+#define POSIX_ACL_WRITE (0x02)
+#define POSIX_ACL_EXECUTE (0x01)
+
+#define POSIX_ACL_UNDEFINED_TAG (0x00)
+#define POSIX_ACL_USER_OBJ (0x01)
+#define POSIX_ACL_USER (0x02)
+#define POSIX_ACL_GROUP_OBJ (0x04)
+#define POSIX_ACL_GROUP (0x08)
+#define POSIX_ACL_MASK (0x10)
+#define POSIX_ACL_OTHER (0x20)
+
+#define POSIX_ACL_UNDEFINED_ID ((id_t)-1)
+
+
+struct posix_ace {
+ uint16_t tag;
+ uint16_t perm;
+ uint32_t id;
+};
+
+
+struct posix_acl {
+ int refcnt;
+ int count;
+ struct posix_ace entries[0];
+};
+
+
+struct posix_acl_ctx {
+ uid_t uid;
+ gid_t gid;
+ mode_t perm;
+ struct posix_acl *acl_access;
+ struct posix_acl *acl_default;
+};
+
+
+struct posix_acl_conf {
+ gf_lock_t acl_lock;
+ struct posix_acl *minimal_acl;
+};
+
+
+struct posix_acl *posix_acl_new (xlator_t *this, int entry_count);
+struct posix_acl *posix_acl_ref (xlator_t *this, struct posix_acl *acl);
+void posix_acl_unref (xlator_t *this, struct posix_acl *acl);
+void posix_acl_destroy (xlator_t *this, struct posix_acl *acl);
+struct posix_acl_ctx *posix_acl_ctx_get (inode_t *inode, xlator_t *this);
+int posix_acl_get (inode_t *inode, xlator_t *this,
+ struct posix_acl **acl_access_p,
+ struct posix_acl **acl_default_p);
+int posix_acl_set (inode_t *inode, xlator_t *this, struct posix_acl *acl_access,
+ struct posix_acl *acl_default);
+
+#endif /* !_POSIX_ACL_H */