summaryrefslogtreecommitdiffstats
path: root/xlators/features/access-control
diff options
context:
space:
mode:
authorShehjar Tikoo <shehjart@gluster.com>2010-03-31 07:27:00 +0000
committerAnand V. Avati <avati@dev.gluster.com>2010-03-31 07:43:51 -0700
commitfce2ae459b356977aab9b0100db22047e54a994b (patch)
treeb6ae45046e7630a2441b353197f1c7a38076cafb /xlators/features/access-control
parent26f826c75c9e629c71535f3d9e5962d4aedb6898 (diff)
access-control: Introduce new translator
The access control translator is required for performing permission checks on files and directories in line with posix conformance. This is required to be kept separate from storage/posix so that ACL support can also be later integrated into this translator. Signed-off-by: Shehjar Tikoo <shehjart@gluster.com> Signed-off-by: Anand V. Avati <avati@dev.gluster.com> BUG: 400 (Support auxiliary gids in GlusterFS) URL: http://bugs.gluster.com/cgi-bin/bugzilla3/show_bug.cgi?id=400
Diffstat (limited to 'xlators/features/access-control')
-rw-r--r--xlators/features/access-control/Makefile.am3
-rw-r--r--xlators/features/access-control/src/Makefile.am13
-rw-r--r--xlators/features/access-control/src/access-control.c1844
-rw-r--r--xlators/features/access-control/src/access-control.h55
4 files changed, 1915 insertions, 0 deletions
diff --git a/xlators/features/access-control/Makefile.am b/xlators/features/access-control/Makefile.am
new file mode 100644
index 00000000000..a985f42a877
--- /dev/null
+++ b/xlators/features/access-control/Makefile.am
@@ -0,0 +1,3 @@
+SUBDIRS = src
+
+CLEANFILES =
diff --git a/xlators/features/access-control/src/Makefile.am b/xlators/features/access-control/src/Makefile.am
new file mode 100644
index 00000000000..6ab8cc4ec4e
--- /dev/null
+++ b/xlators/features/access-control/src/Makefile.am
@@ -0,0 +1,13 @@
+xlator_LTLIBRARIES = access-control.la
+xlatordir = $(libdir)/glusterfs/$(PACKAGE_VERSION)/xlator/features
+access_control_la_LDFLAGS = -module -avoidversion
+access_control_la_SOURCES = access-control.c
+access_control_la_LIBADD = $(top_builddir)/libglusterfs/src/libglusterfs.la
+
+noinst_HEADERS = access-control.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/features/access-control/src/access-control.c b/xlators/features/access-control/src/access-control.c
new file mode 100644
index 00000000000..f90184d1a3a
--- /dev/null
+++ b/xlators/features/access-control/src/access-control.c
@@ -0,0 +1,1844 @@
+/*
+ Copyright (c) 2010 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 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+#include "config.h"
+#endif
+
+#include "access-control.h"
+#include "xlator.h"
+#include "call-stub.h"
+#include "defaults.h"
+#include "iatt.h"
+
+/* Careful, this function erases the stub from frame->local. Dont call this if
+ * a subsequent callback requires retaining access to the stub. This should be
+ * called at the end of all access-control related operations, i.e. once the
+ * frame will be handed off to the actual fop and the next callback that will
+ * be called is the default callback. IOW, the function where call_resume is
+ * called.
+ * NOTE: this is required because FRAME_DESTROY tries to free frame->local if
+ * it finds it to be non-NULL.
+ */
+call_stub_t *
+__get_frame_stub (call_frame_t *fr)
+{
+ call_stub_t *st = NULL;
+
+ if (!fr)
+ return NULL;
+
+ st = fr->local;
+ fr->local = NULL;
+
+ return st;
+}
+
+
+int
+ac_test_owner_access (struct iatt *ia, uid_t uid, int accesstest)
+{
+ int ret = -1;
+
+ if (!ia)
+ return -1;
+
+ /* First test permissions using the uid. */
+ if (ia->ia_uid != uid) {
+ ret = -1;
+ goto out;
+ }
+
+ /* At this point we know, the uid matches that of the stat structure, so
+ * if the caller does not care, we should return success.
+ */
+ if (ac_test_dontcare (accesstest)) {
+ ret = 0;
+ goto out;
+ }
+
+ if (ac_test_read (accesstest))
+ ret = IA_PROT_RUSR (ia->ia_prot);
+
+ if (ac_test_write (accesstest))
+ ret = IA_PROT_WUSR (ia->ia_prot);
+
+ if (ac_test_exec (accesstest))
+ ret = IA_PROT_XUSR (ia->ia_prot);
+
+ /* For failed access test for owner, we need to return EACCES */
+ if (!ret)
+ ret = -1;
+ else
+ ret = 0;
+out:
+ return ret;
+}
+
+
+int
+ac_test_group_access (struct iatt *ia, gid_t gid, gid_t *auxgids, int auxcount,
+ int accesstest)
+{
+ int ret = -1;
+ int testgid = -1;
+ int x = 0;
+
+ if (!ia)
+ return -1;
+ /* First, determine which gid to test against. This will be determined
+ * by first checking which of the gids given to us match the gid in the
+ * stat. If none match, then we go to checking with others as the user.
+ */
+
+ /* If we are only given the primary gid. Dont depend on @auxgids
+ * being NULL since I know users of this function can pass statically
+ * allocated arrays which cant be NULL and yet contain no valid gids.
+ */
+
+ if ((ia->ia_gid != gid) && (auxcount == 0)) {
+ ret = -1;
+ goto out;
+ }
+
+ if (ia->ia_gid == gid)
+ testgid = gid;
+ else {
+ for (; x < auxcount; ++x) {
+ if (ia->ia_gid == auxgids[x]) {
+ testgid = ia->ia_gid;
+ break;
+ }
+ }
+ }
+
+ /* None of the gids match with the gid in the stat. */
+ if (testgid == -1) {
+ ret = -1;
+ goto out;
+ }
+
+ /* At this point, at least one gid matches that in the stat, now we must
+ * check whether the caller is interested in the access check at all.
+ */
+ if (ac_test_dontcare (accesstest)) {
+ ret = 0;
+ goto out;
+ }
+
+ if (ac_test_read (accesstest))
+ ret = IA_PROT_RGRP (ia->ia_prot);
+
+ if (ac_test_write (accesstest))
+ ret = IA_PROT_WGRP (ia->ia_prot);
+
+ if (ac_test_exec (accesstest))
+ ret = IA_PROT_XGRP (ia->ia_prot);
+
+ if (!ret)
+ ret = -1;
+ else
+ ret = 0;
+
+out:
+ return ret;
+}
+
+
+int
+ac_test_other_access (struct iatt *ia, int accesstest)
+{
+ int ret = 0;
+
+ if (!ia)
+ return -1;
+
+ if (ac_test_read (accesstest))
+ ret = IA_PROT_ROTH (ia->ia_prot);
+
+ if (ac_test_write (accesstest))
+ ret = IA_PROT_WOTH (ia->ia_prot);
+
+ if (ac_test_exec (accesstest))
+ ret = IA_PROT_XOTH (ia->ia_prot);
+
+ if (!ret)
+ ret = -1;
+ else
+ ret = 0;
+
+ return ret;
+}
+
+
+/* Returns -1 on a failed access test with @operrno set to the relevant error
+ * number.
+ */
+int
+ac_test_access (struct iatt *ia, uid_t uid, gid_t gid, gid_t *auxgids,
+ int auxcount, int accesstest, int testwho, int *operrno)
+{
+ int ret = -1;
+
+ if ((!ia) || (!operrno))
+ return -1;
+
+ if ((uid == 0) && (gid == 0)) {
+ gf_log (ACTRL, GF_LOG_TRACE, "Root has access");
+ return 0;
+ }
+
+ if (ac_test_owner (testwho)) {
+ gf_log (ACTRL, GF_LOG_TRACE, "Testing owner access");
+ ret = ac_test_owner_access (ia, uid, accesstest);
+ }
+
+ if (ret == 0) {
+ gf_log (ACTRL, GF_LOG_TRACE, "Owner has access");
+ goto out;
+ }
+
+ if (ac_test_group (testwho)) {
+ gf_log (ACTRL, GF_LOG_TRACE, "Testing group access");
+ ret = ac_test_group_access (ia, gid, auxgids, auxcount,
+ accesstest);
+ }
+
+ if (ret == 0) {
+ gf_log (ACTRL, GF_LOG_TRACE, "Group has access");
+ goto out;
+ }
+
+ if (ac_test_other (testwho)) {
+ gf_log (ACTRL, GF_LOG_TRACE, "Testing other access");
+ ret = ac_test_other_access (ia, accesstest);
+ }
+
+ if (ret == 0)
+ gf_log (ACTRL, GF_LOG_TRACE, "Other has access");
+out:
+ if (ret == -1) {
+ gf_log (ACTRL, GF_LOG_TRACE, "No access allowed");
+ *operrno = EPERM;
+ }
+
+ return ret;
+}
+
+
+int
+ac_loc_fill (loc_t *loc, inode_t *inode, inode_t *parent, char *path)
+{
+ int ret = -EFAULT;
+
+ if (!loc)
+ return ret;
+
+ if (inode) {
+ loc->inode = inode_ref (inode);
+ loc->ino = inode->ino;
+ }
+
+ if (parent)
+ loc->parent = inode_ref (parent);
+
+ loc->path = strdup (path);
+ if (!loc->path) {
+ gf_log (ACTRL, GF_LOG_ERROR, "strdup failed");
+ goto loc_wipe;
+ }
+
+ loc->name = strrchr (loc->path, '/');
+ if (loc->name)
+ loc->name++;
+ else
+ goto loc_wipe;
+
+ ret = 0;
+loc_wipe:
+ if (ret < 0)
+ loc_wipe (loc);
+
+ return ret;
+}
+
+
+int
+ac_inode_loc_fill (inode_t *inode, loc_t *loc)
+{
+ char *resolvedpath = NULL;
+ inode_t *parent = NULL;
+ int ret = -EFAULT;
+
+ if ((!inode) || (!loc))
+ return ret;
+
+ if ((inode) && (inode->ino == 1))
+ goto ignore_parent;
+
+ parent = inode_parent (inode, 0, NULL);
+ if (!parent)
+ goto err;
+
+ignore_parent:
+ ret = inode_path (inode, NULL, &resolvedpath);
+ if (ret < 0)
+ goto err;
+
+ ret = ac_loc_fill (loc, inode, parent, resolvedpath);
+ if (ret < 0)
+ goto err;
+
+err:
+ if (parent)
+ inode_unref (parent);
+
+ if (resolvedpath)
+ FREE (resolvedpath);
+
+ return ret;
+}
+
+
+int
+ac_parent_loc_fill (loc_t *parentloc, loc_t *childloc)
+{
+ if ((!parentloc) || (!childloc))
+ return -1;
+
+ return ac_inode_loc_fill (childloc->parent, parentloc);
+}
+
+
+int32_t
+ac_truncate_resume (call_frame_t *frame, xlator_t *this, loc_t *loc,
+ off_t offset)
+{
+ STACK_WIND (frame, default_truncate_cbk, FIRST_CHILD(this),
+ FIRST_CHILD(this)->fops->truncate, loc, offset);
+ return 0;
+}
+
+
+int32_t
+ac_truncate_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int32_t op_ret, int32_t op_errno, struct iatt *buf)
+{
+ call_stub_t *stub = NULL;
+
+ stub = __get_frame_stub (frame);
+ if (op_ret == -1)
+ goto out;
+
+ op_ret = ac_test_access (buf, frame->root->uid, frame->root->gid,
+ frame->root->groups, frame->root->ngrps,
+ ACCTEST_WRITE, ACCTEST_ANY, &op_errno);
+ if (op_ret == -1)
+ goto out;
+
+ call_resume (stub);
+out:
+ if (op_ret < 0) {
+ STACK_UNWIND_STRICT (truncate, frame, -1, op_errno, NULL, NULL);
+ if (stub)
+ call_stub_destroy (stub);
+ }
+
+ return 0;
+}
+
+
+int32_t
+ac_truncate (call_frame_t *frame, xlator_t *this, loc_t *loc, off_t offset)
+{
+ call_stub_t *stub = NULL;
+ int ret = -EFAULT;
+
+ stub = fop_truncate_stub (frame, ac_truncate_resume, loc, offset);
+ if (!stub) {
+ gf_log (this->name, GF_LOG_ERROR, "cannot create call stub: "
+ "(out of memory)");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ frame->local = stub;
+ STACK_WIND (frame, ac_truncate_stat_cbk, FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->stat, loc);
+
+ ret = 0;
+out:
+ if (ret < 0)
+ STACK_UNWIND_STRICT (truncate, frame, -1, -ret, NULL, NULL);
+
+ return 0;
+}
+
+
+int32_t
+ac_access_resume (call_frame_t *frame, xlator_t *this, loc_t *loc, int32_t mask)
+{
+ STACK_WIND (frame, default_access_cbk, FIRST_CHILD(this),
+ FIRST_CHILD(this)->fops->access, loc, mask);
+ return 0;
+}
+
+
+int32_t
+ac_access_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int32_t op_ret, int32_t op_errno, struct iatt *buf)
+{
+ call_stub_t *stub = NULL;
+ int32_t mask = 0;
+ int acctest = 0;
+
+ stub = __get_frame_stub (frame);
+ mask = stub->args.access.mask;
+
+ /* If mask requests test for file existence then do not
+ * return a failure with ENOENT, instead return a failed
+ * access test.
+ */
+ if (op_ret == -1) {
+ if (mask & F_OK)
+ op_errno = EACCES;
+ else
+ op_errno = errno;
+
+ goto out;
+ }
+
+ if (R_OK & mask)
+ acctest |= ACCTEST_READ;
+ else if (W_OK & mask)
+ acctest |= ACCTEST_WRITE;
+ else if (X_OK & mask)
+ acctest |= ACCTEST_EXEC;
+ else
+ acctest = 0;
+
+ op_ret = ac_test_access (buf, frame->root->uid, frame->root->gid,
+ frame->root->groups, frame->root->ngrps,
+ acctest, ACCTEST_ANY, &op_errno);
+ if (op_ret == -1)
+ goto out;
+
+ call_resume (stub);
+out:
+ if (op_ret < 0) {
+ STACK_UNWIND_STRICT (access, frame, -1, op_errno);
+ if (stub)
+ call_stub_destroy (stub);
+ }
+
+ return 0;
+}
+
+
+int32_t
+ac_access (call_frame_t *frame, xlator_t *this, loc_t *loc, int32_t mask)
+{
+ call_stub_t *stub = NULL;
+ int ret = -EFAULT;
+
+ stub = fop_access_stub (frame, ac_access_resume, loc, mask);
+ if (!stub) {
+ gf_log (this->name, GF_LOG_ERROR, "cannot create call stub: "
+ "(out of memory)");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ frame->local = stub;
+ STACK_WIND (frame, ac_access_stat_cbk, FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->stat, loc);
+ ret = 0;
+
+out:
+ if (ret < 0)
+ STACK_UNWIND_STRICT (access, frame, -1, -ret);
+
+ return 0;
+}
+
+
+int32_t
+ac_readlink_resume (call_frame_t *frame, xlator_t *this, loc_t *loc,
+ size_t size)
+{
+ STACK_WIND (frame, default_readlink_cbk, FIRST_CHILD(this),
+ FIRST_CHILD(this)->fops->readlink, loc, size);
+ return 0;
+}
+
+
+int32_t
+ac_readlink_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int32_t op_ret, int32_t op_errno, struct iatt *buf)
+{
+ call_stub_t *stub = NULL;
+
+ stub = __get_frame_stub (frame);
+ if (op_ret == -1)
+ goto out;
+
+ op_ret = ac_test_access (buf, frame->root->uid, frame->root->gid,
+ frame->root->groups, frame->root->ngrps,
+ ACCTEST_READ, ACCTEST_ANY, &op_errno);
+ if (op_ret == -1)
+ goto out;
+
+ call_resume (stub);
+out:
+ if (op_ret < 0) {
+ STACK_UNWIND_STRICT (readlink, frame, -1, op_errno, NULL, NULL);
+ if (stub)
+ call_stub_destroy (stub);
+ }
+
+ return 0;
+}
+
+
+int32_t
+ac_readlink (call_frame_t *frame, xlator_t *this, loc_t *loc, size_t size)
+{
+ call_stub_t *stub = NULL;
+ int ret = -EFAULT;
+
+ stub = fop_readlink_stub (frame, ac_readlink_resume, loc, size);
+ if (!stub) {
+ gf_log (this->name, GF_LOG_ERROR, "cannot create call stub: "
+ "(out of memory)");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ frame->local = stub;
+ STACK_WIND (frame, ac_readlink_stat_cbk, FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->stat, loc);
+ ret = 0;
+
+out:
+ if (ret < 0)
+ STACK_UNWIND_STRICT (readlink, frame, -1, -ret, NULL, NULL);
+
+ return 0;
+}
+
+
+int32_t
+ac_mknod_resume (call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode,
+ dev_t rdev)
+{
+ STACK_WIND (frame, default_mknod_cbk, FIRST_CHILD(this),
+ FIRST_CHILD(this)->fops->mknod, loc, mode, rdev);
+ return 0;
+}
+
+
+int32_t
+ac_mknod_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int32_t op_ret, int32_t op_errno, struct iatt *buf)
+{
+ call_stub_t *stub = NULL;
+
+ stub = __get_frame_stub (frame);
+ if (op_ret == -1)
+ goto out;
+
+ op_ret = ac_test_access (buf, frame->root->uid, frame->root->gid,
+ frame->root->groups, frame->root->ngrps,
+ ACCTEST_WRITE, ACCTEST_ANY, &op_errno);
+ if (op_ret == -1) {
+ op_errno = EACCES;
+ goto out;
+ }
+
+ call_resume (stub);
+out:
+ if (op_ret < 0) {
+ STACK_UNWIND_STRICT (mknod, frame, -1, op_errno, NULL, NULL,
+ NULL, NULL);
+ if (stub)
+ call_stub_destroy (stub);
+ }
+
+ return 0;
+}
+
+
+int32_t
+ac_mknod (call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode,
+ dev_t rdev)
+{
+ call_stub_t *stub = NULL;
+ int ret = -EFAULT;
+ loc_t parentloc = {0, };
+
+ stub = fop_mknod_stub (frame, ac_mknod_resume, loc, mode, rdev);
+ if (!stub) {
+ gf_log (this->name, GF_LOG_ERROR, "cannot create call stub: "
+ "(out of memory)");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ frame->local = stub;
+ ret = ac_parent_loc_fill (&parentloc, loc);
+ if (ret < 0)
+ goto out;
+
+ STACK_WIND (frame, ac_mknod_stat_cbk, FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->stat, &parentloc);
+ loc_wipe (&parentloc);
+ ret = 0;
+
+out:
+ if (ret < 0) {
+ /* Erase any stored frame before unwinding. */
+ stub = __get_frame_stub (frame);
+ if (stub)
+ call_stub_destroy (stub);
+ STACK_UNWIND_STRICT (mknod, frame, -1, -ret, NULL, NULL, NULL,
+ NULL);
+ }
+
+ return 0;
+}
+
+
+int32_t
+ac_mkdir_resume (call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode)
+{
+ STACK_WIND (frame, default_mkdir_cbk, FIRST_CHILD(this),
+ FIRST_CHILD(this)->fops->mkdir, loc, mode);
+ return 0;
+}
+
+
+int32_t
+ac_mkdir_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int32_t op_ret, int32_t op_errno, struct iatt *buf)
+{
+ call_stub_t *stub = NULL;
+
+ stub = __get_frame_stub (frame);
+ if (op_ret == -1)
+ goto out;
+
+ op_ret = ac_test_access (buf, frame->root->uid, frame->root->gid,
+ frame->root->groups, frame->root->ngrps,
+ ACCTEST_WRITE, ACCTEST_ANY, &op_errno);
+ if (op_ret == -1) {
+ /* On a failed write test on parent dir, we need to return
+ * EACCES, not EPERM that is returned by default by
+ * ac_test_access.
+ */
+ op_errno = EACCES;
+ goto out;
+ }
+
+ call_resume (stub);
+out:
+ if (op_ret < 0) {
+ STACK_UNWIND_STRICT (mkdir, frame, -1, op_errno, NULL, NULL,
+ NULL, NULL);
+ if (stub)
+ call_stub_destroy (stub);
+ }
+
+ return 0;
+}
+
+
+int32_t
+ac_mkdir (call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode)
+{
+ call_stub_t *stub = NULL;
+ int ret = -EFAULT;
+ loc_t parentloc = {0, };
+
+ stub = fop_mkdir_stub (frame, ac_mkdir_resume, loc, mode);
+ if (!stub) {
+ gf_log (this->name, GF_LOG_ERROR, "cannot create call stub: "
+ "(out of memory)");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ frame->local = stub;
+ ret = ac_parent_loc_fill (&parentloc, loc);
+ if (ret < 0)
+ goto out;
+
+ STACK_WIND (frame, ac_mkdir_stat_cbk, FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->stat, &parentloc);
+ loc_wipe (&parentloc);
+ ret = 0;
+
+out:
+ if (ret < 0) {
+ /* Erase the stored stub before unwinding. */
+ stub = __get_frame_stub (frame);
+ if (stub)
+ call_stub_destroy (stub);
+ STACK_UNWIND_STRICT (mkdir, frame, -1, -ret, NULL, NULL, NULL,
+ NULL);
+ }
+
+ return 0;
+}
+
+
+int32_t
+ac_unlink_resume (call_frame_t *frame, xlator_t *this, loc_t *loc)
+{
+ STACK_WIND (frame, default_unlink_cbk, FIRST_CHILD(this),
+ FIRST_CHILD(this)->fops->unlink, loc);
+ return 0;
+}
+
+
+int32_t
+ac_unlink_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int32_t op_ret, int32_t op_errno, struct iatt *buf)
+{
+ call_stub_t *stub = NULL;
+
+ stub = __get_frame_stub (frame);
+ if (op_ret == -1)
+ goto out;
+
+ op_ret = ac_test_access (buf, frame->root->uid, frame->root->gid,
+ frame->root->groups, frame->root->ngrps,
+ ACCTEST_WRITE, ACCTEST_ANY, &op_errno);
+ if (op_ret == -1) {
+ op_errno = EACCES;
+ goto out;
+ }
+
+ call_resume (stub);
+out:
+ if (op_ret < 0) {
+ STACK_UNWIND_STRICT (unlink, frame, -1, op_errno, NULL, NULL);
+ if (stub)
+ call_stub_destroy (stub);
+ }
+
+ return 0;
+}
+
+
+int32_t
+ac_unlink (call_frame_t *frame, xlator_t *this, loc_t *loc)
+{
+ call_stub_t *stub = NULL;
+ int ret = -EFAULT;
+ loc_t parentloc = {0, };
+
+ stub = fop_unlink_stub (frame, ac_unlink_resume, loc);
+ if (!stub) {
+ gf_log (this->name, GF_LOG_ERROR, "cannot create call stub: "
+ "(out of memory)");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ frame->local = stub;
+ ret = ac_parent_loc_fill (&parentloc, loc);
+ if (ret < 0)
+ goto out;
+
+ STACK_WIND (frame, ac_unlink_stat_cbk, FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->stat, &parentloc);
+ loc_wipe (&parentloc);
+ ret = 0;
+
+out:
+ if (ret < 0) {
+ /* Erase the stored stub before unwinding. */
+ stub = __get_frame_stub (frame);
+ if (stub)
+ call_stub_destroy (stub);
+ STACK_UNWIND_STRICT (unlink, frame, -1, -ret, NULL, NULL);
+ }
+
+ return 0;
+}
+
+
+int32_t
+ac_rmdir_resume (call_frame_t *frame, xlator_t *this, loc_t *loc)
+{
+ STACK_WIND (frame, default_rmdir_cbk, FIRST_CHILD(this),
+ FIRST_CHILD(this)->fops->rmdir, loc);
+ return 0;
+}
+
+
+int32_t
+ac_rmdir_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int32_t op_ret, int32_t op_errno, struct iatt *buf)
+{
+ call_stub_t *stub = NULL;
+
+ stub = __get_frame_stub (frame);
+ if (op_ret == -1)
+ goto out;
+
+ op_ret = ac_test_access (buf, frame->root->uid, frame->root->gid,
+ frame->root->groups, frame->root->ngrps,
+ ACCTEST_WRITE, ACCTEST_ANY, &op_errno);
+ if (op_ret == -1) {
+ op_errno = EACCES;
+ goto out;
+ }
+
+ call_resume (stub);
+out:
+ if (op_ret < 0) {
+ STACK_UNWIND_STRICT (rmdir, frame, -1, op_errno, NULL, NULL);
+ if (stub)
+ call_stub_destroy (stub);
+ }
+
+ return 0;
+}
+
+
+int32_t
+ac_rmdir (call_frame_t *frame, xlator_t *this, loc_t *loc)
+{
+ call_stub_t *stub = NULL;
+ int ret = -EFAULT;
+ loc_t parentloc = {0, };
+
+ stub = fop_rmdir_stub (frame, ac_rmdir_resume, loc);
+ if (!stub) {
+ gf_log (this->name, GF_LOG_ERROR, "cannot create call stub: "
+ "(out of memory)");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ frame->local = stub;
+ ret = ac_parent_loc_fill (&parentloc, loc);
+ if (ret < 0)
+ goto out;
+
+ STACK_WIND (frame, ac_rmdir_stat_cbk, FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->stat, &parentloc);
+ loc_wipe (&parentloc);
+ ret = 0;
+
+out:
+ if (ret < 0) {
+ /* Erase the stored stub before unwinding. */
+ stub = __get_frame_stub (frame);
+ if (stub)
+ call_stub_destroy (stub);
+ STACK_UNWIND_STRICT (rmdir, frame, -1, -ret, NULL, NULL);
+ }
+
+ return 0;
+}
+
+
+int32_t
+ac_symlink_resume (call_frame_t *frame, xlator_t *this, const char *linkname,
+ loc_t *loc)
+{
+ STACK_WIND (frame, default_symlink_cbk, FIRST_CHILD(this),
+ FIRST_CHILD(this)->fops->symlink, linkname, loc);
+ return 0;
+}
+
+
+int32_t
+ac_symlink_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int32_t op_ret, int32_t op_errno, struct iatt *buf)
+{
+ call_stub_t *stub = NULL;
+
+ stub = __get_frame_stub (frame);
+ if (op_ret == -1)
+ goto out;
+
+ op_ret = ac_test_access (buf, frame->root->uid, frame->root->gid,
+ frame->root->groups, frame->root->ngrps,
+ ACCTEST_WRITE, ACCTEST_ANY, &op_errno);
+ if (op_ret == -1) {
+ op_errno = EACCES;
+ goto out;
+ }
+
+ call_resume (stub);
+out:
+ if (op_ret < 0) {
+ STACK_UNWIND_STRICT (symlink, frame, -1, op_errno, NULL, NULL,
+ NULL, NULL);
+ if (stub)
+ call_stub_destroy (stub);
+ }
+
+ return 0;
+}
+
+
+int32_t
+ac_symlink (call_frame_t *frame, xlator_t *this, const char *linkname,
+ loc_t *loc)
+{
+ call_stub_t *stub = NULL;
+ int ret = -EFAULT;
+ loc_t parentloc = {0, };
+
+ stub = fop_symlink_stub (frame, ac_symlink_resume, linkname, loc);
+ if (!stub) {
+ gf_log (this->name, GF_LOG_ERROR, "cannot create call stub: "
+ "(out of memory)");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ frame->local = stub;
+ ret = ac_parent_loc_fill (&parentloc, loc);
+ if (ret < 0)
+ goto out;
+
+ STACK_WIND (frame, ac_symlink_stat_cbk, FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->stat, &parentloc);
+ loc_wipe (&parentloc);
+ ret = 0;
+
+out:
+ if (ret < 0) {
+ /* Erase the stored stub before unwinding. */
+ stub = __get_frame_stub (frame);
+ if (stub)
+ call_stub_destroy (stub);
+ STACK_UNWIND_STRICT (symlink, frame, -1, -ret, NULL, NULL, NULL,
+ NULL);
+ }
+
+ return 0;
+}
+
+
+int32_t
+ac_rename_resume (call_frame_t *frame, xlator_t *this, loc_t *oldloc,
+ loc_t *newloc)
+{
+ STACK_WIND (frame, default_rename_cbk, FIRST_CHILD(this),
+ FIRST_CHILD(this)->fops->rename, oldloc, newloc);
+ return 0;
+}
+
+
+int32_t
+ac_rename_dst_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int32_t op_ret, int32_t op_errno, struct iatt *buf)
+{
+ call_stub_t *stub = NULL;
+
+ stub = __get_frame_stub (frame);
+ if (op_ret == -1)
+ goto out;
+
+ op_ret = ac_test_access (buf, frame->root->uid,
+ frame->root->gid, frame->root->groups,
+ frame->root->ngrps, ACCTEST_WRITE,
+ ACCTEST_ANY, &op_errno);
+ if (op_ret == -1) {
+ op_errno = EACCES;
+ goto out;
+ }
+
+ call_resume (stub);
+out:
+ if (op_ret < 0) {
+ STACK_UNWIND_STRICT (rename, frame, -1, op_errno, NULL, NULL,
+ NULL, NULL, NULL);
+ if (stub)
+ call_stub_destroy (stub);
+ }
+
+ return 0;
+}
+
+
+int32_t
+ac_rename_src_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int32_t op_ret, int32_t op_errno, struct iatt *buf)
+{
+ call_stub_t *stub = NULL;
+ loc_t parentloc = {0, };
+
+ stub = frame->local;
+ if (op_ret == -1)
+ goto out;
+
+ op_ret = ac_test_access (buf, frame->root->uid,
+ frame->root->gid, frame->root->groups,
+ frame->root->ngrps, ACCTEST_WRITE,
+ ACCTEST_ANY, &op_errno);
+ if (op_ret == -1) {
+ op_errno = EACCES;
+ goto out;
+ }
+
+ op_ret = ac_parent_loc_fill (&parentloc, &stub->args.rename.new);
+ if (op_ret == -1) {
+ op_errno = -EFAULT;
+ goto out;
+ }
+
+ STACK_WIND (frame, ac_rename_dst_stat_cbk, FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->stat, &parentloc);
+ loc_wipe (&parentloc);
+
+out:
+ if (op_ret < 0) {
+ /* Erase the stored stub before unwinding. */
+ stub = __get_frame_stub (frame);
+ if (stub)
+ call_stub_destroy (stub);
+ STACK_UNWIND_STRICT (rename, frame, -1, op_errno, NULL, NULL,
+ NULL, NULL, NULL);
+ }
+
+ return 0;
+}
+
+
+int32_t
+ac_rename (call_frame_t *frame, xlator_t *this, loc_t *oldloc, loc_t *newloc)
+{
+ call_stub_t *stub = NULL;
+ int ret = -EFAULT;
+ loc_t parentloc = {0, };
+
+ stub = fop_rename_stub (frame, ac_rename_resume, oldloc, newloc);
+ if (!stub) {
+ gf_log (this->name, GF_LOG_ERROR, "cannot create call stub: "
+ "(out of memory)");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ frame->local = stub;
+ ret = ac_parent_loc_fill (&parentloc, oldloc);
+ if (ret < 0)
+ goto out;
+
+ STACK_WIND (frame, ac_rename_src_stat_cbk, FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->stat, &parentloc);
+ loc_wipe (&parentloc);
+ ret = 0;
+
+out:
+ if (ret < 0) {
+ /* Erase the stored stub before unwinding. */
+ stub = __get_frame_stub (frame);
+ if (stub)
+ call_stub_destroy (stub);
+ STACK_UNWIND_STRICT (rename, frame, -1, -ret, NULL, NULL, NULL,
+ NULL, NULL);
+ }
+
+ return 0;
+}
+
+
+int32_t
+ac_link_resume (call_frame_t *frame, xlator_t *this, loc_t *oldloc,
+ loc_t *newloc)
+{
+ STACK_WIND (frame, default_link_cbk, FIRST_CHILD(this),
+ FIRST_CHILD(this)->fops->link, oldloc, newloc);
+ return 0;
+}
+
+
+int32_t
+ac_link_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int32_t op_ret, int32_t op_errno, struct iatt *buf)
+{
+ call_stub_t *stub = NULL;
+
+ stub = __get_frame_stub (frame);
+ if (op_ret == -1)
+ goto out;
+
+ op_ret = ac_test_access (buf, frame->root->uid, frame->root->gid,
+ frame->root->groups, frame->root->ngrps,
+ ACCTEST_WRITE, ACCTEST_ANY, &op_errno);
+ if (op_ret == -1) {
+ /* By default ac_test_access sets the op_errno to EPERM
+ * but in the case of link, we need to return EACCES to meet
+ * posix requirements when a write permission is not available
+ * for the new directory.
+ */
+ op_errno = EACCES;
+ goto out;
+ }
+
+ call_resume (stub);
+out:
+ if (op_ret < 0) {
+ STACK_UNWIND_STRICT (link, frame, -1, op_errno, NULL, NULL,
+ NULL, NULL);
+ if (stub)
+ call_stub_destroy (stub);
+ }
+
+ return 0;
+}
+
+
+int32_t
+ac_link (call_frame_t *frame, xlator_t *this, loc_t *oldloc, loc_t *newloc)
+{
+ call_stub_t *stub = NULL;
+ int ret = -EFAULT;
+ loc_t parentloc = {0, };
+
+ stub = fop_link_stub (frame, ac_link_resume, oldloc, newloc);
+ if (!stub) {
+ gf_log (this->name, GF_LOG_ERROR, "cannot create call stub: "
+ "(out of memory)");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ frame->local = stub;
+ ret = ac_parent_loc_fill (&parentloc, newloc);
+ if (ret < 0)
+ goto out;
+
+ STACK_WIND (frame, ac_link_stat_cbk, FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->stat, &parentloc);
+ loc_wipe (&parentloc);
+ ret = 0;
+
+out:
+ if (ret < 0) {
+ /* Erase the stored stub before unwinding. */
+ stub = __get_frame_stub (frame);
+ if (stub)
+ call_stub_destroy (stub);
+ STACK_UNWIND_STRICT (link, frame, -1, -ret, NULL, NULL, NULL,
+ NULL);
+ }
+
+ return 0;
+}
+
+
+int32_t
+ac_create_resume (call_frame_t *frame, xlator_t *this, loc_t *loc,
+ int32_t flags, mode_t mode, fd_t *fd)
+{
+ STACK_WIND (frame, default_create_cbk, FIRST_CHILD(this),
+ FIRST_CHILD(this)->fops->create, loc, flags, mode, fd);
+ return 0;
+}
+
+
+int32_t
+ac_create_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int32_t op_ret, int32_t op_errno, struct iatt *buf)
+{
+ call_stub_t *stub = NULL;
+
+ stub = __get_frame_stub (frame);
+ if (op_ret == -1)
+ goto out;
+
+ op_ret = ac_test_access (buf, frame->root->uid, frame->root->gid,
+ frame->root->groups, frame->root->ngrps,
+ ACCTEST_WRITE, ACCTEST_ANY, &op_errno);
+ if (op_ret == -1) {
+ op_errno = EACCES;
+ goto out;
+ }
+
+ call_resume (stub);
+out:
+ if (op_ret < 0) {
+ STACK_UNWIND_STRICT (create, frame, -1, op_errno, NULL, NULL,
+ NULL, NULL, NULL);
+ if (stub)
+ call_stub_destroy (stub);
+ }
+
+ return 0;
+}
+
+
+int32_t
+ac_create (call_frame_t *frame, xlator_t *this, loc_t *loc, int32_t flags,
+ mode_t mode, fd_t *fd)
+{
+ call_stub_t *stub = NULL;
+ int ret = -EFAULT;
+ loc_t parentloc = {0, };
+
+ stub = fop_create_stub (frame, ac_create_resume, loc, flags, mode, fd);
+ if (!stub) {
+ gf_log (this->name, GF_LOG_ERROR, "cannot create call stub: "
+ "(out of memory)");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ frame->local = stub;
+ ret = ac_parent_loc_fill (&parentloc, loc);
+ if (ret < 0)
+ goto out;
+
+ STACK_WIND (frame, ac_create_stat_cbk, FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->stat, &parentloc);
+ loc_wipe (&parentloc);
+ ret = 0;
+
+out:
+ if (ret < 0) {
+ /* Erase the stored stub before unwinding. */
+ stub = __get_frame_stub (frame);
+ if (stub)
+ call_stub_destroy (stub);
+ STACK_UNWIND_STRICT (create, frame, -1, -ret, NULL, NULL, NULL,
+ NULL, NULL);
+ }
+
+ return 0;
+}
+
+
+int32_t
+ac_open_resume (call_frame_t *frame, xlator_t *this, loc_t *loc, int32_t flags,
+ fd_t *fd, int32_t wbflags)
+{
+ STACK_WIND (frame, default_open_cbk, FIRST_CHILD(this),
+ FIRST_CHILD(this)->fops->open, loc, flags, fd, wbflags);
+ return 0;
+}
+
+
+int32_t
+ac_open_create_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int32_t op_ret, int32_t op_errno, struct iatt *buf)
+{
+ call_stub_t *stub = NULL;
+
+ stub = __get_frame_stub (frame);
+ if (op_ret == -1)
+ goto out;
+
+ op_ret = ac_test_access (buf, frame->root->uid, frame->root->gid,
+ frame->root->groups, frame->root->ngrps,
+ ACCTEST_WRITE, ACCTEST_ANY, &op_errno);
+ if (op_ret == -1) {
+ op_errno = EACCES;
+ goto out;
+ }
+
+ call_resume (stub);
+out:
+ if (op_ret < 0) {
+ STACK_UNWIND_STRICT (open, frame, -1, op_errno, NULL);
+ if (stub)
+ call_stub_destroy (stub);
+ }
+
+ return 0;
+}
+
+
+int
+ac_open_create (call_stub_t *stub)
+{
+ int ret = -EFAULT;
+ loc_t parentloc = {0, };
+ xlator_t *this = NULL;
+
+ if (!stub)
+ return ret;
+
+ ret = ac_parent_loc_fill (&parentloc, &stub->args.open.loc);
+ if (ret < 0)
+ goto out;
+
+ this = stub->frame->this;
+ STACK_WIND (stub->frame, ac_open_create_stat_cbk, FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->stat, &parentloc);
+ loc_wipe (&parentloc);
+ ret = 0;
+
+out:
+ return ret;
+}
+
+
+int32_t
+ac_open_only_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int32_t op_ret, int32_t op_errno, struct iatt *buf)
+{
+ call_stub_t *stub = NULL;
+ int acctest = 0;
+ int32_t flags = 0;
+
+ stub = __get_frame_stub (frame);
+ if (op_ret == -1)
+ goto out;
+
+ flags = stub->args.open.flags;
+ /* The permissions we test for depend on how the open needs to be
+ * performed. */
+ if ((flags & O_ACCMODE) == O_RDONLY)
+ acctest = ACCTEST_READ;
+ else if (((flags & O_ACCMODE) == O_RDWR) ||
+ ((flags & O_ACCMODE) == O_WRONLY))
+ acctest = ACCTEST_WRITE;
+
+ op_ret = ac_test_access (buf, frame->root->uid, frame->root->gid,
+ frame->root->groups, frame->root->ngrps,
+ acctest, ACCTEST_ANY, &op_errno);
+ if (op_ret == -1)
+ goto out;
+
+ call_resume (stub);
+out:
+ if (op_ret < 0) {
+ STACK_UNWIND_STRICT (open, frame, -1, op_errno, NULL);
+ if (stub)
+ call_stub_destroy (stub);
+ }
+
+ return 0;
+}
+
+
+int
+ac_open_only (call_stub_t *stub)
+{
+ int ret = -EFAULT;
+ xlator_t *this = NULL;
+
+ if (!stub)
+ return ret;
+
+ this = stub->frame->this;
+ STACK_WIND (stub->frame, ac_open_only_stat_cbk, FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->stat, &stub->args.open.loc);
+ return 0;
+}
+
+int32_t
+ac_open (call_frame_t *frame, xlator_t *this, loc_t *loc, int32_t flags,
+ fd_t *fd, int32_t wbflags)
+{
+ call_stub_t *stub = NULL;
+ int ret = -EFAULT;
+
+ stub = fop_open_stub (frame, ac_open_resume, loc, flags, fd, wbflags);
+ if (!stub) {
+ gf_log (this->name, GF_LOG_ERROR, "cannot create call stub: "
+ "(out of memory)");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ frame->local = stub;
+ /* If we are not supposed to create the file then there is no need to
+ * check the parent dir permissions. */
+ if (!(flags & O_CREAT))
+ ret = ac_open_create (stub);
+ else
+ ret = ac_open_only (stub);
+
+out:
+ if (ret < 0) {
+ /* Erase the stored stub before unwinding. */
+ stub = __get_frame_stub (frame);
+ if (stub)
+ call_stub_destroy (stub);
+ STACK_UNWIND_STRICT (open, frame, -1, -ret, NULL);
+ }
+
+ return 0;
+}
+
+
+int32_t
+ac_readv_resume (call_frame_t *frame, xlator_t *this, fd_t *fd, size_t size,
+ off_t offset)
+{
+ STACK_WIND (frame, default_readv_cbk, FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->readv, fd, size, offset);
+ return 0;
+}
+
+
+int32_t
+ac_readv_fstat_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int32_t op_ret, int32_t op_errno, struct iatt *buf)
+{
+ call_stub_t *stub = NULL;
+
+ stub = __get_frame_stub (frame);
+ if (op_ret == -1)
+ goto out;
+
+ op_ret = ac_test_access (buf, frame->root->uid, frame->root->gid,
+ frame->root->groups, frame->root->ngrps,
+ ACCTEST_READ, ACCTEST_ANY, &op_errno);
+ if (op_ret == -1) {
+ op_errno = EACCES;
+ goto out;
+ }
+
+ call_resume (stub);
+out:
+ if (op_ret < 0) {
+ STACK_UNWIND_STRICT (readv, frame, -1, op_errno, NULL, 0, NULL,
+ NULL);
+ if (stub)
+ call_stub_destroy (stub);
+ }
+
+ return 0;
+}
+
+
+int32_t
+ac_readv (call_frame_t *frame, xlator_t *this, fd_t *fd, size_t size,
+ off_t offset)
+{
+ call_stub_t *stub = NULL;
+ int ret = -EFAULT;
+
+ stub = fop_readv_stub (frame, ac_readv_resume, fd, size, offset);
+ if (!stub) {
+ gf_log (this->name, GF_LOG_ERROR, "cannot create call stub: "
+ "(out of memory)");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ frame->local = stub;
+ STACK_WIND (frame, ac_readv_fstat_cbk, FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->fstat, fd);
+ ret = 0;
+
+out:
+ if (ret < 0)
+ STACK_UNWIND_STRICT (readv, frame, -1, -ret, NULL, 0, NULL,
+ NULL);
+
+ return 0;
+}
+
+
+int32_t
+ac_writev_resume (call_frame_t *frame, xlator_t *this, fd_t *fd,
+ struct iovec *vector, int32_t count, off_t offset,
+ struct iobref *iobref)
+{
+ STACK_WIND (frame, default_writev_cbk, FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->writev, fd, vector, count, offset,
+ iobref);
+ return 0;
+}
+
+
+int32_t
+ac_writev_fstat_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int32_t op_ret, int32_t op_errno, struct iatt *buf)
+{
+ call_stub_t *stub = NULL;
+
+ stub = __get_frame_stub (frame);
+ if (op_ret == -1)
+ goto out;
+
+ op_ret = ac_test_access (buf, frame->root->uid, frame->root->gid,
+ frame->root->groups, frame->root->ngrps,
+ ACCTEST_WRITE, ACCTEST_ANY, &op_errno);
+ if (op_ret == -1) {
+ op_errno = EACCES;
+ goto out;
+ }
+
+ call_resume (stub);
+out:
+ if (op_ret < 0) {
+ STACK_UNWIND_STRICT (writev, frame, -1, op_errno, NULL, NULL);
+ if (stub)
+ call_stub_destroy (stub);
+ }
+
+ return 0;
+}
+
+
+int32_t
+ac_writev (call_frame_t *frame, xlator_t *this, fd_t *fd, struct iovec *vector,
+ int32_t count, off_t offset, struct iobref *iobref)
+{
+ call_stub_t *stub = NULL;
+ int ret = -EFAULT;
+
+ stub = fop_writev_stub (frame, ac_writev_resume, fd, vector, count,
+ offset, iobref);
+ if (!stub) {
+ gf_log (this->name, GF_LOG_ERROR, "cannot create call stub: "
+ "(out of memory)");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ frame->local = stub;
+ STACK_WIND (frame, ac_writev_fstat_cbk, FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->fstat, fd);
+ ret = 0;
+
+out:
+ if (ret < 0)
+ STACK_UNWIND_STRICT (writev, frame, -1, -ret, NULL, NULL);
+
+ return 0;
+}
+
+
+int32_t
+ac_opendir_resume (call_frame_t *frame, xlator_t *this, loc_t *loc, fd_t *fd)
+{
+ STACK_WIND (frame, default_opendir_cbk, FIRST_CHILD(this),
+ FIRST_CHILD(this)->fops->opendir, loc, fd);
+ return 0;
+}
+
+
+int32_t
+ac_opendir_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int32_t op_ret, int32_t op_errno, struct iatt *buf)
+{
+ call_stub_t *stub = NULL;
+
+ stub = __get_frame_stub (frame);
+ if (op_ret == -1)
+ goto out;
+
+ op_ret = ac_test_access (buf, frame->root->uid, frame->root->gid,
+ frame->root->groups, frame->root->ngrps,
+ ACCTEST_READ, ACCTEST_ANY, &op_errno);
+ if (op_ret == -1)
+ goto out;
+
+ call_resume (stub);
+out:
+ if (op_ret < 0) {
+ STACK_UNWIND_STRICT (opendir, frame, -1, op_errno, NULL);
+ if (stub)
+ call_stub_destroy (stub);
+ }
+
+ return 0;
+}
+
+int32_t
+ac_opendir (call_frame_t *frame, xlator_t *this, loc_t *loc, fd_t *fd)
+{
+ call_stub_t *stub = NULL;
+ int ret = -EFAULT;
+
+ stub = fop_opendir_stub (frame, ac_opendir_resume, loc, fd);
+ if (!stub) {
+ gf_log (this->name, GF_LOG_ERROR, "cannot create call stub: "
+ "(out of memory)");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ frame->local = stub;
+ STACK_WIND (frame, ac_opendir_stat_cbk, FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->stat, loc);
+ ret = 0;
+
+out:
+ if (ret < 0)
+ STACK_UNWIND_STRICT (opendir, frame, -1, -ret, NULL);
+
+ return 0;
+}
+
+
+int32_t
+ac_setattr_resume (call_frame_t *frame, xlator_t *this, loc_t *loc,
+ struct iatt *buf, int32_t valid)
+{
+ STACK_WIND (frame, default_setattr_cbk, FIRST_CHILD(this),
+ FIRST_CHILD(this)->fops->setattr, loc, buf, valid);
+ return 0;
+}
+
+
+int32_t
+ac_setattr_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int32_t op_ret, int32_t op_errno, struct iatt *buf)
+{
+ call_stub_t *stub = NULL;
+ int32_t valid = 0;
+ struct iatt *setbuf = NULL;
+
+ stub = __get_frame_stub (frame);
+ if (op_ret == -1)
+ goto out;
+
+ op_ret = ac_test_access (buf, frame->root->uid, frame->root->gid,
+ frame->root->groups, frame->root->ngrps,
+ ACCTEST_DONTCARE, ACCTEST_OWNER,
+ &op_errno);
+ if (op_ret == -1)
+ goto out;
+
+ valid = stub->args.setattr.valid;
+ setbuf = &stub->args.setattr.stbuf;
+ if (gf_attr_uid_set (valid) || gf_attr_gid_set (valid)) {
+ /* chown returns EPERM if the operation would change the
+ * ownership, but the effective user ID is not the
+ * super-user and the process is not an owner of the file.
+ * Ref: posix-testsuite/chown/07.t
+ */
+ if ((frame->root->uid != 0) && (gf_attr_uid_set (valid))) {
+ if (buf->ia_uid != setbuf->ia_uid) {
+ op_ret = -1;
+ op_errno = EPERM;
+ goto out;
+ }
+ }
+
+ /* non-super-user can modify file group if he is owner of a
+ * file and gid he is setting is in his groups list.
+ * Ref: posix-testsuite/chown/00.t
+ */
+ if ((frame->root->uid != 0) && (gf_attr_gid_set (valid))) {
+ if (frame->root->uid != buf->ia_uid) {
+ op_ret = -1;
+ op_errno = EPERM;
+ goto out;
+ }
+
+ op_ret = ac_test_access (setbuf, 0, frame->root->gid,
+ frame->root->groups,
+ frame->root->ngrps,
+ ACCTEST_DONTCARE,
+ ACCTEST_GROUP, &op_errno);
+ if (op_ret == -1)
+ goto out;
+ }
+ }
+
+ call_resume (stub);
+out:
+ if (op_ret < 0) {
+ STACK_UNWIND_STRICT (setattr, frame, -1, op_errno, NULL, NULL);
+ if (stub)
+ call_stub_destroy (stub);
+ }
+
+ return 0;
+}
+
+int32_t
+ac_setattr (call_frame_t *frame, xlator_t *this, loc_t *loc, struct iatt *buf,
+ int32_t valid)
+{
+ call_stub_t *stub = NULL;
+ int ret = -EFAULT;
+
+ stub = fop_setattr_stub (frame, ac_setattr_resume, loc, buf, valid);
+ if (!stub) {
+ gf_log (this->name, GF_LOG_ERROR, "cannot create call stub: "
+ "(out of memory)");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ frame->local = stub;
+ STACK_WIND (frame, ac_setattr_stat_cbk, FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->stat, loc);
+ ret = 0;
+
+out:
+ if (ret < 0)
+ STACK_UNWIND_STRICT (setattr, frame, -1, -ret, NULL, NULL);
+
+ return 0;
+}
+
+
+int32_t
+ac_fsetattr_resume (call_frame_t *frame, xlator_t *this, fd_t *fd,
+ struct iatt *buf, int32_t valid)
+{
+ STACK_WIND (frame, default_fsetattr_cbk, FIRST_CHILD(this),
+ FIRST_CHILD(this)->fops->fsetattr, fd, buf, valid);
+ return 0;
+}
+
+
+int32_t
+ac_fsetattr_fstat_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int32_t op_ret, int32_t op_errno, struct iatt *buf)
+{
+ call_stub_t *stub = NULL;
+ int32_t valid = 0;
+ struct iatt *setbuf = NULL;
+
+ stub = __get_frame_stub (frame);
+ if (op_ret == -1)
+ goto out;
+
+ op_ret = ac_test_access (buf, frame->root->uid, frame->root->gid,
+ frame->root->groups, frame->root->ngrps,
+ ACCTEST_DONTCARE, ACCTEST_OWNER,
+ &op_errno);
+ if (op_ret == -1)
+ goto out;
+
+ valid = stub->args.fsetattr.valid;
+ setbuf = &stub->args.fsetattr.stbuf;
+ if (gf_attr_uid_set (valid) && gf_attr_gid_set (valid)) {
+ /* chown returns EPERM if the operation would change the
+ * ownership, but the effective user ID is not the
+ * super-user and the process is not an owner of the file.
+ * Ref: posix-testsuite/chown/07.t
+ */
+ if ((frame->root->uid != 0) && (gf_attr_uid_set (valid))) {
+ if (buf->ia_uid != setbuf->ia_uid) {
+ op_ret = -1;
+ op_errno = EPERM;
+ goto out;
+ }
+ }
+
+ /* non-super-user can modify file group if he is owner of a
+ * file and gid he is setting is in his groups list.
+ * Ref: posix-testsuite/chown/00.t
+ */
+ if ((frame->root->uid != 0) && (gf_attr_gid_set (valid))) {
+ if (frame->root->uid != buf->ia_uid) {
+ op_ret = -1;
+ op_errno = EPERM;
+ goto out;
+ }
+
+ op_ret = ac_test_access (buf, 0, frame->root->gid,
+ frame->root->groups,
+ frame->root->ngrps,
+ ACCTEST_DONTCARE,
+ ACCTEST_GROUP, &op_errno);
+ if (op_ret == -1)
+ goto out;
+ }
+ }
+
+ call_resume (stub);
+out:
+ if (op_ret < 0) {
+ STACK_UNWIND_STRICT (fsetattr, frame, -1, op_errno, NULL, NULL);
+ if (stub)
+ call_stub_destroy (stub);
+ }
+
+ return 0;
+}
+
+
+int32_t
+ac_fsetattr (call_frame_t *frame, xlator_t *this, fd_t *fd, struct iatt *buf,
+ int32_t valid)
+{
+ call_stub_t *stub = NULL;
+ int ret = -EFAULT;
+
+ stub = fop_fsetattr_stub (frame, ac_fsetattr_resume, fd, buf, valid);
+ if (!stub) {
+ gf_log (this->name, GF_LOG_ERROR, "cannot create call stub: "
+ "(out of memory)");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ frame->local = stub;
+ STACK_WIND (frame, ac_fsetattr_fstat_cbk, FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->fstat, fd);
+ ret = 0;
+
+out:
+ if (ret < 0)
+ STACK_UNWIND_STRICT (fsetattr, frame, -1, -ret, NULL, NULL);
+
+ return 0;
+}
+
+
+struct xlator_fops fops = {
+ .truncate = ac_truncate,
+ .access = ac_access,
+ .readlink = ac_readlink,
+ .mknod = ac_mknod,
+ .mkdir = ac_mkdir,
+ .unlink = ac_unlink,
+ .rmdir = ac_rmdir,
+ .symlink = ac_symlink,
+ .rename = ac_rename,
+ .link = ac_link,
+ .create = ac_create,
+ .open = ac_open,
+ .readv = ac_readv,
+ .writev = ac_writev,
+ .opendir = ac_opendir,
+ .setattr = ac_setattr,
+ .fsetattr = ac_fsetattr,
+};
+
+int
+init (xlator_t *this)
+{
+ return 0;
+}
+
+void
+fini (xlator_t *this)
+{
+ return;
+}
+
+struct xlator_mops mops = {
+};
+
+struct xlator_cbks cbks = {
+};
diff --git a/xlators/features/access-control/src/access-control.h b/xlators/features/access-control/src/access-control.h
new file mode 100644
index 00000000000..bfc0d775270
--- /dev/null
+++ b/xlators/features/access-control/src/access-control.h
@@ -0,0 +1,55 @@
+/*
+ Copyright (c) 2010 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 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+
+#ifndef __ACCESS_CONTROL_H_
+#define __ACCESS_CONTROL_H_
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+#include "config.h"
+#endif
+
+#define ACTRL "access-control"
+#define ACCTEST_READ 0x1
+#define ACCTEST_WRITE 0x2
+#define ACCTEST_EXEC 0x4
+#define ACCTEST_DONTCARE 0x8
+
+/* Note if the caller is only interested in ownership test i.e. one of the below
++ * in combination with GF_ACCTEST_DONTCARE, then only one type of user's owner
++ * ship can be tested with one call to gf_test_access, i.e. we can only
++ * check of either owner and group, if both need to be tested for a specific
++ * (uid, gid) pair then two calls will be needed.
++ */
+#define ACCTEST_OWNER 0x1
+#define ACCTEST_GROUP 0x2
+#define ACCTEST_OTHER 0x4
+
+/* Signifies any user, as long as we get access. */
+#define ACCTEST_ANY (ACCTEST_OWNER | ACCTEST_GROUP | ACCTEST_OTHER)
+
+#define ac_test_owner(acc) ((acc) & ACCTEST_OWNER)
+#define ac_test_group(acc) ((acc) & ACCTEST_GROUP)
+#define ac_test_other(acc) ((acc) & ACCTEST_OTHER)
+#define ac_test_dontcare(acc) ((acc) & ACCTEST_DONTCARE)
+#define ac_test_read(acc) ((acc) & ACCTEST_READ)
+#define ac_test_write(acc) ((acc) & ACCTEST_WRITE)
+#define ac_test_exec(acc) ((acc) & ACCTEST_EXEC)
+#endif