diff options
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | xlators/features/Makefile.am | 2 | ||||
-rw-r--r-- | xlators/features/access-control/Makefile.am | 3 | ||||
-rw-r--r-- | xlators/features/access-control/src/Makefile.am | 13 | ||||
-rw-r--r-- | xlators/features/access-control/src/access-control.c | 1844 | ||||
-rw-r--r-- | xlators/features/access-control/src/access-control.h | 55 |
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 |