summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac2
-rw-r--r--xlators/features/Makefile.am2
-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
6 files changed, 1918 insertions, 1 deletions
diff --git a/configure.ac b/configure.ac
index 63a2f345205..e9be1012b2c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -105,6 +105,8 @@ AC_CONFIG_FILES([Makefile
extras/volgen/glusterfs-volgen
contrib/Makefile
contrib/fuse-util/Makefile
+ xlators/features/access-control/Makefile
+ xlators/features/access-control/src/Makefile
glusterfs.spec])
AC_CANONICAL_HOST
diff --git a/xlators/features/Makefile.am b/xlators/features/Makefile.am
index 44c74a41ec6..303767d350f 100644
--- a/xlators/features/Makefile.am
+++ b/xlators/features/Makefile.am
@@ -1,3 +1,3 @@
-SUBDIRS = locks trash quota read-only #path-converter # filter
+SUBDIRS = locks trash quota read-only access-control #path-converter # filter
CLEANFILES =
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