diff options
Diffstat (limited to 'xlators/features/access-control/src/access-control.c')
| -rw-r--r-- | xlators/features/access-control/src/access-control.c | 1844 | 
1 files changed, 1844 insertions, 0 deletions
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 = { +};  | 
